diff --git a/apps/flipcash/features/balance/build.gradle.kts b/apps/flipcash/features/balance/build.gradle.kts index b106c56866..dfe9882d54 100644 --- a/apps/flipcash/features/balance/build.gradle.kts +++ b/apps/flipcash/features/balance/build.gradle.kts @@ -13,7 +13,7 @@ dependencies { implementation(libs.compose.paging) - implementation(project(":apps:flipcash:shared:activityfeed")) + implementation(project(":apps:flipcash:shared:transaction-history")) implementation(project(":apps:flipcash:shared:analytics")) implementation(project(":apps:flipcash:shared:chat")) implementation(project(":apps:flipcash:shared:featureflags")) diff --git a/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/BalanceViewModel.kt b/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/BalanceViewModel.kt index c10d5d0294..bbb77fd3b4 100644 --- a/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/BalanceViewModel.kt +++ b/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/BalanceViewModel.kt @@ -5,7 +5,7 @@ import com.flipcash.app.analytics.Analytics import com.flipcash.app.analytics.FlipcashAnalyticsService import com.flipcash.app.balance.internal.components.OnboardingItem import com.flipcash.app.core.AppRoute -import com.flipcash.app.activityfeed.ActivityFeedCoordinator +import com.flipcash.shared.transactionhistory.ActivityFeedCoordinator import com.flipcash.app.funding.PurchaseMethodController import com.flipcash.app.userflags.UserFlagsCoordinator import com.flipcash.shared.chat.ChatCoordinator diff --git a/apps/flipcash/features/balance/src/test/kotlin/com/flipcash/app/balance/internal/BalanceViewModelTest.kt b/apps/flipcash/features/balance/src/test/kotlin/com/flipcash/app/balance/internal/BalanceViewModelTest.kt index 30fec12ab6..0fd6889461 100644 --- a/apps/flipcash/features/balance/src/test/kotlin/com/flipcash/app/balance/internal/BalanceViewModelTest.kt +++ b/apps/flipcash/features/balance/src/test/kotlin/com/flipcash/app/balance/internal/BalanceViewModelTest.kt @@ -5,7 +5,7 @@ import com.flipcash.app.analytics.StubFlipcashAnalytics import com.flipcash.app.core.AppRoute import com.flipcash.app.core.MainCoroutineRule import com.flipcash.app.core.dispatchers.TestDispatchers -import com.flipcash.app.activityfeed.ActivityFeedCoordinator +import com.flipcash.shared.transactionhistory.ActivityFeedCoordinator import com.flipcash.app.funding.PurchaseMethodController import com.flipcash.app.userflags.UserFlagsCoordinator import com.flipcash.shared.chat.ChatCoordinator diff --git a/apps/flipcash/features/transactions/build.gradle.kts b/apps/flipcash/features/transactions/build.gradle.kts index 01090039d6..8884ad1a81 100644 --- a/apps/flipcash/features/transactions/build.gradle.kts +++ b/apps/flipcash/features/transactions/build.gradle.kts @@ -14,7 +14,7 @@ dependencies { implementation(libs.compose.paging) - implementation(project(":apps:flipcash:shared:activityfeed")) + implementation(project(":apps:flipcash:shared:transaction-history")) implementation(project(":apps:flipcash:shared:featureflags")) implementation(project(":apps:flipcash:shared:tokens")) diff --git a/apps/flipcash/features/transactions/src/main/kotlin/com/flipcash/app/transactions/internal/TransactionHistoryViewModel.kt b/apps/flipcash/features/transactions/src/main/kotlin/com/flipcash/app/transactions/internal/TransactionHistoryViewModel.kt index b9be827a3b..fab032c2d2 100644 --- a/apps/flipcash/features/transactions/src/main/kotlin/com/flipcash/app/transactions/internal/TransactionHistoryViewModel.kt +++ b/apps/flipcash/features/transactions/src/main/kotlin/com/flipcash/app/transactions/internal/TransactionHistoryViewModel.kt @@ -1,7 +1,7 @@ package com.flipcash.app.transactions.internal import androidx.lifecycle.viewModelScope -import com.flipcash.app.activityfeed.ActivityFeedCoordinator +import com.flipcash.shared.transactionhistory.ActivityFeedCoordinator import com.flipcash.app.core.extensions.onResult import com.flipcash.app.core.feed.ActivityFeedMessage import com.flipcash.app.core.feed.MessageMetadata diff --git a/apps/flipcash/features/transactions/src/test/kotlin/com/flipcash/app/transactions/internal/TransactionHistoryViewModelErrorTest.kt b/apps/flipcash/features/transactions/src/test/kotlin/com/flipcash/app/transactions/internal/TransactionHistoryViewModelErrorTest.kt index afcc4eb071..e01bc8b2d1 100644 --- a/apps/flipcash/features/transactions/src/test/kotlin/com/flipcash/app/transactions/internal/TransactionHistoryViewModelErrorTest.kt +++ b/apps/flipcash/features/transactions/src/test/kotlin/com/flipcash/app/transactions/internal/TransactionHistoryViewModelErrorTest.kt @@ -1,6 +1,6 @@ package com.flipcash.app.transactions.internal -import com.flipcash.app.activityfeed.ActivityFeedCoordinator +import com.flipcash.shared.transactionhistory.ActivityFeedCoordinator import com.flipcash.app.featureflags.FeatureFlagController import com.flipcash.app.tokens.TokenCoordinator import com.flipcash.services.user.UserManager diff --git a/apps/flipcash/features/withdrawal/build.gradle.kts b/apps/flipcash/features/withdrawal/build.gradle.kts index cac773750d..bb01cdd496 100644 --- a/apps/flipcash/features/withdrawal/build.gradle.kts +++ b/apps/flipcash/features/withdrawal/build.gradle.kts @@ -13,7 +13,7 @@ dependencies { testImplementation(libs.bundles.compose.ui.testing) implementation(project(":apps:flipcash:shared:amount-entry")) - implementation(project(":apps:flipcash:shared:activityfeed")) + implementation(project(":apps:flipcash:shared:transaction-history")) implementation(project(":apps:flipcash:shared:analytics")) implementation(project(":apps:flipcash:shared:tokens")) implementation(project(":apps:flipcash:shared:userflags")) diff --git a/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/WithdrawalViewModel.kt b/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/WithdrawalViewModel.kt index de37c58174..d413acfb65 100644 --- a/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/WithdrawalViewModel.kt +++ b/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/WithdrawalViewModel.kt @@ -5,7 +5,7 @@ import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.flipcash.app.activityfeed.ActivityFeedCoordinator +import com.flipcash.shared.transactionhistory.ActivityFeedCoordinator import com.flipcash.app.analytics.Analytics import com.flipcash.app.analytics.FlipcashAnalyticsService import com.flipcash.app.core.extensions.onResult diff --git a/apps/flipcash/features/withdrawal/src/test/kotlin/com/flipcash/app/withdrawal/WithdrawalViewModelErrorTest.kt b/apps/flipcash/features/withdrawal/src/test/kotlin/com/flipcash/app/withdrawal/WithdrawalViewModelErrorTest.kt index 586afae301..e3df7ad4fb 100644 --- a/apps/flipcash/features/withdrawal/src/test/kotlin/com/flipcash/app/withdrawal/WithdrawalViewModelErrorTest.kt +++ b/apps/flipcash/features/withdrawal/src/test/kotlin/com/flipcash/app/withdrawal/WithdrawalViewModelErrorTest.kt @@ -1,7 +1,7 @@ package com.flipcash.app.withdrawal import android.content.ClipboardManager -import com.flipcash.app.activityfeed.ActivityFeedCoordinator +import com.flipcash.shared.transactionhistory.ActivityFeedCoordinator import com.flipcash.app.analytics.FlipcashAnalyticsService import com.flipcash.app.tokens.TokenCoordinator import com.flipcash.app.userflags.UserFlagsCoordinator diff --git a/apps/flipcash/shared/activityfeed/build.gradle.kts b/apps/flipcash/shared/activityfeed/build.gradle.kts index f2a5ba4068..5a6f87db1f 100644 --- a/apps/flipcash/shared/activityfeed/build.gradle.kts +++ b/apps/flipcash/shared/activityfeed/build.gradle.kts @@ -7,11 +7,10 @@ android { } dependencies { - implementation(libs.bundles.room) - implementation(libs.androidx.paging.runtime) - - compileOnly(project(":apps:flipcash:shared:persistence:db")) - implementation(project(":apps:flipcash:shared:persistence:sources")) - implementation(project(":services:flipcash")) - implementation(project(":libs:datetime")) + // Deprecated shim module: it only forwards to the new home (:shared:transaction-history) via + // @Deprecated typealiases. `api` (not implementation) so the underlying types resolve + // transitively for callers still importing the old com.flipcash.app.activityfeed package + // during the phase-out. Removed once all consumers migrate (plan Task 8, gated on v2 launch). + // See docs/superpowers/plans/2026-08-11-activityfeed-phaseout.md. + api(project(":apps:flipcash:shared:transaction-history")) } diff --git a/apps/flipcash/shared/activityfeed/src/main/kotlin/com/flipcash/app/activityfeed/ActivityFeedCoordinator.kt b/apps/flipcash/shared/activityfeed/src/main/kotlin/com/flipcash/app/activityfeed/ActivityFeedCoordinator.kt index 6c2c10981f..d5b95e87f8 100644 --- a/apps/flipcash/shared/activityfeed/src/main/kotlin/com/flipcash/app/activityfeed/ActivityFeedCoordinator.kt +++ b/apps/flipcash/shared/activityfeed/src/main/kotlin/com/flipcash/app/activityfeed/ActivityFeedCoordinator.kt @@ -1,111 +1,12 @@ package com.flipcash.app.activityfeed -import androidx.paging.ExperimentalPagingApi -import androidx.paging.Pager -import androidx.paging.PagingConfig -import androidx.paging.PagingData -import androidx.paging.filter -import androidx.paging.map -import com.flipcash.app.core.feed.ActivityFeedMessage -import com.flipcash.app.core.feed.ActivityFeedMessageWithToken -import com.flipcash.app.persistence.sources.MessageDataSource -import com.flipcash.app.persistence.sources.mapper.notifications.MessageEntityToFeedMessageMapper -import com.flipcash.app.persistence.sources.mediator.FeedRemoteMediator -import com.flipcash.services.controllers.ActivityFeedController -import com.flipcash.services.models.ActivityFeedType -import com.flipcash.services.models.NotificationState -import com.flipcash.services.models.QueryOptions -import com.flipcash.services.user.UserManager -import com.getcode.opencode.providers.TokenMetadataProvider -import com.getcode.solana.keys.Mint -import com.getcode.utils.TraceType -import com.getcode.utils.trace -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ActivityFeedCoordinator @Inject constructor( - private val activityFeedController: ActivityFeedController, - private val dataSource: MessageDataSource, - private val mapper: MessageEntityToFeedMessageMapper, - private val userManager: UserManager, - private val tokenProvider: TokenMetadataProvider, -) { - private val pagingConfig = PagingConfig(pageSize = 20) - - @OptIn(ExperimentalPagingApi::class) - private val _messages: Flow> = userManager.state - .filter { it.authState.canAccessAuthenticatedApis } - .flatMapLatest { - Pager( - config = pagingConfig, - remoteMediator = FeedRemoteMediator(activityFeedController, dataSource) - ) { - dataSource.observe() - }.flow.map { page -> page.map { entity -> mapper.map(entity) } } - } - - @OptIn(ExperimentalCoroutinesApi::class) - val messages: Flow> = userManager.state - .mapNotNull { it.authState } - .filter { it.canAccessAuthenticatedApis } - .flatMapLatest { _messages } - - @OptIn(ExperimentalCoroutinesApi::class) - fun transactions(mint: Mint): Flow> = messages.map { page -> - page.filter { message -> - message.amount?.mint == mint - }.map { msg -> - val result = msg.amount?.mint?.let { tokenProvider.getTokenMetadata(it) }?.getOrNull() - ActivityFeedMessageWithToken(msg, result?.token) - } - } - - /** Reactive "has the user ever added money" — any completed deposit/buy in the feed. */ - fun hasEverAddedMoney(): Flow = dataSource.hasEverAddedMoney() - - suspend fun checkPendingMessagesForUpdates(): Result { - val pendingMessages = dataSource.query(whereClause = "state = '${NotificationState.PENDING.name}'") - - if (pendingMessages.isEmpty()) { - trace( - tag = "ActivityFeedCoordinator", - message = "No pending messages found", - type = TraceType.Silent - ) - return Result.success(0) - } - - return activityFeedController.getNotificationsById(pendingMessages.map { it.id }) - .map { notifications -> - val completedCount = notifications.count { it.state == NotificationState.COMPLETED } - - trace( - tag = "ActivityFeedCoordinator", - message = "$completedCount notifications completed from batch", - type = TraceType.Silent - ) - - dataSource.upsert(notifications) - completedCount - } - } - - suspend fun fetchSinceLatest(count: Int = 20): Result { - val latest = dataSource.getMostRecent() - return activityFeedController.queryNotificationsFor( - type = ActivityFeedType.TransactionHistory, - queryOptions = QueryOptions( - limit = count, - token = latest?.id, - descending = latest == null, - ) - ).onSuccess { dataSource.upsert(it) }.map { Unit } - } -} \ No newline at end of file +@Deprecated( + "Moved to :shared:transaction-history as part of the v2 (FeatureFlag.NewUi) phase-out. " + + "Import com.flipcash.shared.transactionhistory.ActivityFeedCoordinator instead. " + + "Plan: docs/superpowers/plans/2026-08-11-activityfeed-phaseout.md", + ReplaceWith( + "ActivityFeedCoordinator", + "com.flipcash.shared.transactionhistory.ActivityFeedCoordinator", + ), +) +typealias ActivityFeedCoordinator = com.flipcash.shared.transactionhistory.ActivityFeedCoordinator diff --git a/apps/flipcash/shared/activityfeed/src/main/kotlin/com/flipcash/app/activityfeed/ActivityFeedUpdater.kt b/apps/flipcash/shared/activityfeed/src/main/kotlin/com/flipcash/app/activityfeed/ActivityFeedUpdater.kt index 0b8d6ee323..6d90363ff3 100644 --- a/apps/flipcash/shared/activityfeed/src/main/kotlin/com/flipcash/app/activityfeed/ActivityFeedUpdater.kt +++ b/apps/flipcash/shared/activityfeed/src/main/kotlin/com/flipcash/app/activityfeed/ActivityFeedUpdater.kt @@ -1,12 +1,12 @@ package com.flipcash.app.activityfeed -import com.flipcash.app.core.updater.NetworkUpdater -import javax.inject.Inject - -class ActivityFeedUpdater @Inject constructor( - private val coordinator: ActivityFeedCoordinator, -): NetworkUpdater() { - override suspend fun doUpdate() { - coordinator.fetchSinceLatest(count = 50) - } -} +@Deprecated( + "Moved to :shared:transaction-history as part of the v2 (FeatureFlag.NewUi) phase-out. " + + "Import com.flipcash.shared.transactionhistory.ActivityFeedUpdater instead. " + + "Plan: docs/superpowers/plans/2026-08-11-activityfeed-phaseout.md", + ReplaceWith( + "ActivityFeedUpdater", + "com.flipcash.shared.transactionhistory.ActivityFeedUpdater", + ), +) +typealias ActivityFeedUpdater = com.flipcash.shared.transactionhistory.ActivityFeedUpdater diff --git a/apps/flipcash/shared/blocklist/src/main/kotlin/com/flipcash/app/blocklist/BlocklistCoordinator.kt b/apps/flipcash/shared/blocklist/src/main/kotlin/com/flipcash/app/blocklist/BlocklistCoordinator.kt index f141305b15..bd3860929a 100644 --- a/apps/flipcash/shared/blocklist/src/main/kotlin/com/flipcash/app/blocklist/BlocklistCoordinator.kt +++ b/apps/flipcash/shared/blocklist/src/main/kotlin/com/flipcash/app/blocklist/BlocklistCoordinator.kt @@ -30,7 +30,7 @@ import javax.inject.Singleton /** * Owns the offline-first blocklist stream and unblock action for the account's blocklist screen. * - * Mirrors [com.flipcash.app.activityfeed.ActivityFeedCoordinator]: gated on an authenticated + * Mirrors [com.flipcash.shared.transactionhistory.ActivityFeedCoordinator]: gated on an authenticated * session, it wires a [Pager] + [BlocklistRemoteMediator] over the Room-backed * [BlockedUserDataSource] and maps cached entities to display [BlockedUserProfile]s. */ diff --git a/apps/flipcash/shared/session/build.gradle.kts b/apps/flipcash/shared/session/build.gradle.kts index 7e6f54817c..8373dbca93 100644 --- a/apps/flipcash/shared/session/build.gradle.kts +++ b/apps/flipcash/shared/session/build.gradle.kts @@ -16,7 +16,7 @@ dependencies { implementation(project(":apps:flipcash:shared:blocklist")) implementation(project(":apps:flipcash:shared:chat")) implementation(project(":apps:flipcash:shared:contacts")) - implementation(project(":apps:flipcash:shared:activityfeed")) + implementation(project(":apps:flipcash:shared:transaction-history")) implementation(project(":apps:flipcash:shared:analytics")) implementation(project(":apps:flipcash:shared:appsettings")) implementation(project(":apps:flipcash:shared:google-play-billing")) diff --git a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/RealSessionController.kt b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/RealSessionController.kt index f7ba05de49..2dfc5d4005 100644 --- a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/RealSessionController.kt +++ b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/RealSessionController.kt @@ -1,7 +1,7 @@ package com.flipcash.app.session.internal -import com.flipcash.app.activityfeed.ActivityFeedCoordinator -import com.flipcash.app.activityfeed.ActivityFeedUpdater +import com.flipcash.shared.transactionhistory.ActivityFeedCoordinator +import com.flipcash.shared.transactionhistory.ActivityFeedUpdater import com.flipcash.app.appsettings.AppSettingValue import com.flipcash.app.appsettings.AppSettingsCoordinator import com.flipcash.app.billing.BillingClient diff --git a/apps/flipcash/shared/tokens/build.gradle.kts b/apps/flipcash/shared/tokens/build.gradle.kts index 9b516fe608..c13c49b28e 100644 --- a/apps/flipcash/shared/tokens/build.gradle.kts +++ b/apps/flipcash/shared/tokens/build.gradle.kts @@ -20,7 +20,7 @@ dependencies { implementation(project(":apps:flipcash:shared:amount-entry")) implementation(project(":apps:flipcash:shared:funding")) - implementation(project(":apps:flipcash:shared:activityfeed")) + implementation(project(":apps:flipcash:shared:transaction-history")) implementation(project(":apps:flipcash:shared:analytics")) implementation(project(":apps:flipcash:shared:featureflags")) implementation(project(":apps:flipcash:shared:onramp:coinbase")) diff --git a/apps/flipcash/shared/tokens/src/main/kotlin/com/flipcash/app/tokens/ui/SwapViewModel.kt b/apps/flipcash/shared/tokens/src/main/kotlin/com/flipcash/app/tokens/ui/SwapViewModel.kt index 329114fec3..e7fa945866 100644 --- a/apps/flipcash/shared/tokens/src/main/kotlin/com/flipcash/app/tokens/ui/SwapViewModel.kt +++ b/apps/flipcash/shared/tokens/src/main/kotlin/com/flipcash/app/tokens/ui/SwapViewModel.kt @@ -1,7 +1,7 @@ package com.flipcash.app.tokens.ui import androidx.lifecycle.viewModelScope -import com.flipcash.app.activityfeed.ActivityFeedCoordinator +import com.flipcash.shared.transactionhistory.ActivityFeedCoordinator import com.flipcash.app.analytics.Analytics import com.flipcash.app.analytics.Button import com.flipcash.app.analytics.FlipcashAnalyticsService diff --git a/apps/flipcash/shared/tokens/src/test/kotlin/com/flipcash/app/tokens/ui/SwapViewModelErrorTest.kt b/apps/flipcash/shared/tokens/src/test/kotlin/com/flipcash/app/tokens/ui/SwapViewModelErrorTest.kt index a516f92acd..fb914d1f0b 100644 --- a/apps/flipcash/shared/tokens/src/test/kotlin/com/flipcash/app/tokens/ui/SwapViewModelErrorTest.kt +++ b/apps/flipcash/shared/tokens/src/test/kotlin/com/flipcash/app/tokens/ui/SwapViewModelErrorTest.kt @@ -1,6 +1,6 @@ package com.flipcash.app.tokens.ui -import com.flipcash.app.activityfeed.ActivityFeedCoordinator +import com.flipcash.shared.transactionhistory.ActivityFeedCoordinator import com.flipcash.app.analytics.FlipcashAnalyticsService import com.flipcash.app.core.tokens.SwapPurpose import com.flipcash.app.onramp.CoinbaseOnRampController diff --git a/apps/flipcash/shared/transaction-history/build.gradle.kts b/apps/flipcash/shared/transaction-history/build.gradle.kts new file mode 100644 index 0000000000..a390367792 --- /dev/null +++ b/apps/flipcash/shared/transaction-history/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + alias(libs.plugins.flipcash.android.feature) +} + +android { + namespace = "com.flipcash.shared.transactionhistory" +} + +// Home of the transaction-history data engine (ActivityFeedCoordinator / ActivityFeedUpdater), +// consolidated out of the deprecated :shared:activityfeed. Mirrors that module's dependencies. +// See docs/superpowers/plans/2026-08-11-activityfeed-phaseout.md. +dependencies { + implementation(libs.bundles.room) + implementation(libs.androidx.paging.runtime) + + compileOnly(project(":apps:flipcash:shared:persistence:db")) + implementation(project(":apps:flipcash:shared:persistence:sources")) + implementation(project(":services:flipcash")) + implementation(project(":libs:datetime")) +} diff --git a/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityFeedCoordinator.kt b/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityFeedCoordinator.kt new file mode 100644 index 0000000000..fd74e9bf85 --- /dev/null +++ b/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityFeedCoordinator.kt @@ -0,0 +1,111 @@ +package com.flipcash.shared.transactionhistory + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.filter +import androidx.paging.map +import com.flipcash.app.core.feed.ActivityFeedMessage +import com.flipcash.app.core.feed.ActivityFeedMessageWithToken +import com.flipcash.app.persistence.sources.MessageDataSource +import com.flipcash.app.persistence.sources.mapper.notifications.MessageEntityToFeedMessageMapper +import com.flipcash.app.persistence.sources.mediator.FeedRemoteMediator +import com.flipcash.services.controllers.ActivityFeedController +import com.flipcash.services.models.ActivityFeedType +import com.flipcash.services.models.NotificationState +import com.flipcash.services.models.QueryOptions +import com.flipcash.services.user.UserManager +import com.getcode.opencode.providers.TokenMetadataProvider +import com.getcode.solana.keys.Mint +import com.getcode.utils.TraceType +import com.getcode.utils.trace +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ActivityFeedCoordinator @Inject constructor( + private val activityFeedController: ActivityFeedController, + private val dataSource: MessageDataSource, + private val mapper: MessageEntityToFeedMessageMapper, + private val userManager: UserManager, + private val tokenProvider: TokenMetadataProvider, +) { + private val pagingConfig = PagingConfig(pageSize = 20) + + @OptIn(ExperimentalPagingApi::class) + private val _messages: Flow> = userManager.state + .filter { it.authState.canAccessAuthenticatedApis } + .flatMapLatest { + Pager( + config = pagingConfig, + remoteMediator = FeedRemoteMediator(activityFeedController, dataSource) + ) { + dataSource.observe() + }.flow.map { page -> page.map { entity -> mapper.map(entity) } } + } + + @OptIn(ExperimentalCoroutinesApi::class) + val messages: Flow> = userManager.state + .mapNotNull { it.authState } + .filter { it.canAccessAuthenticatedApis } + .flatMapLatest { _messages } + + @OptIn(ExperimentalCoroutinesApi::class) + fun transactions(mint: Mint): Flow> = messages.map { page -> + page.filter { message -> + message.amount?.mint == mint + }.map { msg -> + val result = msg.amount?.mint?.let { tokenProvider.getTokenMetadata(it) }?.getOrNull() + ActivityFeedMessageWithToken(msg, result?.token) + } + } + + /** Reactive "has the user ever added money" — any completed deposit/buy in the feed. */ + fun hasEverAddedMoney(): Flow = dataSource.hasEverAddedMoney() + + suspend fun checkPendingMessagesForUpdates(): Result { + val pendingMessages = dataSource.query(whereClause = "state = '${NotificationState.PENDING.name}'") + + if (pendingMessages.isEmpty()) { + trace( + tag = "ActivityFeedCoordinator", + message = "No pending messages found", + type = TraceType.Silent + ) + return Result.success(0) + } + + return activityFeedController.getNotificationsById(pendingMessages.map { it.id }) + .map { notifications -> + val completedCount = notifications.count { it.state == NotificationState.COMPLETED } + + trace( + tag = "ActivityFeedCoordinator", + message = "$completedCount notifications completed from batch", + type = TraceType.Silent + ) + + dataSource.upsert(notifications) + completedCount + } + } + + suspend fun fetchSinceLatest(count: Int = 20): Result { + val latest = dataSource.getMostRecent() + return activityFeedController.queryNotificationsFor( + type = ActivityFeedType.TransactionHistory, + queryOptions = QueryOptions( + limit = count, + token = latest?.id, + descending = latest == null, + ) + ).onSuccess { dataSource.upsert(it) }.map { Unit } + } +} \ No newline at end of file diff --git a/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityFeedUpdater.kt b/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityFeedUpdater.kt new file mode 100644 index 0000000000..418e094440 --- /dev/null +++ b/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityFeedUpdater.kt @@ -0,0 +1,12 @@ +package com.flipcash.shared.transactionhistory + +import com.flipcash.app.core.updater.NetworkUpdater +import javax.inject.Inject + +class ActivityFeedUpdater @Inject constructor( + private val coordinator: ActivityFeedCoordinator, +): NetworkUpdater() { + override suspend fun doUpdate() { + coordinator.fetchSinceLatest(count = 50) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index f478edbdaf..31fdeed473 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -51,6 +51,7 @@ include( ":apps:flipcash:shared:appsettings", ":apps:flipcash:shared:authentication", ":apps:flipcash:shared:activityfeed", + ":apps:flipcash:shared:transaction-history", ":apps:flipcash:shared:blocklist", ":apps:flipcash:shared:bills", ":apps:flipcash:shared:bill-customization",