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 d7ebc86859..791222825c 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 @@ -858,9 +858,9 @@ object ChatController { return emptyList() } - suspend fun apiGetChat(rh: Long?, type: ChatType, id: Long, pagination: ChatPagination = ChatPagination.Last(ChatPagination.INITIAL_COUNT), search: String = ""): Chat? { + suspend fun apiGetChat(rh: Long?, type: ChatType, id: Long, pagination: ChatPagination = ChatPagination.Last(ChatPagination.INITIAL_COUNT), search: String = ""): Pair? { val r = sendCmd(rh, CC.ApiGetChat(type, id, pagination, search)) - if (r is CR.ApiChat) return if (rh == null) r.chat else r.chat.copy(remoteHostId = rh) + if (r is CR.ApiChat) return if (rh == null) Pair(r.chat, r.section) else Pair(r.chat.copy(remoteHostId = rh), r.section) if (r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorStore && r.chatError.storeError is StoreError.ChatItemNotFound) { AlertManager.shared.showAlertMsg(generalGetString(MR.strings.failed_to_get_chat_item_not_found_title), generalGetString(MR.strings.failed_to_get_chat_item_not_found_description)) } else { @@ -3466,12 +3466,14 @@ sealed class ChatPagination { class After(val chatItemId: Long, val count: Int): ChatPagination() class Before(val chatItemId: Long, val count: Int): ChatPagination() class Around(val chatItemId: Long, val count: Int): ChatPagination() + class Initial(val count: Int): ChatPagination() val cmdString: String get() = when (this) { is Last -> "count=${this.count}" is After -> "after=${this.chatItemId} count=${this.count}" is Before -> "before=${this.chatItemId} count=${this.count}" is Around -> "around=${this.chatItemId} count=${this.count}" + is Initial -> "initial=${this.count}" } companion object { @@ -4856,7 +4858,10 @@ class APIResponse(val resp: CR, val remoteHostId: Long?, val corr: String? = nul } else if (type == "apiChat") { val user: UserRef = json.decodeFromJsonElement(resp["user"]!!.jsonObject) val chat = parseChatData(resp["chat"]!!) - return APIResponse(CR.ApiChat(user, chat), remoteHostId, corr) + val section = resp["section"]?.jsonObject?.get("type")?.let { + json.decodeFromJsonElement(it) + } ?: ChatLandingSection.Latest + return APIResponse(CR.ApiChat(user, chat, section), remoteHostId, corr) } else if (type == "chatCmdError") { val userObject = resp["user_"]?.jsonObject val user = runCatching { json.decodeFromJsonElement(userObject!!) }.getOrNull() @@ -4913,7 +4918,7 @@ sealed class CR { @Serializable @SerialName("chatRunning") class ChatRunning: CR() @Serializable @SerialName("chatStopped") class ChatStopped: CR() @Serializable @SerialName("apiChats") class ApiChats(val user: UserRef, val chats: List): CR() - @Serializable @SerialName("apiChat") class ApiChat(val user: UserRef, val chat: Chat): CR() + @Serializable @SerialName("apiChat") class ApiChat(val user: UserRef, val chat: Chat, val section: ChatLandingSection): CR() @Serializable @SerialName("chatItemInfo") class ApiChatItemInfo(val user: UserRef, val chatItem: AChatItem, val chatItemInfo: ChatItemInfo): CR() @Serializable @SerialName("userProtoServers") class UserProtoServers(val user: UserRef, val servers: UserProtocolServers): CR() @Serializable @SerialName("serverTestResult") class ServerTestResult(val user: UserRef, val testServer: String, val testFailure: ProtocolTestFailure? = null): CR() @@ -5263,7 +5268,7 @@ sealed class CR { is ChatRunning -> noDetails() is ChatStopped -> noDetails() is ApiChats -> withUser(user, json.encodeToString(chats)) - is ApiChat -> withUser(user, json.encodeToString(chat)) + is ApiChat -> withUser(user, "section: ${json.encodeToString(section)}\n${json.encodeToString(chat)}") is ApiChatItemInfo -> withUser(user, "chatItem: ${json.encodeToString(chatItem)}\n${json.encodeToString(chatItemInfo)}") is UserProtoServers -> withUser(user, "servers: ${json.encodeToString(servers)}") is ServerTestResult -> withUser(user, "server: $testServer\nresult: ${json.encodeToString(testFailure)}") @@ -5503,6 +5508,12 @@ sealed class GroupLinkPlan { @Serializable @SerialName("known") class Known(val groupInfo: GroupInfo): GroupLinkPlan() } +@Serializable +enum class ChatLandingSection { + @SerialName("latest") Latest, + @SerialName("unread") Unread, +} + abstract class TerminalItem { abstract val id: Long abstract val remoteHostId: Long? diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatSections.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatSections.kt index b17d4722fe..fa2095e7a8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatSections.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatSections.kt @@ -194,7 +194,7 @@ fun ChatSection.excessItemCount(): Int { suspend fun apiLoadMessagesAroundItem(chatInfo: ChatInfo, chatModel: ChatModel, aroundItemId: Long, rhId: Long?, chatSectionLoad: ChatSectionLoad) { val pagination = ChatPagination.Around(aroundItemId, ChatPagination.PRELOAD_COUNT * 2) - val chat = chatModel.controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId, pagination) ?: return + val (chat) = chatModel.controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId, pagination) ?: return if (chatModel.chatId.value != chat.id) return withContext(Dispatchers.Main) { val itemsToAdd = chatSectionLoad.prepareItems(chat.chatItems) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt index 9981d70a52..613117b10e 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt @@ -70,8 +70,9 @@ fun GroupMemberInfoView( getContactChat = { chatModel.getContactChat(it) }, openDirectChat = { withBGApi { - val c = chatModel.controller.apiGetChat(rhId, ChatType.Direct, it) - if (c != null) { + val r = chatModel.controller.apiGetChat(rhId, ChatType.Direct, it, ChatPagination.Initial(ChatPagination.INITIAL_COUNT)) + if (r != null) { + val (c) = r withChats { if (chatModel.getContactChat(it) == null) { addChat(c) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt index 7fdb57a1d9..4c65c26247 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt @@ -205,23 +205,23 @@ suspend fun noteFolderChatAction(rhId: Long?, noteFolder: NoteFolder) { } suspend fun openDirectChat(rhId: Long?, contactId: Long, chatModel: ChatModel) = coroutineScope { - val chat = chatModel.controller.apiGetChat(rhId, ChatType.Direct, contactId) + val chat = chatModel.controller.apiGetChat(rhId, ChatType.Direct, contactId, ChatPagination.Initial(ChatPagination.INITIAL_COUNT)) if (chat != null && isActive) { - openLoadedChat(chat, chatModel) + openLoadedChat(chat.first, chatModel) } } suspend fun openGroupChat(rhId: Long?, groupId: Long, chatModel: ChatModel) = coroutineScope { - val chat = chatModel.controller.apiGetChat(rhId, ChatType.Group, groupId) + val chat = chatModel.controller.apiGetChat(rhId, ChatType.Group, groupId, ChatPagination.Initial(ChatPagination.INITIAL_COUNT)) if (chat != null && isActive) { - openLoadedChat(chat, chatModel) + openLoadedChat(chat.first, chatModel) } } suspend fun openChat(rhId: Long?, chatInfo: ChatInfo, chatModel: ChatModel) = coroutineScope { - val chat = chatModel.controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId) + val chat = chatModel.controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId, ChatPagination.Initial(ChatPagination.INITIAL_COUNT)) if (chat != null && isActive) { - openLoadedChat(chat, chatModel) + openLoadedChat(chat.first, chatModel) } } @@ -234,7 +234,7 @@ fun openLoadedChat(chat: Chat, chatModel: ChatModel) { suspend fun apiLoadPrevMessages(ch: Chat, chatModel: ChatModel, beforeChatItemId: Long, search: String, chatSectionLoad: ChatSectionLoad) { val chatInfo = ch.chatInfo val pagination = ChatPagination.Before(beforeChatItemId, ChatPagination.PRELOAD_COUNT) - val chat = chatModel.controller.apiGetChat(ch.remoteHostId, chatInfo.chatType, chatInfo.apiId, pagination, search) ?: return + val (chat) = chatModel.controller.apiGetChat(ch.remoteHostId, chatInfo.chatType, chatInfo.apiId, pagination, search) ?: return if (chatModel.chatId.value != chat.id) return withContext(Dispatchers.Main) { val itemsToAdd = chatSectionLoad.prepareItems(chat.chatItems) @@ -247,7 +247,7 @@ suspend fun apiLoadPrevMessages(ch: Chat, chatModel: ChatModel, beforeChatItemId suspend fun apiLoadAfterMessages(ch: Chat, chatModel: ChatModel, afterChatItemId: Long, search: String, chatSectionLoad: ChatSectionLoad) { val chatInfo = ch.chatInfo val pagination = ChatPagination.After(afterChatItemId, ChatPagination.PRELOAD_COUNT) - val chat = chatModel.controller.apiGetChat(ch.remoteHostId, chatInfo.chatType, chatInfo.apiId, pagination, search) ?: return + val (chat) = chatModel.controller.apiGetChat(ch.remoteHostId, chatInfo.chatType, chatInfo.apiId, pagination, search) ?: return if (chatModel.chatId.value != chat.id) return withContext(Dispatchers.Main) { val itemsToAdd = chatSectionLoad.prepareItems(chat.chatItems) @@ -259,7 +259,7 @@ suspend fun apiLoadAfterMessages(ch: Chat, chatModel: ChatModel, afterChatItemId suspend fun apiFindMessages(ch: Chat, chatModel: ChatModel, search: String) { val chatInfo = ch.chatInfo - val chat = chatModel.controller.apiGetChat(ch.remoteHostId, chatInfo.chatType, chatInfo.apiId, search = search) ?: return + val (chat) = chatModel.controller.apiGetChat(ch.remoteHostId, chatInfo.chatType, chatInfo.apiId, search = search) ?: return if (chatModel.chatId.value != chat.id) return chatModel.chatItems.replaceAll(chat.chatItems) }