Skip to content

Commit c72c683

Browse files
authored
Merge pull request #440 from urnetwork/redeem-balance-code
redeem transfer balance code
2 parents dcab1a6 + f639ac2 commit c72c683

24 files changed

Lines changed: 379 additions & 16 deletions

File tree

app/app/src/main/java/com/bringyour/network/ui/MainNavHost.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ import com.bringyour.network.ui.shared.viewmodels.SolanaPaymentViewModel
114114
import com.bringyour.network.ui.theme.Pink
115115
import com.bringyour.network.ui.upgrade.UpgradeScreen
116116
import com.bringyour.sdk.ReliabilityWindow
117+
import kotlinx.coroutines.delay
117118
import kotlinx.coroutines.launch
118119

119120
@OptIn(ExperimentalMaterial3Api::class)
@@ -316,8 +317,8 @@ fun MainNavHost(
316317
toggleProvideCell = settingsViewModel.toggleAllowProvideOnCell,
317318
planViewModel = planViewModel,
318319
solanaPaymentViewModel = solanaPaymentViewModel,
319-
isCheckingSolanaTransaction = isCheckingSolanaTransaction
320-
// overlayViewModel = overlayViewModel
320+
isCheckingSolanaTransaction = isCheckingSolanaTransaction,
321+
overlayViewModel = overlayViewModel
321322
)
322323
} else {
323324

@@ -509,9 +510,11 @@ fun IntroNavHost(
509510
planViewModel: PlanViewModel,
510511
isCheckingSolanaTransaction: Boolean,
511512
solanaPaymentViewModel: SolanaPaymentViewModel,
513+
overlayViewModel: OverlayViewModel
512514
) {
513515

514516
val introNavController = rememberNavController()
517+
val scope = rememberCoroutineScope()
515518

516519
NavHost(
517520
navController = introNavController,
@@ -529,6 +532,15 @@ fun IntroNavHost(
529532
subscriptionBalanceViewModel.pollSubscriptionBalance()
530533
dismiss()
531534
},
535+
onRedeemTransferBalanceCodeSuccess = {
536+
subscriptionBalanceViewModel.pollSubscriptionBalance()
537+
overlayViewModel.launch(OverlayMode.Upgrade)
538+
scope.launch {
539+
// bandaid for overlapping modal state getting weird
540+
delay(1000)
541+
dismiss()
542+
}
543+
},
532544
isCheckingSolanaTransaction = isCheckingSolanaTransaction
533545
)
534546
}

app/app/src/main/java/com/bringyour/network/ui/account/AccountScreen.kt

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,26 @@ import androidx.compose.foundation.verticalScroll
2222
import androidx.compose.material.icons.Icons
2323
import androidx.compose.material.icons.filled.ArrowOutward
2424
import androidx.compose.material.icons.filled.LocationOn
25+
import androidx.compose.material3.Button
2526
import androidx.compose.material3.CircularProgressIndicator
2627
import androidx.compose.material3.ExperimentalMaterial3Api
2728
import androidx.compose.material3.HorizontalDivider
2829
import androidx.compose.material3.Icon
2930
import androidx.compose.material3.MaterialTheme
3031
import androidx.compose.material3.Scaffold
3132
import androidx.compose.material3.Text
33+
import androidx.compose.material3.TextButton
3234
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
3335
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
36+
import androidx.compose.material3.rememberModalBottomSheetState
3437
import androidx.compose.runtime.Composable
3538
import androidx.compose.runtime.LaunchedEffect
3639
import androidx.compose.runtime.collectAsState
3740
import androidx.compose.runtime.getValue
41+
import androidx.compose.runtime.mutableStateOf
42+
import androidx.compose.runtime.remember
3843
import androidx.compose.runtime.rememberCoroutineScope
44+
import androidx.compose.runtime.setValue
3945
import androidx.compose.ui.Alignment
4046
import androidx.compose.ui.Modifier
4147
import androidx.compose.ui.platform.LocalContext
@@ -55,6 +61,7 @@ import com.bringyour.network.ui.components.AccountSwitcher
5561
import com.bringyour.network.ui.components.LoginMode
5662
import com.bringyour.network.ui.components.UsageBar
5763
import com.bringyour.network.ui.components.overlays.OverlayMode
64+
import com.bringyour.network.ui.components.redeemTransferBalanceCode.RedeemTransferBalanceCodeSheet
5865
import com.bringyour.network.ui.shared.viewmodels.OverlayViewModel
5966
import com.bringyour.network.ui.shared.viewmodels.Plan
6067
import com.bringyour.network.ui.shared.viewmodels.PlanViewModel
@@ -67,6 +74,8 @@ import com.bringyour.network.ui.theme.TextMuted
6774
import com.bringyour.network.ui.theme.URNetworkTheme
6875
import com.bringyour.network.utils.formatDecimalString
6976
import kotlinx.coroutines.CoroutineScope
77+
import kotlinx.coroutines.delay
78+
import kotlinx.coroutines.launch
7079

7180
@OptIn(ExperimentalMaterial3Api::class)
7281
@Composable
@@ -93,6 +102,9 @@ fun AccountScreen(
93102

94103
val refreshState = rememberPullToRefreshState()
95104

105+
var isPresentingRedeemTransferBalanceSheet by remember { mutableStateOf(false) }
106+
val redeemTransferBalanceSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
107+
96108
LaunchedEffect(Unit) {
97109
subscriptionBalanceViewModel.fetchSubscriptionBalance()
98110
}
@@ -136,11 +148,28 @@ fun AccountScreen(
136148
pendingBytes = subscriptionBalanceViewModel.pendingBalanceByteCount,
137149
availableBytes = availableBalanceByteCount,
138150
totalReferrals = totalReferrals,
139-
meanReliabilityWeight = meanReliabilityWeight
151+
meanReliabilityWeight = meanReliabilityWeight,
152+
launchRedeemTransferBalanceCodeSheet = {
153+
isPresentingRedeemTransferBalanceSheet = true
154+
}
140155
)
141156
}
142157
}
143158
}
159+
160+
if (isPresentingRedeemTransferBalanceSheet) {
161+
RedeemTransferBalanceCodeSheet(
162+
sheetState = redeemTransferBalanceSheetState,
163+
setIsPresenting = {
164+
isPresentingRedeemTransferBalanceSheet = it
165+
},
166+
onSuccess = {
167+
subscriptionBalanceViewModel.pollSubscriptionBalance()
168+
overlayViewModel.launch(OverlayMode.Upgrade)
169+
}
170+
)
171+
}
172+
144173
}
145174
}
146175

