Updated on 2026-08-14
This commit is contained in:
parent
ebc4350919
commit
7e23a3a63b
18 changed files with 336 additions and 42 deletions
|
|
@ -41,6 +41,7 @@ import com.tangem.domain.settings.IncrementAppLaunchCounterUseCase
|
||||||
import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase
|
import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase
|
||||||
import com.tangem.domain.staking.FetchStakingTokensUseCase
|
import com.tangem.domain.staking.FetchStakingTokensUseCase
|
||||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.requireColdWallet
|
import com.tangem.domain.wallets.models.requireColdWallet
|
||||||
import com.tangem.domain.wallets.usecase.AssociateWalletsWithApplicationIdUseCase
|
import com.tangem.domain.wallets.usecase.AssociateWalletsWithApplicationIdUseCase
|
||||||
import com.tangem.domain.wallets.usecase.GetSavedWalletChangesUseCase
|
import com.tangem.domain.wallets.usecase.GetSavedWalletChangesUseCase
|
||||||
|
|
@ -200,7 +201,12 @@ internal class MainViewModel @Inject constructor(
|
||||||
userWalletsListManager.selectedUserWallet
|
userWalletsListManager.selectedUserWallet
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.onEach { userWallet ->
|
.onEach { userWallet ->
|
||||||
Analytics.setContext(userWallet.requireColdWallet().scanResponse) // TODO [REDACTED_TASK_KEY]
|
when (userWallet) {
|
||||||
|
is UserWallet.Cold -> Analytics.setContext(userWallet.scanResponse)
|
||||||
|
is UserWallet.Hot -> {
|
||||||
|
// TODO [REDACTED_TASK_KEY]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.flowOn(dispatchers.io)
|
.flowOn(dispatchers.io)
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,23 @@ import com.tangem.domain.wallets.models.requireColdWallet
|
||||||
internal class DefaultAuthProvider(private val userWalletsListManager: UserWalletsListManager) : AuthProvider {
|
internal class DefaultAuthProvider(private val userWalletsListManager: UserWalletsListManager) : AuthProvider {
|
||||||
|
|
||||||
override fun getCardPublicKey(): String {
|
override fun getCardPublicKey(): String {
|
||||||
return userWalletsListManager.selectedUserWalletSync
|
val userWallet = userWalletsListManager.selectedUserWalletSync
|
||||||
?.requireColdWallet()?.scanResponse?.card?.cardPublicKey?.toHexString() ?: ""
|
|
||||||
|
return when (userWallet) {
|
||||||
|
is UserWallet.Cold -> userWallet.scanResponse.card.cardPublicKey.toHexString()
|
||||||
|
is UserWallet.Hot -> userWallet.wallets?.firstOrNull()?.publicKey?.toHexString() ?: ""
|
||||||
|
null -> ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCardId(): String {
|
override fun getCardId(): String {
|
||||||
return userWalletsListManager.selectedUserWalletSync
|
val userWallet = userWalletsListManager.selectedUserWalletSync
|
||||||
?.requireColdWallet()?.scanResponse?.card?.cardId ?: ""
|
|
||||||
|
if (userWallet !is UserWallet.Cold) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return userWallet.requireColdWallet().scanResponse.card.cardId
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCardsPublicKeys(): Map<String, String> {
|
override fun getCardsPublicKeys(): Map<String, String> {
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ fun WalletManagerFactory.makeWalletManagerForApp(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makePublicKey(
|
fun makePublicKey(
|
||||||
seedKey: ByteArray,
|
seedKey: ByteArray,
|
||||||
blockchain: Blockchain,
|
blockchain: Blockchain,
|
||||||
derivationPath: DerivationPath,
|
derivationPath: DerivationPath,
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ import com.tangem.domain.walletmanager.utils.*
|
||||||
import com.tangem.domain.walletmanager.utils.WalletManagerFactory
|
import com.tangem.domain.walletmanager.utils.WalletManagerFactory
|
||||||
import com.tangem.domain.wallets.models.UserWallet
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.domain.wallets.models.requireColdWallet
|
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
|
@ -371,21 +370,31 @@ class DefaultWalletManagersFacade(
|
||||||
initMutex.withLock {
|
initMutex.withLock {
|
||||||
val userWallet = getUserWallet(userWalletId)
|
val userWallet = getUserWallet(userWalletId)
|
||||||
|
|
||||||
userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY]
|
|
||||||
|
|
||||||
var walletManager = walletManagersStore.getSyncOrNull(
|
var walletManager = walletManagersStore.getSyncOrNull(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
blockchain = blockchain,
|
blockchain = blockchain,
|
||||||
derivationPath = derivationPath,
|
derivationPath = derivationPath,
|
||||||
)
|
)
|
||||||
|
val path = derivationPath?.let { DerivationPath(rawPath = it) }
|
||||||
|
|
||||||
if (walletManager == null) {
|
if (walletManager == null) {
|
||||||
|
when (userWallet) {
|
||||||
|
is UserWallet.Hot -> {
|
||||||
|
walletManager = walletManagerFactory.createWalletManagerForHot(
|
||||||
|
hotWallet = userWallet,
|
||||||
|
blockchain = blockchain,
|
||||||
|
derivationPath = path,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is UserWallet.Cold -> {
|
||||||
walletManager = walletManagerFactory.createWalletManager(
|
walletManager = walletManagerFactory.createWalletManager(
|
||||||
scanResponse = userWallet.scanResponse,
|
scanResponse = userWallet.scanResponse,
|
||||||
blockchain = blockchain,
|
blockchain = blockchain,
|
||||||
derivationPath = derivationPath?.let { DerivationPath(rawPath = it) },
|
derivationPath = path,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
walletManager ?: return null
|
walletManager ?: return null
|
||||||
|
|
||||||
walletManagersStore.store(userWalletId, walletManager)
|
walletManagersStore.store(userWalletId, walletManager)
|
||||||
}
|
}
|
||||||
return walletManager
|
return walletManager
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,11 @@ import com.tangem.blockchain.common.WalletManager
|
||||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||||
import com.tangem.crypto.hdWallet.DerivationPath
|
import com.tangem.crypto.hdWallet.DerivationPath
|
||||||
import com.tangem.domain.common.DerivationStyleProvider
|
import com.tangem.domain.common.DerivationStyleProvider
|
||||||
|
import com.tangem.domain.common.extensions.makePublicKey
|
||||||
import com.tangem.domain.common.extensions.makeWalletManagerForApp
|
import com.tangem.domain.common.extensions.makeWalletManagerForApp
|
||||||
import com.tangem.domain.common.util.derivationStyleProvider
|
import com.tangem.domain.common.util.derivationStyleProvider
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
internal class WalletManagerFactory(
|
internal class WalletManagerFactory(
|
||||||
|
|
@ -34,6 +36,42 @@ internal class WalletManagerFactory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun createWalletManagerForHot(
|
||||||
|
hotWallet: UserWallet.Hot,
|
||||||
|
blockchain: Blockchain,
|
||||||
|
derivationPath: DerivationPath?,
|
||||||
|
): WalletManager? {
|
||||||
|
val curve = blockchain.getSupportedCurves().first()
|
||||||
|
val selectedWallet = hotWallet.wallets.orEmpty().firstOrNull { it.curve == curve }
|
||||||
|
?: return null
|
||||||
|
return try {
|
||||||
|
val factory = blockchainSDKFactory.getWalletManagerFactorySync() ?: return null
|
||||||
|
|
||||||
|
if (derivationPath == null) {
|
||||||
|
factory.createLegacyWalletManager(
|
||||||
|
blockchain = blockchain,
|
||||||
|
walletPublicKey = selectedWallet.publicKey,
|
||||||
|
curve = selectedWallet.curve,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
factory.createWalletManager(
|
||||||
|
blockchain = blockchain,
|
||||||
|
publicKey = makePublicKey(
|
||||||
|
seedKey = selectedWallet.publicKey,
|
||||||
|
blockchain = blockchain,
|
||||||
|
derivationPath = derivationPath,
|
||||||
|
derivedWalletKeys = selectedWallet.derivedKeys,
|
||||||
|
isWallet2 = true,
|
||||||
|
) ?: return null,
|
||||||
|
curve = selectedWallet.curve,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.w(e, "Failed to create wallet manager for $blockchain")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getDerivationParams(
|
private fun getDerivationParams(
|
||||||
derivationPath: DerivationPath?,
|
derivationPath: DerivationPath?,
|
||||||
derivationStyleProvider: DerivationStyleProvider,
|
derivationStyleProvider: DerivationStyleProvider,
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,22 @@ package com.tangem.domain.models
|
||||||
data class ArtworkModel(
|
data class ArtworkModel(
|
||||||
val verifiedArtwork: ByteArray? = null,
|
val verifiedArtwork: ByteArray? = null,
|
||||||
val defaultUrl: String,
|
val defaultUrl: String,
|
||||||
)
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as ArtworkModel
|
||||||
|
|
||||||
|
if (!verifiedArtwork.contentEquals(other.verifiedArtwork)) return false
|
||||||
|
if (defaultUrl != other.defaultUrl) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = verifiedArtwork?.contentHashCode() ?: 0
|
||||||
|
result = 31 * result + defaultUrl.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.tangem.domain.models
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
import com.tangem.common.card.EllipticCurve
|
||||||
|
import com.tangem.crypto.hdWallet.DerivationPath
|
||||||
|
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||||
|
import com.tangem.domain.models.serialization.MobileWalletAsStringSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable(with = MobileWalletAsStringSerializer::class)
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
class MobileWallet(
|
||||||
|
@Json(name = "publicKey")
|
||||||
|
val publicKey: ByteArray,
|
||||||
|
@Json(name = "chainCode")
|
||||||
|
val chainCode: ByteArray?,
|
||||||
|
@Json(name = "curve")
|
||||||
|
val curve: EllipticCurve,
|
||||||
|
@Json(name = "derivedKeys")
|
||||||
|
val derivedKeys: Map<DerivationPath, ExtendedPublicKey>,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val extendedPublicKey: ExtendedPublicKey?
|
||||||
|
get() = chainCode?.let {
|
||||||
|
ExtendedPublicKey(
|
||||||
|
publicKey = publicKey,
|
||||||
|
chainCode = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.tangem.domain.models.serialization
|
||||||
|
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||||
|
import com.tangem.common.json.TangemSdkAdapter
|
||||||
|
import com.tangem.domain.models.MobileWallet
|
||||||
|
import com.tangem.domain.models.scan.serialization.*
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
|
||||||
|
internal object MobileWalletAsStringSerializer : KSerializer<MobileWallet> {
|
||||||
|
|
||||||
|
private val moshi = Moshi.Builder()
|
||||||
|
.add(WalletDerivedKeysMapAdapter())
|
||||||
|
.add(ScanResponseDerivedKeysMapAdapter())
|
||||||
|
.add(ByteArrayKeyAdapter())
|
||||||
|
.add(ExtendedPublicKeysMapAdapter())
|
||||||
|
.add(DerivationPathAdapterWithMigration())
|
||||||
|
.add(TangemSdkAdapter.DateAdapter())
|
||||||
|
.add(TangemSdkAdapter.DerivationNodeAdapter())
|
||||||
|
.addLast(KotlinJsonAdapterFactory())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MobileWallet", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): MobileWallet {
|
||||||
|
return moshi.adapter(MobileWallet::class.java).fromJson(decoder.decodeString())!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: MobileWallet) {
|
||||||
|
encoder.encodeString(moshi.adapter(MobileWallet::class.java).toJson(value)!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,7 @@ dependencies {
|
||||||
// region Tangem libraries
|
// region Tangem libraries
|
||||||
implementation(tangemDeps.blockchain) // android-library
|
implementation(tangemDeps.blockchain) // android-library
|
||||||
implementation(tangemDeps.card.core)
|
implementation(tangemDeps.card.core)
|
||||||
|
implementation(tangemDeps.hot.core)
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region DI
|
// region DI
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ plugins {
|
||||||
dependencies {
|
dependencies {
|
||||||
// region Tangem libraries
|
// region Tangem libraries
|
||||||
implementation(tangemDeps.card.core)
|
implementation(tangemDeps.card.core)
|
||||||
|
implementation(tangemDeps.hot.core)
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Domain modules
|
// region Domain modules
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
package com.tangem.domain.wallets.models
|
package com.tangem.domain.wallets.models
|
||||||
|
|
||||||
|
import com.tangem.domain.models.MobileWallet
|
||||||
import com.tangem.domain.models.scan.CardDTO
|
import com.tangem.domain.models.scan.CardDTO
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.domain.wallets.models.UserWallet.Cold
|
import com.tangem.hot.sdk.model.HotWalletId
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlin.contracts.ExperimentalContracts
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
|
@ -56,17 +57,21 @@ sealed interface UserWallet {
|
||||||
data class Hot(
|
data class Hot(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val walletId: UserWalletId,
|
override val walletId: UserWalletId,
|
||||||
val isLocked: Boolean,
|
val hotWalletId: HotWalletId,
|
||||||
) : UserWallet
|
val wallets: List<MobileWallet>?,
|
||||||
|
) : UserWallet {
|
||||||
|
|
||||||
|
val isLocked: Boolean get() = wallets == null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalContracts::class)
|
@OptIn(ExperimentalContracts::class)
|
||||||
fun UserWallet.requireColdWallet(): Cold {
|
fun UserWallet.requireColdWallet(): UserWallet.Cold {
|
||||||
contract {
|
contract {
|
||||||
returns() implies (this@requireColdWallet is Cold)
|
returns() implies (this@requireColdWallet is UserWallet.Cold)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this as? Cold
|
return this as? UserWallet.Cold
|
||||||
?: error("This user wallet is not a cold wallet")
|
?: error("This user wallet is not a cold wallet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.tangem.domain.wallets.builder
|
||||||
|
|
||||||
|
import com.tangem.blockchain.common.Blockchain
|
||||||
|
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||||
|
import com.tangem.domain.models.MobileWallet
|
||||||
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
|
||||||
|
import com.tangem.hot.sdk.TangemHotSdk
|
||||||
|
import com.tangem.hot.sdk.model.DeriveWalletRequest
|
||||||
|
import com.tangem.hot.sdk.model.HotAuth
|
||||||
|
import com.tangem.hot.sdk.model.HotWalletId
|
||||||
|
import com.tangem.hot.sdk.model.UnlockHotWallet
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
|
||||||
|
class HotUserWalletBuilder @AssistedInject constructor(
|
||||||
|
@Assisted private val hotWalletId: HotWalletId,
|
||||||
|
private val hotSdk: TangemHotSdk,
|
||||||
|
private val generateWalletNameUseCase: GenerateWalletNameUseCase,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun build(): UserWallet.Hot {
|
||||||
|
val allNetworks = Blockchain.entries // TODO use HotDerivationsRepository to get supported networks
|
||||||
|
val requests = allNetworks.map {
|
||||||
|
val curves = it.getSupportedCurves()
|
||||||
|
val derivationPath = it.derivationPath(DerivationStyle.V3)
|
||||||
|
|
||||||
|
curves.map {
|
||||||
|
DeriveWalletRequest.Request(
|
||||||
|
curve = it,
|
||||||
|
paths = listOfNotNull(derivationPath),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.flatten()
|
||||||
|
|
||||||
|
val derivationResult = hotSdk.derivePublicKey(
|
||||||
|
unlockHotWallet = UnlockHotWallet(
|
||||||
|
walletId = hotWalletId,
|
||||||
|
auth = HotAuth.NoAuth,
|
||||||
|
),
|
||||||
|
request = DeriveWalletRequest(
|
||||||
|
requests = requests,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val wallets = derivationResult.responses.map {
|
||||||
|
MobileWallet(
|
||||||
|
publicKey = it.seedKey.publicKey,
|
||||||
|
chainCode = it.seedKey.chainCode,
|
||||||
|
curve = it.curve,
|
||||||
|
derivedKeys = it.publicKeys,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return UserWallet.Hot(
|
||||||
|
name = generateWalletNameUseCase.invokeForHot(),
|
||||||
|
walletId = UserWalletId(wallets.first().publicKey),
|
||||||
|
hotWalletId = hotWalletId,
|
||||||
|
wallets = wallets,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
fun create(hotWalletId: HotWalletId): HotUserWalletBuilder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.tangem.domain.wallets.repository
|
||||||
|
|
||||||
|
import com.tangem.domain.models.network.Network
|
||||||
|
|
||||||
|
interface HotDerivationsRepository {
|
||||||
|
|
||||||
|
fun getAllSupportedNetworks(): Set<Network>
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,12 @@ class GenerateWalletNameUseCase(
|
||||||
return suggestedWalletName(defaultName, existingNames)
|
return suggestedWalletName(defaultName, existingNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun invokeForHot(): String {
|
||||||
|
val defaultName = "Mobile Wallet"
|
||||||
|
val existingNames = userWalletsListManager.userWalletsSync.map { it.name }.toSet()
|
||||||
|
return suggestedWalletName(defaultName, existingNames)
|
||||||
|
}
|
||||||
|
|
||||||
private fun suggestedWalletName(defaultName: String, existingNames: Set<String>): String {
|
private fun suggestedWalletName(defaultName: String, existingNames: Set<String>): String {
|
||||||
val startIndex = 2
|
val startIndex = 2
|
||||||
if (!existingNames.contains(defaultName)) {
|
if (!existingNames.contains(defaultName)) {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
package com.tangem.features.hotwallet.createmobilewallet
|
package com.tangem.features.hotwallet.createmobilewallet
|
||||||
|
|
||||||
|
import com.tangem.common.routing.AppRoute
|
||||||
import com.tangem.core.decompose.di.ModelScoped
|
import com.tangem.core.decompose.di.ModelScoped
|
||||||
import com.tangem.core.decompose.model.Model
|
import com.tangem.core.decompose.model.Model
|
||||||
import com.tangem.core.decompose.navigation.Router
|
import com.tangem.core.decompose.navigation.Router
|
||||||
|
import com.tangem.domain.wallets.builder.HotUserWalletBuilder
|
||||||
|
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
|
||||||
import com.tangem.features.hotwallet.createmobilewallet.entity.CreateMobileWalletUM
|
import com.tangem.features.hotwallet.createmobilewallet.entity.CreateMobileWalletUM
|
||||||
import com.tangem.hot.sdk.TangemHotSdk
|
import com.tangem.hot.sdk.TangemHotSdk
|
||||||
import com.tangem.hot.sdk.model.HotAuth
|
import com.tangem.hot.sdk.model.HotAuth
|
||||||
|
|
@ -10,12 +13,15 @@ import com.tangem.hot.sdk.model.MnemonicType
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ModelScoped
|
@ModelScoped
|
||||||
internal class CreateMobileWalletModel @Inject constructor(
|
internal class CreateMobileWalletModel @Inject constructor(
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
private val hotUserWalletBuilderFactory: HotUserWalletBuilder.Factory,
|
||||||
|
private val saveUserWalletUseCase: SaveWalletUseCase,
|
||||||
private val router: Router,
|
private val router: Router,
|
||||||
private val tangemHotSdk: TangemHotSdk,
|
private val tangemHotSdk: TangemHotSdk,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
@ -25,19 +31,28 @@ internal class CreateMobileWalletModel @Inject constructor(
|
||||||
CreateMobileWalletUM(
|
CreateMobileWalletUM(
|
||||||
onBackClick = { router.pop() },
|
onBackClick = { router.pop() },
|
||||||
onCreateClick = ::onCreateClick,
|
onCreateClick = ::onCreateClick,
|
||||||
|
createButtonLoading = false,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun onCreateClick() {
|
private fun onCreateClick() {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
tangemHotSdk.generateWallet(HotAuth.NoAuth, mnemonicType = MnemonicType.Words12)
|
uiState.update {
|
||||||
// TODO
|
it.copy(createButtonLoading = true)
|
||||||
// val hotUserWalletBuilder = hotUserWalletBuilderFactory.create(hotWalletId, tangemHotSdk)
|
}
|
||||||
// saveUserWalletUseCase(
|
|
||||||
// hotUserWalletBuilder.build(),
|
|
||||||
// )
|
|
||||||
|
|
||||||
// router.push(AppRoute.Wallet)
|
runCatching {
|
||||||
|
val hotWalletId = tangemHotSdk.generateWallet(HotAuth.NoAuth, mnemonicType = MnemonicType.Words12)
|
||||||
|
val hotUserWalletBuilder = hotUserWalletBuilderFactory.create(hotWalletId)
|
||||||
|
saveUserWalletUseCase(
|
||||||
|
hotUserWalletBuilder.build(),
|
||||||
|
)
|
||||||
|
router.push(AppRoute.Wallet)
|
||||||
|
}.onFailure {
|
||||||
|
uiState.update {
|
||||||
|
it.copy(createButtonLoading = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tangem.features.hotwallet.createmobilewallet.entity
|
package com.tangem.features.hotwallet.createmobilewallet.entity
|
||||||
|
|
||||||
internal data class CreateMobileWalletUM(
|
internal data class CreateMobileWalletUM(
|
||||||
|
val createButtonLoading: Boolean,
|
||||||
val onBackClick: () -> Unit,
|
val onBackClick: () -> Unit,
|
||||||
val onCreateClick: () -> Unit,
|
val onCreateClick: () -> Unit,
|
||||||
)
|
)
|
||||||
|
|
@ -85,7 +85,7 @@ internal fun CreateMobileWalletContent(state: CreateMobileWalletUM, modifier: Mo
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
text = stringResourceSafe(R.string.common_create),
|
text = stringResourceSafe(R.string.common_create),
|
||||||
showProgress = false,
|
showProgress = state.createButtonLoading,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
onClick = state.onCreateClick,
|
onClick = state.onCreateClick,
|
||||||
)
|
)
|
||||||
|
|
@ -133,6 +133,7 @@ private fun PreviewCreateWalletContent() {
|
||||||
CreateMobileWalletContent(
|
CreateMobileWalletContent(
|
||||||
state = CreateMobileWalletUM(
|
state = CreateMobileWalletUM(
|
||||||
onBackClick = {},
|
onBackClick = {},
|
||||||
|
createButtonLoading = false,
|
||||||
onCreateClick = {},
|
onCreateClick = {},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||||
import com.tangem.domain.common.util.cardTypesResolver
|
import com.tangem.domain.common.util.cardTypesResolver
|
||||||
import com.tangem.domain.wallets.models.UserWallet
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.requireColdWallet
|
|
||||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
|
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
|
||||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||||
|
|
@ -26,14 +25,37 @@ internal class WalletLoadingStateFactory(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun create(userWallet: UserWallet): WalletState {
|
fun create(userWallet: UserWallet): WalletState {
|
||||||
userWallet.requireColdWallet()
|
return when (userWallet) {
|
||||||
|
is UserWallet.Cold -> {
|
||||||
return userWallet.createStateByWalletType(
|
userWallet.createStateByWalletType(
|
||||||
multiCurrencyCreator = { createLoadingMultiCurrencyContent(userWallet) },
|
multiCurrencyCreator = { createLoadingMultiCurrencyContent(userWallet) },
|
||||||
singleCurrencyCreator = { createLoadingSingleCurrencyContent(userWallet) },
|
singleCurrencyCreator = { createLoadingSingleCurrencyContent(userWallet) },
|
||||||
visaWalletCreator = { createLoadingVisaWalletContent(userWallet) },
|
visaWalletCreator = { createLoadingVisaWalletContent(userWallet) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is UserWallet.Hot -> {
|
||||||
|
createLoadingHotWalletContent(userWallet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createLoadingHotWalletContent(userWallet: UserWallet.Hot): WalletState.MultiCurrency.Content {
|
||||||
|
return WalletState.MultiCurrency.Content(
|
||||||
|
pullToRefreshConfig = createPullToRefreshConfig(),
|
||||||
|
walletCardState = WalletCardState.Loading(
|
||||||
|
id = userWallet.walletId,
|
||||||
|
title = userWallet.name,
|
||||||
|
additionalInfo = null, // TODO [REDACTED_TASK_KEY]
|
||||||
|
imageResId = null, // TODO [REDACTED_TASK_KEY]
|
||||||
|
dropDownItems = persistentListOf(),
|
||||||
|
),
|
||||||
|
buttons = createMultiWalletActions(userWallet),
|
||||||
|
warnings = persistentListOf(),
|
||||||
|
bottomSheetConfig = null,
|
||||||
|
tokensListState = WalletTokensListState.ContentState.Loading,
|
||||||
|
nftState = WalletNFTItemUM.Hidden,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun createLoadingMultiCurrencyContent(userWallet: UserWallet.Cold): WalletState.MultiCurrency.Content {
|
private fun createLoadingMultiCurrencyContent(userWallet: UserWallet.Cold): WalletState.MultiCurrency.Content {
|
||||||
return WalletState.MultiCurrency.Content(
|
return WalletState.MultiCurrency.Content(
|
||||||
|
|
@ -99,15 +121,21 @@ internal class WalletLoadingStateFactory(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createMultiWalletActions(userWallet: UserWallet.Cold): PersistentList<WalletManageButton> {
|
private fun createMultiWalletActions(userWallet: UserWallet): PersistentList<WalletManageButton> {
|
||||||
val isSingleWalletWithToken = userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
|
val isSingleWalletWithToken =
|
||||||
|
userWallet is UserWallet.Cold && userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
|
||||||
if (isSingleWalletWithToken) return persistentListOf()
|
if (isSingleWalletWithToken) return persistentListOf()
|
||||||
|
|
||||||
return persistentListOf(
|
return persistentListOf(
|
||||||
WalletManageButton.Buy(
|
WalletManageButton.Buy(
|
||||||
enabled = true,
|
enabled = true,
|
||||||
dimContent = false,
|
dimContent = false,
|
||||||
onClick = { clickIntents.onMultiWalletBuyClick(userWalletId = userWallet.walletId, WALLET_TYPE) },
|
onClick = {
|
||||||
|
clickIntents.onMultiWalletBuyClick(
|
||||||
|
userWalletId = userWallet.walletId,
|
||||||
|
WALLET_TYPE,
|
||||||
|
)
|
||||||
|
},
|
||||||
),
|
),
|
||||||
WalletManageButton.Swap(
|
WalletManageButton.Swap(
|
||||||
enabled = true,
|
enabled = true,
|
||||||
|
|
@ -124,14 +152,24 @@ internal class WalletLoadingStateFactory(
|
||||||
|
|
||||||
private fun createVisaDimmedButtons(): PersistentList<WalletManageButton> {
|
private fun createVisaDimmedButtons(): PersistentList<WalletManageButton> {
|
||||||
return persistentListOf(
|
return persistentListOf(
|
||||||
WalletManageButton.Receive(enabled = true, dimContent = true, onClick = {}, onLongClick = null),
|
WalletManageButton.Receive(
|
||||||
|
enabled = true,
|
||||||
|
dimContent = true,
|
||||||
|
onClick = {},
|
||||||
|
onLongClick = null,
|
||||||
|
),
|
||||||
WalletManageButton.Buy(enabled = true, dimContent = true, onClick = {}),
|
WalletManageButton.Buy(enabled = true, dimContent = true, onClick = {}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createDimmedButtons(): PersistentList<WalletManageButton> {
|
private fun createDimmedButtons(): PersistentList<WalletManageButton> {
|
||||||
return persistentListOf(
|
return persistentListOf(
|
||||||
WalletManageButton.Receive(enabled = true, dimContent = true, onClick = {}, onLongClick = null),
|
WalletManageButton.Receive(
|
||||||
|
enabled = true,
|
||||||
|
dimContent = true,
|
||||||
|
onClick = {},
|
||||||
|
onLongClick = null,
|
||||||
|
),
|
||||||
WalletManageButton.Send(enabled = true, dimContent = true, onClick = {}),
|
WalletManageButton.Send(enabled = true, dimContent = true, onClick = {}),
|
||||||
WalletManageButton.Buy(enabled = true, dimContent = true, onClick = {}),
|
WalletManageButton.Buy(enabled = true, dimContent = true, onClick = {}),
|
||||||
WalletManageButton.Sell(enabled = true, dimContent = true, onClick = {}),
|
WalletManageButton.Sell(enabled = true, dimContent = true, onClick = {}),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue