Skip to content

Commit 760389c

Browse files
authored
Merge pull request #559 from code-payments/feat/chat-start
feat: update RPC for fetching TwitterUser; drive chat flow
2 parents d7dc0bc + 02a9abc commit 760389c

37 files changed

Lines changed: 370 additions & 129 deletions

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package com.getcode.db
22

3-
import androidx.paging.PagingData
43
import androidx.paging.PagingSource
54
import androidx.room.Dao
65
import androidx.room.Delete
76
import androidx.room.Insert
87
import androidx.room.OnConflictStrategy
98
import androidx.room.Query
109
import androidx.room.RewriteQueriesToDropUnusedColumns
11-
import androidx.room.Transaction
1210
import com.getcode.model.Conversation
1311
import com.getcode.model.ConversationWithLastPointers
1412
import com.getcode.model.ID

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package com.getcode.model
22

3+
import kotlinx.serialization.Serializable
4+
35
sealed interface Value
46

7+
@Serializable
58
data class Fiat(
69
val currency: CurrencyCode,
710
val amount: Double,

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ data class KinAmount(
2727
}
2828

2929
companion object {
30+
val Zero = newInstance(0, Rate.oneToOne)
31+
3032
fun newInstance(kin: Int, rate: Rate): KinAmount {
3133
return newInstance(fromKin(kin), rate)
3234
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ data class TwitterUser(
2121
override val imageUrl: String?,
2222
val displayName: String,
2323
val followerCount: Int,
24-
val verificationStatus: VerificationStatus
24+
val verificationStatus: VerificationStatus,
25+
val costOfFriendship: Fiat,
26+
val isFriend: Boolean,
2527
): TipMetadata {
2628

2729
override val platform: String = "X"
@@ -52,7 +54,9 @@ data class TwitterUser(
5254
imageUrl = avatarUrl,
5355
followerCount = proto.followerCount,
5456
tipAddress = tipAddress,
55-
verificationStatus = VerificationStatus.entries.getOrNull(proto.verifiedTypeValue) ?: VerificationStatus.unknown
57+
verificationStatus = VerificationStatus.entries.getOrNull(proto.verifiedTypeValue) ?: VerificationStatus.unknown,
58+
costOfFriendship = Fiat(currency = CurrencyCode.USD, amount = 1.00),
59+
isFriend = proto.isFriend
5660
)
5761
}
5862
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.getcode.network
2+
3+
import androidx.paging.Pager
4+
import androidx.paging.PagingConfig
5+
import androidx.paging.PagingSource
6+
import androidx.paging.PagingState
7+
import com.getcode.model.chat.Chat
8+
import javax.inject.Inject
9+
10+
class ConversationListController @Inject constructor(
11+
private val historyController: ChatHistoryController,
12+
) {
13+
private val pagingConfig = PagingConfig(pageSize = 20)
14+
15+
fun observeConversations() =
16+
Pager(
17+
config = pagingConfig,
18+
initialKey = null,
19+
) { ChatPagingSource(historyController.chats.value.orEmpty()) }.flow
20+
}
21+
22+
class ChatPagingSource(
23+
private val chats: List<Chat>
24+
) : PagingSource<Int, Chat>() {
25+
26+
override fun getRefreshKey(state: PagingState<Int, Chat>): Int? {
27+
return state.anchorPosition?.let { anchorPosition ->
28+
val anchorPage = state.closestPageToPosition(anchorPosition)
29+
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
30+
}
31+
}
32+
33+
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Chat> {
34+
val currentList = chats
35+
val position = params.key ?: 0
36+
val pageSize = params.loadSize
37+
38+
return try {
39+
val items = currentList.subList(
40+
position.coerceAtMost(currentList.size),
41+
(position + pageSize).coerceAtMost(currentList.size)
42+
)
43+
44+
LoadResult.Page(
45+
data = items,
46+
prevKey = if (position > 0) position - pageSize else null,
47+
nextKey = if (position + pageSize < currentList.size) position + pageSize else null
48+
)
49+
} catch (e: Exception) {
50+
LoadResult.Error(e)
51+
}
52+
}
53+
}

api/src/main/java/com/getcode/network/TipController.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import kotlin.concurrent.fixedRateTimer
3939

4040
typealias TipUser = Pair<String, CodePayload>
4141

42+
4243
@Singleton
4344
class TipController @Inject constructor(
4445
private val client: Client,
@@ -108,10 +109,11 @@ class TipController @Inject constructor(
108109

109110
private suspend fun callForConnectedUser() {
110111
Timber.d("twitter poll call")
111-
val tipAddress = SessionManager.getOrganizer()?.primaryVault ?: return
112+
val organizer = SessionManager.getOrganizer() ?: return
113+
val tipAddress = organizer.primaryVault
112114
// only set lastPoll if we actively attempt to reach RPC
113115
lastPoll = System.currentTimeMillis()
114-
client.fetchTwitterUser(tipAddress)
116+
client.fetchTwitterUser(organizer, tipAddress)
115117
.onSuccess {
116118
Timber.d("current user twitter connected @ ${it.username}")
117119
prefRepository.set(PrefsString.KEY_TIP_ACCOUNT, Json.encodeToString(it))
@@ -152,10 +154,11 @@ class TipController @Inject constructor(
152154
}
153155

154156
suspend fun fetch(username: String): TwitterUser? {
157+
val organizer = SessionManager.getOrganizer() ?: return null
155158
val key = username.lowercase()
156159
return cachedUsers.getOrPutIfNonNull(key) {
157160
Timber.d("fetching user $username")
158-
client.fetchTwitterUser(username).getOrThrow()
161+
client.fetchTwitterUser(organizer, username).getOrThrow()
159162
}
160163
}
161164

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.getcode.network
2+
3+
import com.getcode.manager.SessionManager
4+
import com.getcode.model.TwitterUser
5+
import com.getcode.network.client.Client
6+
import com.getcode.network.client.fetchTwitterUser
7+
import com.getcode.network.repository.BetaFlagsRepository
8+
import com.getcode.network.repository.PrefRepository
9+
import com.getcode.utils.getOrPutIfNonNull
10+
import timber.log.Timber
11+
import javax.inject.Inject
12+
import javax.inject.Singleton
13+
14+
@Singleton
15+
class TwitterUserController @Inject constructor(
16+
private val client: Client,
17+
betaFlags: BetaFlagsRepository,
18+
private val prefRepository: PrefRepository,
19+
) {
20+
private var cachedUsers = mutableMapOf<String, TwitterUser>()
21+
22+
23+
suspend fun fetchUser(username: String, ignoreCache: Boolean = false): TwitterUser? {
24+
val organizer = SessionManager.getOrganizer() ?: return null
25+
val key = username.lowercase()
26+
27+
if (ignoreCache) {
28+
val user = client.fetchTwitterUser(organizer, username).getOrThrow()
29+
cachedUsers[key] = user
30+
return user
31+
}
32+
33+
return cachedUsers.getOrPutIfNonNull(key) {
34+
Timber.d("fetching user $username")
35+
client.fetchTwitterUser(organizer, username).getOrThrow()
36+
}
37+
}
38+
}

api/src/main/java/com/getcode/network/api/ChatApiV2.kt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,15 @@ import com.codeinc.gen.common.v1.Model
1212
import com.getcode.ed25519.Ed25519.KeyPair
1313
import com.getcode.model.Cursor
1414
import com.getcode.model.ID
15-
import com.getcode.model.chat.Chat
1615
import com.getcode.model.chat.OutgoingMessageContent
1716
import com.getcode.model.chat.Platform
1817
import com.getcode.model.chat.StartChatRequest
1918
import com.getcode.model.chat.StartChatResponse
20-
import com.getcode.model.description
2119
import com.getcode.network.core.GrpcApi
2220
import com.getcode.network.repository.toByteString
2321
import com.getcode.network.repository.toSolanaAccount
24-
import com.getcode.utils.TraceType
2522
import com.getcode.utils.bytes
2623
import com.getcode.utils.sign
27-
import com.getcode.utils.trace
2824
import io.grpc.ManagedChannel
2925
import io.grpc.stub.StreamObserver
3026
import kotlinx.coroutines.Dispatchers
@@ -54,11 +50,11 @@ class ChatApiV2 @Inject constructor(
5450
) : GrpcApi(managedChannel) {
5551
private val api = ChatGrpc.newStub(managedChannel)
5652

57-
fun createTipChat(owner: KeyPair, intentId: ID): Flow<StartChatResponse> {
53+
fun startChat(owner: KeyPair, intentId: ID): Flow<StartChatResponse> {
5854
val request = StartChatRequest.newBuilder()
5955
.setOwner(owner.publicKeyBytes.toSolanaAccount())
60-
.setTipChat(
61-
ChatService.StartTipChatParameters.newBuilder()
56+
.setTwoWayChat(
57+
ChatService.StartTwoWayChatParameters.newBuilder()
6258
.setIntentId(IntentId.newBuilder()
6359
.setValue(intentId.toByteString()))
6460
.build()

api/src/main/java/com/getcode/network/client/Client_Identity.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,16 @@ suspend fun Client.updatePreferences(organizer: Organizer): Result<Boolean> {
1717
)
1818
}
1919

20-
suspend fun Client.fetchTwitterUser(username: String): Result<TwitterUser> {
21-
return identityRepository.fetchTwitterUserByUsername(username)
20+
suspend fun Client.fetchTwitterUser(
21+
organizer: Organizer,
22+
username: String
23+
): Result<TwitterUser> {
24+
return identityRepository.fetchTwitterUserByUsername(organizer.ownerKeyPair, username)
2225
}
2326

24-
suspend fun Client.fetchTwitterUser(address: PublicKey): Result<TwitterUser> {
25-
return identityRepository.fetchTwitterUserByAddress(address)
27+
suspend fun Client.fetchTwitterUser(
28+
organizer: Organizer,
29+
address: PublicKey
30+
): Result<TwitterUser> {
31+
return identityRepository.fetchTwitterUserByAddress(organizer.ownerKeyPair, address)
2632
}

api/src/main/java/com/getcode/network/repository/IdentityRepository.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,10 @@ class IdentityRepository @Inject constructor(
303303
}
304304
}
305305

306-
suspend fun fetchTwitterUserByUsername(username: String): Result<TwitterUser> {
306+
suspend fun fetchTwitterUserByUsername(owner: KeyPair, username: String): Result<TwitterUser> {
307307
val request = GetTwitterUserRequest.newBuilder()
308308
.setUsername(username)
309+
.setRequestor(owner.publicKeyBytes.toSolanaAccount())
309310
.build()
310311

311312
return try {
@@ -349,9 +350,10 @@ class IdentityRepository @Inject constructor(
349350
}
350351
}
351352

352-
suspend fun fetchTwitterUserByAddress(address: PublicKey): Result<TwitterUser> {
353+
suspend fun fetchTwitterUserByAddress(owner: KeyPair, address: PublicKey): Result<TwitterUser> {
353354
val request = GetTwitterUserRequest.newBuilder()
354355
.setTipAddress(address.byteArray.toSolanaAccount())
356+
.setRequestor(owner.publicKeyBytes.toSolanaAccount())
355357
.build()
356358

357359
return try {

0 commit comments

Comments
 (0)