diff --git a/library/src/androidTest/java/org/xmtp/android/library/ReactionTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ReactionTest.kt index 2ec03df49..ee7ae3fe5 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ReactionTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ReactionTest.kt @@ -1,10 +1,12 @@ package org.xmtp.android.library import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.protobuf.kotlin.toByteStringUtf8 import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith import org.xmtp.android.library.codecs.ContentTypeReaction +import org.xmtp.android.library.codecs.EncodedContent import org.xmtp.android.library.codecs.Reaction import org.xmtp.android.library.codecs.ReactionAction import org.xmtp.android.library.codecs.ReactionCodec @@ -14,6 +16,50 @@ import org.xmtp.android.library.messages.walletAddress @RunWith(AndroidJUnit4::class) class ReactionTest { + @Test + fun testCanDecodeLegacyForm() { + val codec = ReactionCodec() + + // This is how clients send reactions now. + val canonicalEncoded = EncodedContent.newBuilder().also { + it.type = ContentTypeReaction + it.content = """ + { + "action": "added", + "content": "smile", + "reference": "abc123", + "schema": "shortcode" + } + """.trimIndent().toByteStringUtf8() + }.build() + + // Previously, some clients sent reactions like this. + // So we test here to make sure we can still decode them. + val legacyEncoded = EncodedContent.newBuilder().also { + it.type = ContentTypeReaction + it.putAllParameters( + mapOf( + "action" to "added", + "reference" to "abc123", + "schema" to "shortcode", + ) + ) + it.content = "smile".toByteStringUtf8() + }.build() + + val canonical = codec.decode(canonicalEncoded) + val legacy = codec.decode(legacyEncoded) + + assertEquals(ReactionAction.added, canonical.action) + assertEquals(ReactionAction.added, legacy.action) + assertEquals("smile", canonical.content) + assertEquals("smile", legacy.content) + assertEquals("abc123", canonical.reference) + assertEquals("abc123", legacy.reference) + assertEquals(ReactionSchema.shortcode, canonical.schema) + assertEquals(ReactionSchema.shortcode, legacy.schema) + } + @Test fun testCanUseReactionCodec() { Client.register(codec = ReactionCodec()) diff --git a/library/src/main/java/org/xmtp/android/library/codecs/ReactionCodec.kt b/library/src/main/java/org/xmtp/android/library/codecs/ReactionCodec.kt index 3eebe1aef..824b52265 100644 --- a/library/src/main/java/org/xmtp/android/library/codecs/ReactionCodec.kt +++ b/library/src/main/java/org/xmtp/android/library/codecs/ReactionCodec.kt @@ -37,7 +37,20 @@ data class ReactionCodec(override var contentType: ContentTypeId = ContentTypeRe } override fun decode(content: EncodedContent): Reaction { - val gson = GsonBuilder().create() - return gson.fromJson(content.content.toStringUtf8(), Reaction::class.java) + val text = content.content.toStringUtf8() + + // First try to decode it in the canonical form. + try { + return GsonBuilder().create().fromJson(text, Reaction::class.java) + } catch (ignore: Exception) { + } + + // If that fails, try to decode it in the legacy form. + return Reaction( + reference = content.parametersMap["reference"] ?: "", + action = content.parametersMap["action"]?.let { ReactionAction.valueOf(it) }!!, + schema = content.parametersMap["schema"]?.let { ReactionSchema.valueOf(it) }!!, + content = text, + ) } }