diff --git a/ConversationsClassic/AppData/Store/ClientsStore.swift b/ConversationsClassic/AppData/Store/ClientsStore.swift index 45cad55..5710dcd 100644 --- a/ConversationsClassic/AppData/Store/ClientsStore.swift +++ b/ConversationsClassic/AppData/Store/ClientsStore.swift @@ -76,21 +76,6 @@ extension ClientsStore { } extension ClientsStore { - func addRoster(_ credentials: Credentials, contactJID: String, name: String?, groups: [String]) async throws { - // check that roster exist in db as locally deleted and undelete it - let deletedLocally = try await Roster.fetchDeletedLocally() - if var roster = deletedLocally.first(where: { $0.contactBareJid == contactJID }) { - try await roster.setLocallyDeleted(false) - return - } - - // add new roster - guard let client = client(for: credentials) else { - throw ClientStoreError.clientNotFound - } - try await client.addRoster(contactJID, name: name, groups: groups) - } - private func resubscribeRosters() { let clientsJids = clients .filter { $0.state != .disabled } @@ -108,4 +93,26 @@ extension ClientsStore { self?.actualRosters = rosters } } + + func addRoster(_ credentials: Credentials, contactJID: String, name: String?, groups: [String]) async throws { + // check that roster exist in db as locally deleted and undelete it + let deletedLocally = try await Roster.fetchDeletedLocally() + if var roster = deletedLocally.first(where: { $0.contactBareJid == contactJID }) { + try await roster.setLocallyDeleted(false) + return + } + + // add new roster + guard let client = client(for: credentials) else { + throw ClientStoreError.clientNotFound + } + try await client.addRoster(contactJID, name: name, groups: groups) + } + + func deleteRoster(_ roster: Roster) async throws { + guard let client = clients.first(where: { $0.credentials.bareJid == roster.bareJid }) else { + throw ClientStoreError.clientNotFound + } + try await client.deleteRoster(roster) + } } diff --git a/ConversationsClassic/Resources/Strings/Localizable.strings b/ConversationsClassic/Resources/Strings/Localizable.strings index 11921d7..a8dbb8d 100644 --- a/ConversationsClassic/Resources/Strings/Localizable.strings +++ b/ConversationsClassic/Resources/Strings/Localizable.strings @@ -32,6 +32,14 @@ "Contacts.Add.title" = "Add Contact"; "Contacts.Add.explanation" = "Contact or group/channel name are usually JID in format name@domain.ltd (like email)"; "Contacts.Add.serverError" = "Contact adding dailed. Server returned error"; +"Contacts.deleteContact" = "Delete contact"; +"Contacts.Delete.deleteFromDevice" = "Delete from device"; +"Contacts.Delete.deleteCompletely" = "Delete completely"; +"Contacts.sendMessage" = "Send message"; +"Contacts.editContact" = "Edit contact"; +"Contacts.selectContact" = "Select contact"; +"Contacts.Delete.message" = "You can delete contact from this device (contact will be available on other devices), or delete it completely"; +"Contacts.Delete.error" = "Contact not deleted. Server returns error."; @@ -39,27 +47,6 @@ -// MARK: Onboar screen -//"Login.title" = "Let\'s go!"; -//"Login.subtitle" = "Enter your JID, it should looks like email address"; -//"Login.Hint.jid" = "user@domain.im"; -//"Login.Hint.password" = "password"; -//"Login.btn" = "Continue"; -//"Login.Error.wrongPassword" = "Wrong password or JID"; -//"Login.Error.noServer" = "Server not exists"; -//"Login.Error.serverError" = "Server error. Check internet connection"; - -// MARK: Contacts screen -//"Contacts.sendMessage" = "Send message"; -//"Contacts.editContact" = "Edit contact"; -//"Contacts.selectContact" = "Select contact"; -//"Contacts.deleteContact" = "Delete contact"; -//"Contacts.Delete.title" = "Delete contact"; -//"Contacts.Delete.message" = "You can delete contact from this device (contact will be available on other devices), or delete it completely"; -//"Contacts.Delete.deleteFromDevice" = "Delete from device"; -//"Contacts.Delete.deleteCompletely" = "Delete completely"; -//"Contacts.Delete.error" = "Contact not deleted. Server returns error."; - // MARK: Chats screen //"Chats.title" = "Chats"; @@ -72,21 +59,7 @@ //"Chats.Create.Main.createPrivateGroup" = "Create private group"; //"Chats.Create.Main.findGroup" = "Find public group"; -// MARK: Accounts add screen -//"Accounts.Add.or" = "or"; -//"Accounts.Add.Exist.title" = "Add existing\naccount"; -//"Accounts.Add.Exist.Prompt.jid" = "Enter your XMPP ID"; -//"Accounts.Add.Exist.Prompt.password" = "Enter password"; -//"Accounts.Add.Exist.Hint.jid" = "user@domain.im"; -//"Accounts.Add.Exist.Hint.password" = "password"; -//"Accounts.Add.Exist.Btn.link" = "create a new one"; -//"Accounts.Add.Exist.Btn.main" = "Continue"; -//"Accounts.Add.Exist.loginError" = "Wrong login or password"; -// MARK: Server connecting indicator -//"ServerConnectingIndicator.State.connecting" = "Connecting to server"; -//"ServerConnectingIndicator.State.connected" = "Connected"; -//"ServerConnectingIndicator.State.error" = "Server unreachable. Check internet connection and server name"; // MARK: Attachments //"Attachment.Prompt.main" = "Select attachment"; diff --git a/ConversationsClassic/View/Main/Contacts/ContactsScreen.swift b/ConversationsClassic/View/Main/Contacts/ContactsScreen.swift index 7a9feef..6436a5b 100644 --- a/ConversationsClassic/View/Main/Contacts/ContactsScreen.swift +++ b/ConversationsClassic/View/Main/Contacts/ContactsScreen.swift @@ -41,49 +41,14 @@ struct ContactsScreen: View { } } } - // .task { - // rosters = await clientsStore.actualRosters - // } - // .alert(isPresented: $isErrorAlertPresented) { - // Alert( - // title: Text(L10n.Global.Error.title), - // message: Text(errorAlertMessage), - // dismissButton: .default(Text(L10n.Global.ok)) - // ) - // } } - - // private func fetchRosters() async { - // let jids = clientsStore.clients - // .filter { $0.state != .disabled } - // .map { $0.credentials.bareJid } - // - // do { - // try await withThrowingTaskGroup(of: [Roster].self) { group in - // for jid in jids { - // group.addTask { - // try await Roster.fetchAll(for: jid) - // } - // } - // - // var allRosters: [Roster] = [] - // for try await rosters in group { - // allRosters.append(contentsOf: rosters) - // } - // self.rosters = allRosters.sorted { $0.contactBareJid < $1.contactBareJid } - // } - // } catch {} - // } } private struct ContactsScreenRow: View { + @Environment(\.router) var router + @EnvironmentObject var clientsStore: ClientsStore + var roster: Roster - // @State private var isShowingMenu = false - // @State private var isDeleteAlertPresented = false - // - // @Binding var isErrorAlertPresented: Bool - // @Binding var errorAlertMessage: String - // @Binding var isShowingLoader: Bool var body: some View { SharedListRow( @@ -93,67 +58,93 @@ private struct ContactsScreenRow: View { .onTapGesture { // store.dispatch(.chatsAction(.startChat(accountJid: roster.bareJid, participantJid: roster.contactBareJid))) } - // .onLongPressGesture { - // isShowingMenu.toggle() - // } - // .swipeActions(edge: .trailing, allowsFullSwipe: false) { - // Button { - // isDeleteAlertPresented = true - // } label: { - // Label(L10n.Contacts.sendMessage, systemImage: "trash") - // } - // .tint(Color.red) - // } - // .contextMenu { - // Button(L10n.Contacts.sendMessage, systemImage: "message") { - // store.dispatch(.chatsAction(.startChat(accountJid: roster.bareJid, participantJid: roster.contactBareJid))) - // } - // Divider() - // - // Button(L10n.Contacts.editContact) { - // print("Edit contact") - // } - // - // Button(L10n.Contacts.selectContact) { - // print("Select contact") - // } - // - // Divider() - // Button(L10n.Contacts.deleteContact, systemImage: "trash", role: .destructive) { - // isDeleteAlertPresented = true - // } - // } - // .actionSheet(isPresented: $isDeleteAlertPresented) { - // ActionSheet( - // title: Text(L10n.Contacts.Delete.title), - // message: Text(L10n.Contacts.Delete.message), - // buttons: [ - // .destructive(Text(L10n.Contacts.Delete.deleteFromDevice)) { - // store.dispatch(.rostersAction(.markRosterAsLocallyDeleted(ownerJID: roster.bareJid, contactJID: roster.contactBareJid))) - // }, - // .destructive(Text(L10n.Contacts.Delete.deleteCompletely)) { - // isShowingLoader = true - // store.dispatch(.rostersAction(.deleteRoster(ownerJID: roster.bareJid, contactJID: roster.contactBareJid))) - // }, - // .cancel(Text(L10n.Global.cancel)) - // ] - // ) - // } - // .onChange(of: store.state.rostersState.rosters) { _ in - // endOfDeleting() - // } - // .onChange(of: store.state.rostersState.deleteRosterError) { _ in - // endOfDeleting() - // } + .swipeActions(edge: .trailing, allowsFullSwipe: false) { + Button { + router.showAlert(.confirmationDialog, title: L10n.Contacts.deleteContact, subtitle: L10n.Contacts.Delete.message) { + deleteConfirmation + } + } label: { + Label("", systemImage: "trash") + } + .tint(Color.red) + } + .contextMenu { + Button(L10n.Contacts.sendMessage, systemImage: "message") { + // store.dispatch(.chatsAction(.startChat(accountJid: roster.bareJid, participantJid: roster.contactBareJid))) + } + Divider() + + Button(L10n.Contacts.editContact) { + print("Edit contact") + } + + Button(L10n.Contacts.selectContact) { + print("Select contact") + } + + Divider() + Button(L10n.Contacts.deleteContact, systemImage: "trash", role: .destructive) { + router.showAlert(.confirmationDialog, title: L10n.Contacts.deleteContact, subtitle: L10n.Contacts.Delete.message) { + deleteConfirmation + } + } + } } - // private func endOfDeleting() { - // if isShowingLoader { - // isShowingLoader = false - // if let error = store.state.rostersState.deleteRosterError { - // errorAlertMessage = error - // isErrorAlertPresented = true - // } - // } - // } + @ViewBuilder private var deleteConfirmation: some View { + Button(role: .destructive) { + Task { + await deleteFromDevice() + } + } label: { + Text(L10n.Contacts.Delete.deleteFromDevice) + } + + Button(role: .destructive) { + Task { + await deleteCompletely() + } + } label: { + Text(L10n.Contacts.Delete.deleteCompletely) + } + + Button(role: .cancel) {} label: { + Text(L10n.Global.cancel) + } + } + + private func deleteFromDevice() async { + router.showModal { + LoadingScreen() + } + + defer { + router.dismissModal() + } + + var roster = roster + try? await roster.setLocallyDeleted(true) + } + + private func deleteCompletely() async { + router.showModal { + LoadingScreen() + } + + defer { + router.dismissModal() + } + + do { + try await clientsStore.deleteRoster(roster) + } catch { + router.showAlert( + .alert, + title: L10n.Global.Error.title, + subtitle: L10n.Contacts.Delete.error + ) { + Button(L10n.Global.ok, role: .cancel) {} + } + } + } } diff --git a/ConversationsClassic/View/Main/MainTabScreen.swift b/ConversationsClassic/View/Main/MainTabScreen.swift index 736bbcb..bbeae39 100644 --- a/ConversationsClassic/View/Main/MainTabScreen.swift +++ b/ConversationsClassic/View/Main/MainTabScreen.swift @@ -27,9 +27,7 @@ struct MainTabScreen: View { // ConversationsScreen() case .contacts: - RouterView { _ in - ContactsScreen() - } + ContactsScreen() case .settings: Color.green