Updated on 2026-08-14
This commit is contained in:
parent
941863f79b
commit
411a591509
18 changed files with 453 additions and 43 deletions
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.transaction.WalletAddressServiceRepository
|
||||
|
|
@ -9,8 +10,8 @@ import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase
|
|||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.delegate.DefaultUserWalletsSyncDelegate
|
||||
import com.tangem.domain.wallets.delegate.UserWalletsSyncDelegate
|
||||
import com.tangem.domain.wallets.hot.HotWalletAccessor
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
|
|
@ -372,4 +373,32 @@ internal object WalletsDomainModule {
|
|||
walletsRepository = walletsRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesUnlockHotWalletContextualUseCase(
|
||||
hotWalletAccessor: HotWalletAccessor,
|
||||
): UnlockHotWalletContextualUseCase {
|
||||
return UnlockHotWalletContextualUseCase(
|
||||
hotWalletAccessor = hotWalletAccessor,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesClearHotWalletContextualUnlockUseCase(
|
||||
hotWalletAccessor: HotWalletAccessor,
|
||||
): ClearHotWalletContextualUnlockUseCase {
|
||||
return ClearHotWalletContextualUnlockUseCase(
|
||||
hotWalletAccessor = hotWalletAccessor,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesExportSeedPhraseUseCase(hotWalletAccessor: HotWalletAccessor): ExportSeedPhraseUseCase {
|
||||
return ExportSeedPhraseUseCase(
|
||||
hotWalletAccessor = hotWalletAccessor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,13 @@ class TangemHotSDKProxy @Inject constructor() : TangemHotSdk {
|
|||
override suspend fun exportBackup(unlockHotWallet: UnlockHotWallet): ByteArray =
|
||||
callSdk { exportBackup(unlockHotWallet) }
|
||||
|
||||
override suspend fun clearUnlockContext(hotWalletId: HotWalletId) {
|
||||
callSdk { clearUnlockContext(hotWalletId) }
|
||||
}
|
||||
|
||||
override suspend fun getContextUnlock(unlockHotWallet: UnlockHotWallet): UnlockHotWallet =
|
||||
callSdk { getContextUnlock(unlockHotWallet) }
|
||||
|
||||
override suspend fun delete(id: HotWalletId) = callSdk { delete(id) }
|
||||
|
||||
override suspend fun changeAuth(unlockHotWallet: UnlockHotWallet, auth: HotAuth): HotWalletId =
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.tangem.features.hotwallet.CreateWalletBackupComponent
|
|||
import com.tangem.features.hotwallet.UpdateAccessCodeComponent
|
||||
import com.tangem.features.hotwallet.HotWalletFeatureToggles
|
||||
import com.tangem.features.hotwallet.WalletBackupComponent
|
||||
import com.tangem.features.hotwallet.ViewPhraseComponent
|
||||
import com.tangem.features.managetokens.component.ChooseManagedTokensComponent
|
||||
import com.tangem.features.managetokens.component.ManageTokensComponent
|
||||
import com.tangem.features.managetokens.component.ManageTokensSource
|
||||
|
|
@ -109,6 +110,7 @@ internal class ChildFactory @Inject constructor(
|
|||
private val walletActivationComponentFactory: WalletActivationComponent.Factory,
|
||||
private val createWalletBackupComponentFactory: CreateWalletBackupComponent.Factory,
|
||||
private val updateAccessCodeComponentFactory: UpdateAccessCodeComponent.Factory,
|
||||
private val viewPhraseComponentFactory: ViewPhraseComponent.Factory,
|
||||
private val sendWithSwapComponentFactory: SendWithSwapComponent.Factory,
|
||||
private val sendEntryPointComponentFactory: SendEntryPointComponent.Factory,
|
||||
private val tangemPayDetailsComponentFactory: TangemPayDetailsComponent.Factory,
|
||||
|
|
@ -532,6 +534,15 @@ internal class ChildFactory @Inject constructor(
|
|||
componentFactory = updateAccessCodeComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.ViewPhrase -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = ViewPhraseComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
),
|
||||
componentFactory = viewPhraseComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.SendEntryPoint -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
|
|
|
|||
|
|
@ -334,6 +334,11 @@ sealed class AppRoute(val path: String) : Route {
|
|||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/update_access_code/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class ViewPhrase(
|
||||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/view_seed_phrase/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class SendEntryPoint(
|
||||
val userWalletId: UserWalletId,
|
||||
|
|
|
|||
|
|
@ -9,15 +9,26 @@ import com.tangem.domain.wallets.repository.WalletsRepository
|
|||
import com.tangem.hot.sdk.TangemHotSdk
|
||||
import com.tangem.hot.sdk.exception.WrongPasswordException
|
||||
import com.tangem.hot.sdk.model.*
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.set
|
||||
|
||||
class DefaultHotWalletAccessor @Inject constructor(
|
||||
private val tangemHotSdk: TangemHotSdk,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val hotWalletPasswordRequester: HotWalletPasswordRequester,
|
||||
private val walletsRepository: WalletsRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
) : HotWalletAccessor {
|
||||
|
||||
private val scope = CoroutineScope(context = SupervisorJob() + dispatchers.io)
|
||||
|
||||
private var contextualUnlockHotWallet: ConcurrentHashMap<HotWalletId, UnlockHotWallet?> = ConcurrentHashMap()
|
||||
|
||||
override suspend fun signHashes(hotWalletId: HotWalletId, dataToSign: List<DataToSign>): List<SignedData> =
|
||||
hotSdkRequest(hotWalletId) { unlock ->
|
||||
tangemHotSdk.signHashes(unlockHotWallet = unlock, dataToSign = dataToSign)
|
||||
|
|
@ -30,6 +41,24 @@ class DefaultHotWalletAccessor @Inject constructor(
|
|||
tangemHotSdk.derivePublicKey(unlockHotWallet = unlock, request = request)
|
||||
}
|
||||
|
||||
override suspend fun exportSeedPhrase(hotWalletId: HotWalletId): SeedPhrasePrivateInfo {
|
||||
val unlockHotWallet = contextualUnlockHotWallet[hotWalletId] ?: hotSdkRequest(hotWalletId) { it }
|
||||
return tangemHotSdk.exportMnemonic(unlockHotWallet = unlockHotWallet)
|
||||
}
|
||||
|
||||
override suspend fun unlockContextual(hotWalletId: HotWalletId): UnlockHotWallet = hotSdkRequest(hotWalletId) {
|
||||
tangemHotSdk.getContextUnlock(it).also { unlockHotWallet ->
|
||||
contextualUnlockHotWallet[hotWalletId] = unlockHotWallet
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearContextualUnlock(hotWalletId: HotWalletId) {
|
||||
contextualUnlockHotWallet[hotWalletId] = null
|
||||
scope.launch {
|
||||
tangemHotSdk.clearUnlockContext(hotWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T> hotSdkRequest(hotWalletId: HotWalletId, block: suspend (unlock: UnlockHotWallet) -> T): T {
|
||||
val isAccessCodeRequired = walletsRepository.requireAccessCode()
|
||||
|
||||
|
|
|
|||
|
|
@ -7,4 +7,10 @@ interface HotWalletAccessor {
|
|||
suspend fun signHashes(hotWalletId: HotWalletId, dataToSign: List<DataToSign>): List<SignedData>
|
||||
|
||||
suspend fun derivePublicKeys(hotWalletId: HotWalletId, request: DeriveWalletRequest): DerivedPublicKeyResponse
|
||||
|
||||
suspend fun exportSeedPhrase(hotWalletId: HotWalletId): SeedPhrasePrivateInfo
|
||||
|
||||
suspend fun unlockContextual(hotWalletId: HotWalletId): UnlockHotWallet
|
||||
|
||||
fun clearContextualUnlock(hotWalletId: HotWalletId)
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.wallets.hot.HotWalletAccessor
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
|
||||
class ClearHotWalletContextualUnlockUseCase(
|
||||
private val hotWalletAccessor: HotWalletAccessor,
|
||||
) {
|
||||
|
||||
operator fun invoke(hotWalletId: HotWalletId): Either<Throwable, Unit> {
|
||||
return Either.catch {
|
||||
hotWalletAccessor.clearContextualUnlock(hotWalletId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.wallets.hot.HotWalletAccessor
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.hot.sdk.model.SeedPhrasePrivateInfo
|
||||
|
||||
class ExportSeedPhraseUseCase(
|
||||
private val hotWalletAccessor: HotWalletAccessor,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(hotWalletId: HotWalletId): Either<Throwable, SeedPhrasePrivateInfo> {
|
||||
return Either.catch {
|
||||
hotWalletAccessor.exportSeedPhrase(hotWalletId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.wallets.hot.HotWalletAccessor
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.hot.sdk.model.UnlockHotWallet
|
||||
|
||||
class UnlockHotWalletContextualUseCase(
|
||||
private val hotWalletAccessor: HotWalletAccessor,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(hotWalletId: HotWalletId): Either<Throwable, UnlockHotWallet> {
|
||||
return Either.catch {
|
||||
hotWalletAccessor.unlockContextual(hotWalletId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -149,7 +149,7 @@ private fun WalletBlock(
|
|||
.padding(top = 8.dp)
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.secondary,
|
||||
color = TangemTheme.colors.field.primary,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
.clickable(onClick = onClick)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.features.hotwallet
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
interface ViewPhraseComponent : ComposableContentComponent {
|
||||
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, ViewPhraseComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.features.hotwallet.viewphrase
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.features.hotwallet.ViewPhraseComponent
|
||||
import com.tangem.features.hotwallet.viewphrase.model.ViewPhraseModel
|
||||
import com.tangem.features.hotwallet.viewphrase.ui.ViewPhraseContent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultViewPhraseComponent @AssistedInject constructor(
|
||||
@Assisted private val context: AppComponentContext,
|
||||
@Assisted private val params: ViewPhraseComponent.Params,
|
||||
) : ViewPhraseComponent, AppComponentContext by context {
|
||||
private val model: ViewPhraseModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
ViewPhraseContent(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : ViewPhraseComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: ViewPhraseComponent.Params,
|
||||
): DefaultViewPhraseComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.features.hotwallet.viewphrase.di
|
||||
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.hotwallet.ViewPhraseComponent
|
||||
import com.tangem.features.hotwallet.viewphrase.DefaultViewPhraseComponent
|
||||
import com.tangem.features.hotwallet.viewphrase.model.ViewPhraseModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ViewPhraseModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(ViewPhraseModel::class)
|
||||
fun bindViewPhraseModel(model: ViewPhraseModel): Model
|
||||
|
||||
@Binds
|
||||
fun bindViewPhraseComponentFactory(factory: DefaultViewPhraseComponent.Factory): ViewPhraseComponent.Factory
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.features.hotwallet.viewphrase.entity
|
||||
|
||||
import com.tangem.core.ui.components.grid.entity.EnumeratedTwoColumnGridItem
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal data class ViewPhraseUM(
|
||||
val onBackClick: () -> Unit,
|
||||
val words: ImmutableList<EnumeratedTwoColumnGridItem> = persistentListOf(),
|
||||
)
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.tangem.features.hotwallet.viewphrase.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.ui.components.grid.entity.EnumeratedTwoColumnGridItem
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ClearHotWalletContextualUnlockUseCase
|
||||
import com.tangem.domain.wallets.usecase.ExportSeedPhraseUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.hotwallet.ViewPhraseComponent
|
||||
import com.tangem.features.hotwallet.viewphrase.entity.ViewPhraseUM
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class ViewPhraseModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val exportSeedPhraseUseCase: ExportSeedPhraseUseCase,
|
||||
private val clearHotWalletContextualUnlockUseCase: ClearHotWalletContextualUnlockUseCase,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<ViewPhraseComponent.Params>()
|
||||
|
||||
private var hotWalletId: HotWalletId? = null
|
||||
|
||||
internal val uiState: StateFlow<ViewPhraseUM>
|
||||
field = MutableStateFlow(
|
||||
ViewPhraseUM(
|
||||
onBackClick = { router.pop() },
|
||||
),
|
||||
)
|
||||
|
||||
init {
|
||||
loadSeedPhrase()
|
||||
}
|
||||
|
||||
private fun loadSeedPhrase() {
|
||||
val userWallet = getUserWalletUseCase(params.userWalletId)
|
||||
.getOrElse { error("User wallet with id ${params.userWalletId} not found") }
|
||||
if (userWallet is UserWallet.Hot) {
|
||||
hotWalletId = userWallet.hotWalletId
|
||||
modelScope.launch {
|
||||
val words = exportSeedPhraseUseCase.invoke(userWallet.hotWalletId)
|
||||
.getOrElse { error("Unable to export seed phrase for wallet with id ${params.userWalletId}") }
|
||||
.mnemonic
|
||||
.mnemonicComponents
|
||||
uiState.update {
|
||||
it.copy(
|
||||
words = words.mapIndexed { index, s ->
|
||||
EnumeratedTwoColumnGridItem(index + 1, s)
|
||||
}.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
hotWalletId?.let { clearHotWalletContextualUnlockUseCase.invoke(it) }
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package com.tangem.features.hotwallet.viewphrase.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.components.grid.EnumeratedTwoColumnGrid
|
||||
import com.tangem.core.ui.components.grid.entity.EnumeratedTwoColumnGridItem
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.hotwallet.impl.R
|
||||
import com.tangem.features.hotwallet.viewphrase.entity.ViewPhraseUM
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
internal fun ViewPhraseContent(state: ViewPhraseUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding(),
|
||||
) {
|
||||
AppBarWithBackButton(
|
||||
text = stringResourceSafe(R.string.common_backup),
|
||||
onBackClick = state.onBackClick,
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.imePadding()
|
||||
.weight(1f),
|
||||
) {
|
||||
TitleBlock(
|
||||
state = state,
|
||||
modifier = Modifier.padding(top = 20.dp),
|
||||
)
|
||||
|
||||
EnumeratedTwoColumnGrid(
|
||||
items = state.words,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 20.dp, bottom = 32.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TitleBlock(state: ViewPhraseUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(horizontal = TangemTheme.dimens.size36)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.backup_seed_title),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(
|
||||
R.string.backup_seed_caution,
|
||||
state.words.size,
|
||||
),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 640)
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 640, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
ViewPhraseContent(
|
||||
state = ViewPhraseUM(
|
||||
onBackClick = {},
|
||||
words = List(12) {
|
||||
EnumeratedTwoColumnGridItem(
|
||||
index = it + 1,
|
||||
mnemonic = "word${it + 1}",
|
||||
)
|
||||
}.toImmutableList(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +1,33 @@
|
|||
package com.tangem.features.hotwallet.walletbackup.model
|
||||
|
||||
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
|
||||
import com.tangem.core.ui.components.bottomsheets.message.icon
|
||||
import com.tangem.core.ui.components.bottomsheets.message.infoBlock
|
||||
import com.tangem.core.ui.components.bottomsheets.message.onClick
|
||||
import com.tangem.core.ui.components.bottomsheets.message.secondaryButton
|
||||
import com.tangem.core.ui.components.label.entity.LabelStyle
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.domain.wallets.usecase.UnlockHotWalletContextualUseCase
|
||||
import com.tangem.features.hotwallet.WalletBackupComponent
|
||||
import com.tangem.features.hotwallet.walletbackup.entity.BackupStatus
|
||||
import com.tangem.features.hotwallet.walletbackup.entity.WalletBackupUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class WalletBackupModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
getWalletUseCase: GetUserWalletUseCase,
|
||||
private val router: Router,
|
||||
private val getWalletUseCase: GetUserWalletUseCase,
|
||||
private val unlockHotWalletContextualUseCase: UnlockHotWalletContextualUseCase,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
|
||||
private val router: Router,
|
||||
) : Model() {
|
||||
|
||||
private val params: WalletBackupComponent.Params = paramsContainer.require()
|
||||
|
|
@ -57,34 +52,16 @@ internal class WalletBackupModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
|
||||
private val makeBackupAtFirstAlertBS
|
||||
get() = bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.ic_passcode_lock_32) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Accent
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
||||
}
|
||||
title = resourceReference(R.string.hw_backup_need_title)
|
||||
body = resourceReference(R.string.hw_backup_need_description)
|
||||
}
|
||||
secondaryButton {
|
||||
text = resourceReference(R.string.hw_backup_need_action)
|
||||
onClick {
|
||||
router.push(AppRoute.CreateWalletBackup(params.userWalletId))
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
getWalletUseCase.invokeFlow(params.userWalletId)
|
||||
.map { it.getOrNull() }
|
||||
.distinctUntilChanged()
|
||||
.filterNotNull()
|
||||
.onEach {
|
||||
updateBackupStatuses(it)
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
getWalletUseCase.invoke(params.userWalletId)
|
||||
.fold(
|
||||
ifLeft = {
|
||||
Timber.e("Error on getting user wallet: $it")
|
||||
},
|
||||
ifRight = {
|
||||
updateBackupStatuses(it)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateBackupStatuses(userWallet: UserWallet) {
|
||||
|
|
@ -118,9 +95,37 @@ internal class WalletBackupModel @Inject constructor(
|
|||
|
||||
private fun onRecoveryPhraseClick() {
|
||||
if (uiState.value.backedUp) {
|
||||
// TODO [REDACTED_TASK_KEY]
|
||||
getWalletUseCase.invoke(params.userWalletId)
|
||||
.fold(
|
||||
ifLeft = {
|
||||
Timber.e("Error on getting user wallet: $it")
|
||||
},
|
||||
ifRight = { userWallet ->
|
||||
when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
val userWalletId = userWallet.walletId
|
||||
Timber.e("Unexpected cold wallet when request seed phrase: $userWalletId")
|
||||
}
|
||||
is UserWallet.Hot -> showSeedPhrase(userWallet)
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
uiMessageSender.send(makeBackupAtFirstAlertBS)
|
||||
router.push(AppRoute.CreateWalletBackup(params.userWalletId))
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSeedPhrase(hotWallet: UserWallet.Hot) {
|
||||
modelScope.launch {
|
||||
unlockHotWalletContextualUseCase.invoke(hotWallet.hotWalletId)
|
||||
.fold(
|
||||
ifLeft = {
|
||||
Timber.e("Error while export seed phrase: $it")
|
||||
},
|
||||
ifRight = { seedPhrasePrivateInfo ->
|
||||
router.push(AppRoute.ViewPhrase(params.userWalletId))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ tangemCardSdk = "develop-561"
|
|||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
tangemVico = "2.0.0-alpha.25-tangem12"
|
||||
#tangemVico = "0.0.1" # Keep it! - used for local builds ^
|
||||
tangemHotSdk = "develop-525"
|
||||
tangemHotSdk = "develop-526"
|
||||
#tangemHotSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue