Skip to content

Commit ed166a4

Browse files
authored
Merge branch 'feat/nav3' into fix/skip-autorefresh-for-disabled-widgets
2 parents e93ca82 + 595dfbb commit ed166a4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+979
-923
lines changed

app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ class VssBackupClient @Inject constructor(
155155
}
156156
}
157157

158-
companion object Companion {
158+
companion object {
159159
private const val TAG = "VssBackupClient"
160160
}
161161
}

app/src/main/java/to/bitkit/ui/ContentView.kt

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,19 @@ fun ContentView(
144144
LaunchedEffect(appViewModel, navigator) {
145145
appViewModel.sendEffect.collect { effect ->
146146
when (effect) {
147-
is SendEffect.NavigateToAddress -> navigator.navigate(Routes.SendAddress)
148-
is SendEffect.NavigateToAmount -> navigator.navigate(Routes.SendAmount())
149-
is SendEffect.NavigateToScan -> navigator.navigate(Routes.SendQrScanner)
150-
is SendEffect.NavigateToCoinSelection -> navigator.navigate(Routes.SendCoinSelection)
151-
is SendEffect.NavigateToConfirm -> navigator.navigate(Routes.SendConfirm)
152-
is SendEffect.NavigateToQuickPay -> navigator.navigate(Routes.SendQuickPay)
153-
is SendEffect.NavigateToWithdrawConfirm -> navigator.navigate(Routes.SendWithdrawConfirm)
154-
is SendEffect.NavigateToWithdrawError -> navigator.navigate(Routes.SendWithdrawError)
155-
is SendEffect.NavigateToFee -> navigator.navigate(Routes.SendFeeRate)
156-
is SendEffect.NavigateToFeeCustom -> navigator.navigate(Routes.SendFeeCustom)
147+
is SendEffect.NavigateToAddress -> navigator.navigate(Routes.Send.Address)
148+
is SendEffect.NavigateToAmount -> navigator.navigate(Routes.Send.Amount())
149+
is SendEffect.NavigateToScan -> navigator.navigate(Routes.Send.QrScanner)
150+
is SendEffect.NavigateToCoinSelection -> navigator.navigate(Routes.Send.CoinSelection)
151+
is SendEffect.NavigateToConfirm -> navigator.navigate(Routes.Send.Confirm)
152+
is SendEffect.NavigateToQuickPay -> navigator.navigate(Routes.Send.QuickPay)
153+
is SendEffect.NavigateToWithdrawConfirm -> navigator.navigate(Routes.Send.WithdrawConfirm)
154+
is SendEffect.NavigateToWithdrawError -> navigator.navigate(Routes.Send.WithdrawError)
155+
is SendEffect.NavigateToFee -> navigator.navigate(Routes.Send.FeeRate)
156+
is SendEffect.NavigateToFeeCustom -> navigator.navigate(Routes.Send.FeeCustom)
157157
is SendEffect.PaymentSuccess -> {
158158
appViewModel.clearClipboardForAutoRead()
159-
navigator.navigate(Routes.SendSuccess)
159+
navigator.navigate(Routes.Send.Success)
160160
}
161161

162162
is SendEffect.PopBack -> navigator.popBackTo(effect.route)
@@ -310,8 +310,8 @@ fun ContentView(
310310
modifier = Modifier.align(Alignment.BottomCenter),
311311
) {
312312
TabBar(
313-
onSendClick = { navigator.navigate(Routes.SendRecipient) },
314-
onReceiveClick = { navigator.navigate(Routes.ReceiveQr) },
313+
onSendClick = { navigator.navigate(Routes.Send.Recipient) },
314+
onReceiveClick = { navigator.navigate(Routes.Receive.Qr) },
315315
onScanClick = { navigator.navigate(Routes.QrScanner) },
316316
)
317317
}
@@ -322,7 +322,6 @@ fun ContentView(
322322
navigator = navigator,
323323
hasSeenWidgetsIntro = hasSeenWidgetsIntro,
324324
hasSeenShopIntro = hasSeenShopIntro,
325-
modifier = Modifier.align(Alignment.TopEnd),
326325
)
327326
}
328327
}

app/src/main/java/to/bitkit/ui/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ private fun OnboardingContent(
217217
appViewModel: AppViewModel,
218218
walletViewModel: WalletViewModel,
219219
) {
220-
val backStack = rememberNavBackStack(Routes.Terms)
220+
val backStack = rememberNavBackStack(Routes.Onboarding.Terms)
221221
val navigator = remember(backStack) { Navigator(backStack) }
222222
val isGeoBlocked by appViewModel.isGeoBlocked.collectAsStateWithLifecycle()
223223

app/src/main/java/to/bitkit/ui/components/AuthCheckScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ fun AuthCheckScreen(
5454
}
5555

5656
AuthCheckAction.NAV_TO_RESET -> {
57-
navigator.navigate(Routes.ResetAndRestoreSettings)
57+
navigator.navigate(Routes.Settings.ResetAndRestore)
5858
}
5959
}
6060
},

app/src/main/java/to/bitkit/ui/components/DrawerMenu.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,29 +96,29 @@ fun DrawerMenu(
9696
scope.launch { drawerState.close() }
9797
},
9898
onActivityClick = {
99-
navigator.navigate(Routes.AllActivity)
99+
navigator.navigate(Routes.Activity.All)
100100
scope.launch { drawerState.close() }
101101
},
102102
onContactsClick = null, // TODO IMPLEMENT CONTACTS
103103
onProfileClick = null, // TODO IMPLEMENT PROFILE
104104
onWidgetsClick = {
105105
if (!hasSeenWidgetsIntro) {
106-
navigator.navigate(Routes.WidgetsIntro)
106+
navigator.navigate(Routes.Widgets.Intro)
107107
} else {
108-
navigator.navigate(Routes.AddWidget)
108+
navigator.navigate(Routes.Widgets.Add)
109109
}
110110
scope.launch { drawerState.close() }
111111
},
112112
onShopClick = {
113113
if (!hasSeenShopIntro) {
114-
navigator.navigate(Routes.ShopIntro)
114+
navigator.navigate(Routes.Shop.Intro)
115115
} else {
116-
navigator.navigate(Routes.ShopDiscover)
116+
navigator.navigate(Routes.Shop.Discover)
117117
}
118118
scope.launch { drawerState.close() }
119119
},
120120
onSettingsClick = {
121-
navigator.navigate(Routes.Settings)
121+
navigator.navigate(Routes.Settings.Main)
122122
scope.launch { drawerState.close() }
123123
},
124124
onAppStatusClick = {
Lines changed: 167 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,178 @@
11
package to.bitkit.ui.components
22

3+
import androidx.activity.compose.BackHandler
4+
import androidx.compose.animation.AnimatedVisibility
5+
import androidx.compose.animation.core.Animatable
6+
import androidx.compose.animation.core.animateFloatAsState
7+
import androidx.compose.animation.core.tween
8+
import androidx.compose.animation.fadeIn
9+
import androidx.compose.animation.fadeOut
10+
import androidx.compose.animation.slideInVertically
11+
import androidx.compose.animation.slideOutVertically
12+
import androidx.compose.foundation.background
13+
import androidx.compose.foundation.clickable
14+
import androidx.compose.foundation.gestures.detectVerticalDragGestures
15+
import androidx.compose.foundation.layout.Box
16+
import androidx.compose.foundation.layout.Column
17+
import androidx.compose.foundation.layout.fillMaxSize
18+
import androidx.compose.foundation.layout.fillMaxWidth
19+
import androidx.compose.foundation.layout.navigationBarsPadding
20+
import androidx.compose.foundation.layout.offset
21+
import androidx.compose.runtime.Composable
22+
import androidx.compose.runtime.LaunchedEffect
23+
import androidx.compose.runtime.getValue
24+
import androidx.compose.runtime.mutableFloatStateOf
25+
import androidx.compose.runtime.mutableStateOf
26+
import androidx.compose.runtime.remember
27+
import androidx.compose.runtime.rememberCoroutineScope
28+
import androidx.compose.runtime.setValue
29+
import androidx.compose.ui.Alignment
30+
import androidx.compose.ui.Modifier
31+
import androidx.compose.ui.draw.clip
32+
import androidx.compose.ui.graphics.Color
33+
import androidx.compose.ui.input.pointer.pointerInput
34+
import androidx.compose.ui.layout.onSizeChanged
35+
import androidx.compose.ui.unit.IntOffset
36+
import kotlinx.coroutines.launch
37+
import to.bitkit.ui.shared.modifiers.sheetHeight
38+
import to.bitkit.ui.theme.AppShapes
39+
import to.bitkit.ui.theme.Colors
40+
import kotlin.math.roundToInt
41+
42+
private const val MS_DURATION_ANIM = 300
43+
private const val THRESHOLD_DISMISS = 0.45f
44+
private const val OFFSET_MIN_DRAG = -0.1f
45+
private val sheetContainerColor = Color.Black
46+
347
enum class SheetSize { LARGE, MEDIUM, SMALL, CALENDAR; }
448

5-
/**@param priority Priority levels for timed sheets (higher number = higher priority)*/
49+
/** @param priority Priority levels for timed sheets (higher number = higher priority)*/
650
enum class TimedSheetType(val priority: Int) {
751
APP_UPDATE(priority = 5),
852
BACKUP(priority = 4),
953
NOTIFICATIONS(priority = 3),
1054
QUICK_PAY(priority = 2),
1155
HIGH_BALANCE(priority = 1)
1256
}
57+
58+
@Composable
59+
fun SheetHost(
60+
sheetSize: SheetSize,
61+
onDismiss: () -> Unit,
62+
content: @Composable () -> Unit,
63+
) {
64+
val scope = rememberCoroutineScope()
65+
var sheetHeightPx by remember { mutableFloatStateOf(0f) }
66+
val offsetY = remember { Animatable(1f) }
67+
var sheetVisible by remember { mutableStateOf(false) }
68+
69+
LaunchedEffect(Unit) {
70+
sheetVisible = true
71+
offsetY.animateTo(0f, animationSpec = tween(MS_DURATION_ANIM))
72+
}
73+
74+
fun dismiss() {
75+
scope.launch {
76+
offsetY.animateTo(1f, animationSpec = tween(MS_DURATION_ANIM))
77+
sheetVisible = false
78+
onDismiss()
79+
}
80+
}
81+
82+
BackHandler(enabled = offsetY.value < 1f) {
83+
dismiss()
84+
}
85+
86+
Box(modifier = Modifier.fillMaxSize()) {
87+
Scrim(
88+
isVisible = offsetY.value < 1f,
89+
progress = 1f - offsetY.value,
90+
onClick = { dismiss() },
91+
)
92+
93+
AnimatedVisibility(
94+
visible = sheetVisible,
95+
enter = slideInVertically { it } + fadeIn(),
96+
exit = slideOutVertically { it } + fadeOut(),
97+
modifier = Modifier.align(Alignment.BottomCenter),
98+
) {
99+
Column(
100+
horizontalAlignment = Alignment.CenterHorizontally,
101+
modifier = Modifier
102+
.offset { IntOffset(0, (offsetY.value * sheetHeightPx).roundToInt()) }
103+
.onSizeChanged { sheetHeightPx = it.height.toFloat() }
104+
.clip(AppShapes.sheet)
105+
.background(sheetContainerColor)
106+
.fillMaxWidth()
107+
.pointerInput(Unit) {
108+
detectVerticalDragGestures(
109+
onDragEnd = {
110+
scope.launch {
111+
if (offsetY.value > THRESHOLD_DISMISS) {
112+
offsetY.animateTo(
113+
1f,
114+
animationSpec = tween(MS_DURATION_ANIM)
115+
)
116+
sheetVisible = false
117+
onDismiss()
118+
} else {
119+
offsetY.animateTo(
120+
0f,
121+
animationSpec = tween(MS_DURATION_ANIM)
122+
)
123+
}
124+
}
125+
},
126+
onVerticalDrag = { _, dragAmount ->
127+
val newOffset = offsetY.value + (dragAmount / sheetHeightPx)
128+
scope.launch {
129+
offsetY.snapTo(newOffset.coerceIn(OFFSET_MIN_DRAG, 1f))
130+
}
131+
}
132+
)
133+
}
134+
) {
135+
Box(
136+
contentAlignment = Alignment.TopCenter,
137+
modifier = Modifier
138+
.fillMaxWidth()
139+
.background(Colors.White08)
140+
) {
141+
SheetDragHandle()
142+
}
143+
Box(
144+
modifier = Modifier
145+
.sheetHeight(sheetSize)
146+
.navigationBarsPadding()
147+
) {
148+
content()
149+
}
150+
}
151+
}
152+
}
153+
}
154+
155+
@Composable
156+
private fun Scrim(
157+
isVisible: Boolean,
158+
progress: Float,
159+
onClick: () -> Unit,
160+
) {
161+
val scrimAlpha by animateFloatAsState(
162+
targetValue = if (isVisible) progress * 0.5f else 0f,
163+
animationSpec = tween(durationMillis = MS_DURATION_ANIM),
164+
label = "sheetScrimAlpha"
165+
)
166+
if (scrimAlpha > 0f || isVisible) {
167+
Box(
168+
modifier = Modifier
169+
.fillMaxSize()
170+
.background(Colors.Black.copy(alpha = scrimAlpha))
171+
.clickable(
172+
interactionSource = null,
173+
indication = null,
174+
onClick = onClick,
175+
)
176+
)
177+
}
178+
}

app/src/main/java/to/bitkit/ui/components/SuggestionCard.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ private const val MAX_ALPHA_GRADIENT = 0.9f
4848

4949
@Composable
5050
fun SuggestionCard(
51-
modifier: Modifier = Modifier,
5251
gradientColor: Color,
5352
title: String,
5453
description: String,
5554
@DrawableRes icon: Int,
55+
modifier: Modifier = Modifier,
5656
onClose: (() -> Unit)? = null,
5757
size: Int = 152,
5858
disableGlow: Boolean = false,
@@ -122,7 +122,7 @@ fun SuggestionCard(
122122
painter = painterResource(icon),
123123
contentDescription = null,
124124
contentScale = ContentScale.FillHeight,
125-
modifier = Modifier.weight(1f)
125+
modifier = Modifier.weight(1f),
126126
)
127127

128128
if (onClose != null) {
@@ -184,7 +184,7 @@ private fun Preview() {
184184
description = stringResource(item.description),
185185
icon = item.icon,
186186
onClose = {},
187-
onClick = {}, // All cards are clickable
187+
onClick = {},
188188
)
189189
}
190190
}

app/src/main/java/to/bitkit/ui/nav/DeepLinks.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,42 +108,42 @@ class DeepLinkMatcher<T : Routes>(
108108
object DeepLinkPatterns {
109109
// bitkit:// scheme patterns
110110
val RECOVERY_MODE = DeepLinkPattern(
111-
routeClass = Routes.RecoveryMode::class,
111+
routeClass = Routes.Recovery.Mode::class,
112112
uriPattern = "${UriScheme.BITKIT.withSlashes}recovery-mode".toUri()
113113
)
114114

115115
// bitcoin:// scheme patterns (BIP21)
116116
// Note: bitcoin: URIs use address as "host" not path
117117
val SEND_BITCOIN = DeepLinkPattern(
118-
routeClass = Routes.SendAddress::class,
118+
routeClass = Routes.Send.Address::class,
119119
uriPattern = "${UriScheme.BITCOIN.withSlashes}{address}".toUri()
120120
)
121121

122122
// lightning:// scheme patterns
123123
val SEND_LIGHTNING = DeepLinkPattern(
124-
routeClass = Routes.SendAddress::class,
124+
routeClass = Routes.Send.Address::class,
125125
uriPattern = "${UriScheme.LIGHTNING.withSlashes}{invoice}".toUri()
126126
)
127127

128128
// lnurl:// scheme patterns
129129
val LNURL_PAY = DeepLinkPattern(
130-
routeClass = Routes.SendAddress::class,
130+
routeClass = Routes.Send.Address::class,
131131
uriPattern = "${UriScheme.LNURL_PAY.withSlashes}{data}".toUri()
132132
)
133133

134134
val LNURL_WITHDRAW = DeepLinkPattern(
135-
routeClass = Routes.ReceiveQr::class,
135+
routeClass = Routes.Receive.Qr::class,
136136
uriPattern = "${UriScheme.LNURL_WITHDRAW.withSlashes}{data}".toUri()
137137
)
138138

139139
val LNURL_CHANNEL = DeepLinkPattern(
140-
routeClass = Routes.LnurlChannel::class,
140+
routeClass = Routes.Sheet.LnurlChannel::class,
141141
uriPattern = "${UriScheme.LNURL_CHANNEL.withSlashes}{data}".toUri()
142142
)
143143

144144
// https:// scheme patterns (App Links)
145145
val TREASURE_HUNT = DeepLinkPattern(
146-
routeClass = Routes.GiftLoading::class,
146+
routeClass = Routes.Gift.Loading::class,
147147
uriPattern = "${UriScheme.HTTPS.withSlashes}www.bitkit.to/treasure-hunt".toUri()
148148
)
149149

0 commit comments

Comments
 (0)