Add ErrorActionHandler

This commit is contained in:
Djorkaeff Alexandre 2024-02-15 16:44:15 -03:00
parent 0113dbe49e
commit cdbab6ec07
12 changed files with 193 additions and 19 deletions

View File

@ -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
}
}
}

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -1,11 +1,17 @@
{
"sourceLanguage" : "en",
"strings" : {
"Delete" : {
},
"Load More..." : {
},
"Message" : {
},
"Resend" : {
},
"Rooms" : {

View File

@ -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 {

View File

@ -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()
}
}

View File

@ -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) {

View File

@ -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
)
}
}
}

View File

@ -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";