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"