diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 58bbfdbfd8..60d5082584 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -3523,8 +3523,12 @@ extension MsgReaction: Decodable { let type = try container.decode(String.self, forKey: CodingKeys.type) switch type { case "emoji": - let emoji = try container.decode(MREmojiChar.self, forKey: CodingKeys.emoji) - self = .emoji(emoji: emoji) + do { + let emoji = try container.decode(MREmojiChar.self, forKey: CodingKeys.emoji) + self = .emoji(emoji: emoji) + } catch { + self = .unknown(type: "emoji") + } default: self = .unknown(type: type) } 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 51c27b21de..04fd6b380d 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 @@ -3311,7 +3311,7 @@ sealed class MsgReaction { MREmojiChar.Heart -> "❤️" else -> emoji.value } - is Unknown -> "" + is Unknown -> "?" } companion object { @@ -3341,8 +3341,13 @@ object MsgReactionSerializer : KSerializer { return if (json is JsonObject && "type" in json) { when(val t = json["type"]?.jsonPrimitive?.content ?: "") { "emoji" -> { - val emoji = Json.decodeFromString(json["emoji"].toString()) - if (emoji == null) MsgReaction.Unknown(t, json) else MsgReaction.Emoji(emoji) + val msgReaction = try { + val emoji = Json.decodeFromString(json["emoji"].toString()) + MsgReaction.Emoji(emoji) + } catch (e: Throwable) { + MsgReaction.Unknown(t, json) + } + msgReaction } else -> MsgReaction.Unknown(t, json) } diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index ebc63e131b..79ff9333c6 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -3746,8 +3746,8 @@ chatCommandP = "/_delete member item #" *> (APIDeleteMemberChatItem <$> A.decimal <*> _strP), "/_archive reports #" *> (APIArchiveReceivedReports <$> A.decimal), "/_delete reports #" *> (APIDeleteReceivedReports <$> A.decimal <*> _strP <*> _strP), - "/_reaction " *> (APIChatItemReaction <$> chatRefP <* A.space <*> A.decimal <* A.space <*> onOffP <* A.space <*> jsonP), - "/_reaction members " *> (APIGetReactionMembers <$> A.decimal <* " #" <*> A.decimal <* A.space <*> A.decimal <* A.space <*> jsonP), + "/_reaction " *> (APIChatItemReaction <$> chatRefP <* A.space <*> A.decimal <* A.space <*> onOffP <* A.space <*> (knownReaction <$?> jsonP)), + "/_reaction members " *> (APIGetReactionMembers <$> A.decimal <* " #" <*> A.decimal <* A.space <*> A.decimal <* A.space <*> (knownReaction <$?> jsonP)), "/_forward plan " *> (APIPlanForwardChatItems <$> chatRefP <*> _strP), "/_forward " *> (APIForwardChatItems <$> chatRefP <* A.space <*> chatRefP <*> _strP <*> sendMessageTTLP), "/_read user " *> (APIUserRead <$> A.decimal), diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 1e5815bab3..9c8c044630 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -425,6 +425,13 @@ data MsgReaction = MREmoji {emoji :: MREmojiChar} | MRUnknown {tag :: Text, json emojiTag :: IsString a => a emojiTag = "emoji" +knownReaction :: MsgReaction -> Either String MsgReaction +knownReaction = \case + r@MREmoji {} -> Right r + MRUnknown {} -> Left "unknown MsgReaction" + +-- parseJSON for MsgReaction parses unknown emoji reactions as MRUnknown with type "emoji", +-- allowing to add new emojis in a backwards compatible way - UI shows them as ? instance FromJSON MsgReaction where parseJSON (J.Object v) = do tag <- v .: "type"