@@ -3,23 +3,43 @@ package com.flipcash.app.directsend
33import androidx.compose.runtime.Composable
44import androidx.compose.runtime.LaunchedEffect
55import androidx.compose.runtime.getValue
6+ import androidx.compose.runtime.mutableStateOf
7+ import androidx.compose.runtime.remember
8+ import androidx.compose.runtime.setValue
69import androidx.hilt.navigation.compose.hiltViewModel
710import androidx.lifecycle.compose.collectAsStateWithLifecycle
811import androidx.navigation3.runtime.NavEntry
912import androidx.navigation3.runtime.NavKey
1013import androidx.navigation3.runtime.entryProvider
14+ import com.flipcash.app.contacts.device.DeviceContact
15+ import com.flipcash.app.core.AppRoute
1116import com.flipcash.app.core.send.SendResult
1217import com.flipcash.app.core.send.SendStep
18+ import com.flipcash.app.core.tokens.TokenPurpose
19+ import com.flipcash.app.core.ui.ConfirmationStyle
20+ import com.flipcash.app.core.ui.TokenSelectionPill
1321import com.flipcash.app.directsend.internal.SendFlowViewModel
22+ import com.flipcash.app.directsend.internal.screens.AmountEntryResult
23+ import com.flipcash.app.directsend.internal.screens.AmountEntryScreen
1424import com.flipcash.app.directsend.internal.screens.ContactListScreen
1525import com.flipcash.app.directsend.internal.screens.ContactsPermissionGateScreen
1626import com.flipcash.app.directsend.internal.screens.PhoneGateLandingScreen
27+ import com.flipcash.features.directsend.R
28+ import com.getcode.manager.BottomBarManager
1729import com.getcode.navigation.annotatedEntry
1830import com.getcode.navigation.flow.FlowExitReason
1931import com.getcode.navigation.flow.FlowHost
20- import com.getcode.navigation.results.NavResultStateRegistry
32+ import com.getcode.navigation.flow.LocalOuterCodeNavigator
2133import com.getcode.navigation.flow.flowSharedViewModel
34+ import com.getcode.navigation.flow.rememberFlowNavigator
35+ import com.getcode.navigation.results.NavResultStateRegistry
2236import com.getcode.navigation.scenes.LocalBottomSheetDismissDispatcher
37+ import com.getcode.opencode.model.financial.Fiat
38+ import com.getcode.solana.keys.PublicKey
39+ import com.getcode.util.resources.LocalResources
40+ import kotlinx.coroutines.flow.filterIsInstance
41+ import kotlinx.coroutines.flow.launchIn
42+ import kotlinx.coroutines.flow.onEach
2343
2444@Composable
2545fun SendFlowScreen (resultStateRegistry : NavResultStateRegistry ) {
@@ -42,19 +62,107 @@ fun SendFlowScreen(resultStateRegistry: NavResultStateRegistry) {
4262 )
4363}
4464
45- private fun sendEntryProvider (): (NavKey ) -> NavEntry <NavKey > = entryProvider {
46- annotatedEntry<SendStep .PhoneGate > {
47- SyncStep (it)
48- PhoneGateLandingScreen ()
65+ @Composable
66+ private fun sendEntryProvider (): (NavKey ) -> NavEntry <NavKey > {
67+ return entryProvider {
68+ annotatedEntry<SendStep .PhoneGate > {
69+ SyncStep (it)
70+ PhoneGateLandingScreen ()
71+ }
72+ annotatedEntry<SendStep .ContactsGate > {
73+ SyncStep (it)
74+ ContactsPermissionGateScreen ()
75+ }
76+ annotatedEntry<SendStep .ContactList > {
77+ SyncStep (it)
78+ ContactListScreen ()
79+ }
80+ annotatedEntry<SendStep .AmountEntry > { step ->
81+ SyncStep (step)
82+ SendAmountEntryScreen ()
83+ }
4984 }
50- annotatedEntry<SendStep .ContactsGate > {
51- SyncStep (it)
52- ContactsPermissionGateScreen ()
85+ }
86+
87+ @Composable
88+ private fun SendAmountEntryScreen () {
89+ val flowNavigator = rememberFlowNavigator<SendStep , SendResult >()
90+ val sharedVm = flowSharedViewModel<SendFlowViewModel >()
91+ val sharedState by sharedVm.stateFlow.collectAsStateWithLifecycle()
92+ val resources = LocalResources .current
93+
94+ var contact by remember {
95+ mutableStateOf<DeviceContact ?>(null )
5396 }
54- annotatedEntry< SendStep . ContactList > {
55- SyncStep (it)
56- ContactListScreen ( )
97+
98+ var resolvedAuthority by remember {
99+ mutableStateOf< PublicKey ?>( null )
57100 }
101+
102+ LaunchedEffect (Unit ) {
103+ sharedVm.eventFlow
104+ .filterIsInstance<SendFlowViewModel .Event .ResolveCompleted >()
105+ .onEach {
106+ contact = it.contact
107+ resolvedAuthority = it.authority
108+ }
109+ .launchIn(this )
110+ }
111+
112+ LaunchedEffect (sharedVm) {
113+ sharedVm.eventFlow
114+ .filterIsInstance<SendFlowViewModel .Event .SendComplete >()
115+ .onEach { event ->
116+ BottomBarManager .showInfo(
117+ title = resources.getString(R .string.prompt_title_fundsSentToContact),
118+ message = resources.getString(
119+ R .string.prompt_description_fundsSentToContact,
120+ event.amount.formatted(rule = Fiat .FormattingRule .Truncated ),
121+ contact?.displayName ? : " your selected recipient"
122+ ),
123+ onDismiss = { flowNavigator.back() }
124+ )
125+ }.launchIn(this )
126+ }
127+
128+ AmountEntryScreen (
129+ title = { token ->
130+ TokenSelectionPill (token) {
131+ flowNavigator.navigate(
132+ AppRoute .Sheets .TokenSelection (TokenPurpose .Select )
133+ )
134+ }
135+ },
136+ // region lives outside the flow
137+ // flow replaces LocalCodeNavigator for the flow as the "inner" navigator
138+ navigator = LocalOuterCodeNavigator .current,
139+ canChangeCurrency = true ,
140+ confirmationStyle = ConfirmationStyle .Slide ,
141+ confirmationState = sharedState.sendProgress,
142+ onResult = { result ->
143+ when (result) {
144+ AmountEntryResult .Cancelled -> flowNavigator.back()
145+ is AmountEntryResult .Confirmed -> {
146+ val destination = resolvedAuthority
147+ if (destination == null ) {
148+ BottomBarManager .showAlert(
149+ title = resources.getString(R .string.error_title_contactNotOnFlipcash),
150+ message = resources.getString(R .string.error_description_contactNotOnFlipcash),
151+ onDismiss = { flowNavigator.back() }
152+ )
153+ } else {
154+ sharedVm.dispatchEvent(
155+ SendFlowViewModel .Event .OnSendRequested (
156+ amount = result.amount,
157+ token = result.token,
158+ destinationOwner = destination,
159+ )
160+ )
161+ }
162+ }
163+ }
164+ },
165+ )
58166}
59167
60168@Composable
0 commit comments