diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt index fad7fc0d4..4ec82b7f1 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt @@ -248,6 +248,22 @@ class ChatModel(val controller: ChatController) { fun removeChat(id: String) { chats.removeAll { it.id == id } } + + fun upsertGroupMember(groupInfo: GroupInfo, member: GroupMember): Boolean { + // update current chat + return if (chatId.value == groupInfo.id) { + val memberIndex = groupMembers.indexOfFirst { it.id == member.id } + if (memberIndex >= 0) { + groupMembers[memberIndex] = member + false + } else { + groupMembers.add(member) + true + } + } else { + false + } + } } enum class ChatType(val type: String) { 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 e3ee2d55f..b3e2a681f 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 @@ -605,6 +605,13 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager Log.e(TAG, "apiAddMember bad response: ${r.responseType} ${r.details}") } +// suspend fun apiAddMember(groupId: Long, contactId: Long, memberRole: GroupMemberRole): GroupMember? { +// val r = sendCmd(CC.ApiAddMember(groupId, contactId, memberRole)) +// if (r is CR.SentGroupInvitation) return r.member +// Log.e(TAG, "apiAddMember bad response: ${r.responseType} ${r.details}") +// return null +// } + suspend fun apiJoinGroup(groupId: Long) { val r = sendCmd(CC.ApiJoinGroup(groupId)) when (r) { @@ -756,12 +763,22 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager chatModel.addChat(Chat(chatInfo = ChatInfo.Group(r.groupInfo), chatItems = listOf())) // TODO NtfManager.shared.notifyGroupInvitation } + is CR.JoinedGroupMemberConnecting -> + chatModel.upsertGroupMember(r.groupInfo, r.member) + is CR.DeletedMemberUser -> // TODO update user member + chatModel.updateGroup(r.groupInfo) + is CR.DeletedMember -> + chatModel.upsertGroupMember(r.groupInfo, r.deletedMember) + is CR.LeftMember -> + chatModel.upsertGroupMember(r.groupInfo, r.member) + is CR.GroupDeleted -> // TODO update user member + chatModel.updateGroup(r.groupInfo) is CR.UserJoinedGroup -> chatModel.updateGroup(r.groupInfo) - is CR.GroupDeleted -> - chatModel.updateGroup(r.groupInfo) - is CR.DeletedMemberUser -> - chatModel.updateGroup(r.groupInfo) + is CR.JoinedGroupMember -> + chatModel.upsertGroupMember(r.groupInfo, r.member) + is CR.ConnectedToGroupMember -> + chatModel.upsertGroupMember(r.groupInfo, r.member) is CR.GroupUpdated -> chatModel.updateGroup(r.toGroup) is CR.RcvFileStart -> @@ -1391,6 +1408,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): 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("userDeletedMember") class UserDeletedMember(val groupInfo: GroupInfo, val member: GroupMember): CR() @Serializable @SerialName("leftMemberUser") class LeftMemberUser(val groupInfo: GroupInfo): CR() @@ -1403,11 +1421,11 @@ sealed class CR { @Serializable @SerialName("leftMember") class LeftMember(val groupInfo: GroupInfo, val member: GroupMember): CR() @Serializable @SerialName("groupDeleted") class GroupDeleted(val groupInfo: GroupInfo, val member: GroupMember): CR() @Serializable @SerialName("contactsMerged") class ContactsMerged(val intoContact: Contact, val mergedContact: Contact): CR() - @Serializable @SerialName("groupInvitation") class GroupInvitation(val groupInfo: GroupInfo): CR() + @Serializable @SerialName("groupInvitation") class GroupInvitation(val groupInfo: GroupInfo): CR() // unused @Serializable @SerialName("userJoinedGroup") class UserJoinedGroup(val groupInfo: GroupInfo): CR() @Serializable @SerialName("joinedGroupMember") class JoinedGroupMember(val groupInfo: GroupInfo, val member: GroupMember): CR() @Serializable @SerialName("connectedToGroupMember") class ConnectedToGroupMember(val groupInfo: GroupInfo, val member: GroupMember): CR() - @Serializable @SerialName("groupRemoved") class GroupRemoved(val groupInfo: GroupInfo): CR() + @Serializable @SerialName("groupRemoved") class GroupRemoved(val groupInfo: GroupInfo): CR() // unused @Serializable @SerialName("groupUpdated") class GroupUpdated(val toGroup: GroupInfo): CR() // receiving file events @Serializable @SerialName("rcvFileAccepted") class RcvFileAccepted(val chatItem: AChatItem): CR() diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt index 0411843d2..57dc06f4c 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt @@ -42,6 +42,7 @@ fun AddGroupMembersView(groupInfo: GroupInfo, chatModel: ChatModel, close: () -> withApi { selectedContacts.forEach { chatModel.controller.apiAddMember(groupInfo.groupId, it, selectedRole.value) + // chatModel.upsertGroupMember(groupInfo, member) } close.invoke() } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt index 1f303e63d..6a0209cc1 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt @@ -37,19 +37,22 @@ fun GroupMemberInfoView(groupInfo: GroupInfo, member: GroupMember, connStats: Co member, connStats, developerTools, - removeMember = { removeMemberDialog(member, chatModel, close) } + removeMember = { removeMemberDialog(groupInfo, member, chatModel, close) } ) } } -fun removeMemberDialog(member: GroupMember, chatModel: ChatModel, close: (() -> Unit)? = null) { +fun removeMemberDialog(groupInfo: GroupInfo, member: GroupMember, chatModel: ChatModel, close: (() -> Unit)? = null) { AlertManager.shared.showAlertMsg( title = generalGetString(R.string.button_remove_member), text = generalGetString(R.string.member_will_be_removed_from_group_cannot_be_undone), confirmText = generalGetString(R.string.remove_member_confirmation), onConfirm = { withApi { - chatModel.controller.apiRemoveMember(member.groupId, member.groupMemberId) + val removedMember = chatModel.controller.apiRemoveMember(member.groupId, member.groupMemberId) + if (removedMember != null) { + chatModel.upsertGroupMember(groupInfo, removedMember) + } close?.invoke() } } diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index e1987c3f2..b0cecea3e 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -309,17 +309,6 @@ final class ChatModel: ObservableObject { return false } } - - func removeGroupMember(_ groupInfo: GroupInfo, _ member: GroupMember) { - // remove from current chat - if chatId == groupInfo.id { - if let i = groupMembers.firstIndex(where: { $0.id == member.id }) { - _ = withAnimation { - self.groupMembers.remove(at: i) - } - } - } - } } final class Chat: ObservableObject, Identifiable { diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 0df0b5104..fd114528a 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -823,9 +823,9 @@ func processReceivedMsg(_ res: ChatResponse) async { case let .deletedMemberUser(groupInfo, _): // TODO update user member m.updateGroup(groupInfo) case let .deletedMember(groupInfo, _, deletedMember): - m.removeGroupMember(groupInfo, deletedMember) + _ = m.upsertGroupMember(groupInfo, deletedMember) case let .leftMember(groupInfo, member): - m.removeGroupMember(groupInfo, member) + _ = m.upsertGroupMember(groupInfo, member) case let .groupDeleted(groupInfo, _): // TODO update user member m.updateGroup(groupInfo) case let .userJoinedGroup(groupInfo): diff --git a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift index 7ce57c464..a34f5dc72 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift @@ -109,7 +109,7 @@ struct GroupMemberInfoView: View { do { let member = try await apiRemoveMember(groupInfo.groupId, member.groupMemberId) await MainActor.run { - ChatModel.shared.removeGroupMember(groupInfo, member) + _ = ChatModel.shared.upsertGroupMember(groupInfo, member) dismiss() } } catch let error {