From 4eaf940e5617758d4ceb0abdbfb7e517cd9f55a8 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 12 Aug 2026 15:45:10 -0400 Subject: [PATCH] feat(wallet): dive into full paged activity history MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The wallet's recent-activity preview shows only a glimpse. Make its "Recent" header a tappable chevron row that pushes the full, unbounded, paged history. - New AppRoute.Sheets.ActivityHistory + entry. - ActivityHistoryScreen / ActivityHistoryViewModel live in :shared:transaction-history, next to the feed data they render. The VM exposes the coordinator's recentTransactions() paged feed (cachedIn), reusing the same ActivityFeedRow the preview uses — token/profile resolution stays cache-only and reactive. - Top edge fades via verticalScrollStateGradient as rows scroll up. - app module depends on :shared:transaction-history. --- apps/flipcash/app/build.gradle.kts | 1 + .../ui/navigation/AppScreenContent.kt | 2 + .../kotlin/com/flipcash/app/core/AppRoute.kt | 5 ++ .../core/src/main/res/values/strings.xml | 1 + .../balance/internal/WalletScreenContent.kt | 39 +++++++--- .../ActivityHistoryScreen.kt | 73 +++++++++++++++++++ .../ActivityHistoryViewModel.kt | 22 ++++++ 7 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityHistoryScreen.kt create mode 100644 apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityHistoryViewModel.kt diff --git a/apps/flipcash/app/build.gradle.kts b/apps/flipcash/app/build.gradle.kts index af51b75b70..e4f8edd006 100644 --- a/apps/flipcash/app/build.gradle.kts +++ b/apps/flipcash/app/build.gradle.kts @@ -209,6 +209,7 @@ dependencies { implementation(project(":apps:flipcash:shared:invite")) implementation(project(":apps:flipcash:shared:tipping")) implementation(project(":apps:flipcash:shared:tokens")) + implementation(project(":apps:flipcash:shared:transaction-history")) implementation(project(":apps:flipcash:shared:web")) implementation(project(":apps:flipcash:shared:workers")) implementation(project(":apps:flipcash:features:login")) diff --git a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/AppScreenContent.kt b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/AppScreenContent.kt index a7c4f683d0..c51d2fff9a 100644 --- a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/AppScreenContent.kt +++ b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/AppScreenContent.kt @@ -29,6 +29,7 @@ import com.flipcash.app.contact.verification.VerificationFlowScreen import com.flipcash.app.currencycreator.CurrencyCreatorFlowScreen import com.flipcash.app.tipping.TipAmountEntryScreen import com.flipcash.app.tipping.TippingFlowScreen +import com.flipcash.shared.transactionhistory.ActivityHistoryScreen import com.flipcash.app.core.AppRoute import com.flipcash.app.core.navigation.DeeplinkAction import com.flipcash.app.currency.RegionSelectionScreen @@ -108,6 +109,7 @@ fun appEntryProvider( } } annotatedEntry { ShareAppScreen() } + annotatedEntry { ActivityHistoryScreen() } annotatedEntry { MenuScreen() } // Messaging diff --git a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt index 0b6ff5b239..9c092e6cb6 100644 --- a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt +++ b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt @@ -178,6 +178,11 @@ sealed interface AppRoute : NavKey, Parcelable { @Serializable data object Wallet : Sheets + + /** Full unified paged activity history — the "dive in" from the wallet's recent-activity preview. */ + @Serializable + data object ActivityHistory : Sheets + @Serializable data object Menu : Sheets diff --git a/apps/flipcash/core/src/main/res/values/strings.xml b/apps/flipcash/core/src/main/res/values/strings.xml index 86417e48b7..edd8ebee14 100644 --- a/apps/flipcash/core/src/main/res/values/strings.xml +++ b/apps/flipcash/core/src/main/res/values/strings.xml @@ -424,6 +424,7 @@ Advanced Wallet Recent + Activity Wallet Send as a Link diff --git a/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/WalletScreenContent.kt b/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/WalletScreenContent.kt index 596e624e05..85be9e60d8 100644 --- a/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/WalletScreenContent.kt +++ b/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/WalletScreenContent.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -124,7 +125,7 @@ internal fun WalletScreenContent( TokenCardStack( tokens = tokens, modifier = Modifier.fillMaxWidth(), - pinInset = statusBarInset, + pinInset = statusBarInset + CodeTheme.dimens.grid.x2, scrolledPast = scrolledPast, onCardClick = { token -> dispatchEvent( @@ -167,15 +168,33 @@ internal fun WalletScreenContent( if (balanceState.transactions.isNotEmpty()) { item(key = "recentHeader") { - Text( - text = stringResource(R.string.title_recentActivity), - style = CodeTheme.typography.screenTitle, - color = CodeTheme.colors.textMain, - modifier = Modifier.padding( - top = CodeTheme.dimens.grid.x4, - bottom = CodeTheme.dimens.grid.x1, - ), - ) + // Tap the header to dive into the full paged activity history. + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + dispatchEvent( + WalletViewModel.Event.OpenScreen(AppRoute.Sheets.ActivityHistory) + ) + } + .padding( + top = CodeTheme.dimens.grid.x4, + bottom = CodeTheme.dimens.grid.x1, + ), + horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x1), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = stringResource(R.string.title_recentActivity), + style = CodeTheme.typography.screenTitle, + color = CodeTheme.colors.textMain, + ) + Icon( + painter = painterResource(R.drawable.ic_chevron_right), + contentDescription = null, + tint = CodeTheme.colors.textSecondary, + ) + } } // Preview of the most recent activity (newest first); the full history lives on its own // screen. The VM/coordinator already bounds this list, so just render it. diff --git a/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityHistoryScreen.kt b/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityHistoryScreen.kt new file mode 100644 index 0000000000..ab6940453a --- /dev/null +++ b/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityHistoryScreen.kt @@ -0,0 +1,73 @@ +package com.flipcash.shared.transactionhistory + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.paging.compose.collectAsLazyPagingItems +import androidx.paging.compose.itemKey +import com.getcode.navigation.core.LocalCodeNavigator +import com.getcode.theme.CodeTheme +import com.getcode.ui.components.AppBarWithTitle +import com.getcode.ui.core.verticalScrollStateGradient + +/** + * The full unified paged activity history — the "dive in" from the wallet's recent-activity preview. + * Renders the same [ActivityFeedRow]s, unbounded and paged via [ActivityHistoryViewModel]. + */ +@Composable +fun ActivityHistoryScreen() { + val navigator = LocalCodeNavigator.current + val viewModel = hiltViewModel() + val items = viewModel.transactions.collectAsLazyPagingItems() + + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + AppBarWithTitle( + title = stringResource(R.string.title_activity), + titleAlignment = Alignment.CenterHorizontally, + onBackIconClicked = { navigator.pop() }, + ) + val listState = rememberLazyListState() + LazyColumn( + modifier = Modifier + .fillMaxSize() + // Fade rows out as they scroll up under the app bar; no bottom fade. + .verticalScrollStateGradient( + scrollState = listState, + color = CodeTheme.colors.background, + showAtEnd = false, + ), + state = listState, + contentPadding = PaddingValues( + start = CodeTheme.dimens.inset, + end = CodeTheme.dimens.inset, + ), + ) { + items( + count = items.itemCount, + key = items.itemKey { it.id }, + ) { index -> + val item = items[index] ?: return@items + ActivityFeedRow(item = item, modifier = Modifier.fillMaxWidth()) + } + + item { + Spacer(Modifier.windowInsetsPadding(WindowInsets.navigationBars)) + } + } + } +} diff --git a/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityHistoryViewModel.kt b/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityHistoryViewModel.kt new file mode 100644 index 0000000000..70f364f601 --- /dev/null +++ b/apps/flipcash/shared/transaction-history/src/main/kotlin/com/flipcash/shared/transactionhistory/ActivityHistoryViewModel.kt @@ -0,0 +1,22 @@ +package com.flipcash.shared.transactionhistory + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData +import androidx.paging.cachedIn +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +/** + * Backs the full unified paged activity history — the "dive in" from a recent-activity preview. + * Uses the coordinator's presentation-ready paged feed (avatars/titles/icons resolve reactively from + * the observed caches), the same rows a preview shows, just unbounded. + */ +@HiltViewModel +class ActivityHistoryViewModel @Inject constructor( + feedCoordinator: ActivityFeedCoordinator, +) : ViewModel() { + val transactions: Flow> = + feedCoordinator.recentTransactions().cachedIn(viewModelScope) +}