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 d1e2af892..e08e97329 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 @@ -577,11 +577,26 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager Log.e(TAG, "apiAddMember bad response: ${r.responseType} ${r.details}") } - suspend fun apiJoinGroup(groupId: Long): GroupInfo? { + suspend fun apiJoinGroup(groupId: Long) { val r = sendCmd(CC.ApiJoinGroup(groupId)) - if (r is CR.UserAcceptedGroupSent) return r.groupInfo - Log.e(TAG, "apiJoinGroup bad response: ${r.responseType} ${r.details}") - return null + when (r) { + is CR.UserAcceptedGroupSent -> + chatModel.updateGroup(r.groupInfo) + is CR.ChatCmdError -> { + val e = r.chatError + suspend fun deleteGroup() { if (apiDeleteChat(ChatType.Group, groupId)) { chatModel.removeChat("#$groupId") } } + if (e is ChatError.ChatErrorAgent && e.agentError is AgentErrorType.SMP && e.agentError.smpErr is SMPErrorType.AUTH) { + deleteGroup() + AlertManager.shared.showAlertMsg(generalGetString(R.string.alert_title_group_invitation_expired), generalGetString(R.string.alert_message_group_invitation_expired)) + } else if (e is ChatError.ChatErrorStore && e.storeError is StoreError.GroupNotFound) { + deleteGroup() + AlertManager.shared.showAlertMsg(generalGetString(R.string.alert_title_no_group), generalGetString(R.string.alert_message_no_group)) + } else { + AlertManager.shared.showAlertMsg(generalGetString(R.string.alert_title_join_group_error), "$e") + } + } + else -> Log.e(TAG, "apiJoinGroup bad response: ${r.responseType} ${r.details}") + } } suspend fun apiRemoveMember(groupId: Long, memberId: Long): GroupMember? { @@ -776,13 +791,6 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager } } - suspend fun joinGroup(groupId: Long) { - val groupInfo = apiJoinGroup(groupId) - if (groupInfo != null) { - chatModel.updateGroup(groupInfo) - } - } - suspend fun leaveGroup(groupId: Long) { val groupInfo = apiLeaveGroup(groupId) if (groupInfo != null) { @@ -1507,8 +1515,10 @@ sealed class ChatErrorType { sealed class StoreError { val string: String get() = when (this) { is UserContactLinkNotFound -> "userContactLinkNotFound" + is GroupNotFound -> "groupNotFound" } @Serializable @SerialName("userContactLinkNotFound") class UserContactLinkNotFound: StoreError() + @Serializable @SerialName("groupNotFound") class GroupNotFound: StoreError() } @Serializable diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt index 8e214e239..30e45a4ed 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt @@ -140,7 +140,7 @@ fun ChatView(chatModel: ChatModel) { withApi { chatModel.controller.receiveFile(fileId) } }, joinGroup = { groupId -> - withApi { chatModel.controller.joinGroup(groupId) } + withApi { chatModel.controller.apiJoinGroup(groupId) } }, startCall = { media -> val cInfo = chat.chatInfo diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt index 5d60090f4..c92f40407 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt @@ -193,7 +193,7 @@ fun JoinGroupAction(groupInfo: GroupInfo, chatModel: ChatModel, showMenu: Mutabl stringResource(R.string.join_group_button), Icons.Outlined.Login, onClick = { - withApi { chatModel.controller.joinGroup(groupInfo.groupId) } + withApi { chatModel.controller.apiJoinGroup(groupInfo.groupId) } showMenu.value = false } ) @@ -359,7 +359,7 @@ fun acceptGroupInvitationAlertDialog(groupInfo: GroupInfo, chatModel: ChatModel) title = generalGetString(R.string.join_group_question), text = generalGetString(R.string.you_are_invited_to_group_join_to_connect_with_group_members), confirmText = generalGetString(R.string.join_group_button), - onConfirm = { withApi { chatModel.controller.joinGroup(groupInfo.groupId) } }, + onConfirm = { withApi { chatModel.controller.apiJoinGroup(groupInfo.groupId) } }, dismissText = generalGetString(R.string.delete_verb), onDismiss = { deleteGroup(groupInfo, chatModel) } ) 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 189bb8e17..f98fc9066 100644 --- a/apps/android/app/src/main/res/values-ru/strings.xml +++ b/apps/android/app/src/main/res/values-ru/strings.xml @@ -511,6 +511,11 @@ Вы перестанете получать сообщения от этой группы. История чата будет сохранена. Пригласить участников Группа неактивна + Приглашение истекло! + Приглашение в группу больше не действительно, оно было удалено отправителем. + Группа не найдена! + Эта группа больше не существует. + Ошибка приглашения Вы отправили приглашение в группу diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml index 98d7e9efa..6c8be15b2 100644 --- a/apps/android/app/src/main/res/values/strings.xml +++ b/apps/android/app/src/main/res/values/strings.xml @@ -513,6 +513,11 @@ You will stop receiving messages from this group. Chat history will be preserved. Invite members Group inactive + Invitation expired! + Group invitation is no longer valid, it was removed by sender. + Group not found! + This group no longer exists. + Error joining group You sent group invitation diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index c01608411..99d93372a 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -579,19 +579,20 @@ func apiAddMember(groupId: Int64, contactId: Int64, memberRole: GroupMemberRole) throw r } -func joinGroup(_ groupId: Int64) async { - do { - let groupInfo = try await apiJoinGroup(groupId) - DispatchQueue.main.async { ChatModel.shared.updateGroup(groupInfo) } - } catch let error { - logger.error("joinGroup error: \(responseError(error))") - } +enum JoinGroupResult { + case joined(groupInfo: GroupInfo) + case invitationRemoved + case groupNotFound } -func apiJoinGroup(_ groupId: Int64) async throws -> GroupInfo { +func apiJoinGroup(_ groupId: Int64) async throws -> JoinGroupResult { let r = await chatSendCmd(.apiJoinGroup(groupId: groupId)) - if case let .userAcceptedGroupSent(groupInfo) = r { return groupInfo } - throw r + switch r { + case let .userAcceptedGroupSent(groupInfo): return .joined(groupInfo: groupInfo) + case .chatCmdError(.errorAgent(.SMP(.AUTH))): return .invitationRemoved + case .chatCmdError(.errorStore(.groupNotFound)): return .groupNotFound + default: throw r + } } func apiRemoveMember(groupId: Int64, memberId: Int64) async throws -> GroupMember { diff --git a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift index 71ed4453a..ecf978e67 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift @@ -331,6 +331,36 @@ struct ChatListNavLink: View { } } +func joinGroup(_ groupId: Int64) async { + do { + let r = try await apiJoinGroup(groupId) + switch r { + case let .joined(groupInfo): + await MainActor.run { ChatModel.shared.updateGroup(groupInfo) } + case .invitationRemoved: + AlertManager.shared.showAlertMsg(title: "Invitation expired!", message: "Group invitation is no longer valid, it was removed by sender.") + await deleteGroup() + case .groupNotFound: + AlertManager.shared.showAlertMsg(title: "No group!", message: "This group no longer exists.") + await deleteGroup() + } + } catch let error { + let err = responseError(error) + AlertManager.shared.showAlert(Alert(title: Text("Error joining group"), message: Text(err))) + logger.error("apiJoinGroup error: \(err)") + } + + func deleteGroup() async { + do { + // TODO this API should update chat item with the invitation as well + try await apiDeleteChat(type: .group, id: groupId) + await MainActor.run { ChatModel.shared.removeChat("#\(groupId)") } + } catch { + logger.error("apiDeleteChat error: \(responseError(error))") + } + } +} + struct ChatListNavLink_Previews: PreviewProvider { static var previews: some View { @State var chatId: String? = "@1"