From 72912f1be1c4bd18c8217386cb237f44662bf46c Mon Sep 17 00:00:00 2001 From: Evgeny Date: Fri, 9 Jan 2026 08:25:40 +0000 Subject: [PATCH] ui: api for media gallery content types (#6556) --- apps/ios/Shared/Model/AppAPITypes.swift | 12 +++++++++--- apps/ios/Shared/Model/SimpleXAPI.swift | 6 ++++++ apps/ios/SimpleXChat/ChatTypes.swift | 2 +- .../kotlin/chat/simplex/common/model/SimpleXAPI.kt | 14 ++++++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/apps/ios/Shared/Model/AppAPITypes.swift b/apps/ios/Shared/Model/AppAPITypes.swift index 193b675a57..e213f1c076 100644 --- a/apps/ios/Shared/Model/AppAPITypes.swift +++ b/apps/ios/Shared/Model/AppAPITypes.swift @@ -41,6 +41,7 @@ enum ChatCommand: ChatCmdProtocol { case apiGetChatTags(userId: Int64) case apiGetChats(userId: Int64) case apiGetChat(chatId: ChatId, scope: GroupChatScope?, contentTag: MsgContentTag?, pagination: ChatPagination, search: String) + case apiGetChatContentTypes(chatId: ChatId, scope: GroupChatScope?) case apiGetChatItemInfo(type: ChatType, id: Int64, scope: GroupChatScope?, itemId: Int64) case apiSendMessages(type: ChatType, id: Int64, scope: GroupChatScope?, live: Bool, ttl: Int?, composedMessages: [ComposedMessage]) case apiCreateChatTag(tag: ChatTagData) @@ -224,7 +225,8 @@ enum ChatCommand: ChatCmdProtocol { case let .apiGetChats(userId): return "/_get chats \(userId) pcc=on" case let .apiGetChat(chatId, scope, contentTag, pagination, search): let tag = contentTag != nil ? " content=\(contentTag!.rawValue)" : "" - return "/_get chat \(chatId)\(scopeRef(scope: scope))\(tag) \(pagination.cmdString)" + (search == "" ? "" : " search=\(search)") + return "/_get chat \(chatId)\(scopeRef(scope))\(tag) \(pagination.cmdString)" + (search == "" ? "" : " search=\(search)") + case let .apiGetChatContentTypes(chatId, scope): return "/_get content types \(chatId)\(scopeRef(scope))" case let .apiGetChatItemInfo(type, id, scope, itemId): return "/_get item info \(ref(type, id, scope: scope)) \(itemId)" case let .apiSendMessages(type, id, scope, live, ttl, composedMessages): let msgs = encodeJSON(composedMessages) @@ -417,6 +419,7 @@ enum ChatCommand: ChatCmdProtocol { case .apiGetChatTags: return "apiGetChatTags" case .apiGetChats: return "apiGetChats" case .apiGetChat: return "apiGetChat" + case .apiGetChatContentTypes: return "apiGetChatContentTypes" case .apiGetChatItemInfo: return "apiGetChatItemInfo" case .apiSendMessages: return "apiSendMessages" case .apiCreateChatTag: return "apiCreateChatTag" @@ -559,10 +562,10 @@ enum ChatCommand: ChatCmdProtocol { } func ref(_ type: ChatType, _ id: Int64, scope: GroupChatScope?) -> String { - "\(type.rawValue)\(id)\(scopeRef(scope: scope))" + "\(type.rawValue)\(id)\(scopeRef(scope))" } - func scopeRef(scope: GroupChatScope?) -> String { + func scopeRef(_ scope: GroupChatScope?) -> String { switch (scope) { case .none: "" case let .memberSupport(groupMemberId_): @@ -648,6 +651,7 @@ enum ChatResponse0: Decodable, ChatAPIResult { case chatStopped case apiChats(user: UserRef, chats: [ChatData]) case apiChat(user: UserRef, chat: ChatData, navInfo: NavigationInfo?) + case chatContentTypes(contentTypes: [MsgContentTag]) case chatTags(user: UserRef, userTags: [ChatTag]) case chatItemInfo(user: UserRef, chatItem: AChatItem, chatItemInfo: ChatItemInfo) case serverTestResult(user: UserRef, testServer: String, testFailure: ProtocolTestFailure?) @@ -680,6 +684,7 @@ enum ChatResponse0: Decodable, ChatAPIResult { case .chatStopped: "chatStopped" case .apiChats: "apiChats" case .apiChat: "apiChat" + case .chatContentTypes: "chatContentTypes" case .chatTags: "chatTags" case .chatItemInfo: "chatItemInfo" case .serverTestResult: "serverTestResult" @@ -714,6 +719,7 @@ enum ChatResponse0: Decodable, ChatAPIResult { case .chatStopped: return noDetails case let .apiChats(u, chats): return withUser(u, String(describing: chats)) case let .apiChat(u, chat, navInfo): return withUser(u, "chat: \(String(describing: chat))\nnavInfo: \(String(describing: navInfo))") + case let .chatContentTypes(types): return "content types: \(String(describing: types))" case let .chatTags(u, userTags): return withUser(u, "userTags: \(String(describing: userTags))") case let .chatItemInfo(u, chatItem, chatItemInfo): return withUser(u, "chatItem: \(String(describing: chatItem))\nchatItemInfo: \(String(describing: chatItemInfo))") case let .serverTestResult(u, server, testFailure): return withUser(u, "server: \(server)\nresult: \(String(describing: testFailure))") diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 5a042a6252..52a0c343ff 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -444,6 +444,12 @@ func apiGetChat(chatId: ChatId, scope: GroupChatScope?, contentTag: MsgContentTa throw r.unexpected } +func apiGetChatContentTypes(chatId: ChatId, scope: GroupChatScope?) async throws -> [MsgContentTag] { + let r: ChatResponse0 = try await chatSendCmd(.apiGetChatContentTypes(chatId: chatId, scope: scope)) + if case let .chatContentTypes(types) = r { return types } + throw r.unexpected +} + func loadChat(chat: Chat, im: ItemsModel, search: String = "", clearItems: Bool = true) async { await loadChat(chatId: chat.chatInfo.id, im: im, search: search, clearItems: clearItems) } diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 5d1d5b4302..e1bf8614e2 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -4601,7 +4601,7 @@ extension MsgContent: Encodable { } } -public enum MsgContentTag: String { +public enum MsgContentTag: String, Decodable { case text case link case image 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 a244293edb..b2817291ce 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 @@ -1036,6 +1036,14 @@ object ChatController { return null } + suspend fun apiGetChatContentTypes(rh: Long?, type: ChatType, id: Long, scope: GroupChatScope?): List? { + val r = sendCmd(rh, CC.ApiGetChatContentTypes(type, id, scope)) + if (r is API.Result && r.res is CR.ChatContentTypes) return r.res.contentTypes + Log.e(TAG, "apiGetChatContentTypes bad response: ${r.responseType} ${r.details}") + AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_loading_details), "${r.responseType}: ${r.details}") + return null + } + suspend fun apiCreateChatTag(rh: Long?, tag: ChatTagData): List? { val r = sendCmd(rh, CC.ApiCreateChatTag(tag)) if (r is API.Result && r.res is CR.ChatTags) return r.res.userTags @@ -3542,6 +3550,7 @@ sealed class CC { class ApiGetChatTags(val userId: Long): CC() class ApiGetChats(val userId: Long): CC() class ApiGetChat(val type: ChatType, val id: Long, val scope: GroupChatScope?, val contentTag: MsgContentTag?, val pagination: ChatPagination, val search: String = ""): CC() + class ApiGetChatContentTypes(val type: ChatType, val id: Long, val scope: GroupChatScope?): CC() class ApiGetChatItemInfo(val type: ChatType, val id: Long, val scope: GroupChatScope?, val itemId: Long): CC() class ApiSendMessages(val type: ChatType, val id: Long, val scope: GroupChatScope?, val live: Boolean, val ttl: Int?, val composedMessages: List): CC() class ApiCreateChatTag(val tag: ChatTagData): CC() @@ -3726,6 +3735,7 @@ sealed class CC { } "/_get chat ${chatRef(type, id, scope)}$tag ${pagination.cmdString}" + (if (search == "") "" else " search=$search") } + is ApiGetChatContentTypes -> "/_get content types ${chatRef(type, id, scope)})" is ApiGetChatItemInfo -> "/_get item info ${chatRef(type, id, scope)} $itemId" is ApiSendMessages -> { val msgs = json.encodeToString(composedMessages) @@ -3912,6 +3922,7 @@ sealed class CC { is ApiGetChatTags -> "apiGetChatTags" is ApiGetChats -> "apiGetChats" is ApiGetChat -> "apiGetChat" + is ApiGetChatContentTypes -> "apiGetChatContentTypes" is ApiGetChatItemInfo -> "apiGetChatItemInfo" is ApiSendMessages -> "apiSendMessages" is ApiCreateChatTag -> "apiCreateChatTag" @@ -6097,6 +6108,7 @@ sealed class 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, val navInfo: NavigationInfo = NavigationInfo()): CR() + @Serializable @SerialName("chatContentTypes") class ChatContentTypes(val contentTypes: List): CR() @Serializable @SerialName("chatTags") class ChatTags(val user: UserRef, val userTags: List): CR() @Serializable @SerialName("chatItemInfo") class ApiChatItemInfo(val user: UserRef, val chatItem: AChatItem, val chatItemInfo: ChatItemInfo): CR() @Serializable @SerialName("serverTestResult") class ServerTestResult(val user: UserRef, val testServer: String, val testFailure: ProtocolTestFailure? = null): CR() @@ -6278,6 +6290,7 @@ sealed class CR { is ChatStopped -> "chatStopped" is ApiChats -> "apiChats" is ApiChat -> "apiChat" + is ChatContentTypes -> "chatContentTypes" is ChatTags -> "chatTags" is ApiChatItemInfo -> "chatItemInfo" is ServerTestResult -> "serverTestResult" @@ -6451,6 +6464,7 @@ sealed class CR { is ChatStopped -> noDetails() is ApiChats -> withUser(user, json.encodeToString(chats)) is ApiChat -> withUser(user, "remoteHostId: ${chat.remoteHostId}\nchatInfo: ${chat.chatInfo}\nchatStats: ${chat.chatStats}\nnavInfo: ${navInfo}\nchatItems: ${chat.chatItems}") + is ChatContentTypes -> "content types: ${json.encodeToString(contentTypes)}" is ChatTags -> withUser(user, "userTags: ${json.encodeToString(userTags)}") is ApiChatItemInfo -> withUser(user, "chatItem: ${json.encodeToString(chatItem)}\n${json.encodeToString(chatItemInfo)}") is ServerTestResult -> withUser(user, "server: $testServer\nresult: ${json.encodeToString(testFailure)}")