Add ErrorActionHandler
This commit is contained in:
parent
0113dbe49e
commit
cdbab6ec07
|
@ -0,0 +1,29 @@
|
|||
protocol ErrorActionHandling {
|
||||
func handle(error: RocketChatError)
|
||||
}
|
||||
|
||||
final class ErrorActionHandler {
|
||||
@Dependency private var database: Database
|
||||
@Dependency private var serversDB: ServersDatabase
|
||||
@Dependency private var router: AppRouting
|
||||
|
||||
private let server: Server
|
||||
|
||||
init(server: Server) {
|
||||
self.server = server
|
||||
}
|
||||
}
|
||||
|
||||
extension ErrorActionHandler: ErrorActionHandling {
|
||||
func handle(error: RocketChatError) {
|
||||
switch error {
|
||||
case .unauthorized:
|
||||
router.route(to: .serverList)
|
||||
|
||||
database.remove()
|
||||
serversDB.remove(server)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@ protocol ServersDatabase {
|
|||
func user(id: String) -> LoggedUser?
|
||||
func servers() -> [Server]
|
||||
|
||||
func remove(_ server: Server)
|
||||
|
||||
func save()
|
||||
|
||||
func process(updatedServer: WatchMessage.Server)
|
||||
|
@ -67,6 +69,12 @@ final class DefaultDatabase: ServersDatabase {
|
|||
return (try? viewContext.fetch(request)) ?? []
|
||||
}
|
||||
|
||||
func remove(_ server: Server) {
|
||||
viewContext.delete(server)
|
||||
|
||||
save()
|
||||
}
|
||||
|
||||
func process(updatedServer: WatchMessage.Server) {
|
||||
if let server = server(url: updatedServer.url) {
|
||||
server.url = updatedServer.url
|
||||
|
|
|
@ -6,10 +6,14 @@ protocol Database {
|
|||
func room(id: String) -> Room?
|
||||
func message(id: String) -> Message?
|
||||
func createTempMessage(msg: String, in room: Room, for loggedUser: LoggedUser) -> String
|
||||
func updateMessage(_ id: String, status: String)
|
||||
func remove(_ message: Message)
|
||||
|
||||
func process(subscription: SubscriptionsResponse.Subscription)
|
||||
func process(subscription: SubscriptionsResponse.Subscription?, in updatedRoom: RoomsResponse.Room)
|
||||
func process(updatedMessage: MessageResponse, in room: Room)
|
||||
|
||||
func remove()
|
||||
}
|
||||
|
||||
final class RocketChatDatabase: Database {
|
||||
|
@ -102,6 +106,19 @@ final class RocketChatDatabase: Database {
|
|||
return id
|
||||
}
|
||||
|
||||
func updateMessage(_ id: String, status: String) {
|
||||
let message = message(id: id) ?? createMessage(id: id)
|
||||
message.status = status
|
||||
|
||||
save()
|
||||
}
|
||||
|
||||
func remove(_ message: Message) {
|
||||
viewContext.delete(message)
|
||||
|
||||
save()
|
||||
}
|
||||
|
||||
func user(id: String) -> User? {
|
||||
let user = User(context: viewContext)
|
||||
user.id = id
|
||||
|
@ -225,4 +242,16 @@ final class RocketChatDatabase: Database {
|
|||
|
||||
save()
|
||||
}
|
||||
|
||||
func remove() {
|
||||
guard let path = container.persistentStoreDescriptions.first?.url?.path else {
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
try FileManager.default.removeItem(atPath: path)
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import Foundation
|
|||
|
||||
protocol MessageSending {
|
||||
func sendMessage(_ msg: String, in room: Room)
|
||||
func resendMessage(messageID: String, msg: String, in room: Room)
|
||||
}
|
||||
|
||||
final class MessageSender {
|
||||
|
@ -18,15 +19,21 @@ final class MessageSender {
|
|||
|
||||
extension MessageSender: MessageSending {
|
||||
func sendMessage(_ msg: String, in room: Room) {
|
||||
guard let rid = room.id else { return }
|
||||
|
||||
let messageID = database.createTempMessage(msg: msg, in: room, for: server.loggedUser)
|
||||
|
||||
resendMessage(messageID: messageID, msg: msg, in: room)
|
||||
}
|
||||
|
||||
func resendMessage(messageID: String, msg: String, in room: Room) {
|
||||
guard let rid = room.id else { return }
|
||||
|
||||
client.sendMessage(id: messageID, rid: rid, msg: msg)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.subscribe(Subscribers.Sink { completion in
|
||||
if case .failure(let error) = completion {
|
||||
print(error)
|
||||
.subscribe(Subscribers.Sink { [weak self] completion in
|
||||
guard let self else { return }
|
||||
|
||||
if case .failure = completion {
|
||||
self.database.updateMessage(messageID, status: "error")
|
||||
}
|
||||
} receiveValue: { [weak self] messageResponse in
|
||||
guard let self else {
|
||||
|
|
|
@ -16,6 +16,7 @@ final class MessagesLoader {
|
|||
@Dependency private var client: RocketChatClientProtocol
|
||||
@Dependency private var database: Database
|
||||
@Dependency private var serversDB: ServersDatabase
|
||||
@Dependency private var errorActionHandler: ErrorActionHandling
|
||||
|
||||
private var roomID: String?
|
||||
|
||||
|
@ -32,9 +33,9 @@ final class MessagesLoader {
|
|||
|
||||
client.syncMessages(rid: rid, updatedSince: date)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { completion in
|
||||
.sink { [weak self] completion in
|
||||
if case .failure(let error) = completion {
|
||||
print(error)
|
||||
self?.errorActionHandler.handle(error: error)
|
||||
}
|
||||
} receiveValue: { [weak self] messagesResponse in
|
||||
let messages = messagesResponse.result.updated
|
||||
|
@ -57,9 +58,9 @@ final class MessagesLoader {
|
|||
|
||||
client.getHistory(rid: rid, t: room.t ?? "", latest: date)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { completion in
|
||||
.sink { [weak self] completion in
|
||||
if case .failure(let error) = completion {
|
||||
print(error)
|
||||
self?.errorActionHandler.handle(error: error)
|
||||
}
|
||||
} receiveValue: { [weak self] messagesResponse in
|
||||
let messages = messagesResponse.messages
|
||||
|
@ -84,9 +85,9 @@ final class MessagesLoader {
|
|||
|
||||
client.sendRead(rid: rid)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { completion in
|
||||
.sink { [weak self] completion in
|
||||
if case .failure(let error) = completion {
|
||||
print(error)
|
||||
self?.errorActionHandler.handle(error: error)
|
||||
}
|
||||
} receiveValue: { _ in
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ final class RoomsLoader {
|
|||
@Dependency private var client: RocketChatClientProtocol
|
||||
@Dependency private var database: Database
|
||||
@Dependency private var serversDB: ServersDatabase
|
||||
@Dependency private var errorActionHandler: ErrorActionHandling
|
||||
|
||||
private var timer: Timer?
|
||||
private var cancellable = CancelBag()
|
||||
|
@ -31,10 +32,9 @@ final class RoomsLoader {
|
|||
client.getSubscriptions(updatedSince: updatedSince)
|
||||
)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { completion in
|
||||
.sink { [weak self] completion in
|
||||
if case .failure(let error) = completion {
|
||||
// TODO: LOGOUT
|
||||
print(error)
|
||||
self?.errorActionHandler.handle(error: error)
|
||||
}
|
||||
} receiveValue: { (roomsResponse, subscriptionsResponse) in
|
||||
let rooms = roomsResponse.update
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"Delete" : {
|
||||
|
||||
},
|
||||
"Load More..." : {
|
||||
|
||||
},
|
||||
"Message" : {
|
||||
|
||||
},
|
||||
"Resend" : {
|
||||
|
||||
},
|
||||
"Rooms" : {
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ struct LoggedInView: View {
|
|||
Store.register(Database.self, factory: database)
|
||||
Store.register(RocketChatClientProtocol.self, factory: RocketChatClient(server: server))
|
||||
Store.register(MessageSending.self, factory: MessageSender(server: server))
|
||||
Store.register(ErrorActionHandling.self, factory: ErrorActionHandler(server: server))
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import SwiftUI
|
||||
|
||||
struct MessageActionView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
private let action: (MessageAction) -> Void
|
||||
private let message: Message
|
||||
|
||||
init(message: Message, action: @escaping (MessageAction) -> Void) {
|
||||
self.action = action
|
||||
self.message = message
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Button(action: {
|
||||
dismiss()
|
||||
|
||||
guard let messageID = message.id, let msg = message.msg else { return }
|
||||
|
||||
action(.resend(messageID, msg))
|
||||
}, label: {
|
||||
Text("Resend")
|
||||
})
|
||||
Button(action: {
|
||||
dismiss()
|
||||
|
||||
action(.delete(message))
|
||||
}, label: {
|
||||
Text("Delete")
|
||||
.foregroundStyle(.red)
|
||||
})
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
|
@ -54,7 +54,16 @@ struct MessageListView: View {
|
|||
MessageView(
|
||||
client: client,
|
||||
viewModel: .init(message: message, previousMessage: previousMessage, server: server, lastOpen: lastOpen)
|
||||
)
|
||||
) { action in
|
||||
switch action {
|
||||
case .resend(let id, let msg):
|
||||
messageSender.resendMessage(messageID: id, msg: msg, in: room)
|
||||
|
||||
lastOpen = nil
|
||||
case .delete(let message):
|
||||
database.remove(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageComposerView(room: room) {
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
import SwiftUI
|
||||
|
||||
enum MessageAction {
|
||||
case resend(String, String)
|
||||
case delete(Message)
|
||||
}
|
||||
|
||||
struct MessageView: View {
|
||||
@ObservedObject private var viewModel: MessageViewModel
|
||||
@State private var message: Message?
|
||||
|
||||
private let action: (MessageAction) -> Void
|
||||
private let client: RocketChatClientProtocol
|
||||
|
||||
init(client: RocketChatClientProtocol, viewModel: MessageViewModel) {
|
||||
init(client: RocketChatClientProtocol, viewModel: MessageViewModel, action: @escaping (MessageAction) -> Void) {
|
||||
self.action = action
|
||||
self.client = client
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
@ -68,9 +76,25 @@ struct MessageView: View {
|
|||
.font(.caption.italic())
|
||||
.foregroundStyle(.primary)
|
||||
} else if let text = viewModel.message.msg {
|
||||
Text(text)
|
||||
.font(.caption)
|
||||
.foregroundStyle(viewModel.message.status == "temp" ? .secondary : .primary)
|
||||
HStack {
|
||||
Text(text)
|
||||
.font(.caption)
|
||||
.foregroundStyle(viewModel.message.status == "temp" ? .secondary : .primary)
|
||||
|
||||
if viewModel.message.status == "error" {
|
||||
Button(
|
||||
action: {
|
||||
message = viewModel.message
|
||||
},
|
||||
label: {
|
||||
Image(systemName: "exclamationmark.circle")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.red)
|
||||
}
|
||||
)
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
}
|
||||
}
|
||||
if let attachments = viewModel.message.attachments?.allObjects as? Array<Attachment> {
|
||||
ForEach(attachments) { attachment in
|
||||
|
@ -78,5 +102,11 @@ struct MessageView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.sheet(item: $message) { message in
|
||||
MessageActionView(
|
||||
message: message,
|
||||
action: action
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
1E01C82D2511337700FEF824 /* RoomKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E01C82C2511337700FEF824 /* RoomKey.swift */; };
|
||||
1E0426E6251A5467008F022C /* RoomType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E0426E5251A5467008F022C /* RoomType.swift */; };
|
||||
1E0426E7251A54B4008F022C /* RoomType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E0426E5251A5467008F022C /* RoomType.swift */; };
|
||||
1E06561B2B7E91FB0081B01F /* ErrorActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E06561A2B7E91FB0081B01F /* ErrorActionHandler.swift */; };
|
||||
1E06561D2B7E9C1C0081B01F /* MessageActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E06561C2B7E9C1C0081B01F /* MessageActionView.swift */; };
|
||||
1E068CFE24FD2DC700A0FFC1 /* AppGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E068CFD24FD2DC700A0FFC1 /* AppGroup.swift */; };
|
||||
1E068CFF24FD2DC700A0FFC1 /* AppGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E068CFD24FD2DC700A0FFC1 /* AppGroup.swift */; };
|
||||
1E068D0124FD2E0500A0FFC1 /* AppGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E068D0024FD2E0500A0FFC1 /* AppGroup.m */; };
|
||||
|
@ -336,6 +338,8 @@
|
|||
1E01C82A2511335A00FEF824 /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = "<group>"; };
|
||||
1E01C82C2511337700FEF824 /* RoomKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomKey.swift; sourceTree = "<group>"; };
|
||||
1E0426E5251A5467008F022C /* RoomType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomType.swift; sourceTree = "<group>"; };
|
||||
1E06561A2B7E91FB0081B01F /* ErrorActionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorActionHandler.swift; sourceTree = "<group>"; };
|
||||
1E06561C2B7E9C1C0081B01F /* MessageActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActionView.swift; sourceTree = "<group>"; };
|
||||
1E068CFD24FD2DC700A0FFC1 /* AppGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppGroup.swift; sourceTree = "<group>"; };
|
||||
1E068D0024FD2E0500A0FFC1 /* AppGroup.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppGroup.m; sourceTree = "<group>"; };
|
||||
1E1C2F7F250FCB69005DCE7D /* Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Database.swift; sourceTree = "<group>"; };
|
||||
|
@ -572,6 +576,14 @@
|
|||
name = RocketChatRN;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1E0656192B7E91F00081B01F /* ActionHandler */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1E06561A2B7E91FB0081B01F /* ErrorActionHandler.swift */,
|
||||
);
|
||||
path = ActionHandler;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1E068CFB24FD2DAF00A0FFC1 /* AppGroup */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -797,6 +809,7 @@
|
|||
1E4AFC262B5B23C600E2AA7D /* RetryView.swift */,
|
||||
1EDB30F12B5B453A00532C7E /* LoggedInView.swift */,
|
||||
1E638E982B5F0A2900E645E4 /* ChatScrollView.swift */,
|
||||
1E06561C2B7E9C1C0081B01F /* MessageActionView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
@ -813,6 +826,7 @@
|
|||
1ED0388F2B507B4C00C007D4 /* RocketChat Watch App */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1E0656192B7E91F00081B01F /* ActionHandler */,
|
||||
1EDFD0FB2B589FC4002FEE5F /* DependencyInjection */,
|
||||
1EDFD0F82B589B82002FEE5F /* Loaders */,
|
||||
1E29A31E2B5871BE0093C03C /* Formatters */,
|
||||
|
@ -1855,6 +1869,7 @@
|
|||
files = (
|
||||
1E29A3242B5874FF0093C03C /* MessageComposerView.swift in Sources */,
|
||||
1EB375892B55DBFB00AEC3D7 /* Server.swift in Sources */,
|
||||
1E06561D2B7E9C1C0081B01F /* MessageActionView.swift in Sources */,
|
||||
1E29A3162B5868DF0093C03C /* MessageListView.swift in Sources */,
|
||||
1E4AFC172B5AF09C00E2AA7D /* Store.swift in Sources */,
|
||||
1E29A2F42B585B070093C03C /* SubscriptionsResponse.swift in Sources */,
|
||||
|
@ -1874,6 +1889,7 @@
|
|||
1E29A3202B5871C80093C03C /* RoomFormatter.swift in Sources */,
|
||||
1EDFD0FA2B589B8F002FEE5F /* MessagesLoader.swift in Sources */,
|
||||
1E29A3102B5865B80093C03C /* RoomViewModel.swift in Sources */,
|
||||
1E06561B2B7E91FB0081B01F /* ErrorActionHandler.swift in Sources */,
|
||||
1E29A2FC2B585B070093C03C /* SendMessageRequest.swift in Sources */,
|
||||
1E29A30C2B585D1D0093C03C /* String+Extensions.swift in Sources */,
|
||||
1ED033CD2B55D671004F4930 /* RocketChatDatabase.swift in Sources */,
|
||||
|
@ -2325,6 +2341,7 @@
|
|||
MARKETING_VERSION = 1.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.watchkitapp;
|
||||
PRODUCT_NAME = Rocket.Chat;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.reactnative.watchkitapp";
|
||||
|
@ -2372,6 +2389,7 @@
|
|||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.watchkitapp;
|
||||
PRODUCT_NAME = Rocket.Chat;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.reactnative.watchkitapp";
|
||||
|
|
Loading…
Reference in New Issue