Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix camera screen not congruent #1380

Merged
merged 3 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.vultisig.wallet.data.usecases

import com.vultisig.wallet.data.common.JOIN_KEYGEN_FLOW
import com.vultisig.wallet.data.common.JOIN_KEYSIGN_FLOW
import com.vultisig.wallet.data.common.JOIN_SEND_ON_ADDRESS_FLOW
import com.vultisig.wallet.ui.navigation.Destination
import com.vultisig.wallet.ui.utils.getAddressFromQrCode
import com.vultisig.wallet.ui.utils.isReshare
import timber.log.Timber
import javax.inject.Inject
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi

internal interface GetDirectionByQrCodeUseCase : suspend (String, String?) -> Destination

internal class GetDirectionByQrCodeUseCaseImpl @Inject constructor(
private val getFlowType: GetFlowTypeUseCase,
) : GetDirectionByQrCodeUseCase {
@OptIn(ExperimentalEncodingApi::class)
override suspend fun invoke(qr: String, vaultId: String?): Destination {
Timber.d("joinOrSend(qr = $qr)")
val flowType = getFlowType(qr)
val qrBase64 = Base64.UrlSafe.encode(qr.toByteArray())
return try {
when (flowType) {
JOIN_KEYSIGN_FLOW -> {
Destination.JoinKeysign(
vaultId = requireNotNull(vaultId),
qr = qrBase64,
)
}

JOIN_KEYGEN_FLOW -> {
Destination.JoinKeygen(
qr = qrBase64,
isReshare = qr.isReshare(),
)
}

JOIN_SEND_ON_ADDRESS_FLOW -> {
val address = qr.getAddressFromQrCode()
Destination.Send(vaultId = requireNotNull(vaultId), address = address)
}

else -> Destination.ScanError
}

} catch (e: Exception) {
Timber.e(e, "Failed to navigate to destination")
Destination.ScanError
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,10 @@ internal interface UseCasesModule {
fun bindGetSendDstByKeysignInitType(
impl: GetSendDstByKeysignInitTypeImpl
): GetSendDstByKeysignInitType

@Binds
@Singleton
fun bindGetDirectionByQrCodeUseCase(
impl: GetDirectionByQrCodeUseCaseImpl
): GetDirectionByQrCodeUseCase
}
67 changes: 7 additions & 60 deletions app/src/main/java/com/vultisig/wallet/ui/models/ScanQrViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,82 +3,29 @@ package com.vultisig.wallet.ui.models
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vultisig.wallet.data.common.DeepLinkHelper
import com.vultisig.wallet.data.usecases.GetDirectionByQrCodeUseCase
import com.vultisig.wallet.data.usecases.GetFlowTypeUseCase
import com.vultisig.wallet.ui.navigation.Destination
import com.vultisig.wallet.ui.navigation.Navigator
import com.vultisig.wallet.ui.utils.getAddressFromQrCode
import com.vultisig.wallet.ui.utils.isJson
import com.vultisig.wallet.ui.utils.isReshare
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi

@HiltViewModel
internal class ScanQrViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val navigator: Navigator<Destination>,
private val getFlowTypeUseCase: GetFlowTypeUseCase,
private val getDirectionByQrCodeUseCase: GetDirectionByQrCodeUseCase,
) : ViewModel() {

private val vaultId: String? = savedStateHandle[Destination.ARG_VAULT_ID]

@OptIn(ExperimentalEncodingApi::class)
fun joinOrSend(qr: String) {
Timber.d("joinOrSend(qr = $qr)")
viewModelScope.launch {
val flowType = getFlowType(qr)
val qrBase64 = Base64.UrlSafe.encode(qr.toByteArray())
try {
navigator.navigate(
when (flowType) {
JOIN_KEYSIGN_FLOW -> {
Destination.JoinKeysign(
vaultId = requireNotNull(vaultId),
qr = qrBase64,
)
}

JOIN_KEYGEN_FLOW -> {
Destination.JoinKeygen(
qr = qrBase64,
isReshare = qr.isReshare(),
)
}

JOIN_SEND_ON_ADDRESS_FLOW -> {
val address = qr.getAddressFromQrCode()
Destination.Send(vaultId = requireNotNull(vaultId), address = address)
}

else -> Destination.ScanError
}
)
} catch (e: Exception) {
Timber.e(e, "Failed to navigate to destination")
}
}
fun joinOrSend(qr: String) = viewModelScope.launch {
navigator.navigate(getDirectionByQrCodeUseCase(qr, vaultId))
}

fun getFlowType(qr: String): String {
return try {
DeepLinkHelper(qr).getFlowType()?: throw IllegalArgumentException("No flowType found")
} catch (e: Exception) {
Timber.e(e, "Failed to parse QR-code via DeepLinkHelper")
if (qr.isJson()) {
UNKNOWN_FLOW
} else {
JOIN_SEND_ON_ADDRESS_FLOW
}
}
return getFlowTypeUseCase(qr)
}

companion object {
const val JOIN_KEYSIGN_FLOW = "SignTransaction"
const val JOIN_KEYGEN_FLOW = "NewVault"
const val JOIN_SEND_ON_ADDRESS_FLOW = "SendOnAddress"
const val UNKNOWN_FLOW = "Unknown"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.vultisig.wallet.data.repositories.BalanceVisibilityRepository
import com.vultisig.wallet.data.repositories.VaultDataStoreRepository
import com.vultisig.wallet.data.repositories.VaultRepository
import com.vultisig.wallet.data.repositories.vault.VaultMetadataRepo
import com.vultisig.wallet.data.usecases.GetDirectionByQrCodeUseCase
import com.vultisig.wallet.data.usecases.IsGlobalBackupReminderRequiredUseCase
import com.vultisig.wallet.data.usecases.NeverShowGlobalBackupReminderUseCase
import com.vultisig.wallet.ui.models.mappers.AddressToUiModelMapper
Expand All @@ -40,6 +41,7 @@ internal data class VaultAccountsUiModel(
val isRefreshing: Boolean = false,
val totalFiatValue: String? = null,
val isBalanceValueVisible: Boolean = true,
val showCameraBottomSheet: Boolean = false,
val accounts: List<AccountUiModel> = emptyList(),
) {
val isSwapEnabled = accounts.any { it.model.chain.IsSwapSupported }
Expand Down Expand Up @@ -69,6 +71,7 @@ internal class VaultAccountsViewModel @Inject constructor(
private val vaultMetadataRepo: VaultMetadataRepo,
private val isGlobalBackupReminderRequired: IsGlobalBackupReminderRequiredUseCase,
private val setNeverShowGlobalBackupReminder: NeverShowGlobalBackupReminderUseCase,
private val getDirectionByQrCodeUseCase: GetDirectionByQrCodeUseCase,
) : ViewModel() {
private var vaultId: String? = null

Expand Down Expand Up @@ -124,11 +127,8 @@ internal class VaultAccountsViewModel @Inject constructor(
}
}

fun joinKeysign() {
val vaultId = vaultId ?: return
viewModelScope.launch {
navigator.navigate(Destination.JoinThroughQr(vaultId = vaultId))
}
fun openCamera() {
uiState.update { it.copy(showCameraBottomSheet = true) }
}

fun openAccount(account: AccountUiModel) {
Expand Down Expand Up @@ -229,6 +229,15 @@ internal class VaultAccountsViewModel @Inject constructor(
uiState.update { it.copy(showMonthlyBackupReminder = false) }
}

fun dismissCameraBottomSheet() {
uiState.update { it.copy(showCameraBottomSheet = false) }
}

fun onScanSuccess(qr: String) = viewModelScope.launch {
navigator.navigate(getDirectionByQrCodeUseCase(qr, vaultId))
uiState.update { it.copy(showCameraBottomSheet = false) }
}

fun doNotRemindBackup() = viewModelScope.launch {
setNeverShowGlobalBackupReminder()
dismissBackupReminder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import com.vultisig.wallet.ui.models.AccountUiModel
import com.vultisig.wallet.ui.models.VaultAccountsUiModel
import com.vultisig.wallet.ui.models.VaultAccountsViewModel
import com.vultisig.wallet.ui.navigation.Screen
import com.vultisig.wallet.ui.screens.scan.ScanQrBottomSheet
import com.vultisig.wallet.ui.theme.Theme
import kotlinx.coroutines.launch

Expand Down Expand Up @@ -78,14 +79,20 @@ internal fun VaultAccountsScreen(
onDoNotRemind = viewModel::doNotRemindBackup,
)
}
if (state.showCameraBottomSheet) {
ScanQrBottomSheet (
onDismiss = viewModel::dismissCameraBottomSheet,
onScanSuccess = viewModel::onScanSuccess,
)
}

VaultAccountsScreen(
state = state,
isRearrangeMode = isRearrangeMode,
onRefresh = viewModel::refreshData,
onSend = viewModel::send,
onSwap = viewModel::swap,
onJoinKeysign = viewModel::joinKeysign,
openCamera = viewModel::openCamera,
onAccountClick = viewModel::openAccount,
onChooseChains = {
navHostController.navigate(
Expand All @@ -106,7 +113,7 @@ private fun VaultAccountsScreen(
onSend: () -> Unit = {},
onSwap: () -> Unit = {},
onRefresh: () -> Unit = {},
onJoinKeysign: () -> Unit = {},
openCamera: () -> Unit = {},
onAccountClick: (AccountUiModel) -> Unit = {},
onChooseChains: () -> Unit = {},
onToggleBalanceVisibility: () -> Unit = {},
Expand Down Expand Up @@ -238,7 +245,7 @@ private fun VaultAccountsScreen(
size = 40.dp,
contentDescription = "join keysign",
tint = Theme.colors.oxfordBlue600Main,
onClick = onJoinKeysign,
onClick = openCamera,
modifier = Modifier
.background(
color = Theme.colors.turquoise600Main,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.vultisig.wallet.ui.screens.scan

import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vultisig.wallet.ui.theme.Theme

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ScanQrBottomSheet (
onDismiss: () -> Unit,
onScanSuccess: (qr: String) -> Unit,
) {

ModalBottomSheet(
sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true,
),
dragHandle = null,
containerColor = Theme.colors.transparent,
onDismissRequest = onDismiss,
) {
Spacer(modifier = Modifier.height(100.dp))
ScanQrScreen(
onScanSuccess = onScanSuccess,
roundedCorners = true,
onDismiss = onDismiss,
)
}
}
Loading