Skip to content

Commit fe2dc51

Browse files
committed
chore(tokens): add loading state to token info
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 48863bb commit fe2dc51

4 files changed

Lines changed: 217 additions & 147 deletions

File tree

apps/flipcash/features/tokens/src/main/kotlin/com/flipcash/app/tokens/TokenInfoScreen.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class TokenInfoScreen(
6161
AppBarWithTitle(
6262
isInModal = true,
6363
title = {
64-
state.token?.let { token ->
64+
state.token.dataOrNull?.let { token ->
6565
if (state.isCashReserve && state.cashReservesEnabled) {
6666
AppBarDefaults.Title(text = stringResource(R.string.title_cashReserves))
6767
} else {
@@ -78,9 +78,11 @@ class TokenInfoScreen(
7878
AppBarDefaults.UpNavigation { navigator.pop() }
7979
},
8080
rightContents = {
81-
if (!state.isCashReserve) {
82-
AppBarDefaults.Share {
83-
viewModel.dispatchEvent(TokenInfoViewModel.Event.Share)
81+
state.token.dataOrNull?.let {
82+
if (!state.isCashReserve) {
83+
AppBarDefaults.Share {
84+
viewModel.dispatchEvent(TokenInfoViewModel.Event.Share)
85+
}
8486
}
8587
}
8688
},
Lines changed: 186 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.flipcash.app.tokens.internal
22

3+
import androidx.compose.foundation.Image
34
import androidx.compose.foundation.layout.Arrangement
45
import androidx.compose.foundation.layout.Box
56
import androidx.compose.foundation.layout.PaddingValues
67
import androidx.compose.foundation.layout.Row
78
import androidx.compose.foundation.layout.Spacer
9+
import androidx.compose.foundation.layout.aspectRatio
810
import androidx.compose.foundation.layout.fillMaxSize
911
import androidx.compose.foundation.layout.fillMaxWidth
1012
import androidx.compose.foundation.layout.height
@@ -21,6 +23,9 @@ import androidx.compose.runtime.remember
2123
import androidx.compose.runtime.setValue
2224
import androidx.compose.ui.Alignment
2325
import androidx.compose.ui.Modifier
26+
import androidx.compose.ui.graphics.Color
27+
import androidx.compose.ui.graphics.StrokeCap
28+
import androidx.compose.ui.res.painterResource
2429
import androidx.compose.ui.res.stringResource
2530
import androidx.compose.ui.unit.dp
2631
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -39,6 +44,7 @@ import com.getcode.ui.core.measured
3944
import com.getcode.ui.core.verticalScrollStateGradient
4045
import com.getcode.ui.theme.ButtonState
4146
import com.getcode.ui.theme.CodeButton
47+
import com.getcode.ui.theme.CodeCircularProgressIndicator
4248
import com.getcode.ui.theme.CodeScaffold
4349
import com.getcode.ui.utils.calculateEndPadding
4450
import com.getcode.ui.utils.calculateStartPadding
@@ -79,93 +85,138 @@ private fun TokenInfoScreen(
7985
.sheetResignmentBehavior(listState),
8086
state = listState,
8187
) {
82-
item {
83-
TokenBalance(
84-
modifier = Modifier
85-
.fillMaxWidth()
86-
.padding(horizontal = CodeTheme.dimens.inset),
87-
balance = state.balance.nativeAmount,
88-
appreciation = state.appreciation?.nativeAmount?.takeIf { state.showAppreciation },
89-
onClick = {
90-
dispatch(
91-
TokenInfoViewModel.Event.OpenScreen(
92-
AppRoute.Main.RegionSelection(kind = RegionSelectionKind.Balance)
93-
)
94-
)
88+
when (val loadable = state.token) {
89+
is Loadable.Loading -> {
90+
item {
91+
Box(modifier = Modifier.fillParentMaxSize()) {
92+
Box(
93+
modifier = Modifier
94+
.fillParentMaxSize(0.24f)
95+
.aspectRatio(1f)
96+
.align(Alignment.Center),
97+
) {
98+
CodeCircularProgressIndicator(
99+
modifier = Modifier
100+
.matchParentSize(),
101+
strokeWidth = CodeTheme.dimens.grid.x1,
102+
color = Color.White,
103+
backgroundColor = Color.White.copy(0.30f),
104+
strokeCap = StrokeCap.Butt,
105+
)
106+
}
107+
}
95108
}
96-
)
97-
}
98-
99-
if (!state.isCashReserve && state.showTransactionHistory) {
100-
item {
101-
CodeButton(
102-
modifier = Modifier
103-
.fillParentMaxWidth()
104-
.padding(horizontal = CodeTheme.dimens.inset)
105-
.padding(top = CodeTheme.dimens.grid.x5),
106-
buttonState = ButtonState.Filled10,
107-
text = stringResource(R.string.action_viewTransactionHistory),
108-
) {
109-
dispatch(
110-
TokenInfoViewModel.Event.OpenScreen(
111-
AppRoute.Token.Transactions(state.token?.address!!)
112-
)
109+
}
110+
is Loadable.Error -> {
111+
item {
112+
Box(modifier = Modifier.fillParentMaxSize()) {
113+
Box(
114+
modifier = Modifier
115+
.fillParentMaxSize(0.24f)
116+
.aspectRatio(1f)
117+
.align(Alignment.Center),
118+
) {
119+
Image(
120+
modifier = Modifier
121+
.matchParentSize(),
122+
painter = painterResource(R.drawable.ic_circle_exclamation_large),
123+
contentDescription = null,
124+
)
125+
}
126+
}
127+
}
128+
}
129+
is Loadable.Loaded -> {
130+
item {
131+
TokenBalance(
132+
modifier = Modifier
133+
.fillMaxWidth()
134+
.padding(horizontal = CodeTheme.dimens.inset),
135+
balance = state.balance.nativeAmount,
136+
appreciation = state.appreciation?.nativeAmount?.takeIf { state.showAppreciation },
137+
onClick = {
138+
dispatch(
139+
TokenInfoViewModel.Event.OpenScreen(
140+
AppRoute.Main.RegionSelection(kind = RegionSelectionKind.Balance)
141+
)
142+
)
143+
}
113144
)
114145
}
115146

116-
Divider(
117-
modifier = Modifier.padding(
118-
horizontal = CodeTheme.dimens.inset,
119-
vertical = CodeTheme.dimens.grid.x5
120-
),
121-
color = CodeTheme.colors.dividerVariant,
122-
)
123-
}
124-
}
147+
if (!state.isCashReserve && state.showTransactionHistory) {
148+
item {
149+
CodeButton(
150+
modifier = Modifier
151+
.fillParentMaxWidth()
152+
.padding(horizontal = CodeTheme.dimens.inset)
153+
.padding(top = CodeTheme.dimens.grid.x5),
154+
buttonState = ButtonState.Filled10,
155+
text = stringResource(R.string.action_viewTransactionHistory),
156+
) {
157+
dispatch(
158+
TokenInfoViewModel.Event.OpenScreen(
159+
AppRoute.Token.Transactions(loadable.data.address)
160+
)
161+
)
162+
}
125163

126-
// currency info
127-
item {
128-
if (state.isCashReserve && state.cashReservesEnabled) {
129-
Text(
130-
modifier = Modifier
131-
.fillParentMaxWidth()
132-
.padding(horizontal = CodeTheme.dimens.inset),
133-
text = state.token?.description.orEmpty(),
134-
style = CodeTheme.typography.textMedium,
135-
color = CodeTheme.colors.textSecondary,
136-
)
137-
} else {
138-
TokenDetailsSection(
139-
modifier = Modifier
140-
.fillParentMaxWidth(),
141-
state = state,
142-
dispatch = dispatch
143-
)
144-
}
145-
}
164+
Divider(
165+
modifier = Modifier.padding(
166+
horizontal = CodeTheme.dimens.inset,
167+
vertical = CodeTheme.dimens.grid.x5
168+
),
169+
color = CodeTheme.colors.dividerVariant,
170+
)
171+
}
172+
}
146173

147-
if (!state.isCashReserve) {
148-
// market cap
149-
state.marketCap?.let { mcap ->
150-
val loadable = state.historicalMarketCapData[state.selectedPeriod] ?: Loadable.Loaded(emptyList())
174+
// currency info
151175
item {
152-
MarketCapSection(
153-
modifier = Modifier
154-
.fillParentMaxWidth(),
155-
contentPadding = PaddingValues(horizontal = CodeTheme.dimens.inset),
156-
marketCap = mcap,
157-
chartEnabled = state.marketCapChartEnabled,
158-
selectedPeriod = state.selectedPeriod,
159-
rawHistoricalData = loadable,
160-
onPeriodSelected = {
161-
dispatch(TokenInfoViewModel.Event.OnMarketCapPeriodSelected(it))
162-
},
163-
)
176+
if (state.isCashReserve && state.cashReservesEnabled) {
177+
Text(
178+
modifier = Modifier
179+
.fillParentMaxWidth()
180+
.padding(horizontal = CodeTheme.dimens.inset),
181+
text = loadable.data.description,
182+
style = CodeTheme.typography.textMedium,
183+
color = CodeTheme.colors.textSecondary,
184+
)
185+
} else {
186+
TokenDetailsSection(
187+
modifier = Modifier
188+
.fillParentMaxWidth(),
189+
state = state,
190+
dispatch = dispatch
191+
)
192+
}
193+
}
194+
195+
if (!state.isCashReserve) {
196+
// market cap
197+
state.marketCap?.let { mcap ->
198+
val loadable = state.historicalMarketCapData[state.selectedPeriod] ?: Loadable.Loaded(emptyList())
199+
item {
200+
MarketCapSection(
201+
modifier = Modifier
202+
.fillParentMaxWidth(),
203+
contentPadding = PaddingValues(horizontal = CodeTheme.dimens.inset),
204+
marketCap = mcap,
205+
chartEnabled = state.marketCapChartEnabled,
206+
selectedPeriod = state.selectedPeriod,
207+
rawHistoricalData = loadable,
208+
onPeriodSelected = {
209+
dispatch(TokenInfoViewModel.Event.OnMarketCapPeriodSelected(it))
210+
},
211+
)
212+
}
213+
}
164214
}
215+
216+
item { Spacer(Modifier.height(innerPadding.calculateBottomPadding())) }
165217
}
166218
}
167219

168-
item { Spacer(Modifier.height(innerPadding.calculateBottomPadding())) }
169220
}
170221
}
171222
}
@@ -213,65 +264,71 @@ private fun BottomBarButtons(
213264
modifier: Modifier = Modifier,
214265
dispatch: (TokenInfoViewModel.Event) -> Unit
215266
) {
216-
Row(
217-
modifier = modifier
218-
.fillMaxWidth(),
219-
verticalAlignment = Alignment.CenterVertically,
220-
horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2),
221-
) {
222-
if (state.isCashReserve && state.cashReservesEnabled) {
223-
CodeButton(
224-
modifier = Modifier.weight(1f),
225-
buttonState = ButtonState.Filled,
226-
text = stringResource(R.string.action_withdraw),
267+
when (val loadable = state.token) {
268+
is Loadable.Error -> Unit
269+
is Loadable.Loaded -> {
270+
Row(
271+
modifier = modifier
272+
.fillMaxWidth(),
273+
verticalAlignment = Alignment.CenterVertically,
274+
horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2),
227275
) {
228-
dispatch(
229-
TokenInfoViewModel.Event.OpenScreen(
230-
AppRoute.Transfers.Withdrawal.Amount(state.mint!!)
231-
)
232-
)
233-
}
234-
} else if (state.cashReservesEnabled) {
235-
CodeButton(
236-
modifier = Modifier.weight(1f),
237-
buttonState = ButtonState.Filled,
238-
text = stringResource(R.string.action_buy),
239-
) {
240-
dispatch(TokenInfoViewModel.Event.OpenPurchaseMethods(forNeededFunds = isForNeededFunds))
241-
}
276+
if (state.isCashReserve && state.cashReservesEnabled) {
277+
CodeButton(
278+
modifier = Modifier.weight(1f),
279+
buttonState = ButtonState.Filled,
280+
text = stringResource(R.string.action_withdraw),
281+
) {
282+
dispatch(
283+
TokenInfoViewModel.Event.OpenScreen(
284+
AppRoute.Transfers.Withdrawal.Amount(loadable.data.address)
285+
)
286+
)
287+
}
288+
} else if (state.cashReservesEnabled) {
289+
CodeButton(
290+
modifier = Modifier.weight(1f),
291+
buttonState = ButtonState.Filled,
292+
text = stringResource(R.string.action_buy),
293+
) {
294+
dispatch(TokenInfoViewModel.Event.OpenPurchaseMethods(forNeededFunds = isForNeededFunds))
295+
}
242296

243-
if (state.canSell) {
244-
CodeButton(
245-
modifier = Modifier
246-
.weight(1f),
247-
buttonState = ButtonState.Filled20,
248-
text = stringResource(R.string.action_sell),
249-
) {
250-
dispatch(
251-
TokenInfoViewModel.Event.OpenScreen(
252-
AppRoute.Token.SwapTransact(
253-
purpose = TokenSwapPurpose.Sell(state.token!!.address),
297+
if (state.canSell) {
298+
CodeButton(
299+
modifier = Modifier
300+
.weight(1f),
301+
buttonState = ButtonState.Filled20,
302+
text = stringResource(R.string.action_sell),
303+
) {
304+
dispatch(
305+
TokenInfoViewModel.Event.OpenScreen(
306+
AppRoute.Token.SwapTransact(
307+
purpose = TokenSwapPurpose.Sell(loadable.data.address),
308+
)
309+
)
310+
)
311+
}
312+
}
313+
} else {
314+
CodeButton(
315+
modifier = Modifier
316+
.fillMaxWidth()
317+
.padding(horizontal = CodeTheme.dimens.inset)
318+
.padding(bottom = CodeTheme.dimens.grid.x3)
319+
.navigationBarsPadding(),
320+
buttonState = ButtonState.Filled,
321+
text = stringResource(R.string.action_give),
322+
) {
323+
dispatch(
324+
TokenInfoViewModel.Event.OpenScreen(
325+
AppRoute.Main.Give(mint = loadable.data.address)
254326
)
255327
)
256-
)
328+
}
257329
}
258330
}
259-
} else {
260-
CodeButton(
261-
modifier = Modifier
262-
.fillMaxWidth()
263-
.padding(horizontal = CodeTheme.dimens.inset)
264-
.padding(bottom = CodeTheme.dimens.grid.x3)
265-
.navigationBarsPadding(),
266-
buttonState = ButtonState.Filled,
267-
text = stringResource(R.string.action_give),
268-
) {
269-
dispatch(
270-
TokenInfoViewModel.Event.OpenScreen(
271-
AppRoute.Main.Give(mint = state.token?.address!!)
272-
)
273-
)
274-
}
275331
}
332+
is Loadable.Loading -> Unit
276333
}
277334
}

0 commit comments

Comments
 (0)