Skip to content

Commit 7af2f68

Browse files
committed
feat(give): add token selection
Signed-off-by: Brandon McAnsh <[email protected]>
1 parent 797d16d commit 7af2f68

File tree

16 files changed

+226
-57
lines changed

16 files changed

+226
-57
lines changed

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ sealed interface AppRoute : ScreenProvider, Parcelable {
3838
// TODO: is there a better place for this to live?
3939
data class RegionSelection(val kind: RegionSelectionKind) : Main
4040

41-
data class Give(val mint: Mint) : Main
41+
data class Give(val mint: Mint? = null) : Main
4242
}
4343

4444
@Parcelize

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/onramp/deeplinks/WalletDeeplinkConnectionResult.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ sealed class OnRampDeeplinkOrigin: Parcelable {
3636
data object Menu : OnRampDeeplinkOrigin()
3737

3838
@Parcelize
39-
data class Give(val tokenAddress: PublicKey) : OnRampDeeplinkOrigin()
39+
data class Give(val tokenAddress: PublicKey?) : OnRampDeeplinkOrigin()
4040

4141
@Parcelize
4242
data object Wallet: OnRampDeeplinkOrigin()
@@ -55,7 +55,7 @@ sealed class OnRampDeeplinkOrigin: Parcelable {
5555
is PoolWithId -> "pool-id_${id.base58}"
5656
is PoolWithRendezvous -> "pool-seed_${keyPair.seed.base64}"
5757
Menu -> "menu"
58-
is Give -> "give-${tokenAddress.base58()}"
58+
is Give -> "give-${tokenAddress?.base58()}"
5959
Wallet -> "wallet"
6060
Reserves -> "reserves"
6161
}.lowercase()

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/tokens/TokenPurpose.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import kotlinx.parcelize.Parcelize
55

66
@Parcelize
77
sealed interface TokenPurpose: Parcelable {
8-
data object Send : TokenPurpose
8+
data object Select : TokenPurpose
99
data object Withdraw: TokenPurpose
1010
data object Deposit: TokenPurpose
1111
data object Balance : TokenPurpose

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/ui/TokenBalanceRow.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,18 @@ import androidx.compose.foundation.layout.height
1010
import androidx.compose.foundation.layout.padding
1111
import androidx.compose.foundation.layout.size
1212
import androidx.compose.foundation.layout.width
13+
import androidx.compose.foundation.layout.wrapContentWidth
1314
import androidx.compose.foundation.shape.CircleShape
1415
import androidx.compose.material.Text
1516
import androidx.compose.runtime.Composable
1617
import androidx.compose.ui.Alignment
1718
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.draw.alpha
1820
import androidx.compose.ui.draw.clip
1921
import androidx.compose.ui.res.painterResource
2022
import androidx.compose.ui.text.TextStyle
2123
import androidx.compose.ui.unit.dp
24+
import com.flipcash.core.R
2225
import com.getcode.opencode.compose.LocalExchange
2326
import com.getcode.opencode.model.financial.Fiat
2427
import com.getcode.opencode.model.financial.Token
@@ -33,6 +36,7 @@ fun TokenBalanceRow(
3336
modifier: Modifier = Modifier,
3437
showName: Boolean = true,
3538
showFlag: Boolean = false,
39+
isSelected: Boolean? = null,
3640
formattedBalance: (Fiat) -> String = { it.formatted() },
3741
horizontalArrangement: Arrangement.Horizontal = Arrangement.SpaceBetween,
3842
nameTextStyle: TextStyle = CodeTheme.typography.screenTitle,
@@ -47,6 +51,7 @@ fun TokenBalanceRow(
4751
formattedBalance = formattedBalance,
4852
nameTextStyle = nameTextStyle,
4953
balanceTextStyle = balanceTextStyle,
54+
isSelected = isSelected,
5055
modifier = modifier,
5156
showName = showName,
5257
showFlag = showFlag,
@@ -62,6 +67,7 @@ fun TokenBalanceRow(
6267
modifier: Modifier = Modifier,
6368
showName: Boolean = true,
6469
showFlag: Boolean = false,
70+
isSelected: Boolean? = null,
6571
formattedBalance: (Fiat) -> String = { it.formatted() },
6672
horizontalArrangement: Arrangement.Horizontal = Arrangement.SpaceBetween,
6773
nameTextStyle: TextStyle = CodeTheme.typography.screenTitle,
@@ -75,6 +81,7 @@ fun TokenBalanceRow(
7581
balance = balance,
7682
showName = showName,
7783
showFlag = showFlag,
84+
isSelected = isSelected,
7885
modifier = modifier,
7986
nameTextStyle = nameTextStyle,
8087
balanceTextStyle = balanceTextStyle,
@@ -93,6 +100,7 @@ fun TokenBalanceRow(
93100
modifier: Modifier = Modifier,
94101
showName: Boolean = true,
95102
showFlag: Boolean = false,
103+
isSelected: Boolean? = null,
96104
formattedBalance: (Fiat) -> String = { it.formatted() },
97105
horizontalArrangement: Arrangement.Horizontal = Arrangement.SpaceBetween,
98106
nameTextStyle: TextStyle = CodeTheme.typography.screenTitle,
@@ -151,6 +159,19 @@ fun TokenBalanceRow(
151159
style = balanceTextStyle,
152160
color = CodeTheme.colors.textMain,
153161
)
162+
163+
if (isSelected != null) {
164+
Image(
165+
modifier = Modifier
166+
.wrapContentWidth()
167+
.padding(start = CodeTheme.dimens.grid.x3),
168+
painter = painterResource(
169+
if (isSelected)
170+
R.drawable.ic_checked else R.drawable.ic_unchecked
171+
),
172+
contentDescription = ""
173+
)
174+
}
154175
}
155176
}
156177
}
Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2-
android:width="26dp"
3-
android:height="26dp"
4-
android:viewportWidth="26"
5-
android:viewportHeight="26">
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
66
<path
7-
android:strokeWidth="1"
8-
android:pathData="M13,13m-12,0a12,12 0,1 1,24 0a12,12 0,1 1,-24 0"
9-
android:fillColor="#738179"
10-
android:strokeColor="#738179"/>
11-
<path
12-
android:pathData="M8.4145,11.9495l4.2426,4.2426l-1.4142,1.4142l-4.2426,-4.2426z"
7+
android:pathData="M12,0L12,0A12,12 0,0 1,24 12L24,12A12,12 0,0 1,12 24L12,24A12,12 0,0 1,0 12L0,12A12,12 0,0 1,12 0z"
138
android:fillColor="#ffffff"/>
149
<path
15-
android:pathData="M9.8286,16.1921l7.0711,-7.0711l1.4142,1.4142l-7.0711,7.0711z"
16-
android:fillColor="#ffffff"/>
10+
android:pathData="M10.711,18.174C10.305,18.174 10,18.028 9.728,17.705L6.452,13.737C6.224,13.477 6.141,13.242 6.141,12.976C6.141,12.379 6.585,11.941 7.201,11.941C7.557,11.941 7.811,12.068 8.045,12.347L10.68,15.616L15.872,7.428C16.126,7.028 16.386,6.875 16.805,6.875C17.415,6.875 17.859,7.307 17.859,7.904C17.859,8.126 17.789,8.361 17.624,8.615L11.708,17.66C11.479,18.009 11.143,18.174 10.711,18.174Z"
11+
android:fillColor="#001A0C"/>
1712
</vector>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:strokeWidth="1"
8+
android:pathData="M12,0.5L12,0.5A11.5,11.5 0,0 1,23.5 12L23.5,12A11.5,11.5 0,0 1,12 23.5L12,23.5A11.5,11.5 0,0 1,0.5 12L0.5,12A11.5,11.5 0,0 1,12 0.5z"
9+
android:fillColor="#00000000"
10+
android:strokeColor="#ffffff"/>
11+
</vector>

apps/flipcash/features/cash/src/main/kotlin/com/flipcash/app/cash/CashScreen.kt

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,45 @@
11
package com.flipcash.app.cash
22

33
import android.os.Parcelable
4+
import androidx.compose.foundation.Image
5+
import androidx.compose.foundation.layout.Arrangement
6+
import androidx.compose.foundation.layout.Box
47
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.Row
59
import androidx.compose.foundation.layout.fillMaxSize
10+
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.width
12+
import androidx.compose.foundation.layout.wrapContentHeight
13+
import androidx.compose.foundation.layout.wrapContentWidth
14+
import androidx.compose.foundation.shape.CircleShape
615
import androidx.compose.runtime.Composable
716
import androidx.compose.runtime.LaunchedEffect
17+
import androidx.compose.runtime.getValue
818
import androidx.compose.ui.Alignment
919
import androidx.compose.ui.Modifier
20+
import androidx.compose.ui.draw.clip
21+
import androidx.compose.ui.res.painterResource
1022
import androidx.compose.ui.res.stringResource
23+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
1124
import cafe.adriel.voyager.core.registry.ScreenRegistry
1225
import cafe.adriel.voyager.core.screen.ScreenKey
1326
import cafe.adriel.voyager.core.screen.uniqueScreenKey
1427
import cafe.adriel.voyager.hilt.getViewModel
1528
import cafe.adriel.voyager.navigator.currentOrThrow
1629
import com.flipcash.app.cash.internal.CashScreenViewModel
1730
import com.flipcash.app.cash.internal.GiveScreenContent
31+
import com.flipcash.app.core.AppRoute
32+
import com.flipcash.app.core.tokens.TokenPurpose
33+
import com.flipcash.app.core.ui.TokenIconWithName
1834
import com.flipcash.app.session.LocalSessionController
1935
import com.flipcash.features.cash.R
2036
import com.getcode.navigation.core.LocalCodeNavigator
2137
import com.getcode.navigation.modal.ModalScreen
2238
import com.getcode.solana.keys.Mint
39+
import com.getcode.theme.CodeTheme
40+
import com.getcode.ui.components.AppBarDefaults
2341
import com.getcode.ui.components.AppBarWithTitle
42+
import com.getcode.ui.core.rememberedClickable
2443
import kotlinx.coroutines.flow.filterIsInstance
2544
import kotlinx.coroutines.flow.launchIn
2645
import kotlinx.coroutines.flow.map
@@ -30,8 +49,8 @@ import kotlinx.parcelize.Parcelize
3049

3150
@Parcelize
3251
class CashScreen(
33-
private val selectedMint: Mint
34-
): ModalScreen, Parcelable {
52+
private val selectedMint: Mint?
53+
) : ModalScreen, Parcelable {
3554

3655
@IgnoredOnParcel
3756
override val key: ScreenKey = uniqueScreenKey
@@ -42,6 +61,7 @@ class CashScreen(
4261
val session = LocalSessionController.currentOrThrow
4362

4463
val viewModel = getViewModel<CashScreenViewModel>()
64+
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
4565

4666
LaunchedEffect(viewModel) {
4767
viewModel.eventFlow
@@ -59,16 +79,58 @@ class CashScreen(
5979
) {
6080
AppBarWithTitle(
6181
isInModal = true,
62-
title = stringResource(R.string.title_enterAmount),
63-
titleAlignment = Alignment.CenterHorizontally,
64-
backButton = true,
65-
onBackIconClicked = { navigator.pop() },
82+
title = {
83+
Box(
84+
modifier = Modifier
85+
.fillMaxWidth()
86+
.wrapContentHeight(),
87+
contentAlignment = Alignment.Center
88+
) {
89+
Row(
90+
modifier = Modifier
91+
.clip(CircleShape)
92+
.rememberedClickable {
93+
navigator.push(
94+
ScreenRegistry.get(
95+
AppRoute.Sheets.TokenSelection(TokenPurpose.Select)
96+
)
97+
)
98+
},
99+
verticalAlignment = Alignment.CenterVertically,
100+
horizontalArrangement = Arrangement.spacedBy(
101+
space = CodeTheme.dimens.grid.x1,
102+
alignment = Alignment.CenterHorizontally
103+
)
104+
) {
105+
state.token?.let { (token, _) ->
106+
TokenIconWithName(
107+
token = token,
108+
imageSize = CodeTheme.dimens.staticGrid.x5,
109+
spacing = CodeTheme.dimens.grid.x1,
110+
)
111+
112+
Image(
113+
modifier = Modifier
114+
.width(CodeTheme.dimens.grid.x4),
115+
painter = painterResource(R.drawable.ic_dropdown),
116+
contentDescription = ""
117+
)
118+
}
119+
120+
}
121+
}
122+
},
123+
rightContents = {
124+
AppBarDefaults.Close { navigator.hide() }
125+
}
66126
)
67127
GiveScreenContent(viewModel)
68128
}
69129

70130
LaunchedEffect(viewModel, selectedMint) {
71-
viewModel.dispatchEvent(CashScreenViewModel.Event.OnTokenSelected(selectedMint))
131+
selectedMint?.let {
132+
viewModel.dispatchEvent(CashScreenViewModel.Event.OnTokenSelected(it))
133+
}
72134
}
73135

74136
LaunchedEffect(viewModel) {

apps/flipcash/features/cash/src/main/kotlin/com/flipcash/app/cash/internal/CashScreenViewModel.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import com.getcode.view.LoadingSuccessState
3434
import dagger.hilt.android.lifecycle.HiltViewModel
3535
import kotlinx.coroutines.Dispatchers
3636
import kotlinx.coroutines.flow.combine
37+
import kotlinx.coroutines.flow.distinctUntilChanged
3738
import kotlinx.coroutines.flow.filter
3839
import kotlinx.coroutines.flow.filterIsInstance
3940
import kotlinx.coroutines.flow.filterNotNull
@@ -194,6 +195,11 @@ internal class CashScreenViewModel @Inject constructor(
194195
exchange.fetchRatesIfNeeded()
195196
}
196197

198+
tokenController.observeSelectedTokenMint()
199+
.distinctUntilChanged()
200+
.onEach { dispatchEvent(Event.OnTokenSelected(it)) }
201+
.launchIn(viewModelScope)
202+
197203
stateFlow
198204
.mapNotNull { it.selectedTokenAddress }
199205
.flatMapLatest { tokenAddress ->

apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/ScannerDecorItem.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import com.flipcash.app.core.AppRoute
44
import com.flipcash.app.core.tokens.TokenPurpose
55

66
sealed class ScannerDecorItem(val screen: AppRoute) {
7-
data object Give : ScannerDecorItem(AppRoute.Sheets.TokenSelection(TokenPurpose.Send))
7+
data object Give : ScannerDecorItem(AppRoute.Main.Give())
88

99
data object Wallet : ScannerDecorItem(AppRoute.Sheets.Wallet)
1010
data object Menu : ScannerDecorItem(AppRoute.Sheets.Menu)

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

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import cafe.adriel.voyager.core.registry.ScreenRegistry
1212
import cafe.adriel.voyager.core.screen.ScreenKey
1313
import cafe.adriel.voyager.core.screen.uniqueScreenKey
1414
import cafe.adriel.voyager.hilt.getViewModel
15-
import com.flipcash.app.core.AppRoute.Main.Give
1615
import com.flipcash.app.core.AppRoute.Menu.Deposit
1716
import com.flipcash.app.core.AppRoute.Transfers.Withdrawal.Amount
1817
import com.flipcash.app.core.tokens.TokenPurpose
@@ -21,8 +20,8 @@ import com.flipcash.features.tokens.R
2120
import com.getcode.navigation.core.LocalCodeNavigator
2221
import com.getcode.navigation.modal.ModalScreen
2322
import com.getcode.navigation.screens.NamedScreen
24-
import com.getcode.ui.components.AppBarDefaults
2523
import com.getcode.ui.components.AppBarWithTitle
24+
import kotlinx.coroutines.flow.filter
2625
import kotlinx.coroutines.flow.filterIsInstance
2726
import kotlinx.coroutines.flow.launchIn
2827
import kotlinx.coroutines.flow.map
@@ -49,14 +48,9 @@ class SelectTokenScreen(private val purpose: TokenPurpose) : ModalScreen, NamedS
4948
AppBarWithTitle(
5049
isInModal = true,
5150
title = name,
52-
backButton = purpose !is TokenPurpose.Send,
51+
backButton = true,
5352
onBackIconClicked = { navigator.pop() },
5453
titleAlignment = Alignment.CenterHorizontally,
55-
endContent = {
56-
if (purpose is TokenPurpose.Send) {
57-
AppBarDefaults.Close { navigator.hide() }
58-
}
59-
},
6054
)
6155
val viewModel = getViewModel<SelectTokenViewModel>()
6256
SelectTokenScreen(viewModel)
@@ -76,23 +70,20 @@ class SelectTokenScreen(private val purpose: TokenPurpose) : ModalScreen, NamedS
7670
LaunchedEffect(viewModel) {
7771
viewModel.eventFlow
7872
.filterIsInstance<SelectTokenViewModel.Event.OnTokenSelected>()
79-
.map { it.token }
73+
.filter { it.fromUser }
74+
.map { it.mint }
8075
.onEach { token ->
8176
when (purpose) {
8277
TokenPurpose.Balance -> Unit
83-
TokenPurpose.Send -> {
84-
navigator.push(
85-
ScreenRegistry.get(Give(token.address))
86-
)
87-
}
78+
TokenPurpose.Select -> navigator.pop()
8879
TokenPurpose.Withdraw -> {
8980
navigator.push(
90-
ScreenRegistry.get(Amount(token.address))
81+
ScreenRegistry.get(Amount(token))
9182
)
9283
}
9384
TokenPurpose.Deposit -> {
9485
navigator.push(
95-
ScreenRegistry.get(Deposit(token.address))
86+
ScreenRegistry.get(Deposit(token))
9687
)
9788
}
9889
}

0 commit comments

Comments
 (0)