11package to.bitkit.ui.sheets.hardware
22
3+ import android.content.Context
34import androidx.compose.runtime.Immutable
45import androidx.lifecycle.ViewModel
56import androidx.lifecycle.viewModelScope
67import com.synonym.bitkitcore.TrezorFeatures
78import dagger.hilt.android.lifecycle.HiltViewModel
9+ import dagger.hilt.android.qualifiers.ApplicationContext
810import kotlinx.coroutines.Job
911import kotlinx.coroutines.delay
1012import kotlinx.coroutines.flow.MutableSharedFlow
@@ -14,6 +16,7 @@ import kotlinx.coroutines.flow.asStateFlow
1416import kotlinx.coroutines.flow.update
1517import kotlinx.coroutines.isActive
1618import kotlinx.coroutines.launch
19+ import to.bitkit.R
1720import to.bitkit.repositories.HwWalletRepo
1821import to.bitkit.repositories.HwWalletRepo.Companion.DEVICE_LABEL_MAX_LENGTH
1922import to.bitkit.repositories.resolveHwWalletName
@@ -30,6 +33,7 @@ import kotlin.time.Duration.Companion.seconds
3033@HiltViewModel
3134class HwConnectViewModel @Inject constructor(
3235 private val hwWalletRepo : HwWalletRepo ,
36+ @ApplicationContext private val context : Context ,
3337) : ViewModel() {
3438 companion object {
3539 /* * Delay between scan attempts while searching for a nearby device. */
@@ -54,6 +58,7 @@ class HwConnectViewModel @Inject constructor(
5458
5559 fun onIntroContinue (includeBluetooth : Boolean = true) {
5660 includeBluetoothInScan = includeBluetooth
61+ _uiState .update { it.copy(errorMessage = null ) }
5762 setEffect(HwConnectEffect .NavigateToSearching )
5863 startSearching()
5964 }
@@ -71,6 +76,7 @@ class HwConnectViewModel @Inject constructor(
7176 isSearching = false ,
7277 foundDeviceId = deviceId,
7378 deviceModel = deviceModel.ifBlank { resolveHwWalletName(label = null , model = null ) },
79+ errorMessage = null ,
7480 )
7581 }
7682 scanUsbBeforeConnect = true
@@ -81,14 +87,21 @@ class HwConnectViewModel @Inject constructor(
8187 val deviceId = deviceIdOverride ? : state.foundDeviceId ? : return
8288 val shouldScanUsbBeforeConnect = scanUsbBeforeConnect
8389 searchJob?.cancel()
84- _uiState .update { it.copy(isConnecting = true ) }
90+ _uiState .update { it.copy(isConnecting = true , errorMessage = null ) }
8591 viewModelScope.launch {
8692 if (shouldScanUsbBeforeConnect) {
8793 hwWalletRepo.scan(includeBluetooth = false )
8894 }
8995 hwWalletRepo.connect(deviceId)
9096 .onSuccess { onConnected(deviceId, it) }
91- .onFailure { _uiState .update { state -> state.copy(isConnecting = false ) } }
97+ .onFailure {
98+ _uiState .update { state ->
99+ state.copy(
100+ isConnecting = false ,
101+ errorMessage = context.getString(R .string.hardware__connect_error),
102+ )
103+ }
104+ }
92105 }
93106 }
94107
@@ -118,20 +131,30 @@ class HwConnectViewModel @Inject constructor(
118131 private fun startSearching () {
119132 if (searchJob?.isActive == true ) return
120133 scanUsbBeforeConnect = false
121- _uiState .update { it.copy(isSearching = true ) }
134+ _uiState .update { it.copy(isSearching = true , errorMessage = null ) }
122135 searchJob = viewModelScope.launch {
123136 while (isActive) {
124- hwWalletRepo.scan(includeBluetooth = includeBluetoothInScan)
137+ val scanResult = hwWalletRepo.scan(includeBluetooth = includeBluetoothInScan)
138+ if (scanResult.isFailure) {
139+ _uiState .update {
140+ it.copy(errorMessage = context.getString(R .string.hardware__search_error))
141+ }
142+ delay(SCAN_INTERVAL )
143+ continue
144+ }
145+ _uiState .update { it.copy(errorMessage = null ) }
125146 val device = hwWalletRepo.deviceState.value.nearbyDevices.firstOrNull()
126147 if (device != null ) {
148+ val deviceModel = resolveHwWalletName(label = null , model = device.model)
127149 _uiState .update {
128150 it.copy(
129151 isSearching = false ,
130152 foundDeviceId = device.id,
131- deviceModel = resolveHwWalletName(label = null , model = device.model),
153+ deviceModel = deviceModel,
154+ errorMessage = null ,
132155 )
133156 }
134- setEffect(HwConnectEffect .NavigateToFound )
157+ setEffect(HwConnectEffect .NavigateToFound (device.id, deviceModel) )
135158 return @launch
136159 }
137160 delay(SCAN_INTERVAL )
@@ -147,6 +170,7 @@ class HwConnectViewModel @Inject constructor(
147170 pairedDeviceId = deviceId,
148171 deviceName = name,
149172 labelInput = if (labelInitialized) it.labelInput else name,
173+ errorMessage = null ,
150174 )
151175 }
152176 labelInitialized = true
@@ -191,11 +215,12 @@ data class HwConnectUiState(
191215 val deviceModel : String = " " ,
192216 val balanceSats : ULong = 0uL ,
193217 val labelInput : String = " " ,
218+ val errorMessage : String? = null ,
194219)
195220
196221sealed interface HwConnectEffect {
197222 data object NavigateToSearching : HwConnectEffect
198- data object NavigateToFound : HwConnectEffect
223+ data class NavigateToFound ( val deviceId : String , val deviceModel : String ) : HwConnectEffect
199224 data object NavigateToPairCode : HwConnectEffect
200225 data object NavigateToPaired : HwConnectEffect
201226 data object Dismiss : HwConnectEffect
0 commit comments