Skip to content

Commit 3fa0e3b

Browse files
authored
Merge pull request #942 from synonymdev/feat/calculator-widget-v61
feat: calculator v61 redesign
2 parents 395e16f + edeb6a1 commit 3fa0e3b

21 files changed

Lines changed: 2304 additions & 993 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package to.bitkit.models
2+
3+
enum class MoneyType {
4+
BITCOIN,
5+
FIAT,
6+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,32 @@
11
package to.bitkit.models.widget
22

33
import kotlinx.serialization.Serializable
4+
import to.bitkit.ext.removeSpaces
5+
import to.bitkit.models.BitcoinDisplayUnit
6+
import to.bitkit.ui.screens.widgets.calculator.calculatorBtcValueToSats
47

58
@Serializable
69
data class CalculatorValues(
710
val btcValue: String = "10000",
811
val fiatValue: String = "",
12+
val satsValue: Long? = null,
13+
val displayUnit: BitcoinDisplayUnit? = null,
914
)
15+
16+
internal fun CalculatorValues.resolveCalculatorSatsValue(): Long {
17+
satsValue?.let { return it }
18+
if (btcValue.isEmpty()) return 0L
19+
return calculatorBtcValueToSats(
20+
btcValue = btcValue,
21+
displayUnit = displayUnit ?: inferLegacyCalculatorDisplayUnit(btcValue),
22+
)
23+
}
24+
25+
private fun inferLegacyCalculatorDisplayUnit(btcValue: String): BitcoinDisplayUnit {
26+
val normalizedValue = btcValue.removeSpaces()
27+
return if (normalizedValue.any { it == '.' || it == ',' }) {
28+
BitcoinDisplayUnit.CLASSIC
29+
} else {
30+
BitcoinDisplayUnit.MODERN
31+
}
32+
}

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,8 @@ fun ContentView(
497497
}
498498
) {
499499
Box(modifier = Modifier.fillMaxSize()) {
500+
var isHomeCalculatorInputActive by remember { mutableStateOf(false) }
501+
500502
RootNavHost(
501503
navController = navController,
502504
drawerState = drawerState,
@@ -506,6 +508,7 @@ fun ContentView(
506508
settingsViewModel = settingsViewModel,
507509
currencyViewModel = currencyViewModel,
508510
transferViewModel = transferViewModel,
511+
onHomeCalculatorInputActiveChanged = { isHomeCalculatorInputActive = it },
509512
)
510513

511514
val navBackStackEntry by navController.currentBackStackEntryAsState()
@@ -516,9 +519,18 @@ fun ContentView(
516519
Routes.Savings::class.qualifiedName,
517520
Routes.Spending::class.qualifiedName,
518521
)
522+
val hideTabBarForCalculator =
523+
currentRoute == Routes.Home::class.qualifiedName && isHomeCalculatorInputActive
524+
525+
LaunchedEffect(currentRoute) {
526+
if (currentRoute != Routes.Home::class.qualifiedName) {
527+
isHomeCalculatorInputActive = false
528+
}
529+
}
519530

520531
if (showTabBar) {
521532
TabBar(
533+
isVisible = !hideTabBarForCalculator,
522534
onSendClick = { appViewModel.showSheet(Sheet.Send()) },
523535
onReceiveClick = { appViewModel.showSheet(Sheet.Receive()) },
524536
onScanClick = { appViewModel.showSheet(Sheet.Send(SendRoute.QrScanner)) },
@@ -557,6 +569,7 @@ private fun RootNavHost(
557569
settingsViewModel: SettingsViewModel,
558570
currencyViewModel: CurrencyViewModel,
559571
transferViewModel: TransferViewModel,
572+
onHomeCalculatorInputActiveChanged: (Boolean) -> Unit,
560573
) {
561574
val scope = rememberCoroutineScope()
562575

@@ -568,6 +581,7 @@ private fun RootNavHost(
568581
settingsViewModel = settingsViewModel,
569582
navController = navController,
570583
drawerState = drawerState,
584+
onCalculatorInputActiveChanged = onHomeCalculatorInputActiveChanged,
571585
)
572586
allActivity(
573587
activityListViewModel = activityListViewModel,
@@ -594,7 +608,7 @@ private fun RootNavHost(
594608
logs(navController)
595609
suggestions(navController)
596610
support(navController)
597-
widgets(navController, settingsViewModel, currencyViewModel)
611+
widgets(navController, settingsViewModel)
598612
update()
599613
recoveryMode(navController, appViewModel)
600614

@@ -811,6 +825,7 @@ private fun NavGraphBuilder.home(
811825
settingsViewModel: SettingsViewModel,
812826
navController: NavHostController,
813827
drawerState: DrawerState,
828+
onCalculatorInputActiveChanged: (Boolean) -> Unit,
814829
) {
815830
composable<Routes.Home> {
816831
val isRefreshing by walletViewModel.isRefreshing.collectAsStateWithLifecycle()
@@ -837,6 +852,7 @@ private fun NavGraphBuilder.home(
837852
walletViewModel = walletViewModel,
838853
appViewModel = appViewModel,
839854
activityListViewModel = activityListViewModel,
855+
onCalculatorInputActiveChanged = onCalculatorInputActiveChanged,
840856
)
841857
}
842858
}
@@ -1465,7 +1481,6 @@ private fun NavGraphBuilder.support(
14651481
private fun NavGraphBuilder.widgets(
14661482
navController: NavHostController,
14671483
settingsViewModel: SettingsViewModel,
1468-
currencyViewModel: CurrencyViewModel,
14691484
) {
14701485
composableWithDefaultTransitions<Routes.WidgetsIntro> {
14711486
WidgetsIntroScreen(
@@ -1508,7 +1523,6 @@ private fun NavGraphBuilder.widgets(
15081523
CalculatorPreviewScreen(
15091524
onClose = { navController.navigateToHome() },
15101525
onBack = { navController.popBackStack() },
1511-
currencyViewModel = currencyViewModel
15121526
)
15131527
}
15141528
navigationWithDefaultTransitions<Routes.Headlines>(

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ import to.bitkit.viewmodels.WalletViewModel
6464
@AndroidEntryPoint
6565
class MainActivity : FragmentActivity() {
6666
private companion object {
67-
const val KEY_CONSUMED_DEEPLINK_URI = "consumed_deeplink_uri"
67+
const val KEY_CONSUMED_LAUNCH_INTENT = "consumed_launch_intent"
6868
}
6969

7070
private val appViewModel by viewModels<AppViewModel>()
@@ -76,7 +76,7 @@ class MainActivity : FragmentActivity() {
7676
private val settingsViewModel by viewModels<SettingsViewModel>()
7777
private val backupsViewModel by viewModels<BackupsViewModel>()
7878

79-
@Suppress("LongMethod")
79+
@Suppress("LongMethod", "CyclomaticComplexMethod")
8080
override fun onCreate(savedInstanceState: Bundle?) {
8181
super.onCreate(savedInstanceState)
8282

@@ -88,9 +88,9 @@ class MainActivity : FragmentActivity() {
8888
importance = NotificationManager.IMPORTANCE_LOW
8989
)
9090

91-
val consumedUri = savedInstanceState?.getString(KEY_CONSUMED_DEEPLINK_URI)
92-
val currentUri = intent?.data?.toString()
93-
if (currentUri == null || currentUri != consumedUri) {
91+
val consumedLaunchIntent = savedInstanceState?.getString(KEY_CONSUMED_LAUNCH_INTENT)
92+
val currentLaunchIntent = intent.launchKey()
93+
if (currentLaunchIntent == null || currentLaunchIntent != consumedLaunchIntent) {
9494
appViewModel.handleDeeplinkIntent(intent)
9595
}
9696

@@ -212,7 +212,7 @@ class MainActivity : FragmentActivity() {
212212

213213
override fun onSaveInstanceState(outState: Bundle) {
214214
super.onSaveInstanceState(outState)
215-
intent?.data?.toString()?.let { outState.putString(KEY_CONSUMED_DEEPLINK_URI, it) }
215+
intent.launchKey()?.let { outState.putString(KEY_CONSUMED_LAUNCH_INTENT, it) }
216216
}
217217

218218
override fun onDestroy() {
@@ -237,6 +237,14 @@ class MainActivity : FragmentActivity() {
237237
}
238238
}
239239

240+
private fun Intent?.launchKey(): String? {
241+
this ?: return null
242+
return when (action) {
243+
Intent.ACTION_VIEW -> data?.toString()
244+
else -> null
245+
}
246+
}
247+
240248
@Composable
241249
private fun OnboardingNav(
242250
startupNavController: NavHostController,

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

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.BoxScope
77
import androidx.compose.foundation.layout.BoxWithConstraints
88
import androidx.compose.foundation.layout.fillMaxWidth
99
import androidx.compose.foundation.layout.height
10+
import androidx.compose.foundation.layout.navigationBarsPadding
1011
import androidx.compose.foundation.lazy.grid.GridCells
1112
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
1213
import androidx.compose.foundation.lazy.grid.items
@@ -71,13 +72,21 @@ fun NumberPad(
7172
modifier: Modifier = Modifier,
7273
type: NumberPadType = NumberPadType.SIMPLE,
7374
availableHeight: Dp = defaultHeight,
75+
decimalSeparator: String = KEY_DECIMAL,
7476
errorKey: String? = null,
77+
includeNavigationBarsPadding: Boolean = false,
78+
onDeleteLongPress: (() -> Unit)? = null,
7579
) {
7680
val focusRequester = remember { FocusRequester() }
7781
LaunchedEffect(Unit) { focusRequester.requestFocus() }
82+
val safeAreaModifier = if (includeNavigationBarsPadding) {
83+
modifier.navigationBarsPadding()
84+
} else {
85+
modifier
86+
}
7887

7988
BoxWithConstraints(
80-
modifier = modifier
89+
modifier = safeAreaModifier
8190
.focusRequester(focusRequester)
8291
.onPreviewKeyEvent { keyEvent ->
8392
if (keyEvent.type != KeyEventType.KeyDown) return@onPreviewKeyEvent false
@@ -124,9 +133,10 @@ fun NumberPad(
124133
)
125134

126135
NumberPadType.DECIMAL -> NumberPadKeyButton(
127-
text = KEY_DECIMAL,
136+
text = decimalSeparator,
128137
onPress = onPress,
129138
height = buttonHeight,
139+
key = KEY_DECIMAL,
130140
hasError = errorKey == KEY_DECIMAL,
131141
testTag = "NDecimal",
132142
)
@@ -143,6 +153,7 @@ fun NumberPad(
143153
item {
144154
NumberPadDeleteButton(
145155
onPress = { onPress(KEY_DELETE) },
156+
onLongPress = onDeleteLongPress,
146157
height = buttonHeight,
147158
modifier = Modifier.testTag("NRemove"),
148159
)
@@ -161,14 +172,19 @@ fun NumberPad(
161172
currencies: CurrencyState = LocalCurrencies.current,
162173
type: NumberPadType = viewModel.getNumberPadType(currencies),
163174
availableHeight: Dp = defaultHeight,
175+
decimalSeparator: String = KEY_DECIMAL,
176+
includeNavigationBarsPadding: Boolean = false,
164177
) {
165178
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
166179
NumberPad(
167180
onPress = { key -> viewModel.handleNumberPadInput(key, currencies) },
168181
modifier = modifier,
169182
type = type,
170183
availableHeight = availableHeight,
184+
decimalSeparator = decimalSeparator,
171185
errorKey = uiState.errorKey,
186+
includeNavigationBarsPadding = includeNavigationBarsPadding,
187+
onDeleteLongPress = viewModel::clearInput,
172188
)
173189
}
174190

@@ -186,7 +202,7 @@ private val hardwareKeyMap = mapOf(
186202
Key.Eight to "8", Key.NumPad8 to "8",
187203
Key.Nine to "9", Key.NumPad9 to "9",
188204
Key.Backspace to KEY_DELETE, Key.Delete to KEY_DELETE,
189-
Key.Period to KEY_DECIMAL, Key.NumPadDot to KEY_DECIMAL,
205+
Key.Period to KEY_DECIMAL, Key.NumPadDot to KEY_DECIMAL, Key.Comma to KEY_DECIMAL,
190206
)
191207

192208
private fun mapHardwareKey(key: Key, type: NumberPadType): String? {
@@ -201,11 +217,12 @@ fun NumberPadKeyButton(
201217
onPress: (String) -> Unit,
202218
height: Dp,
203219
modifier: Modifier = Modifier,
220+
key: String = text,
204221
hasError: Boolean = false,
205222
testTag: String = "N$text",
206223
) {
207224
NumberPadKey(
208-
onClick = { onPress(text) },
225+
onClick = { onPress(key) },
209226
height = height,
210227
haptic = if (hasError) errorHaptic else pressHaptic,
211228
modifier = modifier.testTag(testTag),
@@ -228,13 +245,15 @@ internal fun NumberPadDeleteButton(
228245
onPress: () -> Unit,
229246
height: Dp,
230247
modifier: Modifier = Modifier,
248+
onLongPress: (() -> Unit)? = null,
231249
) {
232250
NumberPadKeyIcon(
233251
icon = R.drawable.ic_backspace,
234252
contentDescription = stringResource(R.string.common__delete),
235253
onClick = onPress,
254+
onLongClick = onLongPress,
236255
height = height,
237-
modifier = modifier,
256+
modifier = modifier
238257
)
239258
}
240259

@@ -245,11 +264,13 @@ fun NumberPadKeyIcon(
245264
onClick: () -> Unit,
246265
height: Dp,
247266
modifier: Modifier = Modifier,
267+
onLongClick: (() -> Unit)? = null,
248268
) {
249269
NumberPadKey(
250270
onClick = onClick,
271+
onLongClick = onLongClick,
251272
height = height,
252-
modifier = modifier,
273+
modifier = modifier
253274
) {
254275
Icon(
255276
painter = painterResource(icon),
@@ -264,6 +285,7 @@ fun NumberPadKey(
264285
height: Dp,
265286
modifier: Modifier = Modifier,
266287
haptic: HapticFeedbackType = pressHaptic,
288+
onLongClick: (() -> Unit)? = null,
267289
content: @Composable (BoxScope.() -> Unit),
268290
) {
269291
val haptics = LocalHapticFeedback.current
@@ -273,10 +295,20 @@ fun NumberPadKey(
273295
modifier = modifier
274296
.height(height)
275297
.fillMaxWidth()
276-
.clickableAlpha(ALPHA_PRESSED, debounce = Duration.ZERO) {
277-
haptics.performHapticFeedback(haptic)
278-
onClick()
279-
},
298+
.clickableAlpha(
299+
pressedAlpha = ALPHA_PRESSED,
300+
debounce = Duration.ZERO,
301+
onLongClick = onLongClick?.let {
302+
{
303+
haptics.performHapticFeedback(haptic)
304+
it()
305+
}
306+
},
307+
onClick = {
308+
haptics.performHapticFeedback(haptic)
309+
onClick()
310+
},
311+
),
280312
)
281313
}
282314

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,20 @@ fun RowScope.FillWidth(
5858
@Composable
5959
fun StatusBarSpacer(modifier: Modifier = Modifier) {
6060
Spacer(
61-
modifier = modifier.height(Insets.Top),
61+
modifier = modifier.height(Insets.Top)
6262
)
6363
}
6464

6565
@Composable
6666
fun TopBarSpacer(modifier: Modifier = Modifier) {
6767
Spacer(
68-
modifier = modifier.height(TopBarHeight),
68+
modifier = modifier.height(TopBarHeight)
69+
)
70+
}
71+
72+
@Composable
73+
fun NavBarSpacer(modifier: Modifier = Modifier) {
74+
Spacer(
75+
modifier = modifier.height(Insets.Bottom)
6976
)
7077
}

0 commit comments

Comments
 (0)