diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 1d50e94f11..6d2897338f 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -431,15 +431,15 @@ func apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, re throw r } -func apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode) async throws -> (ChatItem, ChatItem?) { - let r = await chatSendCmd(.apiDeleteChatItem(type: type, id: id, itemId: itemId, mode: mode), bgDelay: msgDelay) - if case let .chatItemDeleted(_, deletedChatItem, toChatItem, _) = r { return (deletedChatItem.chatItem, toChatItem?.chatItem) } +func apiDeleteChatItems(type: ChatType, id: Int64, itemIds: [Int64], mode: CIDeleteMode) async throws -> [ChatItemDeletion] { + let r = await chatSendCmd(.apiDeleteChatItem(type: type, id: id, itemIds: itemIds, mode: mode), bgDelay: msgDelay) + if case let .chatItemsDeleted(_, items, _) = r { return items } throw r } -func apiDeleteMemberChatItem(groupId: Int64, groupMemberId: Int64, itemId: Int64) async throws -> (ChatItem, ChatItem?) { - let r = await chatSendCmd(.apiDeleteMemberChatItem(groupId: groupId, groupMemberId: groupMemberId, itemId: itemId), bgDelay: msgDelay) - if case let .chatItemDeleted(_, deletedChatItem, toChatItem, _) = r { return (deletedChatItem.chatItem, toChatItem?.chatItem) } +func apiDeleteMemberChatItems(groupId: Int64, itemIds: [Int64]) async throws -> [ChatItemDeletion] { + let r = await chatSendCmd(.apiDeleteMemberChatItem(groupId: groupId, itemIds: itemIds), bgDelay: msgDelay) + if case let .chatItemsDeleted(_, items, _) = r { return items } throw r } @@ -1746,21 +1746,25 @@ func processReceivedMsg(_ res: ChatResponse) async { m.updateChatItem(r.chatInfo, r.chatReaction.chatItem) } } - case let .chatItemDeleted(user, deletedChatItem, toChatItem, _): + case let .chatItemsDeleted(user, items, _): if !active(user) { - if toChatItem == nil && deletedChatItem.chatItem.isRcvNew && deletedChatItem.chatInfo.ntfsEnabled { - await MainActor.run { - m.decreaseUnreadCounter(user: user) + for item in items { + if item.toChatItem == nil && item.deletedChatItem.chatItem.isRcvNew && item.deletedChatItem.chatInfo.ntfsEnabled { + await MainActor.run { + m.decreaseUnreadCounter(user: user) + } } } return } await MainActor.run { - if let toChatItem = toChatItem { - _ = m.upsertChatItem(toChatItem.chatInfo, toChatItem.chatItem) - } else { - m.removeChatItem(deletedChatItem.chatInfo, deletedChatItem.chatItem) + for item in items { + if let toChatItem = item.toChatItem { + _ = m.upsertChatItem(toChatItem.chatInfo, toChatItem.chatItem) + } else { + m.removeChatItem(item.deletedChatItem.chatInfo, item.deletedChatItem.chatItem) + } } } case let .receivedGroupInvitation(user, groupInfo, _, _): diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 9887865233..5e2825d30c 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -1265,24 +1265,20 @@ struct ChatView: View { if itemIds.count > 0 { let chatInfo = chat.chatInfo Task { - var deletedItems: [ChatItem] = [] - for itemId in itemIds { - do { - let (di, _) = try await apiDeleteChatItem( - type: chatInfo.chatType, - id: chatInfo.apiId, - itemId: itemId, - mode: .cidmInternal - ) - deletedItems.append(di) - } catch { - logger.error("ChatView.deleteMessage error: \(error.localizedDescription)") - } - } - await MainActor.run { - for di in deletedItems { - m.removeChatItem(chatInfo, di) + do { + let deletedItems = try await apiDeleteChatItems( + type: chatInfo.chatType, + id: chatInfo.apiId, + itemIds: itemIds, + mode: .cidmInternal + ) + await MainActor.run { + for di in deletedItems { + m.removeChatItem(chatInfo, di.deletedChatItem.chatItem) + } } + } catch { + logger.error("ChatView.deleteMessage error: \(error.localizedDescription)") } } } @@ -1294,29 +1290,28 @@ struct ChatView: View { logger.debug("ChatView deleteMessage: in Task") do { if let di = deletingItem { - var deletedItem: ChatItem - var toItem: ChatItem? - if case .cidmBroadcast = mode, + let r = if case .cidmBroadcast = mode, let (groupInfo, groupMember) = di.memberToModerate(chat.chatInfo) { - (deletedItem, toItem) = try await apiDeleteMemberChatItem( + try await apiDeleteMemberChatItems( groupId: groupInfo.apiId, - groupMemberId: groupMember.groupMemberId, - itemId: di.id + itemIds: [di.id] ) } else { - (deletedItem, toItem) = try await apiDeleteChatItem( + try await apiDeleteChatItems( type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, - itemId: di.id, + itemIds: [di.id], mode: mode ) } - DispatchQueue.main.async { - deletingItem = nil - if let toItem = toItem { - _ = m.upsertChatItem(chat.chatInfo, toItem) - } else { - m.removeChatItem(chat.chatInfo, deletedItem) + if let itemDeletion = r.first { + await MainActor.run { + deletingItem = nil + if let toItem = itemDeletion.toChatItem { + _ = m.upsertChatItem(chat.chatInfo, toItem.chatItem) + } else { + m.removeChatItem(chat.chatInfo, itemDeletion.deletedChatItem.chatItem) + } } } } diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 4a45892ee5..df95b5ebb6 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -46,8 +46,8 @@ public enum ChatCommand { case apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool, ttl: Int?) case apiCreateChatItem(noteFolderId: Int64, file: CryptoFile?, msg: MsgContent) case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent, live: Bool) - case apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode) - case apiDeleteMemberChatItem(groupId: Int64, groupMemberId: Int64, itemId: Int64) + case apiDeleteChatItem(type: ChatType, id: Int64, itemIds: [Int64], mode: CIDeleteMode) + case apiDeleteMemberChatItem(groupId: Int64, itemIds: [Int64]) case apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction) case apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64, ttl: Int?) case apiGetNtfToken @@ -199,8 +199,8 @@ public enum ChatCommand { let msg = encodeJSON(ComposedMessage(fileSource: file, msgContent: mc)) return "/_create *\(noteFolderId) json \(msg)" case let .apiUpdateChatItem(type, id, itemId, mc, live): return "/_update item \(ref(type, id)) \(itemId) live=\(onOff(live)) \(mc.cmdString)" - case let .apiDeleteChatItem(type, id, itemId, mode): return "/_delete item \(ref(type, id)) \(itemId) \(mode.rawValue)" - case let .apiDeleteMemberChatItem(groupId, groupMemberId, itemId): return "/_delete member item #\(groupId) \(groupMemberId) \(itemId)" + case let .apiDeleteChatItem(type, id, itemIds, mode): return "/_delete item \(ref(type, id)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) \(mode.rawValue)" + case let .apiDeleteMemberChatItem(groupId, itemIds): return "/_delete member item #\(groupId) \(itemIds.map({ "\($0)" }).joined(separator: ","))" case let .apiChatItemReaction(type, id, itemId, add, reaction): return "/_reaction \(ref(type, id)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))" case let .apiForwardChatItem(toChatType, toChatId, fromChatType, fromChatId, itemId, ttl): let ttlStr = ttl != nil ? "\(ttl!)" : "default" @@ -598,7 +598,7 @@ public enum ChatResponse: Decodable, Error { case chatItemUpdated(user: UserRef, chatItem: AChatItem) case chatItemNotChanged(user: UserRef, chatItem: AChatItem) case chatItemReaction(user: UserRef, added: Bool, reaction: ACIReaction) - case chatItemDeleted(user: UserRef, deletedChatItem: AChatItem, toChatItem: AChatItem?, byUser: Bool) + case chatItemsDeleted(user: UserRef, chatItemDeletions: [ChatItemDeletion], byUser: Bool) case contactsList(user: UserRef, contacts: [Contact]) // group events case groupCreated(user: UserRef, groupInfo: GroupInfo) @@ -767,7 +767,7 @@ public enum ChatResponse: Decodable, Error { case .chatItemUpdated: return "chatItemUpdated" case .chatItemNotChanged: return "chatItemNotChanged" case .chatItemReaction: return "chatItemReaction" - case .chatItemDeleted: return "chatItemDeleted" + case .chatItemsDeleted: return "chatItemsDeleted" case .contactsList: return "contactsList" case .groupCreated: return "groupCreated" case .sentGroupInvitation: return "sentGroupInvitation" @@ -934,7 +934,10 @@ public enum ChatResponse: Decodable, Error { case let .chatItemUpdated(u, chatItem): return withUser(u, String(describing: chatItem)) case let .chatItemNotChanged(u, chatItem): return withUser(u, String(describing: chatItem)) case let .chatItemReaction(u, added, reaction): return withUser(u, "added: \(added)\n\(String(describing: reaction))") - case let .chatItemDeleted(u, deletedChatItem, toChatItem, byUser): return withUser(u, "deletedChatItem:\n\(String(describing: deletedChatItem))\ntoChatItem:\n\(String(describing: toChatItem))\nbyUser: \(byUser)") + case let .chatItemsDeleted(u, items, byUser): + let itemsString = items.map { item in + "deletedChatItem:\n\(String(describing: item.deletedChatItem))\ntoChatItem:\n\(String(describing: item.toChatItem))" }.joined(separator: "\n") + return withUser(u, itemsString + "\nbyUser: \(byUser)") case let .contactsList(u, contacts): return withUser(u, String(describing: contacts)) case let .groupCreated(u, groupInfo): return withUser(u, String(describing: groupInfo)) case let .sentGroupInvitation(u, groupInfo, contact, member): return withUser(u, "groupInfo: \(groupInfo)\ncontact: \(contact)\nmember: \(member)") diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 54e8a80332..d173f2fad8 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -2234,6 +2234,11 @@ public struct NtfMsgInfo: Decodable, Hashable { public var msgTs: Date } +public struct ChatItemDeletion: Decodable, Hashable { + public var deletedChatItem: AChatItem + public var toChatItem: AChatItem? = nil +} + public struct AChatItem: Decodable, Hashable { public var chatInfo: ChatInfo public var chatItem: ChatItem diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 025f722734..16a61da6b8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -1760,6 +1760,12 @@ enum class ConnStatus { } } +@Serializable +data class ChatItemDeletion ( + val deletedChatItem: AChatItem, + val toChatItem: AChatItem? = null +) + @Serializable class AChatItem ( val chatInfo: ChatInfo, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 37945729dd..87bf26aea5 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -885,16 +885,16 @@ object ChatController { return null } - suspend fun apiDeleteChatItem(rh: Long?, type: ChatType, id: Long, itemId: Long, mode: CIDeleteMode): CR.ChatItemDeleted? { - val r = sendCmd(rh, CC.ApiDeleteChatItem(type, id, itemId, mode)) - if (r is CR.ChatItemDeleted) return r + suspend fun apiDeleteChatItems(rh: Long?, type: ChatType, id: Long, itemIds: List, mode: CIDeleteMode): List? { + val r = sendCmd(rh, CC.ApiDeleteChatItem(type, id, itemIds, mode)) + if (r is CR.ChatItemsDeleted) return r.chatItemDeletions Log.e(TAG, "apiDeleteChatItem bad response: ${r.responseType} ${r.details}") return null } - suspend fun apiDeleteMemberChatItem(rh: Long?, groupId: Long, groupMemberId: Long, itemId: Long): Pair? { - val r = sendCmd(rh, CC.ApiDeleteMemberChatItem(groupId, groupMemberId, itemId)) - if (r is CR.ChatItemDeleted) return r.deletedChatItem.chatItem to r.toChatItem?.chatItem + suspend fun apiDeleteMemberChatItems(rh: Long?, groupId: Long, itemIds: List): List? { + val r = sendCmd(rh, CC.ApiDeleteMemberChatItem(groupId, itemIds)) + if (r is CR.ChatItemsDeleted) return r.chatItemDeletions Log.e(TAG, "apiDeleteMemberChatItem bad response: ${r.responseType} ${r.details}") return null } @@ -2104,31 +2104,34 @@ object ChatController { chatModel.updateChatItem(r.reaction.chatInfo, r.reaction.chatReaction.chatItem) } } - is CR.ChatItemDeleted -> { + is CR.ChatItemsDeleted -> { if (!active(r.user)) { - if (r.toChatItem == null && r.deletedChatItem.chatItem.isRcvNew && r.deletedChatItem.chatInfo.ntfsEnabled) { - chatModel.decreaseUnreadCounter(rhId, r.user) + r.chatItemDeletions.forEach { (deletedChatItem, toChatItem) -> + if (toChatItem == null && deletedChatItem.chatItem.isRcvNew && deletedChatItem.chatInfo.ntfsEnabled) { + chatModel.decreaseUnreadCounter(rhId, r.user) + } } return } - - val cInfo = r.deletedChatItem.chatInfo - val cItem = r.deletedChatItem.chatItem - AudioPlayer.stop(cItem) - val isLastChatItem = chatModel.getChat(cInfo.id)?.chatItems?.lastOrNull()?.id == cItem.id - if (isLastChatItem && ntfManager.hasNotificationsForChat(cInfo.id)) { - ntfManager.cancelNotificationsForChat(cInfo.id) - ntfManager.displayNotification( - r.user, - cInfo.id, - cInfo.displayName, - generalGetString(if (r.toChatItem != null) MR.strings.marked_deleted_description else MR.strings.deleted_description) - ) - } - if (r.toChatItem == null) { - chatModel.removeChatItem(rhId, cInfo, cItem) - } else { - chatModel.upsertChatItem(rhId, cInfo, r.toChatItem.chatItem) + r.chatItemDeletions.forEach { (deletedChatItem, toChatItem) -> + val cInfo = deletedChatItem.chatInfo + val cItem = deletedChatItem.chatItem + AudioPlayer.stop(cItem) + val isLastChatItem = chatModel.getChat(cInfo.id)?.chatItems?.lastOrNull()?.id == cItem.id + if (isLastChatItem && ntfManager.hasNotificationsForChat(cInfo.id)) { + ntfManager.cancelNotificationsForChat(cInfo.id) + ntfManager.displayNotification( + r.user, + cInfo.id, + cInfo.displayName, + generalGetString(if (toChatItem != null) MR.strings.marked_deleted_description else MR.strings.deleted_description) + ) + } + if (toChatItem == null) { + chatModel.removeChatItem(rhId, cInfo, cItem) + } else { + chatModel.upsertChatItem(rhId, cInfo, toChatItem.chatItem) + } } } is CR.ReceivedGroupInvitation -> { @@ -2737,8 +2740,8 @@ sealed class CC { class ApiSendMessage(val type: ChatType, val id: Long, val file: CryptoFile?, val quotedItemId: Long?, val mc: MsgContent, val live: Boolean, val ttl: Int?): CC() class ApiCreateChatItem(val noteFolderId: Long, val file: CryptoFile?, val mc: MsgContent): CC() class ApiUpdateChatItem(val type: ChatType, val id: Long, val itemId: Long, val mc: MsgContent, val live: Boolean): CC() - class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemId: Long, val mode: CIDeleteMode): CC() - class ApiDeleteMemberChatItem(val groupId: Long, val groupMemberId: Long, val itemId: Long): CC() + class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemIds: List, val mode: CIDeleteMode): CC() + class ApiDeleteMemberChatItem(val groupId: Long, val itemIds: List): CC() class ApiChatItemReaction(val type: ChatType, val id: Long, val itemId: Long, val add: Boolean, val reaction: MsgReaction): CC() class ApiForwardChatItem(val toChatType: ChatType, val toChatId: Long, val fromChatType: ChatType, val fromChatId: Long, val itemId: Long, val ttl: Int?): CC() class ApiNewGroup(val userId: Long, val incognito: Boolean, val groupProfile: GroupProfile): CC() @@ -2887,8 +2890,8 @@ sealed class CC { "/_create *$noteFolderId json ${json.encodeToString(ComposedMessage(file, null, mc))}" } is ApiUpdateChatItem -> "/_update item ${chatRef(type, id)} $itemId live=${onOff(live)} ${mc.cmdString}" - is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id)} $itemId ${mode.deleteMode}" - is ApiDeleteMemberChatItem -> "/_delete member item #$groupId $groupMemberId $itemId" + is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id)} ${itemIds.joinToString(",")} ${mode.deleteMode}" + is ApiDeleteMemberChatItem -> "/_delete member item #$groupId ${itemIds.joinToString(",")}" is ApiChatItemReaction -> "/_reaction ${chatRef(type, id)} $itemId ${onOff(add)} ${json.encodeToString(reaction)}" is ApiForwardChatItem -> { val ttlStr = if (ttl != null) "$ttl" else "default" @@ -4679,7 +4682,7 @@ sealed class CR { @Serializable @SerialName("chatItemUpdated") class ChatItemUpdated(val user: UserRef, val chatItem: AChatItem): CR() @Serializable @SerialName("chatItemNotChanged") class ChatItemNotChanged(val user: UserRef, val chatItem: AChatItem): CR() @Serializable @SerialName("chatItemReaction") class ChatItemReaction(val user: UserRef, val added: Boolean, val reaction: ACIReaction): CR() - @Serializable @SerialName("chatItemDeleted") class ChatItemDeleted(val user: UserRef, val deletedChatItem: AChatItem, val toChatItem: AChatItem? = null, val byUser: Boolean): CR() + @Serializable @SerialName("chatItemsDeleted") class ChatItemsDeleted(val user: UserRef, val chatItemDeletions: List, val byUser: Boolean): CR() // group events @Serializable @SerialName("groupCreated") class GroupCreated(val user: UserRef, val groupInfo: GroupInfo): CR() @Serializable @SerialName("sentGroupInvitation") class SentGroupInvitation(val user: UserRef, val groupInfo: GroupInfo, val contact: Contact, val member: GroupMember): CR() @@ -4854,7 +4857,7 @@ sealed class CR { is ChatItemUpdated -> "chatItemUpdated" is ChatItemNotChanged -> "chatItemNotChanged" is ChatItemReaction -> "chatItemReaction" - is ChatItemDeleted -> "chatItemDeleted" + is ChatItemsDeleted -> "chatItemsDeleted" is GroupCreated -> "groupCreated" is SentGroupInvitation -> "sentGroupInvitation" is UserAcceptedGroupSent -> "userAcceptedGroupSent" @@ -5021,7 +5024,7 @@ sealed class CR { is ChatItemUpdated -> withUser(user, json.encodeToString(chatItem)) is ChatItemNotChanged -> withUser(user, json.encodeToString(chatItem)) is ChatItemReaction -> withUser(user, "added: $added\n${json.encodeToString(reaction)}") - is ChatItemDeleted -> withUser(user, "deletedChatItem:\n${json.encodeToString(deletedChatItem)}\ntoChatItem:\n${json.encodeToString(toChatItem)}\nbyUser: $byUser") + is ChatItemsDeleted -> withUser(user, "${chatItemDeletions.map { (deletedChatItem, toChatItem) -> "deletedChatItem: ${json.encodeToString(deletedChatItem)}\ntoChatItem: ${json.encodeToString(toChatItem)}" }} \nbyUser: $byUser") is GroupCreated -> withUser(user, json.encodeToString(groupInfo)) is SentGroupInvitation -> withUser(user, "groupInfo: $groupInfo\ncontact: $contact\nmember: $member") is UserAcceptedGroupSent -> json.encodeToString(groupInfo) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index ce91834838..d293bfe1fb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -249,30 +249,30 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId: val groupMember = toModerate?.second val deletedChatItem: ChatItem? val toChatItem: ChatItem? - if (mode == CIDeleteMode.cidmBroadcast && groupInfo != null && groupMember != null) { - val r = chatModel.controller.apiDeleteMemberChatItem( + val r = if (mode == CIDeleteMode.cidmBroadcast && groupInfo != null && groupMember != null) { + chatModel.controller.apiDeleteMemberChatItems( chatRh, groupId = groupInfo.groupId, - groupMemberId = groupMember.groupMemberId, - itemId = itemId + itemIds = listOf(itemId) ) - deletedChatItem = r?.first - toChatItem = r?.second } else { - val r = chatModel.controller.apiDeleteChatItem( + chatModel.controller.apiDeleteChatItems( chatRh, type = cInfo.chatType, id = cInfo.apiId, - itemId = itemId, + itemIds = listOf(itemId), mode = mode ) - deletedChatItem = r?.deletedChatItem?.chatItem - toChatItem = r?.toChatItem?.chatItem } - if (toChatItem == null && deletedChatItem != null) { - chatModel.removeChatItem(chatRh, cInfo, deletedChatItem) - } else if (toChatItem != null) { - chatModel.upsertChatItem(chatRh, cInfo, toChatItem) + val deleted = r?.firstOrNull() + if (deleted != null) { + deletedChatItem = deleted.deletedChatItem.chatItem + toChatItem = deleted.toChatItem?.chatItem + if (toChatItem != null) { + chatModel.upsertChatItem(chatRh, cInfo, toChatItem) + } else { + chatModel.removeChatItem(chatRh, cInfo, deletedChatItem) + } } } }, @@ -280,18 +280,14 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId: if (itemIds.isNotEmpty()) { val chatInfo = chat.chatInfo withBGApi { - val deletedItems: ArrayList = arrayListOf() - for (itemId in itemIds) { - val di = chatModel.controller.apiDeleteChatItem( - chatRh, chatInfo.chatType, chatInfo.apiId, itemId, CIDeleteMode.cidmInternal - )?.deletedChatItem?.chatItem - if (di != null) { - deletedItems.add(di) + val deleted = chatModel.controller.apiDeleteChatItems( + chatRh, chatInfo.chatType, chatInfo.apiId, itemIds, CIDeleteMode.cidmInternal + ) + if (deleted != null) { + for (di in deleted) { + chatModel.removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem) } } - for (di in deletedItems) { - chatModel.removeChatItem(chatRh, chatInfo, di) - } } } },