Skip to content

Commit

Permalink
Merge pull request #198 from xmtp/user/alexrisch/custom-content-prepare
Browse files Browse the repository at this point in the history
Add Support for Custom Content Types when preparing messages
  • Loading branch information
alexrisch authored Jan 3, 2024
2 parents e70335a + 7b50ba0 commit 8021f6b
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 10 deletions.
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ repositories {
dependencies {
implementation project(':expo-modules-core')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
implementation "org.xmtp:android:0.7.0"
implementation "org.xmtp:android:0.7.1"
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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,39 @@ class XMTPModule : Module() {
).toJson()
}

AsyncFunction("prepareEncodedMessage") { clientAddress: String, conversationTopic: String, encodedContentData: List<Int> ->
logV("prepareEncodedMessage")
val conversation =
findConversation(
clientAddress = clientAddress,
topic = conversationTopic
)
?: throw XMTPException("no conversation found for $conversationTopic")

val encodedContentDataBytes =
encodedContentData.foldIndexed(ByteArray(encodedContentData.size)) { i, a, v ->
a.apply {
set(
i,
v.toByte()
)
}
}
val encodedContent = EncodedContent.parseFrom(encodedContentDataBytes)

val prepared = conversation.prepareMessage(
encodedContent = encodedContent,
)
val preparedAtMillis = prepared.envelopes[0].timestampNs / 1_000_000
val preparedFile = File.createTempFile(prepared.messageId, null)
preparedFile.writeBytes(prepared.toSerializedData())
PreparedLocalMessage(
messageId = prepared.messageId,
preparedFileUri = preparedFile.toURI().toString(),
preparedAt = preparedAtMillis,
).toJson()
}

AsyncFunction("sendPreparedMessage") { clientAddress: String, preparedLocalMessageJson: String ->
logV("sendPreparedMessage")
val client = clients[clientAddress] ?: throw XMTPException("No client")
Expand Down
14 changes: 7 additions & 7 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ PODS:
- React-jsinspector (0.71.14)
- React-logger (0.71.14):
- glog
- react-native-blob-util (0.19.4):
- react-native-blob-util (0.19.6):
- React-Core
- react-native-encrypted-storage (4.0.3):
- React-Core
Expand Down Expand Up @@ -411,15 +411,15 @@ PODS:
- GenericJSON (~> 2.0)
- Logging (~> 1.0.0)
- secp256k1.swift (~> 0.1)
- XMTP (0.7.3-alpha0):
- XMTP (0.7.4-alpha0):
- Connect-Swift (= 0.3.0)
- GzipSwift
- web3.swift
- XMTPRust (= 0.3.7-beta0)
- XMTPReactNative (0.1.0):
- ExpoModulesCore
- MessagePacker
- XMTP (= 0.7.3-alpha0)
- XMTP (= 0.7.4-alpha0)
- XMTPRust (0.3.7-beta0)
- Yoga (1.14.0)

Expand Down Expand Up @@ -646,7 +646,7 @@ SPEC CHECKSUMS:
React-jsiexecutor: 94cfc1788637ceaf8841ef1f69b10cc0d62baadc
React-jsinspector: 7bf923954b4e035f494b01ac16633963412660d7
React-logger: 655ff5db8bd922acfbe76a4983ffab048916343e
react-native-blob-util: 30a6c9fd067aadf9177e61a998f2c7efb670598d
react-native-blob-util: d8fa1a7f726867907a8e43163fdd8b441d4489ea
react-native-encrypted-storage: db300a3f2f0aba1e818417c1c0a6be549038deb7
react-native-mmkv: e97c0c79403fb94577e5d902ab1ebd42b0715b43
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
Expand All @@ -668,11 +668,11 @@ SPEC CHECKSUMS:
secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634
SwiftProtobuf: b02b5075dcf60c9f5f403000b3b0c202a11b6ae1
web3.swift: 2263d1e12e121b2c42ffb63a5a7beb1acaf33959
XMTP: dc02c96b475e326a4a7b3d3912cc45cf3527bd0b
XMTPReactNative: 5c1111c5bd3456e75b3fa67d1ddccabb7a01df11
XMTP: 9ba94e797211aa4f7cbed9ed2a2f4c44d32c8d06
XMTPReactNative: 6f194a2f3ab388d2517f92feae01cff961ee88ab
XMTPRust: 8848a2ba761b2c961d666632f2ad27d1082faa93
Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9

PODFILE CHECKSUM: 522d88edc2d5fac4825e60a121c24abc18983367

COCOAPODS: 1.13.0
COCOAPODS: 1.14.3
36 changes: 36 additions & 0 deletions example/src/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,42 @@ test('register and use custom content types', async () => {
return true
})

test('register and use custom content types when preparing message', async () => {
const bob = await Client.createRandom({
env: 'local',
codecs: [new NumberCodec()],
})
const alice = await Client.createRandom({
env: 'local',
codecs: [new NumberCodec()],
})

bob.register(new NumberCodec())
alice.register(new NumberCodec())

const bobConvo = await bob.conversations.newConversation(alice.address)
const aliceConvo = await alice.conversations.newConversation(bob.address)

const prepped = await bobConvo.prepareMessage(12, {
contentType: ContentTypeNumber,
})

await bobConvo.sendPreparedMessage(prepped)

const messages = await aliceConvo.messages()
assert(messages.length === 1, 'did not get messages')

const message = messages[0]
const messageContent = message.content()

assert(
messageContent === 12,
'did not get content properly: ' + JSON.stringify(messageContent)
)

return true
})

test('calls preCreateIdentityCallback when supplied', async () => {
let isCallbackCalled = false
const preCreateIdentityCallback = () => {
Expand Down
24 changes: 24 additions & 0 deletions ios/XMTPModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,30 @@ public class XMTPModule: Module {
).toJson()
}

AsyncFunction("prepareEncodedMessage") { (
clientAddress: String,
conversationTopic: String,
encodedContentData: [UInt8]
) -> String in
guard let conversation = try await findConversation(clientAddress: clientAddress, topic: conversationTopic) else {
throw Error.conversationNotFound("no conversation found for \(conversationTopic)")
}
let encodedContent = try EncodedContent(serializedData: Data(encodedContentData))

let prepared = try await conversation.prepareMessage(
encodedContent: encodedContent
)
let preparedAtMillis = prepared.envelopes[0].timestampNs / 1_000_000
let preparedData = try prepared.serializedData()
let preparedFile = FileManager.default.temporaryDirectory.appendingPathComponent(prepared.messageID)
try preparedData.write(to: preparedFile)
return try PreparedLocalMessage(
messageId: prepared.messageID,
preparedFileUri: preparedFile.absoluteString,
preparedAt: preparedAtMillis
).toJson()
}

AsyncFunction("sendPreparedMessage") { (clientAddress: String, preparedLocalMessageJson: String) -> String in
guard let client = await clientsManager.getClient(key: clientAddress) else {
throw Error.noClient
Expand Down
2 changes: 1 addition & 1 deletion ios/XMTPReactNative.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ Pod::Spec.new do |s|
s.source_files = "**/*.{h,m,swift}"
s.dependency 'secp256k1.swift'
s.dependency "MessagePacker"
s.dependency "XMTP", "= 0.7.3-alpha0"
s.dependency "XMTP", "= 0.7.4-alpha0"
end
20 changes: 20 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,26 @@ export async function prepareMessage(
return JSON.parse(preparedJson)
}

export async function prepareMessageWithContentType<T>(
clientAddress: string,
conversationTopic: string,
content: any,
codec: ContentCodec<T>
): Promise<PreparedLocalMessage> {
if ('contentKey' in codec) {
return prepareMessage(clientAddress, conversationTopic, content)
}
const encodedContent = codec.encode(content)
encodedContent.fallback = codec.fallback(content)
const encodedContentData = EncodedContent.encode(encodedContent).finish()
const preparedJson = await XMTPModule.prepareEncodedMessage(
clientAddress,
conversationTopic,
Array.from(encodedContentData)
)
return JSON.parse(preparedJson)
}

export async function sendPreparedMessage(
clientAddress: string,
preparedLocalMessage: PreparedLocalMessage
Expand Down
29 changes: 28 additions & 1 deletion src/lib/Conversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,27 @@ export class Conversation<ContentTypes> {
}
}

private async _prepareWithJSCodec<T>(
content: T,
contentType: XMTP.ContentTypeId
): Promise<PreparedLocalMessage> {
const codec =
this.client.codecRegistry[
`${contentType.authorityId}/${contentType.typeId}:${contentType.versionMajor}.${contentType.versionMinor}`
]

if (!codec) {
throw new Error(`no codec found for: ${contentType}`)
}

return await XMTP.prepareMessageWithContentType(
this.client.address,
this.topic,
content,
codec,
)
}

/**
* Prepares a message to be sent, yielding a `PreparedLocalMessage` object.
*
Expand All @@ -146,7 +167,13 @@ export class Conversation<ContentTypes> {
* @returns {Promise<PreparedLocalMessage>} A Promise that resolves to a `PreparedLocalMessage` object.
* @throws {Error} Throws an error if there is an issue with preparing the message.
*/
async prepareMessage(content: any): Promise<PreparedLocalMessage> {
async prepareMessage(
content: any,
opts?: SendOptions
): Promise<PreparedLocalMessage> {
if (opts && opts.contentType) {
return await this._prepareWithJSCodec(content, opts.contentType)
}
try {
if (typeof content === 'string') {
content = { text: content }
Expand Down

0 comments on commit 8021f6b

Please sign in to comment.