From 72ddf5ae12ae35eb6cd5162b0f53c6037e4c948c Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 11 Jun 2024 15:39:15 +0400 Subject: [PATCH 01/19] Updated on 2026-08-14 --- domain/tokens/models/build.gradle.kts | 3 +- .../domain/tokens/model/CryptoCurrency.kt | 25 ++++++++----- .../com/tangem/domain/tokens/model/Network.kt | 37 +++++++++++-------- domain/wallets/models/build.gradle.kts | 5 +++ .../domain/wallets/models/UserWalletId.kt | 5 ++- 5 files changed, 47 insertions(+), 28 deletions(-) diff --git a/domain/tokens/models/build.gradle.kts b/domain/tokens/models/build.gradle.kts index 948bad320b..a243ada498 100644 --- a/domain/tokens/models/build.gradle.kts +++ b/domain/tokens/models/build.gradle.kts @@ -1,7 +1,7 @@ plugins { alias(deps.plugins.android.library) alias(deps.plugins.kotlin.android) - id("kotlin-parcelize") + alias(deps.plugins.kotlin.serialization) id("configuration") } @@ -10,6 +10,7 @@ android { } dependencies { + implementation(deps.kotlin.serialization) implementation(projects.domain.txhistory.models) implementation(projects.core.analytics.models) implementation(deps.tangem.blockchain) { diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt index db909d08f3..e650d2b8ea 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt @@ -1,7 +1,6 @@ package com.tangem.domain.tokens.model -import android.os.Parcelable -import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable /** * Represents a generic cryptocurrency. @@ -14,8 +13,8 @@ import kotlinx.parcelize.Parcelize * @property iconUrl Optional URL of the cryptocurrency icon. `null` if not found. * @property isCustom Indicates whether the currency is a custom user-added currency or not. */ -@Parcelize -sealed class CryptoCurrency : Parcelable { +@Serializable +sealed class CryptoCurrency { abstract val id: ID abstract val network: Network @@ -28,6 +27,7 @@ sealed class CryptoCurrency : Parcelable { /** * Represents a native coin in the blockchain network. */ + @Serializable data class Coin( override val id: ID, override val network: Network, @@ -48,6 +48,7 @@ sealed class CryptoCurrency : Parcelable { * * @property contractAddress Address of the contract managing the token. */ + @Serializable data class Token( override val id: ID, override val network: Network, @@ -75,12 +76,12 @@ sealed class CryptoCurrency : Parcelable { * @property rawCurrencyId Represents not unique currency ID from the blockchain network. `null` if * its ID of the custom token. */ - @Parcelize + @Serializable data class ID( private val prefix: Prefix, private val body: Body, private val suffix: Suffix, - ) : Parcelable { + ) { val value: String get() = buildString { @@ -121,12 +122,13 @@ sealed class CryptoCurrency : Parcelable { * * The body can be either a raw network ID or a raw network ID with a network derivation path. */ - @Parcelize - sealed class Body : Parcelable { + @Serializable + sealed class Body { /** The value of the body. */ abstract val value: String + @Serializable /** Represents a raw network ID. */ data class NetworkId(val rawId: String) : Body() { override val value: String get() = rawId @@ -137,6 +139,7 @@ sealed class CryptoCurrency : Parcelable { * * Should be used for a cryptocurrencies with custom derivation path. * */ + @Serializable data class NetworkIdWithDerivationPath( val rawId: String, val derivationPath: String, @@ -155,13 +158,14 @@ sealed class CryptoCurrency : Parcelable { * * The suffix can either be a raw ID or a contract address. */ - @Parcelize - sealed class Suffix : Parcelable { + @Serializable + sealed class Suffix { /** The value of the suffix, which could be either a raw ID or a contract address. */ abstract val value: String /** Represents a raw ID suffix. */ + @Serializable data class RawID(val rawId: String, val contractAddress: String? = null) : Suffix() { override val value: String get() = buildString { @@ -174,6 +178,7 @@ sealed class CryptoCurrency : Parcelable { } /** Represents a contract address suffix. */ + @Serializable data class ContractAddress(val contractAddress: String) : Suffix() { override val value: String get() = contractAddress } diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/Network.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/Network.kt index 5dcdb275ef..59d8d3a517 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/Network.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/Network.kt @@ -1,7 +1,6 @@ package com.tangem.domain.tokens.model -import android.os.Parcelable -import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable /** * Represents a blockchain network, identified by a unique ID, a human-readable name, and its standard type. @@ -20,7 +19,7 @@ import kotlinx.parcelize.Parcelize * that cannot be represented in a fiat currency. * (For those blockchains that have FeeResource instead of a standard type of fee) */ -@Parcelize +@Serializable data class Network( val id: ID, val backendId: String, @@ -30,7 +29,7 @@ data class Network( val isTestnet: Boolean, val standardType: StandardType, val hasFiatFeeRate: Boolean, -) : Parcelable { +) { init { require(name.isNotBlank()) { "Network name must not be blank" } @@ -42,8 +41,8 @@ data class Network( * @property value The string representation of the network ID. */ @JvmInline - @Parcelize - value class ID(val value: String) : Parcelable { + @Serializable + value class ID(val value: String) { init { require(value.isNotBlank()) { "Network ID must not be blank" } @@ -56,8 +55,8 @@ data class Network( * This class represents such paths in a generic manner, allowing for predefined card-based paths, * custom paths, or even no derivation path at all. */ - @Parcelize - sealed class DerivationPath : Parcelable { + @Serializable + sealed class DerivationPath { /** The actual derivation path value, if any. */ abstract val value: String? @@ -67,6 +66,7 @@ data class Network( * * @property value The derivation path string. */ + @Serializable data class Card(override val value: String) : DerivationPath() /** @@ -74,12 +74,14 @@ data class Network( * * @property value The derivation path string. */ + @Serializable data class Custom(override val value: String) : DerivationPath() /** * Represents a lack of derivation path. */ - object None : DerivationPath() { + @Serializable + data object None : DerivationPath() { override val value: String? get() = null } } @@ -93,31 +95,36 @@ data class Network( * * @property name The human-readable name of the standard type. */ - @Parcelize - sealed class StandardType : Parcelable { + @Serializable + sealed class StandardType { abstract val name: String /** Represents the ERC20 token standard, common on the Ethereum network. */ - object ERC20 : StandardType() { + @Serializable + data object ERC20 : StandardType() { override val name: String get() = "ERC20" } /** Represents the TRC20 token standard, common on the TRON network. */ - object TRC20 : StandardType() { + @Serializable + data object TRC20 : StandardType() { override val name: String get() = "TRC20" } /** Represents the BEP20 token standard, common on the Binance Smart Chain network. */ - object BEP20 : StandardType() { + @Serializable + data object BEP20 : StandardType() { override val name: String get() = "BEP20" } /** Represents the BEP2 token standard, common on the Binance Chain network. */ - object BEP2 : StandardType() { + @Serializable + data object BEP2 : StandardType() { override val name: String get() = "BEP2" } /** Represents a network that does not adhere to a predefined standard type. */ + @Serializable data class Unspecified(override val name: String) : StandardType() } } \ No newline at end of file diff --git a/domain/wallets/models/build.gradle.kts b/domain/wallets/models/build.gradle.kts index 537d724d84..1b3ce4dfaf 100644 --- a/domain/wallets/models/build.gradle.kts +++ b/domain/wallets/models/build.gradle.kts @@ -1,5 +1,6 @@ plugins { alias(deps.plugins.kotlin.jvm) + alias(deps.plugins.kotlin.serialization) id("configuration") } @@ -11,4 +12,8 @@ dependencies { // region Domain modules implementation(project(":domain:models")) // endregion + + // region Other libraries + implementation(deps.kotlin.serialization) + // endregion } \ No newline at end of file diff --git a/domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/UserWalletId.kt b/domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/UserWalletId.kt index 970ee3ad9f..4ea8ab824c 100644 --- a/domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/UserWalletId.kt +++ b/domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/UserWalletId.kt @@ -2,9 +2,10 @@ package com.tangem.domain.wallets.models import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString -import java.io.Serializable +import kotlinx.serialization.Serializable -data class UserWalletId(val stringValue: String) : Serializable { +@Serializable +data class UserWalletId(val stringValue: String) { val value = stringValue.hexToBytes() From 084d4282f7c20c162408f5e9a26c646743d2f77a Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 11 Jun 2024 15:40:27 +0400 Subject: [PATCH 02/19] Updated on 2026-08-14 --- app/build.gradle.kts | 1 + common/routing/.gitignore | 1 + common/routing/build.gradle.kts | 25 +++ .../com/tangem/common/routing/AppRoute.kt | 164 ++++++++++++++++++ .../com/tangem/common/routing/AppRouter.kt | 42 +++++ .../common/routing/bundle/BundleDecoder.kt | 108 ++++++++++++ .../common/routing/bundle/BundleEncoder.kt | 107 ++++++++++++ .../common/routing/bundle/BundleUtils.kt | 60 +++++++ .../routing/bundle/RouteBundleParams.kt | 8 + .../routing/entity/SerializableBundle.kt | 24 +++ .../routing/entity/SerializableIntent.kt | 51 ++++++ .../common/routing/utils/AppRouterExt.kt | 16 ++ settings.gradle.kts | 1 + 13 files changed, 608 insertions(+) create mode 100644 common/routing/.gitignore create mode 100644 common/routing/build.gradle.kts create mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt create mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/AppRouter.kt create mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleDecoder.kt create mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleEncoder.kt create mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleUtils.kt create mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/bundle/RouteBundleParams.kt create mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableBundle.kt create mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt create mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/utils/AppRouterExt.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 72424b337b..3362aa5bbb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -76,6 +76,7 @@ dependencies { implementation(projects.domain.walletConnect) implementation(projects.common) + implementation(projects.common.routing) implementation(projects.core.analytics) implementation(projects.core.analytics.models) implementation(projects.core.navigation) diff --git a/common/routing/.gitignore b/common/routing/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/common/routing/.gitignore @@ -0,0 +1 @@ +/build diff --git a/common/routing/build.gradle.kts b/common/routing/build.gradle.kts new file mode 100644 index 0000000000..980934bf31 --- /dev/null +++ b/common/routing/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.serialization) + id("configuration") +} + +android { + namespace = "com.tangem.common.routing" +} + +dependencies { + /* Core */ + implementation(projects.core.decompose) + + /* Domain */ + implementation(projects.domain.qrScanning.models) + implementation(projects.domain.tokens.models) + implementation(projects.domain.wallets.models) + + /* Libs - Other */ + implementation(deps.androidx.core.ktx) + implementation(deps.kotlin.serialization) + implementation(deps.timber) +} \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt new file mode 100644 index 0000000000..2a337dbeaa --- /dev/null +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -0,0 +1,164 @@ +package com.tangem.common.routing + +import android.os.Bundle +import com.tangem.common.routing.bundle.RouteBundleParams +import com.tangem.common.routing.bundle.bundle +import com.tangem.common.routing.entity.SerializableIntent +import com.tangem.core.decompose.navigation.Route +import com.tangem.domain.qrscanning.models.SourceType +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.wallets.models.UserWalletId +import kotlinx.serialization.Serializable + +@Serializable +sealed class AppRoute(val path: String) : Route, RouteBundleParams { + + override fun getBundle(): Bundle = bundle(serializer()) + + @Serializable + data object Initial : AppRoute(path = "/initial") + + @Serializable + data object Home : AppRoute(path = "/home") + + @Serializable + data class Welcome( + val intent: SerializableIntent? = null, + ) : AppRoute(path = "/welcome") { + + companion object { + const val INITIAL_INTENT_KEY = "intent" + } + } + + @Serializable + data object Disclaimer : AppRoute(path = "/disclaimer") + + @Serializable + data object OnboardingNote : AppRoute(path = "/onboarding/note") + + @Serializable + data class OnboardingWallet( + val canSkipBackup: Boolean = true, + ) : AppRoute(path = "/onboarding/wallet${if (canSkipBackup) "/skippable" else ""}") { + + companion object { + const val CAN_SKIP_BACKUP_KEY = "canSkipBackup" + } + } + + @Serializable + data object OnboardingTwins : AppRoute(path = "/onboarding/twins") + + @Serializable + data object OnboardingOther : AppRoute(path = "/onboarding/other") + + @Serializable + data object Wallet : AppRoute(path = "/wallet") + + @Serializable + data class CurrencyDetails( + val userWalletId: UserWalletId, + val currency: CryptoCurrency, + ) : AppRoute(path = "/currency_details/${userWalletId.stringValue}/${currency.id.value}") { + + companion object { + const val USER_WALLET_ID_KEY = "userWalletId" + const val CRYPTO_CURRENCY_KEY = "currency" + } + } + + @Serializable + data class Send( + val userWalletId: UserWalletId, + val currency: CryptoCurrency, + val transactionId: String? = null, + val amount: String? = null, + val tag: String? = null, + val destinationAddress: String? = null, + ) : AppRoute( + path = "/send/${userWalletId.stringValue}/${currency.id.value}?" + + "&$transactionId" + + "&$amount" + + "&$tag" + + "&$destinationAddress", + ) { + companion object { + const val USER_WALLET_ID_KEY = "userWalletId" + const val CRYPTO_CURRENCY_KEY = "currency" + const val TRANSACTION_ID_KEY = "transactionId" + const val AMOUNT_KEY = "amount" + const val TAG_KEY = "tag" + const val DESTINATION_ADDRESS_KEY = "destinationAddress" + } + } + + @Serializable + data class Details( + val userWalletId: UserWalletId, + ) : AppRoute(path = "/details/${userWalletId.stringValue}") + + @Serializable + data object DetailsSecurity : AppRoute(path = "/details/security") + + @Serializable + data object CardSettings : AppRoute(path = "/card_settings") + + @Serializable + data object AppSettings : AppRoute(path = "/app_settings") + + @Serializable + data object ResetToFactory : AppRoute(path = "/reset_to_factory") + + @Serializable + data object AccessCodeRecovery : AppRoute(path = "/access_code_recovery") + + @Serializable + data object ManageTokens : AppRoute(path = "/manage_tokens") + + @Serializable + data object AddCustomToken : AppRoute(path = "/add_custom_token") + + @Serializable + data object WalletConnectSessions : AppRoute(path = "/wallet_connect_sessions") + + @Serializable + data class QrScanning( + val source: SourceType, + val networkName: String? = null, + ) : AppRoute(path = "/qr_scanning") { + + companion object { + const val SOURCE_KEY = "source" + const val NETWORK_KEY = "networkName" + } + } + + @Serializable + data object ReferralProgram : AppRoute(path = "/referral_program") + + @Serializable + data class Swap( + val currency: CryptoCurrency, + ) : AppRoute(path = "/swap") { + + companion object { + const val CURRENCY_BUNDLE_KEY = "currency" + } + } + + @Serializable + data object TesterMenu : AppRoute(path = "/tester_menu") + + @Serializable + data object SaveWallet : AppRoute(path = "/save_wallet") + + @Serializable + data object AppCurrencySelector : AppRoute(path = "/app_currency_selector") + + @Serializable + data object ModalNotification : AppRoute(path = "/modal_notification") + + @Serializable + data object Staking : AppRoute(path = "/staking") +} \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRouter.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRouter.kt new file mode 100644 index 0000000000..9e81df0ec5 --- /dev/null +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRouter.kt @@ -0,0 +1,42 @@ +package com.tangem.common.routing + +import kotlin.reflect.KClass + +/** + * Interface for a router in the application. + * It provides methods for navigating through the application. + * + * Same as [com.tangem.core.decompose.navigation.Router] but without Decompose dependency. + * + * ***Must be removed after Decompose migration.*** + */ +interface AppRouter { + + /** + * The current navigation stack. + */ + val backStack: List + + /** + * Pushes a new route to the navigation stack. + * + * @param route The route to push. + * @param onComplete The callback to be invoked when the operation is complete. + */ + fun push(route: AppRoute, onComplete: (isSuccess: Boolean) -> Unit = {}) + + /** + * Pops the top route from the navigation stack. + * + * @param onComplete The callback to be invoked when the operation is complete. + */ + fun pop(onComplete: (isSuccess: Boolean) -> Unit = {}) + + /** + * Pops routes from the navigation stack until the specified route class is found. + * + * @param routeClass The route class to pop to. + * @param onComplete The callback to be invoked when the operation is complete. + */ + fun popTo(routeClass: KClass, onComplete: (isSuccess: Boolean) -> Unit = {}) +} \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleDecoder.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleDecoder.kt new file mode 100644 index 0000000000..6a51004489 --- /dev/null +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleDecoder.kt @@ -0,0 +1,108 @@ +package com.tangem.common.routing.bundle + +import android.os.Bundle +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.StructureKind +import kotlinx.serialization.encoding.AbstractDecoder +import kotlinx.serialization.encoding.CompositeDecoder +import kotlinx.serialization.modules.SerializersModule + +@ExperimentalSerializationApi +internal class BundleDecoder( + private val bundle: Bundle, + private val elementsCount: Int = -1, + private val isInitializer: Boolean = true, + override val serializersModule: SerializersModule, +) : AbstractDecoder() { + + private var index = -1 + private var elementKey: String? = null + + override fun decodeElementIndex(descriptor: SerialDescriptor): Int { + if (++index >= elementsCount) { + return CompositeDecoder.DECODE_DONE + } + + elementKey = descriptor.getElementName(index) + return index + } + + override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder { + val b = if (isInitializer) { + bundle + } else { + requireNotNull(bundle.getBundle(elementKey)) { + "Bundle is missing for key $elementKey while decoding" + } + } + + val count = when (descriptor.kind) { + StructureKind.MAP, + StructureKind.LIST, + -> b.getInt("\$size") + else -> descriptor.elementsCount + } + + return BundleDecoder( + bundle = b, + elementsCount = count, + isInitializer = false, + serializersModule = serializersModule, + ) + } + + override fun endStructure(descriptor: SerialDescriptor) { + /* no-op */ + } + + override fun decodeBoolean(): Boolean { + return bundle.getBoolean(elementKey) + } + + override fun decodeByte(): Byte { + return bundle.getByte(elementKey) + } + + override fun decodeChar(): Char { + return bundle.getChar(elementKey) + } + + override fun decodeDouble(): Double { + return bundle.getDouble(elementKey) + } + + override fun decodeEnum(enumDescriptor: SerialDescriptor): Int { + return bundle.getInt(elementKey) + } + + override fun decodeFloat(): Float { + return bundle.getFloat(elementKey) + } + + override fun decodeInt(): Int { + return bundle.getInt(elementKey) + } + + override fun decodeLong(): Long { + return bundle.getLong(elementKey) + } + + override fun decodeNotNullMark(): Boolean { + return bundle.containsKey(elementKey) + } + + override fun decodeNull(): Nothing? { + return null + } + + override fun decodeShort(): Short { + return bundle.getShort(elementKey) + } + + override fun decodeString(): String { + return requireNotNull(bundle.getString(elementKey)) { + "String is missing for key $elementKey while decoding" + } + } +} \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleEncoder.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleEncoder.kt new file mode 100644 index 0000000000..e0be7b8aa3 --- /dev/null +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleEncoder.kt @@ -0,0 +1,107 @@ +package com.tangem.common.routing.bundle + +import android.os.Bundle +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.StructureKind +import kotlinx.serialization.encoding.AbstractEncoder +import kotlinx.serialization.encoding.CompositeEncoder +import kotlinx.serialization.modules.SerializersModule + +@ExperimentalSerializationApi +internal class BundleEncoder( + private val bundle: Bundle, + private val parentBundle: Bundle? = null, + private val keyInParent: String? = null, + private val isInitializer: Boolean = true, + override val serializersModule: SerializersModule, +) : AbstractEncoder() { + + private var elementKey: String? = null + + override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean { + elementKey = descriptor.getElementName(index) + return super.encodeElement(descriptor, index) + } + + override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder { + return if (isInitializer) { + BundleEncoder( + bundle = bundle, + parentBundle = null, + keyInParent = elementKey, + isInitializer = false, + serializersModule = serializersModule, + ) + } else { + BundleEncoder( + bundle = Bundle(), + parentBundle = bundle, + keyInParent = elementKey, + isInitializer = false, + serializersModule = serializersModule, + ) + } + } + + override fun endStructure(descriptor: SerialDescriptor) { + if (descriptor.kind in arrayOf(StructureKind.LIST, StructureKind.MAP)) { + val size = elementKey?.toIntOrNull()?.let { it + 1 } ?: 0 + bundle.putInt("\$size", size) + } + + if (keyInParent.isNullOrBlank()) { + return + } + + parentBundle?.putBundle(keyInParent, bundle) + } + + override fun encodeBoolean(value: Boolean) { + bundle.putBoolean(elementKey, value) + } + + override fun encodeByte(value: Byte) { + bundle.putByte(elementKey, value) + } + + override fun encodeChar(value: Char) { + bundle.putChar(elementKey, value) + } + + override fun encodeDouble(value: Double) { + bundle.putDouble(elementKey, value) + } + + override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) { + bundle.putInt(elementKey, index) + } + + override fun encodeFloat(value: Float) { + bundle.putFloat(elementKey, value) + } + + override fun encodeInt(value: Int) { + bundle.putInt(elementKey, value) + } + + override fun encodeLong(value: Long) { + bundle.putLong(elementKey, value) + } + + override fun encodeNull() { + /* no-op */ + } + + override fun encodeShort(value: Short) { + bundle.putShort(elementKey, value) + } + + override fun encodeString(value: String) { + bundle.putString(elementKey, value) + } + + override fun encodeNotNullMark() { + /* no-op */ + } +} \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleUtils.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleUtils.kt new file mode 100644 index 0000000000..4c638fde65 --- /dev/null +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/BundleUtils.kt @@ -0,0 +1,60 @@ +package com.tangem.common.routing.bundle + +import android.os.Bundle +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.modules.EmptySerializersModule +import kotlinx.serialization.modules.SerializersModule + +val defaultSerializersModule: SerializersModule = EmptySerializersModule() + +/** + * Deserialize this bundle into an object of type [T]. + * + * @receiver [Bundle] to deserialize. + * @param deserializer [DeserializationStrategy] of the [T] class. + * + * @return Object of type T deserialized from bundle. + */ +@OptIn(ExperimentalSerializationApi::class) +fun Bundle.unbundle( + deserializer: DeserializationStrategy, + serializersModule: SerializersModule = defaultSerializersModule, +): T { + val decoder = BundleDecoder( + bundle = this, + elementsCount = -1, + isInitializer = true, + serializersModule = serializersModule, + ) + + return deserializer.deserialize(decoder) +} + +/** + * Serialize [T] into a bundle. + * + * @receiver Object to serialize. + * @param serializer [SerializationStrategy] of the [T] class. + * + * @return bundle serialized from value + */ +@OptIn(ExperimentalSerializationApi::class) +fun T.bundle( + serializer: SerializationStrategy, + serializersModule: SerializersModule = defaultSerializersModule, +): Bundle { + val bundle = Bundle(serializer.descriptor.elementsCount) + val encoder = BundleEncoder( + bundle = bundle, + parentBundle = null, + keyInParent = null, + isInitializer = true, + serializersModule = serializersModule, + ) + + serializer.serialize(encoder, value = this) + + return bundle +} \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/RouteBundleParams.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/RouteBundleParams.kt new file mode 100644 index 0000000000..12ae0795d3 --- /dev/null +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/bundle/RouteBundleParams.kt @@ -0,0 +1,8 @@ +package com.tangem.common.routing.bundle + +import android.os.Bundle + +interface RouteBundleParams { + + fun getBundle(): Bundle +} \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableBundle.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableBundle.kt new file mode 100644 index 0000000000..fcac1b0422 --- /dev/null +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableBundle.kt @@ -0,0 +1,24 @@ +package com.tangem.common.routing.entity + +import android.os.Bundle +import kotlinx.serialization.Serializable + +@Serializable +data class SerializableBundle( + val map: Map, +) { + + constructor(bundle: Bundle) : this( + map = bundle.keySet().mapNotNull { key -> + bundle.getString(key)?.let { key to it } + }.toMap(), + ) + + fun toBundle(): Bundle { + return Bundle().apply { + map.forEach { (key, value) -> + putString(key, value) + } + } + } +} \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt new file mode 100644 index 0000000000..38ef6f2096 --- /dev/null +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt @@ -0,0 +1,51 @@ +package com.tangem.common.routing.entity + +import android.content.ComponentName +import android.content.Intent +import android.net.Uri +import kotlinx.serialization.Serializable + +@Serializable +data class SerializableIntent( + val action: String?, + val dataString: String?, + val categories: Set?, + val type: String?, + val packageValue: String?, + val component: String?, + val flags: Int, + val extras: SerializableBundle?, +) { + + constructor(intent: Intent) : this( + action = intent.action, + dataString = intent.dataString, + categories = intent.categories, + type = intent.type, + packageValue = intent.`package`, + component = intent.component?.flattenToString(), + flags = intent.flags, + extras = intent.extras?.let(::SerializableBundle), + ) + + fun toIntent(): Intent { + val intent = Intent() + + intent.action = action + intent.setDataAndType( + dataString?.let { Uri.parse(it) }, + type, + ) + categories?.let { categories -> + for (category in categories) { + intent.addCategory(category) + } + } + intent.`package` = packageValue + intent.component = component?.let { ComponentName.unflattenFromString(it) } + intent.flags = flags + extras?.let { intent.putExtras(it.toBundle()) } + + return intent + } +} \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/utils/AppRouterExt.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/utils/AppRouterExt.kt new file mode 100644 index 0000000000..1bc85f941f --- /dev/null +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/utils/AppRouterExt.kt @@ -0,0 +1,16 @@ +package com.tangem.common.routing.utils + +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter + +/** + * Pops routes from the navigation stack until the specified route [R] is found. + * + * ***Must be removed after Decompose migration.*** + * + * @param R The route to pop to. + * @param onComplete The callback to be invoked when the operation is complete. + */ +inline fun AppRouter.popTo(noinline onComplete: (isSuccess: Boolean) -> Unit = {}) { + popTo(R::class, onComplete) +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 914e2ac058..40841c6327 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -113,6 +113,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") include(":app") include(":common") include(":common:ui-charts") +include(":common:routing") // region Core modules include(":core:analytics") From 7a10191880a5b72e3558f881785e635df7fa5830 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 11 Jun 2024 15:52:15 +0400 Subject: [PATCH 03/19] Updated on 2026-08-14 --- .../tap/di/routing/RoutingComponentModule.kt | 18 ++ .../tangem/tap/routing/RoutingComponent.kt | 33 ++++ .../routing/impl/DefaultRoutingComponent.kt | 49 ++++++ .../tangem/tap/routing/utils/ChildFactory.kt | 155 ++++++++++++++++++ .../tangem/tap/routing/utils/RouteMappers.kt | 39 +++++ .../details/component/DetailsComponent.kt | 5 +- 6 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/di/routing/RoutingComponentModule.kt create mode 100644 app/src/main/java/com/tangem/tap/routing/RoutingComponent.kt create mode 100644 app/src/main/java/com/tangem/tap/routing/impl/DefaultRoutingComponent.kt create mode 100644 app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt create mode 100644 app/src/main/java/com/tangem/tap/routing/utils/RouteMappers.kt diff --git a/app/src/main/java/com/tangem/tap/di/routing/RoutingComponentModule.kt b/app/src/main/java/com/tangem/tap/di/routing/RoutingComponentModule.kt new file mode 100644 index 0000000000..aca0f1678a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/routing/RoutingComponentModule.kt @@ -0,0 +1,18 @@ +package com.tangem.tap.di.routing + +import com.tangem.tap.routing.RoutingComponent +import com.tangem.tap.routing.impl.DefaultRoutingComponent +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.scopes.ActivityScoped + +@Module +@InstallIn(ActivityComponent::class) +internal interface RoutingComponentModule { + + @Binds + @ActivityScoped + fun bindRoutingComponentFactory(factory: DefaultRoutingComponent.Factory): RoutingComponent.Factory +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/RoutingComponent.kt b/app/src/main/java/com/tangem/tap/routing/RoutingComponent.kt new file mode 100644 index 0000000000..a7f5148b19 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/routing/RoutingComponent.kt @@ -0,0 +1,33 @@ +package com.tangem.tap.routing + +import android.content.Intent +import androidx.fragment.app.Fragment +import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.decompose.value.Value +import com.tangem.common.routing.AppRoute +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.navigation.Router +import com.tangem.utils.Provider + +internal interface RoutingComponent { + + val router: Router + + val stack: Value> + + sealed class Child { + + data object Initial : Child() + + data class LegacyFragment( + val name: String, + val fragmentProvider: Provider, + ) : Child() + + data class LegacyIntent(val intent: Intent) : Child() + } + + interface Factory { + fun create(context: AppComponentContext): RoutingComponent + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/impl/DefaultRoutingComponent.kt b/app/src/main/java/com/tangem/tap/routing/impl/DefaultRoutingComponent.kt new file mode 100644 index 0000000000..ece22bfeb5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/routing/impl/DefaultRoutingComponent.kt @@ -0,0 +1,49 @@ +package com.tangem.tap.routing.impl + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.backhandler.BackCallback +import com.tangem.common.routing.AppRoute +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.context.childByContext +import com.tangem.core.decompose.navigation.getOrCreateTyped +import com.tangem.tap.routing.RoutingComponent +import com.tangem.tap.routing.RoutingComponent.Child +import com.tangem.tap.routing.utils.ChildFactory +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +@Suppress("LongParameterList") +internal class DefaultRoutingComponent @AssistedInject constructor( + @Assisted context: AppComponentContext, + private val childFactory: ChildFactory, +) : RoutingComponent, AppComponentContext by context { + + private val backCallback = BackCallback(priority = Int.MIN_VALUE, onBack = router::pop) + + override val stack: Value> = childStack( + source = navigationProvider.getOrCreateTyped(), + serializer = AppRoute.serializer(), + initialConfiguration = getInitialRoute(), + handleBackButton = false, + childFactory = ::child, + ) + + init { + backHandler.register(backCallback) + } + + private fun getInitialRoute(): AppRoute = AppRoute.Initial + + private fun child(route: AppRoute, context: ComponentContext): Child { + return childFactory.createChild(route, { childByContext(context) }) + } + + @AssistedFactory + interface Factory : RoutingComponent.Factory { + override fun create(context: AppComponentContext): DefaultRoutingComponent + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt new file mode 100644 index 0000000000..6e3ac96e92 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -0,0 +1,155 @@ +package com.tangem.tap.routing.utils + +import com.tangem.common.routing.AppRoute +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.feature.qrscanning.QrScanningRouter +import com.tangem.feature.referral.ReferralFragment +import com.tangem.feature.swap.presentation.SwapFragment +import com.tangem.features.details.component.DetailsComponent +import com.tangem.features.send.api.navigation.SendRouter +import com.tangem.features.staking.api.navigation.StakingRouter +import com.tangem.features.tokendetails.navigation.TokenDetailsRouter +import com.tangem.features.wallet.navigation.WalletRouter +import com.tangem.tap.features.customtoken.impl.presentation.AddCustomTokenFragment +import com.tangem.tap.features.details.ui.appcurrency.AppCurrencySelectorFragment +import com.tangem.tap.features.details.ui.appsettings.AppSettingsFragment +import com.tangem.tap.features.details.ui.cardsettings.CardSettingsFragment +import com.tangem.tap.features.details.ui.cardsettings.coderecovery.AccessCodeRecoveryFragment +import com.tangem.tap.features.details.ui.resetcard.ResetCardFragment +import com.tangem.tap.features.details.ui.securitymode.SecurityModeFragment +import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment +import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment +import com.tangem.tap.features.home.HomeFragment +import com.tangem.tap.features.main.ui.ModalNotificationBottomSheetFragment +import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment +import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment +import com.tangem.tap.features.onboarding.products.twins.ui.OnboardingTwinsFragment +import com.tangem.tap.features.onboarding.products.wallet.ui.OnboardingWalletFragment +import com.tangem.tap.features.saveWallet.ui.SaveWalletBottomSheetFragment +import com.tangem.tap.features.tokens.impl.presentation.TokensListFragment +import com.tangem.tap.features.welcome.ui.WelcomeFragment +import com.tangem.tap.routing.RoutingComponent.Child +import com.tangem.utils.Provider +import dagger.hilt.android.scopes.ActivityScoped +import java.util.WeakHashMap +import javax.inject.Inject + +@ActivityScoped +@Suppress("LongParameterList") +internal class ChildFactory @Inject constructor( + private val detailsComponentFactory: DetailsComponent.Factory, + private val sendRouter: SendRouter, + private val tokenDetailsRouter: TokenDetailsRouter, + private val walletRouter: WalletRouter, + private val qrScanningRouter: QrScanningRouter, + private val stakingRouter: StakingRouter, +) { + + @Suppress("LongMethod", "CyclomaticComplexMethod") + fun createChild(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child { + componentContexts[route] = contextFactory(route) + + return when (route) { + is AppRoute.Initial -> { + Child.Initial + } + is AppRoute.AccessCodeRecovery -> { + route.asFragmentChild(Provider { AccessCodeRecoveryFragment() }) + } + is AppRoute.AddCustomToken -> { + route.asFragmentChild(Provider { AddCustomTokenFragment() }) + } + is AppRoute.AppCurrencySelector -> { + route.asFragmentChild(Provider { AppCurrencySelectorFragment() }) + } + is AppRoute.ModalNotification -> { + route.asFragmentChild(Provider { ModalNotificationBottomSheetFragment() }) + } + is AppRoute.SaveWallet -> { + route.asFragmentChild(Provider { SaveWalletBottomSheetFragment() }) + } + is AppRoute.Send -> { + route.asFragmentChild(Provider { sendRouter.getEntryFragment() }) + } + is AppRoute.AppSettings -> { + route.asFragmentChild(Provider { AppSettingsFragment() }) + } + is AppRoute.CardSettings -> { + route.asFragmentChild(Provider { CardSettingsFragment() }) + } + is AppRoute.Details -> { + route.asComponentChild( + contextProvider = contextProvider(route, contextFactory), + params = DetailsComponent.Params(route.userWalletId), + componentFactory = detailsComponentFactory, + ) + } + is AppRoute.DetailsSecurity -> { + route.asFragmentChild(Provider { SecurityModeFragment() }) + } + is AppRoute.Disclaimer -> { + route.asFragmentChild(Provider { DisclaimerFragment() }) + } + is AppRoute.Home -> { + route.asFragmentChild(Provider { HomeFragment() }) + } + is AppRoute.ManageTokens -> { + route.asFragmentChild(Provider { TokensListFragment() }) + } + is AppRoute.OnboardingNote -> { + route.asFragmentChild(Provider { OnboardingNoteFragment() }) + } + is AppRoute.OnboardingOther -> { + route.asFragmentChild(Provider { OnboardingOtherCardsFragment() }) + } + is AppRoute.OnboardingTwins -> { + route.asFragmentChild(Provider { OnboardingTwinsFragment() }) + } + is AppRoute.OnboardingWallet -> { + route.asFragmentChild(Provider { OnboardingWalletFragment() }) + } + is AppRoute.QrScanning -> { + route.asFragmentChild(Provider { qrScanningRouter.getEntryFragment() }) + } + is AppRoute.ReferralProgram -> { + route.asFragmentChild(Provider { ReferralFragment() }) + } + is AppRoute.ResetToFactory -> { + route.asFragmentChild(Provider { ResetCardFragment() }) + } + is AppRoute.Swap -> { + route.asFragmentChild(Provider { SwapFragment() }) + } + is AppRoute.Wallet -> { + route.asFragmentChild(Provider { walletRouter.getEntryFragment() }) + } + is AppRoute.WalletConnectSessions -> { + route.asFragmentChild(Provider { WalletConnectFragment() }) + } + is AppRoute.CurrencyDetails -> { + route.asFragmentChild(Provider { tokenDetailsRouter.getEntryFragment() }) + } + is AppRoute.Welcome -> { + route.asFragmentChild(Provider { WelcomeFragment() }) + } + is AppRoute.TesterMenu -> { + TODO("Will be implemented later") + } + is AppRoute.Staking -> { + route.asFragmentChild(Provider { stakingRouter.getEntryFragment() }) + } + } + } + + private fun contextProvider( + appRoute: AppRoute, + contextFactory: (route: AppRoute) -> AppComponentContext, + ): Provider = Provider { + componentContexts.getOrPut(appRoute) { contextFactory(appRoute) } + } + + private companion object { + + val componentContexts = WeakHashMap() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/utils/RouteMappers.kt b/app/src/main/java/com/tangem/tap/routing/utils/RouteMappers.kt new file mode 100644 index 0000000000..c1203a4758 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/routing/utils/RouteMappers.kt @@ -0,0 +1,39 @@ +package com.tangem.tap.routing.utils + +import androidx.fragment.app.Fragment +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.bundle.RouteBundleParams +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.ComposableContentComponent +import com.tangem.tap.DecomposeFragment +import com.tangem.tap.routing.RoutingComponent.Child +import com.tangem.utils.Provider + +internal fun AppRoute.asFragmentChild(fragmentProvider: Provider): Child { + val provider = Provider { + val bundle = (this as? RouteBundleParams)?.getBundle() + + fragmentProvider().apply { + arguments = bundle + } + } + + return Child.LegacyFragment(path, provider) +} + +internal fun > AppRoute.asComponentChild( + contextProvider: Provider, + params: P, + componentFactory: F, +): Child { + val fragmentProvider = Provider { + DecomposeFragment.newInstance( + contextProvider = contextProvider, + params = params, + componentFactory = componentFactory, + ) + } + + return Child.LegacyFragment(path, fragmentProvider) +} \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt index 2aa9daebfe..da63581436 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt @@ -2,6 +2,7 @@ package com.tangem.features.details.component import androidx.compose.material3.SnackbarHostState import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.ComposableContentComponent import com.tangem.domain.wallets.models.UserWalletId @@ -9,9 +10,9 @@ interface DetailsComponent : ComposableContentComponent { val snackbarHostState: SnackbarHostState - interface Factory { + interface Factory : ComponentFactory { - fun create(context: AppComponentContext, params: Params): DetailsComponent + override fun create(context: AppComponentContext, params: Params): DetailsComponent } data class Params( From e4e18ffddd525ee6f2bbfddfd982cce8bed3eaa7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 11 Jun 2024 15:54:46 +0400 Subject: [PATCH 04/19] Updated on 2026-08-14 --- .../com/tangem/tap/ApplicationEntryPoint.kt | 3 + .../main/java/com/tangem/tap/MainActivity.kt | 41 +++++++++++++ .../java/com/tangem/tap/TangemApplication.kt | 5 ++ .../tap/common/extensions/Navigation.kt | 44 +++++++++++++ .../tangem/tap/di/routing/AppRouterModule.kt | 26 ++++++++ .../tap/proxy/redux/DaggerGraphState.kt | 2 + .../com/tangem/tap/routing/ProxyAppRouter.kt | 61 +++++++++++++++++++ .../routing/configurator/AppRouterConfig.kt | 12 ++++ .../configurator/MutableAppRouterConfig.kt | 12 ++++ common/routing/build.gradle.kts | 3 +- .../com/tangem/common/routing/AppRouter.kt | 20 +++++- 11 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/di/routing/AppRouterModule.kt create mode 100644 app/src/main/java/com/tangem/tap/routing/ProxyAppRouter.kt create mode 100644 app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt create mode 100644 app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt diff --git a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt index 45dae620de..6f45b6b45b 100644 --- a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt +++ b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt @@ -2,6 +2,7 @@ package com.tangem.tap import com.tangem.TangemSdkLogger import com.tangem.blockchainsdk.BlockchainSDKFactory +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.filter.OneTimeEventFilter import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.navigation.share.ShareManager @@ -116,4 +117,6 @@ interface ApplicationEntryPoint { fun getUrlOpener(): UrlOpener fun getShareManager(): ShareManager + + fun getAppRouter(): AppRouter } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 70192d4bb6..dfd4c4cc91 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -27,9 +27,13 @@ import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import arrow.core.getOrElse import by.kirich1409.viewbindingdelegate.viewBinding +import com.arkivanov.decompose.value.observe +import com.arkivanov.essenty.lifecycle.asEssentyLifecycle import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.di.RootAppComponentContext import com.tangem.core.deeplink.DeepLinksRegistry import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction @@ -60,6 +64,7 @@ import com.tangem.tap.common.DialogManager import com.tangem.tap.common.OnActivityResultCallback import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder +import com.tangem.tap.common.extensions.showFragmentAllowingStateLoss import com.tangem.tap.common.redux.NotificationsHandler import com.tangem.tap.domain.sdk.TangemSdkManager import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor @@ -72,6 +77,8 @@ import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction import com.tangem.tap.features.welcome.ui.WelcomeFragment import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.redux.DaggerGraphAction +import com.tangem.tap.routing.RoutingComponent +import com.tangem.tap.routing.configurator.AppRouterConfig import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler import com.tangem.wallet.BuildConfig import com.tangem.wallet.R @@ -162,6 +169,16 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac @Inject lateinit var stakingRouter: StakingRouter + @Inject + @RootAppComponentContext + internal lateinit var rootComponentContext: AppComponentContext + + @Inject + internal lateinit var appRouterConfig: AppRouterConfig + + @Inject + internal lateinit var routingComponentFactory: RoutingComponent.Factory + internal val viewModel: MainViewModel by viewModels() private lateinit var appThemeModeFlow: SharedFlow @@ -188,6 +205,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac observeAppThemeModeUpdates() setContentView(R.layout.activity_main) + installRouting() initContent() checkForNotificationPermission() @@ -199,6 +217,29 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac } } + private fun installRouting() { + val routingComponent = routingComponentFactory.create( + context = rootComponentContext, + ) + + appRouterConfig.routerScope = lifecycleScope + appRouterConfig.componentRouter = routingComponent.router + + routingComponent.stack.observe(lifecycle.asEssentyLifecycle()) { childStack -> + appRouterConfig.baclStack = childStack.backStack.map { it.configuration } + + when (val child = childStack.active.instance) { + is RoutingComponent.Child.Initial -> Unit + is RoutingComponent.Child.LegacyFragment -> { + supportFragmentManager.showFragmentAllowingStateLoss(child.name, child.fragmentProvider) + } + is RoutingComponent.Child.LegacyIntent -> { + startActivity(child.intent) + } + } + } + } + private fun observeStateUpdates() { viewModel.state .flowWithLifecycle(lifecycle) diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index 414c48552b..5ecb0622ca 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -13,6 +13,7 @@ import com.tangem.LogFormat import com.tangem.TangemSdkLogger import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder import com.tangem.blockchainsdk.BlockchainSDKFactory +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.filter.OneTimeEventFilter import com.tangem.core.featuretoggle.manager.FeatureTogglesManager @@ -193,6 +194,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { private val shareManager get() = entryPoint.getShareManager() + + private val appRouter: AppRouter + get() = entryPoint.getAppRouter() // endregion override fun onCreate() { @@ -284,6 +288,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { detailsEntryPoint = detailsEntryPoint, urlOpener = urlOpener, shareManager = shareManager, + appRouter = appRouter, ), ), ) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 70631857fc..8282d27be6 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -28,9 +28,53 @@ import com.tangem.tap.features.tokens.impl.presentation.TokensListFragment import com.tangem.tap.features.welcome.ui.WelcomeFragment import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store +import com.tangem.utils.Provider import com.tangem.wallet.R import timber.log.Timber +fun FragmentManager.showFragmentAllowingStateLoss(name: String, fragmentProvider: Provider) { + Timber.d("Showing $name route") + + val isPoppedBack = popBackStackImmediate(name, 0) + + if (!isPoppedBack) { + val fragment = fragmentProvider() + + if (fragment is DialogFragment) { + fragment.showDialog(fragmentManager = this, name) + } else { + fragment.showFragment(fragmentManager = this, name) + } + + Timber.d("Route $name is shown") + } else { + Timber.d("Route $name is found in backstack and shown") + } +} + +private fun DialogFragment.showDialog(fragmentManager: FragmentManager, name: String) { + val transaction = fragmentManager.beginTransaction() + + try { + transaction.addToBackStack(name) + show(transaction, name) + } catch (e: IllegalStateException) { + transaction.add(this, name) + transaction.addToBackStack(name) + + transaction.commitAllowingStateLoss() + } +} + +private fun Fragment.showFragment(fragmentManager: FragmentManager, name: String) { + val transaction = fragmentManager.beginTransaction() + + transaction.replace(R.id.fragment_container, this, name) + transaction.addToBackStack(name) + + transaction.commitAllowingStateLoss() +} + fun FragmentActivity.openFragment( screen: AppScreen, addToBackstack: Boolean, diff --git a/app/src/main/java/com/tangem/tap/di/routing/AppRouterModule.kt b/app/src/main/java/com/tangem/tap/di/routing/AppRouterModule.kt new file mode 100644 index 0000000000..4f4aa7e79b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/routing/AppRouterModule.kt @@ -0,0 +1,26 @@ +package com.tangem.tap.di.routing + +import com.tangem.common.routing.AppRouter +import com.tangem.tap.routing.ProxyAppRouter +import com.tangem.tap.routing.configurator.AppRouterConfig +import com.tangem.tap.routing.configurator.MutableAppRouterConfig +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object AppRouterModule { + + @Provides + @Singleton + fun provideAppRouter(config: AppRouterConfig, dispatchers: CoroutineDispatcherProvider): AppRouter = + ProxyAppRouter(config, dispatchers) + + @Provides + @Singleton + fun provideAppRouterConfigurator(): AppRouterConfig = MutableAppRouterConfig() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 08d6c0997f..08744d3939 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -2,6 +2,7 @@ package com.tangem.tap.proxy.redux import com.tangem.TangemSdkLogger import com.tangem.blockchainsdk.BlockchainSDKFactory +import com.tangem.common.routing.AppRouter import com.tangem.core.navigation.email.EmailSender import com.tangem.core.navigation.share.ShareManager import com.tangem.core.navigation.url.UrlOpener @@ -85,4 +86,5 @@ data class DaggerGraphState( val stakingRouter: StakingRouter? = null, val urlOpener: UrlOpener? = null, val shareManager: ShareManager? = null, + val appRouter: AppRouter? = null, ) : StateType \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/ProxyAppRouter.kt b/app/src/main/java/com/tangem/tap/routing/ProxyAppRouter.kt new file mode 100644 index 0000000000..aca4ce8efb --- /dev/null +++ b/app/src/main/java/com/tangem/tap/routing/ProxyAppRouter.kt @@ -0,0 +1,61 @@ +package com.tangem.tap.routing + +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter +import com.tangem.core.decompose.navigation.Router +import com.tangem.tap.routing.configurator.AppRouterConfig +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kotlin.reflect.KClass + +internal class ProxyAppRouter( + private val config: AppRouterConfig, + private val dispatchers: CoroutineDispatcherProvider, +) : AppRouter { + + private val routerScope: CoroutineScope + get() = requireNotNull(config.routerScope) { + "Router scope is not set in config" + } + + private val innerRouter: Router + get() = requireNotNull(config.componentRouter) { + "Inner router is not set in config" + } + + override val stack: List + get() = requireNotNull(config.stack) { + "Stack is not set in config" + } + + override fun push(route: AppRoute, onComplete: (isSuccess: Boolean) -> Unit) { + routerScope.launch(dispatchers.mainImmediate) { + innerRouter.push(route, onComplete) + } + } + + override fun replaceAll(vararg routes: AppRoute, onComplete: (isSuccess: Boolean) -> Unit) { + routerScope.launch(dispatchers.mainImmediate) { + innerRouter.replaceAll(*routes, onComplete = onComplete) + } + } + + override fun pop(onComplete: (isSuccess: Boolean) -> Unit) { + routerScope.launch(dispatchers.mainImmediate) { + innerRouter.pop(onComplete) + } + } + + override fun popTo(route: AppRoute, onComplete: (isSuccess: Boolean) -> Unit) { + routerScope.launch(dispatchers.mainImmediate) { + innerRouter.popTo(route, onComplete) + } + } + + override fun popTo(routeClass: KClass, onComplete: (isSuccess: Boolean) -> Unit) { + routerScope.launch(dispatchers.mainImmediate) { + innerRouter.popTo(routeClass, onComplete) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt b/app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt new file mode 100644 index 0000000000..470c85f674 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt @@ -0,0 +1,12 @@ +package com.tangem.tap.routing.configurator + +import com.tangem.common.routing.AppRoute +import com.tangem.core.decompose.navigation.Router +import kotlinx.coroutines.CoroutineScope + +internal interface AppRouterConfig { + + var routerScope: CoroutineScope? + var componentRouter: Router? + var stack: List? +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt b/app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt new file mode 100644 index 0000000000..ae724ac04e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt @@ -0,0 +1,12 @@ +package com.tangem.tap.routing.configurator + +import com.tangem.common.routing.AppRoute +import com.tangem.core.decompose.navigation.Router +import kotlinx.coroutines.CoroutineScope + +internal class MutableAppRouterConfig : AppRouterConfig { + + override var routerScope: CoroutineScope? = null + override var componentRouter: Router? = null + override var stack: List? = null +} \ No newline at end of file diff --git a/common/routing/build.gradle.kts b/common/routing/build.gradle.kts index 980934bf31..ae4552a1f0 100644 --- a/common/routing/build.gradle.kts +++ b/common/routing/build.gradle.kts @@ -17,9 +17,10 @@ dependencies { implementation(projects.domain.qrScanning.models) implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) + implementation(projects.domain.staking.models) /* Libs - Other */ + api(deps.kotlin.serialization) implementation(deps.androidx.core.ktx) - implementation(deps.kotlin.serialization) implementation(deps.timber) } \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRouter.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRouter.kt index 9e81df0ec5..cb0f21a793 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRouter.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRouter.kt @@ -15,7 +15,7 @@ interface AppRouter { /** * The current navigation stack. */ - val backStack: List + val stack: List /** * Pushes a new route to the navigation stack. @@ -25,6 +25,14 @@ interface AppRouter { */ fun push(route: AppRoute, onComplete: (isSuccess: Boolean) -> Unit = {}) + /** + * Replaces ***all*** routes in the navigation stack with the specified [routes]. + * + * @param routes The routes to replace the current stack with. + * @param onComplete The callback to be invoked when the operation is complete. + */ + fun replaceAll(vararg routes: AppRoute, onComplete: (isSuccess: Boolean) -> Unit = {}) + /** * Pops the top route from the navigation stack. * @@ -33,7 +41,15 @@ interface AppRouter { fun pop(onComplete: (isSuccess: Boolean) -> Unit = {}) /** - * Pops routes from the navigation stack until the specified route class is found. + * Pops routes from the navigation stack until the specified [route] is found. + * + * @param route The route to pop to. + * @param onComplete The callback to be invoked when the operation is complete. + */ + fun popTo(route: AppRoute, onComplete: (isSuccess: Boolean) -> Unit = {}) + + /** + * Pops routes from the navigation stack until the ***first*** specified [routeClass] is found. * * @param routeClass The route class to pop to. * @param onComplete The callback to be invoked when the operation is complete. From e5e35519b94981bd54c14b6413358969733af705 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 12:29:33 +0400 Subject: [PATCH 05/19] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 19 +++---- .../com/tangem/tap/common/extensions/Store.kt | 5 ++ .../domain/scanCard/LegacyScanProcessor.kt | 12 ++-- .../domain/scanCard/UseCaseScanProcessor.kt | 9 ++- .../chains/CheckForOnboardingChain.kt | 8 +-- .../domain/scanCard/chains/DisclaimerChain.kt | 4 +- .../scanCard/chains/ScanChainException.kt | 4 +- .../com/tangem/tap/features/BaseFragment.kt | 5 +- .../routers/DefaultCustomTokenRouter.kt | 12 ++-- .../details/redux/DetailsMiddleware.kt | 33 ++++++----- .../features/details/redux/DetailsReducer.kt | 6 +- .../walletconnect/WalletConnectMiddleware.kt | 17 ++---- .../AppCurrencySelectorViewModel.kt | 9 +-- .../ui/appsettings/AppSettingsFragment.kt | 6 +- .../ui/appsettings/AppSettingsViewModel.kt | 7 ++- .../ui/cardsettings/CardSettingsFragment.kt | 6 +- .../AccessCodeRecoveryFragment.kt | 5 +- .../details/ui/details/DetailsFragment.kt | 5 +- .../details/ui/details/DetailsViewModel.kt | 22 ++++---- .../details/ui/resetcard/ResetCardFragment.kt | 5 +- .../ui/resetcard/ResetCardViewModel.kt | 10 ++-- .../ui/securitymode/SecurityModeFragment.kt | 5 +- .../ui/walletconnect/WalletConnectFragment.kt | 5 +- .../dialogs/ClipboardOrScanQrDialog.kt | 17 ++---- .../disclaimer/redux/DisclaimerAction.kt | 3 +- .../disclaimer/redux/DisclaimerMiddleware.kt | 11 ++-- .../disclaimer/redux/DisclaimerReducer.kt | 2 +- .../disclaimer/redux/DisclaimerSource.kt | 5 ++ .../disclaimer/redux/DisclaimerState.kt | 4 +- .../disclaimer/ui/DisclaimerFragment.kt | 12 ++-- .../tangem/tap/features/home/HomeFragment.kt | 7 ++- .../tap/features/home/redux/HomeMiddleware.kt | 9 ++- .../tangem/tap/features/main/MainViewModel.kt | 10 ++-- .../features/onboarding/OnboardingHelper.kt | 20 +++---- .../note/redux/OnboardingNoteMiddleware.kt | 5 +- .../twins/redux/TwinCardsMiddleware.kt | 19 ++++--- .../redux/OnboardingWalletMiddleware.kt | 20 +++---- .../wallet/ui/OnboardingWalletFragment.kt | 4 +- .../saveWallet/redux/SaveWalletMiddleware.kt | 30 +++++----- .../send/redux/middlewares/SendMiddleware.kt | 6 +- .../tap/features/send/ui/SendFragment.kt | 21 +++---- .../tap/features/send/ui/SendViewModel.kt | 4 +- .../router/DefaultTokensListRouter.kt | 12 ++-- .../viewmodels/TokensListMigration.kt | 8 +-- .../viewmodels/TokensListViewModel.kt | 8 +-- .../middlewares/TradeCryptoMiddleware.kt | 56 +++++++++---------- .../welcome/redux/WelcomeMiddleware.kt | 11 ++-- .../features/welcome/ui/WelcomeFragment.kt | 4 -- .../features/welcome/ui/WelcomeViewModel.kt | 10 +++- 49 files changed, 266 insertions(+), 271 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerSource.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index dfd4c4cc91..ed2a8d35af 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -19,7 +19,6 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat -import androidx.core.os.bundleOf import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat import androidx.lifecycle.Lifecycle @@ -31,11 +30,12 @@ import com.arkivanov.decompose.value.observe import com.arkivanov.essenty.lifecycle.asEssentyLifecycle import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.entity.SerializableIntent import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.di.RootAppComponentContext import com.tangem.core.deeplink.DeepLinksRegistry -import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.email.EmailSender import com.tangem.core.ui.event.StateEvent @@ -64,6 +64,7 @@ import com.tangem.tap.common.DialogManager import com.tangem.tap.common.OnActivityResultCallback import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.extensions.showFragmentAllowingStateLoss import com.tangem.tap.common.redux.NotificationsHandler import com.tangem.tap.domain.sdk.TangemSdkManager @@ -74,7 +75,6 @@ import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHan import com.tangem.tap.features.main.MainViewModel import com.tangem.tap.features.main.model.Toast import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction -import com.tangem.tap.features.welcome.ui.WelcomeFragment import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.redux.DaggerGraphAction import com.tangem.tap.routing.RoutingComponent @@ -518,16 +518,11 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac private fun navigateToInitialScreen(intentWhichStartedActivity: Intent?) { if (userWalletsListManager.isLockable && userWalletsListManager.hasUserWallets) { - store.dispatch( - NavigationAction.NavigateTo( - screen = AppScreen.Welcome, - bundle = intentWhichStartedActivity?.let { - bundleOf(WelcomeFragment.INITIAL_INTENT_KEY to it) - }, - ), - ) + store.dispatchNavigationAction { + push(AppRoute.Welcome(intentWhichStartedActivity?.let(::SerializableIntent))) + } } else { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Home)) + store.dispatchNavigationAction { push(AppRoute.Home) } lifecycleScope.launch { intentProcessor.handleIntent(intentWhichStartedActivity, false) } diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt index 6c7ab7e524..27fc3c3bc3 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt @@ -1,5 +1,6 @@ package com.tangem.tap.common.extensions +import com.tangem.common.routing.AppRouter import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.redux.StateDialog import com.tangem.domain.wallets.models.UserWallet @@ -89,6 +90,10 @@ fun Store.dispatchShare(url: String) { inject(DaggerGraphState::shareManager).shareText(url) } +fun Store.dispatchNavigationAction(action: AppRouter.() -> Unit) { + inject(DaggerGraphState::appRouter).action() +} + inline fun Store.inject(getDependency: DaggerGraphState.() -> T?): T { return requireNotNull(state.daggerGraphState.getDependency()) { "${T::class.simpleName} isn't initialized " diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt index ce649ece46..0bbdc3f5f4 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt @@ -5,12 +5,11 @@ import com.tangem.common.core.TangemError import com.tangem.common.core.TangemSdkError import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess +import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.Basic -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction import com.tangem.domain.common.TapWorkarounds.canSkipBackup import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.twinsIsTwinned @@ -21,6 +20,7 @@ import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.disclaimer.createDisclaimer import com.tangem.tap.features.disclaimer.redux.DisclaimerAction import com.tangem.tap.features.disclaimer.redux.DisclaimerCallback +import com.tangem.tap.features.disclaimer.redux.DisclaimerSource import com.tangem.tap.features.onboarding.OnboardingHelper import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep @@ -118,7 +118,7 @@ internal object LegacyScanProcessor { disclaimerWillShow() store.dispatchWithMain( DisclaimerAction.Show( - fromScreen = AppScreen.Home, + from = DisclaimerSource.Home, callback = DisclaimerCallback( onAccept = { scope.launch(Dispatchers.Main) { @@ -170,7 +170,7 @@ internal object LegacyScanProcessor { if (scanResponse.twinsIsTwinned() && !wasTwinsOnboardingShown) { onWalletNotCreated() store.dispatchOnMain(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(scanResponse))) - navigateTo(AppScreen.OnboardingTwins) { onProgressStateChange(it) } + navigateTo(AppRoute.OnboardingTwins) { onProgressStateChange(it) } } else { delay(DELAY_SDK_DIALOG_CLOSE) onSuccess(scanResponse) @@ -178,9 +178,9 @@ internal object LegacyScanProcessor { } } - private suspend inline fun navigateTo(screen: AppScreen, onProgressStateChange: (showProgress: Boolean) -> Unit) { + private suspend inline fun navigateTo(route: AppRoute, onProgressStateChange: (showProgress: Boolean) -> Unit) { delay(DELAY_SDK_DIALOG_CLOSE) - store.dispatchOnMain(NavigationAction.NavigateTo(screen)) + store.dispatchNavigationAction { push(route) } onProgressStateChange(false) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt index bdb9786c3c..10646e2bfa 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt @@ -3,16 +3,15 @@ package com.tangem.tap.domain.scanCard import arrow.fx.coroutines.resourceScope import com.tangem.common.CompletionResult import com.tangem.common.core.TangemError +import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.Basic -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction import com.tangem.domain.card.ScanCardException import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.redux.StateDialog import com.tangem.tap.common.extensions.dispatchDialogShow -import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.extensions.inject import com.tangem.tap.domain.scanCard.chains.* import com.tangem.tap.domain.scanCard.utils.ScanCardExceptionConverter @@ -132,8 +131,8 @@ internal object UseCaseScanProcessor { action() } - private suspend inline fun navigateTo(screen: AppScreen) { + private suspend inline fun navigateTo(route: AppRoute) { delay(DELAY_SDK_DIALOG_CLOSE) - store.dispatchOnMain(NavigationAction.NavigateTo(screen)) + store.dispatchNavigationAction { push(route) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForOnboardingChain.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForOnboardingChain.kt index 66f708e28d..72d0b6ee59 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForOnboardingChain.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForOnboardingChain.kt @@ -2,8 +2,8 @@ package com.tangem.tap.domain.scanCard.chains import arrow.core.left import arrow.core.right +import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen import com.tangem.domain.card.ScanCardException import com.tangem.domain.common.TapWorkarounds.canSkipBackup import com.tangem.domain.common.util.twinsIsTwinned @@ -55,8 +55,8 @@ class CheckForOnboardingChain( canSkipBackup = previousChainResult.card.canSkipBackup, ), ) - val appScreen = OnboardingHelper.whereToNavigate(previousChainResult) - ScanChainException.OnboardingNeeded(appScreen).left() + val route = OnboardingHelper.whereToNavigate(previousChainResult) + ScanChainException.OnboardingNeeded(route).left() } else -> { Analytics.setContext(previousChainResult) @@ -69,7 +69,7 @@ class CheckForOnboardingChain( store.dispatchOnMain( TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(previousChainResult)), ) - ScanChainException.OnboardingNeeded(AppScreen.OnboardingTwins).left() + ScanChainException.OnboardingNeeded(AppRoute.OnboardingTwins).left() } else { delay(DELAY_SDK_DIALOG_CLOSE) previousChainResult.right() diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/DisclaimerChain.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/DisclaimerChain.kt index 35ad2efbab..8aa003b952 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/DisclaimerChain.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/DisclaimerChain.kt @@ -2,7 +2,6 @@ package com.tangem.tap.domain.scanCard.chains import arrow.core.left import arrow.core.right -import com.tangem.core.navigation.AppScreen import com.tangem.domain.card.ScanCardException import com.tangem.domain.core.chain.Chain import com.tangem.domain.core.chain.ResultChain @@ -13,6 +12,7 @@ import com.tangem.tap.features.disclaimer.Disclaimer import com.tangem.tap.features.disclaimer.createDisclaimer import com.tangem.tap.features.disclaimer.redux.DisclaimerAction import com.tangem.tap.features.disclaimer.redux.DisclaimerCallback +import com.tangem.tap.features.disclaimer.redux.DisclaimerSource import kotlinx.coroutines.suspendCancellableCoroutine import org.rekotlin.Store import kotlin.coroutines.resume @@ -50,7 +50,7 @@ internal class DisclaimerChain( return suspendCancellableCoroutine { continuation -> store.dispatchOnMain( DisclaimerAction.Show( - fromScreen = AppScreen.Home, + from = DisclaimerSource.Home, callback = DisclaimerCallback( onAccept = { if (continuation.isActive) { diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanChainException.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanChainException.kt index ae5e9f8ad3..b36fb89f0e 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanChainException.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanChainException.kt @@ -1,6 +1,6 @@ package com.tangem.tap.domain.scanCard.chains -import com.tangem.core.navigation.AppScreen +import com.tangem.common.routing.AppRoute import com.tangem.domain.card.ScanCardException sealed class ScanChainException : ScanCardException.ChainException() { @@ -19,5 +19,5 @@ sealed class ScanChainException : ScanCardException.ChainException() { * * @param onboardingRoute route where to navigate * */ - data class OnboardingNeeded(val onboardingRoute: AppScreen) : ScanChainException() + data class OnboardingNeeded(val onboardingRoute: AppRoute) : ScanChainException() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/BaseFragment.kt b/app/src/main/java/com/tangem/tap/features/BaseFragment.kt index d9e668bf04..6321957ee1 100644 --- a/app/src/main/java/com/tangem/tap/features/BaseFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/BaseFragment.kt @@ -14,7 +14,8 @@ import androidx.lifecycle.Lifecycle import androidx.transition.TransitionInflater import com.google.android.material.snackbar.Snackbar import com.tangem.common.extensions.VoidCallback -import com.tangem.core.navigation.NavigationAction +import com.tangem.common.routing.AppRouter +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.store import com.tangem.wallet.R @@ -36,7 +37,7 @@ abstract class BaseFragment(layoutId: Int) : Fragment(layoutId), FragmentOnBackP } override fun handleOnBackPressed() { - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/DefaultCustomTokenRouter.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/DefaultCustomTokenRouter.kt index 6391cc840f..858d49c31c 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/DefaultCustomTokenRouter.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/DefaultCustomTokenRouter.kt @@ -1,9 +1,11 @@ package com.tangem.tap.features.customtoken.impl.presentation.routers import com.tangem.blockchain.common.Blockchain -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter +import com.tangem.common.routing.utils.popTo import com.tangem.tap.common.extensions.dispatchDialogShow +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.store import com.tangem.wallet.R @@ -12,11 +14,11 @@ import com.tangem.wallet.R internal class DefaultCustomTokenRouter : CustomTokenRouter { override fun popBackStack() { - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) } override fun openWalletScreen() { - store.dispatch(NavigationAction.PopBackTo(screen = AppScreen.Wallet)) + store.dispatchNavigationAction { popTo() } } override fun openUnsupportedNetworkAlert(blockchain: Blockchain) { @@ -32,7 +34,7 @@ internal class DefaultCustomTokenRouter : CustomTokenRouter { val alert = AppDialog.SimpleOkDialogRes( headerId = R.string.common_error, messageId = R.string.common_unknown_error, - onOk = { store.dispatch(NavigationAction.PopBackTo()) }, + onOk = { store.dispatchNavigationAction(AppRouter::pop) }, ) store.dispatchDialogShow(alert) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index a9f6bdca6d..d0ce3c2f74 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -5,9 +5,11 @@ import com.tangem.common.* import com.tangem.common.core.TangemError import com.tangem.common.core.TangemSdkError import com.tangem.common.core.UserCodeRequestPolicy +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter +import com.tangem.common.routing.utils.popTo import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction + import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference @@ -70,7 +72,8 @@ class DetailsMiddleware { is DetailsAction.AppSettings -> appSettingsMiddleware.handle(state, action) is DetailsAction.ReCreateTwinsWallet -> { store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet)) - store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins)) + + store.dispatchNavigationAction { push(AppRoute.OnboardingTwins) } } is DetailsAction.AccessCodeRecovery -> accessCodeRecoveryMiddleware.handle(state, action) is DetailsAction.ScanCard -> scanCard(state) @@ -90,7 +93,7 @@ class DetailsMiddleware { store.dispatch(DetailsAction.ReCreateTwinsWallet) return } else { - store.dispatch(NavigationAction.NavigateTo(AppScreen.ResetToFactory)) + store.dispatchNavigationAction { push(AppRoute.ResetToFactory) } } } is DetailsAction.ResetToFactory.Proceed -> { @@ -131,15 +134,15 @@ class DetailsMiddleware { val selectedUserWallet = userWalletsListManager.selectedUserWalletSync if (selectedUserWallet != null) { - store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet)) + store.dispatchNavigationAction { popTo() } store.onUserWalletSelected(selectedUserWallet) } else { val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync } .fold(onSuccess = { true }, onFailure = { false }) if (isLocked && userWalletsListManager.hasUserWallets) { - store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome)) + store.dispatchNavigationAction { popTo() } } else { - store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home)) + store.dispatchNavigationAction { popTo() } } } } @@ -163,7 +166,7 @@ class DetailsMiddleware { fun handle(action: DetailsAction.ManageSecurity, detailsState: DetailsState) { when (action) { is DetailsAction.ManageSecurity.OpenSecurity -> { - store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsSecurity)) + store.dispatchNavigationAction { push(AppRoute.DetailsSecurity) } } is DetailsAction.ManageSecurity.SaveChanges -> { val cardSettingsState = detailsState.cardSettingsState @@ -182,7 +185,7 @@ class DetailsMiddleware { is CompletionResult.Success -> { Analytics.send(Settings.CardSettings.SecurityModeChanged(paramValue)) store.dispatch(GlobalAction.UpdateSecurityOptions(selectedOption)) - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Success) } is CompletionResult.Failure -> { @@ -392,7 +395,7 @@ class DetailsMiddleware { deleteSavedAccessCodes() store.inject(DaggerGraphState::walletsRepository).saveShouldSaveUserWallets(item = false) - store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Home)) + store.dispatchNavigationAction { popTo() } return CompletionResult.Success(Unit) } @@ -431,7 +434,7 @@ class DetailsMiddleware { when (action) { is DetailsAction.AccessCodeRecovery.Open -> { Analytics.send(Settings.CardSettings.AccessCodeRecoveryButton()) - store.dispatch(NavigationAction.NavigateTo(AppScreen.AccessCodeRecovery)) + store.dispatchNavigationAction { push(AppRoute.AccessCodeRecovery) } } is DetailsAction.AccessCodeRecovery.SaveChanges -> { scope.launch { @@ -443,7 +446,7 @@ class DetailsMiddleware { AnalyticsParam.AccessCodeRecoveryStatus.from(action.enabled), ), ) - store.dispatchOnMain(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) store.dispatchOnMain( DetailsAction.AccessCodeRecovery.SaveChanges.Success(action.enabled), ) @@ -502,7 +505,7 @@ class DetailsMiddleware { store.dispatchWithMain(DetailsAction.ScanAndSaveUserWallet.Success) }, disclaimerWillShow = { - store.dispatchOnMain(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) }, onSuccess = { scanResponse -> createUserWallet(scanResponse) @@ -546,7 +549,7 @@ class DetailsMiddleware { store.onUserWalletSelected(userWallet) store.dispatchWithMain(DetailsAction.ScanAndSaveUserWallet.Success) - store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Wallet)) + store.dispatchNavigationAction { popTo() } } .doOnFailure { error -> if (error is UserWalletsListError.WalletAlreadySaved) { @@ -554,7 +557,7 @@ class DetailsMiddleware { store.onUserWalletSelected(userWallet) store.dispatchWithMain(DetailsAction.ScanAndSaveUserWallet.Success) - store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Wallet)) + store.dispatchNavigationAction { popTo() } } else { Timber.e(error, "Unable to create user wallet") handleError(error, prevUseBiometricsForAccessCode) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index d8f6637f7f..d89b862c1b 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -1,6 +1,6 @@ package com.tangem.tap.features.details.redux -import com.tangem.core.navigation.AppScreen +import com.tangem.common.routing.AppRoute import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver @@ -69,10 +69,12 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { } private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: AppState): DetailsState { + val router = store.inject(DaggerGraphState::appRouter) + return DetailsState( scanResponse = action.scanResponse, // If the current screen is ResetToFactory, we must save the ResetToFactory's state - cardSettingsState = if (store.state.navigationState.backStack.lastOrNull() == AppScreen.ResetToFactory) { + cardSettingsState = if (router.backStack.lastOrNull() is AppRoute.ResetToFactory) { state.detailsState.cardSettingsState } else { null diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index b4917ceabb..f55288968a 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -1,19 +1,17 @@ package com.tangem.tap.features.details.redux.walletconnect -import androidx.core.os.bundleOf import com.tangem.blockchain.common.WalletManager import com.tangem.blockchainsdk.utils.toNetworkId -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction +import com.tangem.common.routing.AppRoute import com.tangem.domain.qrscanning.models.SourceType -import com.tangem.feature.qrscanning.QrScanningRouter +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.inject import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository +import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest import com.tangem.tap.domain.walletconnect2.domain.models.Account import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError @@ -62,14 +60,7 @@ class WalletConnectMiddleware { // TODO check store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri)) } else { - store.dispatchOnMain( - NavigationAction.NavigateTo( - screen = AppScreen.QrScanning, - bundle = bundleOf( - QrScanningRouter.SOURCE_KEY to SourceType.WALLET_CONNECT, - ), - ), - ) + store.dispatchNavigationAction { push(AppRoute.QrScanning(SourceType.WALLET_CONNECT)) } } } is WalletConnectAction.ShowClipboardOrScanQrDialog -> { diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appcurrency/AppCurrencySelectorViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appcurrency/AppCurrencySelectorViewModel.kt index 7b07e7d471..41d27d02a9 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appcurrency/AppCurrencySelectorViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appcurrency/AppCurrencySelectorViewModel.kt @@ -2,8 +2,9 @@ package com.tangem.tap.features.details.ui.appcurrency import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.core.navigation.ReduxNavController + import com.tangem.domain.appcurrency.GetAvailableCurrenciesUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.SelectAppCurrencyUseCase @@ -20,7 +21,7 @@ internal class AppCurrencySelectorViewModel @Inject constructor( private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getAvailableCurrenciesUseCase: GetAvailableCurrenciesUseCase, private val selectAppCurrencyUseCase: SelectAppCurrencyUseCase, - private val reduxNavController: ReduxNavController, + private val router: AppRouter, private val dispatchers: CoroutineDispatcherProvider, private val analyticsEventHandler: AnalyticsEventHandler, ) : ViewModel(), AppCurrencySelectorIntents { @@ -34,7 +35,7 @@ internal class AppCurrencySelectorViewModel @Inject constructor( val uiState: StateFlow = stateController.stateFlow override fun onBackClick() { - reduxNavController.popBackStack() + router.pop() } override fun onSearchClick() { @@ -52,7 +53,7 @@ internal class AppCurrencySelectorViewModel @Inject constructor( analyticsEventHandler.send( event = Settings.AppSettings.MainCurrencyChanged(currencyType = currency.name), ) - reduxNavController.popBackStack() + router.pop() } } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt index 2efc912cca..2d6e9d554c 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt @@ -6,10 +6,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.tangem.core.navigation.NavigationAction +import com.tangem.common.routing.AppRouter + import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.screen.ComposeFragment import com.tangem.domain.appcurrency.repository.AppCurrencyRepository +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.store import dagger.hilt.android.AndroidEntryPoint @@ -36,7 +38,7 @@ internal class AppSettingsFragment : ComposeFragment() { state = state, onBackClick = { store.dispatch(DetailsAction.ResetCardSettingsData) - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) }, ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt index 1a473c49ec..60f7de63fd 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt @@ -4,13 +4,14 @@ import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import androidx.lifecycle.lifecycleScope +import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction + import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Settings +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.features.details.redux.AppSetting @@ -128,7 +129,7 @@ internal class AppSettingsViewModel @Inject constructor( } private fun showAppCurrencySelector() { - store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.AppCurrencySelector)) + store.dispatchNavigationAction { push(AppRoute.AppCurrencySelector) } } private fun showThemeModeSelector(selectedMode: AppThemeMode) { diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsFragment.kt index c81e2d819e..6fc835cc9c 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsFragment.kt @@ -4,9 +4,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.fragment.app.viewModels -import com.tangem.core.navigation.NavigationAction +import com.tangem.common.routing.AppRouter + import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.screen.ComposeFragment +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.store import dagger.hilt.android.AndroidEntryPoint @@ -29,7 +31,7 @@ internal class CardSettingsFragment : ComposeFragment() { state = viewModel.screenState.value, onBackClick = { store.dispatch(DetailsAction.ResetCardSettingsData) - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) }, ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryFragment.kt index 7a365126b6..3beea0503a 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryFragment.kt @@ -4,9 +4,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier -import com.tangem.core.navigation.NavigationAction +import com.tangem.common.routing.AppRouter import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.screen.ComposeFragment +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.store import dagger.hilt.android.AndroidEntryPoint @@ -28,7 +29,7 @@ class AccessCodeRecoveryFragment : ComposeFragment(), StoreSubscriber() } } else { - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) + store.dispatchNavigationAction { popTo() } } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/SecurityModeFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/SecurityModeFragment.kt index c44b9ca3fa..f61e54fd78 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/SecurityModeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/SecurityModeFragment.kt @@ -4,9 +4,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier -import com.tangem.core.navigation.NavigationAction +import com.tangem.common.routing.AppRouter import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.screen.ComposeFragment +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.store import dagger.hilt.android.AndroidEntryPoint @@ -29,7 +30,7 @@ internal class SecurityModeFragment : ComposeFragment(), StoreSubscriber
- store.dispatch( - NavigationAction.NavigateTo( - screen = AppScreen.QrScanning, - bundle = bundleOf( - QrScanningRouter.SOURCE_KEY to SourceType.WALLET_CONNECT, - ), - ), - ) + store.dispatchNavigationAction { + push(AppRoute.QrScanning(source = SourceType.WALLET_CONNECT)) + } } setOnDismissListener { store.dispatch(GlobalAction.HideDialog) diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerAction.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerAction.kt index 52e3057f46..9611dc80b5 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerAction.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerAction.kt @@ -1,6 +1,5 @@ package com.tangem.tap.features.disclaimer.redux -import com.tangem.core.navigation.AppScreen import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.features.disclaimer.Disclaimer import org.rekotlin.Action @@ -10,7 +9,7 @@ sealed class DisclaimerAction : Action { data class SetDisclaimer(val disclaimer: Disclaimer) : DisclaimerAction() data class Show( - val fromScreen: AppScreen, + val from: DisclaimerSource, val callback: DisclaimerCallback? = null, ) : DisclaimerAction() diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt index 3e152187ea..20c8df7fa9 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt @@ -1,7 +1,8 @@ package com.tangem.tap.features.disclaimer.redux -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.redux.AppState import com.tangem.tap.mainScope import com.tangem.tap.store @@ -25,17 +26,17 @@ private fun handleDisclaimerMiddleware(action: Action, appState: AppState) { when (action) { is DisclaimerAction.Show -> { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) + store.dispatchNavigationAction { push(AppRoute.Disclaimer) } } is DisclaimerAction.AcceptDisclaimer -> { mainScope.launch { state.disclaimer.accept() - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) state.callback?.onAccept?.invoke() } } is DisclaimerAction.OnBackPressed -> { - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) state.callback?.onDismiss?.invoke() } } diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerReducer.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerReducer.kt index 6c2205ca01..9b655697ba 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerReducer.kt @@ -17,7 +17,7 @@ private fun internalReduce(action: Action, state: AppState): DisclaimerState { disclaimer = action.disclaimer, ) is DisclaimerAction.Show -> disclaimerState.copy( - showedFromScreen = action.fromScreen, + showedFrom = action.from, callback = action.callback, ) is DisclaimerAction.AcceptDisclaimer, is DisclaimerAction.OnBackPressed -> disclaimerState.copy( diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerSource.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerSource.kt new file mode 100644 index 0000000000..cdfcbbdb90 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerSource.kt @@ -0,0 +1,5 @@ +package com.tangem.tap.features.disclaimer.redux + +enum class DisclaimerSource { + Home, Details +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerState.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerState.kt index 8d37c385eb..c5ccfc7cfc 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerState.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerState.kt @@ -1,7 +1,7 @@ package com.tangem.tap.features.disclaimer.redux import com.tangem.common.extensions.VoidCallback -import com.tangem.core.navigation.AppScreen + import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.features.disclaimer.Disclaimer import com.tangem.tap.features.disclaimer.DummyDisclaimer @@ -9,7 +9,7 @@ import org.rekotlin.StateType data class DisclaimerState( val disclaimer: Disclaimer = DummyDisclaimer(), - val showedFromScreen: AppScreen = AppScreen.Home, + val showedFrom: DisclaimerSource = DisclaimerSource.Home, val callback: DisclaimerCallback? = null, val progressState: ProgressState? = null, ) : StateType diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt index d6767e13ac..9d217e6111 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt @@ -7,7 +7,7 @@ import android.webkit.WebView import androidx.lifecycle.lifecycleScope import androidx.transition.TransitionInflater import by.kirich1409.viewbindingdelegate.viewBinding -import com.tangem.core.navigation.AppScreen + import com.tangem.core.ui.extensions.setStatusBarColor import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.common.extensions.beginDelayedTransition @@ -18,6 +18,7 @@ import com.tangem.tap.features.BaseFragment import com.tangem.tap.features.addBackPressHandler import com.tangem.tap.features.disclaimer.Disclaimer import com.tangem.tap.features.disclaimer.redux.DisclaimerAction +import com.tangem.tap.features.disclaimer.redux.DisclaimerSource import com.tangem.tap.features.disclaimer.redux.DisclaimerState import com.tangem.tap.store import com.tangem.wallet.R @@ -76,17 +77,14 @@ class DisclaimerFragment : BaseFragment(R.layout.fragment_disclaimer), StoreSubs override fun configureTransitions() { val inflater = TransitionInflater.from(requireContext()) - when (store.state.disclaimerState.showedFromScreen) { - AppScreen.Home -> { + when (store.state.disclaimerState.showedFrom) { + DisclaimerSource.Home -> { enterTransition = inflater.inflateTransition(android.R.transition.slide_bottom) exitTransition = inflater.inflateTransition(android.R.transition.slide_top) } - AppScreen.Details -> { + DisclaimerSource.Details -> { super.configureTransitions() } - else -> { - /* no-op */ - } } } diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index 8e081474fb..e27f58f4a3 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -9,14 +9,15 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.core.view.WindowCompat import androidx.lifecycle.lifecycleScope +import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction + import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.components.SystemBarsEffect import com.tangem.core.ui.screen.ComposeFragment import com.tangem.domain.tokens.TokensAction import com.tangem.tap.common.analytics.events.IntroductionProcess +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.home.compose.StoriesScreen import com.tangem.tap.features.home.redux.HomeAction @@ -86,7 +87,7 @@ class HomeFragment : ComposeFragment(), StoreSubscriber { }, onSearchTokensClick = { Analytics.send(IntroductionProcess.ButtonTokensList()) - store.dispatch(NavigationAction.NavigateTo(AppScreen.ManageTokens)) + store.dispatchNavigationAction { push(AppRoute.ManageTokens) } store.dispatch(TokensAction.SetArgs.ReadAccess) }, ) diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 17e8d7dd0a..ad934cdebd 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -4,11 +4,10 @@ import com.tangem.common.doOnFailure import com.tangem.common.doOnResult import com.tangem.common.doOnSuccess import com.tangem.common.extensions.guard +import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.Basic -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.builder.UserWalletBuilder @@ -119,7 +118,7 @@ private fun proceedWithScanResponse(scanResponse: ScanResponse) = scope.launch { scope.launch { store.onUserWalletSelected(userWallet = userWallet) } } .doOnResult { - navigateTo(AppScreen.Wallet) + navigateTo(AppRoute.Wallet) } } @@ -143,8 +142,8 @@ private fun sendSignedInCardAnalyticsEvent(scanResponse: ScanResponse) { } } -private suspend fun navigateTo(appScreen: AppScreen) { - store.dispatchOnMain(NavigationAction.NavigateTo(appScreen)) +private suspend fun navigateTo(route: AppRoute) { + store.dispatchNavigationAction { push(route) } delay(HIDE_PROGRESS_DELAY) store.dispatch(HomeAction.ScanInProgress(scanInProgress = false)) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt index c900ea3b50..8d567fc5a5 100644 --- a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt @@ -3,11 +3,11 @@ package com.tangem.tap.features.main import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.tangem.blockchainsdk.BlockchainSDKFactory +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.models.Basic -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.ReduxNavController + import com.tangem.domain.appcurrency.FetchAppCurrenciesUseCase import com.tangem.domain.balancehiding.BalanceHidingSettings import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase @@ -36,7 +36,7 @@ import javax.inject.Inject internal class MainViewModel @Inject constructor( private val updateBalanceHidingSettingsUseCase: UpdateBalanceHidingSettingsUseCase, private val listenToFlipsUseCase: ListenToFlipsUseCase, - private val reduxNavController: ReduxNavController, + private val router: AppRouter, private val fetchAppCurrenciesUseCase: FetchAppCurrenciesUseCase, private val deleteDeprecatedLogsUseCase: DeleteDeprecatedLogsUseCase, private val incrementAppLaunchCounterUseCase: IncrementAppLaunchCounterUseCase, @@ -152,7 +152,7 @@ internal class MainViewModel @Inject constructor( if (state.value.modalNotification?.isShow != true && !it.isUpdateFromToast) { listenToFlipsUseCase.changeUpdateEnabled(false) stateHolder.updateWithHiddenBalancesNotification() - reduxNavController.navigate(NavigationAction.NavigateTo(AppScreen.ModalNotification)) + router.push(AppRoute.ModalNotification) } } .launchIn(viewModelScope) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt index f03dc73425..f8fb8f2ba8 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -3,10 +3,10 @@ package com.tangem.tap.features.onboarding import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess import com.tangem.common.extensions.guard +import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.models.Basic -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction + import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.twinsIsTwinned import com.tangem.domain.models.scan.CardDTO @@ -56,18 +56,18 @@ object OnboardingHelper { } } - fun whereToNavigate(scanResponse: ScanResponse): AppScreen { + fun whereToNavigate(scanResponse: ScanResponse): AppRoute { return when (val type = scanResponse.productType) { - ProductType.Note -> AppScreen.OnboardingNote + ProductType.Note -> AppRoute.OnboardingNote ProductType.Wallet, ProductType.Wallet2, ProductType.Ring, -> if (scanResponse.card.settings.isBackupAllowed) { - AppScreen.OnboardingWallet + AppRoute.OnboardingWallet() } else { - AppScreen.OnboardingOther + AppRoute.OnboardingOther } - ProductType.Twins -> AppScreen.OnboardingTwins + ProductType.Twins -> AppRoute.OnboardingTwins ProductType.Start2Coin, ProductType.Visa, -> throw UnsupportedOperationException("Onboarding for ${type.name} cards is not supported") @@ -111,14 +111,14 @@ object OnboardingHelper { backupCardsIds = backupCardsIds?.toSet(), ), ) - store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet)) + store.dispatchNavigationAction { push(AppRoute.Wallet) } delay(timeMillis = 1_800) - store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.SaveWallet)) + store.dispatchNavigationAction { push(AppRoute.SaveWallet) } } // If device has no biometry and save wallet screen has been shown, then go through old scenario else -> { proceedWithScanResponse(scanResponse, backupCardsIds, hasBackupError) - store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet)) + store.dispatchNavigationAction { push(AppRoute.Wallet) } } } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index ef5296a6b4..ef320383b2 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -2,8 +2,9 @@ package com.tangem.tap.features.onboarding.products.note.redux import com.tangem.common.CompletionResult import com.tangem.common.extensions.guard +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.NavigationAction + import com.tangem.data.tokens.utils.CryptoCurrencyFactory import com.tangem.domain.common.extensions.makePrimaryWalletManager import com.tangem.domain.common.extensions.withMainContext @@ -204,7 +205,7 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch OnboardingDialog.InterruptOnboarding( onOk = { OnboardingHelper.onInterrupted() - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) }, ), ) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt index 033b790e84..2bc6f07ce1 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -3,9 +3,10 @@ package com.tangem.tap.features.onboarding.products.twins.redux import com.tangem.blockchain.extensions.Result import com.tangem.common.CompletionResult import com.tangem.common.extensions.guard +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.utils.popTo import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction + import com.tangem.data.tokens.utils.CryptoCurrencyFactory import com.tangem.domain.common.extensions.makePrimaryWalletManager import com.tangem.domain.common.extensions.withMainContext @@ -39,6 +40,7 @@ import kotlinx.coroutines.runBlocking import org.rekotlin.Action import org.rekotlin.DispatchFunction import org.rekotlin.Middleware +import kotlin.reflect.KClass object TwinCardsMiddleware { val handler = twinsWalletMiddleware @@ -328,7 +330,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) { if (walletsRepository.shouldSaveUserWalletsSync()) { OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(scanResponse) } else { - store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home)) + store.dispatchNavigationAction { popTo() } } } } @@ -352,7 +354,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) { store.dispatch(TwinCardsAction.CardsManager.Release) action.shouldResetTwinCardsWidget(shouldReturnCardBack) { - store.dispatchOnMain(NavigationAction.PopBackTo(getPopBackScreen())) + store.dispatchNavigationAction { popTo(routeClass = getPopBackScreen()) } } } store.dispatchDialogShow(OnboardingDialog.InterruptOnboarding(onOkCallback)) @@ -362,18 +364,19 @@ private fun handle(action: Action, dispatch: DispatchFunction) { } } -private fun getPopBackScreen(): AppScreen { +private fun getPopBackScreen(): KClass { val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager) return if (userWalletsListManager.hasUserWallets) { val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync } .fold(onSuccess = { true }, onFailure = { false }) + if (isLocked) { - AppScreen.Welcome + AppRoute.Welcome::class } else { - AppScreen.Wallet + AppRoute.Wallet::class } } else { - AppScreen.Home + AppRoute.Home::class } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 925dc9bcc2..efa987c847 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -7,16 +7,16 @@ import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard import com.tangem.common.extensions.ifNotNull import com.tangem.common.extensions.toHexString +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.common.services.Result import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.wallets.models.Artwork import com.tangem.domain.wallets.builder.UserWalletBuilder +import com.tangem.domain.wallets.models.Artwork import com.tangem.feature.onboarding.data.model.CreateWalletResponse import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource import com.tangem.feature.wallet.presentation.wallet.domain.BackupValidator @@ -25,10 +25,7 @@ import com.tangem.operations.backup.BackupService import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Onboarding -import com.tangem.tap.common.extensions.dispatchDialogShow -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.dispatchWithMain -import com.tangem.tap.common.extensions.inject +import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse @@ -164,7 +161,7 @@ private fun handleWalletAction(action: Action) { store.dispatch(GlobalAction.Onboarding.Stop) if (scanResponse == null) { - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) store.dispatch(HomeAction.ReadCard(scope = action.scope)) } else { val backupState = store.state.onboardingWalletState.backupState @@ -529,7 +526,8 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) addedBackupCardsCount = backupService.addedBackupCardsCount, ), ) - store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet)) + + store.dispatchNavigationAction { push(AppRoute.OnboardingWallet()) } } is BackupAction.SkipBackup -> { Analytics.send(Onboarding.Backup.Skipped()) @@ -614,7 +612,7 @@ private fun handleOnBackPressed(state: OnboardingWalletState) { } BackupStep.Finished -> { OnboardingHelper.onInterrupted() - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) } } } @@ -625,7 +623,7 @@ private fun showInterruptOnboardingDialog() { onOk = { OnboardingHelper.onInterrupted() store.dispatch(BackupAction.DiscardBackup) - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) }, ), ) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index b2d51cca3a..b66984164b 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -21,13 +21,13 @@ import com.google.android.material.tabs.TabLayoutMediator import com.tangem.common.CardIdFormatter import com.tangem.common.CompletionResult import com.tangem.common.core.CardIdDisplayFormat +import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.Basic import com.tangem.core.ui.extensions.setStatusBarColor import com.tangem.domain.common.util.cardTypesResolver import com.tangem.feature.onboarding.data.model.CreateWalletResponse -import com.tangem.feature.onboarding.navigation.OnboardingRouter import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseMediator import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseRouter @@ -66,7 +66,7 @@ class OnboardingWalletFragment : internal val bindingSeedPhrase: LayoutOnboardingSeedPhraseBinding by lazy { binding.onboardingSeedPhraseContainer } - private val canSkipBackup by lazy { arguments?.getBoolean(OnboardingRouter.CAN_SKIP_BACKUP) ?: true } + private val canSkipBackup by lazy { arguments?.getBoolean(AppRoute.OnboardingWallet.CAN_SKIP_BACKUP_KEY) ?: true } private lateinit var seedPhraseStateHandler: OnboardingSeedPhraseStateHandler diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt index 932c1a1965..541ceefdcc 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt @@ -3,26 +3,22 @@ package com.tangem.tap.features.saveWallet.redux import com.tangem.common.* import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.utils.popTo import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction import com.tangem.domain.wallets.builder.UserWalletBuilder import com.tangem.domain.wallets.models.UserWallet import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.MainScreen import com.tangem.tap.common.analytics.events.Onboarding -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.dispatchWithMain -import com.tangem.tap.common.extensions.inject -import com.tangem.tap.common.extensions.onUserWalletSelected +import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn import kotlinx.coroutines.launch import org.rekotlin.Middleware -import org.rekotlin.Store import timber.log.Timber internal class SaveWalletMiddleware { @@ -80,7 +76,7 @@ internal class SaveWalletMiddleware { Timber.e(error, "Unable to save user wallet") } .doOnSuccess { mainScope.launch { store.onUserWalletSelected(userWallet) } } - .doOnResult { store.navigateToWallet() } + .doOnResult { navigateToWallet() } } } @@ -130,7 +126,7 @@ internal class SaveWalletMiddleware { ) store.dispatchWithMain(SaveWalletAction.AllowToUseBiometrics.Success) - store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Wallet)) + store.dispatchNavigationAction { popTo() } } private fun dismiss(state: SaveWalletState) { @@ -159,13 +155,13 @@ internal class SaveWalletMiddleware { } } - private suspend fun Store.navigateToWallet() { - dispatchWithMain( - if (store.state.navigationState.backStack.contains(AppScreen.Wallet)) { - NavigationAction.PopBackTo(AppScreen.Wallet) - } else { - NavigationAction.NavigateTo(AppScreen.Wallet) - }, - ) + private fun navigateToWallet() { + store.dispatchNavigationAction { + popTo { isSuccess -> + if (!isSuccess) { + clear { push(AppRoute.Wallet) } + } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 9067bffe39..b1be6e7b39 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -14,10 +14,10 @@ import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.extensions.SimpleResult import com.tangem.blockchainsdk.utils.minimalAmount import com.tangem.common.core.TangemSdkError +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.Basic -import com.tangem.core.navigation.NavigationAction import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.demo.DemoTransactionSender @@ -270,7 +270,7 @@ private fun sendTransaction( ), ) Analytics.sendSelectedCurrencyEvent(mainCurrencyType) - dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) } } is SimpleResult.Failure -> { @@ -326,7 +326,7 @@ private fun sendTransaction( AppDialog.SimpleOkDialogRes( headerId = R.string.common_done, messageId = R.string.alert_demo_feature_disabled, - onOk = { dispatch(NavigationAction.PopBackTo()) }, + onOk = { store.dispatchNavigationAction(AppRouter::pop) }, ), ) } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index fd6d7105d3..34650ad800 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -8,7 +8,6 @@ import android.text.method.DigitsKeyListener import android.view.View import android.view.inputmethod.EditorInfo import android.widget.EditText -import androidx.core.os.bundleOf import androidx.core.view.postDelayed import androidx.core.widget.addTextChangedListener import androidx.fragment.app.viewModels @@ -20,19 +19,18 @@ import androidx.recyclerview.widget.RecyclerView import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.textfield.TextInputEditText import com.tangem.Message +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.qrscanning.models.SourceType import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.sdk.extensions.hideSoftKeyboard import com.tangem.tap.common.KeyboardObserver import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.extensions.setOnImeActionListener import com.tangem.tap.common.recyclerView.SpaceItemDecoration @@ -162,14 +160,9 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { imvQrCode.setOnClickListener { Analytics.send(Token.Send.ButtonQRCode()) - store.dispatchOnMain( - NavigationAction.NavigateTo( - screen = AppScreen.QrScanning, - bundle = bundleOf( - QrScanningRouter.SOURCE_KEY to SourceType.SEND, - ), - ), - ) + store.dispatchNavigationAction { + push(AppRoute.QrScanning(source = SourceType.SEND)) + } } } @@ -370,7 +363,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { override fun handleOnBackPressed() { val externalTransactionData = store.state.sendState.externalTransactionData if (externalTransactionData == null) { - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) } else { store.dispatch(TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)) } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendViewModel.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendViewModel.kt index 9f59533686..881e9f6df2 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendViewModel.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.send.ui import androidx.lifecycle.* import arrow.core.getOrElse +import com.tangem.common.routing.AppRoute import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.qrscanning.models.SourceType import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase @@ -12,7 +13,6 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.features.send.api.navigation.SendRouter import com.tangem.tap.common.analytics.events.Token import com.tangem.tap.di.DelayedWork import com.tangem.tap.features.send.redux.AddressActionUi @@ -52,7 +52,7 @@ internal class SendViewModel @Inject constructor( .launchIn(viewModelScope) } - private val cryptoCurrency: CryptoCurrency? = savedStateHandle[SendRouter.CRYPTO_CURRENCY_KEY] + private val cryptoCurrency: CryptoCurrency? = savedStateHandle[AppRoute.Send.CRYPTO_CURRENCY_KEY] override fun onCreate(owner: LifecycleOwner) { getBalanceHidingSettingsUseCase() diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt index 27418f3d01..a62f01e74b 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt @@ -1,9 +1,11 @@ package com.tangem.tap.features.tokens.impl.presentation.router import com.tangem.blockchain.common.Blockchain -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter + import com.tangem.tap.common.extensions.dispatchDialogShow +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.extensions.dispatchNotification import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.store @@ -18,11 +20,11 @@ import com.tangem.wallet.R internal class DefaultTokensListRouter : TokensListRouter { override fun popBackStack() { - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) } override fun openAddCustomTokenScreen() { - store.dispatch(NavigationAction.NavigateTo(AppScreen.AddCustomToken)) + store.dispatchNavigationAction { push(AppRoute.AddCustomToken) } } override fun showAddressCopiedNotification() { @@ -54,7 +56,7 @@ internal class DefaultTokensListRouter : TokensListRouter { val alert = AppDialog.SimpleOkDialogRes( headerId = R.string.common_error, messageId = R.string.common_unknown_error, - onOk = { store.dispatch(NavigationAction.PopBackTo()) }, + onOk = { store.dispatchNavigationAction(AppRouter::pop) }, ) store.dispatchDialogShow(alert) } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt index 050dc5f876..b10741f96c 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt @@ -3,8 +3,8 @@ package com.tangem.tap.features.tokens.impl.presentation.viewmodels import arrow.core.Either import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.utils.popTo import com.tangem.data.tokens.utils.CryptoCurrencyFactory import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.common.util.derivationStyleProvider @@ -16,7 +16,7 @@ import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.tap.common.extensions.dispatchDebugErrorNotification -import com.tangem.tap.common.extensions.dispatchWithMain +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.extensions.inject import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store @@ -126,7 +126,7 @@ internal class TokensListMigration( val isNothingToDoWithBlockchain = blockchainsToAdd.isEmpty() && blockchainsToRemove.isEmpty() if (isNothingToDoWithTokens && isNothingToDoWithBlockchain) { store.dispatchDebugErrorNotification(message = "Nothing to save") - store.dispatchWithMain(NavigationAction.PopBackTo(screen = AppScreen.Wallet)) + store.dispatchNavigationAction { popTo() } return } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt index 53867da367..f5dc7f92b4 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt @@ -11,9 +11,9 @@ import androidx.lifecycle.viewModelScope import androidx.paging.* import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.utils.fromNetworkId +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.utils.popTo import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction import com.tangem.core.ui.extensions.getActiveIconRes import com.tangem.core.ui.extensions.getGreyedOutIconRes import com.tangem.domain.card.DerivePublicKeysUseCase @@ -26,7 +26,7 @@ import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase import com.tangem.domain.tokens.TokenWithBlockchain import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.tap.common.extensions.dispatchWithMain +import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.extensions.fullNameWithoutTestnet import com.tangem.tap.common.extensions.getNetworkName import com.tangem.tap.features.customtoken.impl.presentation.models.SupportBlockchainType @@ -326,7 +326,7 @@ internal class TokensListViewModel @Inject constructor( ) uiState = state.copy(isSavingInProgress = false) - store.dispatchWithMain(NavigationAction.PopBackTo(screen = AppScreen.Wallet)) + store.dispatchNavigationAction { popTo() } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 39a0168df7..a4233d6ea8 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -1,18 +1,16 @@ package com.tangem.tap.features.wallet.redux.middlewares -import androidx.core.os.bundleOf import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkAddress -import com.tangem.feature.swap.presentation.SwapFragment -import com.tangem.features.send.api.navigation.SendRouter +import com.tangem.domain.wallets.models.UserWalletId import com.tangem.tap.common.analytics.events.Token import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder import com.tangem.tap.common.extensions.* @@ -150,7 +148,7 @@ object TradeCryptoMiddleware { } private fun openReceiptUrl(transactionId: String) { - store.dispatchOnMain(NavigationAction.PopBackTo()) + store.dispatchNavigationAction(AppRouter::pop) store.state.globalState.exchangeManager.getSellCryptoReceiptUrl( action = CurrencyExchangeManager.Action.Sell, transactionId = transactionId, @@ -158,11 +156,7 @@ object TradeCryptoMiddleware { } private fun openSwap(currency: CryptoCurrency) { - val bundle = bundleOf( - SwapFragment.CURRENCY_BUNDLE_KEY to currency, - ) - - store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Swap, bundle = bundle)) + store.dispatchNavigationAction { push(AppRoute.Swap(currency = currency)) } } private fun handleSendToken(action: TradeCryptoAction.SendToken) { @@ -214,11 +208,12 @@ object TradeCryptoMiddleware { ) } - val bundle = bundleOf( - SendRouter.CRYPTO_CURRENCY_KEY to currency, - SendRouter.USER_WALLET_ID_KEY to action.userWallet.walletId.stringValue, + val route = AppRoute.Send( + currency = currency, + userWalletId = action.userWallet.walletId, ) - store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle)) + + store.dispatchNavigationAction { push(route) } } } @@ -285,17 +280,17 @@ object TradeCryptoMiddleware { ) } - val bundle = bundleOf( - SendRouter.CRYPTO_CURRENCY_KEY to currency, - SendRouter.USER_WALLET_ID_KEY to action.userWallet.walletId.stringValue, + val route = AppRoute.Send( + currency = currency, + userWalletId = action.userWallet.walletId, ) - store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle)) + store.dispatchNavigationAction { push(route) } } } private fun handleNewSendToken(action: TradeCryptoAction.SendToken) { handleNewSend( - userWalletId = action.userWallet.walletId.stringValue, + userWalletId = action.userWallet.walletId, txInfo = action.transactionInfo, currency = action.tokenCurrency, ) @@ -303,25 +298,26 @@ object TradeCryptoMiddleware { private fun handleNewSendCoin(action: TradeCryptoAction.SendCoin) { handleNewSend( - userWalletId = action.userWallet.walletId.stringValue, + userWalletId = action.userWallet.walletId, txInfo = action.transactionInfo, currency = action.coinStatus.currency, ) } private fun handleNewSend( - userWalletId: String, + userWalletId: UserWalletId, txInfo: TradeCryptoAction.TransactionInfo?, currency: CryptoCurrency, ) { - val bundle = bundleOf( - SendRouter.CRYPTO_CURRENCY_KEY to currency, - SendRouter.USER_WALLET_ID_KEY to userWalletId, - SendRouter.TRANSACTION_ID_KEY to txInfo?.transactionId, - SendRouter.DESTINATION_ADDRESS_KEY to txInfo?.destinationAddress, - SendRouter.AMOUNT_KEY to txInfo?.amount, - SendRouter.TAG_KEY to txInfo?.tag, + val route = AppRoute.Send( + currency = currency, + userWalletId = userWalletId, + transactionId = txInfo?.transactionId, + destinationAddress = txInfo?.destinationAddress, + amount = txInfo?.amount, + tag = txInfo?.tag, ) - store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle)) + + store.dispatchNavigationAction { push(route) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index abfc205397..2a564e8a0d 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -6,11 +6,12 @@ import com.tangem.common.doOnFailure import com.tangem.common.doOnResult import com.tangem.common.doOnSuccess import com.tangem.common.flatMap +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.utils.popTo import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.Basic -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction + import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.builder.UserWalletBuilder @@ -98,7 +99,7 @@ internal class WelcomeMiddleware { signInType = Basic.SignedIn.SignInType.Biometric, ) - store.dispatchWithMain(NavigationAction.NavigateTo(AppScreen.Wallet)) + store.dispatchNavigationAction { push(AppRoute.Wallet) } store.dispatchWithMain(WelcomeAction.ProceedWithBiometrics.Success) store.onUserWalletSelected(userWallet = selectedUserWallet) @@ -130,7 +131,7 @@ internal class WelcomeMiddleware { .doOnSuccess { sendSignedInAnalyticsEvent(scanResponse = scanResponse, signInType = Basic.SignedIn.SignInType.Card) - store.dispatchWithMain(NavigationAction.NavigateTo(AppScreen.Wallet)) + store.dispatchNavigationAction { push(AppRoute.Wallet) } store.dispatchWithMain(WelcomeAction.ProceedWithCard.Success) store.onUserWalletSelected(userWallet = userWallet) @@ -170,7 +171,7 @@ internal class WelcomeMiddleware { } .doOnResult { store.dispatchWithMain(WelcomeAction.CloseError) - store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Home)) + store.dispatchNavigationAction { popTo() } } } diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt index 60ab61900f..41ef679b6a 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt @@ -88,8 +88,4 @@ internal class WelcomeFragment : ComposeFragment() { } } } - - internal companion object { - const val INITIAL_INTENT_KEY = "intent" - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt index bc672b7c2f..bb885005f9 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt @@ -1,8 +1,11 @@ package com.tangem.tap.features.welcome.ui -import android.content.Intent +import android.os.Bundle import androidx.lifecycle.* import com.tangem.common.core.TangemError +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.bundle.unbundle +import com.tangem.common.routing.entity.SerializableIntent import com.tangem.core.analytics.Analytics import com.tangem.domain.wallets.legacy.UserWalletsListError import com.tangem.tap.common.analytics.events.SignIn @@ -26,7 +29,8 @@ internal class WelcomeViewModel @Inject constructor( StoreSubscriber, DefaultLifecycleObserver { - private val initialIntent: Intent? = savedStateHandle[WelcomeFragment.INITIAL_INTENT_KEY] + private val initialIntent: SerializableIntent? = savedStateHandle.get(AppRoute.Welcome.INITIAL_INTENT_KEY) + ?.let { it.unbundle(SerializableIntent.serializer()) } private val stateInternal = MutableStateFlow(WelcomeScreenState()) val state: StateFlow = stateInternal @@ -38,7 +42,7 @@ internal class WelcomeViewModel @Inject constructor( initGlobalState() val welcomeAction = if (initialIntent != null) { - WelcomeAction.ProceedWithIntent(initialIntent) + WelcomeAction.ProceedWithIntent(initialIntent.toIntent()) } else { WelcomeAction.ProceedWithBiometrics() } From 5ea04db5d873f915dc330bc683e3e22e7e918a8b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 12:52:44 +0400 Subject: [PATCH 06/19] Updated on 2026-08-14 --- features/wallet/impl/build.gradle.kts | 1 + .../feature/wallet/di/WalletRouterModule.kt | 8 +-- .../router/DefaultWalletRouter.kt | 58 +++++++------------ .../presentation/router/InnerWalletRouter.kt | 4 +- .../presentation/wallet/ui/WalletScreen.kt | 3 +- .../intents/WalletCardClickIntents.kt | 17 ++---- 6 files changed, 36 insertions(+), 55 deletions(-) diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 1c2c062dbd..7022b31c98 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -52,6 +52,7 @@ dependencies { implementation(projects.core.utils) implementation(projects.core.analytics) implementation(projects.core.analytics.models) + implementation(projects.common.routing) implementation(projects.core.deepLinks) implementation(projects.core.deepLinks.global) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt index 536e255a98..4e031b0729 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt @@ -1,6 +1,6 @@ package com.tangem.feature.wallet.di -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRouter import com.tangem.core.navigation.url.UrlOpener import com.tangem.domain.redux.ReduxStateHolder import com.tangem.feature.wallet.presentation.router.DefaultWalletRouter @@ -18,10 +18,10 @@ internal object WalletRouterModule { @Provides @ActivityScoped fun provideWalletRouter( - reduxNavController: ReduxNavController, - reduxStateHolder: ReduxStateHolder, + appRouter: AppRouter, urlOpener: UrlOpener, + reduxStateHolder: ReduxStateHolder, ): WalletRouter { - return DefaultWalletRouter(reduxNavController, reduxStateHolder, urlOpener) + return DefaultWalletRouter(appRouter, urlOpener, reduxStateHolder) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 535062c3ef..69906449ee 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -6,7 +6,6 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.dp -import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -16,31 +15,27 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.core.navigation.url.UrlOpener import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.redux.StateDialog import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.onboarding.navigation.OnboardingRouter import com.tangem.feature.wallet.presentation.WalletFragment import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScreen import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel -import com.tangem.features.details.DetailsEntryPoint import com.tangem.features.managetokens.navigation.ExpandableState import com.tangem.features.managetokens.navigation.ManageTokensUi -import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import kotlin.properties.Delegates /** Default implementation of wallet feature router */ internal class DefaultWalletRouter( - private val reduxNavController: ReduxNavController, - private val reduxStateHolder: ReduxStateHolder, + private val router: AppRouter, private val urlOpener: UrlOpener, + private val reduxStateHolder: ReduxStateHolder, ) : InnerWalletRouter { private var navController: NavHostController by Delegates.notNull() @@ -99,7 +94,7 @@ internal class DefaultWalletRouter( } @SuppressLint("RestrictedApi") - override fun popBackStack(screen: AppScreen?) { + override fun popBackStack() { /* * It's hack that avoid issue with closing the wallet screen. * We are using NavGraph only inside feature so first backstack's element is entry of NavGraph and @@ -107,11 +102,7 @@ internal class DefaultWalletRouter( * If backstack contains only NavGraph entry and wallet screen entry then we close the wallet fragment. */ if (navController.currentBackStack.value.size == BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN) { - if (screen != null) { - reduxNavController.navigate(action = NavigationAction.PopBackTo(screen)) - } else { - onFinish.invoke() - } + onFinish.invoke() } else { navController.popBackStack() } @@ -122,22 +113,16 @@ internal class DefaultWalletRouter( } override fun openDetailsScreen(selectedWalletId: UserWalletId) { - reduxNavController.navigate( - action = NavigationAction.NavigateTo( - screen = AppScreen.Details, - bundle = bundleOf( - DetailsEntryPoint.USER_WALLET_ID_KEY to selectedWalletId.stringValue, - ), + router.push( + AppRoute.Details( + userWalletId = selectedWalletId, ), ) } override fun openOnboardingScreen() { - reduxNavController.navigate( - action = NavigationAction.NavigateTo( - screen = AppScreen.OnboardingWallet, - bundle = bundleOf(OnboardingRouter.CAN_SKIP_BACKUP to false), - ), + router.push( + AppRoute.OnboardingWallet(canSkipBackup = false), ) } @@ -148,30 +133,29 @@ internal class DefaultWalletRouter( override fun openTokenDetails(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) { val networkAddress = currencyStatus.value.networkAddress if (networkAddress != null && networkAddress.defaultAddress.value.isNotEmpty()) { - reduxNavController.navigate( - action = NavigationAction.NavigateTo( - screen = AppScreen.WalletDetails, - bundle = bundleOf( - TokenDetailsRouter.USER_WALLET_ID_KEY to userWalletId.stringValue, - TokenDetailsRouter.CRYPTO_CURRENCY_KEY to currencyStatus.currency, - ), + router.push( + AppRoute.CurrencyDetails( + userWalletId = userWalletId, + currency = currencyStatus.currency, ), ) } } override fun openStoriesScreen() { - reduxNavController.navigate(action = NavigationAction.NavigateTo(screen = AppScreen.Home)) + router.push(AppRoute.Home) } override fun openSaveUserWalletScreen() { - reduxNavController.navigate(action = NavigationAction.NavigateTo(AppScreen.SaveWallet)) + router.push(AppRoute.SaveWallet) } - override fun isWalletLastScreen(): Boolean = reduxNavController.getBackStack().lastOrNull() == AppScreen.Wallet + override fun isWalletLastScreen(): Boolean { + return router.stack.lastOrNull() is AppRoute.Wallet + } override fun openManageTokensScreen() { - reduxNavController.navigate(action = NavigationAction.NavigateTo(AppScreen.ManageTokens)) + router.push(AppRoute.ManageTokens) } override fun openScanFailedDialog(onTryAgain: () -> Unit) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index d3f0095b29..6b05b626ce 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -2,7 +2,7 @@ package com.tangem.feature.wallet.presentation.router import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable -import com.tangem.core.navigation.AppScreen + import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.managetokens.navigation.ManageTokensUi @@ -29,7 +29,7 @@ internal interface InnerWalletRouter : WalletRouter { fun Initialize(onFinish: () -> Unit, manageTokensUi: ManageTokensUi) /** Pop back stack */ - fun popBackStack(screen: AppScreen? = null) + fun popBackStack() /** Open organize tokens screen */ fun openOrganizeTokensScreen(userWalletId: UserWalletId) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index c930c63241..eccfc5ea55 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -154,7 +154,8 @@ private fun WalletContent( horizontalAlignment = Alignment.CenterHorizontally, ) { item( - key = state.wallets.map { it.walletCardState.id }, + // !!! Type of the key should be saveable via Bundle on Android !!! + key = state.wallets.map { it.walletCardState.id.stringValue }, contentType = state.wallets.map { it.walletCardState.id }, ) { WalletsList( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt index 5b4bf78f5d..7c128ca00d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt @@ -1,21 +1,16 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents import arrow.core.getOrElse +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList -import com.tangem.domain.wallets.usecase.GetWalletNamesUseCase -import com.tangem.domain.wallets.usecase.RenameWalletUseCase -import com.tangem.feature.wallet.impl.R import com.tangem.domain.card.DeleteSavedAccessCodesUseCase -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.ReduxNavController import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.domain.wallets.usecase.DeleteWalletUseCase -import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.domain.wallets.usecase.GetUserWalletUseCase +import com.tangem.domain.wallets.usecase.* +import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController @@ -52,7 +47,7 @@ internal class WalletCardClickIntentsImplementor @Inject constructor( private val deleteSavedAccessCodesUseCase: DeleteSavedAccessCodesUseCase, private val analyticsEventHandler: AnalyticsEventHandler, private val reduxStateHolder: ReduxStateHolder, - private val reduxNavController: ReduxNavController, + private val appRouter: AppRouter, private val dispatchers: CoroutineDispatcherProvider, ) : BaseWalletClickIntents(), WalletCardClickIntents { @@ -123,7 +118,7 @@ internal class WalletCardClickIntentsImplementor @Inject constructor( reduxStateHolder.onUserWalletSelected(selectedWallet) } else { stateHolder.clear() - reduxNavController.navigate(NavigationAction.PopBackTo(AppScreen.Home)) + appRouter.replaceAll(AppRoute.Home) } } } From f895968639fae902366e4e73f154e95d5828c924 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 12:54:24 +0400 Subject: [PATCH 07/19] Updated on 2026-08-14 --- .../navigation/TokenDetailsRouter.kt | 5 ----- features/tokendetails/impl/build.gradle.kts | 2 ++ .../di/TokenDetailsRouterModule.kt | 6 ++--- .../router/DefaultTokenDetailsRouter.kt | 22 +++++++------------ .../viewmodels/TokenDetailsViewModel.kt | 16 +++++++++----- 5 files changed, 23 insertions(+), 28 deletions(-) diff --git a/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt b/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt index a0f630c125..a361d75438 100644 --- a/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt +++ b/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt @@ -5,9 +5,4 @@ import androidx.fragment.app.Fragment interface TokenDetailsRouter { fun getEntryFragment(): Fragment - - companion object { - const val USER_WALLET_ID_KEY = "token_details_user_wallet_id" - const val CRYPTO_CURRENCY_KEY = "token_details_crypto_currency" - } } \ No newline at end of file diff --git a/features/tokendetails/impl/build.gradle.kts b/features/tokendetails/impl/build.gradle.kts index 514d108158..73f39eae8c 100644 --- a/features/tokendetails/impl/build.gradle.kts +++ b/features/tokendetails/impl/build.gradle.kts @@ -38,12 +38,14 @@ dependencies { implementation(deps.tangem.card.core) implementation(deps.timber) implementation(deps.lifecycle.compose) + implementation(deps.kotlin.serialization) /** DI */ implementation(deps.hilt.android) kapt(deps.hilt.kapt) /** Core modules */ + implementation(projects.common.routing) implementation(projects.core.navigation) implementation(projects.core.ui) implementation(projects.core.utils) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsRouterModule.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsRouterModule.kt index 186a4fcc7b..94147e5592 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsRouterModule.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsRouterModule.kt @@ -1,6 +1,6 @@ package com.tangem.feature.tokendetails.di -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRouter import com.tangem.core.navigation.share.ShareManager import com.tangem.core.navigation.url.UrlOpener import com.tangem.feature.tokendetails.presentation.router.DefaultTokenDetailsRouter @@ -18,10 +18,10 @@ internal object TokenDetailsRouterModule { @Provides @ActivityScoped fun provideTokenDetailsRouter( - reduxNavController: ReduxNavController, + appRouter: AppRouter, urlOpener: UrlOpener, shareManager: ShareManager, ): TokenDetailsRouter { - return DefaultTokenDetailsRouter(reduxNavController, urlOpener, shareManager) + return DefaultTokenDetailsRouter(appRouter, urlOpener, shareManager) } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt index 16b2a547ca..880d92fba4 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt @@ -1,19 +1,16 @@ package com.tangem.feature.tokendetails.presentation.router -import androidx.core.os.bundleOf import androidx.fragment.app.Fragment -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.core.navigation.share.ShareManager import com.tangem.core.navigation.url.UrlOpener import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.tokendetails.presentation.TokenDetailsFragment -import com.tangem.features.tokendetails.navigation.TokenDetailsRouter internal class DefaultTokenDetailsRouter( - private val reduxNavController: ReduxNavController, + private val router: AppRouter, private val urlOpener: UrlOpener, private val shareManager: ShareManager, ) : InnerTokenDetailsRouter { @@ -21,7 +18,7 @@ internal class DefaultTokenDetailsRouter( override fun getEntryFragment(): Fragment = TokenDetailsFragment() override fun popBackStack() { - reduxNavController.navigate(NavigationAction.PopBackTo()) + router.pop() } override fun openUrl(url: String) { @@ -33,13 +30,10 @@ internal class DefaultTokenDetailsRouter( } override fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) { - reduxNavController.navigate( - action = NavigationAction.NavigateTo( - screen = AppScreen.WalletDetails, - bundle = bundleOf( - TokenDetailsRouter.USER_WALLET_ID_KEY to userWalletId.stringValue, - TokenDetailsRouter.CRYPTO_CURRENCY_KEY to currency, - ), + router.push( + AppRoute.CurrencyDetails( + userWalletId = userWalletId, + currency = currency, ), ) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 48640d3c90..8542738099 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -1,5 +1,6 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels +import android.os.Bundle import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -8,6 +9,8 @@ import androidx.lifecycle.* import androidx.paging.cachedIn import arrow.core.getOrElse import com.tangem.blockchain.common.address.AddressType +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.bundle.unbundle import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.deeplink.DeepLinksRegistry import com.tangem.core.deeplink.global.BuyCurrencyDeepLink @@ -19,8 +22,8 @@ import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModel import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.haptic.HapticManager import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.haptic.HapticManager import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency @@ -70,7 +73,6 @@ import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles import com.tangem.features.staking.api.navigation.StakingRouter import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles import com.tangem.features.tokendetails.impl.R -import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import com.tangem.lib.crypto.BlockchainUtils.isBitcoin import com.tangem.utils.Provider import com.tangem.utils.coroutines.* @@ -126,12 +128,14 @@ internal class TokenDetailsViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents { - private val userWalletId: UserWalletId = savedStateHandle.get(TokenDetailsRouter.USER_WALLET_ID_KEY) - ?.let { stringValue -> UserWalletId(stringValue) } + private val userWalletId: UserWalletId = savedStateHandle.get(AppRoute.CurrencyDetails.USER_WALLET_ID_KEY) + ?.let { it.unbundle(UserWalletId.serializer()) } ?: error("This screen can't open without `UserWalletId`") - private val cryptoCurrency: CryptoCurrency = savedStateHandle[TokenDetailsRouter.CRYPTO_CURRENCY_KEY] - ?: error("This screen can't open without `CryptoCurrency`") + private val cryptoCurrency: CryptoCurrency = + savedStateHandle.get(AppRoute.CurrencyDetails.CRYPTO_CURRENCY_KEY) + ?.let { it.unbundle(CryptoCurrency.serializer()) } + ?: error("This screen can't open without `CryptoCurrency`") private val userWallet: UserWallet From 87987689d36e15d8368e4b2fe7607f19e142a263 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 12:55:37 +0400 Subject: [PATCH 08/19] Updated on 2026-08-14 --- .../send/api/navigation/SendRouter.kt | 9 ---- features/send/impl/build.gradle.kts | 2 + .../features/send/impl/di/SendRouterModule.kt | 6 +-- .../send/impl/navigation/DefaultSendRouter.kt | 41 ++++++++----------- .../send/impl/presentation/SendFragment.kt | 10 +++-- .../impl/presentation/state/StateRouter.kt | 7 ++-- .../presentation/viewmodel/SendViewModel.kt | 19 +++++---- 7 files changed, 43 insertions(+), 51 deletions(-) diff --git a/features/send/api/src/main/kotlin/com/tangem/features/send/api/navigation/SendRouter.kt b/features/send/api/src/main/kotlin/com/tangem/features/send/api/navigation/SendRouter.kt index 4a2940f972..286f908ad8 100644 --- a/features/send/api/src/main/kotlin/com/tangem/features/send/api/navigation/SendRouter.kt +++ b/features/send/api/src/main/kotlin/com/tangem/features/send/api/navigation/SendRouter.kt @@ -5,13 +5,4 @@ import androidx.fragment.app.Fragment interface SendRouter { fun getEntryFragment(): Fragment - - companion object { - const val CRYPTO_CURRENCY_KEY = "send_crypto_currency" - const val USER_WALLET_ID_KEY = "send_user_wallet_id" - const val TRANSACTION_ID_KEY = "send_transaction_id" - const val AMOUNT_KEY = "send_amount" - const val TAG_KEY = "send_tag" - const val DESTINATION_ADDRESS_KEY = "send_destination_address" - } } \ No newline at end of file diff --git a/features/send/impl/build.gradle.kts b/features/send/impl/build.gradle.kts index 7d61a5c6a0..3937749519 100644 --- a/features/send/impl/build.gradle.kts +++ b/features/send/impl/build.gradle.kts @@ -24,6 +24,7 @@ dependencies { implementation(deps.jodatime) implementation(deps.timber) implementation(deps.reKotlin) + implementation(deps.kotlin.serialization) /** Compose */ implementation(deps.compose.accompanist.systemUiController) @@ -52,6 +53,7 @@ dependencies { /** Common */ implementation(projects.common.ui) + implementation(projects.common.routing) /** Libs */ implementation(projects.libs.crypto) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/di/SendRouterModule.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/di/SendRouterModule.kt index 352d2aaf2b..b99742c742 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/di/SendRouterModule.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/di/SendRouterModule.kt @@ -1,6 +1,6 @@ package com.tangem.features.send.impl.di -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRouter import com.tangem.core.navigation.url.UrlOpener import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.send.impl.navigation.DefaultSendRouter @@ -19,7 +19,7 @@ internal object SendRouterModule { @Provides @ActivityScoped - fun provideSendRouter(reduxNavController: ReduxNavController, urlOpener: UrlOpener): SendRouter { - return DefaultSendRouter(reduxNavController, urlOpener) + fun provideSendRouter(appRouter: AppRouter, urlOpener: UrlOpener): SendRouter { + return DefaultSendRouter(appRouter, urlOpener) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/DefaultSendRouter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/DefaultSendRouter.kt index 59eade45d0..ed9f1f7b14 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/DefaultSendRouter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/DefaultSendRouter.kt @@ -1,20 +1,16 @@ package com.tangem.features.send.impl.navigation -import androidx.core.os.bundleOf import androidx.fragment.app.Fragment -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.core.navigation.url.UrlOpener import com.tangem.domain.qrscanning.models.SourceType import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.features.send.impl.presentation.SendFragment -import com.tangem.features.tokendetails.navigation.TokenDetailsRouter internal class DefaultSendRouter( - private val reduxNavController: ReduxNavController, + private val router: AppRouter, private val urlOpener: UrlOpener, ) : InnerSendRouter { @@ -25,26 +21,23 @@ internal class DefaultSendRouter( } override fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) { - reduxNavController.popBackStack() - reduxNavController.navigate( - action = NavigationAction.NavigateTo( - screen = AppScreen.WalletDetails, - bundle = bundleOf( - TokenDetailsRouter.USER_WALLET_ID_KEY to userWalletId.stringValue, - TokenDetailsRouter.CRYPTO_CURRENCY_KEY to currency, - ), - ), - ) + router.pop { isSuccess -> + if (isSuccess) { + router.push( + AppRoute.CurrencyDetails( + userWalletId = userWalletId, + currency = currency, + ), + ) + } + } } override fun openQrCodeScanner(network: String) { - reduxNavController.navigate( - action = NavigationAction.NavigateTo( - screen = AppScreen.QrScanning, - bundle = bundleOf( - QrScanningRouter.SOURCE_KEY to SourceType.SEND, - QrScanningRouter.NETWORK_KEY to network, - ), + router.push( + AppRoute.QrScanning( + source = SourceType.SEND, + networkName = network, ), ) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt index 1c7d7f9cee..0a3b32c382 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt @@ -5,6 +5,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.fragment.app.viewModels import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.components.SystemBarsEffect @@ -16,7 +18,6 @@ import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.features.send.impl.presentation.ui.SendScreen import com.tangem.features.send.impl.presentation.viewmodel.SendViewModel import dagger.hilt.android.AndroidEntryPoint -import java.lang.ref.WeakReference import javax.inject.Inject /** @@ -31,6 +32,9 @@ internal class SendFragment : ComposeFragment() { @Inject lateinit var router: SendRouter + @Inject + lateinit var appRouter: AppRouter + @Inject lateinit var analyticsEventsHandler: AnalyticsEventHandler @@ -44,11 +48,11 @@ internal class SendFragment : ComposeFragment() { super.onCreate(savedInstanceState) lifecycle.addObserver(viewModel) - val isEditingDisabled = arguments?.getString(SendRouter.TRANSACTION_ID_KEY) != null + val isEditingDisabled = arguments?.getString(AppRoute.Send.TRANSACTION_ID_KEY) != null viewModel.setRouter( innerSendRouter, StateRouter( - fragmentManager = WeakReference(parentFragmentManager), + appRouter = appRouter, isEditingDisabled = isEditingDisabled, analyticsEventsHandler = analyticsEventsHandler, ), diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt index e09467fa9c..6335c3b154 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt @@ -1,15 +1,14 @@ package com.tangem.features.send.impl.presentation.state -import androidx.fragment.app.FragmentManager +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update -import java.lang.ref.WeakReference internal class StateRouter( - private val fragmentManager: WeakReference, + private val appRouter: AppRouter, private val analyticsEventsHandler: AnalyticsEventHandler, private val isEditingDisabled: Boolean, ) { @@ -26,7 +25,7 @@ internal class StateRouter( } fun popBackStack() { - fragmentManager.get()?.popBackStack() + appRouter.pop() } fun onBackClick(isSuccess: Boolean = false) { diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt index 3d37fc0db7..7115f7b525 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt @@ -1,5 +1,6 @@ package com.tangem.features.send.impl.presentation.viewmodel +import android.os.Bundle import android.os.SystemClock import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -10,6 +11,8 @@ import arrow.core.getOrElse import arrow.core.left import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.bundle.unbundle import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase @@ -38,7 +41,6 @@ import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase -import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.send.impl.navigation.InnerSendRouter import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents @@ -101,17 +103,18 @@ internal class SendViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver, SendClickIntents { - private val userWalletId: UserWalletId = savedStateHandle.get(SendRouter.USER_WALLET_ID_KEY) - ?.let { stringValue -> UserWalletId(stringValue) } + private val userWalletId: UserWalletId = savedStateHandle.get(AppRoute.Send.USER_WALLET_ID_KEY) + ?.let { it.unbundle(UserWalletId.serializer()) } ?: error("This screen can't open without `UserWalletId`") - private val cryptoCurrency: CryptoCurrency = savedStateHandle[SendRouter.CRYPTO_CURRENCY_KEY] + private val cryptoCurrency: CryptoCurrency = savedStateHandle.get(AppRoute.Send.CRYPTO_CURRENCY_KEY) + ?.let { it.unbundle(CryptoCurrency.serializer()) } ?: error("This screen can't open without `CryptoCurrency`") - private val transactionId: String? = savedStateHandle[SendRouter.TRANSACTION_ID_KEY] - private val amount: String? = savedStateHandle[SendRouter.AMOUNT_KEY] - private val destinationAddress: String? = savedStateHandle[SendRouter.DESTINATION_ADDRESS_KEY] - private val memo: String? = savedStateHandle[SendRouter.TAG_KEY] + private val transactionId: String? = savedStateHandle[AppRoute.Send.TRANSACTION_ID_KEY] + private val amount: String? = savedStateHandle[AppRoute.Send.AMOUNT_KEY] + private val destinationAddress: String? = savedStateHandle[AppRoute.Send.DESTINATION_ADDRESS_KEY] + private val memo: String? = savedStateHandle[AppRoute.Send.TAG_KEY] private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() From 9d3759986ad492fae1a5d89ed55b6544ece2c3b8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 12:58:41 +0400 Subject: [PATCH 09/19] Updated on 2026-08-14 --- features/swap/presentation/build.gradle.kts | 1 + .../feature/swap/presentation/SwapFragment.kt | 12 +++------ .../tangem/feature/swap/router/SwapRouter.kt | 25 ++++++------------- .../feature/swap/viewmodels/SwapViewModel.kt | 10 +++++--- 4 files changed, 20 insertions(+), 28 deletions(-) diff --git a/features/swap/presentation/build.gradle.kts b/features/swap/presentation/build.gradle.kts index 6335a9d729..aa70954160 100644 --- a/features/swap/presentation/build.gradle.kts +++ b/features/swap/presentation/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { implementation(projects.core.navigation) implementation(projects.core.utils) implementation(projects.core.ui) + implementation(projects.common.routing) /** Domain modules **/ implementation(projects.domain.appCurrency) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt index 6409755135..51ac5b23a2 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/presentation/SwapFragment.kt @@ -5,7 +5,8 @@ import androidx.compose.animation.Crossfade import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.fragment.app.viewModels -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRouter + import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.components.SystemBarsEffect import com.tangem.core.ui.res.TangemTheme @@ -28,7 +29,7 @@ class SwapFragment : ComposeFragment() { override lateinit var uiDependencies: UiDependencies @Inject - lateinit var reduxNavController: ReduxNavController + lateinit var appRouter: AppRouter private val viewModel by viewModels() @@ -37,9 +38,8 @@ class SwapFragment : ComposeFragment() { lifecycle.addObserver(viewModel) viewModel.setRouter( SwapRouter( - fragmentManager = WeakReference(parentFragmentManager), customTabsManager = CustomTabsManager(WeakReference(context)), - reduxNavController = reduxNavController, + router = appRouter, ), ) } @@ -84,8 +84,4 @@ class SwapFragment : ComposeFragment() { lifecycle.removeObserver(viewModel) super.onDestroy() } - - companion object { - const val CURRENCY_BUNDLE_KEY = "swap_currency" - } } \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/router/SwapRouter.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/router/SwapRouter.kt index 081730868d..bc5009da3c 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/router/SwapRouter.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/router/SwapRouter.kt @@ -3,20 +3,14 @@ package com.tangem.feature.swap.router import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.core.os.bundleOf -import androidx.fragment.app.FragmentManager -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.features.tokendetails.navigation.TokenDetailsRouter -import java.lang.ref.WeakReference internal class SwapRouter( - private val fragmentManager: WeakReference, private val customTabsManager: CustomTabsManager, - private val reduxNavController: ReduxNavController, + private val router: AppRouter, ) { var currentScreen by mutableStateOf(SwapNavScreen.Main) @@ -30,7 +24,7 @@ internal class SwapRouter( if (currentScreen == SwapNavScreen.SelectToken) { currentScreen = SwapNavScreen.Main } else { - fragmentManager.get()?.popBackStack() + router.pop() } } @@ -39,13 +33,10 @@ internal class SwapRouter( } fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) { - reduxNavController.navigate( - action = NavigationAction.NavigateTo( - screen = AppScreen.WalletDetails, - bundle = bundleOf( - TokenDetailsRouter.USER_WALLET_ID_KEY to userWalletId.stringValue, - TokenDetailsRouter.CRYPTO_CURRENCY_KEY to currency, - ), + router.push( + AppRoute.CurrencyDetails( + userWalletId = userWalletId, + currency = currency, ), ) } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index 2adc1f3505..8f357bae4a 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -1,11 +1,14 @@ package com.tangem.feature.swap.viewmodels +import android.os.Bundle import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.* import arrow.core.Either import arrow.core.getOrElse +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.bundle.unbundle import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.Basic @@ -32,7 +35,6 @@ import com.tangem.feature.swap.domain.models.domain.* import com.tangem.feature.swap.domain.models.formatToUIRepresentation import com.tangem.feature.swap.domain.models.ui.* import com.tangem.feature.swap.models.* -import com.tangem.feature.swap.presentation.SwapFragment import com.tangem.feature.swap.router.SwapNavScreen import com.tangem.feature.swap.router.SwapRouter import com.tangem.feature.swap.ui.StateBuilder @@ -70,8 +72,10 @@ internal class SwapViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver { - private val initialCryptoCurrency: CryptoCurrency = - savedStateHandle[SwapFragment.CURRENCY_BUNDLE_KEY] ?: error("no expected parameter CryptoCurrency found`") + private val initialCryptoCurrency: CryptoCurrency = savedStateHandle.get(AppRoute.Swap.CURRENCY_BUNDLE_KEY) + ?.let { it.unbundle(CryptoCurrency.serializer()) } + ?: error("no expected parameter CryptoCurrency found`") + private lateinit var initialCryptoCurrencyStatus: CryptoCurrencyStatus private var isBalanceHidden = true From 40e0a8d0e55baf89e6f46341b48c40e55864f7fc Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 13:00:23 +0400 Subject: [PATCH 10/19] Updated on 2026-08-14 --- features/referral/presentation/build.gradle.kts | 1 + .../java/com/tangem/feature/referral/ReferralFragment.kt | 7 +++++-- .../com/tangem/feature/referral/router/ReferralRouter.kt | 9 +++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/features/referral/presentation/build.gradle.kts b/features/referral/presentation/build.gradle.kts index 900ab7de1b..8a99c77aa6 100644 --- a/features/referral/presentation/build.gradle.kts +++ b/features/referral/presentation/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { implementation(projects.core.utils) implementation(projects.core.ui) implementation(projects.libs.crypto) + implementation(projects.common.routing) /** AndroidX */ implementation(deps.androidx.appCompat) diff --git a/features/referral/presentation/src/main/java/com/tangem/feature/referral/ReferralFragment.kt b/features/referral/presentation/src/main/java/com/tangem/feature/referral/ReferralFragment.kt index 2ccd4b74fc..3d157aa005 100644 --- a/features/referral/presentation/src/main/java/com/tangem/feature/referral/ReferralFragment.kt +++ b/features/referral/presentation/src/main/java/com/tangem/feature/referral/ReferralFragment.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.fragment.app.viewModels +import com.tangem.common.routing.AppRouter import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.components.SystemBarsEffect import com.tangem.core.ui.res.TangemTheme @@ -13,7 +14,6 @@ import com.tangem.feature.referral.router.ReferralRouter import com.tangem.feature.referral.ui.ReferralScreen import com.tangem.feature.referral.viewmodels.ReferralViewModel import dagger.hilt.android.AndroidEntryPoint -import java.lang.ref.WeakReference import javax.inject.Inject @AndroidEntryPoint @@ -22,12 +22,15 @@ class ReferralFragment : ComposeFragment() { @Inject override lateinit var uiDependencies: UiDependencies + @Inject + internal lateinit var appRouter: AppRouter + private val viewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.onScreenOpened() - viewModel.setRouter(ReferralRouter(fragmentManager = WeakReference(parentFragmentManager))) + viewModel.setRouter(ReferralRouter(appRouter)) } @Composable diff --git a/features/referral/presentation/src/main/java/com/tangem/feature/referral/router/ReferralRouter.kt b/features/referral/presentation/src/main/java/com/tangem/feature/referral/router/ReferralRouter.kt index ecd73795bf..a8ac11e54b 100644 --- a/features/referral/presentation/src/main/java/com/tangem/feature/referral/router/ReferralRouter.kt +++ b/features/referral/presentation/src/main/java/com/tangem/feature/referral/router/ReferralRouter.kt @@ -1,11 +1,12 @@ package com.tangem.feature.referral.router -import androidx.fragment.app.FragmentManager -import java.lang.ref.WeakReference +import com.tangem.common.routing.AppRouter -internal class ReferralRouter(private val fragmentManager: WeakReference) { +internal class ReferralRouter( + private val appRouter: AppRouter, +) { fun back() { - fragmentManager.get()?.popBackStack() + appRouter.pop() } } \ No newline at end of file From ad88ca4b0872ab9aae7db5e3b0fc0e4c333156e7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 13:01:45 +0400 Subject: [PATCH 11/19] Updated on 2026-08-14 --- .../java/com/tangem/feature/qrscanning/QrScanningRouter.kt | 5 ----- features/qr-scanning/impl/build.gradle.kts | 1 + .../tangem/feature/qrscanning/di/QrScanningRouterModule.kt | 6 +++--- .../qrscanning/navigation/DefaultQrScanningRouter.kt | 7 ++++--- .../feature/qrscanning/viewmodel/QrScanningViewModel.kt | 7 +++---- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/features/qr-scanning/api/src/main/java/com/tangem/feature/qrscanning/QrScanningRouter.kt b/features/qr-scanning/api/src/main/java/com/tangem/feature/qrscanning/QrScanningRouter.kt index 7f5f0352b7..049e039209 100644 --- a/features/qr-scanning/api/src/main/java/com/tangem/feature/qrscanning/QrScanningRouter.kt +++ b/features/qr-scanning/api/src/main/java/com/tangem/feature/qrscanning/QrScanningRouter.kt @@ -5,9 +5,4 @@ import androidx.fragment.app.Fragment interface QrScanningRouter { fun getEntryFragment(): Fragment - - companion object { - const val SOURCE_KEY = "source" - const val NETWORK_KEY = "network" - } } \ No newline at end of file diff --git a/features/qr-scanning/impl/build.gradle.kts b/features/qr-scanning/impl/build.gradle.kts index 5ad7d8346d..f2dfb02aa4 100644 --- a/features/qr-scanning/impl/build.gradle.kts +++ b/features/qr-scanning/impl/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { implementation(projects.core.ui) implementation(projects.core.utils) implementation(projects.core.navigation) + implementation(projects.common.routing) implementation(deps.androidx.fragment.ktx) implementation(deps.androidx.activity.compose) diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/di/QrScanningRouterModule.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/di/QrScanningRouterModule.kt index 5bf6beee3f..b22a6c713f 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/di/QrScanningRouterModule.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/di/QrScanningRouterModule.kt @@ -1,6 +1,6 @@ package com.tangem.feature.qrscanning.di -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRouter import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.feature.qrscanning.navigation.DefaultQrScanningRouter import dagger.Module @@ -15,7 +15,7 @@ internal object QrScanningRouterModule { @Provides @ActivityScoped - fun provideQrScanRouter(reduxNavController: ReduxNavController): QrScanningRouter { - return DefaultQrScanningRouter(reduxNavController) + fun provideQrScanRouter(appRouter: AppRouter): QrScanningRouter { + return DefaultQrScanningRouter(appRouter) } } \ No newline at end of file diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/navigation/DefaultQrScanningRouter.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/navigation/DefaultQrScanningRouter.kt index 17de053b79..3813274b68 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/navigation/DefaultQrScanningRouter.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/navigation/DefaultQrScanningRouter.kt @@ -1,15 +1,16 @@ package com.tangem.feature.qrscanning.navigation import androidx.fragment.app.Fragment -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRouter + import com.tangem.feature.qrscanning.QrScanningFragment class DefaultQrScanningRouter( - private val reduxNavController: ReduxNavController, + private val router: AppRouter, ) : QrScanningInnerRouter { override fun getEntryFragment(): Fragment = QrScanningFragment.create() override fun popBackStack() { - reduxNavController.popBackStack() + router.pop() } } \ No newline at end of file diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt index cca4825f93..cfcd24a19b 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt @@ -3,8 +3,7 @@ package com.tangem.feature.qrscanning.viewmodel import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.tangem.feature.qrscanning.QrScanningRouter.Companion.NETWORK_KEY -import com.tangem.feature.qrscanning.QrScanningRouter.Companion.SOURCE_KEY +import com.tangem.common.routing.AppRoute import com.tangem.domain.qrscanning.models.SourceType import com.tangem.feature.qrscanning.navigation.QrScanningInnerRouter import com.tangem.feature.qrscanning.presentation.QrScanningState @@ -24,8 +23,8 @@ internal class QrScanningViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel() { - private val source: SourceType = savedStateHandle[SOURCE_KEY] ?: error("Source is mandatory") - private val network: String? = savedStateHandle[NETWORK_KEY] + private val source: SourceType = savedStateHandle[AppRoute.QrScanning.SOURCE_KEY] ?: error("Source is mandatory") + private val network: String? = savedStateHandle[AppRoute.QrScanning.NETWORK_KEY] val uiState: StateFlow = stateHolder.uiState val launchGalleryEvent: SharedFlow = clickIntents.launchGallery From d8c5f217bb5e6db04238e5484061ba1ed8aa5f8a Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 13:05:19 +0400 Subject: [PATCH 12/19] Updated on 2026-08-14 --- .../feature/onboarding/navigation/OnboardingRouter.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/OnboardingRouter.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/OnboardingRouter.kt index 5704da9b7f..b6bf1b0f60 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/OnboardingRouter.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/OnboardingRouter.kt @@ -4,9 +4,4 @@ package com.tangem.feature.onboarding.navigation * Onboarding router */ // TODO: Move to onboarding api module [REDACTED_JIRA] -interface OnboardingRouter { - - companion object { - const val CAN_SKIP_BACKUP = "onboarding_wallet_can_skip_backup" - } -} \ No newline at end of file +interface OnboardingRouter \ No newline at end of file From 0225de915a7432f7dc29f48539656aa64389c348 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 13:10:29 +0400 Subject: [PATCH 13/19] Updated on 2026-08-14 --- .../tap/features/details/ui/details/DetailsViewModel.kt | 4 +++- .../main/java/com/tangem/tap/routing/utils/ChildFactory.kt | 4 +++- .../kotlin/com/tangem/features/details/DetailsRouter.kt | 2 +- .../java/com/tangem/features/tester/api/TesterRouter.kt | 4 +++- .../tester/presentation/navigation/DefaultTesterRouter.kt | 6 ++---- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt index 0be2c4cda4..e0122f240b 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt @@ -127,7 +127,9 @@ internal class DetailsViewModel( } private fun navigateToTesterMenu() { - store.state.daggerGraphState.testerRouter?.startTesterScreen() + store.dispatchNavigationAction { + push(AppRoute.TesterMenu) + } } private fun navigateToToS() { diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 6e3ac96e92..5e0319cd2c 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -8,6 +8,7 @@ import com.tangem.feature.swap.presentation.SwapFragment import com.tangem.features.details.component.DetailsComponent import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.staking.api.navigation.StakingRouter +import com.tangem.features.tester.api.TesterRouter import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import com.tangem.features.wallet.navigation.WalletRouter import com.tangem.tap.features.customtoken.impl.presentation.AddCustomTokenFragment @@ -43,6 +44,7 @@ internal class ChildFactory @Inject constructor( private val walletRouter: WalletRouter, private val qrScanningRouter: QrScanningRouter, private val stakingRouter: StakingRouter, + private val testerRouter: TesterRouter, ) { @Suppress("LongMethod", "CyclomaticComplexMethod") @@ -133,7 +135,7 @@ internal class ChildFactory @Inject constructor( route.asFragmentChild(Provider { WelcomeFragment() }) } is AppRoute.TesterMenu -> { - TODO("Will be implemented later") + Child.LegacyIntent(testerRouter.getEntryIntent()) } is AppRoute.Staking -> { route.asFragmentChild(Provider { stakingRouter.getEntryFragment() }) diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt index 93e60c90ef..b723a9a48b 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt @@ -30,7 +30,7 @@ internal class DetailsRouter @Inject constructor( feedbackManager.sendEmail(FeedbackType.Feedback) } is DetailsRoute.TesterMenu -> { - testerRouter.startTesterScreen() + // testerRouter.startTesterScreen() } is DetailsRoute.Url -> { urlOpener.openUrl(route.url) diff --git a/features/tester/api/src/main/java/com/tangem/features/tester/api/TesterRouter.kt b/features/tester/api/src/main/java/com/tangem/features/tester/api/TesterRouter.kt index 913c04a076..7e647c8323 100644 --- a/features/tester/api/src/main/java/com/tangem/features/tester/api/TesterRouter.kt +++ b/features/tester/api/src/main/java/com/tangem/features/tester/api/TesterRouter.kt @@ -1,5 +1,7 @@ package com.tangem.features.tester.api +import android.content.Intent + /** * Outer tester feature router * @@ -8,5 +10,5 @@ package com.tangem.features.tester.api interface TesterRouter { /** Open tester menu */ - fun startTesterScreen() + fun getEntryIntent(): Intent } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/DefaultTesterRouter.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/DefaultTesterRouter.kt index 97126f3540..0d3c250e91 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/DefaultTesterRouter.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/navigation/DefaultTesterRouter.kt @@ -22,10 +22,8 @@ internal class DefaultTesterRouter @Inject constructor( private var navController: NavController? = null - override fun startTesterScreen() { - context.startActivity( - Intent(context, TesterActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), - ) + override fun getEntryIntent(): Intent { + return Intent(context, TesterActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } override fun setNavController(navController: NavController) { From 1526da80ebd4806934e276021f63ed6426e4b9f6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 15:22:15 +0400 Subject: [PATCH 14/19] Updated on 2026-08-14 --- .../com/tangem/tap/ApplicationEntryPoint.kt | 3 - .../java/com/tangem/tap/TangemApplication.kt | 5 -- .../tap/common/extensions/Navigation.kt | 8 +- .../tap/proxy/redux/DaggerGraphState.kt | 2 - .../tangem/tap/routing/utils/ChildFactory.kt | 17 +++-- features/details/api/build.gradle.kts | 1 + .../features/details/DetailsEntryPoint.kt | 13 ---- .../details/component/DetailsComponent.kt | 3 - .../component/UserWalletListComponent.kt | 0 features/details/impl/build.gradle.kts | 1 + .../features/details/DetailsFragment.kt | 74 ------------------- .../tangem/features/details/DetailsRouter.kt | 61 --------------- .../component/impl/DefaultDetailsComponent.kt | 4 - .../preview/PreviewDetailsComponent.kt | 10 +-- .../features/details/di/FeatureModule.kt | 8 -- .../features/details/routing/DetailsRoute.kt | 20 ----- .../features/details/ui/DetailsScreen.kt | 4 +- .../features/details/utils/ItemsBuilder.kt | 20 +++-- .../features/details/utils/SocialsBuilder.kt | 7 +- .../features/details/utils/UserWalletSaver.kt | 6 +- 20 files changed, 41 insertions(+), 226 deletions(-) delete mode 100644 features/details/api/src/main/kotlin/com/tangem/features/details/DetailsEntryPoint.kt rename features/details/{impl => api}/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt (85%) rename features/details/{impl => api}/src/main/kotlin/com/tangem/features/details/component/UserWalletListComponent.kt (100%) delete mode 100644 features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsFragment.kt delete mode 100644 features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt delete mode 100644 features/details/impl/src/main/kotlin/com/tangem/features/details/routing/DetailsRoute.kt diff --git a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt index 6f45b6b45b..24ce4e1822 100644 --- a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt +++ b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt @@ -29,7 +29,6 @@ import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase -import com.tangem.features.details.DetailsEntryPoint import com.tangem.features.details.DetailsFeatureToggles import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles import com.tangem.features.send.api.featuretoggles.SendFeatureToggles @@ -112,8 +111,6 @@ interface ApplicationEntryPoint { fun getDetailsFeatureToggles(): DetailsFeatureToggles - fun getDetailsEntryPoint(): DetailsEntryPoint - fun getUrlOpener(): UrlOpener fun getShareManager(): ShareManager diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index 5ecb0622ca..e4c530273b 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -44,7 +44,6 @@ import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase -import com.tangem.features.details.DetailsEntryPoint import com.tangem.features.details.DetailsFeatureToggles import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles import com.tangem.features.send.api.featuretoggles.SendFeatureToggles @@ -186,9 +185,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { private val detailsFeatureToggles: DetailsFeatureToggles get() = entryPoint.getDetailsFeatureToggles() - private val detailsEntryPoint: DetailsEntryPoint - get() = entryPoint.getDetailsEntryPoint() - private val urlOpener get() = entryPoint.getUrlOpener() @@ -285,7 +281,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { blockchainSDKFactory = blockchainSDKFactory, saveBlockchainErrorUseCase = saveBlockchainErrorUseCase, detailsFeatureToggles = detailsFeatureToggles, - detailsEntryPoint = detailsEntryPoint, urlOpener = urlOpener, shareManager = shareManager, appRouter = appRouter, diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 8282d27be6..d7f3aad533 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -182,13 +182,7 @@ private fun fragmentFactory(screen: AppScreen): Fragment { } } AppScreen.Details -> { - val featureToggles = store.inject(getDependency = DaggerGraphState::detailsFeatureToggles) - - if (featureToggles.isRedesignEnabled) { - store.inject(DaggerGraphState::detailsEntryPoint).entryFragment() - } else { - DetailsFragment() - } + DetailsFragment() } AppScreen.DetailsSecurity -> SecurityModeFragment() AppScreen.CardSettings -> CardSettingsFragment() diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 08744d3939..8a555f904d 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -27,7 +27,6 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.feature.qrscanning.QrScanningRouter -import com.tangem.features.details.DetailsEntryPoint import com.tangem.features.details.DetailsFeatureToggles import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles import com.tangem.features.managetokens.navigation.ManageTokensUi @@ -82,7 +81,6 @@ data class DaggerGraphState( val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase? = null, val assetLoader: AssetLoader? = null, val detailsFeatureToggles: DetailsFeatureToggles? = null, - val detailsEntryPoint: DetailsEntryPoint? = null, val stakingRouter: StakingRouter? = null, val urlOpener: UrlOpener? = null, val shareManager: ShareManager? = null, diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 5e0319cd2c..3199a16968 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -5,6 +5,7 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.feature.referral.ReferralFragment import com.tangem.feature.swap.presentation.SwapFragment +import com.tangem.features.details.DetailsFeatureToggles import com.tangem.features.details.component.DetailsComponent import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.staking.api.navigation.StakingRouter @@ -16,6 +17,7 @@ import com.tangem.tap.features.details.ui.appcurrency.AppCurrencySelectorFragmen import com.tangem.tap.features.details.ui.appsettings.AppSettingsFragment import com.tangem.tap.features.details.ui.cardsettings.CardSettingsFragment import com.tangem.tap.features.details.ui.cardsettings.coderecovery.AccessCodeRecoveryFragment +import com.tangem.tap.features.details.ui.details.DetailsFragment import com.tangem.tap.features.details.ui.resetcard.ResetCardFragment import com.tangem.tap.features.details.ui.securitymode.SecurityModeFragment import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment @@ -45,6 +47,7 @@ internal class ChildFactory @Inject constructor( private val qrScanningRouter: QrScanningRouter, private val stakingRouter: StakingRouter, private val testerRouter: TesterRouter, + private val detailsFeatureToggles: DetailsFeatureToggles, ) { @Suppress("LongMethod", "CyclomaticComplexMethod") @@ -80,11 +83,15 @@ internal class ChildFactory @Inject constructor( route.asFragmentChild(Provider { CardSettingsFragment() }) } is AppRoute.Details -> { - route.asComponentChild( - contextProvider = contextProvider(route, contextFactory), - params = DetailsComponent.Params(route.userWalletId), - componentFactory = detailsComponentFactory, - ) + if (detailsFeatureToggles.isRedesignEnabled) { + route.asComponentChild( + contextProvider = contextProvider(route, contextFactory), + params = DetailsComponent.Params(route.userWalletId), + componentFactory = detailsComponentFactory, + ) + } else { + route.asFragmentChild(Provider { DetailsFragment() }) + } } is AppRoute.DetailsSecurity -> { route.asFragmentChild(Provider { SecurityModeFragment() }) diff --git a/features/details/api/build.gradle.kts b/features/details/api/build.gradle.kts index 5010f64d85..103bd2c268 100644 --- a/features/details/api/build.gradle.kts +++ b/features/details/api/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { /* Project - Core */ implementation(projects.core.decompose) + implementation(projects.core.ui) /* AndroidX */ implementation(deps.androidx.fragment.ktx) diff --git a/features/details/api/src/main/kotlin/com/tangem/features/details/DetailsEntryPoint.kt b/features/details/api/src/main/kotlin/com/tangem/features/details/DetailsEntryPoint.kt deleted file mode 100644 index d3ac9fe6f7..0000000000 --- a/features/details/api/src/main/kotlin/com/tangem/features/details/DetailsEntryPoint.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.features.details - -import androidx.fragment.app.Fragment - -interface DetailsEntryPoint { - - fun entryFragment(): Fragment - - companion object { - - const val USER_WALLET_ID_KEY = "user_wallet_id" - } -} \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt b/features/details/api/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt similarity index 85% rename from features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt rename to features/details/api/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt index da63581436..f53bc84d85 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt +++ b/features/details/api/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt @@ -1,6 +1,5 @@ package com.tangem.features.details.component -import androidx.compose.material3.SnackbarHostState import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.ComposableContentComponent @@ -8,8 +7,6 @@ import com.tangem.domain.wallets.models.UserWalletId interface DetailsComponent : ComposableContentComponent { - val snackbarHostState: SnackbarHostState - interface Factory : ComponentFactory { override fun create(context: AppComponentContext, params: Params): DetailsComponent diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/UserWalletListComponent.kt b/features/details/api/src/main/kotlin/com/tangem/features/details/component/UserWalletListComponent.kt similarity index 100% rename from features/details/impl/src/main/kotlin/com/tangem/features/details/component/UserWalletListComponent.kt rename to features/details/api/src/main/kotlin/com/tangem/features/details/component/UserWalletListComponent.kt diff --git a/features/details/impl/build.gradle.kts b/features/details/impl/build.gradle.kts index 8a26e80f40..8b6951e1e9 100644 --- a/features/details/impl/build.gradle.kts +++ b/features/details/impl/build.gradle.kts @@ -23,6 +23,7 @@ dependencies { implementation(projects.core.featuretoggles) implementation(projects.core.navigation) implementation(projects.core.analytics.models) + implementation(projects.common.routing) /* Project - Domain */ implementation(projects.domain.models) diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsFragment.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsFragment.kt deleted file mode 100644 index 4bd1e416ee..0000000000 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsFragment.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.tangem.features.details - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.fragment.app.Fragment -import com.arkivanov.decompose.defaultComponentContext -import com.tangem.core.decompose.context.AppComponentContext -import com.tangem.core.decompose.context.childByContext -import com.tangem.core.decompose.di.RootAppComponentContext -import com.tangem.core.ui.UiDependencies -import com.tangem.core.ui.message.EventMessageEffect -import com.tangem.core.ui.message.EventMessageHandler -import com.tangem.core.ui.screen.ComposeFragment -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.features.details.component.DetailsComponent -import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject - -// TODO: Remove after [REDACTED_JIRA] -@AndroidEntryPoint -internal class DetailsFragment : ComposeFragment() { - - @Inject - override lateinit var uiDependencies: UiDependencies - - @Inject - internal lateinit var componentFactory: DetailsComponent.Factory - - @Inject - internal lateinit var detailsRouter: DetailsRouter - - @Inject - @RootAppComponentContext - internal lateinit var rootContext: AppComponentContext - - private val component: DetailsComponent by lazy { initComponent() } - - private val messageHandler = EventMessageHandler() - - @Composable - override fun ScreenContent(modifier: Modifier) { - component.Content(modifier = modifier) - - EventMessageEffect( - messageHandler = messageHandler, - snackbarHostState = component.snackbarHostState, - ) - } - - private fun initComponent(): DetailsComponent { - val selectedUserWalletId = arguments?.getString(DetailsEntryPoint.USER_WALLET_ID_KEY) - ?.let(::UserWalletId) - - requireNotNull(selectedUserWalletId) { "UserWalletId must be provided" } - - val context = rootContext.childByContext( - componentContext = defaultComponentContext(requireActivity().onBackPressedDispatcher), - messageHandler = messageHandler, - router = detailsRouter, - ) - - return componentFactory.create( - context = context, - params = DetailsComponent.Params( - selectedUserWalletId = selectedUserWalletId, - ), - ) - } - - companion object : DetailsEntryPoint { - - override fun entryFragment(): Fragment = DetailsFragment() - } -} \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt deleted file mode 100644 index b723a9a48b..0000000000 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.tangem.features.details - -import com.tangem.core.decompose.navigation.Route -import com.tangem.core.decompose.navigation.Router -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.ReduxNavController -import com.tangem.core.navigation.feedback.FeedbackManager -import com.tangem.core.navigation.feedback.FeedbackType -import com.tangem.core.navigation.url.UrlOpener -import com.tangem.features.details.routing.DetailsRoute -import com.tangem.features.tester.api.TesterRouter -import javax.inject.Inject -import kotlin.reflect.KClass - -// TODO: Remove after [REDACTED_JIRA] -internal class DetailsRouter @Inject constructor( - private val reduxNavController: ReduxNavController, - private val feedbackManager: FeedbackManager, - private val testerRouter: TesterRouter, - private val urlOpener: UrlOpener, -) : Router { - - override fun push(route: Route, onComplete: (isSuccess: Boolean) -> Unit) { - if (route is DetailsRoute) { - when (route) { - is DetailsRoute.Screen -> { - reduxNavController.navigate(NavigationAction.NavigateTo(route.screen, bundle = route.params)) - } - is DetailsRoute.Feedback -> { - feedbackManager.sendEmail(FeedbackType.Feedback) - } - is DetailsRoute.TesterMenu -> { - // testerRouter.startTesterScreen() - } - is DetailsRoute.Url -> { - urlOpener.openUrl(route.url) - } - } - onComplete(true) - } else { - onComplete(false) - } - } - - override fun pop(onComplete: (isSuccess: Boolean) -> Unit) { - reduxNavController.popBackStack() - onComplete(true) - } - - override fun popTo(routeClass: KClass, onComplete: (isSuccess: Boolean) -> Unit) { - TODO("This class will be removed in future") - } - - override fun popTo(route: Route, onComplete: (isSuccess: Boolean) -> Unit) { - TODO("This class will be removed in future") - } - - override fun replaceAll(vararg routes: Route, onComplete: (isSuccess: Boolean) -> Unit) { - TODO("This class will be removed in future") - } -} \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/impl/DefaultDetailsComponent.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/impl/DefaultDetailsComponent.kt index bc9a891d84..08d302a183 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/impl/DefaultDetailsComponent.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/impl/DefaultDetailsComponent.kt @@ -1,6 +1,5 @@ package com.tangem.features.details.component.impl -import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier @@ -28,8 +27,6 @@ internal class DefaultDetailsComponent @AssistedInject constructor( context = child(key = "user_wallet_list"), ) - override val snackbarHostState: SnackbarHostState = SnackbarHostState() - init { model.provideUserWalletId(params.selectedUserWalletId) } @@ -41,7 +38,6 @@ internal class DefaultDetailsComponent @AssistedInject constructor( DetailsScreen( modifier = modifier, state = state, - snackbarHostState = snackbarHostState, userWalletListBlockContent = userWalletListComponent, ) } diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewDetailsComponent.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewDetailsComponent.kt index d91ffd5ce1..ccbc7f2409 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewDetailsComponent.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewDetailsComponent.kt @@ -1,9 +1,10 @@ package com.tangem.features.details.component.preview -import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.core.decompose.navigation.DummyRouter +import com.tangem.core.navigation.feedback.DummyFeedbackManager +import com.tangem.core.navigation.url.DummyUrlOpener import com.tangem.features.details.component.DetailsComponent import com.tangem.features.details.entity.DetailsFooterUM import com.tangem.features.details.entity.DetailsUM @@ -14,16 +15,16 @@ import kotlinx.coroutines.runBlocking internal class PreviewDetailsComponent : DetailsComponent { - override val snackbarHostState: SnackbarHostState = SnackbarHostState() - private val previewBlocks = runBlocking { ItemsBuilder( router = DummyRouter(), + urlOpener = DummyUrlOpener(), + feedbackManager = DummyFeedbackManager(), ).buldAll(isWalletConnectAvailable = true) } private val previewFooter = DetailsFooterUM( - socials = SocialsBuilder(DummyRouter()).buildAll(), + socials = SocialsBuilder(DummyUrlOpener()).buildAll(), appVersion = "1.0.0-preview", ) @@ -39,7 +40,6 @@ internal class PreviewDetailsComponent : DetailsComponent { DetailsScreen( modifier = modifier, state = previewState, - snackbarHostState = snackbarHostState, userWalletListBlockContent = PreviewUserWalletListComponent(), ) } diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/di/FeatureModule.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/di/FeatureModule.kt index f9a437ee25..44a655a33f 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/di/FeatureModule.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/di/FeatureModule.kt @@ -2,9 +2,7 @@ package com.tangem.features.details.di import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.features.details.DefaultDetailsFeatureToggles -import com.tangem.features.details.DetailsEntryPoint import com.tangem.features.details.DetailsFeatureToggles -import com.tangem.features.details.DetailsFragment import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -20,10 +18,4 @@ internal object FeatureModule { fun provideFeatureToggles(featureTogglesManager: FeatureTogglesManager): DetailsFeatureToggles { return DefaultDetailsFeatureToggles(featureTogglesManager) } - - @Provides - @Singleton - fun provideEntryPoint(): DetailsEntryPoint { - return DetailsFragment.Companion - } } \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/routing/DetailsRoute.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/routing/DetailsRoute.kt deleted file mode 100644 index 878987aca5..0000000000 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/routing/DetailsRoute.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.tangem.features.details.routing - -import android.os.Bundle -import com.tangem.core.decompose.navigation.Route -import com.tangem.core.navigation.AppScreen - -// TODO: Remove after [REDACTED_JIRA] -internal sealed class DetailsRoute : Route { - - data class Screen( - val screen: AppScreen, - val params: Bundle? = null, - ) : DetailsRoute() - - data class Url(val url: String) : DetailsRoute() - - data object Feedback : DetailsRoute() - - data object TesterMenu : DetailsRoute() -} \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt index 95413f3fc5..af683ca7a8 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.ComposableContentComponent import com.tangem.core.ui.components.SystemBarsEffect import com.tangem.core.ui.components.snackbar.TangemSnackbarHost +import com.tangem.core.ui.res.LocalSnackbarHostState import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.details.component.preview.PreviewDetailsComponent @@ -35,7 +36,6 @@ private const val COLLAPSED_APP_BAR_THRESHOLD = 0.4f @Composable internal fun DetailsScreen( state: DetailsUM, - snackbarHostState: SnackbarHostState, userWalletListBlockContent: ComposableContentComponent, modifier: Modifier = Modifier, ) { @@ -54,7 +54,7 @@ internal fun DetailsScreen( snackbarHost = { TangemSnackbarHost( modifier = Modifier.padding(all = TangemTheme.dimens.spacing16), - hostState = snackbarHostState, + hostState = LocalSnackbarHostState.current, ) }, topBar = { TopBar(state, scrollBehavior) }, diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt index fb7e8768e4..9e87499c97 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt @@ -1,14 +1,16 @@ package com.tangem.features.details.utils +import com.tangem.common.routing.AppRoute import com.tangem.core.decompose.di.ComponentScoped import com.tangem.core.decompose.navigation.Router -import com.tangem.core.navigation.AppScreen +import com.tangem.core.navigation.feedback.FeedbackManager +import com.tangem.core.navigation.feedback.FeedbackType +import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.features.details.entity.DetailsItemUM import com.tangem.features.details.impl.BuildConfig import com.tangem.features.details.impl.R -import com.tangem.features.details.routing.DetailsRoute import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -17,6 +19,8 @@ import javax.inject.Inject @ComponentScoped internal class ItemsBuilder @Inject constructor( private val router: Router, + private val urlOpener: UrlOpener, + private val feedbackManager: FeedbackManager, ) { suspend fun buldAll(isWalletConnectAvailable: Boolean): ImmutableList = buildList { @@ -30,7 +34,7 @@ internal class ItemsBuilder @Inject constructor( private suspend fun buildWalletConnectBlock(isWalletConnectAvailable: Boolean): DetailsItemUM? { return if (isWalletConnectAvailable) { DetailsItemUM.WalletConnect( - onClick = { router.push(DetailsRoute.Screen(AppScreen.WalletConnectSessions)) }, + onClick = { router.push(AppRoute.WalletConnectSessions) }, ) } else { null @@ -46,7 +50,7 @@ internal class ItemsBuilder @Inject constructor( id = "buy_tangem_wallet", title = resourceReference(R.string.details_buy_wallet), iconRes = R.drawable.ic_tangem_24, - onClick = { router.push(DetailsRoute.Url(BUY_TANGEM_URL)) }, + onClick = { urlOpener.openUrl(BUY_TANGEM_URL) }, ), ), ) @@ -58,7 +62,7 @@ internal class ItemsBuilder @Inject constructor( id = "app_settings", title = resourceReference(R.string.app_settings_title), iconRes = R.drawable.ic_settings_24, - onClick = { router.push(DetailsRoute.Screen(AppScreen.AppSettings)) }, + onClick = { router.push(AppRoute.AppSettings) }, ).let(::add) if (BuildConfig.TESTER_MENU_ENABLED) { @@ -66,7 +70,7 @@ internal class ItemsBuilder @Inject constructor( id = "tester_menu", title = stringReference(value = "Tester menu"), iconRes = R.drawable.ic_alert_24, - onClick = { router.push(DetailsRoute.TesterMenu) }, + onClick = { router.push(AppRoute.TesterMenu) }, ).let(::add) } }.toImmutableList(), @@ -79,13 +83,13 @@ internal class ItemsBuilder @Inject constructor( id = "send_feedback", title = resourceReference(R.string.details_send_feedback), iconRes = R.drawable.ic_comment_24, - onClick = { router.push(DetailsRoute.Feedback) }, + onClick = { feedbackManager.sendEmail(FeedbackType.Feedback) }, ), DetailsItemUM.Basic.Item( id = "disclaimer", title = resourceReference(R.string.disclaimer_title), iconRes = R.drawable.ic_text_24, - onClick = { router.push(DetailsRoute.Screen(AppScreen.Disclaimer)) }, + onClick = { router.push(AppRoute.Disclaimer) }, ), ), ) diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/SocialsBuilder.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/SocialsBuilder.kt index 9d54a022c2..8282bbd41c 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/SocialsBuilder.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/SocialsBuilder.kt @@ -2,17 +2,16 @@ package com.tangem.features.details.utils import androidx.compose.ui.text.intl.Locale import com.tangem.core.decompose.di.ComponentScoped -import com.tangem.core.decompose.navigation.Router +import com.tangem.core.navigation.url.UrlOpener import com.tangem.features.details.entity.DetailsFooterUM import com.tangem.features.details.impl.R -import com.tangem.features.details.routing.DetailsRoute import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import javax.inject.Inject @ComponentScoped internal class SocialsBuilder @Inject constructor( - private val router: Router, + private val urlOpener: UrlOpener, ) { fun buildAll(): ImmutableList = Social.all.map { social -> @@ -32,7 +31,7 @@ internal class SocialsBuilder @Inject constructor( social.url } - router.push(DetailsRoute.Url(url)) + urlOpener.openUrl(url) } private enum class Social( diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt index cd539ea937..f1bb1fd398 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt @@ -2,9 +2,11 @@ package com.tangem.features.details.utils import arrow.core.raise.* import arrow.core.recover +import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.decompose.di.ComponentScoped import com.tangem.core.decompose.navigation.Router +import com.tangem.core.decompose.navigation.popTo import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.isNullOrEmpty @@ -42,7 +44,7 @@ internal class UserWalletSaver @Inject constructor( saveWallet(userWallet) - // router.popTo(DetailsRoute.Screen(AppScreen.Wallet)) + router.popTo() }, recover = { error -> val message = error.message @@ -75,7 +77,7 @@ internal class UserWalletSaver @Inject constructor( selectWalletUseCase(userWallet.walletId).bind() } - // router.popTo(DetailsRoute.Screen(AppScreen.Wallet)) + router.popTo() } private suspend fun Raise.createUserWallet(response: ScanResponse): UserWallet { From 7bd4bd3d52cf54a2a7c81991e87ba9f2e065013d Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 15:28:09 +0400 Subject: [PATCH 15/19] Updated on 2026-08-14 --- .../com/tangem/tap/LockUserWalletsTimer.kt | 11 +- .../main/java/com/tangem/tap/MainActivity.kt | 5 - .../tap/common/extensions/Navigation.kt | 165 +----------------- .../com/tangem/tap/common/redux/AppReducer.kt | 2 - .../com/tangem/tap/common/redux/AppState.kt | 4 - .../redux/navigation/NavigationMiddleware.kt | 60 ------- .../redux/navigation/NavigationReducer.kt | 39 ----- .../com/tangem/tap/di/AppStateHolderModule.kt | 5 - .../com/tangem/tap/proxy/AppStateHolder.kt | 16 +- .../core/navigation/NavigationAction.kt | 25 --- .../tangem/core/navigation/NavigationState.kt | 39 ----- .../core/navigation/ReduxNavController.kt | 16 -- ...mentShareTransition.kt => ShareElement.kt} | 10 -- 13 files changed, 9 insertions(+), 388 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt delete mode 100644 core/navigation/src/main/java/com/tangem/core/navigation/NavigationAction.kt delete mode 100644 core/navigation/src/main/java/com/tangem/core/navigation/NavigationState.kt delete mode 100644 core/navigation/src/main/java/com/tangem/core/navigation/ReduxNavController.kt rename core/navigation/src/main/java/com/tangem/core/navigation/{FragmentShareTransition.kt => ShareElement.kt} (74%) diff --git a/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt b/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt index bb6e616898..8a80aff9f7 100644 --- a/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt +++ b/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt @@ -3,13 +3,12 @@ package com.tangem.tap import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.utils.popTo import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.asLockable -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.dispatchWithMain +import com.tangem.tap.common.extensions.dispatchNavigationAction import kotlinx.coroutines.* import timber.log.Timber import kotlin.time.Duration @@ -50,7 +49,7 @@ internal class LockUserWalletsTimer( start() if (shouldOpenWelcomeScreenOnResume) { - store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome)) + store.dispatchNavigationAction { popTo() } settingsRepository.setShouldOpenWelcomeScreenOnResume(value = false) } } @@ -128,7 +127,7 @@ internal class LockUserWalletsTimer( if (wasApplicationStopped) { settingsRepository.setShouldOpenWelcomeScreenOnResume(value = true) } else { - store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Welcome)) + store.dispatchNavigationAction { popTo() } } } } diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index ed2a8d35af..32ce55bfb5 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -36,7 +36,6 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.di.RootAppComponentContext import com.tangem.core.deeplink.DeepLinksRegistry -import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.email.EmailSender import com.tangem.core.ui.event.StateEvent import com.tangem.core.ui.extensions.TextReference @@ -86,7 +85,6 @@ import com.tangem.wallet.databinding.ActivityMainBinding import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.* import kotlinx.coroutines.flow.* -import java.lang.ref.WeakReference import javax.inject.Inject import kotlin.coroutines.CoroutineContext @@ -261,8 +259,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac } private fun installActivityDependencies() { - store.dispatch(NavigationAction.ActivityCreated(WeakReference(this))) - cardSdkLifecycleObserver.onCreate(context = this) tangemSdkManager = injectedTangemSdkManager appStateHolder.tangemSdkManager = tangemSdkManager @@ -376,7 +372,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac } override fun onDestroy() { - store.dispatch(NavigationAction.ActivityDestroyed(WeakReference(this))) intentProcessor.removeAll() cardSdkLifecycleObserver.onDestroy(this) super.onDestroy() diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index d7f3aad533..251edacdf4 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -1,33 +1,8 @@ package com.tangem.tap.common.extensions -import android.os.Bundle -import androidx.fragment.app.* -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.FragmentShareTransition -import com.tangem.feature.referral.ReferralFragment -import com.tangem.feature.swap.presentation.SwapFragment -import com.tangem.tap.features.customtoken.impl.presentation.AddCustomTokenFragment -import com.tangem.tap.features.details.ui.appcurrency.AppCurrencySelectorFragment -import com.tangem.tap.features.details.ui.appsettings.AppSettingsFragment -import com.tangem.tap.features.details.ui.cardsettings.CardSettingsFragment -import com.tangem.tap.features.details.ui.cardsettings.coderecovery.AccessCodeRecoveryFragment -import com.tangem.tap.features.details.ui.details.DetailsFragment -import com.tangem.tap.features.details.ui.resetcard.ResetCardFragment -import com.tangem.tap.features.details.ui.securitymode.SecurityModeFragment -import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment -import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment -import com.tangem.tap.features.home.HomeFragment -import com.tangem.tap.features.main.ui.ModalNotificationBottomSheetFragment -import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment -import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment -import com.tangem.tap.features.onboarding.products.twins.ui.OnboardingTwinsFragment -import com.tangem.tap.features.onboarding.products.wallet.ui.OnboardingWalletFragment -import com.tangem.tap.features.saveWallet.ui.SaveWalletBottomSheetFragment -import com.tangem.tap.features.send.ui.SendFragment -import com.tangem.tap.features.tokens.impl.presentation.TokensListFragment -import com.tangem.tap.features.welcome.ui.WelcomeFragment -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.store +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager import com.tangem.utils.Provider import com.tangem.wallet.R import timber.log.Timber @@ -73,138 +48,4 @@ private fun Fragment.showFragment(fragmentManager: FragmentManager, name: String transaction.addToBackStack(name) transaction.commitAllowingStateLoss() -} - -fun FragmentActivity.openFragment( - screen: AppScreen, - addToBackstack: Boolean, - bundle: Bundle? = null, - fgShareTransition: FragmentShareTransition? = null, -) { - val transaction = supportFragmentManager.beginTransaction().apply { - setReorderingAllowed(true) - } - - val fragment = fragmentFactory(screen).apply { - arguments = bundle - - if (fgShareTransition != null) { - sharedElementEnterTransition = fgShareTransition.enterTransitionSet - sharedElementReturnTransition = fgShareTransition.exitTransitionSet - fgShareTransition.shareElements.forEach { shareElement -> - shareElement.wView.get()?.let { view -> - transaction.addSharedElement(view, shareElement.elementName) - } - } - } - } - - if (screen.isDialogFragment && fragment is DialogFragment) { - fragment.showAllowingStateLoss( - fragmentManager = supportFragmentManager, - baseTransaction = transaction, - tag = screen.name, - addToBackstack = addToBackstack, - ) - } else { - transaction.replace(R.id.fragment_container, fragment, screen.name) - if (addToBackstack) { - transaction.addToBackStack(screen.name) - } - transaction.commitAllowingStateLoss() - } -} - -private fun DialogFragment.showAllowingStateLoss( - fragmentManager: FragmentManager, - baseTransaction: FragmentTransaction, - tag: String, - addToBackstack: Boolean, -) { - runCatching { - if (addToBackstack) baseTransaction.addToBackStack(tag) - show(baseTransaction, tag) - } - .onFailure { throwable -> - if (throwable is IllegalStateException) { - val transaction = fragmentManager.beginTransaction() - transaction.add(this, tag) - if (addToBackstack) transaction.addToBackStack(tag) - - transaction.commitAllowingStateLoss() - } else { - Timber.e(throwable) - } - } -} - -fun FragmentActivity.popBackTo(screen: AppScreen?, inclusive: Boolean = false) { - val inclusiveFlag = if (inclusive) FragmentManager.POP_BACK_STACK_INCLUSIVE else 0 - try { - this.supportFragmentManager.popBackStack(screen?.name, inclusiveFlag) - } catch (e: IllegalStateException) { - Timber.e(e) - } -} - -fun FragmentActivity.getPreviousScreen(): AppScreen? { - val indexOfLastFragment = if (this.supportFragmentManager.backStackEntryCount > 0) { - this.supportFragmentManager.backStackEntryCount - 1 - } else { - 0 - } - val tag = if (indexOfLastFragment < this.supportFragmentManager.backStackEntryCount) { - this.supportFragmentManager.getBackStackEntryAt(indexOfLastFragment).name - } else { - null - } - return tag?.let { AppScreen.valueOf(tag) } -} - -@Suppress("ComplexMethod", "LongMethod") -private fun fragmentFactory(screen: AppScreen): Fragment { - return when (screen) { - AppScreen.Home -> HomeFragment() - AppScreen.OnboardingNote -> OnboardingNoteFragment() - AppScreen.OnboardingWallet -> OnboardingWalletFragment() - AppScreen.OnboardingTwins -> OnboardingTwinsFragment() - AppScreen.OnboardingOther -> OnboardingOtherCardsFragment() - AppScreen.Wallet -> { - store.inject(getDependency = DaggerGraphState::walletRouter).getEntryFragment() - } - AppScreen.Send -> { - val featureToggles = store.inject(getDependency = DaggerGraphState::sendFeatureToggles) - - if (featureToggles.isRedesignedSendEnabled) { - store.inject(getDependency = DaggerGraphState::sendRouter).getEntryFragment() - } else { - SendFragment() - } - } - AppScreen.Details -> { - DetailsFragment() - } - AppScreen.DetailsSecurity -> SecurityModeFragment() - AppScreen.CardSettings -> CardSettingsFragment() - AppScreen.AppSettings -> AppSettingsFragment() - AppScreen.ResetToFactory -> ResetCardFragment() - AppScreen.AccessCodeRecovery -> AccessCodeRecoveryFragment() - AppScreen.Disclaimer -> DisclaimerFragment() - AppScreen.ManageTokens -> TokensListFragment() - AppScreen.AddCustomToken -> AddCustomTokenFragment() - AppScreen.WalletDetails -> { - store.inject(getDependency = DaggerGraphState::tokenDetailsRouter).getEntryFragment() - } - AppScreen.WalletConnectSessions -> WalletConnectFragment() - AppScreen.QrScanning -> { - store.inject(getDependency = DaggerGraphState::qrScanningRouter).getEntryFragment() - } - AppScreen.ReferralProgram -> ReferralFragment() - AppScreen.Swap -> SwapFragment() - AppScreen.Welcome -> WelcomeFragment() - AppScreen.SaveWallet -> SaveWalletBottomSheetFragment() - AppScreen.AppCurrencySelector -> AppCurrencySelectorFragment() - AppScreen.ModalNotification -> ModalNotificationBottomSheetFragment() - AppScreen.Staking -> store.inject(getDependency = DaggerGraphState::stakingRouter).getEntryFragment() - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 7adedbed8f..d047620b5e 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -1,7 +1,6 @@ package com.tangem.tap.common.redux import com.tangem.tap.common.redux.global.globalReducer -import com.tangem.tap.common.redux.navigation.NavigationReducer import com.tangem.tap.features.details.redux.DetailsReducer import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer @@ -23,7 +22,6 @@ fun appReducer(action: Action, state: AppState?, appStateHolder: AppStateHolder) if (action is AppAction.RestoreState) return action.state return AppState( - navigationState = NavigationReducer.reduce(action, state), globalState = globalReducer(action, state, appStateHolder), homeState = HomeReducer.reduce(action, state), onboardingNoteState = OnboardingNoteReducer.reduce(action, state), diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 59b899ee8b..2a83cbf4df 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -1,13 +1,11 @@ package com.tangem.tap.common.redux -import com.tangem.core.navigation.NavigationState import com.tangem.domain.redux.DomainState import com.tangem.domain.redux.domainStore import com.tangem.domain.redux.global.NetworkServices import com.tangem.tap.common.redux.global.GlobalMiddleware import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.common.redux.legacy.LegacyMiddleware -import com.tangem.tap.common.redux.navigation.navigationMiddleware import com.tangem.tap.features.details.redux.DetailsMiddleware import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.features.details.redux.walletconnect.WalletConnectMiddleware @@ -40,7 +38,6 @@ import org.rekotlin.Middleware import org.rekotlin.StateType data class AppState( - val navigationState: NavigationState = NavigationState(), val globalState: GlobalState = GlobalState(), val homeState: HomeState = HomeState(), val onboardingNoteState: OnboardingNoteState = OnboardingNoteState(), @@ -73,7 +70,6 @@ data class AppState( fun getMiddleware(): List> { return listOf( logMiddleware, - navigationMiddleware, notificationsMiddleware, GlobalMiddleware.handler, HomeMiddleware.handler, diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt deleted file mode 100644 index 00f61f18db..0000000000 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.tangem.tap.common.redux.navigation - -import android.content.Intent -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.openFragment -import com.tangem.tap.common.extensions.popBackTo -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.store -import org.rekotlin.Middleware - -val navigationMiddleware: Middleware = { _, state -> - { next -> - { action -> - if (action is NavigationAction) { - val navState = state()?.navigationState - when (action) { - is NavigationAction.NavigateTo -> { - navState?.activity?.get()?.openFragment( - screen = action.screen, - addToBackstack = action.addToBackstack, - fgShareTransition = action.fragmentShareTransition, - bundle = action.bundle, - ) - } - is NavigationAction.PopBackTo -> { - if (navState?.backStack?.lastOrNull() != action.screen) { - when (val screen = action.screen) { - AppScreen.Home, - AppScreen.Welcome, - -> { - if (navState?.backStack?.contains(screen) == false) { - // Pop back to activity - navState.activity?.get()?.popBackTo(screen = null, inclusive = true) - store.dispatchOnMain(NavigationAction.NavigateTo(screen)) - } else { - navState?.activity?.get()?.popBackTo(screen, action.inclusive) - } - } - else -> { - navState?.activity?.get()?.popBackTo(screen, action.inclusive) - } - } - } - } - is NavigationAction.OpenDocument -> { - val intent = Intent(Intent.ACTION_VIEW) - intent.data = action.url - navState?.activity?.get()?.startActivity(intent) - } - is NavigationAction.ActivityCreated, - is NavigationAction.ActivityDestroyed, - -> Unit - } - } - next(action) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt deleted file mode 100644 index ff53145651..0000000000 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.tangem.tap.common.redux.navigation - -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.NavigationState -import com.tangem.tap.common.extensions.getPreviousScreen -import com.tangem.tap.common.redux.AppState -import org.rekotlin.Action - -object NavigationReducer { - fun reduce(action: Action, state: AppState): NavigationState = internalReduce(action, state) -} - -private fun internalReduce(action: Action, state: AppState): NavigationState { - val navigationAction = action as? NavigationAction ?: return state.navigationState - val navState = state.navigationState - - return when (navigationAction) { - is NavigationAction.NavigateTo -> { - navState.copy(backStack = navState.backStack + navigationAction.screen) - } - is NavigationAction.PopBackTo -> { - if (navState.backStack.lastOrNull() == navigationAction.screen) return navState - - val screen = navigationAction.screen ?: navState.activity?.get()?.getPreviousScreen() - val index = navState.backStack.lastIndexOf(screen) + 1 - state.navigationState.copy(backStack = navState.backStack.subList(0, index)) - } - is NavigationAction.ActivityCreated -> navState.copy(activity = navigationAction.activity) - is NavigationAction.ActivityDestroyed -> { - when { - // Destroy the activity if it invoked for the same activity. Prevents overwriting to null if there is a - // new scan from the background [REDACTED_TASK_KEY] - navState.activity?.get() == navigationAction.activity.get() -> navState.copy(activity = null) - else -> navState - } - } - else -> navState - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt b/app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt index c6e0267c6b..267fa733f1 100644 --- a/app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt +++ b/app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt @@ -1,6 +1,5 @@ package com.tangem.tap.di -import com.tangem.core.navigation.ReduxNavController import com.tangem.domain.redux.ReduxStateHolder import com.tangem.tap.proxy.AppStateHolder import dagger.Binds @@ -13,10 +12,6 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) internal interface AppStateHolderModule { - @Binds - @Singleton - fun bindsNavigationStateHolder(appStateHolder: AppStateHolder): ReduxNavController - @Binds @Singleton fun bindsReduxStateHolder(appStateHolder: AppStateHolder): ReduxStateHolder diff --git a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt index 0e1a98b9c7..a2c9dd483f 100644 --- a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt +++ b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt @@ -1,15 +1,11 @@ package com.tangem.tap.proxy -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.ReduxNavController import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.redux.StateDialog import com.tangem.domain.wallets.models.UserWallet import com.tangem.tap.common.extensions.dispatchDialogShow -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 @@ -23,7 +19,7 @@ import javax.inject.Inject * Holds objects from old modules, that missing in DI graph. * Object sets manually to use in new modules and [AppStateHolder] proxies its to DI. */ -class AppStateHolder @Inject constructor() : ReduxNavController, ReduxStateHolder { +class AppStateHolder @Inject constructor() : ReduxStateHolder { @Deprecated("Use scan response from selected user wallet") var scanResponse: ScanResponse? = null @@ -35,16 +31,6 @@ class AppStateHolder @Inject constructor() : ReduxNavController, ReduxStateHolde return scanResponse?.card } - override fun navigate(action: NavigationAction) { - mainStore?.dispatchOnMain(action) - } - - override fun getBackStack(): List = mainStore?.state?.navigationState?.backStack.orEmpty() - - override fun popBackStack(screen: AppScreen?) { - mainStore?.dispatchOnMain(NavigationAction.PopBackTo(screen)) - } - override fun dispatch(action: Action) { mainStore?.dispatch(action) } diff --git a/core/navigation/src/main/java/com/tangem/core/navigation/NavigationAction.kt b/core/navigation/src/main/java/com/tangem/core/navigation/NavigationAction.kt deleted file mode 100644 index ec50c35bc0..0000000000 --- a/core/navigation/src/main/java/com/tangem/core/navigation/NavigationAction.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.tangem.core.navigation - -import android.net.Uri -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import org.rekotlin.Action -import java.lang.ref.WeakReference - -sealed class NavigationAction : Action { - - data class NavigateTo( - val screen: AppScreen, - val fragmentShareTransition: FragmentShareTransition? = null, - val addToBackstack: Boolean = true, - val bundle: Bundle? = null, - ) : NavigationAction() - - data class PopBackTo(val screen: AppScreen? = null, val inclusive: Boolean = false) : NavigationAction() - - data class OpenDocument(val url: Uri) : NavigationAction() - - data class ActivityCreated(val activity: WeakReference) : NavigationAction() - - data class ActivityDestroyed(val activity: WeakReference) : NavigationAction() -} \ No newline at end of file diff --git a/core/navigation/src/main/java/com/tangem/core/navigation/NavigationState.kt b/core/navigation/src/main/java/com/tangem/core/navigation/NavigationState.kt deleted file mode 100644 index 40d6612fed..0000000000 --- a/core/navigation/src/main/java/com/tangem/core/navigation/NavigationState.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.tangem.core.navigation - -import androidx.appcompat.app.AppCompatActivity -import org.rekotlin.StateType -import java.lang.ref.WeakReference - -data class NavigationState( - val backStack: List = emptyList(), - val activity: WeakReference? = null, -) : StateType - -enum class AppScreen(val isDialogFragment: Boolean = false) { - Home, - Disclaimer, - OnboardingNote, - OnboardingWallet, - OnboardingTwins, - OnboardingOther, - Wallet, - WalletDetails, - Send(isDialogFragment = true), - Details, - DetailsSecurity, - CardSettings, - AppSettings, - ResetToFactory, - AccessCodeRecovery, - ManageTokens, - AddCustomToken, - WalletConnectSessions, - QrScanning, - ReferralProgram, - Swap, - Welcome, - SaveWallet(isDialogFragment = true), - AppCurrencySelector, - ModalNotification(isDialogFragment = true), - Staking, -} \ No newline at end of file diff --git a/core/navigation/src/main/java/com/tangem/core/navigation/ReduxNavController.kt b/core/navigation/src/main/java/com/tangem/core/navigation/ReduxNavController.kt deleted file mode 100644 index 20e37f3958..0000000000 --- a/core/navigation/src/main/java/com/tangem/core/navigation/ReduxNavController.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tangem.core.navigation - -/** - * Navigation controller that based on redux actions - * -[REDACTED_AUTHOR] - */ -interface ReduxNavController { - - /** Navigate by [action] */ - fun navigate(action: NavigationAction) - - fun popBackStack(screen: AppScreen? = null) - - fun getBackStack(): List -} \ No newline at end of file diff --git a/core/navigation/src/main/java/com/tangem/core/navigation/FragmentShareTransition.kt b/core/navigation/src/main/java/com/tangem/core/navigation/ShareElement.kt similarity index 74% rename from core/navigation/src/main/java/com/tangem/core/navigation/FragmentShareTransition.kt rename to core/navigation/src/main/java/com/tangem/core/navigation/ShareElement.kt index 2cb2132eec..b3fd049583 100644 --- a/core/navigation/src/main/java/com/tangem/core/navigation/FragmentShareTransition.kt +++ b/core/navigation/src/main/java/com/tangem/core/navigation/ShareElement.kt @@ -1,18 +1,8 @@ package com.tangem.core.navigation import android.view.View -import androidx.transition.TransitionSet import java.lang.ref.WeakReference -/** -[REDACTED_AUTHOR] - */ -data class FragmentShareTransition( - val shareElements: List, - val enterTransitionSet: TransitionSet, - val exitTransitionSet: TransitionSet, -) - /** * For ease of use, the name is used as transitionName\name into the FragmentTransaction.addSharedElement */ From 10171a3b3e121f3b3ad7fa80b1b11826e148818a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 14 Jun 2024 13:18:05 +0400 Subject: [PATCH 16/19] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/MainActivity.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 32ce55bfb5..e55b98f204 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -224,7 +224,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac appRouterConfig.componentRouter = routingComponent.router routingComponent.stack.observe(lifecycle.asEssentyLifecycle()) { childStack -> - appRouterConfig.baclStack = childStack.backStack.map { it.configuration } + appRouterConfig.stack = childStack.backStack + .plus(childStack.active) + .map { it.configuration } when (val child = childStack.active.instance) { is RoutingComponent.Child.Initial -> Unit @@ -514,10 +516,10 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac private fun navigateToInitialScreen(intentWhichStartedActivity: Intent?) { if (userWalletsListManager.isLockable && userWalletsListManager.hasUserWallets) { store.dispatchNavigationAction { - push(AppRoute.Welcome(intentWhichStartedActivity?.let(::SerializableIntent))) + replaceAll(AppRoute.Welcome(intentWhichStartedActivity?.let(::SerializableIntent))) } } else { - store.dispatchNavigationAction { push(AppRoute.Home) } + store.dispatchNavigationAction { replaceAll(AppRoute.Home) } lifecycleScope.launch { intentProcessor.handleIntent(intentWhichStartedActivity, false) } From ecf8e5fc96b7d77d67df2a3d2b9048596beff379 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 14 Jun 2024 13:24:05 +0400 Subject: [PATCH 17/19] Updated on 2026-08-14 --- .../details/redux/DetailsMiddleware.kt | 4 +- .../features/details/redux/DetailsReducer.kt | 2 +- .../saveWallet/redux/SaveWalletMiddleware.kt | 6 +-- .../com/tangem/common/routing/AppRoute.kt | 14 +++++- domain/core/build.gradle.kts | 3 ++ .../serialization/BigDecimalSerializer.kt | 21 +++++++++ .../serialization/SerializedBigDecimal.kt | 6 +++ domain/staking/build.gradle.kts | 7 +-- domain/staking/models/.gitignore | 1 + domain/staking/models/build.gradle.kts | 11 +++++ .../com/tangem/domain/staking/model/Yield.kt | 45 ++++++++++++------- .../domain/tokens/model/CryptoCurrency.kt | 8 ++-- .../staking/api/navigation/StakingRouter.kt | 6 --- features/staking/impl/build.gradle.kts | 1 + .../viewmodel/StakingViewModel.kt | 15 +++++-- .../router/DefaultTokenDetailsRouter.kt | 11 +++++ .../router/InnerTokenDetailsRouter.kt | 3 ++ .../viewmodels/TokenDetailsViewModel.kt | 15 +------ settings.gradle.kts | 1 + 19 files changed, 125 insertions(+), 55 deletions(-) create mode 100644 domain/core/src/main/kotlin/com/tangem/domain/core/serialization/BigDecimalSerializer.kt create mode 100644 domain/core/src/main/kotlin/com/tangem/domain/core/serialization/SerializedBigDecimal.kt create mode 100644 domain/staking/models/.gitignore create mode 100644 domain/staking/models/build.gradle.kts rename domain/staking/{src/main/java => models/src/main/kotlin}/com/tangem/domain/staking/model/Yield.kt (88%) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index d0ce3c2f74..23a552773c 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -140,9 +140,9 @@ class DetailsMiddleware { val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync } .fold(onSuccess = { true }, onFailure = { false }) if (isLocked && userWalletsListManager.hasUserWallets) { - store.dispatchNavigationAction { popTo() } + store.dispatchNavigationAction { replaceAll(AppRoute.Welcome()) } } else { - store.dispatchNavigationAction { popTo() } + store.dispatchNavigationAction { replaceAll(AppRoute.Home) } } } } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index d89b862c1b..658dfcd6ba 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -74,7 +74,7 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: AppS return DetailsState( scanResponse = action.scanResponse, // If the current screen is ResetToFactory, we must save the ResetToFactory's state - cardSettingsState = if (router.backStack.lastOrNull() is AppRoute.ResetToFactory) { + cardSettingsState = if (router.stack.lastOrNull() is AppRoute.ResetToFactory) { state.detailsState.cardSettingsState } else { null diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt index 541ceefdcc..82799c76e6 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt @@ -157,11 +157,7 @@ internal class SaveWalletMiddleware { private fun navigateToWallet() { store.dispatchNavigationAction { - popTo { isSuccess -> - if (!isSuccess) { - clear { push(AppRoute.Wallet) } - } - } + replaceAll(AppRoute.Wallet) } } } \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index 2a337dbeaa..f912745220 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -6,6 +6,7 @@ import com.tangem.common.routing.bundle.bundle import com.tangem.common.routing.entity.SerializableIntent import com.tangem.core.decompose.navigation.Route import com.tangem.domain.qrscanning.models.SourceType +import com.tangem.domain.staking.model.Yield import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import kotlinx.serialization.Serializable @@ -160,5 +161,16 @@ sealed class AppRoute(val path: String) : Route, RouteBundleParams { data object ModalNotification : AppRoute(path = "/modal_notification") @Serializable - data object Staking : AppRoute(path = "/staking") + data class Staking( + val userWalletId: UserWalletId, + val cryptoCurrencyId: CryptoCurrency.ID, + val yield: Yield, + ) : AppRoute(path = "/staking/${userWalletId.stringValue}/${cryptoCurrencyId.value}/${yield.id}") { + + companion object { + const val USER_WALLET_ID_KEY = "userWalletId" + const val CRYPTO_CURRENCY_ID_KEY = "cryptoCurrencyId" + const val YIELD_KEY = "yield" + } + } } \ No newline at end of file diff --git a/domain/core/build.gradle.kts b/domain/core/build.gradle.kts index 63325166f7..46d2670d68 100644 --- a/domain/core/build.gradle.kts +++ b/domain/core/build.gradle.kts @@ -1,5 +1,6 @@ plugins { alias(deps.plugins.kotlin.jvm) + alias(deps.plugins.kotlin.serialization) id("configuration") } @@ -7,4 +8,6 @@ dependencies { api(deps.kotlin.coroutines) api(deps.arrow.core) api(deps.arrow.fx) + + implementation(deps.kotlin.serialization) } \ No newline at end of file diff --git a/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/BigDecimalSerializer.kt b/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/BigDecimalSerializer.kt new file mode 100644 index 0000000000..ea7727681f --- /dev/null +++ b/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/BigDecimalSerializer.kt @@ -0,0 +1,21 @@ +package com.tangem.domain.core.serialization + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.math.BigDecimal + +internal object BigDecimalSerializer : KSerializer { + + override val descriptor = PrimitiveSerialDescriptor(serialName = "BigDecimal", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: BigDecimal) { + encoder.encodeString(value.toString()) + } + + override fun deserialize(decoder: Decoder): BigDecimal { + return BigDecimal(decoder.decodeString()) + } +} \ No newline at end of file diff --git a/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/SerializedBigDecimal.kt b/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/SerializedBigDecimal.kt new file mode 100644 index 0000000000..3243892b18 --- /dev/null +++ b/domain/core/src/main/kotlin/com/tangem/domain/core/serialization/SerializedBigDecimal.kt @@ -0,0 +1,6 @@ +package com.tangem.domain.core.serialization + +import kotlinx.serialization.Serializable +import java.math.BigDecimal + +typealias SerializedBigDecimal = @Serializable(with = BigDecimalSerializer::class) BigDecimal \ No newline at end of file diff --git a/domain/staking/build.gradle.kts b/domain/staking/build.gradle.kts index 252bfb49d9..fc6ced152c 100644 --- a/domain/staking/build.gradle.kts +++ b/domain/staking/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(deps.plugins.android.library) alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.serialization) id("configuration") } @@ -10,10 +11,10 @@ android { dependencies { - implementation(deps.kotlin.coroutines) - implementation(deps.arrow.core) - implementation(deps.kotlin.serialization) + api(projects.domain.staking.models) + api(projects.domain.core) + implementation(deps.kotlin.serialization) implementation(projects.domain.tokens.models) } \ No newline at end of file diff --git a/domain/staking/models/.gitignore b/domain/staking/models/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/domain/staking/models/.gitignore @@ -0,0 +1 @@ +/build diff --git a/domain/staking/models/build.gradle.kts b/domain/staking/models/build.gradle.kts new file mode 100644 index 0000000000..2a478f5d98 --- /dev/null +++ b/domain/staking/models/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + alias(deps.plugins.kotlin.jvm) + alias(deps.plugins.kotlin.serialization) + id("configuration") +} + +dependencies { + implementation(projects.domain.core) + + implementation(deps.kotlin.serialization) +} \ No newline at end of file diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/model/Yield.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/Yield.kt similarity index 88% rename from domain/staking/src/main/java/com/tangem/domain/staking/model/Yield.kt rename to domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/Yield.kt index 853c3d4247..aa55291bb5 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/model/Yield.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/Yield.kt @@ -1,54 +1,64 @@ package com.tangem.domain.staking.model -import java.io.Serializable -import java.math.BigDecimal +import com.tangem.domain.core.serialization.SerializedBigDecimal +import kotlinx.serialization.Serializable +@Serializable data class Yield( val id: String, val token: Token, val tokens: List, val args: Args, val status: Status, - val apy: BigDecimal, + val apy: SerializedBigDecimal, val rewardRate: Double, val rewardType: RewardType, val metadata: Metadata, val validators: List, val isAvailable: Boolean, -) : Serializable { +) { + + @Serializable data class Status( val enter: Boolean, val exit: Boolean?, - ) : Serializable + ) + @Serializable data class Args( val enter: Enter, val exit: Enter?, - ) : Serializable { + ) { + + @Serializable data class Enter( val addresses: Addresses, val args: Map, - ) : Serializable { + ) { + + @Serializable data class Addresses( val address: AddressArgument, val additionalAddresses: Map? = null, - ) : Serializable + ) } } + @Serializable data class Validator( val address: String, val status: String, val name: String, val image: String?, val website: String?, - val apr: BigDecimal?, + val apr: SerializedBigDecimal?, val commission: Double?, val stakedBalance: String?, val votingPower: Double?, val preferred: Boolean, - ) : Serializable + ) + @Serializable data class Metadata( val name: String, val logoUri: String, @@ -67,14 +77,17 @@ data class Yield( val supportsMultipleValidators: Boolean, val revshare: Enabled, val fee: Enabled, - ) : Serializable { + ) { + + @Serializable data class Period( val days: Int, - ) : Serializable + ) + @Serializable data class Enabled( val enabled: Boolean, - ) : Serializable + ) } enum class RewardType { @@ -84,6 +97,7 @@ data class Yield( } } +@Serializable data class Token( val name: String, val network: NetworkType, @@ -93,7 +107,7 @@ data class Token( val coinGeckoId: String?, val logoURI: String?, val isPoints: Boolean?, -) : Serializable { +) { enum class NetworkType { AVALANCHE_C, AVALANCHE_ATOMIC, @@ -165,9 +179,10 @@ data class Token( } } +@Serializable data class AddressArgument( val required: Boolean, val network: String? = null, val minimum: Double? = null, val maximum: Double? = null, -) : Serializable \ No newline at end of file +) \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt index e650d2b8ea..a0468f3100 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrency.kt @@ -188,11 +188,11 @@ sealed class CryptoCurrency { return "ID(value='$value')" } - private companion object { + companion object { // should use delimiters that could be used in URL not like path or query delimiters - const val PREFIX_DELIMITER = '_' - const val SUFFIX_DELIMITER = ';' - const val DERIVATION_PATH_DELIMITER = 'd' + private const val PREFIX_DELIMITER = '_' + private const val SUFFIX_DELIMITER = ';' + private const val DERIVATION_PATH_DELIMITER = 'd' } } diff --git a/features/staking/api/src/main/kotlin/com/tangem/features/staking/api/navigation/StakingRouter.kt b/features/staking/api/src/main/kotlin/com/tangem/features/staking/api/navigation/StakingRouter.kt index 05277770a8..d2b216513e 100644 --- a/features/staking/api/src/main/kotlin/com/tangem/features/staking/api/navigation/StakingRouter.kt +++ b/features/staking/api/src/main/kotlin/com/tangem/features/staking/api/navigation/StakingRouter.kt @@ -5,10 +5,4 @@ import androidx.fragment.app.Fragment interface StakingRouter { fun getEntryFragment(): Fragment - - companion object { - const val CRYPTO_CURRENCY_ID_KEY = "CRYPTO_CURRENCY_ID_KEY" - const val USER_WALLET_ID_KEY = "USER_WALLET_ID_KEY" - const val YIELD_KEY = "YIELD_KEY" - } } \ No newline at end of file diff --git a/features/staking/impl/build.gradle.kts b/features/staking/impl/build.gradle.kts index 072e1bcd7b..1547a55bd2 100644 --- a/features/staking/impl/build.gradle.kts +++ b/features/staking/impl/build.gradle.kts @@ -63,6 +63,7 @@ dependencies { /** Common */ implementation(projects.common.ui) + implementation(projects.common.routing) /** Libs */ implementation(projects.libs.crypto) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt index 7144e4d5ed..68a13a920a 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt @@ -1,5 +1,6 @@ package com.tangem.features.staking.impl.presentation.viewmodel +import android.os.Bundle import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel @@ -7,6 +8,8 @@ import androidx.lifecycle.viewModelScope import arrow.core.getOrElse import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.bundle.unbundle import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.staking.model.Yield import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase @@ -52,14 +55,18 @@ internal class StakingViewModel @Inject constructor( var stakingStateRouter: StakingStateRouter by Delegates.notNull() private set - private val cryptoCurrencyId: CryptoCurrency.ID = savedStateHandle[StakingRouter.CRYPTO_CURRENCY_ID_KEY] + private val cryptoCurrencyId: CryptoCurrency.ID = savedStateHandle.get( + AppRoute.Staking.CRYPTO_CURRENCY_ID_KEY, + ) + ?.let { it.unbundle(CryptoCurrency.ID.serializer()) } ?: error("This screen can't be opened without `CryptoCurrency.ID`") - private val userWalletId: UserWalletId = savedStateHandle.get(StakingRouter.USER_WALLET_ID_KEY) - ?.let { stringValue -> UserWalletId(stringValue) } + private val userWalletId: UserWalletId = savedStateHandle.get(AppRoute.Staking.USER_WALLET_ID_KEY) + ?.let { it.unbundle(UserWalletId.serializer()) } ?: error("This screen can't be opened without `UserWalletId`") - private val yield: Yield = savedStateHandle[StakingRouter.YIELD_KEY] + private val yield: Yield = savedStateHandle.get(AppRoute.Staking.YIELD_KEY) + ?.let { it.unbundle(Yield.serializer()) } ?: error("This screen can't be opened without `Yield`") private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull() diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt index 880d92fba4..c2ddee4828 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt @@ -5,6 +5,7 @@ import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.core.navigation.share.ShareManager import com.tangem.core.navigation.url.UrlOpener +import com.tangem.domain.staking.model.Yield import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.tokendetails.presentation.TokenDetailsFragment @@ -37,4 +38,14 @@ internal class DefaultTokenDetailsRouter( ), ) } + + override fun openStaking(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, yield: Yield) { + router.push( + AppRoute.Staking( + userWalletId = userWalletId, + cryptoCurrencyId = cryptoCurrency.id, + yield = yield, + ), + ) + } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/InnerTokenDetailsRouter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/InnerTokenDetailsRouter.kt index 56eead22a5..7060190ec5 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/InnerTokenDetailsRouter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/InnerTokenDetailsRouter.kt @@ -1,5 +1,6 @@ package com.tangem.feature.tokendetails.presentation.router +import com.tangem.domain.staking.model.Yield import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.tokendetails.navigation.TokenDetailsRouter @@ -16,4 +17,6 @@ internal interface InnerTokenDetailsRouter : TokenDetailsRouter { fun share(text: String) fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) + + fun openStaking(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, yield: Yield) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 8542738099..09a908e65f 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -4,7 +4,6 @@ import android.os.Bundle import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.core.os.bundleOf import androidx.lifecycle.* import androidx.paging.cachedIn import arrow.core.getOrElse @@ -14,8 +13,6 @@ import com.tangem.common.routing.bundle.unbundle import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.deeplink.DeepLinksRegistry import com.tangem.core.deeplink.global.BuyCurrencyDeepLink -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels @@ -70,7 +67,6 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDeta import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.ExchangeStatusBottomSheetConfig import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles -import com.tangem.features.staking.api.navigation.StakingRouter import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles import com.tangem.features.tokendetails.impl.R import com.tangem.lib.crypto.BlockchainUtils.isBitcoin @@ -448,16 +444,7 @@ internal class TokenDetailsViewModel @Inject constructor( val yield = getYieldUseCase.invoke(cryptoCurrency.id, cryptoCurrency.symbol).getOrNull() yield ?: error("Staking is unavailable") - reduxStateHolder.dispatch( - action = NavigationAction.NavigateTo( - screen = AppScreen.Staking, - bundle = bundleOf( - StakingRouter.USER_WALLET_ID_KEY to userWalletId.stringValue, - StakingRouter.CRYPTO_CURRENCY_ID_KEY to cryptoCurrency.id, - StakingRouter.YIELD_KEY to yield, - ), - ), - ) + router.openStaking(userWalletId, cryptoCurrency, yield) } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 40841c6327..ab66fbb133 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -212,6 +212,7 @@ include(":domain:feedback") include(":domain:qr-scanning") include(":domain:qr-scanning:models") include(":domain:staking") +include(":domain:staking:models") include(":domain:wallet-connect") // endregion Domain modules From 7846772be2ccdd0e4e25c4e015d0abf0f1420ef9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 14 Jun 2024 15:07:26 +0400 Subject: [PATCH 18/19] Updated on 2026-08-14 --- .../disclaimer/redux/DisclaimerMiddleware.kt | 4 +++- .../kotlin/com/tangem/common/routing/AppRoute.kt | 9 ++++++++- .../tangem/features/details/utils/ItemsBuilder.kt | 2 +- features/disclaimer/impl/build.gradle.kts | 1 + .../features/disclaimer/impl/DisclaimerFragment.kt | 8 ++++---- .../disclaimer/impl/di/DisclaimerRouterModule.kt | 6 +++--- .../impl/navigation/DefaultDisclaimerRouter.kt | 12 +++++------- 7 files changed, 25 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt index 20c8df7fa9..60ffa51781 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt @@ -26,7 +26,9 @@ private fun handleDisclaimerMiddleware(action: Action, appState: AppState) { when (action) { is DisclaimerAction.Show -> { - store.dispatchNavigationAction { push(AppRoute.Disclaimer) } + store.dispatchNavigationAction { + push(AppRoute.Disclaimer(isTosAccepted = action.from == DisclaimerSource.Details)) + } } is DisclaimerAction.AcceptDisclaimer -> { mainScope.launch { diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index f912745220..ebb991332a 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -33,7 +33,14 @@ sealed class AppRoute(val path: String) : Route, RouteBundleParams { } @Serializable - data object Disclaimer : AppRoute(path = "/disclaimer") + data class Disclaimer( + val isTosAccepted: Boolean, + ) : AppRoute(path = "/disclaimer${if (isTosAccepted) "/tos_accepted" else ""}") { + + companion object { + const val IS_TOS_ACCEPTED_KEY = "isTosAccepted" + } + } @Serializable data object OnboardingNote : AppRoute(path = "/onboarding/note") diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt index 9e87499c97..dbc63d89f5 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt @@ -89,7 +89,7 @@ internal class ItemsBuilder @Inject constructor( id = "disclaimer", title = resourceReference(R.string.disclaimer_title), iconRes = R.drawable.ic_text_24, - onClick = { router.push(AppRoute.Disclaimer) }, + onClick = { router.push(AppRoute.Disclaimer(isTosAccepted = true)) }, ), ), ) diff --git a/features/disclaimer/impl/build.gradle.kts b/features/disclaimer/impl/build.gradle.kts index bc658e93f3..a2053b346f 100644 --- a/features/disclaimer/impl/build.gradle.kts +++ b/features/disclaimer/impl/build.gradle.kts @@ -30,6 +30,7 @@ dependencies { implementation(projects.core.utils) implementation(projects.core.featuretoggles) implementation(projects.core.navigation) + implementation(projects.common.routing) /** Domain modules */ implementation(projects.domain.models) diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/DisclaimerFragment.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/DisclaimerFragment.kt index e0a30ce395..fb87182dd5 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/DisclaimerFragment.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/DisclaimerFragment.kt @@ -5,7 +5,7 @@ import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.fragment.app.viewModels -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRouter import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.components.SystemBarsEffect import com.tangem.core.ui.res.TangemTheme @@ -23,7 +23,7 @@ internal class DisclaimerFragment : ComposeFragment() { override lateinit var uiDependencies: UiDependencies @Inject - lateinit var reduxNavController: ReduxNavController + lateinit var appRouter: AppRouter private val viewModel by viewModels() @@ -40,7 +40,7 @@ internal class DisclaimerFragment : ComposeFragment() { val systemBarsColor = TangemTheme.colors.background.primary BackHandler { if (isTosAccepted) { - reduxNavController.popBackStack() + appRouter.pop() } else { requireActivity().finish() } @@ -48,7 +48,7 @@ internal class DisclaimerFragment : ComposeFragment() { SystemBarsEffect { setSystemBarsColor(systemBarsColor) } - DisclaimerScreen(viewModel.state, reduxNavController::popBackStack) + DisclaimerScreen(viewModel.state, appRouter::pop) } companion object { diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/di/DisclaimerRouterModule.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/di/DisclaimerRouterModule.kt index 8b3d34e029..cdac031d49 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/di/DisclaimerRouterModule.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/di/DisclaimerRouterModule.kt @@ -1,6 +1,6 @@ package com.tangem.features.disclaimer.impl.di -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRouter import com.tangem.features.disclaimer.api.DisclaimerRouter import com.tangem.features.disclaimer.impl.navigation.DefaultDisclaimerRouter import dagger.Module @@ -18,7 +18,7 @@ object DisclaimerRouterModule { @Provides @ActivityScoped - fun provideDisclaimerRouter(reduxNavController: ReduxNavController): DisclaimerRouter { - return DefaultDisclaimerRouter(reduxNavController) + fun provideDisclaimerRouter(appRouter: AppRouter): DisclaimerRouter { + return DefaultDisclaimerRouter(appRouter) } } \ No newline at end of file diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/navigation/DefaultDisclaimerRouter.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/navigation/DefaultDisclaimerRouter.kt index 4860d3265d..a49d4a94c4 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/navigation/DefaultDisclaimerRouter.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/navigation/DefaultDisclaimerRouter.kt @@ -1,14 +1,12 @@ package com.tangem.features.disclaimer.impl.navigation import androidx.fragment.app.Fragment -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.ReduxNavController +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.features.disclaimer.impl.DisclaimerFragment -import javax.inject.Inject -internal class DefaultDisclaimerRouter @Inject constructor( - private val reduxNavController: ReduxNavController, +internal class DefaultDisclaimerRouter( + private val appRouter: AppRouter, ) : InnerDisclaimerRouter { override fun entryFragment(): Fragment = DisclaimerFragment.create() @@ -18,6 +16,6 @@ internal class DefaultDisclaimerRouter @Inject constructor( } override fun openHome() { - reduxNavController.navigate(action = NavigationAction.NavigateTo(screen = AppScreen.Home)) + appRouter.push(AppRoute.Home) } } \ No newline at end of file From 1ea0843733613dd394a7ef50eb4cd765c9b2e156 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 17 Jun 2024 15:16:43 +0400 Subject: [PATCH 19/19] Updated on 2026-08-14 --- .../staking/impl/presentation/viewmodel/StakingViewModel.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt index 68a13a920a..107205fa5b 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt @@ -6,10 +6,10 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import arrow.core.getOrElse -import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase -import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.common.routing.AppRoute import com.tangem.common.routing.bundle.unbundle +import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.staking.model.Yield import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase @@ -18,7 +18,6 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetUserWalletUseCase -import com.tangem.features.staking.api.navigation.StakingRouter import com.tangem.features.staking.impl.navigation.InnerStakingRouter import com.tangem.features.staking.impl.presentation.state.StakingStateController import com.tangem.features.staking.impl.presentation.state.StakingStateRouter