Skip to content

Commit f359342

Browse files
committed
Merge branch 'master' into fix/all-warnings
2 parents b684ca2 + 94dc48b commit f359342

36 files changed

Lines changed: 1886 additions & 227 deletions

app/src/main/java/to/bitkit/data/SettingsStore.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,7 @@ data class SettingsData(
9494
val quickPayAmount: Int = 5,
9595
val lightningSetupStep: Int = 0,
9696
val isPinEnabled: Boolean = false,
97-
val isPinOnLaunchEnabled: Boolean = false,
9897
val isBiometricEnabled: Boolean = false,
99-
val isPinOnIdleEnabled: Boolean = false,
10098
val isPinForPaymentsEnabled: Boolean = false,
10199
val isDevModeEnabled: Boolean = Env.isDebug,
102100
val showWidgets: Boolean = true,
@@ -123,8 +121,6 @@ data class SettingsData(
123121

124122
fun SettingsData.resetPin() = this.copy(
125123
isPinEnabled = false,
126-
isPinOnLaunchEnabled = true,
127-
isPinOnIdleEnabled = false,
128124
isPinForPaymentsEnabled = false,
129125
isBiometricEnabled = false,
130126
)

app/src/main/java/to/bitkit/models/TransactionSpeed.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package to.bitkit.models
22

33
import androidx.compose.runtime.Composable
44
import androidx.compose.ui.res.stringResource
5+
import com.synonym.bitkitcore.FeeRates
56
import kotlinx.serialization.KSerializer
67
import kotlinx.serialization.Serializable
78
import kotlinx.serialization.descriptors.PrimitiveKind
@@ -25,6 +26,13 @@ sealed class TransactionSpeed {
2526
is Custom -> "custom_$satsPerVByte"
2627
}
2728

29+
fun getFeeRate(feeRates: FeeRates): UInt = when (this) {
30+
is Fast -> feeRates.fast
31+
is Medium -> feeRates.mid
32+
is Slow -> feeRates.slow
33+
is Custom -> satsPerVByte
34+
}
35+
2836
companion object {
2937
fun default(): TransactionSpeed = Medium
3038

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package to.bitkit.repositories
2+
3+
import com.synonym.bitkitcore.FeeRates
4+
import com.synonym.bitkitcore.broadcastSweepTransaction
5+
import com.synonym.bitkitcore.checkSweepableBalances
6+
import com.synonym.bitkitcore.prepareSweepTransaction
7+
import kotlinx.coroutines.CoroutineDispatcher
8+
import kotlinx.coroutines.withContext
9+
import to.bitkit.async.ServiceQueue
10+
import to.bitkit.data.keychain.Keychain
11+
import to.bitkit.di.BgDispatcher
12+
import to.bitkit.env.Env
13+
import to.bitkit.models.toCoreNetwork
14+
import to.bitkit.services.CoreService
15+
import to.bitkit.utils.Logger
16+
import to.bitkit.utils.ServiceError
17+
import to.bitkit.viewmodels.SweepResult
18+
import to.bitkit.viewmodels.SweepTransactionPreview
19+
import to.bitkit.viewmodels.SweepableBalances
20+
import javax.inject.Inject
21+
import javax.inject.Singleton
22+
import com.synonym.bitkitcore.SweepResult as BitkitCoreSweepResult
23+
import com.synonym.bitkitcore.SweepTransactionPreview as BitkitCoreSweepTransactionPreview
24+
import com.synonym.bitkitcore.SweepableBalances as BitkitCoreSweepableBalances
25+
26+
@Singleton
27+
class SweepRepo @Inject constructor(
28+
@BgDispatcher private val bgDispatcher: CoroutineDispatcher,
29+
private val keychain: Keychain,
30+
private val coreService: CoreService,
31+
) {
32+
suspend fun checkSweepableBalances(): Result<SweepableBalances> = withContext(bgDispatcher) {
33+
runCatching {
34+
val mnemonic = keychain.loadString(Keychain.Key.BIP39_MNEMONIC.name)
35+
?: throw ServiceError.MnemonicNotFound
36+
val passphrase = keychain.loadString(Keychain.Key.BIP39_PASSPHRASE.name)
37+
38+
Logger.debug("Checking sweepable balances...", context = TAG)
39+
40+
val balances = ServiceQueue.CORE.background {
41+
checkSweepableBalances(
42+
mnemonicPhrase = mnemonic,
43+
network = Env.network.toCoreNetwork(),
44+
bip39Passphrase = passphrase,
45+
electrumUrl = Env.electrumServerUrl,
46+
)
47+
}
48+
49+
balances.toSweepableBalances()
50+
}
51+
}
52+
53+
suspend fun prepareSweepTransaction(
54+
destinationAddress: String,
55+
feeRateSatsPerVbyte: UInt,
56+
): Result<SweepTransactionPreview> = withContext(bgDispatcher) {
57+
runCatching {
58+
val mnemonic = keychain.loadString(Keychain.Key.BIP39_MNEMONIC.name)
59+
?: throw ServiceError.MnemonicNotFound
60+
val passphrase = keychain.loadString(Keychain.Key.BIP39_PASSPHRASE.name)
61+
62+
Logger.debug("Preparing sweep transaction...", context = TAG)
63+
64+
val preview = ServiceQueue.CORE.background {
65+
prepareSweepTransaction(
66+
mnemonicPhrase = mnemonic,
67+
network = Env.network.toCoreNetwork(),
68+
bip39Passphrase = passphrase,
69+
electrumUrl = Env.electrumServerUrl,
70+
destinationAddress = destinationAddress,
71+
feeRateSatsPerVbyte = feeRateSatsPerVbyte,
72+
)
73+
}
74+
75+
preview.toSweepTransactionPreview()
76+
}
77+
}
78+
79+
suspend fun broadcastSweepTransaction(psbt: String): Result<SweepResult> = withContext(bgDispatcher) {
80+
runCatching {
81+
val mnemonic = keychain.loadString(Keychain.Key.BIP39_MNEMONIC.name)
82+
?: throw ServiceError.MnemonicNotFound
83+
val passphrase = keychain.loadString(Keychain.Key.BIP39_PASSPHRASE.name)
84+
85+
Logger.debug("Broadcasting sweep transaction...", context = TAG)
86+
87+
val result = ServiceQueue.CORE.background {
88+
broadcastSweepTransaction(
89+
psbt = psbt,
90+
mnemonicPhrase = mnemonic,
91+
network = Env.network.toCoreNetwork(),
92+
bip39Passphrase = passphrase,
93+
electrumUrl = Env.electrumServerUrl,
94+
)
95+
}
96+
97+
result.toSweepResult()
98+
}
99+
}
100+
101+
suspend fun getFeeRates(): Result<FeeRates> = coreService.blocktank.getFees()
102+
103+
suspend fun hasSweepableFunds(): Result<Boolean> = checkSweepableBalances().map { balances ->
104+
val hasFunds = balances.totalBalance > 0u
105+
if (hasFunds) {
106+
Logger.info("Found ${balances.totalBalance} sats to sweep", context = TAG)
107+
} else {
108+
Logger.debug("No sweepable funds found", context = TAG)
109+
}
110+
hasFunds
111+
}
112+
113+
companion object {
114+
private const val TAG = "SweepRepo"
115+
}
116+
}
117+
118+
private fun BitkitCoreSweepableBalances.toSweepableBalances() = SweepableBalances(
119+
legacyBalance = legacyBalance,
120+
legacyUtxosCount = legacyUtxosCount,
121+
p2shBalance = p2shBalance,
122+
p2shUtxosCount = p2shUtxosCount,
123+
taprootBalance = taprootBalance,
124+
taprootUtxosCount = taprootUtxosCount,
125+
)
126+
127+
private fun BitkitCoreSweepTransactionPreview.toSweepTransactionPreview() = SweepTransactionPreview(
128+
psbt = psbt,
129+
estimatedFee = estimatedFee,
130+
amountAfterFees = amountAfterFees,
131+
estimatedVsize = estimatedVsize,
132+
)
133+
134+
private fun BitkitCoreSweepResult.toSweepResult() = SweepResult(
135+
txid = txid,
136+
amountSwept = amountSwept,
137+
)

app/src/main/java/to/bitkit/services/MigrationService.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -740,8 +740,6 @@ class MigrationService @Inject constructor(
740740
else -> CoinSelectionPreference.SmallestFirst
741741
},
742742
isPinEnabled = settings.pin ?: current.isPinEnabled,
743-
isPinOnLaunchEnabled = settings.pinOnLaunch ?: current.isPinOnLaunchEnabled,
744-
isPinOnIdleEnabled = settings.pinOnIdle ?: current.isPinOnIdleEnabled,
745743
isPinForPaymentsEnabled = settings.pinForPayments ?: current.isPinForPaymentsEnabled,
746744
isBiometricEnabled = settings.biometrics ?: current.isBiometricEnabled,
747745
quickPayIntroSeen = settings.quickpayIntroSeen ?: current.quickPayIntroSeen,
@@ -1632,8 +1630,6 @@ data class RNSettings(
16321630
val enableSendAmountWarning: Boolean? = null,
16331631
val enableSwipeToHideBalance: Boolean? = null,
16341632
val pin: Boolean? = null,
1635-
val pinOnLaunch: Boolean? = null,
1636-
val pinOnIdle: Boolean? = null,
16371633
val pinForPayments: Boolean? = null,
16381634
val biometrics: Boolean? = null,
16391635
val rbf: Boolean? = null,

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

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ import to.bitkit.ui.settings.advanced.AddressViewerScreen
134134
import to.bitkit.ui.settings.advanced.CoinSelectPreferenceScreen
135135
import to.bitkit.ui.settings.advanced.ElectrumConfigScreen
136136
import to.bitkit.ui.settings.advanced.RgsServerScreen
137+
import to.bitkit.ui.settings.advanced.sweep.SweepConfirmScreen
138+
import to.bitkit.ui.settings.advanced.sweep.SweepFeeCustomScreen
139+
import to.bitkit.ui.settings.advanced.sweep.SweepFeeRateScreen
140+
import to.bitkit.ui.settings.advanced.sweep.SweepSettingsScreen
141+
import to.bitkit.ui.settings.advanced.sweep.SweepSuccessScreen
137142
import to.bitkit.ui.settings.appStatus.AppStatusScreen
138143
import to.bitkit.ui.settings.backgroundPayments.BackgroundPaymentsIntroScreen
139144
import to.bitkit.ui.settings.backgroundPayments.BackgroundPaymentsSettings
@@ -169,6 +174,7 @@ import to.bitkit.ui.sheets.LnurlAuthSheet
169174
import to.bitkit.ui.sheets.PinSheet
170175
import to.bitkit.ui.sheets.QuickPayIntroSheet
171176
import to.bitkit.ui.sheets.SendSheet
177+
import to.bitkit.ui.sheets.SweepPromptSheet
172178
import to.bitkit.ui.sheets.UpdateSheet
173179
import to.bitkit.ui.theme.TRANSITION_SHEET_MS
174180
import to.bitkit.ui.utils.AutoReadClipboardHandler
@@ -185,6 +191,7 @@ import to.bitkit.viewmodels.CurrencyViewModel
185191
import to.bitkit.viewmodels.MainScreenEffect
186192
import to.bitkit.viewmodels.RestoreState
187193
import to.bitkit.viewmodels.SettingsViewModel
194+
import to.bitkit.viewmodels.SweepViewModel
188195
import to.bitkit.viewmodels.TransferViewModel
189196
import to.bitkit.viewmodels.WalletViewModel
190197

@@ -333,7 +340,10 @@ fun ContentView(
333340
return
334341
} else if (restoreState is RestoreState.Completed) {
335342
WalletRestoreSuccessView(
336-
onContinue = { walletViewModel.onRestoreContinue() },
343+
onContinue = {
344+
walletViewModel.onRestoreContinue()
345+
appViewModel.checkForSweepableFunds()
346+
},
337347
)
338348
return
339349
}
@@ -397,6 +407,13 @@ fun ContentView(
397407
is Sheet.Backup -> BackupSheet(sheet, onDismiss = { appViewModel.hideSheet() })
398408
is Sheet.LnurlAuth -> LnurlAuthSheet(sheet, appViewModel)
399409
Sheet.ForceTransfer -> ForceTransferSheet(appViewModel, transferViewModel)
410+
Sheet.SweepPrompt -> SweepPromptSheet(
411+
onSweep = {
412+
appViewModel.hideSheet()
413+
navController.navigate(Routes.SweepNav)
414+
},
415+
onCancel = { appViewModel.hideSheet() },
416+
)
400417
is Sheet.Gift -> GiftSheet(sheet, appViewModel)
401418
is Sheet.TimedSheet -> {
402419
when (sheet.type) {
@@ -996,6 +1013,34 @@ private fun NavGraphBuilder.advancedSettings(navController: NavHostController) {
9961013
composableWithDefaultTransitions<Routes.AddressViewer> {
9971014
AddressViewerScreen(navController)
9981015
}
1016+
navigationWithDefaultTransitions<Routes.SweepNav>(
1017+
startDestination = Routes.Sweep,
1018+
) {
1019+
composableWithDefaultTransitions<Routes.Sweep> {
1020+
val parentEntry = remember(it) { navController.getBackStackEntry(Routes.SweepNav) }
1021+
val viewModel = hiltViewModel<SweepViewModel>(parentEntry)
1022+
SweepSettingsScreen(navController, viewModel)
1023+
}
1024+
composableWithDefaultTransitions<Routes.SweepConfirm> {
1025+
val parentEntry = remember(it) { navController.getBackStackEntry(Routes.SweepNav) }
1026+
val viewModel = hiltViewModel<SweepViewModel>(parentEntry)
1027+
SweepConfirmScreen(navController, viewModel)
1028+
}
1029+
composableWithDefaultTransitions<Routes.SweepFeeRate> {
1030+
val parentEntry = remember(it) { navController.getBackStackEntry(Routes.SweepNav) }
1031+
val viewModel = hiltViewModel<SweepViewModel>(parentEntry)
1032+
SweepFeeRateScreen(navController, viewModel)
1033+
}
1034+
composableWithDefaultTransitions<Routes.SweepFeeCustom> {
1035+
val parentEntry = remember(it) { navController.getBackStackEntry(Routes.SweepNav) }
1036+
val viewModel = hiltViewModel<SweepViewModel>(parentEntry)
1037+
SweepFeeCustomScreen(navController, viewModel)
1038+
}
1039+
composableWithDefaultTransitions<Routes.SweepSuccess> {
1040+
val route = it.toRoute<Routes.SweepSuccess>()
1041+
SweepSuccessScreen(navController, amountSats = route.amountSats)
1042+
}
1043+
}
9991044
composableWithDefaultTransitions<Routes.NodeInfo> {
10001045
NodeInfoScreen(navController)
10011046
}
@@ -1689,6 +1734,24 @@ sealed interface Routes {
16891734
@Serializable
16901735
data object AddressViewer : Routes
16911736

1737+
@Serializable
1738+
data object SweepNav : Routes
1739+
1740+
@Serializable
1741+
data object Sweep : Routes
1742+
1743+
@Serializable
1744+
data object SweepConfirm : Routes
1745+
1746+
@Serializable
1747+
data object SweepFeeRate : Routes
1748+
1749+
@Serializable
1750+
data object SweepFeeCustom : Routes
1751+
1752+
@Serializable
1753+
data class SweepSuccess(val amountSats: Long) : Routes
1754+
16921755
@Serializable
16931756
data object AboutSettings : Routes
16941757

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

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import to.bitkit.androidServices.LightningNodeService
3535
import to.bitkit.androidServices.LightningNodeService.Companion.CHANNEL_ID_NODE
3636
import to.bitkit.models.NewTransactionSheetDetails
3737
import to.bitkit.ui.components.AuthCheckView
38-
import to.bitkit.ui.components.InactivityTracker
3938
import to.bitkit.ui.components.IsOnlineTracker
4039
import to.bitkit.ui.components.ToastOverlay
4140
import to.bitkit.ui.onboarding.CreateWalletWithPassphraseScreen
@@ -128,19 +127,17 @@ class MainActivity : FragmentActivity() {
128127
val isAuthenticated by appViewModel.isAuthenticated.collectAsStateWithLifecycle()
129128

130129
IsOnlineTracker(appViewModel)
131-
InactivityTracker(appViewModel, settingsViewModel) {
132-
ContentView(
133-
appViewModel = appViewModel,
134-
walletViewModel = walletViewModel,
135-
blocktankViewModel = blocktankViewModel,
136-
currencyViewModel = currencyViewModel,
137-
activityListViewModel = activityListViewModel,
138-
transferViewModel = transferViewModel,
139-
settingsViewModel = settingsViewModel,
140-
backupsViewModel = backupsViewModel,
141-
modifier = Modifier.hazeSource(hazeState, zIndex = 0f)
142-
)
143-
}
130+
ContentView(
131+
appViewModel = appViewModel,
132+
walletViewModel = walletViewModel,
133+
blocktankViewModel = blocktankViewModel,
134+
currencyViewModel = currencyViewModel,
135+
activityListViewModel = activityListViewModel,
136+
transferViewModel = transferViewModel,
137+
settingsViewModel = settingsViewModel,
138+
backupsViewModel = backupsViewModel,
139+
modifier = Modifier.hazeSource(hazeState, zIndex = 0f)
140+
)
144141

145142
AnimatedVisibility(
146143
visible = !isAuthenticated,

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

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ fun AuthCheckScreen(
1717
val app = appViewModel ?: return
1818
val settings = settingsViewModel ?: return
1919

20-
val isPinOnLaunchEnabled by settings.isPinOnLaunchEnabled.collectAsStateWithLifecycle()
2120
val isBiometricEnabled by settings.isBiometricEnabled.collectAsStateWithLifecycle()
22-
val isPinOnIdleEnabled by settings.isPinOnIdleEnabled.collectAsStateWithLifecycle()
2321
val isPinForPaymentsEnabled by settings.isPinForPaymentsEnabled.collectAsStateWithLifecycle()
2422

2523
AuthCheckView(
@@ -35,16 +33,6 @@ fun AuthCheckScreen(
3533
navController.popBackStack()
3634
}
3735

38-
AuthCheckAction.TOGGLE_PIN_ON_LAUNCH -> {
39-
settings.setIsPinOnLaunchEnabled(!isPinOnLaunchEnabled)
40-
navController.popBackStack()
41-
}
42-
43-
AuthCheckAction.TOGGLE_PIN_ON_IDLE -> {
44-
settings.setIsPinOnIdleEnabled(!isPinOnIdleEnabled)
45-
navController.popBackStack()
46-
}
47-
4836
AuthCheckAction.TOGGLE_PIN_FOR_PAYMENTS -> {
4937
settings.setIsPinForPaymentsEnabled(!isPinForPaymentsEnabled)
5038
navController.popBackStack()
@@ -68,9 +56,7 @@ fun AuthCheckScreen(
6856
}
6957

7058
object AuthCheckAction {
71-
const val TOGGLE_PIN_ON_LAUNCH = "TOGGLE_PIN_ON_LAUNCH"
7259
const val TOGGLE_BIOMETRICS = "TOGGLE_BIOMETRICS"
73-
const val TOGGLE_PIN_ON_IDLE = "TOGGLE_PIN_ON_IDLE"
7460
const val TOGGLE_PIN_FOR_PAYMENTS = "TOGGLE_PIN_FOR_PAYMENTS"
7561
const val DISABLE_PIN = "DISABLE_PIN"
7662
const val NAV_TO_RESET = "NAV_TO_RESET"

0 commit comments

Comments
 (0)