Skip to content

Commit

Permalink
first pass at all the pieces needed for threading
Browse files Browse the repository at this point in the history
  • Loading branch information
nplasterer committed Jan 19, 2024
1 parent 1a1e055 commit fcb9921
Show file tree
Hide file tree
Showing 8 changed files with 2,559 additions and 9 deletions.
48 changes: 42 additions & 6 deletions library/src/main/java/org/xmtp/android/library/Client.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ import org.xmtp.android.library.messages.walletAddress
import org.xmtp.proto.message.api.v1.MessageApiOuterClass
import org.xmtp.proto.message.api.v1.MessageApiOuterClass.BatchQueryResponse
import org.xmtp.proto.message.api.v1.MessageApiOuterClass.QueryRequest
import uniffi.xmtp_dh.FfiInboxOwner
import uniffi.xmtp_dh.FfiXmtpClient
import uniffi.xmtp_dh.createClient
import uniffi.xmtp_dh.org.xmtp.android.library.libxmtp.InboxOwner
import uniffi.xmtp_dh.org.xmtp.android.library.libxmtp.XMTPLogger
import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.time.Instant
Expand Down Expand Up @@ -63,6 +68,8 @@ class Client() {
lateinit var apiClient: ApiClient
lateinit var contacts: Contacts
lateinit var conversations: Conversations
lateinit var logger: XMTPLogger
var libXMTPClient: FfiXmtpClient? = null

companion object {
private const val TAG = "Client"
Expand Down Expand Up @@ -137,12 +144,15 @@ class Client() {
address: String,
privateKeyBundleV1: PrivateKeyBundleV1,
apiClient: ApiClient,
libXmtpClient: FfiXmtpClient? = null
) : this() {
this.address = address
this.address = libXMTPClient?.accountAddress() ?: address
this.privateKeyBundleV1 = privateKeyBundleV1
this.apiClient = apiClient
this.contacts = Contacts(client = this)
this.conversations = Conversations(client = this)
this.conversations = Conversations(client = this, libXMTPConversations = libXmtpClient?.conversations())
this.logger = XMTPLogger()
this.libXMTPClient = libXmtpClient
}

fun buildFrom(bundle: PrivateKeyBundleV1, options: ClientOptions? = null): Client {
Expand All @@ -153,18 +163,44 @@ class Client() {
return Client(address = address, privateKeyBundleV1 = bundle, apiClient = apiClient)
}

fun create(account: SigningKey, options: ClientOptions? = null): Client {
fun create(
account: SigningKey,
options: ClientOptions? = null,
inboxOwner: InboxOwner? = null,
): Client {
val clientOptions = options ?: ClientOptions()
val apiClient =
GRPCApiClient(environment = clientOptions.api.env, secure = clientOptions.api.isSecure)
return create(account = account, apiClient = apiClient, options = options)
return create(
account = account,
apiClient = apiClient,
options = options,
inboxOwner = inboxOwner
)
}

fun create(account: SigningKey, apiClient: ApiClient, options: ClientOptions? = null): Client {
fun create(
account: SigningKey,
apiClient: ApiClient,
options: ClientOptions? = null,
inboxOwner: InboxOwner? = null,
): Client {
return runBlocking {
try {
val privateKeyBundleV1 = loadOrCreateKeys(account, apiClient, options)
val client = Client(account.address, privateKeyBundleV1, apiClient)
val libXMTPClient: FfiXmtpClient? = if (inboxOwner != null) {
createClient(
logger = logger,
ffiInboxOwner = inboxOwner,
host = "https://dev.xmtp.network:5556",
isSecure = true,
db = null,
encryptionKey = null
)
} else {
null
}
val client = Client(account.address, privateKeyBundleV1, apiClient, libXMTPClient)
client.ensureUserContactPublished()
client
} catch (e: java.lang.Exception) {
Expand Down
29 changes: 28 additions & 1 deletion library/src/main/java/org/xmtp/android/library/Conversation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ sealed class Conversation {
data class V1(val conversationV1: ConversationV1) : Conversation()
data class V2(val conversationV2: ConversationV2) : Conversation()

enum class Version { V1, V2 }
data class Group(val group: org.xmtp.android.library.Group) : Conversation()

enum class Version { V1, V2, GROUP }

// This indicates whether this a v1 or v2 conversation.
val version: Version
get() {
return when (this) {
is V1 -> Version.V1
is V2 -> Version.V2
is Group -> Version.GROUP
}
}

Expand All @@ -41,6 +44,7 @@ sealed class Conversation {
return when (this) {
is V1 -> conversationV1.sentAt
is V2 -> conversationV2.createdAt
is Group -> TODO()
}
}

Expand All @@ -50,6 +54,7 @@ sealed class Conversation {
return when (this) {
is V1 -> conversationV1.peerAddress
is V2 -> conversationV2.peerAddress
is Group -> TODO()
}
}

Expand All @@ -60,6 +65,7 @@ sealed class Conversation {
return when (this) {
is V1 -> null
is V2 -> conversationV2.context.conversationId
is Group -> null
}
}

Expand All @@ -68,13 +74,15 @@ sealed class Conversation {
return when (this) {
is V1 -> null
is V2 -> conversationV2.keyMaterial
is Group -> null
}
}

fun consentState(): ConsentState {
val client: Client = when (this) {
is V1 -> conversationV1.client
is V2 -> conversationV2.client
is Group -> group.client
}
return client.contacts.consentList.state(address = peerAddress)
}
Expand All @@ -99,13 +107,15 @@ sealed class Conversation {
.setKeyMaterial(conversationV2.keyMaterial.toByteString()),
),
).build()
is Group -> TODO()
}
}

fun decode(envelope: Envelope): DecodedMessage {
return when (this) {
is V1 -> conversationV1.decode(envelope)
is V2 -> conversationV2.decodeEnvelope(envelope)
is Group -> TODO()
}
}

Expand All @@ -127,6 +137,8 @@ sealed class Conversation {
is V2 -> {
conversationV2.prepareMessage(content = content, options = options)
}

is Group -> TODO()
}
}

Expand All @@ -142,34 +154,40 @@ sealed class Conversation {
is V2 -> {
conversationV2.prepareMessage(encodedContent = encodedContent, options = options)
}

is Group -> TODO()
}
}

fun send(prepared: PreparedMessage): String {
return when (this) {
is V1 -> conversationV1.send(prepared = prepared)
is V2 -> conversationV2.send(prepared = prepared)
is Group -> TODO()
}
}

fun <T> send(content: T, options: SendOptions? = null): String {
return when (this) {
is V1 -> conversationV1.send(content = content, options = options)
is V2 -> conversationV2.send(content = content, options = options)
is Group -> TODO()
}
}

fun send(text: String, sendOptions: SendOptions? = null, sentAt: Date? = null): String {
return when (this) {
is V1 -> conversationV1.send(text = text, sendOptions, sentAt)
is V2 -> conversationV2.send(text = text, sendOptions, sentAt)
is Group -> TODO()
}
}

fun send(encodedContent: EncodedContent, options: SendOptions? = null): String {
return when (this) {
is V1 -> conversationV1.send(encodedContent = encodedContent, options = options)
is V2 -> conversationV2.send(encodedContent = encodedContent, options = options)
is Group -> TODO()
}
}

Expand All @@ -184,6 +202,7 @@ sealed class Conversation {
return when (this) {
is V1 -> conversationV1.topic.description
is V2 -> conversationV2.topic
is Group -> TODO()
}
}

Expand Down Expand Up @@ -221,6 +240,8 @@ sealed class Conversation {
after = after,
direction = direction,
)

is Group -> group.messages()
}
}

Expand All @@ -233,6 +254,7 @@ sealed class Conversation {
return when (this) {
is V1 -> conversationV1.decryptedMessages(limit, before, after, direction)
is V2 -> conversationV2.decryptedMessages(limit, before, after, direction)
is Group -> TODO()
}
}

Expand All @@ -242,6 +264,7 @@ sealed class Conversation {
return when (this) {
is V1 -> conversationV1.decrypt(envelope)
is V2 -> conversationV2.decrypt(envelope)
is Group -> TODO()
}
}

Expand All @@ -251,6 +274,7 @@ sealed class Conversation {
return when (this) {
is V1 -> conversationV1.client
is V2 -> conversationV2.client
is Group -> group.client
}
}

Expand All @@ -262,20 +286,23 @@ sealed class Conversation {
return when (this) {
is V1 -> conversationV1.streamMessages()
is V2 -> conversationV2.streamMessages()
is Group -> TODO()
}
}

fun streamDecryptedMessages(): Flow<DecryptedMessage> {
return when (this) {
is V1 -> conversationV1.streamDecryptedMessages()
is V2 -> conversationV2.streamDecryptedMessages()
is Group -> TODO()
}
}

fun streamEphemeral(): Flow<Envelope> {
return when (this) {
is V1 -> return conversationV1.streamEphemeral()
is V2 -> return conversationV2.streamEphemeral()
is Group -> TODO()
}
}
}
20 changes: 18 additions & 2 deletions library/src/main/java/org/xmtp/android/library/Conversations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ import org.xmtp.proto.keystore.api.v1.Keystore.TopicMap.TopicData
import org.xmtp.proto.message.contents.Contact
import org.xmtp.proto.message.contents.Invitation
import org.xmtp.android.library.messages.DecryptedMessage
import uniffi.xmtp_dh.FfiConversations
import java.util.Date

data class Conversations(
var client: Client,
var conversationsByTopic: MutableMap<String, Conversation> = mutableMapOf(),
val libXMTPConversations: FfiConversations? = null,
) {

companion object {
Expand Down Expand Up @@ -81,6 +83,17 @@ data class Conversations(
)
}

suspend fun newGroup(accountAddress: String): Group {
val group = libXMTPConversations?.createGroup(accountAddress)
?: throw XMTPException("Client does not support Groups")
return Group(client, group)
}

suspend fun listGroups(): List<Group> {
return libXMTPConversations?.list()?.map { Group(client, it) }
?: throw XMTPException("Client does not support Groups")
}

/**
* This creates a new [Conversation] using a specified address
* @param peerAddress The address of the client that you want to start a new conversation
Expand Down Expand Up @@ -174,7 +187,7 @@ data class Conversations(
* Get the list of conversations that current user has
* @return The list of [Conversation] that the current [Client] has.
*/
fun list(): List<Conversation> {
fun list(includeGroups: Boolean = false): List<Conversation> {
val newConversations = mutableListOf<Conversation>()
val mostRecent = conversationsByTopic.values.maxOfOrNull { it.createdAt }
val pagination = Pagination(after = mostRecent)
Expand Down Expand Up @@ -203,7 +216,10 @@ data class Conversations(
it.peerAddress != client.address && Topic.isValidTopic(it.topic)
}.map { Pair(it.topic, it) }

// TODO(perf): use DB to persist + sort
if (includeGroups) {
val groups = runBlocking { listGroups() }
// conversationsByTopic += groups.map { Pair(it.id(), Conversation.Group(it) }
}
return conversationsByTopic.values.sortedByDescending { it.createdAt }
}

Expand Down
26 changes: 26 additions & 0 deletions library/src/main/java/org/xmtp/android/library/Group.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.xmtp.android.library

import org.xmtp.android.library.libxmtp.Message
import uniffi.xmtp_dh.FfiGroup
import uniffi.xmtp_dh.FfiListMessagesOptions

class Group(val client: Client, val libXMTPGroup: FfiGroup) {
val id: List<UByte>
get() = libXMTPGroup.id()

suspend fun send(text: String) {
libXMTPGroup.send(contentBytes = text.toByteArray(Charsets.UTF_8).toUByteArray().toList())
}

suspend fun messages(): List<Message> {
return libXMTPGroup.findMessages(
opts = FfiListMessagesOptions(
sentBeforeNs = null,
sentAfterNs = null,
limit = null
)
).map {
Message(client, it)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package uniffi.xmtp_dh.org.xmtp.android.library.libxmtp

import uniffi.xmtp_dh.FfiInboxOwner

interface InboxOwner : FfiInboxOwner {}
16 changes: 16 additions & 0 deletions library/src/main/java/org/xmtp/android/library/libxmtp/Message.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.xmtp.android.library.libxmtp

import org.xmtp.android.library.Client
import uniffi.xmtp_dh.FfiMessage

data class Message(val client: Client, val libXMTPMessage: FfiMessage) {
val id: ByteArray
get() = libXMTPMessage.id

val senderAddress: String
get() = libXMTPMessage.addrFrom

fun text(): String {
return libXMTPMessage.content.decodeToString()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package uniffi.xmtp_dh.org.xmtp.android.library.libxmtp

import android.util.Log
import uniffi.xmtp_dh.FfiLogger

class XMTPLogger : FfiLogger {
override fun log(level: UInt, levelLabel: String, message: String) {
Log.i("$level $levelLabel", message)
}
}
Loading

0 comments on commit fcb9921

Please sign in to comment.