diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index e5b231d7e1..fd0234332f 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -24,6 +24,7 @@ final class ChatModel: ObservableObject { @Published var chatId: String? @Published var chatItems: [ChatItem] = [] @Published var chatToTop: String? +// @Published var groups: Dictionary = [:] // items in the terminal view @Published var terminalItems: [TerminalItem] = [] @Published var userAddress: String? @@ -124,6 +125,10 @@ final class ChatModel: ObservableObject { } } +// func addGroup(_ group: SimpleXChat.Group) { +// groups[group.groupInfo.id] = group +// } + func addChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) { // update previews if let i = getChatIndex(cInfo.id) { diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 4223c6dc68..a22fdecad4 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -520,6 +520,12 @@ private func sendCommandOkResp(_ cmd: ChatCommand) async throws { throw r } +func apiNewGroup(_ gp: GroupProfile) throws -> GroupInfo { + let r = chatSendCmdSync(.newGroup(groupProfile: gp)) + if case let .groupCreated(groupInfo) = r { return groupInfo } + throw r +} + func initializeChat(start: Bool) throws { logger.debug("initializeChat") do { diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift index fc82d543b6..3e96348f02 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift @@ -16,6 +16,7 @@ struct ChatInfoView: View { @Binding var showChatInfo: Bool @State var alert: ChatInfoViewAlert? = nil @State var deletingContact: Contact? + var contact: Contact enum ChatInfoViewAlert: Identifiable { case deleteContactAlert @@ -35,53 +36,39 @@ struct ChatInfoView: View { Text(chat.chatInfo.fullName).font(.title) .padding(.bottom) - VStack { - if case let .direct(contact) = chat.chatInfo { - HStack { - serverImage() - Text(chat.serverInfo.networkStatus.statusString) - .foregroundColor(.primary) - } - Text(chat.serverInfo.networkStatus.statusExplanation) - .font(.subheadline) - .multilineTextAlignment(.center) - .padding(.horizontal, 64) - .padding(.vertical, 8) + HStack { + serverImage() + Text(chat.serverInfo.networkStatus.statusString) + .foregroundColor(.primary) + } + Text(chat.serverInfo.networkStatus.statusExplanation) + .font(.subheadline) + .multilineTextAlignment(.center) + .padding(.horizontal, 64) + .padding(.vertical, 8) - Spacer() - Button() { - alert = .clearChatAlert - } label: { - Label("Clear conversation", systemImage: "gobackward") - } - .tint(Color.orange) - Button(role: .destructive) { - deletingContact = contact - alert = .deleteContactAlert - } label: { - Label("Delete contact", systemImage: "trash") - } - .padding() - } - else if case .group = chat.chatInfo { - Spacer() - Button() { - alert = .clearChatAlert - } label: { - Label("Clear conversation", systemImage: "gobackward") - } - .tint(Color.orange) - .padding() - } + Spacer() + Button() { + alert = .clearChatAlert + } label: { + Label("Clear conversation", systemImage: "gobackward") } - .alert(item: $alert) { alertItem in - switch(alertItem) { - case .deleteContactAlert: return deleteContactAlert(deletingContact!) - case .clearChatAlert: return clearChatAlert() - } + .tint(Color.orange) + Button(role: .destructive) { + deletingContact = contact + alert = .deleteContactAlert + } label: { + Label("Delete contact", systemImage: "trash") } - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) + .padding() } + .alert(item: $alert) { alertItem in + switch(alertItem) { + case .deleteContactAlert: return deleteContactAlert(deletingContact!) + case .clearChatAlert: return clearChatAlert() + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) } func serverImage() -> some View { @@ -131,6 +118,6 @@ struct ChatInfoView: View { struct ChatInfoView_Previews: PreviewProvider { static var previews: some View { @State var showChatInfo = true - return ChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []), showChatInfo: $showChatInfo) + return ChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []), showChatInfo: $showChatInfo, contact: Contact.sampleData) } } diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index cdf1dcd53b..1fdb140a0f 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -103,7 +103,11 @@ struct ChatView: View { ChatInfoToolbar(chat: chat) } .sheet(isPresented: $showChatInfo) { - ChatInfoView(chat: chat, showChatInfo: $showChatInfo) + if case let .direct(contact) = chat.chatInfo { + ChatInfoView(chat: chat, showChatInfo: $showChatInfo, contact: contact) + } else if case .group = chat.chatInfo { + GroupChatInfoView(chat: chat, showChatInfo: $showChatInfo) + } } } ToolbarItem(placement: .navigationBarTrailing) { diff --git a/apps/ios/Shared/Views/Chat/GroupChatInfoView.swift b/apps/ios/Shared/Views/Chat/GroupChatInfoView.swift new file mode 100644 index 0000000000..58795fb2a2 --- /dev/null +++ b/apps/ios/Shared/Views/Chat/GroupChatInfoView.swift @@ -0,0 +1,106 @@ +// +// GroupChatInfoView.swift +// SimpleX (iOS) +// +// Created by JRoberts on 14.07.2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct GroupChatInfoView: View { + @EnvironmentObject var chatModel: ChatModel + @ObservedObject var alertManager = AlertManager.shared + @ObservedObject var chat: Chat + @Binding var showChatInfo: Bool + @State var alert: ChatInfoViewAlert? = nil + @State var deletingContact: Contact? + + enum ChatInfoViewAlert: Identifiable { + case deleteContactAlert + case clearChatAlert + + var id: ChatInfoViewAlert { get { self } } + } + + var body: some View { + VStack{ + ChatInfoImage(chat: chat) + .frame(width: 192, height: 192) + .padding(.top, 48) + .padding() + Text(chat.chatInfo.localDisplayName).font(.largeTitle) + .padding(.bottom, 2) + Text(chat.chatInfo.fullName).font(.title) + .padding(.bottom) + + Spacer() + Button() { + alert = .clearChatAlert + } label: { + Label("Clear conversation", systemImage: "gobackward") + } + .tint(Color.orange) + .padding() + } + .alert(item: $alert) { alertItem in + switch(alertItem) { + case .deleteContactAlert: return deleteContactAlert(deletingContact!) + case .clearChatAlert: return clearChatAlert() + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) + } + + func serverImage() -> some View { + let status = chat.serverInfo.networkStatus + return Image(systemName: status.imageName) + .foregroundColor(status == .connected ? .green : .secondary) + } + + private func deleteContactAlert(_ contact: Contact) -> Alert { + Alert( + title: Text("Delete contact?"), + message: Text("Contact and all messages will be deleted - this cannot be undone!"), + primaryButton: .destructive(Text("Delete")) { + Task { + do { + try await apiDeleteChat(type: .direct, id: contact.apiId) + DispatchQueue.main.async { + chatModel.removeChat(contact.id) + showChatInfo = false + } + } catch let error { + logger.error("ChatInfoView.deleteContactAlert apiDeleteChat error: \(error.localizedDescription)") + } + } + }, + secondaryButton: .cancel() + ) + } + + // TODO reuse between this and ChatInfoView + private func clearChatAlert() -> Alert { + Alert( + title: Text("Clear conversation?"), + message: Text("All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you."), + primaryButton: .destructive(Text("Clear")) { + Task { + await clearChat(chat) + DispatchQueue.main.async { + showChatInfo = false + } + } + }, + secondaryButton: .cancel() + ) + } +} + +struct GroupChatInfoView_Previews: PreviewProvider { + static var previews: some View { + @State var showChatInfo = true + return GroupChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []), showChatInfo: $showChatInfo) + } +} diff --git a/apps/ios/Shared/Views/NewChat/AddGroupView.swift b/apps/ios/Shared/Views/NewChat/AddGroupView.swift new file mode 100644 index 0000000000..e571367dc8 --- /dev/null +++ b/apps/ios/Shared/Views/NewChat/AddGroupView.swift @@ -0,0 +1,104 @@ +// +// AddGroupView.swift +// SimpleX (iOS) +// +// Created by JRoberts on 13.07.2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct AddGroupView: View { + @Binding var openedSheet: NewChatAction? + @EnvironmentObject var m: ChatModel + @State private var displayName: String = "" + @State private var fullName: String = "" + @FocusState private var focusDisplayName + @FocusState private var focusFullName + + var body: some View { + VStack(alignment: .leading) { + Text("Create group") + .font(.largeTitle) + .padding(.bottom, 4) + ZStack(alignment: .topLeading) { + if !validDisplayName(displayName) { + Image(systemName: "exclamationmark.circle") + .foregroundColor(.red) + .padding(.top, 4) + } + textField("Display name", text: $displayName) + .focused($focusDisplayName) + .submitLabel(.next) + .onSubmit { + if canCreateProfile() { focusFullName = true } + else { focusDisplayName = true } + } + } + textField("Full name (optional)", text: $fullName) + .focused($focusFullName) + .submitLabel(.go) + .onSubmit { + if canCreateProfile() { createGroup() } + else { focusFullName = true } + } + + Spacer() + + Button { + createGroup() + } label: { + Text("Create") + Image(systemName: "greaterthan") + } + .disabled(!canCreateProfile()) + .frame(maxWidth: .infinity, alignment: .trailing) + } + .onAppear() { + focusDisplayName = true + } + .padding() + } + + func textField(_ placeholder: LocalizedStringKey, text: Binding) -> some View { + TextField(placeholder, text: text) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .padding(.leading, 28) + .padding(.bottom) + } + + func createGroup() { + hideKeyboard() + let groupProfile = GroupProfile( + displayName: displayName, + fullName: fullName + ) + do { + let groupInfo = try apiNewGroup(groupProfile) + m.addChat(Chat(chatInfo: .group(groupInfo: groupInfo), chatItems: [])) + openedSheet = nil + DispatchQueue.main.async { + m.chatId = groupInfo.id + } + } catch { + fatalError("Failed to create group: \(responseError(error))") + } + } + + func hideKeyboard() { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + } + + func canCreateProfile() -> Bool { + displayName != "" && validDisplayName(displayName) + } +} + +struct AddGroupView_Previews: PreviewProvider { + static var previews: some View { + @State var openedSheet: NewChatAction? = nil + return AddGroupView(openedSheet: $openedSheet) + } +} diff --git a/apps/ios/Shared/Views/NewChat/CreateGroupView.swift b/apps/ios/Shared/Views/NewChat/CreateGroupView.swift deleted file mode 100644 index 54f1d6c206..0000000000 --- a/apps/ios/Shared/Views/NewChat/CreateGroupView.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// CreateGroupView.swift -// SimpleX -// -// Created by Evgeny Poberezkin on 29/01/2022. -// Copyright © 2022 SimpleX Chat. All rights reserved. -// - -import SwiftUI - -struct CreateGroupView: View { - var body: some View { - EmptyView() - } -} - -struct CreateGroupView_Previews: PreviewProvider { - static var previews: some View { - CreateGroupView() - } -} diff --git a/apps/ios/Shared/Views/NewChat/NewChatButton.swift b/apps/ios/Shared/Views/NewChat/NewChatButton.swift index 3adf3d4c27..e99ebc391d 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatButton.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatButton.swift @@ -13,6 +13,7 @@ enum NewChatAction: Identifiable { case createLink case pasteLink case scanQRCode + case createGroup var id: NewChatAction { get { self } } } @@ -30,12 +31,14 @@ struct NewChatButton: View { Button("Create link / QR code") { addContactAction() } Button("Paste received link") { actionSheet = .pasteLink } Button("Scan QR code") { actionSheet = .scanQRCode } + // Button("Create group") { actionSheet = .createGroup } } .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) } } } diff --git a/apps/ios/Shared/Views/Onboarding/MakeConnection.swift b/apps/ios/Shared/Views/Onboarding/MakeConnection.swift index 92ae376b9b..9bb45fd0ff 100644 --- a/apps/ios/Shared/Views/Onboarding/MakeConnection.swift +++ b/apps/ios/Shared/Views/Onboarding/MakeConnection.swift @@ -94,6 +94,7 @@ struct MakeConnection: View { case .createLink: AddContactView(connReqInvitation: connReq) case .pasteLink: PasteToConnectView(openedSheet: $actionSheet) case .scanQRCode: ScanToConnectView(openedSheet: $actionSheet) + case .createGroup: EmptyView() // TODO refactor / show during onboarding? } } .onChange(of: actionSheet) { _ in checkOnboarding() } diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index f514b58a87..e0cd431c1b 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -83,7 +83,6 @@ 5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; }; 5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; }; 5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */; }; - 5CCD403A27A5F9BE00368C90 /* CreateGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */; }; 5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CDCAD472818589900503DA2 /* NotificationService.swift */; }; 5CE2BA702845308900EC33A6 /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; }; 5CE2BA712845308900EC33A6 /* SimpleXChat.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -112,6 +111,8 @@ 5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; 5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; 640F50E327CF991C001E05C2 /* SMPServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640F50E227CF991C001E05C2 /* SMPServers.swift */; }; + 6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6442E0B9287F169300CEC0F9 /* AddGroupView.swift */; }; + 6442E0BE2880182D00CEC0F9 /* GroupChatInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6442E0BD2880182D00CEC0F9 /* GroupChatInfoView.swift */; }; 6454036F2822A9750090DDFF /* ComposeFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6454036E2822A9750090DDFF /* ComposeFileView.swift */; }; 646BB38C283BEEB9001CE359 /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 646BB38B283BEEB9001CE359 /* LocalAuthentication.framework */; }; 646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */; }; @@ -260,7 +261,6 @@ 5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; 5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = ""; }; 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = ""; }; - 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGroupView.swift; sourceTree = ""; }; 5CDCAD452818589900503DA2 /* SimpleX NSE.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "SimpleX NSE.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 5CDCAD472818589900503DA2 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; 5CDCAD492818589900503DA2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -285,6 +285,8 @@ 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = ""; }; 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; }; 640F50E227CF991C001E05C2 /* SMPServers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMPServers.swift; sourceTree = ""; }; + 6442E0B9287F169300CEC0F9 /* AddGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupView.swift; sourceTree = ""; }; + 6442E0BD2880182D00CEC0F9 /* GroupChatInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupChatInfoView.swift; sourceTree = ""; }; 6454036E2822A9750090DDFF /* ComposeFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeFileView.swift; sourceTree = ""; }; 646BB38B283BEEB9001CE359 /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.4.sdk/System/Library/Frameworks/LocalAuthentication.framework; sourceTree = DEVELOPER_DIR; }; 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAuthenticationUtils.swift; sourceTree = ""; }; @@ -380,6 +382,7 @@ 5C971E1C27AEBEF600C8A3CE /* ChatInfoView.swift */, 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */, 5CE4407127ADB1D0007B033A /* Emoji.swift */, + 6442E0BD2880182D00CEC0F9 /* GroupChatInfoView.swift */, ); path = Chat; sourceTree = ""; @@ -506,8 +509,8 @@ 5CCD403327A5F6DF00368C90 /* AddContactView.swift */, 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */, 3C8C548828133C84000A3EC7 /* PasteToConnectView.swift */, - 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */, 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */, + 6442E0B9287F169300CEC0F9 /* AddGroupView.swift */, ); path = NewChat; sourceTree = ""; @@ -832,6 +835,7 @@ 5C9D13A3282187BB00AB8B43 /* WebRTC.swift in Sources */, 5C9A5BDB2871E05400A5B906 /* SetNotificationsMode.swift in Sources */, 5CB0BA8E2827126500B3292C /* OnboardingView.swift in Sources */, + 6442E0BE2880182D00CEC0F9 /* GroupChatInfoView.swift in Sources */, 5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */, 5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */, 5CC1C99227A6C7F5000D9FF6 /* QRCode.swift in Sources */, @@ -851,6 +855,7 @@ 5C35CFC827B2782E00FB6C6D /* BGManager.swift in Sources */, 5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */, 5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */, + 6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */, 5C971E2127AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */, 5C55A921283CCCB700C4E99E /* IncomingCallView.swift in Sources */, 6454036F2822A9750090DDFF /* ComposeFileView.swift in Sources */, @@ -863,7 +868,6 @@ 5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */, 649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */, 5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */, - 5CCD403A27A5F9BE00368C90 /* CreateGroupView.swift in Sources */, 5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */, 5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */, 5C971E1D27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */, diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 1e400a766a..96d97241de 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -399,6 +399,12 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat { } public struct GroupProfile: Codable, NamedChat { + public init(displayName: String, fullName: String, image: String? = nil) { + self.displayName = displayName + self.fullName = fullName + self.image = image + } + public var displayName: String public var fullName: String public var image: String?