@@ -165,6 +194,7 @@ fun AccountScreenContent(
165194
pendingBytes: Long,
166195
totalReferrals: Long,
167196
meanReliabilityWeight: Double,
197+
launchRedeemTransferBalanceCodeSheet: () -> Unit
168198
) {
169199

170200
val context = LocalContext.current
@@ -227,18 +257,30 @@ fun AccountScreenContent(
227257
navController = navController
228258
)
229259

230-
if (currentPlan != Plan.Supporter) {
260+
Spacer(modifier = Modifier.height(12.dp))
231261

232-
Spacer(modifier = Modifier.height(12.dp))
262+
UsageBar(
263+
usedBytes = usedBytes,
264+
pendingBytes = pendingBytes,
265+
availableBytes = availableBytes,
266+
totalReferrals = totalReferrals,
267+
meanReliabilityWeight = meanReliabilityWeight
268+
)
269+
270+
Spacer(modifier = Modifier.height(8.dp))
233271

234-
UsageBar(
235-
usedBytes = usedBytes,
236-
pendingBytes = pendingBytes,
237-
availableBytes = availableBytes,
238-
totalReferrals = totalReferrals,
239-
meanReliabilityWeight = meanReliabilityWeight
272+
Row(
273+
modifier = Modifier
274+
.fillMaxWidth(),
275+
horizontalArrangement = Arrangement.End
276+
) {
277+
Text(
278+
stringResource(id = R.string.redeem_balance_code),
279+
style = TextStyle(color = BlueMedium),
280+
modifier = Modifier.clickable {
281+
launchRedeemTransferBalanceCodeSheet()
282+
}
240283
)
241-
242284
}
243285

244286
Spacer(modifier = Modifier.height(16.dp))
@@ -453,7 +495,8 @@ private fun AccountSupporterAuthenticatedPreview() {
453495
isPollingSubscriptionBalance = false,
454496
currentStore = null,
455497
totalReferrals = 1,
456-
meanReliabilityWeight = 0.1
498+
meanReliabilityWeight = 0.1,
499+
launchRedeemTransferBalanceCodeSheet = {}
457500
)
458501
}
459502
}
@@ -497,7 +540,8 @@ private fun AccountBasicAuthenticatedPreview() {
497540
isPollingSubscriptionBalance = false,
498541
currentStore = null,
499542
totalReferrals = 1,
500-
meanReliabilityWeight = 0.1
543+
meanReliabilityWeight = 0.1,
544+
launchRedeemTransferBalanceCodeSheet = {}
501545
)
502546
}
503547
}
@@ -540,7 +584,8 @@ private fun AccountGuestPreview() {
540584
isPollingSubscriptionBalance = false,
541585
currentStore = null,
542586
totalReferrals = 1,
543-
meanReliabilityWeight = 0.1
587+
meanReliabilityWeight = 0.1,
588+
launchRedeemTransferBalanceCodeSheet = {}
544589
)
545590
}
546591
}
@@ -582,7 +627,8 @@ private fun AccountGuestNoWalletPreview() {
582627
isPollingSubscriptionBalance = false,
583628
currentStore = null,
584629
totalReferrals = 1,
585-
meanReliabilityWeight = 0.1
630+
meanReliabilityWeight = 0.1,
631+
launchRedeemTransferBalanceCodeSheet = {}
586632
)
587633
}
588634
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.bringyour.network.ui.components.redeemTransferBalanceCode
2+
3+
import android.widget.Toast
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.Spacer
6+
import androidx.compose.foundation.layout.height
7+
import androidx.compose.foundation.layout.padding
8+
import androidx.compose.material3.ExperimentalMaterial3Api
9+
import androidx.compose.material3.ModalBottomSheet
10+
import androidx.compose.material3.SheetState
11+
import androidx.compose.material3.Text
12+
import androidx.compose.runtime.Composable
13+
import androidx.compose.runtime.collectAsState
14+
import androidx.compose.runtime.getValue
15+
import androidx.compose.ui.Modifier
16+
import androidx.compose.ui.platform.LocalContext
17+
import androidx.compose.ui.res.stringResource
18+
import androidx.compose.ui.text.TextStyle
19+
import androidx.compose.ui.unit.dp
20+
import androidx.compose.ui.unit.sp
21+
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
22+
import com.bringyour.network.R
23+
import com.bringyour.network.ui.components.URButton
24+
import com.bringyour.network.ui.components.URTextInput
25+
import com.bringyour.network.ui.theme.ppNeueBitBold
26+
27+
@OptIn(ExperimentalMaterial3Api::class)
28+
@Composable
29+
fun RedeemTransferBalanceCodeSheet(
30+
sheetState: SheetState,
31+
setIsPresenting: (Boolean) -> Unit,
32+
onSuccess: () -> Unit,
33+
viewModel: RedeemTransferBalanceCodeViewModel = hiltViewModel()
34+
) {
35+
36+
val context = LocalContext.current
37+
val errorMsg = stringResource(id = R.string.error_redeeming_transfer_balance_code)
38+
val codeIsValid by viewModel.codeIsValid.collectAsState()
39+
val isLoading by viewModel.isLoading.collectAsState()
40+
41+
ModalBottomSheet(
42+
onDismissRequest = { setIsPresenting(false) },
43+
sheetState = sheetState,
44+
) {
45+
Column(
46+
modifier = Modifier
47+
.padding(16.dp)
48+
) {
49+
50+
Text(
51+
stringResource(id = R.string.redeem_balance_code),
52+
style = TextStyle(
53+
fontFamily = ppNeueBitBold,
54+
fontSize = 24.sp
55+
)
56+
)
57+
Spacer(modifier = Modifier.height(12.dp))
58+
59+
URTextInput(
60+
value = viewModel.code,
61+
onValueChange = { viewModel.onTextChanged(it) },
62+
label = stringResource(id = R.string.balance_code),
63+
placeholder = stringResource(id = R.string.enter_balance_code)
64+
)
65+
66+
Spacer(modifier = Modifier.height(12.dp))
67+
68+
URButton(
69+
onClick = {
70+
viewModel.redeem(
71+
{
72+
setIsPresenting(false)
73+
onSuccess()
74+
},
75+
{
76+
Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show()
77+
}
78+
)
79+
},
80+
enabled = codeIsValid && !isLoading,
81+
) { buttonTextStyle ->
82+
Text(stringResource(id = R.string.redeem), style = buttonTextStyle)
83+
}
84+
85+
}
86+
}
87+
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.bringyour.network.ui.components.redeemTransferBalanceCode
2+
3+
import androidx.compose.ui.text.input.TextFieldValue
4+
import androidx.lifecycle.ViewModel
5+
import androidx.lifecycle.viewModelScope
6+
import com.bringyour.network.DeviceManager
7+
import com.bringyour.network.JwtManager
8+
import com.bringyour.sdk.RedeemBalanceCodeArgs
9+
import dagger.hilt.android.lifecycle.HiltViewModel
10+
import kotlinx.coroutines.flow.MutableStateFlow
11+
import kotlinx.coroutines.flow.StateFlow
12+
import kotlinx.coroutines.flow.asStateFlow
13+
import kotlinx.coroutines.launch
14+
import javax.inject.Inject
15+
16+
@HiltViewModel
17+
class RedeemTransferBalanceCodeViewModel @Inject constructor(
18+
deviceManager: DeviceManager
19+
): ViewModel() {
20+
21+
private val _isLoading = MutableStateFlow(false)
22+
val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()
23+
24+
private val _code = MutableStateFlow(TextFieldValue(""))
25+
val code: TextFieldValue get() = _code.value
26+
27+
val onTextChanged: (newCode: TextFieldValue) -> Unit = {
28+
_code.value = it
29+
_codeIsValid.value = it.text.length == 26
30+
}
31+
32+
private val _codeIsValid = MutableStateFlow(false)
33+
val codeIsValid: StateFlow<Boolean> = _codeIsValid.asStateFlow()
34+
35+
val redeem: (
36+
onSuccess: () -> Unit,
37+
onError: () -> Unit
38+
) -> Unit = { onSuccess, onError ->
39+
40+
if (!_isLoading.value && _codeIsValid.value) {
41+
42+
_isLoading.value = true
43+
44+
val args = RedeemBalanceCodeArgs()
45+
args.secret = code.text
46+
deviceManager.device?.api?.redeemBalanceCode(args) { result, error ->
47+
48+
viewModelScope.launch {
49+
if (error != null) {
50+
_isLoading.value = false
51+
onError()
52+
return@launch
53+
}
54+
55+
if (result.error != null) {
56+
_isLoading.value = false
57+
onError()
58+
return@launch
59+
}
60+
61+
onSuccess()
62+
_isLoading.value = false
63+
64+
}
65+
66+
}
67+
68+
}
69+
70+
}
71+
72+
}

0 commit comments

Comments
 (0)