diff --git a/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/ChatViewModel.kt b/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/ChatViewModel.kt index 6b99e611a6..c4cb22b025 100644 --- a/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/ChatViewModel.kt +++ b/apps/flipcash/features/messenger/src/main/kotlin/com/flipcash/app/messenger/internal/ChatViewModel.kt @@ -24,6 +24,7 @@ import com.flipcash.shared.chat.models.SeparatorConfig import com.flipcash.app.funding.PurchaseMethodController import com.flipcash.app.tokens.TokenCoordinator import com.flipcash.features.messenger.R +import com.flipcash.services.models.TipOrigin import com.flipcash.services.models.UserProfile import com.flipcash.services.models.chat.ChatId import com.flipcash.services.models.chat.ChatType @@ -700,6 +701,7 @@ internal class ChatViewModel @Inject constructor( verifiedFiat = verifiedFiat, token = token, source = source, + origin = TipOrigin.CHAT, ) null -> { dispatchEvent(Event.SendStateUpdated()) @@ -743,7 +745,6 @@ internal class ChatViewModel @Inject constructor( } override fun onCleared() { - super.onCleared() chatCoordinator.setActiveChatId(null) } diff --git a/apps/flipcash/shared/payments/src/main/kotlin/com/flipcash/shared/payments/TipPaymentDelegate.kt b/apps/flipcash/shared/payments/src/main/kotlin/com/flipcash/shared/payments/TipPaymentDelegate.kt index 9d1084c0b5..d8102bc60d 100644 --- a/apps/flipcash/shared/payments/src/main/kotlin/com/flipcash/shared/payments/TipPaymentDelegate.kt +++ b/apps/flipcash/shared/payments/src/main/kotlin/com/flipcash/shared/payments/TipPaymentDelegate.kt @@ -3,6 +3,7 @@ package com.flipcash.shared.payments import com.flipcash.app.tokens.TokenCoordinator import com.flipcash.app.userflags.UserFlagsCoordinator import com.flipcash.services.controllers.ResolverController +import com.flipcash.services.models.TipOrigin import com.flipcash.services.models.buildTipDmPaymentMetadata import com.flipcash.services.models.chat.ChatId import com.flipcash.shared.chat.ChatCoordinator @@ -135,17 +136,19 @@ class TipPaymentDelegate @Inject constructor( /** * Sends [verifiedFiat] of [token] from [source] to the user identified by [userId] as a tip DM: * derives the canonical tip chat, resolves the recipient's on-chain owner, attaches tip-DM app - * metadata, transfers, debits the local balance, and syncs the chat feed. Returns the canonical - * tip [ChatId] (for message reload / navigation), or null if it couldn't be derived. + * metadata, transfers, debits the local balance, and syncs the chat feed. [origin] records + * where the tip was initiated (a tip card vs. an in-chat send). Returns the canonical tip + * [ChatId] (for message reload / navigation), or null if it couldn't be derived. */ suspend fun send( userId: ID, verifiedFiat: VerifiedFiat, token: Token, source: AccountCluster, + origin: TipOrigin, ): Result { val canonicalChatId = chatCoordinator.generateChatId(userId = userId).getOrNull() - val appMetadataBytes = buildTipDmPaymentMetadata(chatId = canonicalChatId) + val appMetadataBytes = buildTipDmPaymentMetadata(chatId = canonicalChatId, origin = origin) return resolverController.resolve(userId = userId) .mapCatching { destination -> diff --git a/apps/flipcash/shared/tipping/src/main/kotlin/com/flipcash/shared/tipping/TippingCoordinator.kt b/apps/flipcash/shared/tipping/src/main/kotlin/com/flipcash/shared/tipping/TippingCoordinator.kt index d56fcf7ce9..bec3ef78f1 100644 --- a/apps/flipcash/shared/tipping/src/main/kotlin/com/flipcash/shared/tipping/TippingCoordinator.kt +++ b/apps/flipcash/shared/tipping/src/main/kotlin/com/flipcash/shared/tipping/TippingCoordinator.kt @@ -12,6 +12,7 @@ import com.flipcash.app.currency.PreferredCurrencyController import com.flipcash.app.funding.PurchaseMethodController import com.flipcash.app.tokens.TokenCoordinator import com.flipcash.services.controllers.ProfileController +import com.flipcash.services.models.TipOrigin import com.flipcash.services.models.UserProfile import com.flipcash.services.user.UserManager import com.flipcash.shared.payments.TipPaymentDelegate @@ -197,6 +198,7 @@ class TippingCoordinator @Inject constructor( verifiedFiat = verifiedFiat, token = token, source = source, + origin = TipOrigin.TIPCARD, ).onSuccess { canonicalChatId -> setSendState(LoadingSuccessState(success = true)) delay(400.milliseconds) diff --git a/definitions/flipcash/protos/src/main/proto/intent/v1/model.proto b/definitions/flipcash/protos/src/main/proto/intent/v1/model.proto index 9be631a4e2..430292e367 100644 --- a/definitions/flipcash/protos/src/main/proto/intent/v1/model.proto +++ b/definitions/flipcash/protos/src/main/proto/intent/v1/model.proto @@ -38,8 +38,14 @@ message ChatMetadata { phone.v1.PhoneNumber destination = 2 [(validate.rules).message.required = true]; } - // For sending a DM tip payment to someone. The message is empty, since - // it's between two user IDs, which map directly to/from public keys. + // For sending a DM payment to someone using their user ID, which maps + // directly to/from a public key. message TipDmPayment { + // Location in the app the payment was sent from + enum Location { + TIPCARD = 0; + CHAT = 1; + } + Location location = 1; } } diff --git a/definitions/flipcash/protos/src/main/proto/messaging/v1/model.proto b/definitions/flipcash/protos/src/main/proto/messaging/v1/model.proto index 33f8e330ad..ea30fa8ce9 100644 --- a/definitions/flipcash/protos/src/main/proto/messaging/v1/model.proto +++ b/definitions/flipcash/protos/src/main/proto/messaging/v1/model.proto @@ -126,13 +126,13 @@ message CashContent { // Reserved for receiver, which will is required for group chats reserved 3; - // Action for how the cash was sent. Clietns should always show SENT as a + // Verb for how the cash was sent. Clietns should always show SENT as a // fallback. - enum Action { + enum Verb { SENT = 0; TIPPED = 1; } - Action action = 4; + Verb verb = 4; } // Reply content diff --git a/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/LocalToProtobuf.kt b/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/LocalToProtobuf.kt index e736c53e4b..2d96ac15c1 100644 --- a/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/LocalToProtobuf.kt +++ b/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/LocalToProtobuf.kt @@ -121,10 +121,10 @@ internal fun MessageContent.asContent(): MessagingModel.Content { .setQuarks(amount.quarks) .setMint(Common.PublicKey.newBuilder().setValue(mint.bytes.toByteString())) ) - .setAction( + .setVerb( when (action) { - MessageContent.Cash.Action.TIPPED -> MessagingModel.CashContent.Action.TIPPED - MessageContent.Cash.Action.SENT -> MessagingModel.CashContent.Action.SENT + MessageContent.Cash.Action.TIPPED -> MessagingModel.CashContent.Verb.TIPPED + MessageContent.Cash.Action.SENT -> MessagingModel.CashContent.Verb.SENT } ) ) diff --git a/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/ProtobufToLocal.kt b/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/ProtobufToLocal.kt index 75b27cbbfc..8cc413ad24 100644 --- a/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/ProtobufToLocal.kt +++ b/services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/ProtobufToLocal.kt @@ -154,8 +154,8 @@ internal fun MessagingModel.Content.toMessageContent(): MessageContent { currencyCode = CurrencyCode.tryValueOf(cash.amount.currency) ?: CurrencyCode.USD, ), mint = cash.amount.mint.value.toByteArray().toMint(), - action = when (cash.action) { - MessagingModel.CashContent.Action.TIPPED -> MessageContent.Cash.Action.TIPPED + action = when (cash.verb) { + MessagingModel.CashContent.Verb.TIPPED -> MessageContent.Cash.Action.TIPPED else -> MessageContent.Cash.Action.SENT }, ) diff --git a/services/flipcash/src/main/kotlin/com/flipcash/services/models/DmPaymentMetadata.kt b/services/flipcash/src/main/kotlin/com/flipcash/services/models/DmPaymentMetadata.kt index aceaf45133..b78a9473a7 100644 --- a/services/flipcash/src/main/kotlin/com/flipcash/services/models/DmPaymentMetadata.kt +++ b/services/flipcash/src/main/kotlin/com/flipcash/services/models/DmPaymentMetadata.kt @@ -30,21 +30,36 @@ fun buildDmPaymentMetadata( ).build().toByteArray() } +/** Where in the app a tip DM payment was initiated, reported to the backend. */ +enum class TipOrigin { TIPCARD, CHAT } + /** * Builds the serialized Flipcash `AppMetadata` proto bytes for a tip DM payment. * * Unlike a contact DM payment there is no phone source/destination — a tip DM is - * between two user IDs, which map directly to/from public keys. Returns `null` when + * between two user IDs, which map directly to/from public keys. [origin] records + * where the tip was sent from (a tip card vs. an in-chat send). Returns `null` when * [chatId] is missing so the caller can pass the result through unconditionally. */ fun buildTipDmPaymentMetadata( chatId: ChatId?, + origin: TipOrigin, ): ByteArray? { if (chatId == null) return null return FlipcashIntentModel.AppMetadata.newBuilder() .setChat( FlipcashIntentModel.ChatMetadata.newBuilder() .setChatId(Common.ChatId.newBuilder().setValue(chatId.bytes.toByteString())) - .setTipDmPayment(FlipcashIntentModel.ChatMetadata.TipDmPayment.newBuilder()) + .setTipDmPayment( + FlipcashIntentModel.ChatMetadata.TipDmPayment.newBuilder() + .setLocation( + when (origin) { + TipOrigin.TIPCARD -> + FlipcashIntentModel.ChatMetadata.TipDmPayment.Location.TIPCARD + TipOrigin.CHAT -> + FlipcashIntentModel.ChatMetadata.TipDmPayment.Location.CHAT + } + ) + ) ).build().toByteArray() }