Updated on 2026-08-14
This commit is contained in:
parent
688b5da7bd
commit
ddf5f8af9f
23 changed files with 0 additions and 2188 deletions
|
|
@ -27,7 +27,6 @@ import com.tangem.tap.features.saveWallet.ui.SaveWalletBottomSheetFragment
|
|||
import com.tangem.tap.features.send.ui.SendFragment
|
||||
import com.tangem.tap.features.shop.ui.ShopFragment
|
||||
import com.tangem.tap.features.tokens.impl.presentation.TokensListFragment
|
||||
import com.tangem.tap.features.walletSelector.ui.WalletSelectorBottomSheetFragment
|
||||
import com.tangem.tap.features.welcome.ui.WelcomeFragment
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -177,7 +176,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
AppScreen.Swap -> SwapFragment()
|
||||
AppScreen.Welcome -> WelcomeFragment()
|
||||
AppScreen.SaveWallet -> SaveWalletBottomSheetFragment()
|
||||
AppScreen.WalletSelector -> WalletSelectorBottomSheetFragment()
|
||||
AppScreen.AppCurrencySelector -> AppCurrencySelectorFragment()
|
||||
AppScreen.ModalNotification -> ModalNotificationBottomSheetFragment()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import com.tangem.tap.features.send.redux.reducers.SendScreenReducer
|
|||
import com.tangem.tap.features.shop.redux.ShopReducer
|
||||
import com.tangem.tap.features.signin.redux.SignInReducer
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokensReducer
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorReducer
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeReducer
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphReducer
|
||||
|
|
@ -41,7 +40,6 @@ fun appReducer(action: Action, state: AppState?, appStateHolder: AppStateHolder)
|
|||
shopState = ShopReducer.reduce(action, state.shopState),
|
||||
welcomeState = WelcomeReducer.reduce(action, state),
|
||||
saveWalletState = SaveWalletReducer.reduce(action, state),
|
||||
walletSelectorState = WalletSelectorReducer.reduce(action, state),
|
||||
signInState = SignInReducer.reduce(action, state),
|
||||
daggerGraphState = DaggerGraphReducer.reduce(action, state),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -35,8 +35,6 @@ import com.tangem.tap.features.signin.redux.SignInMiddleware
|
|||
import com.tangem.tap.features.signin.redux.SignInState
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokensState
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorMiddleware
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorState
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeMiddleware
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeState
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphMiddleware
|
||||
|
|
@ -61,7 +59,6 @@ data class AppState(
|
|||
val shopState: ShopState = ShopState(),
|
||||
val welcomeState: WelcomeState = WelcomeState(),
|
||||
val saveWalletState: SaveWalletState = SaveWalletState(),
|
||||
val walletSelectorState: WalletSelectorState = WalletSelectorState(),
|
||||
val signInState: SignInState = SignInState(),
|
||||
val daggerGraphState: DaggerGraphState = DaggerGraphState(),
|
||||
) : StateType {
|
||||
|
|
@ -99,7 +96,6 @@ data class AppState(
|
|||
ShopMiddleware().shopMiddleware,
|
||||
WelcomeMiddleware().middleware,
|
||||
SaveWalletMiddleware().middleware,
|
||||
WalletSelectorMiddleware().middleware,
|
||||
LockUserWalletsTimerMiddleware().middleware,
|
||||
AccessCodeRequestPolicyMiddleware().middleware,
|
||||
SignInMiddleware.middleware,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation
|
|||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
|
|
@ -234,7 +233,6 @@ class DetailsMiddleware {
|
|||
is DetailsAction.AppSettings.ChangeAppCurrency -> {
|
||||
store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency))
|
||||
store.dispatch(DetailsAction.ChangeAppCurrency(action.fiatCurrency))
|
||||
store.dispatch(WalletSelectorAction.ChangeAppCurrency(action.fiatCurrency))
|
||||
}
|
||||
is DetailsAction.AppSettings.SwitchPrivacySetting.Success,
|
||||
is DetailsAction.AppSettings.SwitchPrivacySetting.Failure,
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.redux
|
||||
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.domain.model.TotalFiatBalance
|
||||
|
||||
data class UserWalletModel(
|
||||
val id: UserWalletId,
|
||||
val name: String,
|
||||
val artworkUrl: String,
|
||||
val type: Type,
|
||||
val fiatBalance: TotalFiatBalance,
|
||||
val isLocked: Boolean,
|
||||
) {
|
||||
sealed interface Type {
|
||||
data class SingleCurrency(
|
||||
val blockchainName: String,
|
||||
) : Type
|
||||
|
||||
data class MultiCurrency(
|
||||
val cardsInWallet: Int,
|
||||
val tokensCount: Int,
|
||||
) : Type
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.redux
|
||||
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import org.rekotlin.Action
|
||||
|
||||
internal sealed interface WalletSelectorAction : Action {
|
||||
data class UserWalletsLoaded(
|
||||
val userWallets: List<UserWallet>,
|
||||
) : WalletSelectorAction
|
||||
|
||||
data class SelectedWalletChanged(
|
||||
val selectedWallet: UserWallet,
|
||||
) : WalletSelectorAction
|
||||
|
||||
data class IsLockedChanged(
|
||||
val isLocked: Boolean,
|
||||
) : WalletSelectorAction
|
||||
|
||||
data class WalletStoresChanged(
|
||||
val walletsStores: Map<UserWalletId, List<WalletStoreModel>>,
|
||||
) : WalletSelectorAction
|
||||
|
||||
data class BalancesLoaded(
|
||||
val userWalletModels: List<UserWalletModel>,
|
||||
) : WalletSelectorAction
|
||||
|
||||
object UnlockWithBiometry : WalletSelectorAction {
|
||||
object Success : WalletSelectorAction
|
||||
data class Error(val error: TangemError) : WalletSelectorAction
|
||||
}
|
||||
|
||||
data class SelectWallet(
|
||||
val userWalletId: UserWalletId,
|
||||
val sendAnalyticsEvent: Boolean = false,
|
||||
) : WalletSelectorAction
|
||||
|
||||
data class RenameWallet(
|
||||
val userWalletId: UserWalletId,
|
||||
val newName: String,
|
||||
) : WalletSelectorAction
|
||||
|
||||
data class RemoveWallets(
|
||||
val userWalletsIds: List<UserWalletId>,
|
||||
) : WalletSelectorAction
|
||||
|
||||
object AddWallet : WalletSelectorAction {
|
||||
object Success : WalletSelectorAction
|
||||
data class Error(val error: TangemError) : WalletSelectorAction
|
||||
}
|
||||
|
||||
data class ChangeAppCurrency(
|
||||
val fiatCurrency: FiatCurrency,
|
||||
) : WalletSelectorAction
|
||||
|
||||
object CloseError : WalletSelectorAction
|
||||
|
||||
object ClearUserWallets : WalletSelectorAction
|
||||
}
|
||||
|
|
@ -1,397 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.redux
|
||||
|
||||
import com.tangem.common.*
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.userwallets.UserWalletBuilder
|
||||
import com.tangem.domain.userwallets.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.legacy.unlockIfLockable
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import com.tangem.tap.common.analytics.events.MyWallets
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.model.TotalFiatBalance
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
|
||||
// Refactoring is coming
|
||||
@Suppress("LargeClass")
|
||||
internal class WalletSelectorMiddleware {
|
||||
val middleware: Middleware<AppState> = { _, appStateProvider ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
val appState = appStateProvider()
|
||||
if (action is WalletSelectorAction && appState != null) {
|
||||
handleAction(action, appState.walletSelectorState)
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAction(action: WalletSelectorAction, state: WalletSelectorState) {
|
||||
when (action) {
|
||||
is WalletSelectorAction.UserWalletsLoaded -> {
|
||||
fetchWalletStores(action.userWallets)
|
||||
}
|
||||
is WalletSelectorAction.WalletStoresChanged -> {
|
||||
updateBalances(action.walletsStores, state)
|
||||
}
|
||||
is WalletSelectorAction.UnlockWithBiometry -> {
|
||||
unlockWallets()
|
||||
}
|
||||
is WalletSelectorAction.AddWallet -> {
|
||||
addWallet()
|
||||
}
|
||||
is WalletSelectorAction.SelectWallet -> {
|
||||
selectWallet(action.userWalletId)
|
||||
}
|
||||
is WalletSelectorAction.RemoveWallets -> {
|
||||
deleteWallets(action.userWalletsIds, state)
|
||||
}
|
||||
is WalletSelectorAction.RenameWallet -> {
|
||||
renameWallet(action.userWalletId, action.newName)
|
||||
}
|
||||
is WalletSelectorAction.ChangeAppCurrency -> {
|
||||
refreshUserWalletsAmounts()
|
||||
}
|
||||
is WalletSelectorAction.ClearUserWallets -> {
|
||||
clearUserWalletsAndCloseError()
|
||||
}
|
||||
is WalletSelectorAction.AddWallet.Success,
|
||||
is WalletSelectorAction.AddWallet.Error,
|
||||
is WalletSelectorAction.SelectedWalletChanged,
|
||||
is WalletSelectorAction.UnlockWithBiometry.Error,
|
||||
is WalletSelectorAction.UnlockWithBiometry.Success,
|
||||
is WalletSelectorAction.BalancesLoaded,
|
||||
is WalletSelectorAction.IsLockedChanged,
|
||||
is WalletSelectorAction.CloseError,
|
||||
-> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchWalletStores(userWallets: List<UserWallet>) {
|
||||
scope.launch {
|
||||
walletStoresManager.fetch(userWallets)
|
||||
.doOnFailure { error ->
|
||||
Timber.e(error, "Unable to fetch wallet stores")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateBalances(
|
||||
updatedWalletStores: Map<UserWalletId, List<WalletStoreModel>>,
|
||||
state: WalletSelectorState,
|
||||
) {
|
||||
if (updatedWalletStores.isNotEmpty()) {
|
||||
scope.launch(Dispatchers.Default) {
|
||||
val updatedWallets = state.wallets.calculateBalanceAndUpdateWalletStores(updatedWalletStores)
|
||||
if (updatedWallets != state.wallets) {
|
||||
store.dispatchOnMain(WalletSelectorAction.BalancesLoaded(updatedWallets))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unlockWallets() {
|
||||
Analytics.send(MyWallets.Button.UnlockWithBiometrics())
|
||||
|
||||
scope.launch {
|
||||
userWalletsListManager.unlockIfLockable()
|
||||
.doOnFailure { error ->
|
||||
Timber.e(error, "Unable to unlock all user wallets")
|
||||
store.dispatchOnMain(WalletSelectorAction.UnlockWithBiometry.Error(error))
|
||||
}
|
||||
.doOnSuccess {
|
||||
store.dispatchOnMain(WalletSelectorAction.UnlockWithBiometry.Success)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addWallet() = scope.launch {
|
||||
Analytics.send(MyWallets.Button.ScanNewCard())
|
||||
|
||||
val cardSdkConfigRepository = store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository)
|
||||
|
||||
val prevUseBiometricsForAccessCode = cardSdkConfigRepository.isBiometricsRequestPolicy()
|
||||
|
||||
// Update access code policy for access code saving when a card was scanned
|
||||
cardSdkConfigRepository.setAccessCodeRequestPolicy(
|
||||
isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes,
|
||||
)
|
||||
|
||||
store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan(
|
||||
analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.MyWallets),
|
||||
onWalletNotCreated = {
|
||||
// No need to rollback policy, continue with the policy set before the card scan
|
||||
store.dispatchOnMain(WalletSelectorAction.AddWallet.Success)
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
},
|
||||
disclaimerWillShow = {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
},
|
||||
onSuccess = { scanResponse ->
|
||||
saveUserWalletAndPopBackToWalletScreen(scanResponse)
|
||||
.doOnFailure { error ->
|
||||
// Rollback policy if card saving was failed
|
||||
cardSdkConfigRepository.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode)
|
||||
Timber.e(error, "Unable to save user wallet")
|
||||
store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error))
|
||||
}
|
||||
},
|
||||
onFailure = { error ->
|
||||
// Rollback policy if card scanning was failed
|
||||
cardSdkConfigRepository.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode)
|
||||
Timber.e(error, "Unable to scan card")
|
||||
store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun saveUserWalletAndPopBackToWalletScreen(scanResponse: ScanResponse): CompletionResult<Unit> {
|
||||
val userWallet = UserWalletBuilder(scanResponse).build()
|
||||
?: return CompletionResult.Failure(TangemSdkError.WalletIsNotCreated())
|
||||
|
||||
return userWalletsListManager.save(userWallet)
|
||||
.doOnSuccess {
|
||||
store.dispatchOnMain(WalletSelectorAction.AddWallet.Success)
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
store.onUserWalletSelected(userWallet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectWallet(userWalletId: UserWalletId) {
|
||||
scope.launch {
|
||||
userWalletsListManager.get(userWalletId)
|
||||
.flatMap { userWallet ->
|
||||
if (userWallet.isLocked) {
|
||||
unlockUserWalletWithScannedCard(userWallet)
|
||||
} else {
|
||||
userWalletsListManager.select(userWalletId)
|
||||
}
|
||||
}
|
||||
.doOnFailure { error ->
|
||||
Timber.e(
|
||||
error,
|
||||
"""
|
||||
Unable to select user wallet
|
||||
|- Wallet ID to select: $userWalletId
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
.doOnSuccess {
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
|
||||
if (selectedUserWallet != null) {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
store.onUserWalletSelected(
|
||||
userWallet = selectedUserWallet,
|
||||
sendAnalyticsEvent = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun unlockUserWalletWithScannedCard(userWallet: UserWallet): CompletionResult<Unit> {
|
||||
Analytics.send(MyWallets.Button.WalletUnlockTapped())
|
||||
tangemSdkManager.changeDisplayedCardIdNumbersCount(userWallet.scanResponse)
|
||||
return store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan()
|
||||
.map { scanResponse ->
|
||||
val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build()
|
||||
if (scannedUserWalletId == userWallet.walletId) {
|
||||
userWallet.updateCardWallets(scanResponse)
|
||||
} else {
|
||||
// TODO: Display error
|
||||
Timber.e(
|
||||
"""
|
||||
Unable to unlock and select user wallet
|
||||
|- Excepted ID: ${userWallet.walletId}
|
||||
|- Received ID: $scannedUserWalletId
|
||||
""".trimIndent(),
|
||||
)
|
||||
error("Wrong card")
|
||||
}
|
||||
}
|
||||
.flatMap { updatedUserWallet ->
|
||||
userWalletsListManager.save(updatedUserWallet, canOverride = true)
|
||||
}
|
||||
.doOnFailure {
|
||||
tangemSdkManager.changeDisplayedCardIdNumbersCount(
|
||||
scanResponse = userWalletsListManager.selectedUserWalletSync?.scanResponse,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun UserWallet.updateCardWallets(scanResponse: ScanResponse): UserWallet {
|
||||
return this.copy(
|
||||
scanResponse = this.scanResponse.copy(
|
||||
card = this.scanResponse.card.copy(
|
||||
wallets = scanResponse.card.wallets,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun deleteWallets(userWalletsIds: List<UserWalletId>, state: WalletSelectorState) {
|
||||
Analytics.send(MyWallets.Button.DeleteWalletTapped())
|
||||
|
||||
scope.launch {
|
||||
when (userWalletsIds.size) {
|
||||
state.wallets.size -> clearUserWalletsAndPopBackToHomeScreen()
|
||||
else -> deleteUserWallets(
|
||||
userWalletsIds = userWalletsIds,
|
||||
currentSelectedWalletId = state.selectedWalletId,
|
||||
)
|
||||
}
|
||||
.doOnFailure { error ->
|
||||
Timber.e(
|
||||
error,
|
||||
"""
|
||||
Unable to delete user wallets
|
||||
|- Wallets IDs to delete: $userWalletsIds
|
||||
|- Current wallets IDs: ${state.wallets.map { it.id }}
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renameWallet(userWalletId: UserWalletId, newName: String) {
|
||||
scope.launch {
|
||||
userWalletsListManager.update(userWalletId) { it.copy(name = newName) }
|
||||
.doOnFailure { error ->
|
||||
Timber.e(
|
||||
error,
|
||||
"""
|
||||
Unable to rename user wallet
|
||||
|- Wallet ID: $userWalletId
|
||||
|- New name: $newName
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshUserWalletsAmounts() {
|
||||
val featureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles)
|
||||
if (featureToggles.isRedesignedScreenEnabled) return
|
||||
|
||||
scope.launch {
|
||||
walletStoresManager.updateAmounts(
|
||||
userWallets = userWalletsListManager.userWallets.firstOrNull().orEmpty(),
|
||||
)
|
||||
.doOnFailure { error ->
|
||||
Timber.e(error, "Unable to refresh user wallets amounts")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearUserWalletsAndCloseError() = scope.launch {
|
||||
clearUserWallets()
|
||||
.doOnSuccess {
|
||||
store.dispatchWithMain(WalletSelectorAction.CloseError)
|
||||
popBackToHome()
|
||||
}
|
||||
.doOnFailure { e ->
|
||||
Timber.e(e, "Unable to clear user wallets")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun clearUserWalletsAndPopBackToHomeScreen(): CompletionResult<Unit> {
|
||||
return clearUserWallets()
|
||||
.doOnSuccess { popBackToHome() }
|
||||
}
|
||||
|
||||
private suspend fun clearUserWallets(): CompletionResult<Unit> {
|
||||
return userWalletsListManager.clear()
|
||||
.flatMap { walletStoresManager.clear() }
|
||||
.flatMap { tangemSdkManager.clearSavedUserCodes() }
|
||||
}
|
||||
|
||||
private suspend fun popBackToHome() {
|
||||
// !!! Workaround !!!
|
||||
store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
delay(timeMillis = 280)
|
||||
store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
|
||||
private suspend fun deleteUserWallets(
|
||||
userWalletsIds: List<UserWalletId>,
|
||||
currentSelectedWalletId: UserWalletId?,
|
||||
): CompletionResult<Unit> {
|
||||
return userWalletsListManager.delete(userWalletsIds)
|
||||
.flatMap { walletStoresManager.delete(userWalletsIds) }
|
||||
.flatMap { deleteAccessCodes(userWalletsIds) }
|
||||
.doOnSuccess {
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
|
||||
|
||||
when {
|
||||
selectedUserWallet == null -> {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome))
|
||||
}
|
||||
currentSelectedWalletId != selectedUserWallet.walletId -> {
|
||||
store.onUserWalletSelected(
|
||||
userWallet = selectedUserWallet,
|
||||
sendAnalyticsEvent = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun deleteAccessCodes(userWalletsIds: List<UserWalletId>): CompletionResult<Unit> {
|
||||
val cardsIds = userWalletsListManager.userWallets.firstOrNull()
|
||||
?.asSequence()
|
||||
?.filter { it.walletId in userWalletsIds }
|
||||
?.flatMap { it.cardsInWallet }
|
||||
?.toSet()
|
||||
|
||||
return if (cardsIds.isNullOrEmpty()) {
|
||||
CompletionResult.Success(Unit)
|
||||
} else {
|
||||
tangemSdkManager.deleteSavedUserCodes(cardsIds.toSet())
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun List<UserWalletModel>.calculateBalanceAndUpdateWalletStores(
|
||||
walletStores: Map<UserWalletId, List<WalletStoreModel>>,
|
||||
): List<UserWalletModel> {
|
||||
return this
|
||||
.associateWith { walletStores[it.id] }
|
||||
.map { (wallet, walletStores) ->
|
||||
wallet.calculateBalanceAndUpdateWalletStores(walletStores)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun UserWalletModel.calculateBalanceAndUpdateWalletStores(
|
||||
walletStores: List<WalletStoreModel>?,
|
||||
): UserWalletModel {
|
||||
return this.copy(
|
||||
type = when (type) {
|
||||
is UserWalletModel.Type.MultiCurrency -> type.copy(
|
||||
tokensCount = walletStores?.flatMap { it.walletsData }?.size ?: 0,
|
||||
)
|
||||
is UserWalletModel.Type.SingleCurrency -> type
|
||||
},
|
||||
fiatBalance = totalFiatBalanceCalculator.calculate(
|
||||
walletStores = walletStores.orEmpty(),
|
||||
initial = TotalFiatBalance.Loading,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.redux
|
||||
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.model.TotalFiatBalance
|
||||
import org.rekotlin.Action
|
||||
|
||||
internal object WalletSelectorReducer {
|
||||
fun reduce(action: Action, state: AppState): WalletSelectorState {
|
||||
return if (action is WalletSelectorAction) {
|
||||
internalReduce(action, state.walletSelectorState)
|
||||
} else {
|
||||
state.walletSelectorState
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("ComplexMethod")
|
||||
private fun internalReduce(action: WalletSelectorAction, state: WalletSelectorState): WalletSelectorState {
|
||||
return when (action) {
|
||||
is WalletSelectorAction.UserWalletsLoaded -> state.copy(
|
||||
wallets = action.userWallets.updateWalletsModels(state.wallets),
|
||||
)
|
||||
is WalletSelectorAction.SelectedWalletChanged -> state.copy(
|
||||
selectedWalletId = action.selectedWallet.walletId,
|
||||
)
|
||||
is WalletSelectorAction.IsLockedChanged -> state.copy(
|
||||
isLocked = action.isLocked,
|
||||
)
|
||||
is WalletSelectorAction.BalancesLoaded -> state.copy(
|
||||
wallets = action.userWalletModels,
|
||||
)
|
||||
is WalletSelectorAction.CloseError -> state.copy(error = null)
|
||||
is WalletSelectorAction.UnlockWithBiometry -> state.copy(
|
||||
isUnlockInProgress = true,
|
||||
)
|
||||
is WalletSelectorAction.UnlockWithBiometry.Error -> state.copy(
|
||||
isUnlockInProgress = false,
|
||||
error = action.error,
|
||||
)
|
||||
is WalletSelectorAction.UnlockWithBiometry.Success -> state.copy(
|
||||
isUnlockInProgress = false,
|
||||
)
|
||||
is WalletSelectorAction.AddWallet -> state.copy(
|
||||
isCardSavingInProgress = true,
|
||||
)
|
||||
is WalletSelectorAction.AddWallet.Error -> state.copy(
|
||||
isCardSavingInProgress = false,
|
||||
error = action.error,
|
||||
)
|
||||
is WalletSelectorAction.AddWallet.Success -> state.copy(
|
||||
isCardSavingInProgress = false,
|
||||
)
|
||||
is WalletSelectorAction.ChangeAppCurrency -> state.copy(
|
||||
fiatCurrency = action.fiatCurrency,
|
||||
)
|
||||
is WalletSelectorAction.WalletStoresChanged,
|
||||
is WalletSelectorAction.SelectWallet,
|
||||
is WalletSelectorAction.RemoveWallets,
|
||||
is WalletSelectorAction.RenameWallet,
|
||||
is WalletSelectorAction.ClearUserWallets,
|
||||
-> state
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<UserWallet>.updateWalletsModels(prevWallets: List<UserWalletModel>): List<UserWalletModel> {
|
||||
return this.map { userWallet ->
|
||||
prevWallets
|
||||
.find { it.id == userWallet.walletId }
|
||||
?.let {
|
||||
it.copy(
|
||||
name = userWallet.name,
|
||||
artworkUrl = userWallet.artworkUrl,
|
||||
isLocked = userWallet.isLocked,
|
||||
type = userWallet.getType(prevType = it.type),
|
||||
)
|
||||
}
|
||||
?: with(userWallet) {
|
||||
UserWalletModel(
|
||||
id = walletId,
|
||||
name = name,
|
||||
artworkUrl = artworkUrl,
|
||||
type = getType(),
|
||||
fiatBalance = TotalFiatBalance.Loading,
|
||||
isLocked = isLocked,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun UserWallet.getType(prevType: UserWalletModel.Type? = null): UserWalletModel.Type {
|
||||
return if (isMultiCurrency) {
|
||||
UserWalletModel.Type.MultiCurrency(
|
||||
cardsInWallet = (scanResponse.card.backupStatus as? CardDTO.BackupStatus.Active)
|
||||
?.cardCount?.inc()
|
||||
?: 1,
|
||||
tokensCount = (prevType as? UserWalletModel.Type.MultiCurrency)?.tokensCount ?: 0,
|
||||
)
|
||||
} else {
|
||||
UserWalletModel.Type.SingleCurrency(
|
||||
blockchainName = scanResponse.cardTypesResolver.getBlockchain().fullName,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.redux
|
||||
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import org.rekotlin.StateType
|
||||
|
||||
data class WalletSelectorState(
|
||||
val wallets: List<UserWalletModel> = emptyList(),
|
||||
val selectedWalletId: UserWalletId? = null,
|
||||
val isLocked: Boolean = false,
|
||||
val fiatCurrency: FiatCurrency = FiatCurrency.Default,
|
||||
val isCardSavingInProgress: Boolean = false,
|
||||
val isUnlockInProgress: Boolean = false,
|
||||
val error: TangemError? = null,
|
||||
) : StateType
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui
|
||||
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.extensions.toFormattedFiatValue
|
||||
import com.tangem.tap.domain.model.TotalFiatBalance
|
||||
import com.tangem.tap.features.walletSelector.redux.UserWalletModel
|
||||
import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
|
||||
|
||||
internal fun List<UserWalletModel>.toUiModels(appCurrency: FiatCurrency): Sequence<UserWalletItem> {
|
||||
return this.asSequence().map { userWalletModel ->
|
||||
with(userWalletModel) {
|
||||
val balance = when (fiatBalance) {
|
||||
is TotalFiatBalance.Failed -> UserWalletItem.Balance.Failed
|
||||
is TotalFiatBalance.Loading -> UserWalletItem.Balance.Loading
|
||||
is TotalFiatBalance.Loaded -> UserWalletItem.Balance.Loaded(
|
||||
amount = fiatBalance.amount.toFormattedFiatValue(appCurrency.symbol, appCurrency.code),
|
||||
showWarning = fiatBalance.isWarning,
|
||||
)
|
||||
}
|
||||
when (type) {
|
||||
is UserWalletModel.Type.MultiCurrency -> MultiCurrencyUserWalletItem(
|
||||
id = id,
|
||||
name = name,
|
||||
imageUrl = artworkUrl,
|
||||
balance = balance,
|
||||
isLocked = isLocked,
|
||||
cardsInWallet = type.cardsInWallet,
|
||||
tokensCount = type.tokensCount,
|
||||
)
|
||||
is UserWalletModel.Type.SingleCurrency -> SingleCurrencyUserWalletItem(
|
||||
id = id,
|
||||
name = name,
|
||||
imageUrl = artworkUrl,
|
||||
balance = balance,
|
||||
isLocked = isLocked,
|
||||
tokenName = type.blockchainName,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.SnackbarHost
|
||||
import androidx.compose.material.SnackbarHostState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.ui.components.wallets.RenameWalletDialogContent
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.screen.ComposeBottomSheetFragment
|
||||
import com.tangem.core.ui.theme.AppThemeModeHolder
|
||||
import com.tangem.tap.common.analytics.events.MyWallets
|
||||
import com.tangem.tap.features.details.ui.cardsettings.resolveReference
|
||||
import com.tangem.tap.features.walletSelector.ui.components.*
|
||||
import com.tangem.tap.features.walletSelector.ui.model.DialogModel
|
||||
import com.tangem.tap.features.walletSelector.ui.model.WarningModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
internal class WalletSelectorBottomSheetFragment : ComposeBottomSheetFragment() {
|
||||
|
||||
@Inject
|
||||
override lateinit var appThemeModeHolder: AppThemeModeHolder
|
||||
|
||||
private val viewModel by viewModels<WalletSelectorViewModel>()
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
Analytics.send(MyWallets.MyWalletsScreenOpened())
|
||||
|
||||
return super.onCreateDialog(savedInstanceState)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val errorMessage by rememberUpdatedState(newValue = state.error?.resolveReference())
|
||||
val dialog by rememberUpdatedState(newValue = state.dialog)
|
||||
|
||||
Box(modifier = modifier.nestedScroll(rememberNestedScrollInteropConnection())) {
|
||||
WalletSelectorScreenContent(
|
||||
state = state,
|
||||
onWalletClick = viewModel::walletClicked,
|
||||
onWalletLongClick = viewModel::walletLongClicked,
|
||||
onUnlockClick = viewModel::unlock,
|
||||
onAddCardClick = viewModel::addWallet,
|
||||
onClearSelectedClick = viewModel::cancelWalletsEditing,
|
||||
onEditSelectedWalletClick = viewModel::renameWallet,
|
||||
onDeleteSelectedWalletsClick = viewModel::deleteWallets,
|
||||
)
|
||||
|
||||
SnackbarHost(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(vertical = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
hostState = snackbarHostState,
|
||||
)
|
||||
}
|
||||
|
||||
Dialog(dialog = dialog)
|
||||
|
||||
LaunchedEffect(key1 = errorMessage) {
|
||||
errorMessage?.let {
|
||||
snackbarHostState.showSnackbar(it)
|
||||
viewModel.closeError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("TopLevelComposableFunctions")
|
||||
@Composable
|
||||
private fun Dialog(dialog: DialogModel?) {
|
||||
if (dialog == null) return
|
||||
when (dialog) {
|
||||
is DialogModel.RemoveWalletDialog -> RemoveWalletDialogContent(dialog)
|
||||
is DialogModel.RenameWalletDialog -> {
|
||||
RenameWalletDialogContent(
|
||||
name = dialog.currentName,
|
||||
onConfirm = dialog.onConfirm,
|
||||
onDismiss = dialog.onDismiss,
|
||||
)
|
||||
}
|
||||
is WarningModel.BiometricsLockoutWarning -> BiometricsLockoutWarningContent(dialog)
|
||||
is WarningModel.KeyInvalidatedWarning -> KeyInvalidatedWarningContent(dialog)
|
||||
is WarningModel.BiometricsDisabledWarning -> BiometricsDisabledWarningContent(dialog)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
import com.tangem.tap.features.walletSelector.ui.model.DialogModel
|
||||
import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem
|
||||
|
||||
@Immutable
|
||||
internal data class WalletSelectorScreenState(
|
||||
val multiCurrencyWallets: List<MultiCurrencyUserWalletItem> = emptyList(),
|
||||
val singleCurrencyWallets: List<SingleCurrencyUserWalletItem> = emptyList(),
|
||||
val selectedUserWalletId: UserWalletId? = null,
|
||||
val isLocked: Boolean = false,
|
||||
val editingUserWalletsIds: List<UserWalletId> = listOf(),
|
||||
val dialog: DialogModel? = null,
|
||||
val showAddCardProgress: Boolean = false,
|
||||
val showUnlockProgress: Boolean = false,
|
||||
val error: TextReference? = null,
|
||||
)
|
||||
|
|
@ -1,278 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListError
|
||||
import com.tangem.domain.wallets.legacy.isLocked
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.common.analytics.events.MyWallets
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorState
|
||||
import com.tangem.tap.features.walletSelector.ui.model.DialogModel
|
||||
import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.WarningModel
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.tap.walletStoresManager
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
internal class WalletSelectorViewModel : ViewModel(), StoreSubscriber<WalletSelectorState> {
|
||||
private val stateInternal = MutableStateFlow(WalletSelectorScreenState())
|
||||
val state: StateFlow<WalletSelectorScreenState> = stateInternal
|
||||
|
||||
init {
|
||||
subscribeToStoreChanges()
|
||||
bootstrapWalletListChanges()
|
||||
bootstrapWalletsStoresChanges()
|
||||
bootstrapAppFiatCurrency()
|
||||
}
|
||||
|
||||
fun unlock() {
|
||||
store.dispatch(WalletSelectorAction.UnlockWithBiometry)
|
||||
}
|
||||
|
||||
fun addWallet() {
|
||||
store.dispatch(WalletSelectorAction.AddWallet)
|
||||
}
|
||||
|
||||
fun walletClicked(userWalletId: UserWalletId) = with(state.value) {
|
||||
when {
|
||||
editingUserWalletsIds.isNotEmpty() && !editingUserWalletsIds.contains(userWalletId) -> {
|
||||
editWallet(userWalletId)
|
||||
}
|
||||
editingUserWalletsIds.isNotEmpty() && editingUserWalletsIds.contains(userWalletId) -> {
|
||||
cancelWalletEditing(userWalletId)
|
||||
}
|
||||
selectedUserWalletId != userWalletId -> {
|
||||
store.dispatch(
|
||||
WalletSelectorAction.SelectWallet(
|
||||
userWalletId = userWalletId,
|
||||
sendAnalyticsEvent = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun walletLongClicked(userWalletId: UserWalletId) = with(state.value) {
|
||||
if (editingUserWalletsIds.isEmpty()) {
|
||||
editWallet(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelWalletsEditing() {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
editingUserWalletsIds = emptyList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun renameWallet() = with(state.value) {
|
||||
if (editingUserWalletsIds.isNotEmpty() && dialog == null) {
|
||||
val editedUserWalletId = editingUserWalletsIds.first()
|
||||
val editedUserWallet = (multiCurrencyWallets + singleCurrencyWallets)
|
||||
.find { it.id == editedUserWalletId }
|
||||
|
||||
if (editedUserWallet != null) {
|
||||
Analytics.send(MyWallets.Button.EditWalletTapped())
|
||||
val dialog = DialogModel.RenameWalletDialog(
|
||||
currentName = editedUserWallet.name,
|
||||
onConfirm = { newName ->
|
||||
store.dispatch(WalletSelectorAction.RenameWallet(editedUserWalletId, newName))
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
dialog = null,
|
||||
editingUserWalletsIds = emptyList(),
|
||||
)
|
||||
}
|
||||
},
|
||||
onDismiss = {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
dialog = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
dialog = dialog,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteWallets() = with(state.value) {
|
||||
if (editingUserWalletsIds.isNotEmpty()) {
|
||||
val dialog = DialogModel.RemoveWalletDialog(
|
||||
onConfirm = {
|
||||
store.dispatch(WalletSelectorAction.RemoveWallets(editingUserWalletsIds))
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
dialog = null,
|
||||
editingUserWalletsIds = emptyList(),
|
||||
)
|
||||
}
|
||||
},
|
||||
onDismiss = {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
dialog = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
dialog = dialog,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun closeError() {
|
||||
store.dispatch(WalletSelectorAction.CloseError)
|
||||
}
|
||||
|
||||
// TODO: Refactor errors handling
|
||||
override fun newState(state: WalletSelectorState) {
|
||||
stateInternal.update { prevState ->
|
||||
val walletsUi = state.wallets.toUiModels(state.fiatCurrency)
|
||||
val walletsIds = walletsUi.map { it.id }
|
||||
val multiCurrencyWallets = arrayListOf<MultiCurrencyUserWalletItem>()
|
||||
val singleCurrencyWallets = arrayListOf<SingleCurrencyUserWalletItem>()
|
||||
walletsUi.forEach { wallet ->
|
||||
when (wallet) {
|
||||
is MultiCurrencyUserWalletItem -> {
|
||||
multiCurrencyWallets.add(wallet)
|
||||
}
|
||||
is SingleCurrencyUserWalletItem -> {
|
||||
singleCurrencyWallets.add(wallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
val warningDialog = createWarningDialogIfNeeded(state.error, prevState.dialog)
|
||||
|
||||
prevState.copy(
|
||||
multiCurrencyWallets = multiCurrencyWallets,
|
||||
singleCurrencyWallets = singleCurrencyWallets,
|
||||
selectedUserWalletId = state.selectedWalletId,
|
||||
editingUserWalletsIds = prevState.editingUserWalletsIds.filter { it in walletsIds },
|
||||
isLocked = state.isLocked,
|
||||
showUnlockProgress = state.isUnlockInProgress,
|
||||
showAddCardProgress = state.isCardSavingInProgress,
|
||||
dialog = warningDialog,
|
||||
error = state.error
|
||||
?.takeIf { !it.silent && warningDialog == null }
|
||||
?.let { error ->
|
||||
error.messageResId?.let { TextReference.Res(it) }
|
||||
?: TextReference.Str(error.customMessage)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
private fun createWarningDialogIfNeeded(error: TangemError?, currentDialog: DialogModel?): DialogModel? {
|
||||
return when (error) {
|
||||
is UserWalletsListError.BiometricsAuthenticationLockout -> WarningModel.BiometricsLockoutWarning(
|
||||
isPermanent = error.isPermanent,
|
||||
onDismiss = this::dismissWarningDialog,
|
||||
)
|
||||
is UserWalletsListError.AllKeysInvalidated,
|
||||
is UserWalletsListError.NoUserWalletSelected,
|
||||
-> WarningModel.KeyInvalidatedWarning(
|
||||
onDismiss = this::dismissWarningDialog,
|
||||
)
|
||||
is UserWalletsListError.BiometricsAuthenticationDisabled -> WarningModel.BiometricsDisabledWarning(
|
||||
onDismiss = this::clearUserWallets,
|
||||
)
|
||||
else -> currentDialog
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearUserWallets() {
|
||||
store.dispatch(WalletSelectorAction.ClearUserWallets)
|
||||
}
|
||||
|
||||
private fun dismissWarningDialog() {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
dialog = null,
|
||||
)
|
||||
}
|
||||
closeError()
|
||||
}
|
||||
|
||||
private fun editWallet(userWalletId: UserWalletId) {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
editingUserWalletsIds = prevState.editingUserWalletsIds + userWalletId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelWalletEditing(userWalletId: UserWalletId) {
|
||||
stateInternal.update { prevState ->
|
||||
prevState.copy(
|
||||
editingUserWalletsIds = prevState.editingUserWalletsIds - userWalletId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeToStoreChanges() {
|
||||
store.subscribe(this) { appState ->
|
||||
appState.skip { old, new -> old.walletSelectorState == new.walletSelectorState }
|
||||
.select { it.walletSelectorState }
|
||||
}
|
||||
}
|
||||
|
||||
private fun bootstrapWalletListChanges() {
|
||||
userWalletsListManager.userWallets
|
||||
.onEach {
|
||||
store.dispatchOnMain(WalletSelectorAction.UserWalletsLoaded(userWallets = it))
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
userWalletsListManager.selectedUserWallet
|
||||
.onEach {
|
||||
store.dispatchOnMain(WalletSelectorAction.SelectedWalletChanged(selectedWallet = it))
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
userWalletsListManager.isLocked
|
||||
.onEach {
|
||||
store.dispatchOnMain(WalletSelectorAction.IsLockedChanged(isLocked = it))
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun bootstrapWalletsStoresChanges() {
|
||||
walletStoresManager.getAll()
|
||||
.onEach { walletStores ->
|
||||
store.dispatchOnMain(WalletSelectorAction.WalletStoresChanged(walletStores))
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun bootstrapAppFiatCurrency() {
|
||||
store.dispatch(
|
||||
WalletSelectorAction.ChangeAppCurrency(
|
||||
fiatCurrency = store.state.globalState.appCurrency,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButton
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.walletSelector.ui.model.WarningModel
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun BiometricsDisabledWarningContent(warning: WarningModel.BiometricsDisabledWarning) {
|
||||
BasicDialog(
|
||||
title = stringResource(id = R.string.common_warning),
|
||||
message = stringResource(id = R.string.biometric_unavailable_warning),
|
||||
onDismissDialog = warning.onDismiss,
|
||||
isDismissable = false,
|
||||
confirmButton = DialogButton(
|
||||
title = stringResource(id = R.string.common_ok),
|
||||
onClick = warning.onDismiss,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun BiometricsDisabledWarningContentSample(modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
BiometricsDisabledWarningContent(
|
||||
warning = WarningModel.BiometricsDisabledWarning(
|
||||
onDismiss = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsDisabledWarningContentPreview_Light() {
|
||||
TangemTheme {
|
||||
BiometricsDisabledWarningContentSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsDisabledWarningContentPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
BiometricsDisabledWarningContentSample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButton
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.walletSelector.ui.model.WarningModel
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun BiometricsLockoutWarningContent(warning: WarningModel.BiometricsLockoutWarning) {
|
||||
BasicDialog(
|
||||
title = stringResource(id = R.string.biometric_lockout_warning_title),
|
||||
message = stringResource(
|
||||
id = if (warning.isPermanent) {
|
||||
R.string.biometric_lockout_permanent_warning_description
|
||||
} else {
|
||||
R.string.biometric_lockout_warning_description
|
||||
},
|
||||
),
|
||||
onDismissDialog = warning.onDismiss,
|
||||
confirmButton = DialogButton(
|
||||
title = stringResource(id = R.string.common_ok),
|
||||
onClick = warning.onDismiss,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialogSample(modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
BiometricsLockoutWarningContent(
|
||||
warning = WarningModel.BiometricsLockoutWarning(
|
||||
isPermanent = false,
|
||||
onDismiss = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialogPreview_Light() {
|
||||
TangemTheme {
|
||||
BiometricsLockoutDialogSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialogPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
BiometricsLockoutDialogSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialog_Permanent_Sample(modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
BiometricsLockoutWarningContent(
|
||||
warning = WarningModel.BiometricsLockoutWarning(
|
||||
isPermanent = true,
|
||||
onDismiss = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialog_Permanent_Preview_Light() {
|
||||
TangemTheme {
|
||||
BiometricsLockoutDialog_Permanent_Sample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun BiometricsLockoutDialog_Permanent_Preview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
BiometricsLockoutDialog_Permanent_Sample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButton
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.features.walletSelector.ui.model.WarningModel
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun KeyInvalidatedWarningContent(warning: WarningModel.KeyInvalidatedWarning) {
|
||||
BasicDialog(
|
||||
title = stringResource(id = R.string.common_attention),
|
||||
message = stringResource(id = R.string.key_invalidated_warning_description),
|
||||
onDismissDialog = warning.onDismiss,
|
||||
confirmButton = DialogButton(
|
||||
title = stringResource(id = R.string.common_ok),
|
||||
onClick = warning.onDismiss,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun KeyInvalidatedWarningSample(modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
KeyInvalidatedWarningContent(
|
||||
warning = WarningModel.KeyInvalidatedWarning(onDismiss = {}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun KeyInvalidatedWarningPreview_Light() {
|
||||
TangemTheme {
|
||||
KeyInvalidatedWarningSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun KeyInvalidatedWarningPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
KeyInvalidatedWarningSample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.features.walletSelector.ui.WalletSelectorScreenState
|
||||
import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
|
||||
|
||||
internal object MockData {
|
||||
private val multiCurrencyUserWallet = MultiCurrencyUserWalletItem(
|
||||
id = UserWalletId("wallet_1"),
|
||||
balance = UserWalletItem.Balance.Loaded(
|
||||
amount = "6781.05 $",
|
||||
showWarning = true,
|
||||
),
|
||||
name = "Wallet",
|
||||
imageUrl = "https://app.tangem.com/cards/card_default.png",
|
||||
isLocked = false,
|
||||
tokensCount = 12,
|
||||
cardsInWallet = 3,
|
||||
)
|
||||
|
||||
private val singleCurrencyUserWallet = SingleCurrencyUserWalletItem(
|
||||
id = UserWalletId("wallet_4"),
|
||||
balance = UserWalletItem.Balance.Loaded(
|
||||
amount = "6781.05 $",
|
||||
showWarning = false,
|
||||
),
|
||||
name = "Wallet",
|
||||
imageUrl = "https://app.tangem.com/cards/card_default.png",
|
||||
isLocked = false,
|
||||
tokenName = "Ethereum",
|
||||
)
|
||||
|
||||
val state = WalletSelectorScreenState(
|
||||
multiCurrencyWallets = listOf(
|
||||
multiCurrencyUserWallet,
|
||||
multiCurrencyUserWallet.copy(id = UserWalletId("wallet_2")),
|
||||
multiCurrencyUserWallet.copy(id = UserWalletId("wallet_3"), tokensCount = 2, cardsInWallet = 1),
|
||||
),
|
||||
singleCurrencyWallets = listOf(singleCurrencyUserWallet),
|
||||
selectedUserWalletId = multiCurrencyUserWallet.id,
|
||||
isLocked = false,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButton
|
||||
import com.tangem.tap.features.walletSelector.ui.model.DialogModel
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
internal fun RemoveWalletDialogContent(dialog: DialogModel.RemoveWalletDialog) {
|
||||
BasicDialog(
|
||||
message = stringResource(id = R.string.user_wallet_list_delete_prompt),
|
||||
confirmButton = DialogButton(
|
||||
title = stringResource(id = R.string.common_delete),
|
||||
warning = true,
|
||||
onClick = dialog.onConfirm,
|
||||
),
|
||||
dismissButton = DialogButton(
|
||||
onClick = dialog.onDismiss,
|
||||
),
|
||||
onDismissDialog = dialog.onDismiss,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,330 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SecondaryButtonIconEnd
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.atoms.Hand
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.features.walletSelector.ui.WalletSelectorScreenState
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
internal fun WalletSelectorScreenContent(
|
||||
state: WalletSelectorScreenState,
|
||||
onWalletClick: (UserWalletId) -> Unit,
|
||||
onWalletLongClick: (UserWalletId) -> Unit,
|
||||
onUnlockClick: () -> Unit,
|
||||
onAddCardClick: () -> Unit,
|
||||
onClearSelectedClick: () -> Unit,
|
||||
onEditSelectedWalletClick: () -> Unit,
|
||||
onDeleteSelectedWalletsClick: () -> Unit,
|
||||
) {
|
||||
LazyColumn {
|
||||
stickyHeader {
|
||||
Header(
|
||||
editingWalletsIds = state.editingUserWalletsIds,
|
||||
onClearSelectedClick = onClearSelectedClick,
|
||||
onEditSelectedWalletClick = onEditSelectedWalletClick,
|
||||
onDeleteSelectedWalletsClick = onDeleteSelectedWalletsClick,
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
WalletsTitle(textResId = R.string.user_wallet_list_multi_header, wallets = state.multiCurrencyWallets)
|
||||
}
|
||||
|
||||
itemsIndexed(
|
||||
items = state.multiCurrencyWallets,
|
||||
key = { _, wallet -> wallet.id.stringValue },
|
||||
) { _, wallet ->
|
||||
|
||||
WalletItem(
|
||||
wallet = wallet,
|
||||
isSelected = remember(state.selectedUserWalletId) { wallet.id == state.selectedUserWalletId },
|
||||
isChecked = remember(state.editingUserWalletsIds) { wallet.id in state.editingUserWalletsIds },
|
||||
onWalletClick = { onWalletClick(wallet.id) },
|
||||
onWalletLongClick = { onWalletLongClick(wallet.id) },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
WalletsTitle(textResId = R.string.user_wallet_list_single_header, wallets = state.singleCurrencyWallets)
|
||||
}
|
||||
|
||||
itemsIndexed(
|
||||
items = state.singleCurrencyWallets,
|
||||
key = { _, wallet -> wallet.id.stringValue },
|
||||
) { _, wallet ->
|
||||
WalletItem(
|
||||
wallet = wallet,
|
||||
isSelected = remember(state.selectedUserWalletId) { wallet.id == state.selectedUserWalletId },
|
||||
isChecked = remember(state.editingUserWalletsIds) { wallet.id in state.editingUserWalletsIds },
|
||||
onWalletClick = { onWalletClick(wallet.id) },
|
||||
onWalletLongClick = { onWalletLongClick(wallet.id) },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Footer(
|
||||
isLocked = state.isLocked,
|
||||
showUnlockProgress = state.showUnlockProgress,
|
||||
showAddCardProgress = state.showAddCardProgress,
|
||||
onUnlockClick = onUnlockClick,
|
||||
onAddCardClick = onAddCardClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(
|
||||
editingWalletsIds: List<UserWalletId>,
|
||||
onClearSelectedClick: () -> Unit,
|
||||
onEditSelectedWalletClick: () -> Unit,
|
||||
onDeleteSelectedWalletsClick: () -> Unit,
|
||||
) {
|
||||
val editingWalletsSize by rememberUpdatedState(newValue = editingWalletsIds.size)
|
||||
val hasEditingWallets by remember { derivedStateOf { editingWalletsSize > 0 } }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color = TangemTheme.colors.background.plain,
|
||||
shape = TangemTheme.shapes.bottomSheet,
|
||||
),
|
||||
) {
|
||||
Hand()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size44),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
if (hasEditingWallets) {
|
||||
EditWalletsBar(
|
||||
editingWalletsSize = editingWalletsSize,
|
||||
onClearSelectedClick = onClearSelectedClick,
|
||||
onEditSelectedWalletClick = onEditSelectedWalletClick,
|
||||
onDeleteSelectedWalletsClick = onDeleteSelectedWalletsClick,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = stringResource(R.string.user_wallet_list_title),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletsTitle(@StringRes textResId: Int, wallets: List<*>) {
|
||||
if (wallets.isNotEmpty()) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing16),
|
||||
text = stringResource(id = textResId),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Footer(
|
||||
isLocked: Boolean,
|
||||
showUnlockProgress: Boolean,
|
||||
showAddCardProgress: Boolean,
|
||||
onUnlockClick: () -> Unit,
|
||||
onAddCardClick: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing24,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
if (isLocked) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(
|
||||
id = R.string.user_wallet_list_unlock_all_with,
|
||||
stringResource(id = R.string.common_biometrics),
|
||||
),
|
||||
showProgress = showUnlockProgress,
|
||||
onClick = onUnlockClick,
|
||||
)
|
||||
}
|
||||
SecondaryButtonIconEnd(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.user_wallet_list_add_button),
|
||||
showProgress = showAddCardProgress,
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
onClick = onAddCardClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EditWalletsBar(
|
||||
editingWalletsSize: Int,
|
||||
onClearSelectedClick: () -> Unit,
|
||||
onEditSelectedWalletClick: () -> Unit,
|
||||
onDeleteSelectedWalletsClick: () -> Unit,
|
||||
) {
|
||||
val showEditAction by remember(editingWalletsSize) {
|
||||
derivedStateOf { editingWalletsSize in 1 until 2 }
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
IconButton(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = onClearSelectedClick,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = R.drawable.ic_close_24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = "Unselect wallets",
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(id = R.string.user_wallet_list_editing_count, editingWalletsSize),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerWMax()
|
||||
if (showEditAction) {
|
||||
IconButton(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = onEditSelectedWalletClick,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = R.drawable.ic_pencil_outline_24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = "Change wallet name",
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = onDeleteSelectedWalletsClick,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = R.drawable.ic_trash),
|
||||
tint = TangemTheme.colors.icon.warning,
|
||||
contentDescription = "Delete selected wallets",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContentSample(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(color = TangemTheme.colors.background.primary),
|
||||
) {
|
||||
WalletSelectorScreenContent(
|
||||
state = MockData.state.copy(isLocked = true),
|
||||
onWalletClick = { /* no-op */ },
|
||||
onWalletLongClick = { /* no-op */ },
|
||||
onUnlockClick = { /* no-op */ },
|
||||
onAddCardClick = { /* no-op */ },
|
||||
onEditSelectedWalletClick = { /* no-op */ },
|
||||
onClearSelectedClick = { /* no-op */ },
|
||||
onDeleteSelectedWalletsClick = { /* no-op */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContentPreview_Light() {
|
||||
TangemTheme {
|
||||
WalletSelectorScreenContentSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContentPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
WalletSelectorScreenContentSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContent_EditWallets_Sample(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
WalletSelectorScreenContent(
|
||||
state = MockData.state
|
||||
.copy(editingUserWalletsIds = listOf(MockData.state.multiCurrencyWallets[2].id)),
|
||||
onWalletClick = { /* no-op */ },
|
||||
onWalletLongClick = { /* no-op */ },
|
||||
onUnlockClick = { /* no-op */ },
|
||||
onAddCardClick = { /* no-op */ },
|
||||
onEditSelectedWalletClick = { /* no-op */ },
|
||||
onClearSelectedClick = { /* no-op */ },
|
||||
onDeleteSelectedWalletsClick = { /* no-op */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContent_EditWallets_Preview_Light() {
|
||||
TangemTheme {
|
||||
WalletSelectorScreenContent_EditWallets_Sample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun WalletSelectorScreenContent_EditWallets_Preview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
WalletSelectorScreenContent_EditWallets_Sample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -1,459 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.components
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.BlendMode
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.components.SpacerH2
|
||||
import com.tangem.core.ui.components.SpacerW6
|
||||
import com.tangem.core.ui.components.SpacerW8
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.common.extensions.cardImageData
|
||||
import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
|
||||
import com.tangem.wallet.R
|
||||
import com.valentinilk.shimmer.shimmer
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
internal fun WalletItem(
|
||||
wallet: UserWalletItem,
|
||||
isSelected: Boolean,
|
||||
isChecked: Boolean,
|
||||
onWalletClick: () -> Unit,
|
||||
onWalletLongClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.combinedClickable(
|
||||
onClick = onWalletClick,
|
||||
onLongClick = onWalletLongClick,
|
||||
)
|
||||
.heightIn(min = TangemTheme.dimens.size72)
|
||||
.padding(all = TangemTheme.dimens.spacing16),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
WalletCardImage(
|
||||
cardImageUrl = wallet.imageUrl,
|
||||
isChecked = isChecked,
|
||||
isSelected = isSelected,
|
||||
)
|
||||
SpacerW8()
|
||||
WalletInfo(wallet = wallet, isSelected = isSelected)
|
||||
SpacerW6()
|
||||
TokensInfo(
|
||||
isLocked = wallet.isLocked,
|
||||
balance = wallet.balance,
|
||||
tokensCount = (wallet as? MultiCurrencyUserWalletItem)?.tokensCount,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletCardImage(cardImageUrl: String, isChecked: Boolean, isSelected: Boolean) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(TangemTheme.dimens.size62)
|
||||
.height(TangemTheme.dimens.size42),
|
||||
) {
|
||||
val cardImageModifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.width(TangemTheme.dimens.size56)
|
||||
.height(TangemTheme.dimens.size32)
|
||||
.clip(TangemTheme.shapes.roundedCornersSmall2)
|
||||
|
||||
val tintColor = TangemTheme.colors.icon.accent.copy(alpha = .6f)
|
||||
val checkedColorFilter = remember(isChecked) {
|
||||
if (isChecked) {
|
||||
ColorFilter.tint(
|
||||
color = tintColor,
|
||||
blendMode = BlendMode.SrcOver,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
SubcomposeAsyncImage(
|
||||
modifier = cardImageModifier,
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.cardImageData(cardImageUrl)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
loading = { CardImageShimmer(modifier = cardImageModifier) },
|
||||
error = { CardImagePlaceholder(modifier = cardImageModifier) },
|
||||
colorFilter = checkedColorFilter,
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
when {
|
||||
isChecked -> {
|
||||
CheckedWalletMark(
|
||||
modifier = Modifier.matchParentSize(),
|
||||
)
|
||||
}
|
||||
isSelected -> {
|
||||
SelectedWalletBadge(
|
||||
modifier = Modifier.align(Alignment.TopEnd),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.WalletInfo(wallet: UserWalletItem, isSelected: Boolean) {
|
||||
Column(
|
||||
modifier = Modifier.weight(weight = 2f),
|
||||
verticalArrangement = Arrangement.SpaceAround,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Text(
|
||||
text = wallet.name,
|
||||
color = if (isSelected) TangemTheme.colors.text.accent else TangemTheme.colors.text.primary1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
)
|
||||
SpacerH2()
|
||||
Text(
|
||||
text = when (wallet) {
|
||||
is MultiCurrencyUserWalletItem -> pluralStringResource(
|
||||
id = R.plurals.card_label_card_count,
|
||||
count = wallet.cardsInWallet,
|
||||
wallet.cardsInWallet,
|
||||
)
|
||||
is SingleCurrencyUserWalletItem -> wallet.tokenName
|
||||
},
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.TokensInfo(isLocked: Boolean, balance: UserWalletItem.Balance, tokensCount: Int?) {
|
||||
Column(
|
||||
modifier = Modifier.weight(weight = 3f),
|
||||
verticalArrangement = Arrangement.SpaceAround,
|
||||
horizontalAlignment = Alignment.End,
|
||||
) {
|
||||
if (isLocked) {
|
||||
LockedPlaceholder()
|
||||
} else {
|
||||
when (balance) {
|
||||
is UserWalletItem.Balance.Failed,
|
||||
is UserWalletItem.Balance.Loaded,
|
||||
-> {
|
||||
LoadedTokensInfo(
|
||||
balanceAmount = balance.amount,
|
||||
tokensCount = tokensCount,
|
||||
showWarning = balance.showWarning,
|
||||
)
|
||||
}
|
||||
is UserWalletItem.Balance.Loading -> {
|
||||
LoadingTokensInfo(isMultiCurrencyWallet = tokensCount != null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoadingTokensInfo(isMultiCurrencyWallet: Boolean) {
|
||||
Column(
|
||||
modifier = Modifier.shimmer(),
|
||||
horizontalAlignment = Alignment.End,
|
||||
verticalArrangement = Arrangement.SpaceAround,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(TangemTheme.dimens.size62)
|
||||
.height(TangemTheme.dimens.size16)
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = TangemTheme.shapes.roundedCornersSmall2,
|
||||
),
|
||||
)
|
||||
if (isMultiCurrencyWallet) {
|
||||
SpacerH2()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(TangemTheme.dimens.size48)
|
||||
.height(TangemTheme.dimens.size12)
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = TangemTheme.shapes.roundedCornersSmall2,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoadedTokensInfo(
|
||||
balanceAmount: String,
|
||||
tokensCount: Int?,
|
||||
showWarning: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.End,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
|
||||
) {
|
||||
Text(
|
||||
text = balanceAmount,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.End,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
)
|
||||
if (showWarning) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size16),
|
||||
painter = painterResource(id = R.drawable.ic_alert_24),
|
||||
tint = TangemTheme.colors.icon.attention,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (tokensCount != null) {
|
||||
SpacerH2()
|
||||
Text(
|
||||
text = pluralStringResource(
|
||||
id = R.plurals.token_count,
|
||||
count = tokensCount,
|
||||
tokensCount,
|
||||
),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LockedPlaceholder(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing2)
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = TangemTheme.shapes.roundedCornersMedium,
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing8,
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
)
|
||||
.size(TangemTheme.dimens.size20),
|
||||
painter = painterResource(id = R.drawable.ic_locked_24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CardImagePlaceholder(modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier) {
|
||||
Image(
|
||||
modifier = Modifier.matchParentSize(),
|
||||
painter = painterResource(id = R.drawable.card_placeholder_black),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CardImageShimmer(modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier.shimmer()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.background(TangemTheme.colors.button.secondary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SelectedWalletBadge(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(TangemTheme.dimens.size18)
|
||||
.background(
|
||||
color = Color.White,
|
||||
shape = CircleShape,
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(all = 0.5.dp),
|
||||
painter = painterResource(id = R.drawable.ic_check_circle_18),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CheckedWalletMark(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier,
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_check_24),
|
||||
tint = TangemTheme.colors.icon.primary2,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Suppress("LongMethod") // preview
|
||||
private fun WalletItemSample(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
WalletItem(
|
||||
wallet = MultiCurrencyUserWalletItem(
|
||||
id = UserWalletId(value = null),
|
||||
name = "Tangem Card",
|
||||
imageUrl = "",
|
||||
balance = UserWalletItem.Balance.Loaded(
|
||||
amount = "1412121218 BTC",
|
||||
showWarning = false,
|
||||
),
|
||||
isLocked = false,
|
||||
tokensCount = 2,
|
||||
cardsInWallet = 1,
|
||||
),
|
||||
isSelected = false,
|
||||
isChecked = false,
|
||||
onWalletClick = {},
|
||||
onWalletLongClick = {},
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = 4.dp))
|
||||
WalletItem(
|
||||
wallet = MultiCurrencyUserWalletItem(
|
||||
id = UserWalletId(value = null),
|
||||
name = "Tangem Card",
|
||||
imageUrl = "",
|
||||
balance = UserWalletItem.Balance.Loaded(
|
||||
amount = "1412121218 BTC",
|
||||
showWarning = true,
|
||||
),
|
||||
isLocked = false,
|
||||
tokensCount = 2,
|
||||
cardsInWallet = 1,
|
||||
),
|
||||
isSelected = false,
|
||||
isChecked = false,
|
||||
onWalletClick = {},
|
||||
onWalletLongClick = {},
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = 4.dp))
|
||||
WalletItem(
|
||||
wallet = MultiCurrencyUserWalletItem(
|
||||
id = UserWalletId(value = null),
|
||||
name = "Tangem Card",
|
||||
imageUrl = "",
|
||||
balance = UserWalletItem.Balance.Loading,
|
||||
isLocked = true,
|
||||
tokensCount = 2,
|
||||
cardsInWallet = 1,
|
||||
),
|
||||
isSelected = false,
|
||||
isChecked = false,
|
||||
onWalletClick = {},
|
||||
onWalletLongClick = {},
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = 4.dp))
|
||||
WalletItem(
|
||||
wallet = MultiCurrencyUserWalletItem(
|
||||
id = UserWalletId(value = null),
|
||||
name = "Tangem Card",
|
||||
imageUrl = "",
|
||||
balance = UserWalletItem.Balance.Failed,
|
||||
isLocked = false,
|
||||
tokensCount = 2,
|
||||
cardsInWallet = 1,
|
||||
),
|
||||
isSelected = true,
|
||||
isChecked = false,
|
||||
onWalletClick = {},
|
||||
onWalletLongClick = {},
|
||||
)
|
||||
Divider(modifier = Modifier.padding(vertical = 4.dp))
|
||||
WalletItem(
|
||||
wallet = SingleCurrencyUserWalletItem(
|
||||
id = UserWalletId(value = null),
|
||||
name = "Tangem Card",
|
||||
imageUrl = "",
|
||||
balance = UserWalletItem.Balance.Loaded(
|
||||
amount = "141212121888 BTC",
|
||||
showWarning = false,
|
||||
),
|
||||
isLocked = false,
|
||||
tokenName = "Bitcoin",
|
||||
),
|
||||
isSelected = false,
|
||||
isChecked = true,
|
||||
onWalletClick = {},
|
||||
onWalletLongClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun WalletItemPreview_Light() {
|
||||
TangemTheme {
|
||||
WalletItemSample()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun WalletItemPreview_Dark() {
|
||||
TangemTheme(isDark = true) {
|
||||
WalletItemSample()
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.model
|
||||
|
||||
internal sealed interface DialogModel {
|
||||
data class RenameWalletDialog(
|
||||
val currentName: String,
|
||||
val onConfirm: (newName: String) -> Unit,
|
||||
val onDismiss: () -> Unit,
|
||||
) : DialogModel
|
||||
|
||||
data class RemoveWalletDialog(
|
||||
val onConfirm: () -> Unit,
|
||||
val onDismiss: () -> Unit,
|
||||
) : DialogModel
|
||||
}
|
||||
|
||||
internal sealed interface WarningModel : DialogModel {
|
||||
data class BiometricsLockoutWarning(
|
||||
val isPermanent: Boolean,
|
||||
val onDismiss: () -> Unit,
|
||||
) : WarningModel
|
||||
|
||||
data class KeyInvalidatedWarning(
|
||||
val onDismiss: () -> Unit,
|
||||
) : WarningModel
|
||||
|
||||
data class BiometricsDisabledWarning(
|
||||
val onDismiss: () -> Unit,
|
||||
) : WarningModel
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package com.tangem.tap.features.walletSelector.ui.model
|
||||
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
internal sealed interface UserWalletItem {
|
||||
val id: UserWalletId
|
||||
val name: String
|
||||
val imageUrl: String
|
||||
val balance: Balance
|
||||
val isLocked: Boolean
|
||||
|
||||
sealed class Balance {
|
||||
open val amount: String = BigDecimalFormatter.EMPTY_BALANCE_SIGN
|
||||
open val showWarning: Boolean = false
|
||||
|
||||
object Loading : Balance()
|
||||
|
||||
object Failed : Balance() {
|
||||
override val showWarning: Boolean = true
|
||||
}
|
||||
|
||||
data class Loaded(
|
||||
override val amount: String,
|
||||
override val showWarning: Boolean,
|
||||
) : Balance()
|
||||
}
|
||||
}
|
||||
|
||||
internal data class MultiCurrencyUserWalletItem(
|
||||
override val id: UserWalletId,
|
||||
override val name: String,
|
||||
override val imageUrl: String,
|
||||
override val balance: UserWalletItem.Balance,
|
||||
override val isLocked: Boolean,
|
||||
val tokensCount: Int,
|
||||
val cardsInWallet: Int,
|
||||
) : UserWalletItem
|
||||
|
||||
internal data class SingleCurrencyUserWalletItem(
|
||||
override val id: UserWalletId,
|
||||
override val name: String,
|
||||
override val imageUrl: String,
|
||||
override val balance: UserWalletItem.Balance,
|
||||
override val isLocked: Boolean,
|
||||
val tokenName: String,
|
||||
) : UserWalletItem
|
||||
|
|
@ -34,7 +34,6 @@ enum class AppScreen(val isDialogFragment: Boolean = false) {
|
|||
Swap,
|
||||
Welcome,
|
||||
SaveWallet(isDialogFragment = true),
|
||||
WalletSelector(isDialogFragment = true),
|
||||
AppCurrencySelector,
|
||||
ModalNotification(isDialogFragment = true),
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue