diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 20d6aaffde..30249c7390 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -945,17 +945,22 @@ func startChat() throws { chatLastStartGroupDefault.set(Date.now) } -func changeActiveUser(_ toUserId: Int64) { +func changeActiveUser(_ userId: Int64) { let m = ChatModel.shared do { - m.currentUser = try apiSetActiveUser(toUserId) - m.users = try listUsers() - try getUserChatData() + try changeActiveUser_(userId) } catch let error { logger.error("Unable to set active user: \(responseError(error))") } } +func changeActiveUser_(_ userId: Int64) throws { + let m = ChatModel.shared + m.currentUser = try apiSetActiveUser(userId) + m.users = try listUsers() + try getUserChatData() +} + func getUserChatData() throws { let m = ChatModel.shared m.userAddress = try apiGetUserAddress() diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index 638ce711bb..eaefaa0475 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -80,9 +80,8 @@ struct ChatListView: View { } } label: { let user = chatModel.currentUser ?? User.sampleData - let color = Color(uiColor: .tertiarySystemGroupedBackground) ZStack(alignment: .topTrailing) { - ProfileImage(imageStr: user.image, color: color) + ProfileImage(imageStr: user.image, color: Color(uiColor: .quaternaryLabel)) .frame(width: 32, height: 32) .padding(.trailing, 4) let allRead = chatModel.users diff --git a/apps/ios/Shared/Views/ChatList/UserPicker.swift b/apps/ios/Shared/Views/ChatList/UserPicker.swift index 3503a87615..8d50bff6b0 100644 --- a/apps/ios/Shared/Views/ChatList/UserPicker.swift +++ b/apps/ios/Shared/Views/ChatList/UserPicker.swift @@ -96,7 +96,7 @@ struct UserPicker: View { } }, label: { HStack(spacing: 0) { - ProfileImage(imageStr: user.image) + ProfileImage(imageStr: user.image, color: Color(uiColor: .tertiarySystemFill)) .frame(width: 44, height: 44) .padding(.trailing, 12) Text(user.chatViewName) diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index d133edad3f..a05a43ad21 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -394,7 +394,7 @@ func settingsRow(_ icon: String, color: Color = .secondary, cont struct ProfilePreview: View { var profileOf: NamedChat - var color = Color(uiColor: .tertiarySystemGroupedBackground) + var color = Color(uiColor: .tertiarySystemFill) var body: some View { HStack { diff --git a/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift b/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift index a96bf501f4..6752dab748 100644 --- a/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift @@ -9,15 +9,17 @@ import SimpleXChat struct UserProfilesView: View { @EnvironmentObject private var m: ChatModel @Environment(\.editMode) private var editMode + @State private var showDeleteConfirmation = false + @State private var userToDelete: Int? @State private var alert: UserProfilesAlert? private enum UserProfilesAlert: Identifiable { - case deleteUser(index: Int) + case deleteUser(index: Int, delSMPQueues: Bool) case error(title: LocalizedStringKey, error: LocalizedStringKey = "") var id: String { switch self { - case let .deleteUser(index): return "deleteUser \(index)" + case let .deleteUser(index, delSMPQueues): return "deleteUser \(index) \(delSMPQueues)" case let .error(title, _): return "error \(title)" } } @@ -31,7 +33,8 @@ struct UserProfilesView: View { } .onDelete { indexSet in if let i = indexSet.first { - alert = .deleteUser(index: i) + showDeleteConfirmation = true + userToDelete = i } } @@ -47,14 +50,18 @@ struct UserProfilesView: View { } } .toolbar { EditButton() } + .confirmationDialog("Delete chat profile?", isPresented: $showDeleteConfirmation, titleVisibility: .visible) { + deleteModeButton("Profile and server connections", true) + deleteModeButton("Local profile data only", false) + } .alert(item: $alert) { alert in switch alert { - case let .deleteUser(index): + case let .deleteUser(index, delSMPQueues): return Alert( title: Text("Delete user profile?"), message: Text("All chats and messages will be deleted - this cannot be undone!"), primaryButton: .destructive(Text("Delete")) { - removeUser(index: index) + removeUser(index, delSMPQueues) }, secondaryButton: .cancel() ) @@ -64,24 +71,43 @@ struct UserProfilesView: View { } } - private func removeUser(index: Int) { + private func deleteModeButton(_ title: LocalizedStringKey, _ delSMPQueues: Bool) -> some View { + Button(title, role: .destructive) { + if let i = userToDelete { + alert = .deleteUser(index: i, delSMPQueues: delSMPQueues) + } + } + } + + private func removeUser(_ index: Int, _ delSMPQueues: Bool) { + if index >= m.users.count { return } do { - try apiDeleteUser(m.users[index].user.userId, true) - m.users.remove(at: index) + let u = m.users[index].user + if u.activeUser { + if let newActive = m.users.first(where: { !$0.user.activeUser }) { + try changeActiveUser_(newActive.user.userId) + try deleteUser(u.userId) + } + } else { + try deleteUser(u.userId) + } } catch let error { let a = getErrorAlert(error, "Error deleting user profile") alert = .error(title: a.title, error: a.message) } + + func deleteUser(_ userId: Int64) throws { + try apiDeleteUser(userId, delSMPQueues) + m.users.remove(at: index) + } } @ViewBuilder private func userView(_ user: User) -> some View { Button { - if !user.activeUser { - changeActiveUser(user.userId) - } + changeActiveUser(user.userId) } label: { HStack { - ProfileImage(imageStr: user.image) + ProfileImage(imageStr: user.image, color: Color(uiColor: .tertiarySystemFill)) .frame(width: 44, height: 44) .padding(.vertical, 4) .padding(.trailing, 12) @@ -91,8 +117,9 @@ struct UserProfilesView: View { .foregroundColor(user.activeUser ? .primary : .clear) } } + .disabled(user.activeUser) .foregroundColor(.primary) - .deleteDisabled(user.activeUser) + .deleteDisabled(m.users.count <= 1) } } diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 83ec0a766e..23c2958c17 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -102,7 +102,7 @@ public enum ChatCommand { case let .createActiveUser(profile): return "/create user \(profile.displayName) \(profile.fullName)" case .listUsers: return "/users" case let .apiSetActiveUser(userId): return "/_user \(userId)" - case let .apiDeleteUser(userId, delSMPQueues): return "/_delete user \(userId) delSMPQueues=\(onOff(delSMPQueues))" + case let .apiDeleteUser(userId, delSMPQueues): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))" case let .startChat(subscribe, expire): return "/_start subscribe=\(onOff(subscribe)) expire=\(onOff(expire))" case .apiStopChat: return "/_stop" case .apiActivateChat: return "/_app activate"