diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index 2a4a003608..26da52a4ea 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -952,11 +952,11 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a is CR.ContactConnected -> { if (!r.contact.viaGroupLink) { chatModel.updateContact(r.contact) + chatModel.dismissConnReqView(r.contact.activeConn.id) + chatModel.removeChat(r.contact.activeConn.id) chatModel.updateNetworkStatus(r.contact.id, Chat.NetworkStatus.Connected()) ntfManager.notifyContactConnected(r.contact) } - chatModel.dismissConnReqView(r.contact.activeConn.id) - chatModel.removeChat(r.contact.activeConn.id) } is CR.ContactConnecting -> { if (!r.contact.viaGroupLink) { @@ -1038,8 +1038,13 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a chatModel.addChat(Chat(chatInfo = ChatInfo.Group(r.groupInfo), chatItems = listOf())) // TODO NtfManager.shared.notifyGroupInvitation } - is CR.UserAcceptedGroupSent -> + is CR.UserAcceptedGroupSent -> { chatModel.updateGroup(r.groupInfo) + if (r.hostContact != null) { + chatModel.dismissConnReqView(r.hostContact.activeConn.id) + chatModel.removeChat(r.hostContact.activeConn.id) + } + } is CR.JoinedGroupMemberConnecting -> chatModel.upsertGroupMember(r.groupInfo, r.member) is CR.DeletedMemberUser -> // TODO update user member @@ -1860,7 +1865,7 @@ sealed class CR { // group events @Serializable @SerialName("groupCreated") class GroupCreated(val groupInfo: GroupInfo): CR() @Serializable @SerialName("sentGroupInvitation") class SentGroupInvitation(val groupInfo: GroupInfo, val contact: Contact, val member: GroupMember): CR() - @Serializable @SerialName("userAcceptedGroupSent") class UserAcceptedGroupSent (val groupInfo: GroupInfo): CR() + @Serializable @SerialName("userAcceptedGroupSent") class UserAcceptedGroupSent (val groupInfo: GroupInfo, val hostContact: Contact? = null): CR() @Serializable @SerialName("userDeletedMember") class UserDeletedMember(val groupInfo: GroupInfo, val member: GroupMember): CR() @Serializable @SerialName("leftMemberUser") class LeftMemberUser(val groupInfo: GroupInfo): CR() @Serializable @SerialName("groupMembers") class GroupMembers(val group: Group): CR() diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt index f68287ff92..036289cbd4 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ContactConnectionInfoView.kt @@ -91,7 +91,9 @@ private fun ContactConnectionInfoLayout( } Text( stringResource( - if (contactConnection.viaContactUri) R.string.you_will_be_connected_when_your_connection_request_is_accepted + if (contactConnection.viaContactUri) + if (contactConnection.groupLinkId != null) R.string.you_will_be_connected_when_group_host_device_is_online + else R.string.you_will_be_connected_when_your_connection_request_is_accepted else R.string.you_will_be_connected_when_your_contacts_device_is_online ), Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING) diff --git a/apps/android/app/src/main/res/values-de/strings.xml b/apps/android/app/src/main/res/values-de/strings.xml index d52ed1bcb6..96ad1f5fac 100644 --- a/apps/android/app/src/main/res/values-de/strings.xml +++ b/apps/android/app/src/main/res/values-de/strings.xml @@ -323,6 +323,7 @@ Ungültiger Link! Dieser Link ist kein gültiger Verbindungslink! Verbindungsanfrage gesendet! + ***You will be connected to group when the group host\'s device is online, please wait or check later! Sie werden verbunden, sobald Ihre Verbindungsanfrage akzeptiert wird, bitte warten oder schauen Sie später nochmal nach! Sie werden verbunden, wenn das Gerät Ihres Kontakts online ist, bitte warten oder schauen Sie später nochmal nach! Zeigen Sie Ihrem Kontakt den QR-Code aus der App zum Scannen. diff --git a/apps/android/app/src/main/res/values-ru/strings.xml b/apps/android/app/src/main/res/values-ru/strings.xml index b74bc6c110..f0744b9438 100644 --- a/apps/android/app/src/main/res/values-ru/strings.xml +++ b/apps/android/app/src/main/res/values-ru/strings.xml @@ -323,8 +323,9 @@ Неверная ссылка! Эта ссылка не является ссылкой-приглашением! Запрос на соединение послан! - Соединение будет установлено когда ваш запрос будет принят. Пожалуйста, подождите или проверьте позже! - Соединение будет установлено когда ваш контакт будет онлайн. Пожалуйста, подождите или проверьте позже! + Соединение с группой будет установлено, когда хост группы будет онлайн. Пожалуйста, подождите или проверьте позже! + Соединение будет установлено, когда ваш запрос будет принят. Пожалуйста, подождите или проверьте позже! + Соединение будет установлено, когда ваш контакт будет онлайн. Пожалуйста, подождите или проверьте позже! Ваш контакт может сосканировать QR код в приложении. Если вы не можете встретиться лично, вы можете показать QR код во время видеозвонка или поделиться ссылкой. Ваш профиль будет отправлен\nвашему контакту diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml index 45c8a61a7d..6da6fa7248 100644 --- a/apps/android/app/src/main/res/values/strings.xml +++ b/apps/android/app/src/main/res/values/strings.xml @@ -323,6 +323,7 @@ Invalid link! This link is not a valid connection link! Connection request sent! + You will be connected to group when the group host\'s device is online, please wait or check later! You will be connected when your connection request is accepted, please wait or check later! You will be connected when your contact\'s device is online, please wait or check later! Your contact can scan QR code from the app. diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index ce53f60fbb..fac0a7dc54 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -716,7 +716,7 @@ enum JoinGroupResult { func apiJoinGroup(_ groupId: Int64) async throws -> JoinGroupResult { let r = await chatSendCmd(.apiJoinGroup(groupId: groupId)) switch r { - case let .userAcceptedGroupSent(groupInfo): return .joined(groupInfo: groupInfo) + case let .userAcceptedGroupSent(groupInfo, _): return .joined(groupInfo: groupInfo) case .chatCmdError(.errorAgent(.SMP(.AUTH))): return .invitationRemoved case .chatCmdError(.errorStore(.groupNotFound)): return .groupNotFound default: throw r @@ -893,11 +893,11 @@ func processReceivedMsg(_ res: ChatResponse) async { case let .contactConnected(contact, _): if !contact.viaGroupLink { m.updateContact(contact) + m.dismissConnReqView(contact.activeConn.id) + m.removeChat(contact.activeConn.id) m.updateNetworkStatus(contact.id, .connected) NtfManager.shared.notifyContactConnected(contact) } - m.dismissConnReqView(contact.activeConn.id) - m.removeChat(contact.activeConn.id) case let .contactConnecting(contact): if !contact.viaGroupLink { m.updateContact(contact) @@ -991,8 +991,12 @@ func processReceivedMsg(_ res: ChatResponse) async { chatItems: [] )) // NtfManager.shared.notifyContactRequest(contactRequest) // TODO notifyGroupInvitation? - case let .userAcceptedGroupSent(groupInfo): + case let .userAcceptedGroupSent(groupInfo, hostContact): m.updateGroup(groupInfo) + if let hostContact = hostContact { + m.dismissConnReqView(hostContact.activeConn.id) + m.removeChat(hostContact.activeConn.id) + } case let .joinedGroupMemberConnecting(groupInfo, _, member): _ = m.upsertGroupMember(groupInfo, member) case let .deletedMemberUser(groupInfo, _): // TODO update user member diff --git a/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift b/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift index bf311b1719..72be57b3f7 100644 --- a/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift +++ b/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift @@ -125,7 +125,10 @@ struct ContactConnectionInfo: View { private func contactConnectionText(_ contactConnection: PendingContactConnection) -> LocalizedStringKey { contactConnection.viaContactUri - ? "You will be connected when your connection request is accepted, please wait or check later!" + ? (contactConnection.groupLinkId != nil + ? "You will be connected to group when the group host's device is online, please wait or check later!" + : "You will be connected when your connection request is accepted, please wait or check later!" + ) : "You will be connected when your contact's device is online, please wait or check later!" } } diff --git a/apps/ios/Shared/Views/NewChat/NewChatButton.swift b/apps/ios/Shared/Views/NewChat/NewChatButton.swift index 10da2ce209..ff6b94dffd 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatButton.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatButton.swift @@ -78,6 +78,39 @@ func connectViaLink(_ connectionLink: String, _ dismiss: DismissAction? = nil) { } } +struct CRData: Decodable { + var type: String + var groupLinkId: String? +} + +func parseLinkQueryData(_ connectionLink: String) -> CRData? { + if let hashIndex = connectionLink.firstIndex(of: "#"), + let urlQuery = URL(string: String(connectionLink[connectionLink.index(after: hashIndex)...])), + let components = URLComponents(url: urlQuery, resolvingAgainstBaseURL: false), + let data = components.queryItems?.first(where: { $0.name == "data" })?.value, + let d = data.data(using: .utf8), + let crData = try? getJSONDecoder().decode(CRData.self, from: d) { + return crData + } else { + return nil + } +} + +func checkCRDataGroup(_ crData: CRData) -> Bool { + return crData.type == "group" && crData.groupLinkId != nil +} + +func groupLinkAlert(_ connectionLink: String) -> Alert { + return Alert( + title: Text("Connect via group link?"), + message: Text("You will join a group this link refers to and connect to its group members."), + primaryButton: .default(Text("Connect")) { + connectViaLink(connectionLink) + }, + secondaryButton: .cancel() + ) +} + func connectionReqSentAlert(_ type: ConnReqType) { AlertManager.shared.showAlertMsg( title: "Connection request sent!", diff --git a/apps/ios/Shared/Views/NewChat/PasteToConnectView.swift b/apps/ios/Shared/Views/NewChat/PasteToConnectView.swift index 4be3f91ce0..de390ebad9 100644 --- a/apps/ios/Shared/Views/NewChat/PasteToConnectView.swift +++ b/apps/ios/Shared/Views/NewChat/PasteToConnectView.swift @@ -81,7 +81,14 @@ struct PasteToConnectView: View { } private func connect() { - connectViaLink(connectionLink.trimmingCharacters(in: .whitespaces), dismiss) + let link = connectionLink.trimmingCharacters(in: .whitespaces) + if let crData = parseLinkQueryData(link), + checkCRDataGroup(crData) { + dismiss() + AlertManager.shared.showAlert(groupLinkAlert(link)) + } else { + connectViaLink(link, dismiss) + } } } diff --git a/apps/ios/Shared/Views/NewChat/ScanToConnectView.swift b/apps/ios/Shared/Views/NewChat/ScanToConnectView.swift index 2725faed07..2213dff203 100644 --- a/apps/ios/Shared/Views/NewChat/ScanToConnectView.swift +++ b/apps/ios/Shared/Views/NewChat/ScanToConnectView.swift @@ -52,7 +52,13 @@ struct ScanToConnectView: View { func processQRCode(_ resp: Result) { switch resp { case let .success(r): - Task { connectViaLink(r.string, dismiss) } + if let crData = parseLinkQueryData(r.string), + checkCRDataGroup(crData) { + dismiss() + AlertManager.shared.showAlert(groupLinkAlert(r.string)) + } else { + Task { connectViaLink(r.string, dismiss) } + } case let .failure(e): logger.error("ConnectContactView.processQRCode QR code error: \(e.localizedDescription)") dismiss() diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index a74241b445..f81c7266fc 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -329,7 +329,7 @@ public enum ChatResponse: Decodable, Error { // group events case groupCreated(groupInfo: GroupInfo) case sentGroupInvitation(groupInfo: GroupInfo, contact: Contact, member: GroupMember) - case userAcceptedGroupSent(groupInfo: GroupInfo) + case userAcceptedGroupSent(groupInfo: GroupInfo, hostContact: Contact?) case userDeletedMember(groupInfo: GroupInfo, member: GroupMember) case leftMemberUser(groupInfo: GroupInfo) case groupMembers(group: Group)