Skip to content

Commit bb00599

Browse files
committed
feat: chat stream ping pong and chat creation from tips
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 0b0b8ab commit bb00599

17 files changed

Lines changed: 704 additions & 272 deletions

File tree

api/schemas/com.getcode.db.AppDatabase/12.json

Lines changed: 406 additions & 0 deletions
Large diffs are not rendered by default.

api/src/main/java/com/getcode/db/AppDatabase.kt

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package com.getcode.db
33
import android.content.Context
44
import androidx.room.AutoMigration
55
import androidx.room.Database
6+
import androidx.room.DeleteColumn
67
import androidx.room.DeleteTable
8+
import androidx.room.RenameColumn
79
import androidx.room.Room
810
import androidx.room.RoomDatabase
911
import androidx.room.TypeConverters
@@ -35,13 +37,15 @@ import java.io.File
3537
Conversation::class,
3638
ConversationMessage::class,
3739
ConversationMessageRemoteKey::class,
40+
ConversationIntentIdReference::class,
3841
],
3942
autoMigrations = [
4043
AutoMigration(from = 7, to = 8, spec = AppDatabase.Migration7To8::class),
4144
AutoMigration(from = 8, to = 9, spec = AppDatabase.Migration8To9::class),
4245
AutoMigration(from = 10, to = 11, spec = AppDatabase.Migration10To11::class),
46+
AutoMigration(from = 11, to = 12, spec = AppDatabase.Migration11To12::class),
4347
],
44-
version = 11
48+
version = 12
4549
)
4650
@TypeConverters(Converters::class)
4751
abstract class AppDatabase : RoomDatabase() {
@@ -55,14 +59,42 @@ abstract class AppDatabase : RoomDatabase() {
5559
abstract fun conversationDao(): ConversationDao
5660
abstract fun conversationMessageDao(): ConversationMessageDao
5761
abstract fun conversationMessageRemoteKeyDao(): ConversationMessageRemoteKeyDao
62+
abstract fun conversationIntentMappingDao(): ConversationIntentMappingDao
5863

5964
@DeleteTable(tableName = "HistoricalTransaction")
6065
class Migration7To8 : AutoMigrationSpec
6166

6267
@DeleteTable(tableName = "SendLimit")
6368
class Migration8To9 : AutoMigrationSpec
6469

65-
class Migration10To11: Migration(10, 11), AutoMigrationSpec {
70+
class Migration10To11 : Migration(10, 11), AutoMigrationSpec {
71+
override fun migrate(db: SupportSQLiteDatabase) {
72+
db.execSQL("DROP TABLE messages")
73+
}
74+
}
75+
76+
@RenameColumn.Entries(
77+
RenameColumn(
78+
tableName = "conversations",
79+
fromColumnName = "messageIdBase58",
80+
toColumnName = "idBase58"
81+
)
82+
)
83+
@DeleteColumn.Entries(
84+
DeleteColumn(
85+
tableName = "conversations",
86+
columnName = "cursorBase58"
87+
),
88+
DeleteColumn(
89+
tableName = "conversations",
90+
columnName = "tipAmount"
91+
),
92+
DeleteColumn(
93+
tableName = "conversations",
94+
columnName = "createdByUser"
95+
)
96+
)
97+
class Migration11To12 : Migration(11, 12), AutoMigrationSpec {
6698
override fun migrate(db: SupportSQLiteDatabase) {
6799
db.execSQL("DROP TABLE messages")
68100
}
@@ -92,10 +124,6 @@ object Database {
92124
.fallbackToDestructiveMigration()
93125
.build()
94126

95-
instance?.conversationDao()?.clearConversations()
96-
instance?.conversationMessageDao()?.clearMessages()
97-
instance?.conversationMessageRemoteKeyDao()?.clearRemoteKeys()
98-
99127
isInitSubject.onNext(true)
100128
trace("database init end", type = TraceType.Process)
101129
}

api/src/main/java/com/getcode/db/ConversationDao.kt

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,25 @@ interface ConversationDao {
1818
suspend fun upsertConversations(vararg conversation: Conversation)
1919

2020
@Transaction
21-
@Query("SELECT * FROM conversations WHERE messageIdBase58 = :id")
21+
@Query("SELECT * FROM conversations WHERE idBase58 = :id")
2222
fun observeConversationWithMessages(id: String): Flow<ConversationWithMessages>
2323

24-
fun observeConversationWithMessages(messageId: ID): Flow<ConversationWithMessages> {
25-
return observeConversationWithMessages(messageId.base58)
24+
fun observeConversationWithMessages(id: ID): Flow<ConversationWithMessages> {
25+
return observeConversationWithMessages(id.base58)
2626
}
2727

28-
@Query("SELECT * FROM conversations WHERE messageIdBase58 = :messageId")
29-
fun observeConversation(messageId: String): Flow<Conversation?>
28+
@Query("SELECT * FROM conversations WHERE idBase58 = :id")
29+
fun observeConversation(id: String): Flow<Conversation?>
3030

31-
fun observeConversation(messageId: ID): Flow<Conversation?> {
32-
return observeConversation(messageId.base58)
31+
fun observeConversation(id: ID): Flow<Conversation?> {
32+
return observeConversation(id.base58)
3333
}
3434

35-
@Query("SELECT * FROM conversations WHERE messageIdBase58 = :messageId")
36-
fun observeConversationForMessage(messageId: String): Flow<Conversation?>
35+
@Query("SELECT * FROM conversations WHERE idBase58 = :id")
36+
suspend fun findConversation(id: String): Conversation?
3737

38-
fun observeConversationForMessage(messageId: ID): Flow<Conversation?> {
39-
return observeConversationForMessage(messageId.base58)
40-
}
41-
42-
@Query("SELECT * FROM conversations WHERE messageIdBase58 = :messageId")
43-
suspend fun findConversation(messageId: String): Conversation?
44-
45-
suspend fun findConversation(messageId: ID): Conversation? {
46-
return findConversation(messageId.base58)
47-
}
48-
49-
@Query("SELECT * FROM conversations WHERE messageIdBase58 = :messageId")
50-
suspend fun findConversationForMessage(messageId: String): Conversation?
51-
52-
suspend fun findConversationForMessage(messageId: ID): Conversation? {
53-
return findConversationForMessage(messageId.base58)
38+
suspend fun findConversation(id: ID): Conversation? {
39+
return findConversation(id.base58)
5440
}
5541

5642
@Query("SELECT * FROM conversations")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.getcode.db
2+
3+
import androidx.room.Dao
4+
import androidx.room.Insert
5+
import androidx.room.OnConflictStrategy
6+
import androidx.room.Query
7+
import com.getcode.model.ConversationIntentIdReference
8+
import com.getcode.model.ID
9+
import com.getcode.network.repository.base58
10+
11+
@Dao
12+
interface ConversationIntentMappingDao {
13+
@Insert(onConflict = OnConflictStrategy.REPLACE)
14+
suspend fun insert(mapping: ConversationIntentIdReference)
15+
16+
@Query("SELECT * FROM conversation_intent_id_mapping WHERE intentIdBase58 = :id")
17+
suspend fun conversationIdByReference(id: String): ConversationIntentIdReference?
18+
19+
suspend fun conversationIdByReference(id: ID): ConversationIntentIdReference? {
20+
return conversationIdByReference(id.base58)
21+
}
22+
23+
@Query("DELETE FROM conversation_intent_id_mapping")
24+
suspend fun clearMapping()
25+
}

api/src/main/java/com/getcode/db/ConversationMessageRemoteKeyDao.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import com.getcode.network.repository.base58
1111
@Dao
1212
interface ConversationMessageRemoteKeyDao {
1313
@Insert(onConflict = OnConflictStrategy.REPLACE)
14-
fun insertAll(remoteKey: List<ConversationMessageRemoteKey>)
14+
suspend fun insertAll(remoteKey: List<ConversationMessageRemoteKey>)
1515
@Query("SELECT * FROM messages_remote_keys WHERE messageIdBase58 = :id")
16-
fun remoteKeysByMessageId(id: String): ConversationMessageRemoteKey?
16+
suspend fun remoteKeysByMessageId(id: String): ConversationMessageRemoteKey?
1717

18-
fun remoteKeysByMessageId(id: ID): ConversationMessageRemoteKey? {
18+
suspend fun remoteKeysByMessageId(id: ID): ConversationMessageRemoteKey? {
1919
return remoteKeysByMessageId(id.base58)
2020
}
2121

api/src/main/java/com/getcode/mapper/ChatMemberMapper.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import javax.inject.Inject
1111
class ChatMemberMapper @Inject constructor(): Mapper<ChatService.ChatMember, ChatMember?> {
1212
override fun map(from: ChatService.ChatMember): ChatMember? {
1313
return ChatMember(
14-
id = from.memberId.toByteArray().toList().uuid ?: return null,
14+
id = from.memberId.value.toByteArray().toList().uuid ?: return null,
1515
identity = runCatching { Identity(from.identity) }.getOrNull(),
1616
isMuted = from.isMuted,
1717
isSelf = from.isSelf,

api/src/main/java/com/getcode/mapper/ChatMessageV2Mapper.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ class ChatMessageV2Mapper @Inject constructor(
1717
override fun map(from: Pair<Chat, ApiChatMessage>): ChatMessage {
1818
val (chat, message) = from
1919

20-
val messageId = message.messageId.toByteArray().toList()
21-
val messageSenderId = message.senderId.toByteArray().toList()
20+
val messageId = message.messageId.value.toByteArray().toList()
21+
val messageSenderId = message.senderId.value.toByteArray().toList()
2222
val selfMember = chat.members.firstOrNull { it.isSelf }
2323
val isFromSelf = selfMember?.id == messageSenderId.uuid
2424
val pointers = chat.members.firstOrNull { it.id == messageSenderId.uuid }?.pointers

api/src/main/java/com/getcode/mapper/ConversationMapper.kt

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,21 @@ import com.getcode.model.chat.ChatMessage
88
import com.getcode.model.chat.MessageContent
99
import com.getcode.model.orOneToOne
1010
import com.getcode.network.exchange.Exchange
11+
import com.getcode.network.localized
1112
import com.getcode.network.repository.base58
13+
import com.getcode.util.resources.ResourceHelper
1214
import javax.inject.Inject
1315

1416
class ConversationMapper @Inject constructor(
15-
private val exchange: Exchange,
16-
) : Mapper<Pair<Chat, ChatMessage>, Conversation> {
17-
override fun map(from: Pair<Chat, ChatMessage>): Conversation {
18-
val (chat, message) = from
19-
val exchangeMessage = message.contents.firstOrNull {
20-
it is MessageContent.Exchange
21-
} as? MessageContent.Exchange
17+
private val resources: ResourceHelper,
18+
) : Mapper<Chat, Conversation> {
19+
override fun map(from: Chat): Conversation {
2220

23-
val tipAmount = if (exchangeMessage != null) {
24-
val rate = exchange.rateFor(exchangeMessage.amount.currencyCode).orOneToOne()
25-
exchangeMessage.amount.amountUsing(rate)
26-
} else {
27-
KinAmount.newInstance(0, Rate.oneToOne)
28-
}
29-
30-
val identity = chat.members.filterNot { it.isSelf }.firstNotNullOfOrNull { it.identity }
21+
val identity = from.members.filterNot { it.isSelf }.firstNotNullOfOrNull { it.identity }
3122

3223
return Conversation(
33-
messageIdBase58 = chat.id.base58,
34-
cursorBase58 = chat.cursor.base58,
35-
tipAmount = tipAmount,
36-
createdByUser = true, // only tippee can create a conversation
24+
idBase58 = from.id.base58,
25+
title = from.title.localized(resources),
3726
hasRevealedIdentity = identity != null,
3827
lastActivity = null, // TODO: ?
3928
user = identity?.username,

api/src/main/java/com/getcode/model/Conversation.kt

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,22 @@ import kotlinx.serialization.json.Json
1717
@Entity(tableName = "conversations")
1818
data class Conversation(
1919
@PrimaryKey
20-
val messageIdBase58: String,
21-
val cursorBase58: String,
22-
val tipAmount: KinAmount,
23-
val createdByUser: Boolean, // if this conversation was created as a result of the user messaging the tipper.,
20+
val idBase58: String,
21+
@ColumnInfo(defaultValue = "Tip Chat")
22+
val title: String,
2423
val hasRevealedIdentity: Boolean,
2524
val user: String?,
2625
val userImage: String?,
2726
val lastActivity: Long?,
2827
) {
2928
@Ignore
30-
val messageId: ID = Base58.decode(messageIdBase58).toList()
31-
@Ignore
32-
val cursor: Cursor = Base58.decode(cursorBase58).toList()
29+
val id: ID = Base58.decode(idBase58).toList()
3330

3431
override fun toString(): String {
3532
return """
3633
{
37-
messageId:${messageIdBase58},
38-
tipAmount:$tipAmount,
39-
createByUser:$createdByUser,
34+
id:${idBase58},
35+
title:$title,
4036
hasRevealedIdentity:$hasRevealedIdentity,
4137
user:$user,
4238
}
@@ -68,7 +64,7 @@ data class ConversationMessage(
6864
data class ConversationWithMessages(
6965
@Embedded val user: Conversation,
7066
@Relation(
71-
parentColumn = "messageIdBase58",
67+
parentColumn = "idBase58",
7268
entityColumn = "conversationIdBase58"
7369
)
7470
val messages: List<ConversationMessage>,
@@ -89,6 +85,18 @@ data class ConversationMessageRemoteKey(
8985
val nextCursor: Cursor? = nextCursorBase58?.let { Base58.decode(it).toList() }
9086
}
9187

88+
@Entity(tableName = "conversation_intent_id_mapping")
89+
data class ConversationIntentIdReference(
90+
@PrimaryKey
91+
val conversationIdBase58: String,
92+
val intentIdBase58: String,
93+
) {
94+
@Ignore
95+
val conversationId: ID = Base58.decode(conversationIdBase58).toList()
96+
@Ignore
97+
val intentId: ID = Base58.decode(intentIdBase58).toList()
98+
}
99+
92100
sealed interface ConversationMessageContent {
93101
val kind: Int
94102
val isFromSelf: Boolean
@@ -103,7 +111,7 @@ sealed interface ConversationMessageContent {
103111
}
104112

105113
@Serializable
106-
data class TipMessage(override val isFromSelf: Boolean = false) : ConversationMessageContent {
114+
data class TipMessage(override val isFromSelf: Boolean = false, val kinAmount: KinAmount) : ConversationMessageContent {
107115
override val kind: Int = 1
108116
}
109117
@Serializable
@@ -129,9 +137,9 @@ sealed interface ConversationMessageContent {
129137
is IdentityRevealed,
130138
is IdentityRevealedToYou,
131139
is ThanksReceived,
132-
is ThanksSent,
140+
is ThanksSent -> "$kind|${javaClass.simpleName}"
133141
is TipMessage -> {
134-
"$kind|${javaClass.simpleName}"
142+
"$kind|${Json.encodeToString(this)}"
135143
}
136144
is Text -> "$kind|${Json.encodeToString(this)}"
137145
}
@@ -144,7 +152,7 @@ sealed interface ConversationMessageContent {
144152
val (kind, data) = string.split("|")
145153
return when (kind.toInt()) {
146154
0 -> Json.decodeFromString<Text>(data)
147-
1 -> TipMessage()
155+
1 -> Json.decodeFromString<TipMessage>(data)
148156
2 -> ThanksSent()
149157
3 -> ThanksReceived()
150158
4 -> IdentityRevealed()

api/src/main/java/com/getcode/model/chat/MessageContent.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,14 @@ sealed interface MessageContent {
131131
ChatService.Content.TypeCase.THANK_YOU -> {
132132
ThankYou(
133133
isFromSelf = isFromSelf,
134-
tipIntentId = proto.thankYou.tipIntent.toByteArray().toList()
134+
tipIntentId = proto.thankYou.tipIntent.value.toByteArray().toList()
135135
)
136136
}
137137

138138
ChatService.Content.TypeCase.IDENTITY_REVEALED -> {
139139
IdentityRevealed(
140140
isFromSelf = isFromSelf,
141-
memberId = proto.identityRevealed.memberId.toByteArray().toList(),
141+
memberId = proto.identityRevealed.memberId.value.toByteArray().toList(),
142142
identity = Identity(proto.identityRevealed.identity)
143143
)
144144
}

0 commit comments

Comments
 (0)