Skip to content

Commit 65ef251

Browse files
committed
fix: various ui fixes
- hide 2fa expired alerts in wo - security tab in QR mode should match a WO wallet - disable sign transaction via qr for liquid accounts as this functionality is currently unavailable - fix concurrency modification exception when disconnecting the session multiple times (eg. from qr scan result)
1 parent 5790a29 commit 65ef251

File tree

7 files changed

+67
-51
lines changed

7 files changed

+67
-51
lines changed

common/src/commonMain/kotlin/com/blockstream/common/gdk/GdkSession.kt

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,8 @@ class GdkSession constructor(
774774
}
775775
}
776776

777-
fun disconnect() {
777+
private val disconnectMutex = Mutex()
778+
suspend fun disconnect() {
778779
logger.d { "Disconnect" }
779780

780781
_isConnectedState.value = false
@@ -829,7 +830,7 @@ class GdkSession constructor(
829830
_walletActiveEventInvalidated = true
830831
_accountEmptiedEvent = null
831832

832-
val gaSessionToBeDestroyed = gdkSessions.values.toList()
833+
val gaSessionToBeDestroyed = disconnectMutex.withLock { gdkSessions.values.toList() }
833834

834835
// Create a new gaSession
835836
gdkSessions.clear()
@@ -855,22 +856,6 @@ class GdkSession constructor(
855856
}
856857
}
857858

858-
private fun resetNetwork(network: Network) {
859-
// Remove as active network
860-
activeSessions.remove(network)
861-
862-
gdkSessions.remove(network)?.also { gaSessionToBeDestroyed ->
863-
gdk.destroySession(gaSessionToBeDestroyed)
864-
}
865-
866-
// Init a new Session and connect
867-
try {
868-
gdk.connect(gdkSession(network), createConnectionParams(network))
869-
} catch (e: Exception) {
870-
e.printStackTrace()
871-
}
872-
}
873-
874859
fun disconnectAsync(reason: LogoutReason = LogoutReason.USER_ACTION): Boolean {
875860
// Disconnect only if needed
876861
if (isConnected) {
@@ -903,7 +888,23 @@ class GdkSession constructor(
903888
return false
904889
}
905890

906-
private fun prepareHttpRequest() {
891+
private fun resetNetwork(network: Network) {
892+
// Remove as active network
893+
activeSessions.remove(network)
894+
895+
gdkSessions.remove(network)?.also { gaSessionToBeDestroyed ->
896+
gdk.destroySession(gaSessionToBeDestroyed)
897+
}
898+
899+
// Init a new Session and connect
900+
try {
901+
gdk.connect(gdkSession(network), createConnectionParams(network))
902+
} catch (e: Exception) {
903+
e.printStackTrace()
904+
}
905+
}
906+
907+
private suspend fun prepareHttpRequest() {
907908
logger.i { "Prepare HTTP Request Provider" }
908909
disconnect()
909910

@@ -3081,13 +3082,14 @@ class GdkSession constructor(
30813082
}
30823083

30833084
private fun scanExpired2FA() {
3084-
scope.launch(context = logException(countly)) {
3085-
_expired2FAStateFlow.value = accounts.value.filter {
3086-
3087-
it.type == AccountType.STANDARD && getUnspentOutputs(
3088-
account = it,
3089-
isExpired = true
3090-
).unspentOutputs.isNotEmpty()
3085+
if (!isWatchOnlyValue) {
3086+
scope.launch(context = logException(countly)) {
3087+
_expired2FAStateFlow.value = accounts.value.filter {
3088+
it.type == AccountType.STANDARD && getUnspentOutputs(
3089+
account = it,
3090+
isExpired = true
3091+
).unspentOutputs.isNotEmpty()
3092+
}
30913093
}
30923094
}
30933095
}
@@ -3323,7 +3325,7 @@ class GdkSession constructor(
33233325

33243326
internal fun destroy(disconnect: Boolean = true) {
33253327
if (disconnect) {
3326-
disconnect()
3328+
disconnectAsync()
33273329
}
33283330
scope.cancel("Destroy")
33293331
}

common/src/commonMain/kotlin/com/blockstream/common/models/GreenViewModel.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ import kotlinx.coroutines.withTimeout
104104
import org.koin.core.component.KoinComponent
105105
import org.koin.core.component.inject
106106
import kotlin.time.Duration
107+
import kotlin.time.Duration.Companion.minutes
107108

108109
open class SimpleGreenViewModel(
109110
greenWalletOrNull: GreenWallet? = null,
@@ -239,6 +240,9 @@ open class GreenViewModel constructor(
239240
private val _isHwWatchOnly = MutableStateFlow(false)
240241
val isHwWatchOnly = _isHwWatchOnly
241242

243+
private val _isQrWatchOnly = MutableStateFlow(false)
244+
val isQrWatchOnly = _isQrWatchOnly
245+
242246
init {
243247
// It's better to initiate the ViewModel with a bootstrap() call
244248
// https://kotlinlang.org/docs/inheritance.html#derived-class-initialization-order
@@ -283,6 +287,7 @@ open class GreenViewModel constructor(
283287
sessionOrNull?.isWatchOnly?.onEach {
284288
_isWatchOnly.value = it
285289
_isHwWatchOnly.value = session.isHwWatchOnly
290+
_isQrWatchOnly.value = greenWalletOrNull?.isWatchOnlyQr ?: false
286291
}?.launchIn(this)
287292

288293
_event.onEach {
@@ -1122,7 +1127,7 @@ open class GreenViewModel constructor(
11221127
countly.importWallet(session)
11231128

11241129
wallet
1125-
}, onSuccess = {
1130+
}, timeout = 1.minutes, onSuccess = {
11261131
postSideEffect(SideEffects.NavigateTo(NavigateDestinations.WalletOverview(it)))
11271132
})
11281133
}

common/src/commonMain/kotlin/com/blockstream/common/models/abstract/AbstractScannerViewModel.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import kotlinx.coroutines.CancellationException
1515
import kotlinx.coroutines.CompletableDeferred
1616
import kotlinx.coroutines.flow.StateFlow
1717
import kotlinx.coroutines.launch
18+
import kotlinx.coroutines.sync.Mutex
19+
import kotlinx.coroutines.sync.withLock
1820

1921
abstract class AbstractScannerViewModel(val isDecodeContinuous: Boolean = false, greenWalletOrNull: GreenWallet? = null) :
2022
GreenViewModel(greenWalletOrNull = greenWalletOrNull) {
@@ -28,13 +30,18 @@ abstract class AbstractScannerViewModel(val isDecodeContinuous: Boolean = false,
2830
internal val _progress = MutableStateFlow<Int?>(null)
2931
val progress: StateFlow<Int?> = _progress
3032

31-
private fun barcodeScannerResult(scanResult: ScanResult) {
33+
private val mutex = Mutex()
34+
private suspend fun barcodeScannerResult(scanResult: ScanResult) {
3235
if (appInfo.isDevelopmentOrDebug) {
3336
logger.d { "QR (DevelopmentOrDebug): $scanResult" }
3437
}
3538

36-
isScanComplete = true
37-
setScanResult(scanResult)
39+
mutex.withLock {
40+
if (!isScanComplete) {
41+
isScanComplete = true
42+
setScanResult(scanResult)
43+
}
44+
}
3845
}
3946

4047
internal fun resetScanner() {

compose/src/commonMain/kotlin/com/blockstream/compose/components/GreenConfirmButton.kt

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,17 @@ fun GreenConfirmButton(
2727
val isHwWatchOnly by viewModel.isHwWatchOnly.collectAsStateWithLifecycle()
2828

2929
if (isWatchOnly && !isSweep) {
30-
GreenButton(
31-
text = stringResource(Res.string.id_sign_transaction_via_qr),
32-
enabled = buttonEnabled,
33-
size = GreenButtonSize.BIG,
34-
modifier = Modifier.fillMaxWidth(),
35-
onClick = {
36-
onConfirm.invoke(true)
37-
}
38-
)
30+
if(!viewModel.account.isLiquid) {
31+
GreenButton(
32+
text = stringResource(Res.string.id_sign_transaction_via_qr),
33+
enabled = buttonEnabled,
34+
size = GreenButtonSize.BIG,
35+
modifier = Modifier.fillMaxWidth(),
36+
onClick = {
37+
onConfirm.invoke(true)
38+
}
39+
)
40+
}
3941

4042
if (isHwWatchOnly) {
4143
GreenButton(

compose/src/commonMain/kotlin/com/blockstream/compose/screens/devices/ImportPubKeyScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import com.blockstream.compose.GreenPreview
3333
import com.blockstream.compose.components.GreenButton
3434
import com.blockstream.compose.components.GreenButtonSize
3535
import com.blockstream.compose.components.GreenButtonType
36+
import com.blockstream.compose.components.OnProgressStyle
3637
import com.blockstream.compose.extensions.icon
3738
import com.blockstream.compose.screens.jade.JadeQRResult
3839
import com.blockstream.compose.theme.bodyLarge
@@ -70,7 +71,7 @@ fun ImportPubKeyScreen(
7071
val deviceModel = viewModel.deviceModel
7172
val onProgress by viewModel.onProgress.collectAsStateWithLifecycle()
7273

73-
SetupScreen(viewModel = viewModel) {
74+
SetupScreen(viewModel = viewModel, onProgressStyle = OnProgressStyle.Disabled) {
7475

7576
Column(
7677
modifier = Modifier.weight(2f),

compose/src/commonMain/kotlin/com/blockstream/compose/screens/jade/JadeQRScreen.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import com.blockstream.compose.components.GreenButtonSize
3838
import com.blockstream.compose.components.GreenButtonType
3939
import com.blockstream.compose.components.GreenQR
4040
import com.blockstream.compose.components.GreenScanner
41+
import com.blockstream.compose.components.OnProgressStyle
4142
import com.blockstream.compose.theme.GreenTheme
4243
import com.blockstream.compose.theme.green
4344
import com.blockstream.compose.theme.headlineSmall
@@ -79,7 +80,12 @@ fun JadeQRScreen(
7980
}
8081
}
8182

82-
SetupScreen(viewModel = viewModel, withPadding = false, withBottomInsets = false, sideEffectsHandler = {
83+
SetupScreen(
84+
viewModel = viewModel,
85+
withPadding = false,
86+
withBottomInsets = false,
87+
onProgressStyle = OnProgressStyle.Disabled,
88+
sideEffectsHandler = {
8389
when (it) {
8490
is SideEffects.Success -> {
8591
if (viewModel.operation is JadeQrOperation.PinUnlock) {

compose/src/commonMain/kotlin/com/blockstream/compose/screens/overview/SecurityScreen.kt

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ import androidx.compose.runtime.setValue
1919
import androidx.compose.ui.Alignment
2020
import androidx.compose.ui.Modifier
2121
import androidx.compose.ui.graphics.vector.ImageVector
22-
import androidx.compose.ui.text.SpanStyle
2322
import androidx.compose.ui.unit.dp
24-
import androidx.compose.ui.unit.sp
2523
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2624
import blockstream_green.common.generated.resources.Res
2725
import blockstream_green.common.generated.resources.id_biometrics
@@ -30,15 +28,13 @@ import blockstream_green.common.generated.resources.id_connect_hardware_wallet
3028
import blockstream_green.common.generated.resources.id_firmware_update
3129
import blockstream_green.common.generated.resources.id_genuine_check
3230
import blockstream_green.common.generated.resources.id_hardware
33-
import blockstream_green.common.generated.resources.id_learn_more
3431
import blockstream_green.common.generated.resources.id_mobile
3532
import blockstream_green.common.generated.resources.id_pin
3633
import blockstream_green.common.generated.resources.id_recovery
3734
import blockstream_green.common.generated.resources.id_recovery_phrase
3835
import blockstream_green.common.generated.resources.id_security_level_
3936
import blockstream_green.common.generated.resources.id_unlock_method
4037
import blockstream_green.common.generated.resources.id_watchonly
41-
import blockstream_green.common.generated.resources.id_watchonly_description
4238
import blockstream_green.common.generated.resources.id_your_device
4339
import blockstream_green.common.generated.resources.id_your_jade
4440
import com.adamglin.PhosphorIcons
@@ -76,19 +72,15 @@ import com.blockstream.compose.components.ListHeader
7672
import com.blockstream.compose.components.OnProgressStyle
7773
import com.blockstream.compose.components.Promo
7874
import com.blockstream.compose.screens.overview.components.WatchOnlyWalletDescription
79-
import com.blockstream.compose.theme.bodyMedium
8075
import com.blockstream.compose.theme.displaySmall
8176
import com.blockstream.compose.theme.green
8277
import com.blockstream.compose.theme.labelLarge
83-
import com.blockstream.compose.theme.md_theme_primary
8478
import com.blockstream.compose.theme.titleMedium
8579
import com.blockstream.compose.theme.titleSmall
8680
import com.blockstream.compose.theme.whiteMedium
8781
import com.blockstream.compose.utils.SetupScreen
8882
import com.blockstream.ui.components.GreenColumn
8983
import com.blockstream.ui.components.GreenRow
90-
import com.blockstream.ui.components.RichSpan
91-
import com.blockstream.ui.components.RichText
9284
import com.blockstream.ui.navigation.LocalInnerPadding
9385
import com.blockstream.ui.navigation.getResult
9486
import com.blockstream.ui.utils.bottom
@@ -121,6 +113,7 @@ fun SecurityScreen(viewModel: SecurityViewModelAbstract) {
121113
val isJade by viewModel.isJade.collectAsStateWithLifecycle()
122114

123115
val isWatchOnly by viewModel.isWatchOnly.collectAsStateWithLifecycle()
116+
val isQrWatchOnly by viewModel.isQrWatchOnly.collectAsStateWithLifecycle()
124117
val showGenuineCheck by viewModel.showGenuineCheck.collectAsStateWithLifecycle()
125118

126119
SetupScreen(
@@ -189,7 +182,7 @@ fun SecurityScreen(viewModel: SecurityViewModelAbstract) {
189182
}
190183
}
191184

192-
if (isHardware) {
185+
if (isHardware && !isQrWatchOnly) {
193186

194187
if (isJade) {
195188
item(key = "jade_header") {

0 commit comments

Comments
 (0)