From 1b8c55a0a34b97f8b037f1e52e90909abbd7998d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 30 Jul 2022 18:46:10 +0100 Subject: [PATCH] ios: add group members when group is created (#857) * ios: add group members when group is created * refactor * more refactor --- apps/ios/Shared/Views/Chat/ChatView.swift | 4 +- .../Chat/Group/AddGroupMembersView.swift | 22 ++++++++-- .../Views/Chat/Group/GroupChatInfoView.swift | 27 ++++++------ .../Views/Chat/Group/GroupProfileView.swift | 6 ++- .../Views/ChatList/ChatPreviewView.swift | 4 +- .../Shared/Views/NewChat/AddGroupView.swift | 44 +++++++++++++------ .../Shared/Views/NewChat/NewChatButton.swift | 12 ++--- .../Views/NewChat/PasteToConnectView.swift | 7 ++- .../Views/NewChat/ScanToConnectView.swift | 9 ++-- .../Views/Onboarding/CreateProfile.swift | 4 -- .../Views/Onboarding/MakeConnection.swift | 4 +- 11 files changed, 86 insertions(+), 57 deletions(-) diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 786889c71..0572e03ef 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -73,9 +73,7 @@ struct ChatView: View { } } } - .onTapGesture { - UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) - } + .onTapGesture { hideKeyboard() } } } diff --git a/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift b/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift index 6d4e4653a..bae4f6025 100644 --- a/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift +++ b/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift @@ -14,13 +14,15 @@ struct AddGroupMembersView: View { @Environment(\.dismiss) var dismiss: DismissAction var chat: Chat var groupInfo: GroupInfo - @State var membersToAdd: [Contact] + var membersToAdd: [Contact] + var showSkip: Bool = false + var addedMembersCb: ((Set) -> Void)? = nil @State private var selectedContacts = Set() @State private var selectedRole: GroupMemberRole = .admin var body: some View { NavigationView { - List { + let v = List { ChatInfoToolbar(chat: chat, imageSize: 48) .frame(maxWidth: .infinity, alignment: .center) .listRowBackground(Color.clear) @@ -58,7 +60,20 @@ struct AddGroupMembersView: View { } } } - .navigationBarHidden(true) + + if (showSkip) { + v.toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + if showSkip { + Button ("Skip") { + if let cb = addedMembersCb { cb(selectedContacts) } + } + } + } + } + } else { + v.navigationBarHidden(true) + } } .frame(maxHeight: .infinity, alignment: .top) } @@ -70,6 +85,7 @@ struct AddGroupMembersView: View { await addMember(groupId: chat.chatInfo.apiId, contactId: contactId, memberRole: selectedRole) } await MainActor.run { dismiss() } + if let cb = addedMembersCb { cb(selectedContacts) } } } label: { HStack { diff --git a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift index acfc6567d..57aa33b28 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift @@ -35,19 +35,6 @@ struct GroupChatInfoView: View { groupInfoHeader() .listRowBackground(Color.clear) - if groupInfo.canEdit { - Section { - Button { - showGroupProfile = true - } label: { - Label("Edit group profile", systemImage: "pencil") - } - } - .sheet(isPresented: $showGroupProfile) { - GroupProfileView(groupId: groupInfo.apiId, groupProfile: groupInfo.groupProfile) - } - } - Section("\(members.count + 1) members") { if groupInfo.canAddMembers { addMembersButton() @@ -63,8 +50,14 @@ struct GroupChatInfoView: View { .sheet(item: $selectedMember) { member in GroupMemberInfoView(groupInfo: groupInfo, member: member) } + .sheet(isPresented: $showGroupProfile) { + GroupProfileView(groupId: groupInfo.apiId, groupProfile: groupInfo.groupProfile) + } Section { + if groupInfo.canEdit { + editGroupButton() + } clearChatButton() if groupInfo.canDelete { deleteGroupButton() @@ -156,6 +149,14 @@ struct GroupChatInfoView: View { } } + func editGroupButton() -> some View { + Button { + showGroupProfile = true + } label: { + Label("Edit group profile", systemImage: "pencil") + } + } + func deleteGroupButton() -> some View { Button(role: .destructive) { alert = .deleteGroupAlert diff --git a/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift b/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift index 1548fbe78..eedcf78d9 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift @@ -24,7 +24,7 @@ struct GroupProfileView: View { var body: some View { return VStack(alignment: .leading) { - Text("Group profile is stored on members devices.\nSimpleX servers cannot see group profile.") + Text("Group profile is stored on members' devices, not on the servers.") .padding(.bottom) ZStack(alignment: .center) { @@ -59,7 +59,7 @@ struct GroupProfileView: View { profileNameTextEdit("Group full name (optional)", $groupProfile.fullName) HStack(spacing: 20) { Button("Cancel") { dismiss() } - Button("Save") { saveProfile() } + Button("Save group profile") { saveProfile() } .disabled(groupProfile.displayName == "" || !validDisplayName(groupProfile.displayName)) } } @@ -105,6 +105,8 @@ struct GroupProfileView: View { message: Text("\(saveGroupError ?? "Unexpected error")") ) } + .contentShape(Rectangle()) + .onTapGesture { hideKeyboard() } } func profileNameTextEdit(_ label: String, _ name: Binding) -> some View { diff --git a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift index 9ce4ef17a..486a1824d 100644 --- a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift @@ -73,8 +73,8 @@ struct ChatPreviewView: View { } @ViewBuilder private func groupInactiveIcon() -> some View { - Image(systemName: "minus.circle.fill") - .foregroundColor(.red) + Image(systemName: "multiply.circle.fill") + .foregroundColor(.secondary) .background(Circle().foregroundColor(Color(uiColor: .systemBackground))) } diff --git a/apps/ios/Shared/Views/NewChat/AddGroupView.swift b/apps/ios/Shared/Views/NewChat/AddGroupView.swift index 957c489f4..dab36fd99 100644 --- a/apps/ios/Shared/Views/NewChat/AddGroupView.swift +++ b/apps/ios/Shared/Views/NewChat/AddGroupView.swift @@ -10,8 +10,10 @@ import SwiftUI import SimpleXChat struct AddGroupView: View { - @Binding var openedSheet: NewChatAction? @EnvironmentObject var m: ChatModel + @Environment(\.dismiss) var dismiss: DismissAction + @State private var chat: Chat? + @State private var groupInfo: GroupInfo? @State private var profile = GroupProfile(displayName: "", fullName: "") @FocusState private var focusDisplayName @FocusState private var focusFullName @@ -21,6 +23,22 @@ struct AddGroupView: View { @State private var chosenImage: UIImage? = nil var body: some View { + if let chat = chat, let groupInfo = groupInfo { + AddGroupMembersView(chat: chat, + groupInfo: groupInfo, + membersToAdd: filterMembersToAdd([]), + showSkip: true) { _ in + dismiss() + DispatchQueue.main.async { + m.chatId = groupInfo.id + } + } + } else { + createGroupView() + } + } + + func createGroupView() -> some View { VStack(alignment: .leading) { Text("Create secret group") .font(.largeTitle) @@ -129,14 +147,15 @@ struct AddGroupView: View { func createGroup() { hideKeyboard() do { - let groupInfo = try apiNewGroup(profile) - m.addChat(Chat(chatInfo: .group(groupInfo: groupInfo), chatItems: [])) - openedSheet = nil - DispatchQueue.main.async { - m.chatId = groupInfo.id + let gInfo = try apiNewGroup(profile) + let c = Chat(chatInfo: .group(groupInfo: gInfo), chatItems: []) + m.addChat(c) + withAnimation { + groupInfo = gInfo + chat = c } } catch { - openedSheet = nil + dismiss() AlertManager.shared.showAlert( Alert( title: Text("Error creating group"), @@ -146,18 +165,17 @@ struct AddGroupView: View { } } - func hideKeyboard() { - UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) - } - func canCreateProfile() -> Bool { profile.displayName != "" && validDisplayName(profile.displayName) } } +func hideKeyboard() { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) +} + struct AddGroupView_Previews: PreviewProvider { static var previews: some View { - @State var openedSheet: NewChatAction? = nil - return AddGroupView(openedSheet: $openedSheet) + AddGroupView() } } diff --git a/apps/ios/Shared/Views/NewChat/NewChatButton.swift b/apps/ios/Shared/Views/NewChat/NewChatButton.swift index 74444fa01..e4571cd09 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatButton.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatButton.swift @@ -39,9 +39,9 @@ struct NewChatButton: View { .sheet(item: $actionSheet) { sheet in switch sheet { case .createLink: AddContactView(connReqInvitation: connReq) - case .pasteLink: PasteToConnectView(openedSheet: $actionSheet) - case .scanQRCode: ScanToConnectView(openedSheet: $actionSheet) - case .createGroup: AddGroupView(openedSheet: $actionSheet) + case .pasteLink: PasteToConnectView() + case .scanQRCode: ScanToConnectView() + case .createGroup: AddGroupView() } } } @@ -64,12 +64,12 @@ enum ConnReqType: Equatable { case invitation } -func connectViaLink(_ connectionLink: String, _ openedSheet: Binding? = nil) { +func connectViaLink(_ connectionLink: String, _ dismiss: DismissAction? = nil) { Task { do { let res = try await apiConnect(connReq: connectionLink) DispatchQueue.main.async { - openedSheet?.wrappedValue = nil + dismiss?() if let connReqType = res { connectionReqSentAlert(connReqType) } @@ -77,7 +77,7 @@ func connectViaLink(_ connectionLink: String, _ openedSheet: Binding) { switch resp { case let .success(r): - Task { connectViaLink(r.string, $openedSheet) } + Task { connectViaLink(r.string, dismiss) } case let .failure(e): logger.error("ConnectContactView.processQRCode QR code error: \(e.localizedDescription)") - openedSheet = nil + dismiss() } } } struct ConnectContactView_Previews: PreviewProvider { static var previews: some View { - @State var openedSheet: NewChatAction? = nil - return ScanToConnectView(openedSheet: $openedSheet) + ScanToConnectView() } } diff --git a/apps/ios/Shared/Views/Onboarding/CreateProfile.swift b/apps/ios/Shared/Views/Onboarding/CreateProfile.swift index ef9251515..9810c5d3b 100644 --- a/apps/ios/Shared/Views/Onboarding/CreateProfile.swift +++ b/apps/ios/Shared/Views/Onboarding/CreateProfile.swift @@ -103,10 +103,6 @@ struct CreateProfile: View { } } - func hideKeyboard() { - UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) - } - func canCreateProfile() -> Bool { displayName != "" && validDisplayName(displayName) } diff --git a/apps/ios/Shared/Views/Onboarding/MakeConnection.swift b/apps/ios/Shared/Views/Onboarding/MakeConnection.swift index 9bb45fd0f..d3b46c991 100644 --- a/apps/ios/Shared/Views/Onboarding/MakeConnection.swift +++ b/apps/ios/Shared/Views/Onboarding/MakeConnection.swift @@ -92,8 +92,8 @@ struct MakeConnection: View { .sheet(item: $actionSheet) { sheet in switch sheet { case .createLink: AddContactView(connReqInvitation: connReq) - case .pasteLink: PasteToConnectView(openedSheet: $actionSheet) - case .scanQRCode: ScanToConnectView(openedSheet: $actionSheet) + case .pasteLink: PasteToConnectView() + case .scanQRCode: ScanToConnectView() case .createGroup: EmptyView() // TODO refactor / show during onboarding? } }