Skip to content

Commit 96b9c72

Browse files
committed
feat(balance): update balance sheet UI to display all token balances
Signed-off-by: Brandon McAnsh <[email protected]>
1 parent 63b8986 commit 96b9c72

File tree

12 files changed

+156
-186
lines changed

12 files changed

+156
-186
lines changed

apps/flipcash/app/build.gradle.kts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ dependencies {
202202
implementation(Libs.androidx_lifecycle_viewmodel)
203203
implementation(Libs.androidx_navigation_ui)
204204
implementation(Libs.androidx_work)
205-
implementation("androidx.webkit:webkit:1.13.0")
205+
implementation("androidx.webkit:webkit:1.14.0")
206206

207207
//hilt dependency injection
208208
implementation(Libs.hilt)
@@ -216,7 +216,7 @@ dependencies {
216216
testImplementation(Libs.hilt_android_test)
217217
kspTest(Libs.hilt_android_compiler)
218218

219-
androidTestImplementation("io.mockk:mockk:1.13.17")
219+
androidTestImplementation("io.mockk:mockk:1.14.6")
220220

221221
//Jetpack compose
222222
implementation(platform(Libs.compose_bom))
@@ -231,9 +231,6 @@ dependencies {
231231

232232
implementation(Libs.androidx_activity)
233233

234-
implementation(Libs.coil3)
235-
implementation(Libs.coil3_network)
236-
237234
implementation(Libs.androidx_browser)
238235

239236
implementation(Libs.slf4j)

apps/flipcash/core/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ dependencies {
6464
implementation(Libs.androidx_credentials_play_auth)
6565
implementation(Libs.androidx_datastore)
6666

67+
api(Libs.coil3)
68+
api(Libs.coil3_network)
69+
6770
api(project(":services:flipcash-compose"))
6871

6972
implementation(project(":libs:messaging"))
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.flipcash.app.core.ui
2+
3+
import androidx.compose.foundation.clickable
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Row
6+
import androidx.compose.foundation.layout.Spacer
7+
import androidx.compose.foundation.layout.padding
8+
import androidx.compose.foundation.layout.size
9+
import androidx.compose.foundation.shape.CircleShape
10+
import androidx.compose.material.Text
11+
import androidx.compose.runtime.Composable
12+
import androidx.compose.ui.Alignment
13+
import androidx.compose.ui.Modifier
14+
import androidx.compose.ui.draw.clip
15+
import coil3.compose.AsyncImage
16+
import coil3.compose.LocalPlatformContext
17+
import coil3.request.ImageRequest
18+
import coil3.request.error
19+
import com.getcode.opencode.model.financial.Fiat
20+
import com.getcode.opencode.model.financial.Token
21+
import com.getcode.opencode.model.financial.TokenWithBalance
22+
import com.getcode.opencode.model.financial.TokenWithLocalizedBalance
23+
import com.getcode.theme.CodeTheme
24+
import com.getcode.ui.components.R
25+
26+
@Composable
27+
fun TokenBalanceRow(
28+
tokenWithBalance: TokenWithLocalizedBalance,
29+
modifier: Modifier = Modifier,
30+
onClick: () -> Unit
31+
) {
32+
val (token, balance) = tokenWithBalance
33+
TokenBalanceRow(
34+
token = token,
35+
balance = balance.converted,
36+
modifier = modifier,
37+
onClick = onClick
38+
)
39+
}
40+
41+
@Composable
42+
fun TokenBalanceRow(
43+
tokenWithBalance: TokenWithBalance,
44+
modifier: Modifier = Modifier,
45+
onClick: () -> Unit
46+
) {
47+
val (token, balance) = tokenWithBalance
48+
TokenBalanceRow(
49+
token = token,
50+
balance = balance,
51+
modifier = modifier,
52+
onClick = onClick
53+
)
54+
}
55+
56+
57+
@Composable
58+
fun TokenBalanceRow(
59+
token: Token,
60+
balance: Fiat,
61+
modifier: Modifier = Modifier,
62+
onClick: () -> Unit
63+
) {
64+
Row(
65+
modifier = Modifier
66+
.clickable(onClick = onClick)
67+
.then(modifier)
68+
.padding(
69+
vertical = CodeTheme.dimens.inset,
70+
),
71+
horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2),
72+
verticalAlignment = Alignment.CenterVertically,
73+
) {
74+
AsyncImage(
75+
modifier = Modifier
76+
.size(CodeTheme.dimens.staticGrid.x5)
77+
.clip(CircleShape),
78+
model = ImageRequest.Builder(LocalPlatformContext.current)
79+
.data(token.imageUrl)
80+
.error(R.drawable.ic_placeholder_user)
81+
.placeholderMemoryCacheKey(token.symbol)
82+
.build(),
83+
contentDescription = null,
84+
)
85+
86+
Text(
87+
text = token.name,
88+
style = CodeTheme.typography.screenTitle,
89+
color = CodeTheme.colors.textMain,
90+
)
91+
92+
Spacer(Modifier.weight(1f))
93+
94+
Text(
95+
text = balance.formatted(),
96+
style = CodeTheme.typography.screenTitle,
97+
color = CodeTheme.colors.textMain,
98+
)
99+
}
100+
}

apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/BalanceScreen.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,6 @@ class BalanceScreen: ModalScreen, NamedScreen, Parcelable {
6161
val viewModel = getActivityScopedViewModel<BalanceViewModel>()
6262
BalanceScreen(viewModel)
6363

64-
RepeatOnLifecycle(Lifecycle.State.RESUMED) {
65-
viewModel.dispatchEvent(BalanceViewModel.Event.ResetSelections)
66-
}
67-
6864
LaunchedEffect(viewModel) {
6965
viewModel.eventFlow
7066
.filterIsInstance<BalanceViewModel.Event.OpenCurrencySelection>()

apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/BalanceScreenContent.kt

Lines changed: 27 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ import androidx.compose.foundation.background
44
import androidx.compose.foundation.layout.Arrangement
55
import androidx.compose.foundation.layout.Box
66
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.PaddingValues
78
import androidx.compose.foundation.layout.Spacer
89
import androidx.compose.foundation.layout.WindowInsets
10+
import androidx.compose.foundation.layout.asPaddingValues
911
import androidx.compose.foundation.layout.fillMaxWidth
1012
import androidx.compose.foundation.layout.navigationBars
1113
import androidx.compose.foundation.layout.padding
1214
import androidx.compose.foundation.layout.windowInsetsPadding
1315
import androidx.compose.foundation.lazy.LazyColumn
16+
import androidx.compose.foundation.lazy.items
17+
import androidx.compose.foundation.lazy.itemsIndexed
1418
import androidx.compose.foundation.lazy.rememberLazyListState
1519
import androidx.compose.material.Divider
1620
import androidx.compose.material.Text
@@ -24,45 +28,32 @@ import androidx.compose.ui.platform.LocalContext
2428
import androidx.compose.ui.res.stringResource
2529
import androidx.compose.ui.text.style.TextAlign
2630
import androidx.compose.ui.tooling.preview.Preview
27-
import androidx.paging.LoadState
28-
import androidx.paging.PagingData
29-
import androidx.paging.compose.LazyPagingItems
30-
import androidx.paging.compose.collectAsLazyPagingItems
31-
import androidx.paging.compose.itemKey
3231
import com.flipcash.app.balance.internal.components.BalanceHeader
33-
import com.flipcash.app.balance.internal.components.FeedItem
34-
import com.flipcash.app.core.feed.ActivityFeedMessage
32+
import com.flipcash.app.core.ui.TokenBalanceRow
3533
import com.flipcash.app.onramp.AddCashRow
3634
import com.flipcash.app.theme.FlipcashDesignSystem
3735
import com.flipcash.features.balance.R
3836
import com.getcode.opencode.compose.ExchangeStub
3937
import com.getcode.opencode.compose.LocalExchange
4038
import com.getcode.opencode.model.financial.CurrencyCode
41-
import com.getcode.opencode.model.financial.LocalFiat
4239
import com.getcode.opencode.model.financial.Rate
43-
import com.getcode.opencode.model.financial.toFiat
40+
import com.getcode.solana.keys.base58
4441
import com.getcode.theme.CodeTheme
4542
import com.getcode.ui.core.verticalScrollStateGradient
46-
import com.getcode.ui.theme.ButtonState
47-
import com.getcode.ui.theme.CodeButton
48-
import kotlinx.coroutines.flow.flowOf
4943

5044
@Composable
5145
internal fun BalanceScreen(viewModel: BalanceViewModel) {
5246
val state by viewModel.stateFlow.collectAsState()
53-
val feed = viewModel.feed.collectAsLazyPagingItems()
5447

5548
BalanceScreenContent(
5649
state = state,
57-
feed = feed,
5850
dispatchEvent = viewModel::dispatchEvent
5951
)
6052
}
6153

6254
@Composable
6355
private fun BalanceScreenContent(
6456
state: BalanceViewModel.State,
65-
feed: LazyPagingItems<ActivityFeedMessage>,
6657
dispatchEvent: (BalanceViewModel.Event) -> Unit
6758
) {
6859
Column {
@@ -85,20 +76,18 @@ private fun BalanceScreenContent(
8576
onWithdraw = { dispatchEvent(BalanceViewModel.Event.OnWithdrawClicked) },
8677
)
8778

88-
FeedList(
79+
TokenList(
8980
modifier = Modifier.weight(1f),
9081
state = state,
91-
feed = feed,
9282
dispatchEvent = dispatchEvent
9383
)
9484
}
9585
}
9686

9787
@Composable
98-
private fun FeedList(
88+
private fun TokenList(
9989
modifier: Modifier = Modifier,
10090
state: BalanceViewModel.State,
101-
feed: LazyPagingItems<ActivityFeedMessage>,
10291
dispatchEvent: (BalanceViewModel.Event) -> Unit
10392
) {
10493
val listState = rememberLazyListState()
@@ -109,16 +98,23 @@ private fun FeedList(
10998
color = CodeTheme.colors.background,
11099
showAtEnd = true
111100
),
101+
contentPadding = PaddingValues(
102+
bottom = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
103+
),
112104
state = listState
113105
) {
114-
if (feed.itemCount == 0 && feed.loadState.append is LoadState.NotLoading) {
106+
if (state.balances != null && state.balances.isEmpty()) {
115107
item {
116108
Box(
117-
modifier = Modifier.fillParentMaxSize().padding(bottom = CodeTheme.dimens.inset),
109+
modifier = Modifier
110+
.fillParentMaxSize()
111+
.padding(bottom = CodeTheme.dimens.inset),
118112
contentAlignment = Alignment.Center
119113
) {
120114
Column(
121-
modifier = Modifier.fillMaxWidth().padding(horizontal = CodeTheme.dimens.inset),
115+
modifier = Modifier
116+
.fillMaxWidth()
117+
.padding(horizontal = CodeTheme.dimens.inset),
122118
verticalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x12),
123119
horizontalAlignment = Alignment.CenterHorizontally
124120
) {
@@ -132,25 +128,16 @@ private fun FeedList(
132128
}
133129
}
134130
} else {
135-
items(feed.itemCount, key = feed.itemKey { item -> item.id }) { index ->
136-
val message = feed[index] ?: return@items
137-
FeedItem(
138-
modifier = Modifier
139-
.fillParentMaxWidth()
140-
.animateItem(),
141-
message = message,
142-
canViewDetails = state.canViewDetails,
143-
isExpanded = state.expandedItem == message.id,
144-
dispatch = dispatchEvent
145-
)
131+
itemsIndexed(
132+
state.balances.orEmpty(),
133+
key = { index, item -> item.token.address.base58() }) { index, item ->
134+
TokenBalanceRow(
135+
modifier = Modifier.fillParentMaxWidth()
136+
.padding(horizontal = CodeTheme.dimens.inset),
137+
tokenWithBalance = item
138+
) { }
146139

147-
if (index < feed.itemCount - 1) {
148-
Divider(color = CodeTheme.colors.dividerVariant)
149-
}
150-
}
151-
152-
item {
153-
Spacer(Modifier.windowInsetsPadding(WindowInsets.navigationBars))
140+
Divider(color = CodeTheme.colors.dividerVariant)
154141
}
155142
}
156143
}
@@ -177,7 +164,6 @@ private fun Preview_BalanceScreen_Empty() {
177164
state = BalanceViewModel.State(
178165
balances = emptyList()
179166
),
180-
feed = flowOf(PagingData.empty<ActivityFeedMessage>()).collectAsLazyPagingItems(),
181167
dispatchEvent = {}
182168
)
183169
}

0 commit comments

Comments
 (0)