From e2a55291fc09ffd20fc0bfa2a9c68fc0e5c1bf92 Mon Sep 17 00:00:00 2001 From: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com> Date: Thu, 16 Apr 2026 09:10:48 +0000 Subject: [PATCH] core, ui: tolerate unknown MsgContentTag in chatContentTypes (#6805) --- apps/ios/Shared/Model/SimpleXAPI.swift | 2 +- apps/ios/SimpleXChat/ChatTypes.swift | 39 ++++++++++++- .../chat/simplex/common/model/ChatModel.kt | 55 +++++++++++++++---- .../chat/simplex/common/model/SimpleXAPI.kt | 4 +- 4 files changed, 86 insertions(+), 14 deletions(-) diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index e527df1abd..02478aed80 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -458,7 +458,7 @@ func apiGetChat(chatId: ChatId, scope: GroupChatScope?, contentTag: MsgContentTa func apiGetChatContentTypes(chatId: ChatId, scope: GroupChatScope? = nil) async throws -> [MsgContentTag] { let r: ChatResponse0 = try await chatSendCmd(.apiGetChatContentTypes(chatId: chatId, scope: scope)) - if case let .chatContentTypes(types) = r { return types } + if case let .chatContentTypes(types) = r { return types.filter { if case .unknown = $0 { return false }; return true } } throw r.unexpected } diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 99fdeebac4..21cd050c8c 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -4773,7 +4773,7 @@ extension MsgContent: Encodable { } } -public enum MsgContentTag: String, Decodable { +public enum MsgContentTag: Codable, Hashable { case text case link case image @@ -4781,6 +4781,43 @@ public enum MsgContentTag: String, Decodable { case voice case file case report + case chat + case unknown(type: String) + + public var rawValue: String { + switch self { + case .text: return "text" + case .link: return "link" + case .image: return "image" + case .video: return "video" + case .voice: return "voice" + case .file: return "file" + case .report: return "report" + case .chat: return "chat" + case let .unknown(type): return type + } + } + + public init(from decoder: Decoder) throws { + let s = try decoder.singleValueContainer().decode(String.self) + switch s { + case "text": self = .text + case "link": self = .link + case "image": self = .image + case "video": self = .video + case "voice": self = .voice + case "file": self = .file + case "report": self = .report + case "chat": self = .chat + case "liveText": self = .text + default: self = .unknown(type: s) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(rawValue) + } } public enum MsgChatLink: Codable, Equatable, Hashable { 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 f89a599bb4..ff6b003c25 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 @@ -4492,16 +4492,51 @@ object MsgContentSerializer : KSerializer { } } -@Serializable -enum class MsgContentTag { - @SerialName("text") Text, - @SerialName("link") Link, - @SerialName("image") Image, - @SerialName("video") Video, - @SerialName("voice") Voice, - @SerialName("file") File, - @SerialName("report") Report, - @SerialName("chat") Chat, +@Serializable(with = MsgContentTagSerializer::class) +sealed class MsgContentTag { + @Serializable @SerialName("text") object Text: MsgContentTag() + @Serializable @SerialName("link") object Link: MsgContentTag() + @Serializable @SerialName("image") object Image: MsgContentTag() + @Serializable @SerialName("video") object Video: MsgContentTag() + @Serializable @SerialName("voice") object Voice: MsgContentTag() + @Serializable @SerialName("file") object File: MsgContentTag() + @Serializable @SerialName("report") object Report: MsgContentTag() + @Serializable @SerialName("chat") object Chat: MsgContentTag() + @Serializable @SerialName("unknown") data class Unknown(val type: String): MsgContentTag() + + val cmdString: String get() = when (this) { + is Text -> "text" + is Link -> "link" + is Image -> "image" + is Video -> "video" + is Voice -> "voice" + is File -> "file" + is Report -> "report" + is Chat -> "chat" + is Unknown -> type + } +} + +object MsgContentTagSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MsgContentTag", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): MsgContentTag = + when (val s = decoder.decodeString()) { + "text" -> MsgContentTag.Text + "link" -> MsgContentTag.Link + "image" -> MsgContentTag.Image + "video" -> MsgContentTag.Video + "voice" -> MsgContentTag.Voice + "file" -> MsgContentTag.File + "report" -> MsgContentTag.Report + "chat" -> MsgContentTag.Chat + "liveText" -> MsgContentTag.Text + else -> MsgContentTag.Unknown(s) + } + + override fun serialize(encoder: Encoder, value: MsgContentTag) { + encoder.encodeString(value.cmdString) + } } @Serializable 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 661b7e767f..8a2be99af2 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 @@ -1043,7 +1043,7 @@ object ChatController { 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 + if (r is API.Result && r.res is CR.ChatContentTypes) return r.res.contentTypes.filter { it !is MsgContentTag.Unknown } 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 @@ -3790,7 +3790,7 @@ sealed class CC { val tag = if (contentTag == null) { "" } else { - " content=${contentTag.name.lowercase()}" + " content=${contentTag.cmdString}" } "/_get chat ${chatRef(type, id, scope)}$tag ${pagination.cmdString}" + (if (search == "") "" else " search=$search") }