Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/xmtp/xmtp-android into np/v…
Browse files Browse the repository at this point in the history
…3-consent-upgrades
  • Loading branch information
nplasterer committed Nov 6, 2024
2 parents de2ef93 + 3b17b28 commit 3f3ae1d
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import org.xmtp.proto.message.contents.Invitation
import org.xmtp.proto.message.contents.Invitation.InvitationV1.Context
import java.nio.charset.StandardCharsets
import java.util.Date
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.DurationUnit

@RunWith(AndroidJUnit4::class)
class ConversationTest {
Expand Down Expand Up @@ -418,7 +420,12 @@ class ConversationTest {
val messages = aliceConversation.messages(limit = 1)
assertEquals(1, messages.size)
assertEquals("hey alice 3", messages[0].body)
val messages2 = aliceConversation.messages(limit = 1, after = date)
val messages2 = aliceConversation.messages(
limit = 1,
afterNs = date.time.nanoseconds.toLong(
DurationUnit.NANOSECONDS
)
)
assertEquals(1, messages2.size)
assertEquals("hey alice 3", messages2[0].body)
val messagesAsc =
Expand Down
64 changes: 64 additions & 0 deletions library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,46 @@ class GroupTest {
runBlocking { Client().createV3(account = davonV3Wallet, options = options) }
}

@Test
fun testsCanDualSendConversations() {
val v2Convo = runBlocking { alixClient.conversations.newConversation(bo.walletAddress) }
runBlocking {
alixClient.conversations.syncConversations()
boClient.conversations.syncConversations()
}
val alixDm = runBlocking { alixClient.findDm(bo.walletAddress) }
val boDm = runBlocking { boClient.findDm(alix.walletAddress) }

assertEquals(alixDm?.id, boDm?.id)
assertEquals(runBlocking { alixClient.conversations.list().size }, 1)
assertEquals(runBlocking { alixClient.conversations.listDms().size }, 1)
assertEquals(runBlocking { boClient.conversations.listDms().size }, 1)
assertEquals(runBlocking { boClient.conversations.list().size }, 1)
assertEquals(v2Convo.topic, runBlocking { boClient.conversations.list().first().topic })
}

@Test
fun testsCanDualSendMessages() {
val alixV2Convo = runBlocking { alixClient.conversations.newConversation(bo.walletAddress) }
val boV2Convo = runBlocking { boClient.conversations.list().first() }
runBlocking { boClient.conversations.syncConversations() }
val alixDm = runBlocking { alixClient.findDm(bo.walletAddress) }
val boDm = runBlocking { boClient.findDm(alix.walletAddress) }

runBlocking { alixV2Convo.send("first") }
runBlocking { boV2Convo.send("second") }

runBlocking {
alixDm?.sync()
boDm?.sync()
}

assertEquals(runBlocking { alixV2Convo.messages().size }, 2)
assertEquals(runBlocking { alixV2Convo.messages().size }, runBlocking { boV2Convo.messages().size })
assertEquals(boDm?.messages()?.size, 2)
assertEquals(alixDm?.messages()?.size, 3) // We send the group membership update to the dm
}

@Test
fun testCanCreateAGroupWithDefaultPermissions() {
val boGroup = runBlocking {
Expand Down Expand Up @@ -545,6 +585,30 @@ class GroupTest {
assertEquals(sameGroup.messages(deliveryStatus = MessageDeliveryStatus.PUBLISHED).size, 2)
}

@Test
fun testCanListGroupMessagesAfter() {
val group = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress)) }
val messageId = runBlocking {
group.send("howdy")
group.send("gm")
}
val message = boClient.findMessage(messageId)
assertEquals(group.messages().size, 3)
assertEquals(group.messages(afterNs = message?.sentAtNs).size, 0)
runBlocking {
group.send("howdy")
group.send("gm")
}
assertEquals(group.messages().size, 5)
assertEquals(group.messages(afterNs = message?.sentAtNs).size, 2)

runBlocking { alixClient.conversations.syncConversations() }
val sameGroup = runBlocking { alixClient.conversations.listGroups().last() }
runBlocking { sameGroup.sync() }
assertEquals(sameGroup.messages().size, 4)
assertEquals(sameGroup.messages(afterNs = message?.sentAtNs).size, 2)
}

@Test
fun testCanSendContentTypesToGroup() {
Client.register(codec = ReactionCodec())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import org.xmtp.proto.message.contents.PrivateKeyOuterClass
import org.xmtp.proto.message.contents.PrivateKeyOuterClass.PrivateKeyBundle
import uniffi.xmtpv3.createV2Client
import java.util.Date
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.DurationUnit

@RunWith(AndroidJUnit4::class)
class LocalInstrumentedTest {
Expand Down Expand Up @@ -163,10 +165,19 @@ class LocalInstrumentedTest {
assertEquals(2, messagesLimit.size)
val nowMessage = messages[0]
assertEquals("now", nowMessage.body)
val messages2 = convo.messages(limit = 1, before = nowMessage.sent)
val messages2 = convo.messages(
limit = 1,
beforeNs = nowMessage.sent.time.nanoseconds.toLong(
DurationUnit.NANOSECONDS
)
)
val tenSecondsAgoMessage = messages2[0]
assertEquals("now first", tenSecondsAgoMessage.body)
val messages3 = convo.messages(after = tenSecondsAgoMessage.sent)
val messages3 = convo.messages(
afterNs = tenSecondsAgoMessage.sent.time.nanoseconds.toLong(
DurationUnit.NANOSECONDS
)
)
val nowMessage2 = messages3[0]
assertEquals("now", nowMessage2.body)
val messagesAsc =
Expand Down
51 changes: 36 additions & 15 deletions library/src/main/java/org/xmtp/android/library/Conversation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -169,50 +169,71 @@ sealed class Conversation {
*/
suspend fun messages(
limit: Int? = null,
before: Date? = null,
after: Date? = null,
beforeNs: Long? = null,
afterNs: Long? = null,
direction: PagingInfoSortDirection = MessageApiOuterClass.SortDirection.SORT_DIRECTION_DESCENDING,
): List<DecodedMessage> {
return when (this) {
is V1 -> conversationV1.messages(
limit = limit,
before = before,
after = after,
before = beforeNs?.let { Date(it / 1_000_000) },
after = afterNs?.let { Date(it / 1_000_000) },
direction = direction,
)

is V2 ->
conversationV2.messages(
limit = limit,
before = before,
after = after,
before = beforeNs?.let { Date(it / 1_000_000) },
after = afterNs?.let { Date(it / 1_000_000) },
direction = direction,
)

is Group -> {
group.messages(
limit = limit,
before = before,
after = after,
beforeNs = beforeNs,
afterNs = afterNs,
direction = direction,
)
}

is Dm -> dm.messages(limit, before, after, direction)
is Dm -> dm.messages(limit, beforeNs, afterNs, direction)
}
}

suspend fun decryptedMessages(
limit: Int? = null,
before: Date? = null,
after: Date? = null,
beforeNs: Long? = null,
afterNs: Long? = null,
direction: PagingInfoSortDirection = MessageApiOuterClass.SortDirection.SORT_DIRECTION_DESCENDING,
): List<DecryptedMessage> {
return when (this) {
is V1 -> conversationV1.decryptedMessages(limit, before, after, direction)
is V2 -> conversationV2.decryptedMessages(limit, before, after, direction)
is Group -> group.decryptedMessages(limit, before, after, direction)
is Dm -> dm.decryptedMessages(limit, before, after, direction)
is V1 -> conversationV1.decryptedMessages(
limit = limit,
before = beforeNs?.let { Date(it / 1_000_000) },
after = afterNs?.let { Date(it / 1_000_000) },
direction = direction,
)

is V2 ->
conversationV2.decryptedMessages(
limit = limit,
before = beforeNs?.let { Date(it / 1_000_000) },
after = afterNs?.let { Date(it / 1_000_000) },
direction = direction,
)

is Group -> {
group.decryptedMessages(
limit = limit,
beforeNs = beforeNs,
afterNs = afterNs,
direction = direction,
)
}

is Dm -> dm.decryptedMessages(limit, beforeNs, afterNs, direction)
}
}

Expand Down
12 changes: 11 additions & 1 deletion library/src/main/java/org/xmtp/android/library/ConversationV1.kt
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,16 @@ data class ConversationV1(
}

suspend fun send(prepared: PreparedMessage): String {
if (client.v3Client != null) {
try {
val dm = client.conversations.findOrCreateDm(peerAddress)
prepared.encodedContent?.let {
dm.send(it)
}
} catch (e: Exception) {
Log.e("ConversationV1 send", e.message.toString())
}
}
client.publish(envelopes = prepared.envelopes)
if (client.contacts.consentList.state(address = peerAddress) == ConsentState.UNKNOWN) {
client.contacts.allow(addresses = listOf(peerAddress))
Expand Down Expand Up @@ -274,7 +284,7 @@ data class ConversationV1(
)
client.contacts.hasIntroduced[peerAddress] = true
}
return PreparedMessage(envelopes)
return PreparedMessage(envelopes, encodedContent)
}

private fun generateId(envelope: Envelope): String =
Expand Down
12 changes: 11 additions & 1 deletion library/src/main/java/org/xmtp/android/library/ConversationV2.kt
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,16 @@ data class ConversationV2(
}

suspend fun send(prepared: PreparedMessage): String {
if (client.v3Client != null) {
try {
val dm = client.conversations.findOrCreateDm(peerAddress)
prepared.encodedContent?.let {
dm.send(it)
}
} catch (e: Exception) {
Log.e("ConversationV1 send", e.message.toString())
}
}
client.publish(envelopes = prepared.envelopes)
if (client.contacts.consentList.state(address = peerAddress) == ConsentState.UNKNOWN) {
client.contacts.allow(addresses = listOf(peerAddress))
Expand Down Expand Up @@ -275,7 +285,7 @@ data class ConversationV2(
timestamp = Date(),
message = MessageBuilder.buildFromMessageV2(v2 = message.messageV2).toByteArray(),
)
return PreparedMessage(listOf(envelope))
return PreparedMessage(listOf(envelope), encodedContent)
}

private fun generateId(envelope: Envelope): String =
Expand Down
15 changes: 7 additions & 8 deletions library/src/main/java/org/xmtp/android/library/Conversations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ data class Conversations(
}

suspend fun findOrCreateDm(peerAddress: String): Dm {
if (client.hasV2Client) throw XMTPException("Only supported for V3 only clients.")
if (peerAddress.lowercase() == client.address.lowercase()) {
throw XMTPException("Recipient is sender")
}
Expand Down Expand Up @@ -301,6 +300,13 @@ data class Conversations(
client.contacts.allow(addresses = listOf(peerAddress))
val conversation = Conversation.V2(conversationV2)
conversationsByTopic[conversation.topic] = conversation
if (client.v3Client != null) {
try {
client.conversations.findOrCreateDm(peerAddress)
} catch (e: Exception) {
Log.e("newConversation", e.message.toString())
}
}
return conversation
}

Expand Down Expand Up @@ -330,7 +336,6 @@ data class Conversations(
limit: Int? = null,
consentState: ConsentState? = null,
): List<Dm> {
if (client.hasV2Client) throw XMTPException("Only supported for V3 only clients.")
val ffiDms = libXMTPConversations?.listDms(
opts = FfiListConversationsOptions(
after?.time?.nanoseconds?.toLong(DurationUnit.NANOSECONDS),
Expand All @@ -352,9 +357,6 @@ data class Conversations(
order: ConversationOrder = ConversationOrder.CREATED_AT,
consentState: ConsentState? = null,
): List<Conversation> {
if (client.hasV2Client)
throw XMTPException("Only supported for V3 only clients.")

val ffiConversations = libXMTPConversations?.list(
FfiListConversationsOptions(
after?.time?.nanoseconds?.toLong(DurationUnit.NANOSECONDS),
Expand Down Expand Up @@ -553,7 +555,6 @@ data class Conversations(
}

fun streamConversations(): Flow<Conversation> = callbackFlow {
if (client.hasV2Client) throw XMTPException("Only supported for V3 only clients.")
val conversationCallback = object : FfiConversationCallback {
override fun onConversation(conversation: FfiConversation) {
if (conversation.groupMetadata().conversationType() == "dm") {
Expand Down Expand Up @@ -656,7 +657,6 @@ data class Conversations(
}

fun streamAllConversationMessages(): Flow<DecodedMessage> = callbackFlow {
if (client.hasV2Client) throw XMTPException("Only supported for V3 only clients.")
val messageCallback = object : FfiMessageCallback {
override fun onMessage(message: FfiMessage) {
val conversation = client.findConversation(message.convoId.toHex())
Expand Down Expand Up @@ -684,7 +684,6 @@ data class Conversations(
}

fun streamAllConversationDecryptedMessages(): Flow<DecryptedMessage> = callbackFlow {
if (client.hasV2Client) throw XMTPException("Only supported for V3 only clients.")
val messageCallback = object : FfiMessageCallback {
override fun onMessage(message: FfiMessage) {
val conversation = client.findConversation(message.convoId.toHex())
Expand Down
18 changes: 8 additions & 10 deletions library/src/main/java/org/xmtp/android/library/Dm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import uniffi.xmtpv3.FfiMessage
import uniffi.xmtpv3.FfiMessageCallback
import uniffi.xmtpv3.FfiSubscribeException
import java.util.Date
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.DurationUnit

class Dm(val client: Client, private val libXMTPGroup: FfiConversation) {
val id: String
Expand Down Expand Up @@ -103,15 +101,15 @@ class Dm(val client: Client, private val libXMTPGroup: FfiConversation) {

fun messages(
limit: Int? = null,
before: Date? = null,
after: Date? = null,
beforeNs: Long? = null,
afterNs: Long? = null,
direction: PagingInfoSortDirection = SortDirection.SORT_DIRECTION_DESCENDING,
deliveryStatus: MessageDeliveryStatus = MessageDeliveryStatus.ALL,
): List<DecodedMessage> {
return libXMTPGroup.findMessages(
opts = FfiListMessagesOptions(
sentBeforeNs = before?.time?.nanoseconds?.toLong(DurationUnit.NANOSECONDS),
sentAfterNs = after?.time?.nanoseconds?.toLong(DurationUnit.NANOSECONDS),
sentBeforeNs = beforeNs,
sentAfterNs = afterNs,
limit = limit?.toLong(),
deliveryStatus = when (deliveryStatus) {
MessageDeliveryStatus.PUBLISHED -> FfiDeliveryStatus.PUBLISHED
Expand All @@ -131,15 +129,15 @@ class Dm(val client: Client, private val libXMTPGroup: FfiConversation) {

fun decryptedMessages(
limit: Int? = null,
before: Date? = null,
after: Date? = null,
beforeNs: Long? = null,
afterNs: Long? = null,
direction: PagingInfoSortDirection = SortDirection.SORT_DIRECTION_DESCENDING,
deliveryStatus: MessageDeliveryStatus = MessageDeliveryStatus.ALL,
): List<DecryptedMessage> {
return libXMTPGroup.findMessages(
opts = FfiListMessagesOptions(
sentBeforeNs = before?.time?.nanoseconds?.toLong(DurationUnit.NANOSECONDS),
sentAfterNs = after?.time?.nanoseconds?.toLong(DurationUnit.NANOSECONDS),
sentBeforeNs = beforeNs,
sentAfterNs = afterNs,
limit = limit?.toLong(),
deliveryStatus = when (deliveryStatus) {
MessageDeliveryStatus.PUBLISHED -> FfiDeliveryStatus.PUBLISHED
Expand Down
Loading

0 comments on commit 3f3ae1d

Please sign in to comment.