From 655041c6577f3aab3998d5c96c6a186b7c51e6c9 Mon Sep 17 00:00:00 2001 From: JRoberts <8711996+jr-simplex@users.noreply.github.com> Date: Mon, 7 Nov 2022 20:44:04 +0400 Subject: [PATCH] ios: fix changed member role resetting in view (#1312) --- apps/ios/Shared/Views/Chat/ChatView.swift | 7 +- .../Views/Chat/Group/GroupChatInfoView.swift | 7 +- .../Chat/Group/GroupMemberInfoView.swift | 139 +++++++++--------- 3 files changed, 79 insertions(+), 74 deletions(-) diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index d6282dd6f1..d8f59be88e 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -392,8 +392,11 @@ struct ChatView: View { await MainActor.run { selectedMember = member } } } - .sheet(item: $selectedMember, onDismiss: { memberConnectionStats = nil }) { member in - GroupMemberInfoView(groupInfo: groupInfo, member: member, connectionStats: $memberConnectionStats) + .sheet(item: $selectedMember, onDismiss: { + selectedMember = nil + memberConnectionStats = nil + }) { _ in + GroupMemberInfoView(groupInfo: groupInfo, member: $selectedMember, connectionStats: $memberConnectionStats) } } else { Rectangle().fill(.clear) diff --git a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift index 41db56c39e..57ecb687e1 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift @@ -71,8 +71,11 @@ struct GroupChatInfoView: View { .sheet(isPresented: $showAddMembersSheet) { AddGroupMembersView(chat: chat, groupInfo: groupInfo) } - .sheet(item: $selectedMember, onDismiss: { connectionStats = nil }) { member in - GroupMemberInfoView(groupInfo: groupInfo, member: member, connectionStats: $connectionStats) + .sheet(item: $selectedMember, onDismiss: { + selectedMember = nil + connectionStats = nil + }) { _ in + GroupMemberInfoView(groupInfo: groupInfo, member: $selectedMember, connectionStats: $connectionStats) } .sheet(isPresented: $showGroupProfile) { GroupProfileView(groupId: groupInfo.apiId, groupProfile: groupInfo.groupProfile) diff --git a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift index fb31ff055b..a9aa29bf64 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift @@ -13,22 +13,22 @@ struct GroupMemberInfoView: View { @EnvironmentObject var chatModel: ChatModel @Environment(\.dismiss) var dismiss: DismissAction var groupInfo: GroupInfo - @State var member: GroupMember + @Binding var member: GroupMember? @Binding var connectionStats: ConnectionStats? @State private var newRole: GroupMemberRole = .member @State private var alert: GroupMemberInfoViewAlert? @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false enum GroupMemberInfoViewAlert: Identifiable { - case removeMemberAlert - case changeMemberRoleAlert(role: GroupMemberRole) + case removeMemberAlert(mem: GroupMember) + case changeMemberRoleAlert(mem: GroupMember, role: GroupMemberRole) case switchAddressAlert case error(title: LocalizedStringKey, error: LocalizedStringKey) var id: String { switch self { case .removeMemberAlert: return "removeMemberAlert" - case let .changeMemberRoleAlert(role): return "changeMemberRoleAlert \(role.rawValue)" + case let .changeMemberRoleAlert(_, role): return "changeMemberRoleAlert \(role.rawValue)" case .switchAddressAlert: return "switchAddressAlert" case let .error(title, _): return "error \(title)" } @@ -37,81 +37,77 @@ struct GroupMemberInfoView: View { var body: some View { NavigationView { - List { - groupMemberInfoHeader() - .listRowBackground(Color.clear) + if let member = member { + List { + groupMemberInfoHeader(member) + .listRowBackground(Color.clear) - if let contactId = member.memberContactId { - Section { - openDirectChatButton(contactId) + if let contactId = member.memberContactId { + Section { + openDirectChatButton(contactId) + } } - } - Section("Member") { - infoRow("Group", groupInfo.displayName) + Section("Member") { + infoRow("Group", groupInfo.displayName) - HStack { if let roles = member.canChangeRoleTo(groupInfo: groupInfo) { Picker("Change role", selection: $newRole) { ForEach(roles) { role in Text(role.text) - .foregroundStyle(.secondary) } } } else { - Text("Role") - Spacer() - Text(member.memberRole.text) - .foregroundStyle(.secondary) + infoRow("Role", member.memberRole.text) } - } - .onAppear { newRole = member.memberRole } - .onChange(of: newRole) { _ in - if newRole != member.memberRole { - alert = .changeMemberRoleAlert(role: newRole) + + // TODO invited by - need to get contact by contact id + if let conn = member.activeConn { + let connLevelDesc = conn.connLevel == 0 ? NSLocalizedString("direct", comment: "connection level description") : String.localizedStringWithFormat(NSLocalizedString("indirect (%d)", comment: "connection level description"), conn.connLevel) + infoRow("Connection", connLevelDesc) } } - // TODO invited by - need to get contact by contact id - if let conn = member.activeConn { - let connLevelDesc = conn.connLevel == 0 ? NSLocalizedString("direct", comment: "connection level description") : String.localizedStringWithFormat(NSLocalizedString("indirect (%d)", comment: "connection level description"), conn.connLevel) - infoRow("Connection", connLevelDesc) + Section("Servers") { + // TODO network connection status + if developerTools { + Button("Change receiving address (BETA)") { + alert = .switchAddressAlert + } + } + if let connStats = connectionStats { + smpServers("Receiving via", connStats.rcvServers) + smpServers("Sending via", connStats.sndServers) + } + } + + if member.canBeRemoved(groupInfo: groupInfo) { + Section { + removeMemberButton(member) + } } - } - Section("Servers") { - // TODO network connection status if developerTools { - Button("Change receiving address (BETA)") { - alert = .switchAddressAlert + Section("For console") { + infoRow("Local name", member.localDisplayName) + infoRow("Database ID", "\(member.groupMemberId)") } } - if let connStats = connectionStats { - smpServers("Receiving via", connStats.rcvServers) - smpServers("Sending via", connStats.sndServers) - } } - - if member.canBeRemoved(groupInfo: groupInfo) { - Section { - removeMemberButton() - } - } - - if developerTools { - Section("For console") { - infoRow("Local name", member.localDisplayName) - infoRow("Database ID", "\(member.groupMemberId)") + .navigationBarHidden(true) + .onAppear { newRole = member.memberRole } + .onChange(of: newRole) { _ in + if newRole != member.memberRole { + alert = .changeMemberRoleAlert(mem: member, role: newRole) } } } - .navigationBarHidden(true) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) .alert(item: $alert) { alertItem in switch(alertItem) { - case .removeMemberAlert: return removeMemberAlert() - case .changeMemberRoleAlert: return changeMemberRoleAlert() + case let .removeMemberAlert(mem): return removeMemberAlert(mem) + case let .changeMemberRoleAlert(mem, _): return changeMemberRoleAlert(mem) case .switchAddressAlert: return switchAddressAlert(switchMemberAddress) case let .error(title, error): return Alert(title: Text(title), message: Text(error)) } @@ -142,18 +138,18 @@ struct GroupMemberInfoView: View { } } - private func groupMemberInfoHeader() -> some View { + private func groupMemberInfoHeader(_ mem: GroupMember) -> some View { VStack { - ProfileImage(imageStr: member.image, color: Color(uiColor: .tertiarySystemFill)) + ProfileImage(imageStr: mem.image, color: Color(uiColor: .tertiarySystemFill)) .frame(width: 192, height: 192) .padding(.top, 12) .padding() - Text(member.displayName) + Text(mem.displayName) .font(.largeTitle) .lineLimit(1) .padding(.bottom, 2) - if member.fullName != "" && member.fullName != member.displayName { - Text(member.fullName) + if mem.fullName != "" && mem.fullName != mem.displayName { + Text(mem.fullName) .font(.title2) .lineLimit(2) } @@ -161,25 +157,25 @@ struct GroupMemberInfoView: View { .frame(maxWidth: .infinity, alignment: .center) } - func removeMemberButton() -> some View { + func removeMemberButton(_ mem: GroupMember) -> some View { Button(role: .destructive) { - alert = .removeMemberAlert + alert = .removeMemberAlert(mem: mem) } label: { Label("Remove member", systemImage: "trash") .foregroundColor(Color.red) } } - private func removeMemberAlert() -> Alert { + private func removeMemberAlert(_ mem: GroupMember) -> Alert { Alert( title: Text("Remove member?"), message: Text("Member will be removed from group - this cannot be undone!"), primaryButton: .destructive(Text("Remove")) { Task { do { - let member = try await apiRemoveMember(groupInfo.groupId, member.groupMemberId) + let updatedMember = try await apiRemoveMember(groupInfo.groupId, mem.groupMemberId) await MainActor.run { - _ = chatModel.upsertGroupMember(groupInfo, member) + _ = chatModel.upsertGroupMember(groupInfo, updatedMember) dismiss() } } catch let error { @@ -193,20 +189,21 @@ struct GroupMemberInfoView: View { ) } - private func changeMemberRoleAlert() -> Alert { + private func changeMemberRoleAlert(_ mem: GroupMember) -> Alert { Alert( title: Text("Change member role?"), - message: member.memberCurrent ? Text("Member role will be changed to \"\(newRole.text)\". All group members will be notified.") : Text("Member role will be changed to \"\(newRole.text)\". The member will receive a new invitation."), + message: mem.memberCurrent ? Text("Member role will be changed to \"\(newRole.text)\". All group members will be notified.") : Text("Member role will be changed to \"\(newRole.text)\". The member will receive a new invitation."), primaryButton: .default(Text("Change")) { Task { do { - let mem = try await apiMemberRole(groupInfo.groupId, member.groupMemberId, newRole) + let updatedMember = try await apiMemberRole(groupInfo.groupId, mem.groupMemberId, newRole) await MainActor.run { - member = mem - _ = chatModel.upsertGroupMember(groupInfo, mem) + member = updatedMember + _ = chatModel.upsertGroupMember(groupInfo, updatedMember) } + } catch let error { - newRole = member.memberRole + newRole = mem.memberRole logger.error("apiMemberRole error: \(responseError(error))") let a = getErrorAlert(error, "Error changing role") alert = .error(title: a.title, error: a.message) @@ -214,7 +211,7 @@ struct GroupMemberInfoView: View { } }, secondaryButton: .cancel { - newRole = member.memberRole + newRole = mem.memberRole } ) } @@ -222,7 +219,9 @@ struct GroupMemberInfoView: View { private func switchMemberAddress() { Task { do { - try await apiSwitchGroupMember(groupInfo.apiId, member.groupMemberId) + if let member = member { + try await apiSwitchGroupMember(groupInfo.apiId, member.groupMemberId) + } } catch let error { logger.error("switchMemberAddress apiSwitchGroupMember error: \(responseError(error))") let a = getErrorAlert(error, "Error changing address") @@ -238,7 +237,7 @@ struct GroupMemberInfoView_Previews: PreviewProvider { static var previews: some View { GroupMemberInfoView( groupInfo: GroupInfo.sampleData, - member: GroupMember.sampleData, + member: Binding.constant(GroupMember.sampleData), connectionStats: Binding.constant(nil) ) }