From 47b638aee5e7820fbd0dbcf85ab31db43b156639 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Mon, 27 Nov 2023 20:23:32 -0800 Subject: [PATCH 1/4] bring the android code up to the ios side of custom content types --- android/build.gradle | 2 +- .../modules/xmtpreactnativesdk/XMTPModule.kt | 22 ++++++++++--- .../wrappers/ContentJson.kt | 33 +++++++++++++++---- .../wrappers/DecodedMessageWrapper.kt | 11 +++---- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 59e5353db..341f466ec 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -95,7 +95,7 @@ repositories { dependencies { implementation project(':expo-modules-core') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" - implementation "org.xmtp:android:0.6.12" + implementation "org.xmtp:android:0.6.13" implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.facebook.react:react-native:0.71.3' implementation "com.daveanthonythomas.moshipack:moshipack:1.0.1" diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt index 6848a8e26..16b8ca130 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt @@ -267,6 +267,18 @@ class XMTPModule : Module() { filename = attachment.filename ).toJson() } + + AsyncFunction("sendEncodedContent") { clientAddress: String, topic: String, encodedContentData: ByteArray -> + val conversation = + findConversation( + clientAddress = clientAddress, + topic = topic + ) ?: throw XMTPException("no conversation found for $topic") + + val encodedContent = EncodedContent.parseFrom(encodedContentData.toByteString()) + + conversation.send(encodedContent = encodedContent) + } AsyncFunction("listConversations") { clientAddress: String -> logV("listConversations") @@ -288,7 +300,7 @@ class XMTPModule : Module() { val beforeDate = if (before != null) Date(before) else null val afterDate = if (after != null) Date(after) else null - conversation.messages( + conversation.decryptedMessages( limit = limit, before = beforeDate, after = afterDate, @@ -340,7 +352,7 @@ class XMTPModule : Module() { topicsList.add(Pair(topic, page)) } - client.conversations.listBatchMessages(topicsList) + client.conversations.listBatchDecryptedMessages(topicsList) .map { DecodedMessageWrapper.encode(it) } } @@ -486,7 +498,7 @@ class XMTPModule : Module() { topic = topic ) ?: throw XMTPException("no conversation found for $topic") - val decodedMessage = conversation.decode(envelope) + val decodedMessage = conversation.decrypt(envelope) DecodedMessageWrapper.encode(decodedMessage) } @@ -581,7 +593,7 @@ class XMTPModule : Module() { subscriptions[getMessagesKey(clientAddress)]?.cancel() subscriptions[getMessagesKey(clientAddress)] = CoroutineScope(Dispatchers.IO).launch { try { - client.conversations.streamAllMessages().collect { message -> + client.conversations.streamAllDecryptedMessages().collect { message -> sendEvent( "message", mapOf( @@ -607,7 +619,7 @@ class XMTPModule : Module() { subscriptions[conversation.cacheKey(clientAddress)] = CoroutineScope(Dispatchers.IO).launch { try { - conversation.streamMessages().collect { message -> + conversation.streamDecryptedMessages().collect { message -> sendEvent( "message", mapOf( diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt index 775891a85..ae2dff3d6 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt @@ -1,6 +1,7 @@ package expo.modules.xmtpreactnativesdk.wrappers import android.util.Base64 +import com.google.gson.GsonBuilder import com.google.gson.JsonObject import com.google.gson.JsonParser import com.google.protobuf.ByteString @@ -14,6 +15,7 @@ import org.xmtp.android.library.codecs.ContentTypeReadReceipt import org.xmtp.android.library.codecs.ContentTypeRemoteAttachment import org.xmtp.android.library.codecs.ContentTypeReply import org.xmtp.android.library.codecs.ContentTypeText +import org.xmtp.android.library.codecs.EncodedContent import org.xmtp.android.library.codecs.Reaction import org.xmtp.android.library.codecs.ReactionCodec import org.xmtp.android.library.codecs.ReadReceipt @@ -28,16 +30,17 @@ import org.xmtp.android.library.codecs.description import org.xmtp.android.library.codecs.getReactionAction import org.xmtp.android.library.codecs.getReactionSchema import org.xmtp.android.library.codecs.id -import org.xmtp.proto.message.contents.Content.EncodedContent import java.net.URL class ContentJson( val type: ContentTypeId, val content: Any?, + val encodedContent: EncodedContent? = null, ) { constructor(encoded: EncodedContent) : this( type = encoded.type, content = encoded.decoded(), + encodedContent = encoded ); companion object { @@ -157,7 +160,11 @@ class ContentJson( ContentTypeReply.id -> mapOf( "reply" to mapOf( "reference" to (content as Reply).reference, - "content" to ContentJson(content.contentType, content.content).toJsonMap(), + "content" to ContentJson( + content.contentType, + content.content, + encodedContent + ).toJsonMap(), ) ) @@ -165,11 +172,23 @@ class ContentJson( "readReceipt" to "" ) - else -> mapOf( - "unknown" to mapOf( - "contentTypeId" to type.description - ) - ) + else -> { + val gson = GsonBuilder().create() + val encodedContentJSON = try { + gson.toJson(encodedContent).toString() + } catch (e: Exception) { + null + } + if (!encodedContentJSON.isNullOrBlank()) { + mapOf("encoded" to encodedContentJSON) + } else { + mapOf( + "unknown" to mapOf( + "contentTypeId" to type.description + ) + ) + } + } } } } diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecodedMessageWrapper.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecodedMessageWrapper.kt index a0f7a3126..da02077e5 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecodedMessageWrapper.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecodedMessageWrapper.kt @@ -1,27 +1,26 @@ package expo.modules.xmtpreactnativesdk.wrappers import com.google.gson.GsonBuilder -import org.xmtp.android.library.DecodedMessage import org.xmtp.android.library.codecs.description -import org.xmtp.android.library.codecs.id +import uniffi.xmtp_dh.org.xmtp.android.library.messages.DecryptedMessage class DecodedMessageWrapper { companion object { - fun encode(model: DecodedMessage): String { + fun encode(model: DecryptedMessage): String { val gson = GsonBuilder().create() val message = encodeMap(model) return gson.toJson(message) } - fun encodeMap(model: DecodedMessage): Map = mapOf( + fun encodeMap(model: DecryptedMessage): Map = mapOf( "id" to model.id, "topic" to model.topic, "contentTypeId" to model.encodedContent.type.description, "content" to ContentJson(model.encodedContent).toJsonMap(), "senderAddress" to model.senderAddress, - "sent" to model.sent.time, - "fallback" to model.fallbackContent + "sent" to model.sentAt.time, + "fallback" to model.encodedContent.fallback ) } } From e3fb360c8917c72ab20666d40adf1e7d50599637 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 28 Nov 2023 10:20:32 -0800 Subject: [PATCH 2/4] get the app building --- android/build.gradle | 2 +- .../expo/modules/xmtpreactnativesdk/XMTPModule.kt | 15 ++++++++++++--- .../wrappers/DecodedMessageWrapper.kt | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 341f466ec..e0a2f264c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -95,7 +95,7 @@ repositories { dependencies { implementation project(':expo-modules-core') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" - implementation "org.xmtp:android:0.6.13" + implementation "org.xmtp:android:0.6.14" implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.facebook.react:react-native:0.71.3' implementation "com.daveanthonythomas.moshipack:moshipack:1.0.1" diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt index 16b8ca130..038dcbef9 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt @@ -267,15 +267,24 @@ class XMTPModule : Module() { filename = attachment.filename ).toJson() } - - AsyncFunction("sendEncodedContent") { clientAddress: String, topic: String, encodedContentData: ByteArray -> + + AsyncFunction("sendEncodedContent") { clientAddress: String, topic: String, encodedContentData: List -> val conversation = findConversation( clientAddress = clientAddress, topic = topic ) ?: throw XMTPException("no conversation found for $topic") - val encodedContent = EncodedContent.parseFrom(encodedContentData.toByteString()) + val encodedContentDataBytes = + encodedContentData.foldIndexed(ByteArray(encodedContentData.size)) { i, a, v -> + a.apply { + set( + i, + v.toByte() + ) + } + } + val encodedContent = EncodedContent.parseFrom(encodedContentDataBytes) conversation.send(encodedContent = encodedContent) } diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecodedMessageWrapper.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecodedMessageWrapper.kt index da02077e5..775ef66a5 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecodedMessageWrapper.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecodedMessageWrapper.kt @@ -2,7 +2,7 @@ package expo.modules.xmtpreactnativesdk.wrappers import com.google.gson.GsonBuilder import org.xmtp.android.library.codecs.description -import uniffi.xmtp_dh.org.xmtp.android.library.messages.DecryptedMessage +import org.xmtp.android.library.messages.DecryptedMessage class DecodedMessageWrapper { From e77a713bcbd9e8f754208bbac3a06e989ca21783 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Wed, 29 Nov 2023 17:09:49 -0800 Subject: [PATCH 3/4] fix up the json parsing --- .../wrappers/ContentJson.kt | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt index ae2dff3d6..85f5ee1c8 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt @@ -35,7 +35,7 @@ import java.net.URL class ContentJson( val type: ContentTypeId, val content: Any?, - val encodedContent: EncodedContent? = null, + private val encodedContent: EncodedContent? = null, ) { constructor(encoded: EncodedContent) : this( type = encoded.type, @@ -173,13 +173,19 @@ class ContentJson( ) else -> { - val gson = GsonBuilder().create() - val encodedContentJSON = try { - gson.toJson(encodedContent).toString() - } catch (e: Exception) { - null + val json = JsonObject() + encodedContent?.let { + val gson = GsonBuilder().create() + val encodedType = gson.toJson(encodedContent.type) + val parameters = gson.toJson(encodedContent.parametersMap) + + + json.addProperty("fallback", encodedContent.fallback) + json.add("parameters", JsonParser.parseString(parameters)) + json.add("type", JsonParser.parseString(encodedType)) } - if (!encodedContentJSON.isNullOrBlank()) { + val encodedContentJSON = json.toString() + if (encodedContentJSON.isNotBlank()) { mapOf("encoded" to encodedContentJSON) } else { mapOf( @@ -191,4 +197,4 @@ class ContentJson( } } } -} +} \ No newline at end of file From fd1c631b4b54c606448ce01c2957fc4bceb5d8d5 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Wed, 29 Nov 2023 18:14:54 -0800 Subject: [PATCH 4/4] a small tweak to the json --- .../xmtpreactnativesdk/wrappers/ContentJson.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt index 85f5ee1c8..989f19017 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ContentJson.kt @@ -175,14 +175,16 @@ class ContentJson( else -> { val json = JsonObject() encodedContent?.let { - val gson = GsonBuilder().create() - val encodedType = gson.toJson(encodedContent.type) - val parameters = gson.toJson(encodedContent.parametersMap) - + val typeJson = JsonObject() + typeJson.addProperty("authorityId", encodedContent.type.authorityId) + typeJson.addProperty("typeId", encodedContent.type.typeId) + typeJson.addProperty("versionMajor", encodedContent.type.versionMajor) + typeJson.addProperty("versionMinor", encodedContent.type.versionMinor) + val parameters = GsonBuilder().create().toJson(encodedContent.parametersMap) json.addProperty("fallback", encodedContent.fallback) json.add("parameters", JsonParser.parseString(parameters)) - json.add("type", JsonParser.parseString(encodedType)) + json.add("type", typeJson) } val encodedContentJSON = json.toString() if (encodedContentJSON.isNotBlank()) {