Initial commit: Pezkuwi Wallet Android

Complete rebrand of Nova Wallet for Pezkuwichain ecosystem.

## Features
- Full Pezkuwichain support (HEZ & PEZ tokens)
- Polkadot ecosystem compatibility
- Staking, Governance, DeFi, NFTs
- XCM cross-chain transfers
- Hardware wallet support (Ledger, Polkadot Vault)
- WalletConnect v2
- Push notifications

## Languages
- English, Turkish, Kurmanci (Kurdish), Spanish, French, German, Russian, Japanese, Chinese, Korean, Portuguese, Vietnamese

Based on Nova Wallet by Novasama Technologies GmbH
© Dijital Kurdistan Tech Institute 2026
This commit is contained in:
2026-01-23 01:31:12 +03:00
commit 31c8c5995f
7621 changed files with 425838 additions and 0 deletions
@@ -0,0 +1,50 @@
package io.novafoundation.nova
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.withChildScope
import io.novafoundation.nova.runtime.di.RuntimeApi
import io.novafoundation.nova.runtime.di.RuntimeComponent
import io.novafoundation.nova.runtime.ext.Geneses
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
open class BaseIntegrationTest {
protected val context: Context = ApplicationProvider.getApplicationContext()
protected val runtimeApi = FeatureUtils.getFeature<RuntimeComponent>(context, RuntimeApi::class.java)
val chainRegistry = runtimeApi.chainRegistry()
private val externalRequirementFlow = runtimeApi.externalRequirementFlow()
@Before
fun setup() = runBlocking {
externalRequirementFlow.emit(ChainConnection.ExternalRequirement.ALLOWED)
}
protected fun runTest(action: suspend CoroutineScope.() -> Unit) {
runBlocking {
withChildScope {
action()
}
}
}
protected suspend fun ChainRegistry.polkadot(): Chain {
return getChain(Chain.Geneses.POLKADOT)
}
protected suspend fun ChainRegistry.polkadotAssetHub(): Chain {
return getChain(Chain.Geneses.POLKADOT_ASSET_HUB)
}
}
@@ -0,0 +1,60 @@
package io.novafoundation.nova
import android.content.Context
import android.util.Log
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.novafoundation.nova.common.data.network.runtime.binding.bindEventRecords
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.LOG_TAG
import io.novafoundation.nova.common.utils.system
import io.novafoundation.nova.runtime.di.RuntimeApi
import io.novafoundation.nova.runtime.di.RuntimeComponent
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
import io.novasama.substrate_sdk_android.runtime.metadata.storage
import io.novasama.substrate_sdk_android.runtime.metadata.storageKey
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class BlockParsingIntegrationTest {
private val chainGenesis = "f1cf9022c7ebb34b162d5b5e34e705a5a740b2d0ecc1009fb89023e62a488108" // Shiden
private val runtimeApi = FeatureUtils.getFeature<RuntimeComponent>(
ApplicationProvider.getApplicationContext<Context>(),
RuntimeApi::class.java
)
private val chainRegistry = runtimeApi.chainRegistry()
private val externalRequirementFlow = runtimeApi.externalRequirementFlow()
private val rpcCalls = runtimeApi.rpcCalls()
private val remoteStorage = runtimeApi.remoteStorageSource()
@Test
fun testBlockParsing() = runBlocking {
externalRequirementFlow.emit(ChainConnection.ExternalRequirement.ALLOWED)
val chain = chainRegistry.getChain(chainGenesis)
val block = rpcCalls.getBlock(chain.id)
val logTag = this@BlockParsingIntegrationTest.LOG_TAG
Log.d(logTag, block.block.header.number.toString())
val events = remoteStorage.query(
chainId = chain.id,
keyBuilder = { it.metadata.system().storage("Events").storageKey() },
binding = { scale, runtime ->
Log.d(logTag, scale!!)
bindEventRecords(scale)
}
)
// val eventsRaw = "0x0800000000000000000000000000000002000000010000000000585f8f0900000000020000"
// val type = bindEventRecords(eventsRaw, chainRegistry.getRuntime(chain.id))
}
}
@@ -0,0 +1,129 @@
package io.novafoundation.nova
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.gson.Gson
import androidx.test.platform.app.InstrumentationRegistry
import dagger.Component
import io.novafoundation.nova.common.data.network.NetworkApiCreator
import io.novafoundation.nova.common.di.CommonApi
import io.novafoundation.nova.common.di.FeatureContainer
import io.novafoundation.nova.core_db.model.chain.ChainLocal
import io.novafoundation.nova.core_db.model.chain.NodeSelectionPreferencesLocal
import io.novafoundation.nova.runtime.di.RuntimeApi
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.RemoteToDomainChainMapperFacade
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainAssetToLocal
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainExplorerToLocal
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainExternalApiToLocal
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainLocalToChain
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainNodeToLocal
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainToLocal
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapExternalApisToLocal
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapNodeSelectionPreferencesToLocal
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapRemoteAssetToLocal
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapRemoteChainToLocal
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapRemoteExplorersToLocal
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapRemoteNodesToLocal
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novafoundation.nova.runtime.multiNetwork.chain.remote.ChainFetcher
import io.novafoundation.nova.runtime.multiNetwork.chain.remote.model.ChainRemote
import io.novafoundation.nova.test_shared.assertAllItemsEquals
import javax.inject.Inject
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@Component(
dependencies = [
CommonApi::class,
RuntimeApi::class
]
)
interface MappingTestAppComponent {
fun inject(test: ChainMappingIntegrationTest)
}
@RunWith(AndroidJUnit4::class)
class ChainMappingIntegrationTest {
private val context = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
private val featureContainer = context as FeatureContainer
@Inject
lateinit var networkApiCreator: NetworkApiCreator
@Inject
lateinit var remoteToDomainChainMapperFacade: RemoteToDomainChainMapperFacade
lateinit var chainFetcher: ChainFetcher
private val gson = Gson()
@Before
fun prepare() {
val component = DaggerMappingTestAppComponent.builder()
.commonApi(featureContainer.commonApi())
.runtimeApi(featureContainer.getFeature(RuntimeApi::class.java))
.build()
component.inject(this)
chainFetcher = networkApiCreator.create(ChainFetcher::class.java)
}
@Test
fun testChainMappingIsMatch() {
runBlocking {
val chainsRemote = chainFetcher.getChains()
val remoteToDomain = chainsRemote.map { mapRemoteToDomain(it) }
val remoteToLocalToDomain = chainsRemote.map { mapRemoteToLocalToDomain(it) }
val domainToLocalToDomain = remoteToDomain.map { mapDomainToLocalToDomain(it) }
assertAllItemsEquals(listOf(remoteToDomain, remoteToLocalToDomain, domainToLocalToDomain))
}
}
private fun mapRemoteToLocalToDomain(chainRemote: ChainRemote): Chain {
val chainLocal = mapRemoteChainToLocal(chainRemote, null, ChainLocal.Source.DEFAULT, gson)
val assetsLocal = chainRemote.assets.map { mapRemoteAssetToLocal(chainRemote, it, gson, isEnabled = true) }
val nodesLocal = mapRemoteNodesToLocal(chainRemote)
val explorersLocal = mapRemoteExplorersToLocal(chainRemote)
val externalApisLocal = mapExternalApisToLocal(chainRemote)
return mapChainLocalToChain(
chainLocal = chainLocal,
nodesLocal = nodesLocal,
nodeSelectionPreferences = NodeSelectionPreferencesLocal(chainLocal.id, autoBalanceEnabled = true, selectedNodeUrl = null),
assetsLocal = assetsLocal,
explorersLocal = explorersLocal,
externalApisLocal = externalApisLocal,
gson = gson
)
}
private fun mapRemoteToDomain(chainRemote: ChainRemote): Chain {
return remoteToDomainChainMapperFacade.mapRemoteChainToDomain(chainRemote, Chain.Source.DEFAULT)
}
private fun mapDomainToLocalToDomain(chain: Chain): Chain {
val chainLocal = mapChainToLocal(chain, gson)
val nodesLocal = chain.nodes.nodes.map { mapChainNodeToLocal(it) }
val nodeSelectionPreferencesLocal = mapNodeSelectionPreferencesToLocal(chain)
val assetsLocal = chain.assets.map { mapChainAssetToLocal(it, gson) }
val explorersLocal = chain.explorers.map { mapChainExplorerToLocal(it) }
val externalApisLocal = chain.externalApis.map { mapChainExternalApiToLocal(gson, chain.id, it) }
return mapChainLocalToChain(
chainLocal = chainLocal,
nodesLocal = nodesLocal,
nodeSelectionPreferences = nodeSelectionPreferencesLocal,
assetsLocal = assetsLocal,
explorersLocal = explorersLocal,
externalApisLocal = externalApisLocal,
gson = gson
)
}
}
@@ -0,0 +1,61 @@
package io.novafoundation.nova
import androidx.room.Room
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.google.gson.Gson
import dagger.Component
import io.novafoundation.nova.common.data.network.NetworkApiCreator
import io.novafoundation.nova.common.di.CommonApi
import io.novafoundation.nova.common.di.FeatureContainer
import io.novafoundation.nova.core_db.AppDatabase
import io.novafoundation.nova.runtime.multiNetwork.chain.ChainSyncService
import io.novafoundation.nova.runtime.multiNetwork.chain.remote.ChainFetcher
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import javax.inject.Inject
@Component(
dependencies = [
CommonApi::class,
]
)
interface TestAppComponent {
fun inject(test: ChainSyncServiceIntegrationTest)
}
@RunWith(AndroidJUnit4::class)
class ChainSyncServiceIntegrationTest {
private val context = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
private val featureContainer = context as FeatureContainer
@Inject
lateinit var networkApiCreator: NetworkApiCreator
lateinit var chainSyncService: ChainSyncService
@Before
fun setup() {
val component = DaggerTestAppComponent.builder()
.commonApi(featureContainer.commonApi())
.build()
component.inject(this)
val chainDao = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
.build()
.chainDao()
chainSyncService = ChainSyncService(chainDao, networkApiCreator.create(ChainFetcher::class.java), Gson())
}
@Test
fun shouldFetchAndStoreRealChains() = runBlocking {
chainSyncService.syncUp()
}
}
@@ -0,0 +1,98 @@
package io.novafoundation.nova
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import io.novafoundation.nova.common.address.intoKey
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.emptySubstrateAccountId
import io.novafoundation.nova.feature_account_api.data.fee.FeePaymentCurrency
import io.novafoundation.nova.feature_account_api.domain.model.toDefaultSubstrateAddress
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.tranfers.AssetTransferBase
import io.novafoundation.nova.feature_wallet_api.data.repository.getXcmChain
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
import io.novafoundation.nova.feature_wallet_api.domain.model.xcm.legacy.transferConfiguration
import io.novafoundation.nova.feature_wallet_api.domain.model.xcm.transferConfiguration
import io.novafoundation.nova.runtime.ext.addressOf
import io.novafoundation.nova.runtime.ext.emptyAccountId
import io.novafoundation.nova.runtime.ext.normalizeTokenSymbol
import io.novafoundation.nova.runtime.multiNetwork.findChain
import kotlinx.coroutines.runBlocking
import org.junit.Test
import java.math.BigInteger
class CrossChainTransfersIntegrationTest : BaseIntegrationTest() {
private val walletApi = FeatureUtils.getFeature<WalletFeatureApi>(
ApplicationProvider.getApplicationContext<Context>(),
WalletFeatureApi::class.java
)
private val chainTransfersRepository = walletApi.crossChainTransfersRepository
private val crossChainWeigher = walletApi.crossChainWeigher
private val parachainInfoRepository = runtimeApi.parachainInfoRepository
@Test
fun testParachainToParachain() = performFeeTest(
from = "Moonriver",
what = "xcKAR",
to = "Karura"
)
@Test
fun testRelaychainToParachain() = performFeeTest(
from = "Kusama",
what = "KSM",
to = "Moonriver"
)
@Test
fun testParachainToRelaychain() = performFeeTest(
from = "Moonriver",
what = "xcKSM",
to = "Kusama"
)
@Test
fun testParachainToParachainNonReserve() = performFeeTest(
from = "Karura",
what = "BNC",
to = "Moonriver"
)
private fun performFeeTest(
from: String,
to: String,
what: String
) {
runBlocking {
val originChain = chainRegistry.findChain { it.name == from }!!
val asssetInOrigin = originChain.assets.first { it.symbol.value == what }
val destinationChain = chainRegistry.findChain { it.name == to }!!
val asssetInDestination = destinationChain.assets.first { normalizeTokenSymbol(it.symbol.value) == normalizeTokenSymbol(what) }
val crossChainConfig = chainTransfersRepository.getConfiguration()
val crossChainTransfer = crossChainConfig.transferConfiguration(
originChain = parachainInfoRepository.getXcmChain(originChain),
originAsset = asssetInOrigin,
destinationChain = parachainInfoRepository.getXcmChain(destinationChain),
)!!
val transfer = AssetTransferBase(
recipient = originChain.addressOf(originChain.emptyAccountId()),
originChain = originChain,
originChainAsset = asssetInOrigin,
destinationChain = destinationChain,
destinationChainAsset = asssetInDestination,
feePaymentCurrency = FeePaymentCurrency.Native,
amountPlanks = BigInteger.ZERO
)
val crossChainFeeResult = runCatching { crossChainWeigher.estimateFee(transfer, crossChainTransfer) }
check(crossChainFeeResult.isSuccess)
}
}
}
@@ -0,0 +1,100 @@
package io.novafoundation.nova
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import io.novafoundation.nova.common.address.intoKey
import io.novafoundation.nova.common.data.network.runtime.binding.toResult
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.composeCall
import io.novafoundation.nova.common.utils.xcmPalletName
import io.novafoundation.nova.feature_wallet_api.domain.model.planksFromAmount
import io.novafoundation.nova.feature_xcm_api.asset.MultiAsset
import io.novafoundation.nova.feature_xcm_api.asset.MultiAssets
import io.novafoundation.nova.feature_xcm_api.di.XcmFeatureApi
import io.novafoundation.nova.feature_xcm_api.runtimeApi.dryRun.model.OriginCaller
import io.novafoundation.nova.feature_xcm_api.runtimeApi.dryRun.model.getByLocation
import io.novafoundation.nova.feature_xcm_api.multiLocation.Junctions
import io.novafoundation.nova.feature_xcm_api.multiLocation.MultiLocation
import io.novafoundation.nova.feature_xcm_api.multiLocation.MultiLocation.Junction.ParachainId
import io.novafoundation.nova.feature_xcm_api.multiLocation.asLocation
import io.novafoundation.nova.feature_xcm_api.multiLocation.toMultiLocation
import io.novafoundation.nova.feature_xcm_api.versions.XcmVersion
import io.novafoundation.nova.feature_xcm_api.versions.toEncodableInstance
import io.novafoundation.nova.feature_xcm_api.versions.versionedXcm
import io.novafoundation.nova.feature_xcm_api.weight.WeightLimit
import io.novafoundation.nova.runtime.ext.utilityAsset
import io.novafoundation.nova.runtime.multiNetwork.getRuntime
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
import org.junit.Test
import java.math.BigDecimal
import java.math.BigInteger
class DryRunIntegrationTest : BaseIntegrationTest() {
private val xcmApi = FeatureUtils.getFeature<XcmFeatureApi>(
ApplicationProvider.getApplicationContext<Context>(),
XcmFeatureApi::class.java
)
private val dryRunApi = xcmApi.dryRunApi
@Test
fun testDryRunXcmTeleport() = runTest {
val polkadot = chainRegistry.polkadot()
val polkadotAssetHub = chainRegistry.polkadotAssetHub()
val polkadotRuntime = chainRegistry.getRuntime(polkadot.id)
val polkadotLocation = MultiLocation.Interior.Here.asLocation()
val polkadotAssetHubLocation = Junctions(ParachainId(1000)).asLocation()
val dotLocation = polkadotLocation.toRelative()
val amount = polkadot.utilityAsset.planksFromAmount(BigDecimal.ONE)
val assets = MultiAsset.from(dotLocation, amount)
val origin = "16WWmr2Xqgy5fna35GsNHXMU7vDBM12gzHCFGibQjSmKpAN".toAccountId().intoKey()
val beneficiary = origin.toMultiLocation()
val xcmVersion = XcmVersion.V4
val pahVersionedLocation = polkadotAssetHubLocation.toRelative().versionedXcm(xcmVersion)
// Compose limited_teleport_assets call to execute on Polkadot
val call = polkadotRuntime.composeCall(
moduleName = polkadotRuntime.metadata.xcmPalletName(),
callName = "limited_teleport_assets",
arguments = mapOf(
"dest" to pahVersionedLocation.toEncodableInstance(),
"beneficiary" to beneficiary.versionedXcm(xcmVersion).toEncodableInstance(),
"assets" to MultiAssets(assets).versionedXcm(xcmVersion).toEncodableInstance(),
"fee_asset_item" to BigInteger.ZERO,
"weight_limit" to WeightLimit.Unlimited.toEncodableInstance()
)
)
// Dry run call execution
val dryRunEffects = dryRunApi.dryRunCall(
originCaller = OriginCaller.System.Signed(origin),
call = call,
chainId = polkadot.id,
xcmResultsVersion = XcmVersion.V4
)
.getOrThrow()
.toResult().getOrThrow()
// Find xcm forwarded to Polkadot Asset Hub
val forwardedXcm = dryRunEffects.forwardedXcms.getByLocation(pahVersionedLocation).first()
println(forwardedXcm)
// Dry run execution of forwarded message on Polkadot Asset Hub
val xcmDryRunEffects = dryRunApi.dryRunXcm(
xcm = forwardedXcm,
originLocation = polkadotLocation.fromPointOfViewOf(polkadotAssetHubLocation).versionedXcm(xcmVersion),
chainId = polkadotAssetHub.id
)
.getOrThrow()
.toResult().getOrThrow()
println(xcmDryRunEffects.emittedEvents)
}
}
@@ -0,0 +1,52 @@
package io.novafoundation.nova
import android.util.Log
import io.novafoundation.nova.common.utils.average
import io.novafoundation.nova.common.utils.divideToDecimal
import io.novafoundation.nova.runtime.ethereum.gas.LegacyGasPriceProvider
import io.novafoundation.nova.runtime.ethereum.gas.MaxPriorityFeeGasProvider
import io.novafoundation.nova.runtime.ext.Ids
import io.novafoundation.nova.runtime.multiNetwork.getCallEthereumApiOrThrow
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.take
import org.junit.Test
import java.math.BigInteger
class GasPriceProviderIntegrationTest : BaseIntegrationTest() {
@Test
fun compareLegacyAndImprovedGasPriceEstimations() = runTest {
val api = chainRegistry.getCallEthereumApiOrThrow(Chain.Ids.MOONBEAM)
val legacy = LegacyGasPriceProvider(api)
val improved = MaxPriorityFeeGasProvider(api)
val legacyStats = mutableSetOf<BigInteger>()
val improvedStats = mutableSetOf<BigInteger>()
api.newHeadsFlow().map {
legacyStats.add(legacy.getGasPrice())
improvedStats.add(improved.getGasPrice())
}
.take(1000)
.collect()
legacyStats.printStats("Legacy")
improvedStats.printStats("Improved")
}
private fun Set<BigInteger>.printStats(name: String) {
val min = min()
val max = max()
Log.d("GasPriceProviderIntegrationTest", """
Stats for $name source
Min: $min
Max: $max
Avg: ${average()}
Max/Min ratio: ${max.divideToDecimal(min)}
""")
}
}
@@ -0,0 +1,247 @@
package io.novafoundation.nova
import android.util.Log
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.LOG_TAG
import io.novafoundation.nova.common.utils.firstLoaded
import io.novafoundation.nova.common.utils.inBackground
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.ReferendumId
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.VoteType
import io.novafoundation.nova.feature_governance_api.data.source.SupportedGovernanceOption
import io.novafoundation.nova.feature_governance_api.di.GovernanceFeatureApi
import io.novafoundation.nova.feature_governance_api.domain.referendum.filters.ReferendumType
import io.novafoundation.nova.feature_governance_api.domain.referendum.filters.ReferendumTypeFilter
import io.novafoundation.nova.feature_governance_impl.data.RealGovernanceAdditionalState
import io.novafoundation.nova.runtime.ext.externalApi
import io.novafoundation.nova.runtime.ext.utilityAsset
import io.novafoundation.nova.runtime.multiNetwork.ChainWithAsset
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novafoundation.nova.runtime.multiNetwork.chain.model.FullChainAssetId
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import org.junit.Test
import java.math.BigInteger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
class GovernanceIntegrationTest : BaseIntegrationTest() {
private val accountApi = FeatureUtils.getFeature<AccountFeatureApi>(context, AccountFeatureApi::class.java)
private val governanceApi = FeatureUtils.getFeature<GovernanceFeatureApi>(context, GovernanceFeatureApi::class.java)
@Test
fun shouldRetrieveOnChainReferenda() = runTest {
val chain = chain()
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
val onChainReferendaRepository = source(selectedGovernance).referenda
val referenda = onChainReferendaRepository.getAllOnChainReferenda(chain.id)
Log.d(this@GovernanceIntegrationTest.LOG_TAG, referenda.toString())
}
@Test
fun shouldRetrieveConvictionVotes() = runTest {
val chain = chain()
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
val convictionVotingRepository = source(selectedGovernance).convictionVoting
val accountId = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".toAccountId()
val votes = convictionVotingRepository.votingFor(accountId, chain.id)
Log.d(this@GovernanceIntegrationTest.LOG_TAG, votes.toString())
}
@Test
fun shouldRetrieveTrackLocks() = runTest {
val chain = chain()
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
val convictionVotingRepository = source(selectedGovernance).convictionVoting
val accountId = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".toAccountId()
val fullChainAssetId = FullChainAssetId(chain.id, chain.utilityAsset.id)
val trackLocks = convictionVotingRepository.trackLocksFlow(accountId, fullChainAssetId).first()
Log.d(this@GovernanceIntegrationTest.LOG_TAG, trackLocks.toString())
}
@Test
fun shouldRetrieveReferendaTracks() = runTest {
val chain = chain()
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
val onChainReferendaRepository = source(selectedGovernance).referenda
val tracks = onChainReferendaRepository.getTracks(chain.id)
Log.d(this@GovernanceIntegrationTest.LOG_TAG, tracks.toString())
}
@Test
fun shouldRetrieveDomainReferendaPreviews() = runTest {
val accountRepository = accountApi.provideAccountRepository()
val referendaListInteractor = governanceApi.referendaListInteractor
val updateSystem = governanceApi.governanceUpdateSystem
updateSystem.start()
.inBackground()
.launchIn(this)
val metaAccount = accountRepository.getSelectedMetaAccount()
val accountId = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".toAccountId()
val chain = chain()
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
val filterFlow: Flow<ReferendumTypeFilter> = flow {
val referendaFilter = ReferendumTypeFilter(ReferendumType.ALL)
emit(referendaFilter)
}
val referendaByGroup = referendaListInteractor.referendaListStateFlow(metaAccount, accountId, selectedGovernance, this, filterFlow).firstLoaded()
val referenda = referendaByGroup.groupedReferenda.values.flatten()
Log.d(this@GovernanceIntegrationTest.LOG_TAG, referenda.joinToString("\n"))
}
@Test
fun shouldRetrieveDomainReferendumDetails() = runTest {
val referendumDetailsInteractor = governanceApi.referendumDetailsInteractor
val updateSystem = governanceApi.governanceUpdateSystem
updateSystem.start()
.inBackground()
.launchIn(this)
val accountId = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".toAccountId()
val referendumId = ReferendumId(BigInteger.ZERO)
val chain = chain()
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
val referendumDetails = referendumDetailsInteractor.referendumDetailsFlow(referendumId, selectedGovernance, accountId, CoroutineScope(Dispatchers.Main))
.first()
Log.d(this@GovernanceIntegrationTest.LOG_TAG, referendumDetails.toString())
val callDetails = referendumDetailsInteractor.detailsFor(
preImage = referendumDetails?.onChainMetadata!!.preImage!!,
chain = chain
)
Log.d(this@GovernanceIntegrationTest.LOG_TAG, callDetails.toString())
}
@Test
fun shouldRetrieveVoters() = runTest {
val interactor = governanceApi.referendumVotersInteractor
val referendumId = ReferendumId(BigInteger.ZERO)
val referendumVoters = interactor.votersFlow(referendumId, VoteType.AYE)
.first()
Log.d(this@GovernanceIntegrationTest.LOG_TAG, referendumVoters.toString())
}
@Test
fun shouldFetchDelegatesList() = runTest {
val interactor = governanceApi.delegateListInteractor
val updateSystem = governanceApi.governanceUpdateSystem
updateSystem.start()
.inBackground()
.launchIn(this)
val chain = kusama()
val delegates = interactor.getDelegates(
governanceOption = supportedGovernanceOption(chain, Chain.Governance.V2),
scope = this
)
Log.d(this@GovernanceIntegrationTest.LOG_TAG, delegates.toString())
}
@Test
fun shouldFetchDelegateDetails() = runTest {
val delegateAccountId = "DCZyhphXsRLcW84G9WmWEXtAA8DKGtVGSFZLJYty8Ajjyfa".toAccountId() // ChaosDAO
val interactor = governanceApi.delegateDetailsInteractor
val updateSystem = governanceApi.governanceUpdateSystem
updateSystem.start()
.inBackground()
.launchIn(this)
val delegate = interactor.delegateDetailsFlow(delegateAccountId)
Log.d(this@GovernanceIntegrationTest.LOG_TAG, delegate.toString())
}
@Test
fun shouldFetchChooseTrackData() = runTest {
val interactor = governanceApi.newDelegationChooseTrackInteractor
val updateSystem = governanceApi.governanceUpdateSystem
updateSystem.start()
.inBackground()
.launchIn(this)
val trackData = interactor.observeNewDelegationTrackData().first()
Log.d(
this@GovernanceIntegrationTest.LOG_TAG,
"""
Available: ${trackData.trackPartition.available.size}
Already voted: ${trackData.trackPartition.alreadyVoted.size}
Already delegated: ${trackData.trackPartition.alreadyDelegated.size}
Presets: ${trackData.presets}
""".trimIndent()
)
}
@Test
fun shouldFetchDelegators() = runTest {
val delegateAddress = "DCZyhphXsRLcW84G9WmWEXtAA8DKGtVGSFZLJYty8Ajjyfa" // ChaosDAO
val interactor = governanceApi.delegateDelegatorsInteractor
val updateSystem = governanceApi.governanceUpdateSystem
updateSystem.start()
.inBackground()
.launchIn(this)
val delegators = interactor.delegatorsFlow(delegateAddress.toAccountId()).first()
Log.d(this@GovernanceIntegrationTest.LOG_TAG, delegators.toString())
}
private suspend fun source(supportedGovernance: SupportedGovernanceOption) = governanceApi.governanceSourceRegistry.sourceFor(supportedGovernance)
private fun supportedGovernanceOption(chain: Chain, governance: Chain.Governance) =
SupportedGovernanceOption(
ChainWithAsset(chain, chain.utilityAsset),
RealGovernanceAdditionalState(
governance,
false
)
)
private suspend fun chain(): Chain = chainRegistry.currentChains.map { chains ->
chains.find { it.governance.isNotEmpty() }
}
.filterNotNull()
.first()
private suspend fun kusama(): Chain = chainRegistry.currentChains.mapNotNull { chains ->
chains.find { it.externalApi<Chain.ExternalApi.GovernanceDelegations>() != null }
}.first()
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,60 @@
package io.novafoundation.nova
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
import io.novafoundation.nova.feature_nft_api.NftFeatureApi
import io.novafoundation.nova.feature_nft_api.data.model.isFullySynced
import io.novafoundation.nova.runtime.di.RuntimeApi
import io.novafoundation.nova.runtime.di.RuntimeComponent
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.runBlocking
import org.junit.Test
class NftFullSyncIntegrationTest {
private val nftApi = FeatureUtils.getFeature<NftFeatureApi>(
ApplicationProvider.getApplicationContext<Context>(),
NftFeatureApi::class.java
)
private val accountApi = FeatureUtils.getFeature<AccountFeatureApi>(
ApplicationProvider.getApplicationContext<Context>(),
AccountFeatureApi::class.java
)
private val runtimeApi = FeatureUtils.getFeature<RuntimeComponent>(
ApplicationProvider.getApplicationContext<Context>(),
RuntimeApi::class.java
)
private val externalRequirementFlow = runtimeApi.externalRequirementFlow()
@Test
fun testFullSyncIntegration(): Unit = runBlocking {
externalRequirementFlow.emit(ChainConnection.ExternalRequirement.ALLOWED)
val metaAccount = accountApi.accountUseCase().getSelectedMetaAccount()
val nftRepository = nftApi.nftRepository
nftRepository.initialNftSync(metaAccount, true)
nftRepository.allNftFlow(metaAccount)
.map { nfts -> nfts.filter { !it.isFullySynced } }
.takeWhile { it.isNotEmpty() }
.onEach { unsyncedNfts ->
unsyncedNfts.forEach { nftRepository.fullNftSync(it) }
}
.onCompletion {
print("Full sync done")
}
.launchIn(this)
}
}
@@ -0,0 +1,181 @@
package io.novafoundation.nova
import android.content.Context
import android.util.Log
import androidx.test.core.app.ApplicationProvider
import io.novafoundation.nova.common.data.network.runtime.binding.bindAccountId
import io.novafoundation.nova.common.data.network.runtime.binding.bindBoolean
import io.novafoundation.nova.common.data.network.runtime.binding.bindString
import io.novafoundation.nova.common.data.network.runtime.binding.cast
import io.novafoundation.nova.common.data.network.runtime.binding.getTyped
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.LOG_TAG
import io.novafoundation.nova.common.utils.uniques
import io.novafoundation.nova.runtime.di.RuntimeApi
import io.novafoundation.nova.runtime.di.RuntimeComponent
import io.novafoundation.nova.runtime.ext.addressOf
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
import io.novafoundation.nova.runtime.storage.source.multi.MultiQueryBuilder
import io.novafoundation.nova.runtime.storage.source.query.multi
import io.novasama.substrate_sdk_android.runtime.AccountId
import io.novasama.substrate_sdk_android.runtime.definitions.types.composite.Struct
import io.novasama.substrate_sdk_android.runtime.metadata.storage
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Test
import java.math.BigInteger
data class UniquesClass(
val id: BigInteger,
val metadata: Metadata?,
val details: Details
) {
data class Metadata(
val deposit: BigInteger,
val data: String,
)
data class Details(
val instances: BigInteger,
val frozen: Boolean
)
}
data class UniquesInstance(
val collection: UniquesClass,
val id: BigInteger,
val metadata: Metadata?,
val details: Details
) {
data class Metadata(
val data: String,
)
data class Details(
val owner: String,
val frozen: Boolean,
)
}
class NftUniquesIntegrationTest {
private val chainGenesis = "48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a"
private val runtimeApi = FeatureUtils.getFeature<RuntimeComponent>(
ApplicationProvider.getApplicationContext<Context>(),
RuntimeApi::class.java
)
private val chainRegistry = runtimeApi.chainRegistry()
private val externalRequirementFlow = runtimeApi.externalRequirementFlow()
private val storageRemoteSource = runtimeApi.remoteStorageSource()
@Test
fun testUniquesIntegration(): Unit = runBlocking {
chainRegistry.currentChains.first() // wait till chains are ready
externalRequirementFlow.emit(ChainConnection.ExternalRequirement.ALLOWED)
val chain = chainRegistry.getChain(chainGenesis)
val accountId = "JGKSibhyZgzY7jEe5a9gdybDEbqNNRSxYyJJmeeycbCbQ5v".toAccountId()
val instances = storageRemoteSource.query(chainGenesis) {
val classesWithInstances = runtime.metadata.uniques().storage("Account").keys(accountId)
.map { (_: AccountId, collection: BigInteger, instance: BigInteger) ->
listOf(collection, instance)
}
val classesIds = classesWithInstances.map { (collection, _) -> collection }.distinct()
val classDetailsDescriptor: MultiQueryBuilder.Descriptor<BigInteger, UniquesClass.Details>
val classMetadatasDescriptor: MultiQueryBuilder.Descriptor<BigInteger, UniquesClass.Metadata?>
val instancesDetailsDescriptor: MultiQueryBuilder.Descriptor<Pair<BigInteger, BigInteger>, UniquesInstance.Details>
val instancesMetadataDescriptor: MultiQueryBuilder.Descriptor<Pair<BigInteger, BigInteger>, UniquesInstance.Metadata?>
val multiQueryResults = multi {
classDetailsDescriptor = runtime.metadata.uniques().storage("Class").querySingleArgKeys(
keysArgs = classesIds,
keyExtractor = { it.component1<BigInteger>() },
binding = { parsedValue ->
val classDetailsStruct = parsedValue.cast<Struct.Instance>()
UniquesClass.Details(
instances = classDetailsStruct.getTyped("instances"),
frozen = classDetailsStruct.getTyped("isFrozen")
)
}
)
classMetadatasDescriptor = runtime.metadata.uniques().storage("ClassMetadataOf").querySingleArgKeys(
keysArgs = classesIds,
keyExtractor = { it.component1<BigInteger>() },
binding = { parsedValue ->
parsedValue?.cast<Struct.Instance>()?.let { classMetadataStruct ->
UniquesClass.Metadata(
deposit = classMetadataStruct.getTyped("deposit"),
data = bindString(classMetadataStruct["data"])
)
}
}
)
instancesDetailsDescriptor = runtime.metadata.uniques().storage("Asset").queryKeys(
keysArgs = classesWithInstances,
keyExtractor = { it.component1<BigInteger>() to it.component2<BigInteger>() },
binding = { parsedValue ->
val instanceDetailsStruct = parsedValue.cast<Struct.Instance>()
UniquesInstance.Details(
owner = chain.addressOf(bindAccountId(instanceDetailsStruct["owner"])),
frozen = bindBoolean(instanceDetailsStruct["isFrozen"])
)
}
)
instancesMetadataDescriptor = runtime.metadata.uniques().storage("InstanceMetadataOf").queryKeys(
keysArgs = classesWithInstances,
keyExtractor = { it.component1<BigInteger>() to it.component2<BigInteger>() },
binding = { parsedValue ->
parsedValue?.cast<Struct.Instance>()?.let {
UniquesInstance.Metadata(
data = bindString(it["data"])
)
}
}
)
}
val classDetails = multiQueryResults[classDetailsDescriptor]
val classMetadatas = multiQueryResults[classMetadatasDescriptor]
val instancesDetails = multiQueryResults[instancesDetailsDescriptor]
val instancesMetadatas = multiQueryResults[instancesMetadataDescriptor]
val classes = classesIds.associateWith { classId ->
UniquesClass(
id = classId,
metadata = classMetadatas[classId],
details = classDetails.getValue(classId)
)
}
classesWithInstances.map { (collectionId, instanceId) ->
val instanceKey = collectionId to instanceId
UniquesInstance(
collection = classes.getValue(collectionId),
id = instanceId,
metadata = instancesMetadatas[instanceKey],
details = instancesDetails.getValue(instanceKey)
)
}
}
Log.d(LOG_TAG, instances.toString())
}
}
@@ -0,0 +1,49 @@
package io.novafoundation.nova
import android.util.Log
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.Fraction
import io.novafoundation.nova.common.utils.Perbill
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
import io.novafoundation.nova.feature_staking_api.domain.nominationPool.model.PoolId
import io.novafoundation.nova.feature_staking_impl.data.StakingOption
import io.novafoundation.nova.feature_staking_impl.data.StakingSharedState.OptionAdditionalData
import io.novafoundation.nova.feature_staking_impl.di.StakingFeatureComponent
import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.common.rewards.NominationPoolRewardCalculator
import io.novafoundation.nova.runtime.ext.utilityAsset
import io.novafoundation.nova.runtime.multiNetwork.ChainWithAsset
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain.Asset.StakingType
import kotlinx.coroutines.flow.launchIn
import org.junit.Test
class NominationPoolsRewardCalculatorIntegrationTest : BaseIntegrationTest() {
private val stakingFeatureComponent = FeatureUtils.getFeature<StakingFeatureComponent>(context, StakingFeatureApi::class.java)
private val nominationPoolRewardCalculatorFactory = stakingFeatureComponent.nominationPoolRewardCalculatorFactory
private val stakingUpdateSystem = stakingFeatureComponent.stakingUpdateSystem
private val stakingSharedState = stakingFeatureComponent.stakingSharedState
@Test
fun testRewardCalculator() = runTest {
val polkadot = chainRegistry.polkadot()
val stakingOption = StakingOption(
assetWithChain = ChainWithAsset(polkadot, polkadot.utilityAsset),
additional = OptionAdditionalData(StakingType.NOMINATION_POOLS)
)
stakingSharedState.setSelectedOption(stakingOption)
stakingUpdateSystem.start()
.launchIn(this)
val rewardCalculator = nominationPoolRewardCalculatorFactory.create(stakingOption, sharedComputationScope = this)
Log.d("NominationPoolsRewardCalculatorIntegrationTest", "Max APY: ${rewardCalculator.maxAPY}")
Log.d("NominationPoolsRewardCalculatorIntegrationTest", "APY for Nova Pool: ${rewardCalculator.apyFor(54)}")
}
private fun NominationPoolRewardCalculator.apyFor(poolId: Int): Fraction? {
return apyFor(PoolId(poolId))
}
}
@@ -0,0 +1,71 @@
package io.novafoundation.nova
import android.util.Log
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.domain.ExtendedLoadingState
import io.novafoundation.nova.common.utils.inBackground
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
import io.novafoundation.nova.feature_staking_api.domain.dashboard.model.AggregatedStakingDashboardOption
import io.novafoundation.nova.feature_staking_api.domain.dashboard.model.StakingDashboard
import io.novafoundation.nova.feature_staking_api.domain.dashboard.model.isSyncing
import kotlinx.coroutines.flow.launchIn
import org.junit.Test
import java.lang.reflect.Type
class StakingDashboardIntegrationTest: BaseIntegrationTest() {
private val stakingApi = FeatureUtils.getFeature<StakingFeatureApi>(context, StakingFeatureApi::class.java)
private val interactor = stakingApi.dashboardInteractor
private val updateSystem = stakingApi.dashboardUpdateSystem
private val gson = GsonBuilder()
.registerTypeHierarchyAdapter(AggregatedStakingDashboardOption::class.java, AggregatedStakingDashboardOptionDesirializer())
.create()
@Test
fun syncStakingDashboard() = runTest {
updateSystem.start()
.inBackground()
.launchIn(this)
interactor.stakingDashboardFlow()
.inBackground()
.collect(::logDashboard)
}
private fun logDashboard(dashboard: ExtendedLoadingState<StakingDashboard>) {
if (dashboard !is ExtendedLoadingState.Loaded) return
val serialized = gson.toJson(dashboard)
val message = """
Dashboard state:
Syncing items: ${dashboard.data.syncingItemsCount()}
$serialized
""".trimIndent()
Log.d("StakingDashboardIntegrationTest", message)
}
private class AggregatedStakingDashboardOptionDesirializer : JsonSerializer<AggregatedStakingDashboardOption<*>> {
override fun serialize(src: AggregatedStakingDashboardOption<*>, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
return JsonObject().apply {
add("chain", JsonPrimitive(src.chain.name))
add("stakingState", context.serialize(src.stakingState))
add("syncing", context.serialize(src.syncingStage))
}
}
}
private fun StakingDashboard.syncingItemsCount(): Int {
return withoutStake.count { it.syncingStage.isSyncing() } + hasStake.count { it.syncingStage.isSyncing() }
}
}
@@ -0,0 +1,46 @@
package io.novafoundation.nova
import android.util.Log
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.LOG_TAG
import io.novafoundation.nova.feature_staking_api.data.parachainStaking.turing.repository.AutomationAction
import io.novafoundation.nova.feature_staking_api.data.parachainStaking.turing.repository.OptimalAutomationRequest
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
import io.novafoundation.nova.runtime.multiNetwork.findChain
import kotlinx.coroutines.runBlocking
import org.junit.Test
import java.math.BigInteger
class TuringAutomationIntegrationTest : BaseIntegrationTest() {
private val stakingApi = FeatureUtils.getFeature<StakingFeatureApi>(context, StakingFeatureApi::class.java)
private val automationTasksRepository = stakingApi.turingAutomationRepository
@Test
fun calculateOptimalAutoCompounding(){
runBlocking {
val chain = chainRegistry.findTuringChain()
val request = OptimalAutomationRequest(
collator = "6AEG2WKRVvZteWWT3aMkk2ZE21FvURqiJkYpXimukub8Zb9C",
amount = BigInteger("1000000000000")
)
val response = automationTasksRepository.calculateOptimalAutomation(chain.id, request)
Log.d(LOG_TAG, response.toString())
}
}
@Test
fun calculateAutoCompoundExecutionFees(){
runBlocking {
val chain = chainRegistry.findTuringChain()
val fees = automationTasksRepository.getTimeAutomationFees(chain.id, AutomationAction.AUTO_COMPOUND_DELEGATED_STAKE, executions = 1)
Log.d(LOG_TAG, fees.toString())
}
}
private suspend fun ChainRegistry.findTuringChain() = findChain { it.name == "Turing" }!!
}
@@ -0,0 +1,137 @@
package io.novafoundation.nova
import android.util.Log
import io.novafoundation.nova.common.utils.LOG_TAG
import io.novafoundation.nova.common.utils.second
import io.novafoundation.nova.core.ethereum.Web3Api
import io.novafoundation.nova.core.ethereum.log.Topic
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.types.Balance
import io.novafoundation.nova.runtime.ethereum.contract.base.querySingle
import io.novafoundation.nova.runtime.ethereum.contract.erc20.Erc20Queries
import io.novafoundation.nova.runtime.ethereum.contract.erc20.Erc20Standard
import io.novafoundation.nova.runtime.ethereum.sendSuspend
import io.novafoundation.nova.runtime.multiNetwork.getEthereumApiOrThrow
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.reactive.asFlow
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.web3j.abi.EventEncoder
import org.web3j.abi.TypeEncoder
import org.web3j.abi.datatypes.Address
import org.web3j.protocol.core.DefaultBlockParameterName
import java.math.BigInteger
class Erc20Transfer(
val txHash: String,
val blockNumber: String,
val from: String,
val to: String,
val contract: String,
val amount: BigInteger,
)
class Web3jServiceIntegrationTest : BaseIntegrationTest() {
@Test
fun shouldFetchBalance(): Unit = runBlocking {
val web3j = moonbeamWeb3j()
val balance = web3j.ethGetBalance("0xf977814e90da44bfa03b6295a0616a897441acec", DefaultBlockParameterName.LATEST).sendSuspend()
Log.d(LOG_TAG, balance.balance.toString())
}
@Test
fun shouldFetchComplexStructure(): Unit = runBlocking {
val web3j = moonbeamWeb3j()
val block = web3j.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, true).sendSuspend()
Log.d(LOG_TAG, block.block.hash)
}
@Test
fun shouldSubscribeToNewHeadEvents(): Unit = runBlocking {
val web3j = moonbeamWeb3j()
val newHead = web3j.newHeadsNotifications().asFlow().first()
Log.d(LOG_TAG, "New head appended to chain: ${newHead.params.result.hash}")
}
@Test
fun shouldSubscribeBalances(): Unit = runBlocking {
val web3j = moonbeamWeb3j()
val accountAddress = "0x4A43C16107591AE5Ec904e584ed4Bb05386F98f7"
val moonbeamUsdc = "0x818ec0a7fe18ff94269904fced6ae3dae6d6dc0b"
val balanceUpdates = web3j.erc20BalanceFlow(accountAddress, moonbeamUsdc).take(2).toList()
error("Initial balance: ${balanceUpdates.first()}, new balance: ${balanceUpdates.second()}")
}
private fun Web3Api.erc20BalanceFlow(account: String, contract: String): Flow<Balance> {
return flow {
val erc20 = Erc20Standard().querySingle(contract, web3j = this@erc20BalanceFlow)
val initialBalance = erc20.balanceOfAsync(account).await()
emit(initialBalance)
val changes = accountErcTransfersFlow(account).map {
erc20.balanceOfAsync(account).await()
}
emitAll(changes)
}
}
private fun Web3Api.accountErcTransfersFlow(address: String): Flow<Erc20Transfer> {
val addressTopic = TypeEncoder.encode(Address(address))
val transferEvent = Erc20Queries.TRANSFER_EVENT
val transferEventSignature = EventEncoder.encode(transferEvent)
val contractAddresses = emptyList<String>() // everything
val erc20SendTopic = listOf(
Topic.Single(transferEventSignature), // zero-th topic is event signature
Topic.AnyOf(addressTopic), // our account as `from`
)
val erc20ReceiveTopic = listOf(
Topic.Single(transferEventSignature), // zero-th topic is event signature
Topic.Any, // anyone is `from`
Topic.AnyOf(addressTopic) // out account as `to`
)
val receiveTransferNotifications = logsNotifications(contractAddresses, erc20ReceiveTopic)
val sendTransferNotifications = logsNotifications(contractAddresses, erc20SendTopic)
val transferNotifications = merge(receiveTransferNotifications, sendTransferNotifications)
return transferNotifications.map { logNotification ->
val log = logNotification.params.result
val contract = log.address
val event = Erc20Queries.parseTransferEvent(log)
Erc20Transfer(
txHash = log.transactionHash,
blockNumber = log.blockNumber,
from = event.from.value,
to = event.to.value,
contract = contract,
amount = event.amount.value,
)
}
}
private suspend fun moonbeamWeb3j(): Web3Api {
val moonbeamChainId = "fe58ea77779b7abda7da4ec526d14db9b1e9cd40a217c34892af80a9b332b76d"
return chainRegistry.getEthereumApiOrThrow(moonbeamChainId, Chain.Node.ConnectionType.WSS)
}
}
@@ -0,0 +1,164 @@
package io.novafoundation.nova.balances
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.google.gson.Gson
import io.novafoundation.nova.common.data.network.runtime.binding.AccountInfo
import io.novafoundation.nova.common.data.network.runtime.binding.bindAccountInfo
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.fromJson
import io.novafoundation.nova.common.utils.hasModule
import io.novafoundation.nova.common.utils.system
import io.novafoundation.nova.core.model.CryptoType
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountLocal
import io.novafoundation.nova.feature_account_api.data.ethereum.transaction.TransactionOrigin
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
import io.novafoundation.nova.feature_account_api.domain.model.LightMetaAccount
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
import io.novafoundation.nova.feature_account_impl.di.AccountFeatureComponent
import io.novafoundation.nova.feature_account_impl.domain.account.model.DefaultMetaAccount
import io.novafoundation.nova.runtime.BuildConfig.TEST_CHAINS_URL
import io.novafoundation.nova.runtime.di.RuntimeApi
import io.novafoundation.nova.runtime.di.RuntimeComponent
import io.novafoundation.nova.runtime.extrinsic.systemRemark
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
import io.novafoundation.nova.runtime.multiNetwork.getSocket
import io.novasama.substrate_sdk_android.extensions.fromHex
import io.novasama.substrate_sdk_android.runtime.metadata.storage
import io.novasama.substrate_sdk_android.runtime.metadata.storageKey
import io.novasama.substrate_sdk_android.wsrpc.networkStateFlow
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.math.BigInteger
import java.math.BigInteger.ZERO
import java.net.URL
import kotlin.time.Duration.Companion.seconds
@RunWith(Parameterized::class)
class BalancesIntegrationTest(
private val testChainId: String,
private val testChainName: String,
private val testAccount: String
) {
companion object {
@JvmStatic
@Parameterized.Parameters(name = "{1}")
fun data(): List<Array<String?>> {
val arrayOfNetworks: Array<TestData> = Gson().fromJson(URL(TEST_CHAINS_URL).readText())
return arrayOfNetworks.map { arrayOf(it.chainId, it.name, it.account) }
}
class TestData(
val chainId: String,
val name: String,
val account: String?
)
}
private val maxAmount = BigInteger.valueOf(10).pow(30)
private val runtimeApi = FeatureUtils.getFeature<RuntimeComponent>(
ApplicationProvider.getApplicationContext<Context>(),
RuntimeApi::class.java
)
private val accountApi = FeatureUtils.getFeature<AccountFeatureComponent>(
ApplicationProvider.getApplicationContext<Context>(),
AccountFeatureApi::class.java
)
private val chainRegistry = runtimeApi.chainRegistry()
private val externalRequirementFlow = runtimeApi.externalRequirementFlow()
private val remoteStorage = runtimeApi.remoteStorageSource()
private val extrinsicService = accountApi.extrinsicService()
@Before
fun before() = runBlocking {
externalRequirementFlow.emit(ChainConnection.ExternalRequirement.ALLOWED)
}
@Test
fun testBalancesLoading() = runBlocking(Dispatchers.Default) {
val chains = chainRegistry.getChain(testChainId)
val freeBalance = testBalancesInChainAsync(chains, testAccount)?.data?.free ?: error("Balance was null")
assertTrue("Free balance: $freeBalance is less than $maxAmount", maxAmount > freeBalance)
assertTrue("Free balance: $freeBalance is greater than 0", ZERO < freeBalance)
}
@Test
fun testFeeLoading() = runBlocking(Dispatchers.Default) {
val chains = chainRegistry.getChain(testChainId)
testFeeLoadingAsync(chains)
Unit
}
private suspend fun testBalancesInChainAsync(chain: Chain, currentAccount: String): AccountInfo? {
return coroutineScope {
try {
withTimeout(80.seconds) {
remoteStorage.query(
chainId = chain.id,
keyBuilder = { it.metadata.system().storage("Account").storageKey(it, currentAccount.fromHex()) },
binding = { scale, runtime -> scale?.let { bindAccountInfo(scale, runtime) } }
)
}
} catch (e: Exception) {
throw Exception("Socket state: ${chainRegistry.getSocket(chain.id).networkStateFlow().first()}, error: ${e.message}", e)
}
}
}
private suspend fun testFeeLoadingAsync(chain: Chain) {
return coroutineScope {
withTimeout(80.seconds) {
extrinsicService.estimateFee(chain, testTransactionOrigin()) {
systemRemark(byteArrayOf(0))
val haveBatch = runtime.metadata.hasModule("Utility")
if (haveBatch) {
systemRemark(byteArrayOf(0))
}
}
}
}
}
private fun testTransactionOrigin(): TransactionOrigin = TransactionOrigin.Wallet(
createTestMetaAccount()
)
private fun createTestMetaAccount(): MetaAccount {
val metaAccount = DefaultMetaAccount(
id = 0,
globallyUniqueId = MetaAccountLocal.generateGloballyUniqueId(),
substratePublicKey = testAccount.fromHex(),
substrateCryptoType = CryptoType.SR25519,
substrateAccountId = testAccount.fromHex(),
ethereumAddress = testAccount.fromHex(),
ethereumPublicKey = testAccount.fromHex(),
isSelected = true,
name = "Test",
type = LightMetaAccount.Type.WATCH_ONLY,
status = LightMetaAccount.Status.ACTIVE,
chainAccounts = emptyMap(),
parentMetaId = null
)
return metaAccount
}
}
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">[Debug] Pezkuwi</string>
</resources>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">[Dev] Pezkuwi</string>
</resources>
+234
View File
@@ -0,0 +1,234 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission
android:name="com.google.android.gms.permission.AD_ID"
tools:node="remove" />
<uses-permission
android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:name="io.novafoundation.nova.app.App"
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="true"
tools:replace="android:allowBackup,android:fullBackupContent,android:dataExtractionRules"
tools:targetApi="s">
<activity
android:name="io.novafoundation.nova.app.root.presentation.RootActivity"
android:configChanges="orientation|screenSize"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/Theme.NovaFoundation.Nova"
android:windowSoftInputMode="adjustResize">
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/json" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<data
android:host="buy-success"
android:scheme="pezkuwi" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<intent-filter android:label="@string/app_name">
<data
android:host="@string/deep_linking_host"
android:scheme="@string/deep_linking_scheme" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<intent-filter android:autoVerify="true">
<data android:host="request" />
<data android:scheme="pezkuwiwallet" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<intent-filter android:autoVerify="true">
<data android:host="wc" />
<data android:scheme="pezkuwiwallet" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<intent-filter android:autoVerify="true">
<data
android:pathPattern="/.*@2"
android:scheme="wc" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:scheme="http" />
<data android:host="app.pezkuwichain.io" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"/>
<data android:host="@string/branch_io_link_host"/>
<data android:host="@string/branch_io_link_host_alternate"/>
</intent-filter>
</activity>
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="fullSensor"
android:theme="@style/Theme.NovaFoundation.Nova"
tools:replace="android:theme,screenOrientation" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<receiver
android:name="io.novafoundation.nova.feature_account_impl.presentation.exporting.json.ShareCompletedReceiver"
android:enabled="true"
android:exported="false" />
<receiver
android:name="io.novafoundation.nova.feature_ledger_impl.sdk.connection.usb.UsbLedgerConnection$UsbPermissionReceiver"
android:exported="true"
android:permission="android.permission.BROADCAST_USB" />
<service
android:name="io.novafoundation.nova.feature_push_notifications.NovaFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_pezkuwi" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/android_system_accent" />
<meta-data
android:name="firebase_messaging_auto_init_enabled"
android:value="false" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id" />
<meta-data
android:name="io.novafoundation.nova.transactions_notification_channel_id"
android:value="@string/transactions_notification_channel_id" />
<meta-data
android:name="io.novafoundation.nova.governance_notification_channel_id"
android:value="@string/governance_notification_channel_id" />
<meta-data
android:name="io.novafoundation.nova.staking_notification_channel_id"
android:value="@string/staking_notification_channel_id" />
<meta-data
android:name="io.novafoundation.nova.multisigs_notification_channel_id"
android:value="@string/multisigs_notification_channel_id" />
<meta-data
android:name="io.branch.sdk.BranchKey"
android:value="key_live_dsxlmUqNhbtOYX6e7cfgrpkbqsjGPYBf" />
<meta-data android:name="io.branch.sdk.TestMode" android:value="false" />
</application>
<queries>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="text/plain" />
</intent>
<!-- Allow Google Pay feature in WebView -->
<intent>
<action android:name="org.chromium.intent.action.PAY" />
</intent>
<intent>
<action android:name="org.chromium.intent.action.IS_READY_TO_PAY" />
</intent>
<intent>
<action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS" />
</intent>
</queries>
</manifest>
@@ -0,0 +1,100 @@
package io.novafoundation.nova.app
import android.app.Application
import android.content.Context
import android.content.res.Configuration
import com.walletconnect.android.Core
import com.walletconnect.android.CoreClient
import com.walletconnect.android.relay.ConnectionType
import com.walletconnect.web3.wallet.client.Wallet
import com.walletconnect.web3.wallet.client.Web3Wallet
import io.novafoundation.nova.app.di.app.AppComponent
import io.novafoundation.nova.app.di.deps.FeatureHolderManager
import io.novafoundation.nova.common.di.CommonApi
import io.novafoundation.nova.common.di.FeatureContainer
import io.novafoundation.nova.common.resources.ContextManager
import io.novafoundation.nova.common.resources.LanguagesHolder
import io.novafoundation.nova.common.utils.coroutines.RootScope
import io.novafoundation.nova.feature_deep_linking.presentation.handling.branchIo.BranchIOLinkHandler
import io.novafoundation.nova.feature_wallet_connect_impl.BuildConfig
import javax.inject.Inject
private const val WC_REDIRECT_URL = "pezkuwiwallet://request"
open class App : Application(), FeatureContainer {
@Inject
lateinit var featureHolderManager: FeatureHolderManager
private lateinit var appComponent: AppComponent
private val languagesHolder: LanguagesHolder = LanguagesHolder()
// App global scope using for processes that should work while app is alive
private val rootScope = RootScope()
override fun attachBaseContext(base: Context) {
val contextManager = ContextManager.getInstanceOrInit(base, languagesHolder)
super.attachBaseContext(contextManager.setLocale(base))
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
val contextManager = ContextManager.getInstanceOrInit(this, languagesHolder)
contextManager.setLocale(this)
}
override fun onCreate() {
super.onCreate()
val contextManger = ContextManager.getInstanceOrInit(this, languagesHolder)
appComponent = io.novafoundation.nova.app.di.app.DaggerAppComponent
.builder()
.application(this)
.contextManager(contextManger)
.rootScope(rootScope)
.build()
appComponent.inject(this)
BranchIOLinkHandler.Initializer.init(this)
initializeWalletConnect()
}
override fun <T> getFeature(key: Class<*>): T {
return featureHolderManager.getFeature<T>(key)!!
}
override fun releaseFeature(key: Class<*>) {
featureHolderManager.releaseFeature(key)
}
override fun commonApi(): CommonApi {
return appComponent
}
private fun initializeWalletConnect() {
val projectId = BuildConfig.WALLET_CONNECT_PROJECT_ID
val relayUrl = "relay.walletconnect.com"
val serverUrl = "wss://$relayUrl?projectId=$projectId"
val connectionType = ConnectionType.MANUAL
val appMetaData = Core.Model.AppMetaData(
name = "Pezkuwi Wallet",
description = "Next-gen wallet for Pezkuwichain and Polkadot ecosystem",
url = "https://pezkuwichain.io/",
icons = listOf("https://raw.githubusercontent.com/pezkuwichain/branding/master/logos/Pezkuwi_Wallet_Sun_Color.png"),
redirect = WC_REDIRECT_URL
)
CoreClient.initialize(relayServerUrl = serverUrl, connectionType = connectionType, application = this, metaData = appMetaData) { error ->
// TODO maybe re-initialize client
}
val initParams = Wallet.Params.Init(core = CoreClient)
Web3Wallet.initialize(initParams) { error ->
// TODO maybe re-initialize client
}
}
}
@@ -0,0 +1,44 @@
package io.novafoundation.nova.app.di.app
import dagger.BindsInstance
import dagger.Component
import io.novafoundation.nova.app.App
import io.novafoundation.nova.app.di.app.navigation.NavigationModule
import io.novafoundation.nova.app.di.deps.ComponentHolderModule
import io.novafoundation.nova.common.di.CommonApi
import io.novafoundation.nova.common.di.modules.CommonModule
import io.novafoundation.nova.common.di.modules.NetworkModule
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.common.resources.ContextManager
import io.novafoundation.nova.common.utils.coroutines.RootScope
@ApplicationScope
@Component(
modules = [
AppModule::class,
CommonModule::class,
NetworkModule::class,
NavigationModule::class,
ComponentHolderModule::class,
FeatureManagerModule::class
]
)
interface AppComponent : CommonApi {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: App): Builder
@BindsInstance
fun contextManager(contextManager: ContextManager): Builder
@BindsInstance
fun rootScope(rootScope: RootScope): Builder
fun build(): AppComponent
}
fun inject(app: App)
}
@@ -0,0 +1,29 @@
package io.novafoundation.nova.app.di.app
import android.content.Context
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.App
import io.novafoundation.nova.app.root.presentation.common.RealBuildTypeProvider
import io.novafoundation.nova.app.root.presentation.common.RootActivityIntentProvider
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.common.interfaces.ActivityIntentProvider
import io.novafoundation.nova.common.interfaces.BuildTypeProvider
@Module
class AppModule {
@ApplicationScope
@Provides
fun provideContext(application: App): Context {
return application
}
@Provides
@ApplicationScope
fun provideRootActivityIntentProvider(context: Context): ActivityIntentProvider = RootActivityIntentProvider(context)
@Provides
@ApplicationScope
fun provideBuildTypeProvider(): BuildTypeProvider = RealBuildTypeProvider()
}
@@ -0,0 +1,17 @@
package io.novafoundation.nova.app.di.app
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.di.deps.FeatureHolderManager
import io.novafoundation.nova.common.di.FeatureApiHolder
import io.novafoundation.nova.common.di.scope.ApplicationScope
@Module
class FeatureManagerModule {
@ApplicationScope
@Provides
fun provideFeatureHolderManager(featureApiHolderMap: @JvmSuppressWildcards Map<Class<*>, FeatureApiHolder>): FeatureHolderManager {
return FeatureHolderManager(featureApiHolderMap)
}
}
@@ -0,0 +1,20 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.accountmigration.AccountMigrationNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_account_migration.presentation.AccountMigrationRouter
@Module
class AccountMigrationNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(
navigationHoldersRegistry: NavigationHoldersRegistry
): AccountMigrationRouter = AccountMigrationNavigator(
navigationHoldersRegistry = navigationHoldersRegistry
)
}
@@ -0,0 +1,103 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.account.PolkadotVaultVariantSignCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.account.ScanSeedCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.account.SelectAddressCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.account.SelectMultipleWalletsCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.account.SelectSingleWalletCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.account.SelectWalletCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.cloudBackup.ChangeBackupPasswordCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.cloudBackup.RestoreBackupPasswordCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.cloudBackup.SyncWalletsBackupPasswordCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.pincode.PinCodeTwoFactorVerificationCommunicatorImpl
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.common.sequrity.verification.PinCodeTwoFactorVerificationCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectAddress.SelectAddressCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.account.wallet.list.SelectMultipleWalletsCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.changePassword.ChangeBackupPasswordCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.changePassword.RestoreBackupPasswordCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectWallet.SelectWalletCommunicator
import io.novafoundation.nova.feature_account_impl.data.signer.paritySigner.PolkadotVaultVariantSignCommunicator
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.createPassword.SyncWalletsBackupPasswordCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectSingleWallet.SelectSingleWalletCommunicator
import io.novafoundation.nova.feature_account_impl.presentation.seedScan.ScanSeedCommunicator
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
@Module
class AccountNavigationModule {
@Provides
@ApplicationScope
fun providePinCodeTwoFactorVerificationCommunicator(
navigationHoldersRegistry: NavigationHoldersRegistry
): PinCodeTwoFactorVerificationCommunicator = PinCodeTwoFactorVerificationCommunicatorImpl(navigationHoldersRegistry)
@Provides
@ApplicationScope
fun provideSelectWalletCommunicator(
navigationHoldersRegistry: NavigationHoldersRegistry
): SelectWalletCommunicator = SelectWalletCommunicatorImpl(navigationHoldersRegistry)
@Provides
@ApplicationScope
fun provideParitySignerCommunicator(
navigationHoldersRegistry: NavigationHoldersRegistry
): PolkadotVaultVariantSignCommunicator = PolkadotVaultVariantSignCommunicatorImpl(navigationHoldersRegistry)
@Provides
@ApplicationScope
fun provideSelectAddressCommunicator(
router: AssetsRouter,
navigationHoldersRegistry: NavigationHoldersRegistry
): SelectAddressCommunicator = SelectAddressCommunicatorImpl(router, navigationHoldersRegistry)
@Provides
@ApplicationScope
fun provideScanSeedCommunicator(
navigationHoldersRegistry: NavigationHoldersRegistry
): ScanSeedCommunicator = ScanSeedCommunicatorImpl(navigationHoldersRegistry)
@Provides
@ApplicationScope
fun provideSelectSingleWalletCommunicator(
router: AssetsRouter,
navigationHoldersRegistry: NavigationHoldersRegistry
): SelectSingleWalletCommunicator = SelectSingleWalletCommunicatorImpl(router)
@Provides
@ApplicationScope
fun provideSelectMultipleWalletsCommunicator(
router: AssetsRouter,
navigationHoldersRegistry: NavigationHoldersRegistry
): SelectMultipleWalletsCommunicator = SelectMultipleWalletsCommunicatorImpl(router, navigationHoldersRegistry)
@ApplicationScope
@Provides
fun provideAccountRouter(navigator: Navigator): AccountRouter = navigator
@Provides
@ApplicationScope
fun providePushGovernanceSettingsCommunicator(
router: AccountRouter,
navigationHoldersRegistry: NavigationHoldersRegistry
): SyncWalletsBackupPasswordCommunicator = SyncWalletsBackupPasswordCommunicatorImpl(router, navigationHoldersRegistry)
@Provides
@ApplicationScope
fun provideChangeBackupPasswordCommunicator(
router: AccountRouter,
navigationHoldersRegistry: NavigationHoldersRegistry
): ChangeBackupPasswordCommunicator = ChangeBackupPasswordCommunicatorImpl(router, navigationHoldersRegistry)
@Provides
@ApplicationScope
fun provideRestoreBackupPasswordCommunicator(
router: AccountRouter,
navigationHoldersRegistry: NavigationHoldersRegistry
): RestoreBackupPasswordCommunicator = RestoreBackupPasswordCommunicatorImpl(router, navigationHoldersRegistry)
}
@@ -0,0 +1,18 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.topup.TopUpAddressCommunicatorImpl
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_assets.presentation.topup.TopUpAddressCommunicator
@Module
class AssetNavigationModule {
@ApplicationScope
@Provides
fun provideTopUpAddressCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): TopUpAddressCommunicator {
return TopUpAddressCommunicatorImpl(navigationHoldersRegistry)
}
}
@@ -0,0 +1,17 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.buy.BuyNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_buy_impl.presentation.BuyRouter
@Module
class BuyNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): BuyRouter =
BuyNavigator(navigationHoldersRegistry)
}
@@ -0,0 +1,17 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.chainMigration.ChainMigrationNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_ahm_impl.presentation.ChainMigrationRouter
@Module
class ChainMigrationNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): ChainMigrationRouter =
ChainMigrationNavigator(navigationHoldersRegistry)
}
@@ -0,0 +1,17 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.cloudBackup.CloudBackupNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_cloud_backup_impl.presentation.CloudBackupRouter
@Module
class CloudBackupNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): CloudBackupRouter =
CloudBackupNavigator(navigationHoldersRegistry)
}
@@ -0,0 +1,20 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.wallet.CurrencyNavigator
import io.novafoundation.nova.app.root.presentation.RootRouter
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_currency_api.presentation.CurrencyRouter
@Module
class CurrencyNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(
rootRouter: RootRouter,
navigationHoldersRegistry: NavigationHoldersRegistry,
): CurrencyRouter = CurrencyNavigator(rootRouter, navigationHoldersRegistry)
}
@@ -0,0 +1,26 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.dApp.DAppNavigator
import io.novafoundation.nova.app.root.navigation.navigators.dApp.DAppSearchCommunicatorImpl
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
import io.novafoundation.nova.feature_dapp_impl.presentation.search.DAppSearchCommunicator
@Module
class DAppNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(
navigationHoldersRegistry: NavigationHoldersRegistry
): DAppRouter = DAppNavigator(navigationHoldersRegistry)
@ApplicationScope
@Provides
fun provideSearchDappCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): DAppSearchCommunicator {
return DAppSearchCommunicatorImpl(navigationHoldersRegistry)
}
}
@@ -0,0 +1,29 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.externalSign.ExternalSignCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.externalSign.ExternalSignNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.feature_external_sign_api.model.ExternalSignCommunicator
import io.novafoundation.nova.feature_external_sign_impl.ExternalSignRouter
@Module
class ExternalSignNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): ExternalSignRouter =
ExternalSignNavigator(navigationHoldersRegistry)
@ApplicationScope
@Provides
fun provideSignExtrinsicCommunicator(
navigationHoldersRegistry: NavigationHoldersRegistry,
automaticInteractionGate: AutomaticInteractionGate,
): ExternalSignCommunicator {
return ExternalSignCommunicatorImpl(navigationHoldersRegistry, automaticInteractionGate)
}
}
@@ -0,0 +1,18 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.gift.GiftNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_gift_impl.presentation.GiftRouter
@Module
class GiftNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(commonDelegate: Navigator, navigationHoldersRegistry: NavigationHoldersRegistry): GiftRouter =
GiftNavigator(commonDelegate, navigationHoldersRegistry)
}
@@ -0,0 +1,42 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.governance.GovernanceNavigator
import io.novafoundation.nova.app.root.navigation.navigators.governance.SelectTracksCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.governance.TinderGovVoteCommunicatorImpl
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.common.resources.ContextManager
import io.novafoundation.nova.feature_account_api.presenatation.account.wallet.list.SelectTracksCommunicator
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
import io.novafoundation.nova.feature_governance_impl.presentation.GovernanceRouter
import io.novafoundation.nova.feature_governance_impl.presentation.referenda.vote.setup.tindergov.TinderGovVoteCommunicator
@Module
class GovernanceNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(
navigationHoldersRegistry: NavigationHoldersRegistry,
commonNavigator: Navigator,
contextManager: ContextManager,
dAppRouter: DAppRouter
): GovernanceRouter = GovernanceNavigator(navigationHoldersRegistry, commonNavigator, contextManager, dAppRouter)
@Provides
@ApplicationScope
fun provideSelectTracksCommunicator(
router: GovernanceRouter,
navigationHoldersRegistry: NavigationHoldersRegistry
): SelectTracksCommunicator = SelectTracksCommunicatorImpl(router, navigationHoldersRegistry)
@Provides
@ApplicationScope
fun provideTinderGovVoteCommunicator(
router: GovernanceRouter,
navigationHoldersRegistry: NavigationHoldersRegistry
): TinderGovVoteCommunicator = TinderGovVoteCommunicatorImpl(router, navigationHoldersRegistry)
}
@@ -0,0 +1,34 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.ledger.LedgerNavigator
import io.novafoundation.nova.app.root.navigation.navigators.ledger.LedgerSignCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.ledger.SelectLedgerAddressCommunicatorImpl
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_account_api.presenatation.sign.LedgerSignCommunicator
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
import io.novafoundation.nova.feature_ledger_impl.presentation.LedgerRouter
import io.novafoundation.nova.feature_ledger_impl.presentation.account.connect.legacy.SelectLedgerAddressInterScreenCommunicator
@Module
class LedgerNavigationModule {
@ApplicationScope
@Provides
fun provideSelectLedgerAddressCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectLedgerAddressInterScreenCommunicator {
return SelectLedgerAddressCommunicatorImpl(navigationHoldersRegistry)
}
@Provides
@ApplicationScope
fun provideLedgerSignerCommunicator(
navigationHoldersRegistry: NavigationHoldersRegistry
): LedgerSignCommunicator = LedgerSignCommunicatorImpl(navigationHoldersRegistry)
@ApplicationScope
@Provides
fun provideRouter(router: AccountRouter, navigationHoldersRegistry: NavigationHoldersRegistry): LedgerRouter =
LedgerNavigator(router, navigationHoldersRegistry)
}
@@ -0,0 +1,20 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.multisig.MultisigOperationsNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_multisig_operations.presentation.MultisigOperationsRouter
@Module
class MultisigNavigationModule {
@ApplicationScope
@Provides
fun provideOperationsRouter(
navigationHoldersRegistry: NavigationHoldersRegistry,
commonDelegate: Navigator
): MultisigOperationsRouter = MultisigOperationsNavigator(navigationHoldersRegistry, commonDelegate)
}
@@ -0,0 +1,100 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.di.app.navigation.staking.StakingNavigationModule
import io.novafoundation.nova.app.root.navigation.holders.RootNavigationHolder
import io.novafoundation.nova.app.root.navigation.holders.SplitScreenNavigationHolder
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.presentation.RootRouter
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.common.navigation.DelayedNavigationRouter
import io.novafoundation.nova.common.resources.ContextManager
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
import io.novafoundation.nova.feature_crowdloan_impl.presentation.CrowdloanRouter
import io.novafoundation.nova.feature_onboarding_impl.OnboardingRouter
import io.novafoundation.nova.feature_wallet_connect_impl.WalletConnectRouter
import io.novafoundation.nova.splash.SplashRouter
@Module(
includes = [
AccountNavigationModule::class,
AssetNavigationModule::class,
DAppNavigationModule::class,
NftNavigationModule::class,
StakingNavigationModule::class,
LedgerNavigationModule::class,
CurrencyNavigationModule::class,
GovernanceNavigationModule::class,
WalletConnectNavigationModule::class,
VoteNavigationModule::class,
VersionsNavigationModule::class,
ExternalSignNavigationModule::class,
SettingsNavigationModule::class,
SwapNavigationModule::class,
BuyNavigationModule::class,
PushNotificationsNavigationModule::class,
CloudBackupNavigationModule::class,
AssetNavigationModule::class,
AccountMigrationNavigationModule::class,
MultisigNavigationModule::class,
ChainMigrationNavigationModule::class,
WalletNavigationModule::class,
GiftNavigationModule::class
]
)
class NavigationModule {
@ApplicationScope
@Provides
fun provideMainNavigatorHolder(
contextManager: ContextManager
): SplitScreenNavigationHolder = SplitScreenNavigationHolder(contextManager)
@ApplicationScope
@Provides
fun provideDappNavigatorHolder(
contextManager: ContextManager
): RootNavigationHolder = RootNavigationHolder(contextManager)
@ApplicationScope
@Provides
fun provideNavigationHoldersRegistry(
rootNavigatorHolder: RootNavigationHolder,
splitScreenNavigationHolder: SplitScreenNavigationHolder,
): NavigationHoldersRegistry {
return NavigationHoldersRegistry(splitScreenNavigationHolder, rootNavigatorHolder)
}
@ApplicationScope
@Provides
fun provideNavigator(
navigationHoldersRegistry: NavigationHoldersRegistry,
walletConnectRouter: WalletConnectRouter
): Navigator = Navigator(navigationHoldersRegistry, walletConnectRouter)
@Provides
@ApplicationScope
fun provideRootRouter(navigator: Navigator): RootRouter = navigator
@ApplicationScope
@Provides
fun provideSplashRouter(navigator: Navigator): SplashRouter = navigator
@ApplicationScope
@Provides
fun provideOnboardingRouter(navigator: Navigator): OnboardingRouter = navigator
@ApplicationScope
@Provides
fun provideAssetsRouter(navigator: Navigator): AssetsRouter = navigator
@ApplicationScope
@Provides
fun provideCrowdloanRouter(navigator: Navigator): CrowdloanRouter = navigator
@ApplicationScope
@Provides
fun provideDelayedNavigationRouter(navigator: Navigator): DelayedNavigationRouter = navigator
}
@@ -0,0 +1,17 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.nft.NftNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_nft_impl.NftRouter
@Module
class NftNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): NftRouter =
NftNavigator(navigationHoldersRegistry)
}
@@ -0,0 +1,44 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.push.PushGovernanceSettingsCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.push.PushMultisigSettingsCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.push.PushNotificationsNavigator
import io.novafoundation.nova.app.root.navigation.navigators.push.PushStakingSettingsCommunicatorImpl
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_push_notifications.PushNotificationsRouter
import io.novafoundation.nova.feature_push_notifications.presentation.governance.PushGovernanceSettingsCommunicator
import io.novafoundation.nova.feature_push_notifications.presentation.multisigs.PushMultisigSettingsCommunicator
import io.novafoundation.nova.feature_push_notifications.presentation.staking.PushStakingSettingsCommunicator
@Module
class PushNotificationsNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): PushNotificationsRouter =
PushNotificationsNavigator(navigationHoldersRegistry)
@Provides
@ApplicationScope
fun providePushGovernanceSettingsCommunicator(
router: PushNotificationsRouter,
navigationHoldersRegistry: NavigationHoldersRegistry
): PushGovernanceSettingsCommunicator = PushGovernanceSettingsCommunicatorImpl(router, navigationHoldersRegistry)
@Provides
@ApplicationScope
fun providePushStakingSettingsCommunicator(
router: PushNotificationsRouter,
navigationHoldersRegistry: NavigationHoldersRegistry
): PushStakingSettingsCommunicator = PushStakingSettingsCommunicatorImpl(router, navigationHoldersRegistry)
@Provides
@ApplicationScope
fun providePushMultisigSettingsCommunicator(
router: PushNotificationsRouter,
navigationHoldersRegistry: NavigationHoldersRegistry
): PushMultisigSettingsCommunicator = PushMultisigSettingsCommunicatorImpl(router, navigationHoldersRegistry)
}
@@ -0,0 +1,24 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.settings.SettingsNavigator
import io.novafoundation.nova.app.root.presentation.RootRouter
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_settings_impl.SettingsRouter
import io.novafoundation.nova.feature_wallet_connect_impl.WalletConnectRouter
@Module
class SettingsNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(
rootRouter: RootRouter,
navigationHoldersRegistry: NavigationHoldersRegistry,
walletConnectRouter: WalletConnectRouter,
navigator: Navigator,
): SettingsRouter = SettingsNavigator(navigationHoldersRegistry, rootRouter, walletConnectRouter, navigator)
}
@@ -0,0 +1,20 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.swap.SwapNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_swap_impl.presentation.SwapRouter
@Module
class SwapNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(
navigationHoldersRegistry: NavigationHoldersRegistry,
commonDelegate: Navigator
): SwapRouter = SwapNavigator(navigationHoldersRegistry, commonDelegate)
}
@@ -0,0 +1,22 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.versions.VersionsNavigator
import io.novafoundation.nova.common.data.network.AppLinksProvider
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.common.resources.ContextManager
import io.novafoundation.nova.feature_versions_api.presentation.VersionsRouter
@Module
class VersionsNavigationModule {
@Provides
@ApplicationScope
fun provideRouter(
navigationHoldersRegistry: NavigationHoldersRegistry,
contextManager: ContextManager,
appLinksProvider: AppLinksProvider
): VersionsRouter = VersionsNavigator(navigationHoldersRegistry, contextManager, appLinksProvider.storeUrl)
}
@@ -0,0 +1,16 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.vote.VoteNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_vote.presentation.VoteRouter
@Module
class VoteNavigationModule {
@Provides
@ApplicationScope
fun provideVoteRouter(navigator: Navigator): VoteRouter = VoteNavigator(navigator)
}
@@ -0,0 +1,27 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.walletConnect.ApproveSessionCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.walletConnect.WalletConnectNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.feature_wallet_connect_impl.WalletConnectRouter
import io.novafoundation.nova.feature_wallet_connect_impl.presentation.sessions.approve.ApproveSessionCommunicator
@Module
class WalletConnectNavigationModule {
@Provides
@ApplicationScope
fun provideApproveSessionCommunicator(
navigationHoldersRegistry: NavigationHoldersRegistry,
automaticInteractionGate: AutomaticInteractionGate,
): ApproveSessionCommunicator = ApproveSessionCommunicatorImpl(navigationHoldersRegistry, automaticInteractionGate)
@ApplicationScope
@Provides
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): WalletConnectRouter =
WalletConnectNavigator(navigationHoldersRegistry)
}
@@ -0,0 +1,20 @@
package io.novafoundation.nova.app.di.app.navigation
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.wallet.WalletNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_wallet_impl.presentation.WalletRouter
@Module
class WalletNavigationModule {
@ApplicationScope
@Provides
fun provideRouter(
navigationHoldersRegistry: NavigationHoldersRegistry,
commonDelegate: Navigator
): WalletRouter = WalletNavigator(commonDelegate, navigationHoldersRegistry)
}
@@ -0,0 +1,38 @@
package io.novafoundation.nova.app.di.app.navigation.staking
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.staking.mythos.MythosStakingNavigator
import io.novafoundation.nova.app.root.navigation.navigators.staking.mythos.SelectMythCollatorSettingsInterScreenCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.staking.mythos.SelectMythosCollatorInterScreenCommunicatorImpl
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_staking_impl.presentation.MythosStakingRouter
import io.novafoundation.nova.feature_staking_impl.presentation.StakingDashboardRouter
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.SelectMythosInterScreenCommunicator
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollatorSettings.SelectMythCollatorSettingsInterScreenCommunicator
@Module
class MythosStakingNavigationModule {
@Provides
@ApplicationScope
fun provideMythosStakingRouter(
navigationHoldersRegistry: NavigationHoldersRegistry,
stakingDashboardRouter: StakingDashboardRouter,
): MythosStakingRouter {
return MythosStakingNavigator(navigationHoldersRegistry, stakingDashboardRouter)
}
@Provides
@ApplicationScope
fun provideSelectCollatorCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectMythosInterScreenCommunicator {
return SelectMythosCollatorInterScreenCommunicatorImpl(navigationHoldersRegistry)
}
@Provides
@ApplicationScope
fun provideSelectSettingsCollatorCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectMythCollatorSettingsInterScreenCommunicator {
return SelectMythCollatorSettingsInterScreenCommunicatorImpl(navigationHoldersRegistry)
}
}
@@ -0,0 +1,19 @@
package io.novafoundation.nova.app.di.app.navigation.staking
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.staking.nominationPools.NominationPoolsStakingNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_staking_impl.presentation.NominationPoolsRouter
@Module
class NominationPoolsStakingNavigationModule {
@Provides
@ApplicationScope
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry, navigator: Navigator): NominationPoolsRouter {
return NominationPoolsStakingNavigator(navigationHoldersRegistry, navigator)
}
}
@@ -0,0 +1,38 @@
package io.novafoundation.nova.app.di.app.navigation.staking
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.staking.parachain.ParachainStakingNavigator
import io.novafoundation.nova.app.root.navigation.navigators.staking.parachain.SelectCollatorInterScreenCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.staking.parachain.SelectCollatorSettingsInterScreenCommunicatorImpl
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_staking_impl.presentation.ParachainStakingRouter
import io.novafoundation.nova.feature_staking_impl.presentation.parachainStaking.collator.common.SelectCollatorInterScreenCommunicator
import io.novafoundation.nova.feature_staking_impl.presentation.parachainStaking.collator.settings.SelectCollatorSettingsInterScreenCommunicator
@Module
class ParachainStakingNavigationModule {
@Provides
@ApplicationScope
fun provideParachainStakingRouter(
navigationHoldersRegistry: NavigationHoldersRegistry,
navigator: Navigator
): ParachainStakingRouter {
return ParachainStakingNavigator(navigationHoldersRegistry, navigator)
}
@Provides
@ApplicationScope
fun provideSelectCollatorCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectCollatorInterScreenCommunicator {
return SelectCollatorInterScreenCommunicatorImpl(navigationHoldersRegistry)
}
@Provides
@ApplicationScope
fun provideSelectCollatorSettingsCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectCollatorSettingsInterScreenCommunicator {
return SelectCollatorSettingsInterScreenCommunicatorImpl(navigationHoldersRegistry)
}
}
@@ -0,0 +1,26 @@
package io.novafoundation.nova.app.di.app.navigation.staking
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.staking.relaychain.RelayStakingNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
import io.novafoundation.nova.feature_staking_impl.presentation.StakingDashboardRouter
import io.novafoundation.nova.feature_staking_impl.presentation.StakingRouter
@Module
class RelayStakingNavigationModule {
@Provides
@ApplicationScope
fun provideRelayStakingRouter(
navigationHoldersRegistry: NavigationHoldersRegistry,
navigator: Navigator,
dashboardRouter: StakingDashboardRouter,
dAppRouter: DAppRouter
): StakingRouter {
return RelayStakingNavigator(navigationHoldersRegistry, navigator, dashboardRouter, dAppRouter)
}
}
@@ -0,0 +1,42 @@
package io.novafoundation.nova.app.di.app.navigation.staking
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.staking.StakingDashboardNavigator
import io.novafoundation.nova.app.root.navigation.navigators.staking.StartMultiStakingNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_staking_impl.presentation.StakingDashboardRouter
import io.novafoundation.nova.feature_staking_impl.presentation.StartMultiStakingRouter
@Module(
includes = [
ParachainStakingNavigationModule::class,
RelayStakingNavigationModule::class,
NominationPoolsStakingNavigationModule::class,
MythosStakingNavigationModule::class
]
)
class StakingNavigationModule {
@Provides
@ApplicationScope
fun provideStakingDashboardNavigator(navigationHoldersRegistry: NavigationHoldersRegistry): StakingDashboardNavigator {
return StakingDashboardNavigator(navigationHoldersRegistry)
}
@Provides
@ApplicationScope
fun provideStakingDashboardRouter(relayStakingNavigator: StakingDashboardNavigator): StakingDashboardRouter = relayStakingNavigator
@Provides
@ApplicationScope
fun provideStartMultiStakingRouter(
navigationHoldersRegistry: NavigationHoldersRegistry,
dashboardRouter: StakingDashboardRouter,
commonNavigator: Navigator
): StartMultiStakingRouter {
return StartMultiStakingNavigator(navigationHoldersRegistry, dashboardRouter, commonNavigator)
}
}
@@ -0,0 +1,306 @@
package io.novafoundation.nova.app.di.deps
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import io.novafoundation.nova.app.App
import io.novafoundation.nova.app.root.di.RootApi
import io.novafoundation.nova.app.root.di.RootFeatureHolder
import io.novafoundation.nova.caip.di.CaipApi
import io.novafoundation.nova.caip.di.CaipFeatureHolder
import io.novafoundation.nova.common.di.FeatureApiHolder
import io.novafoundation.nova.common.di.FeatureContainer
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.core_db.di.DbApi
import io.novafoundation.nova.core_db.di.DbHolder
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
import io.novafoundation.nova.feature_account_impl.di.AccountFeatureHolder
import io.novafoundation.nova.feature_account_migration.di.AccountMigrationFeatureApi
import io.novafoundation.nova.feature_account_migration.di.AccountMigrationFeatureHolder
import io.novafoundation.nova.feature_ahm_api.di.ChainMigrationFeatureApi
import io.novafoundation.nova.feature_ahm_impl.di.ChainMigrationFeatureHolder
import io.novafoundation.nova.feature_assets.di.AssetsFeatureApi
import io.novafoundation.nova.feature_assets.di.AssetsFeatureHolder
import io.novafoundation.nova.feature_banners_api.di.BannersFeatureApi
import io.novafoundation.nova.feature_banners_impl.di.BannersFeatureHolder
import io.novafoundation.nova.feature_buy_api.di.BuyFeatureApi
import io.novafoundation.nova.feature_buy_impl.di.BuyFeatureHolder
import io.novafoundation.nova.feature_cloud_backup_api.di.CloudBackupFeatureApi
import io.novafoundation.nova.feature_cloud_backup_impl.di.CloudBackupFeatureHolder
import io.novafoundation.nova.feature_crowdloan_api.di.CrowdloanFeatureApi
import io.novafoundation.nova.feature_crowdloan_impl.di.CrowdloanFeatureHolder
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
import io.novafoundation.nova.feature_currency_impl.di.CurrencyFeatureHolder
import io.novafoundation.nova.feature_dapp_api.di.DAppFeatureApi
import io.novafoundation.nova.feature_dapp_impl.di.DAppFeatureHolder
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureApi
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureHolder
import io.novafoundation.nova.feature_external_sign_api.di.ExternalSignFeatureApi
import io.novafoundation.nova.feature_external_sign_impl.di.ExternalSignFeatureHolder
import io.novafoundation.nova.feature_gift_api.di.GiftFeatureApi
import io.novafoundation.nova.feature_gift_impl.di.GiftFeatureHolder
import io.novafoundation.nova.feature_governance_api.di.GovernanceFeatureApi
import io.novafoundation.nova.feature_governance_impl.di.GovernanceFeatureHolder
import io.novafoundation.nova.feature_ledger_api.di.LedgerFeatureApi
import io.novafoundation.nova.feature_ledger_core.LedgerCoreHolder
import io.novafoundation.nova.feature_ledger_core.di.LedgerCoreApi
import io.novafoundation.nova.feature_ledger_impl.di.LedgerFeatureHolder
import io.novafoundation.nova.feature_multisig_operations.di.MultisigOperationsFeatureApi
import io.novafoundation.nova.feature_multisig_operations.di.MultisigOperationsFeatureHolder
import io.novafoundation.nova.feature_nft_api.NftFeatureApi
import io.novafoundation.nova.feature_nft_impl.di.NftFeatureHolder
import io.novafoundation.nova.feature_onboarding_api.di.OnboardingFeatureApi
import io.novafoundation.nova.feature_onboarding_impl.di.OnboardingFeatureHolder
import io.novafoundation.nova.feature_proxy_api.di.ProxyFeatureApi
import io.novafoundation.nova.feature_proxy_impl.di.ProxyFeatureHolder
import io.novafoundation.nova.feature_push_notifications.di.PushNotificationsFeatureApi
import io.novafoundation.nova.feature_push_notifications.di.PushNotificationsFeatureHolder
import io.novafoundation.nova.feature_settings_api.SettingsFeatureApi
import io.novafoundation.nova.feature_settings_impl.di.SettingsFeatureHolder
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
import io.novafoundation.nova.feature_staking_impl.di.StakingFeatureHolder
import io.novafoundation.nova.feature_swap_api.di.SwapFeatureApi
import io.novafoundation.nova.feature_swap_core.di.SwapCoreHolder
import io.novafoundation.nova.feature_swap_core_api.di.SwapCoreApi
import io.novafoundation.nova.feature_swap_impl.di.SwapFeatureHolder
import io.novafoundation.nova.feature_versions_api.di.VersionsFeatureApi
import io.novafoundation.nova.feature_versions_impl.di.VersionsFeatureHolder
import io.novafoundation.nova.feature_vote.di.VoteFeatureApi
import io.novafoundation.nova.feature_vote.di.VoteFeatureHolder
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
import io.novafoundation.nova.feature_wallet_connect_api.di.WalletConnectFeatureApi
import io.novafoundation.nova.feature_wallet_connect_impl.di.WalletConnectFeatureHolder
import io.novafoundation.nova.feature_wallet_impl.di.WalletFeatureHolder
import io.novafoundation.nova.feature_xcm_api.di.XcmFeatureApi
import io.novafoundation.nova.feature_xcm_impl.di.XcmFeatureHolder
import io.novafoundation.nova.runtime.di.RuntimeApi
import io.novafoundation.nova.runtime.di.RuntimeHolder
import io.novafoundation.nova.splash.di.SplashFeatureApi
import io.novafoundation.nova.splash.di.SplashFeatureHolder
import io.novafoundation.nova.web3names.di.Web3NamesApi
import io.novafoundation.nova.web3names.di.Web3NamesHolder
@Module
interface ComponentHolderModule {
@ApplicationScope
@Binds
fun provideFeatureContainer(application: App): FeatureContainer
@ApplicationScope
@Binds
@ClassKey(SplashFeatureApi::class)
@IntoMap
fun provideSplashFeatureHolder(splashFeatureHolder: SplashFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(DbApi::class)
@IntoMap
fun provideDbFeature(dbHolder: DbHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(OnboardingFeatureApi::class)
@IntoMap
fun provideOnboardingFeature(onboardingFeatureHolder: OnboardingFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(DAppFeatureApi::class)
@IntoMap
fun provideDAppFeature(dAppFeatureHolder: DAppFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(LedgerFeatureApi::class)
@IntoMap
fun provideLedgerFeature(accountFeatureHolder: LedgerFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(LedgerCoreApi::class)
@IntoMap
fun provideLedgerCore(accountFeatureHolder: LedgerCoreHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(GovernanceFeatureApi::class)
@IntoMap
fun provideGovernanceFeature(accountFeatureHolder: GovernanceFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(AccountFeatureApi::class)
@IntoMap
fun provideAccountFeature(accountFeatureHolder: AccountFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(AssetsFeatureApi::class)
@IntoMap
fun provideAssetsFeature(holder: AssetsFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(VoteFeatureApi::class)
@IntoMap
fun provideVoteFeature(holder: VoteFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(WalletFeatureApi::class)
@IntoMap
fun provideWalletFeature(walletFeatureHolder: WalletFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(CurrencyFeatureApi::class)
@IntoMap
fun provideCurrencyFeature(currencyFeatureHolder: CurrencyFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(RootApi::class)
@IntoMap
fun provideMainFeature(accountFeatureHolder: RootFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(StakingFeatureApi::class)
@IntoMap
fun provideStakingFeature(holder: StakingFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(RuntimeApi::class)
@IntoMap
fun provideRuntimeFeature(runtimeHolder: RuntimeHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(Web3NamesApi::class)
@IntoMap
fun provideWeb3Names(web3NamesHolder: Web3NamesHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(CrowdloanFeatureApi::class)
@IntoMap
fun provideCrowdloanFeature(holder: CrowdloanFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(NftFeatureApi::class)
@IntoMap
fun provideNftFeature(holder: NftFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(VersionsFeatureApi::class)
@IntoMap
fun provideVersionsFeature(holder: VersionsFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(CaipApi::class)
@IntoMap
fun provideCaipFeature(holder: CaipFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(ExternalSignFeatureApi::class)
@IntoMap
fun provideExternalSignFeature(holder: ExternalSignFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(WalletConnectFeatureApi::class)
@IntoMap
fun provideWalletConnectFeature(holder: WalletConnectFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(SettingsFeatureApi::class)
@IntoMap
fun provideSettingsFeature(holder: SettingsFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(SwapFeatureApi::class)
@IntoMap
fun provideSwapFeature(holder: SwapFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(BuyFeatureApi::class)
@IntoMap
fun provideBuyFeature(holder: BuyFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(PushNotificationsFeatureApi::class)
@IntoMap
fun providePushNotificationsFeature(holder: PushNotificationsFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(ProxyFeatureApi::class)
@IntoMap
fun provideProxyFeature(holder: ProxyFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(DeepLinkingFeatureApi::class)
@IntoMap
fun provideDeepLinkingFeatureHolder(holder: DeepLinkingFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(CloudBackupFeatureApi::class)
@IntoMap
fun provideCloudBackupFeatureHolder(holder: CloudBackupFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(SwapCoreApi::class)
@IntoMap
fun provideSwapCoreFeatureHolder(holder: SwapCoreHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(BannersFeatureApi::class)
@IntoMap
fun provideBannersFeatureApi(holder: BannersFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(XcmFeatureApi::class)
@IntoMap
fun provideXcmFeatureHolder(holder: XcmFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(MultisigOperationsFeatureApi::class)
@IntoMap
fun provideMultisigOperationsFeatureHolder(holder: MultisigOperationsFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(AccountMigrationFeatureApi::class)
@IntoMap
fun provideAccountMigrationFeatureHolder(holder: AccountMigrationFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(ChainMigrationFeatureApi::class)
@IntoMap
fun provideChainMigrationFeatureHolder(holder: ChainMigrationFeatureHolder): FeatureApiHolder
@ApplicationScope
@Binds
@ClassKey(GiftFeatureApi::class)
@IntoMap
fun provideGiftFeature(giftFeatureHolder: GiftFeatureHolder): FeatureApiHolder
}
@@ -0,0 +1,18 @@
package io.novafoundation.nova.app.di.deps
import io.novafoundation.nova.common.di.FeatureApiHolder
class FeatureHolderManager(
private val mFeatureHolders: Map<Class<*>, FeatureApiHolder>
) {
fun <T> getFeature(key: Class<*>): T? {
val featureApiHolder = mFeatureHolders[key] ?: throw IllegalStateException()
return featureApiHolder.getFeatureApi<T>()
}
fun releaseFeature(key: Class<*>) {
val featureApiHolder = mFeatureHolders[key] ?: throw IllegalStateException()
featureApiHolder.releaseFeatureApi()
}
}
@@ -0,0 +1,30 @@
package io.novafoundation.nova.app.root.di
import android.content.Context
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet
import io.novafoundation.nova.app.root.presentation.common.FirebaseServiceInitializer
import io.novafoundation.nova.common.di.scope.FeatureScope
import io.novafoundation.nova.common.interfaces.CompoundExternalServiceInitializer
import io.novafoundation.nova.common.interfaces.ExternalServiceInitializer
@Module
class ExternalServiceInitializersModule {
@Provides
@IntoSet
fun provideFirebaseServiceInitializer(
context: Context
): ExternalServiceInitializer {
return FirebaseServiceInitializer(context)
}
@Provides
@FeatureScope
fun provideCompoundExternalServiceInitializer(
initializers: Set<@JvmSuppressWildcards ExternalServiceInitializer>
): ExternalServiceInitializer {
return CompoundExternalServiceInitializer(initializers)
}
}
@@ -0,0 +1,3 @@
package io.novafoundation.nova.app.root.di
interface RootApi
@@ -0,0 +1,103 @@
package io.novafoundation.nova.app.root.di
import dagger.BindsInstance
import dagger.Component
import io.novafoundation.nova.app.root.navigation.holders.RootNavigationHolder
import io.novafoundation.nova.app.root.navigation.holders.SplitScreenNavigationHolder
import io.novafoundation.nova.app.root.navigation.navigators.staking.StakingDashboardNavigator
import io.novafoundation.nova.app.root.presentation.RootRouter
import io.novafoundation.nova.app.root.presentation.di.RootActivityComponent
import io.novafoundation.nova.app.root.presentation.main.di.MainFragmentComponent
import io.novafoundation.nova.app.root.presentation.splitScreen.di.SplitScreenFragmentComponent
import io.novafoundation.nova.common.di.CommonApi
import io.novafoundation.nova.common.di.scope.FeatureScope
import io.novafoundation.nova.common.navigation.DelayedNavigationRouter
import io.novafoundation.nova.core_db.di.DbApi
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
import io.novafoundation.nova.feature_account_migration.di.AccountMigrationFeatureApi
import io.novafoundation.nova.feature_ahm_api.di.ChainMigrationFeatureApi
import io.novafoundation.nova.feature_assets.di.AssetsFeatureApi
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
import io.novafoundation.nova.feature_buy_api.di.BuyFeatureApi
import io.novafoundation.nova.feature_crowdloan_api.di.CrowdloanFeatureApi
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
import io.novafoundation.nova.feature_dapp_api.di.DAppFeatureApi
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureApi
import io.novafoundation.nova.feature_gift_api.di.GiftFeatureApi
import io.novafoundation.nova.feature_governance_api.di.GovernanceFeatureApi
import io.novafoundation.nova.feature_governance_impl.presentation.GovernanceRouter
import io.novafoundation.nova.feature_ledger_api.di.LedgerFeatureApi
import io.novafoundation.nova.feature_multisig_operations.di.MultisigOperationsFeatureApi
import io.novafoundation.nova.feature_push_notifications.di.PushNotificationsFeatureApi
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
import io.novafoundation.nova.feature_staking_impl.presentation.StakingRouter
import io.novafoundation.nova.feature_versions_api.di.VersionsFeatureApi
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
import io.novafoundation.nova.feature_wallet_connect_api.di.WalletConnectFeatureApi
import io.novafoundation.nova.runtime.di.RuntimeApi
@Component(
dependencies = [
RootDependencies::class
],
modules = [
RootFeatureModule::class
]
)
@FeatureScope
interface RootComponent {
fun mainActivityComponentFactory(): RootActivityComponent.Factory
fun splitScreenFragmentComponentFactory(): SplitScreenFragmentComponent.Factory
fun mainFragmentComponentFactory(): MainFragmentComponent.Factory
@Component.Factory
interface Factory {
fun create(
@BindsInstance splitScreenNavigationHolder: SplitScreenNavigationHolder,
@BindsInstance rootNavigationHolder: RootNavigationHolder,
@BindsInstance rootRouter: RootRouter,
@BindsInstance governanceRouter: GovernanceRouter,
@BindsInstance dAppRouter: DAppRouter,
@BindsInstance assetsRouter: AssetsRouter,
@BindsInstance accountRouter: AccountRouter,
@BindsInstance stakingRouter: StakingRouter,
@BindsInstance stakingDashboardNavigator: StakingDashboardNavigator,
@BindsInstance delayedNavigationRouter: DelayedNavigationRouter,
deps: RootDependencies
): RootComponent
}
@Component(
dependencies = [
AccountFeatureApi::class,
WalletFeatureApi::class,
StakingFeatureApi::class,
CrowdloanFeatureApi::class,
AssetsFeatureApi::class,
CurrencyFeatureApi::class,
GovernanceFeatureApi::class,
DAppFeatureApi::class,
DbApi::class,
CommonApi::class,
RuntimeApi::class,
VersionsFeatureApi::class,
WalletConnectFeatureApi::class,
PushNotificationsFeatureApi::class,
DeepLinkingFeatureApi::class,
LedgerFeatureApi::class,
BuyFeatureApi::class,
DeepLinkingFeatureApi::class,
AccountMigrationFeatureApi::class,
MultisigOperationsFeatureApi::class,
ChainMigrationFeatureApi::class,
GiftFeatureApi::class
]
)
interface RootFeatureDependenciesComponent : RootDependencies
}
@@ -0,0 +1,192 @@
package io.novafoundation.nova.app.root.di
import android.content.Context
import coil.ImageLoader
import io.novafoundation.nova.common.data.network.AppLinksProvider
import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin
import io.novafoundation.nova.common.mixin.api.NetworkStateMixin
import io.novafoundation.nova.common.resources.ContextManager
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.sequrity.SafeModeService
import io.novafoundation.nova.common.utils.DialogMessageManager
import io.novafoundation.nova.common.utils.ToastMessageManager
import io.novafoundation.nova.common.utils.coroutines.RootScope
import io.novafoundation.nova.common.utils.network.DeviceNetworkStateObserver
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.common.utils.sequrity.BackgroundAccessObserver
import io.novafoundation.nova.common.utils.systemCall.SystemCallExecutor
import io.novafoundation.nova.common.view.bottomSheet.action.ActionBottomSheetLauncher
import io.novafoundation.nova.common.view.bottomSheet.action.ActionBottomSheetLauncherFactory
import io.novafoundation.nova.core_db.dao.BrowserTabsDao
import io.novafoundation.nova.feature_account_api.data.events.MetaAccountChangesEventBus
import io.novafoundation.nova.feature_account_api.data.externalAccounts.ExternalAccountsSyncService
import io.novafoundation.nova.feature_account_api.data.multisig.MultisigPendingOperationsService
import io.novafoundation.nova.feature_account_api.data.multisig.validation.MultisigExtrinsicValidationRequestBus
import io.novafoundation.nova.feature_account_api.data.proxy.validation.ProxyExtrinsicValidationRequestBus
import io.novafoundation.nova.feature_account_api.di.deeplinks.AccountDeepLinks
import io.novafoundation.nova.feature_account_api.domain.account.common.EncryptionDefaults
import io.novafoundation.nova.feature_account_api.domain.cloudBackup.ApplyLocalSnapshotToCloudBackupUseCase
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
import io.novafoundation.nova.feature_account_migration.di.deeplinks.AccountMigrationDeepLinks
import io.novafoundation.nova.feature_ahm_api.data.repository.ChainMigrationRepository
import io.novafoundation.nova.feature_ahm_api.data.repository.MigrationInfoRepository
import io.novafoundation.nova.feature_ahm_api.di.deeplinks.ChainMigrationDeepLinks
import io.novafoundation.nova.feature_ahm_api.domain.ChainMigrationDetailsSelectToShowUseCase
import io.novafoundation.nova.feature_assets.data.network.BalancesUpdateSystem
import io.novafoundation.nova.feature_assets.di.modules.deeplinks.AssetDeepLinks
import io.novafoundation.nova.feature_buy_api.di.deeplinks.BuyDeepLinks
import io.novafoundation.nova.feature_crowdloan_api.data.repository.CrowdloanRepository
import io.novafoundation.nova.feature_crowdloan_api.domain.contributions.ContributionsInteractor
import io.novafoundation.nova.feature_currency_api.domain.CurrencyInteractor
import io.novafoundation.nova.feature_dapp_api.data.repository.BrowserTabExternalRepository
import io.novafoundation.nova.feature_dapp_api.data.repository.DAppMetadataRepository
import io.novafoundation.nova.feature_dapp_api.di.deeplinks.DAppDeepLinks
import io.novafoundation.nova.feature_deep_linking.presentation.handling.PendingDeepLinkProvider
import io.novafoundation.nova.feature_deep_linking.presentation.handling.branchIo.BranchIoLinkConverter
import io.novafoundation.nova.feature_deep_linking.presentation.handling.common.DeepLinkingPreferences
import io.novafoundation.nova.feature_gift_api.di.GiftDeepLinks
import io.novafoundation.nova.feature_governance_api.data.MutableGovernanceState
import io.novafoundation.nova.feature_governance_api.di.deeplinks.GovernanceDeepLinks
import io.novafoundation.nova.feature_multisig_operations.di.deeplink.MultisigDeepLinks
import io.novafoundation.nova.feature_push_notifications.domain.interactor.PushNotificationsInteractor
import io.novafoundation.nova.feature_push_notifications.domain.interactor.WelcomePushNotificationsInteractor
import io.novafoundation.nova.feature_push_notifications.presentation.multisigsWarning.MultisigPushNotificationsAlertMixinFactory
import io.novafoundation.nova.feature_staking_api.di.deeplinks.StakingDeepLinks
import io.novafoundation.nova.feature_staking_api.domain.api.StakingRepository
import io.novafoundation.nova.feature_versions_api.domain.UpdateNotificationsInteractor
import io.novafoundation.nova.feature_wallet_api.domain.interfaces.WalletRepository
import io.novafoundation.nova.feature_wallet_api.domain.validation.MultisigExtrinsicValidationFactory
import io.novafoundation.nova.feature_wallet_api.domain.validation.ProxyHaveEnoughFeeValidationFactory
import io.novafoundation.nova.feature_wallet_connect_api.di.deeplinks.WalletConnectDeepLinks
import io.novafoundation.nova.feature_wallet_connect_api.domain.sessions.WalletConnectSessionsUseCase
import io.novafoundation.nova.feature_wallet_connect_api.presentation.WalletConnectService
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
import kotlinx.coroutines.flow.MutableStateFlow
interface RootDependencies {
val stakingDeepLinks: StakingDeepLinks
val accountDeepLinks: AccountDeepLinks
val dAppDeepLinks: DAppDeepLinks
val governanceDeepLinks: GovernanceDeepLinks
val buyDeepLinks: BuyDeepLinks
val assetDeepLinks: AssetDeepLinks
val giftDeepLinks: GiftDeepLinks
val chainMigrationDeepLinks: ChainMigrationDeepLinks
val walletConnectDeepLinks: WalletConnectDeepLinks
val systemCallExecutor: SystemCallExecutor
val contextManager: ContextManager
val walletConnectService: WalletConnectService
val imageLoader: ImageLoader
val automaticInteractionGate: AutomaticInteractionGate
val walletConnectSessionsUseCase: WalletConnectSessionsUseCase
val pushNotificationsInteractor: PushNotificationsInteractor
val welcomePushNotificationsInteractor: WelcomePushNotificationsInteractor
val applyLocalSnapshotToCloudBackupUseCase: ApplyLocalSnapshotToCloudBackupUseCase
val actionBottomSheetLauncherFactory: ActionBottomSheetLauncherFactory
val tabsDao: BrowserTabsDao
val balancesUpdateSystem: BalancesUpdateSystem
val actionAwaitableMixinFactory: ActionAwaitableMixin.Factory
val browserTabExternalRepository: BrowserTabExternalRepository
val externalAccountsSyncService: ExternalAccountsSyncService
val multisigPendingOperationsService: MultisigPendingOperationsService
val accountMigrationDeepLinks: AccountMigrationDeepLinks
val multisigDeepLinks: MultisigDeepLinks
val deepLinkingPreferences: DeepLinkingPreferences
val branchIoLinkConverter: BranchIoLinkConverter
val pendingDeepLinkProvider: PendingDeepLinkProvider
val multisigExtrinsicValidationRequestBus: MultisigExtrinsicValidationRequestBus
val multisigExtrinsicValidationFactory: MultisigExtrinsicValidationFactory
val actionBottomSheetLauncher: ActionBottomSheetLauncher
val multisigPushNotificationsAlertMixinFactory: MultisigPushNotificationsAlertMixinFactory
val chainMigrationDetailsSelectToShowUseCase: ChainMigrationDetailsSelectToShowUseCase
val deviceNetworkStateObserver: DeviceNetworkStateObserver
fun updateNotificationsInteractor(): UpdateNotificationsInteractor
fun contributionsInteractor(): ContributionsInteractor
fun crowdloanRepository(): CrowdloanRepository
fun networkStateMixin(): NetworkStateMixin
fun externalRequirementsFlow(): MutableStateFlow<ChainConnection.ExternalRequirement>
fun accountRepository(): AccountRepository
fun walletRepository(): WalletRepository
fun appLinksProvider(): AppLinksProvider
fun resourceManager(): ResourceManager
fun currencyInteractor(): CurrencyInteractor
fun stakingRepository(): StakingRepository
fun chainRegistry(): ChainRegistry
fun backgroundAccessObserver(): BackgroundAccessObserver
fun safeModeService(): SafeModeService
fun rootScope(): RootScope
fun governanceStateUpdater(): MutableGovernanceState
fun dappMetadataRepository(): DAppMetadataRepository
fun encryptionDefaults(): EncryptionDefaults
fun proxyExtrinsicValidationRequestBus(): ProxyExtrinsicValidationRequestBus
fun metaAccountChangesRequestBus(): MetaAccountChangesEventBus
fun proxyHaveEnoughFeeValidationFactory(): ProxyHaveEnoughFeeValidationFactory
fun context(): Context
fun toastMessageManager(): ToastMessageManager
fun dialogMessageManager(): DialogMessageManager
fun chainMigrationRepository(): ChainMigrationRepository
fun migrationInfoRepository(): MigrationInfoRepository
}
@@ -0,0 +1,94 @@
package io.novafoundation.nova.app.root.di
import io.novafoundation.nova.app.root.navigation.holders.RootNavigationHolder
import io.novafoundation.nova.app.root.navigation.holders.SplitScreenNavigationHolder
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.app.root.navigation.navigators.staking.StakingDashboardNavigator
import io.novafoundation.nova.common.di.FeatureApiHolder
import io.novafoundation.nova.common.di.FeatureContainer
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.common.navigation.DelayedNavigationRouter
import io.novafoundation.nova.core_db.di.DbApi
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
import io.novafoundation.nova.feature_account_migration.di.AccountMigrationFeatureApi
import io.novafoundation.nova.feature_ahm_api.di.ChainMigrationFeatureApi
import io.novafoundation.nova.feature_assets.di.AssetsFeatureApi
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
import io.novafoundation.nova.feature_buy_api.di.BuyFeatureApi
import io.novafoundation.nova.feature_crowdloan_api.di.CrowdloanFeatureApi
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
import io.novafoundation.nova.feature_dapp_api.di.DAppFeatureApi
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureApi
import io.novafoundation.nova.feature_gift_api.di.GiftFeatureApi
import io.novafoundation.nova.feature_governance_api.di.GovernanceFeatureApi
import io.novafoundation.nova.feature_governance_impl.presentation.GovernanceRouter
import io.novafoundation.nova.feature_ledger_api.di.LedgerFeatureApi
import io.novafoundation.nova.feature_multisig_operations.di.MultisigOperationsFeatureApi
import io.novafoundation.nova.feature_push_notifications.di.PushNotificationsFeatureApi
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
import io.novafoundation.nova.feature_staking_impl.presentation.StakingRouter
import io.novafoundation.nova.feature_versions_api.di.VersionsFeatureApi
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
import io.novafoundation.nova.feature_wallet_connect_api.di.WalletConnectFeatureApi
import io.novafoundation.nova.runtime.di.RuntimeApi
import javax.inject.Inject
@ApplicationScope
class RootFeatureHolder @Inject constructor(
private val splitScreenNavigationHolder: SplitScreenNavigationHolder,
private val rootNavigationHolder: RootNavigationHolder,
private val navigator: Navigator,
private val governanceRouter: GovernanceRouter,
private val dAppRouter: DAppRouter,
private val accountRouter: AccountRouter,
private val assetsRouter: AssetsRouter,
private val stakingRouter: StakingRouter,
private val stakingDashboardNavigator: StakingDashboardNavigator,
private val delayedNavRouter: DelayedNavigationRouter,
featureContainer: FeatureContainer
) : FeatureApiHolder(featureContainer) {
override fun initializeDependencies(): Any {
val rootFeatureDependencies = DaggerRootComponent_RootFeatureDependenciesComponent.builder()
.commonApi(commonApi())
.dbApi(getFeature(DbApi::class.java))
.accountFeatureApi(getFeature(AccountFeatureApi::class.java))
.walletFeatureApi(getFeature(WalletFeatureApi::class.java))
.stakingFeatureApi(getFeature(StakingFeatureApi::class.java))
.assetsFeatureApi(getFeature(AssetsFeatureApi::class.java))
.currencyFeatureApi(getFeature(CurrencyFeatureApi::class.java))
.crowdloanFeatureApi(getFeature(CrowdloanFeatureApi::class.java))
.governanceFeatureApi(getFeature(GovernanceFeatureApi::class.java))
.dAppFeatureApi(getFeature(DAppFeatureApi::class.java))
.runtimeApi(getFeature(RuntimeApi::class.java))
.versionsFeatureApi(getFeature(VersionsFeatureApi::class.java))
.walletConnectFeatureApi(getFeature(WalletConnectFeatureApi::class.java))
.pushNotificationsFeatureApi(getFeature(PushNotificationsFeatureApi::class.java))
.deepLinkingFeatureApi(getFeature(DeepLinkingFeatureApi::class.java))
.ledgerFeatureApi(getFeature(LedgerFeatureApi::class.java))
.buyFeatureApi(getFeature(BuyFeatureApi::class.java))
.deepLinkingFeatureApi(getFeature(DeepLinkingFeatureApi::class.java))
.accountMigrationFeatureApi(getFeature(AccountMigrationFeatureApi::class.java))
.multisigOperationsFeatureApi(getFeature(MultisigOperationsFeatureApi::class.java))
.chainMigrationFeatureApi(getFeature(ChainMigrationFeatureApi::class.java))
.giftFeatureApi(getFeature(GiftFeatureApi::class.java))
.build()
return DaggerRootComponent.factory()
.create(
splitScreenNavigationHolder,
rootNavigationHolder,
navigator,
governanceRouter,
dAppRouter,
assetsRouter,
accountRouter,
stakingRouter,
stakingDashboardNavigator,
delayedNavRouter,
rootFeatureDependencies
)
}
}
@@ -0,0 +1,47 @@
package io.novafoundation.nova.app.root.di
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.di.busHandler.RequestBusHandlerModule
import io.novafoundation.nova.app.root.di.deeplink.DeepLinksModule
import io.novafoundation.nova.app.root.domain.RootInteractor
import io.novafoundation.nova.common.di.scope.FeatureScope
import io.novafoundation.nova.feature_account_api.data.externalAccounts.ExternalAccountsSyncService
import io.novafoundation.nova.feature_account_api.data.multisig.MultisigPendingOperationsService
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
import io.novafoundation.nova.feature_ahm_api.data.repository.ChainMigrationRepository
import io.novafoundation.nova.feature_ahm_api.data.repository.MigrationInfoRepository
import io.novafoundation.nova.feature_assets.data.network.BalancesUpdateSystem
import io.novafoundation.nova.feature_wallet_api.domain.interfaces.WalletRepository
@Module(
includes = [
RequestBusHandlerModule::class,
ExternalServiceInitializersModule::class,
DeepLinksModule::class
]
)
class RootFeatureModule {
@Provides
@FeatureScope
fun provideRootInteractor(
walletRepository: WalletRepository,
accountRepository: AccountRepository,
balancesUpdateSystem: BalancesUpdateSystem,
multisigPendingOperationsService: MultisigPendingOperationsService,
externalAccountsSyncService: ExternalAccountsSyncService,
chainMigrationRepository: ChainMigrationRepository,
migrationInfoRepository: MigrationInfoRepository
): RootInteractor {
return RootInteractor(
updateSystem = balancesUpdateSystem,
walletRepository = walletRepository,
accountRepository = accountRepository,
multisigPendingOperationsService = multisigPendingOperationsService,
externalAccountsSyncService = externalAccountsSyncService,
chainMigrationRepository = chainMigrationRepository,
migrationInfoRepository = migrationInfoRepository
)
}
}
@@ -0,0 +1,98 @@
package io.novafoundation.nova.app.root.di.busHandler
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet
import io.novafoundation.nova.app.root.presentation.RootRouter
import io.novafoundation.nova.app.root.presentation.requestBusHandler.CloudBackupSyncRequestBusHandler
import io.novafoundation.nova.app.root.presentation.requestBusHandler.CompoundRequestBusHandler
import io.novafoundation.nova.app.root.presentation.requestBusHandler.MultisigExtrinsicValidationRequestBusHandler
import io.novafoundation.nova.app.root.presentation.requestBusHandler.ProxyExtrinsicValidationRequestBusHandler
import io.novafoundation.nova.app.root.presentation.requestBusHandler.PushSettingsSyncRequestBusHandler
import io.novafoundation.nova.app.root.presentation.requestBusHandler.RequestBusHandler
import io.novafoundation.nova.common.di.scope.FeatureScope
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.common.view.bottomSheet.action.ActionBottomSheetLauncher
import io.novafoundation.nova.feature_account_api.data.proxy.validation.ProxyExtrinsicValidationRequestBus
import io.novafoundation.nova.feature_account_api.data.events.MetaAccountChangesEventBus
import io.novafoundation.nova.feature_account_api.data.multisig.validation.MultisigExtrinsicValidationRequestBus
import io.novafoundation.nova.feature_account_api.domain.cloudBackup.ApplyLocalSnapshotToCloudBackupUseCase
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
import io.novafoundation.nova.feature_push_notifications.domain.interactor.PushNotificationsInteractor
import io.novafoundation.nova.feature_wallet_api.domain.validation.MultisigExtrinsicValidationFactory
import io.novafoundation.nova.feature_wallet_api.domain.validation.ProxyHaveEnoughFeeValidationFactory
@Module
class RequestBusHandlerModule {
@Provides
@FeatureScope
@IntoSet
fun providePushSettingsSyncRequestBusHandler(
metaAccountChangesEventBus: MetaAccountChangesEventBus,
pushNotificationsInteractor: PushNotificationsInteractor
): RequestBusHandler {
return PushSettingsSyncRequestBusHandler(
metaAccountChangesEventBus,
pushNotificationsInteractor
)
}
@Provides
@FeatureScope
@IntoSet
fun provideProxyExtrinsicValidationRequestBusHandler(
proxyProxyExtrinsicValidationRequestBus: ProxyExtrinsicValidationRequestBus,
proxyHaveEnoughFeeValidationFactory: ProxyHaveEnoughFeeValidationFactory
): RequestBusHandler {
return ProxyExtrinsicValidationRequestBusHandler(
proxyProxyExtrinsicValidationRequestBus,
proxyHaveEnoughFeeValidationFactory
)
}
@Provides
@FeatureScope
@IntoSet
fun provideMultisigExtrinsicValidationRequestBusHandler(
multisigExtrinsicValidationRequestBus: MultisigExtrinsicValidationRequestBus,
multisigExtrinsicValidationFactory: MultisigExtrinsicValidationFactory
): RequestBusHandler {
return MultisigExtrinsicValidationRequestBusHandler(
multisigExtrinsicValidationRequestBus,
multisigExtrinsicValidationFactory
)
}
@Provides
@FeatureScope
@IntoSet
fun provideCloudBackupSyncRequestBusHandler(
rootRouter: RootRouter,
resourceManager: ResourceManager,
metaAccountChangesEventBus: MetaAccountChangesEventBus,
applyLocalSnapshotToCloudBackupUseCase: ApplyLocalSnapshotToCloudBackupUseCase,
accountRepository: AccountRepository,
actionBottomSheetLauncher: ActionBottomSheetLauncher,
automaticInteractionGate: AutomaticInteractionGate
): RequestBusHandler {
return CloudBackupSyncRequestBusHandler(
rootRouter,
resourceManager,
metaAccountChangesEventBus,
applyLocalSnapshotToCloudBackupUseCase,
accountRepository,
actionBottomSheetLauncher,
automaticInteractionGate
)
}
@Provides
@FeatureScope
fun provideCompoundRequestBusHandler(
handlers: Set<@JvmSuppressWildcards RequestBusHandler>
): CompoundRequestBusHandler {
return CompoundRequestBusHandler(handlers)
}
}
@@ -0,0 +1,75 @@
package io.novafoundation.nova.app.root.di.deeplink
import dagger.Module
import dagger.Provides
import io.novafoundation.nova.common.di.scope.FeatureScope
import io.novafoundation.nova.feature_account_api.di.deeplinks.AccountDeepLinks
import io.novafoundation.nova.feature_account_migration.di.deeplinks.AccountMigrationDeepLinks
import io.novafoundation.nova.feature_ahm_api.di.deeplinks.ChainMigrationDeepLinks
import io.novafoundation.nova.feature_assets.di.modules.deeplinks.AssetDeepLinks
import io.novafoundation.nova.feature_buy_api.di.deeplinks.BuyDeepLinks
import io.novafoundation.nova.feature_dapp_api.di.deeplinks.DAppDeepLinks
import io.novafoundation.nova.feature_deep_linking.presentation.handling.DeepLinkHandler
import io.novafoundation.nova.feature_deep_linking.presentation.handling.PendingDeepLinkProvider
import io.novafoundation.nova.feature_deep_linking.presentation.handling.RootDeepLinkHandler
import io.novafoundation.nova.feature_deep_linking.presentation.handling.branchIo.BranchIOLinkHandler
import io.novafoundation.nova.feature_deep_linking.presentation.handling.branchIo.BranchIoLinkConverter
import io.novafoundation.nova.feature_gift_api.di.GiftDeepLinks
import io.novafoundation.nova.feature_governance_api.di.deeplinks.GovernanceDeepLinks
import io.novafoundation.nova.feature_multisig_operations.di.deeplink.MultisigDeepLinks
import io.novafoundation.nova.feature_staking_api.di.deeplinks.StakingDeepLinks
import io.novafoundation.nova.feature_wallet_connect_api.di.deeplinks.WalletConnectDeepLinks
@Module
class DeepLinksModule {
@Provides
@FeatureScope
fun provideDeepLinkHandlers(
stakingDeepLinks: StakingDeepLinks,
accountDeepLinks: AccountDeepLinks,
dAppDeepLinks: DAppDeepLinks,
governanceDeepLinks: GovernanceDeepLinks,
buyDeepLinks: BuyDeepLinks,
assetDeepLinks: AssetDeepLinks,
walletConnectDeepLinks: WalletConnectDeepLinks,
accountMigrationDeepLinks: AccountMigrationDeepLinks,
multisigDeepLinks: MultisigDeepLinks,
chainMigrationDeepLinks: ChainMigrationDeepLinks,
giftDeepLinks: GiftDeepLinks
): List<@JvmWildcard DeepLinkHandler> {
return buildList {
addAll(stakingDeepLinks.deepLinkHandlers)
addAll(accountDeepLinks.deepLinkHandlers)
addAll(dAppDeepLinks.deepLinkHandlers)
addAll(governanceDeepLinks.deepLinkHandlers)
addAll(buyDeepLinks.deepLinkHandlers)
addAll(assetDeepLinks.deepLinkHandlers)
addAll(walletConnectDeepLinks.deepLinkHandlers)
addAll(accountMigrationDeepLinks.deepLinkHandlers)
addAll(multisigDeepLinks.deepLinkHandlers)
addAll(chainMigrationDeepLinks.deepLinkHandlers)
addAll(giftDeepLinks.deepLinkHandlers)
}
}
@Provides
@FeatureScope
fun provideRootDeepLinkHandler(
pendingDeepLinkProvider: PendingDeepLinkProvider,
nestedHandlers: @JvmWildcard List<DeepLinkHandler>
): RootDeepLinkHandler {
return RootDeepLinkHandler(
pendingDeepLinkProvider,
nestedHandlers
)
}
@Provides
@FeatureScope
fun provideBranchIOLinkHandler(
branchIoLinkConverter: BranchIoLinkConverter
): BranchIOLinkHandler {
return BranchIOLinkHandler(branchIoLinkConverter)
}
}
@@ -0,0 +1,56 @@
package io.novafoundation.nova.app.root.domain
import io.novafoundation.nova.common.data.memory.ComputationalScope
import io.novafoundation.nova.core.updater.Updater
import io.novafoundation.nova.feature_account_api.data.externalAccounts.ExternalAccountsSyncService
import io.novafoundation.nova.feature_account_api.data.multisig.MultisigPendingOperationsService
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
import io.novafoundation.nova.feature_ahm_api.data.repository.ChainMigrationRepository
import io.novafoundation.nova.feature_ahm_api.data.repository.MigrationInfoRepository
import io.novafoundation.nova.feature_assets.data.network.BalancesUpdateSystem
import io.novafoundation.nova.feature_wallet_api.domain.interfaces.WalletRepository
import kotlinx.coroutines.flow.Flow
class RootInteractor(
private val updateSystem: BalancesUpdateSystem,
private val walletRepository: WalletRepository,
private val accountRepository: AccountRepository,
private val multisigPendingOperationsService: MultisigPendingOperationsService,
private val externalAccountsSyncService: ExternalAccountsSyncService,
private val chainMigrationRepository: ChainMigrationRepository,
private val migrationInfoRepository: MigrationInfoRepository
) {
fun runBalancesUpdate(): Flow<Updater.SideEffect> = updateSystem.start()
suspend fun updatePhishingAddresses() {
runCatching {
walletRepository.updatePhishingAddresses()
}
}
suspend fun isAccountSelected(): Boolean {
return accountRepository.isAccountSelected()
}
suspend fun isPinCodeSet(): Boolean {
return accountRepository.isCodeSet()
}
fun syncExternalAccounts() {
externalAccountsSyncService.sync()
}
context(ComputationalScope)
fun syncPendingMultisigOperations(): Flow<Unit> {
return multisigPendingOperationsService.performMultisigOperationsSync()
}
suspend fun cacheBalancesForChainMigrationDetection() {
chainMigrationRepository.cacheBalancesForChainMigrationDetection()
}
suspend fun loadMigrationDetailsConfigs() {
migrationInfoRepository.loadConfigs()
}
}
@@ -0,0 +1,23 @@
package io.novafoundation.nova.app.root.domain
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
import io.novafoundation.nova.feature_dapp_api.data.model.SimpleTabModel
import io.novafoundation.nova.feature_dapp_api.data.repository.BrowserTabExternalRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
class SplitScreenInteractor(
private val repository: BrowserTabExternalRepository,
private val accountRepository: AccountRepository
) {
fun observeTabNamesById(): Flow<List<SimpleTabModel>> {
return accountRepository.selectedMetaAccountFlow()
.flatMapLatest { repository.observeTabsWithNames(it.id) }
}
suspend fun removeAllTabs() {
val metaAccount = accountRepository.getSelectedMetaAccount()
repository.removeTabsForMetaAccount(metaAccount.id)
}
}
@@ -0,0 +1,39 @@
package io.novafoundation.nova.app.root.navigation
import android.annotation.SuppressLint
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import androidx.navigation.NavGraph
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.delayedNavigation.NavComponentDelayedNavigation
import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator
import io.novafoundation.nova.app.root.presentation.splitScreen.SplitScreenFragment
import io.novafoundation.nova.app.root.presentation.splitScreen.SplitScreenPayload
@SuppressLint("RestrictedApi")
fun NavController.getBackStackEntryBefore(@IdRes id: Int): NavBackStackEntry {
val initial = getBackStackEntry(id)
val backStack = backStack.toList()
val initialIndex = backStack.indexOf(initial)
var previousIndex = initialIndex - 1
// ignore nav graphs
while (previousIndex > 0 && backStack[previousIndex].destination is NavGraph) {
previousIndex--
}
return backStack[previousIndex]
}
fun BaseNavigator.openSplitScreenWithInstantAction(actionId: Int, nestedActionExtras: Bundle? = null) {
val delayedNavigation = NavComponentDelayedNavigation(actionId, nestedActionExtras)
val splitScreenPayload = SplitScreenPayload.InstantNavigationOnAttach(delayedNavigation)
navigationBuilder().action(R.id.action_open_split_screen)
.setArgs(SplitScreenFragment.createPayload(splitScreenPayload))
.navigateInRoot()
}
@@ -0,0 +1,46 @@
package io.novafoundation.nova.app.root.navigation
import io.novafoundation.nova.common.navigation.InterScreenCommunicator
import io.novafoundation.nova.common.utils.singleReplaySharedFlow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
abstract class FlowInterScreenCommunicator<I : Any, O : Any> :
InterScreenCommunicator<I, O>,
CoroutineScope by CoroutineScope(Dispatchers.Main) {
private var response: O? = null
override val responseFlow = singleReplaySharedFlow<O>()
private var _request: I? = null
override val latestResponse: O?
get() = response
override val lastState: O?
get() = latestResponse
override val lastInput: I?
get() = _request
abstract fun dispatchRequest(request: I)
@OptIn(ExperimentalCoroutinesApi::class)
override fun openRequest(request: I) {
_request = request
response = null
responseFlow.resetReplayCache()
dispatchRequest(request)
}
override fun respond(response: O) {
launch {
this@FlowInterScreenCommunicator.response = response
responseFlow.emit(response)
}
}
}
@@ -0,0 +1,78 @@
package io.novafoundation.nova.app.root.navigation
import android.os.Parcelable
import androidx.annotation.CallSuper
import androidx.lifecycle.asFlow
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.builder.NavigationBuilderRegistry
import io.novafoundation.nova.app.root.navigation.navigators.navigationBuilder
import io.novafoundation.nova.common.navigation.InterScreenCommunicator
import kotlinx.coroutines.flow.Flow
import java.util.UUID
abstract class NavStackInterScreenCommunicator<I : Parcelable, O : Parcelable>(
private val navigationHoldersRegistry: NavigationHoldersRegistry
) : InterScreenCommunicator<I, O> {
private val responseKey = UUID.randomUUID().toString()
private val requestKey = UUID.randomUUID().toString()
protected val navController: NavController
get() = navigationHoldersRegistry.firstAttachedNavController!!
// from requester - retrieve from current entry
override val latestResponse: O?
get() = navController.currentBackStackEntry!!.savedStateHandle
.get(responseKey)
// from responder - retrieve from previous (requester) entry
override val lastState: O?
get() = navController.previousBackStackEntry!!.savedStateHandle
.get(responseKey)
override val responseFlow: Flow<O>
get() = createResponseFlow()
// from responder - retrieve from previous (requester) entry
override val lastInput: I?
get() = navController.previousBackStackEntry!!.savedStateHandle
.get(requestKey)
@CallSuper
override fun openRequest(request: I) {
saveRequest(request)
}
fun clearedResponseFlow(): Flow<O> {
navController.currentBackStackEntry!!.savedStateHandle.apply {
remove<O>(requestKey)
remove<O>(responseKey)
}
return createResponseFlow()
}
override fun respond(response: O) {
// previousBackStackEntry since we want to report to previous screen
saveResultTo(navController.previousBackStackEntry!!, response)
}
protected fun saveResultTo(backStackEntry: NavBackStackEntry, response: O) {
backStackEntry.savedStateHandle.set(responseKey, response)
}
private fun saveRequest(request: I) {
navController.currentBackStackEntry!!.savedStateHandle.set(requestKey, request)
}
private fun createResponseFlow(): Flow<O> {
return navController.currentBackStackEntry!!.savedStateHandle
.getLiveData<O>(responseKey)
.asFlow()
}
protected fun navigationBuilder(): NavigationBuilderRegistry {
return navigationHoldersRegistry.navigationBuilder()
}
}
@@ -0,0 +1,7 @@
package io.novafoundation.nova.app.root.navigation.delayedNavigation
import io.novafoundation.nova.common.navigation.DelayedNavigation
import kotlinx.parcelize.Parcelize
@Parcelize
object BackDelayedNavigation : DelayedNavigation
@@ -0,0 +1,8 @@
package io.novafoundation.nova.app.root.navigation.delayedNavigation
import android.os.Bundle
import io.novafoundation.nova.common.navigation.DelayedNavigation
import kotlinx.parcelize.Parcelize
@Parcelize
class NavComponentDelayedNavigation(val globalActionId: Int, val extras: Bundle? = null) : DelayedNavigation
@@ -0,0 +1,46 @@
package io.novafoundation.nova.app.root.navigation.holders
import androidx.navigation.NavController
import io.novafoundation.nova.common.resources.ContextManager
abstract class NavigationHolder(val contextManager: ContextManager) {
var navController: NavController? = null
private set
fun isControllerAttached(): Boolean {
return navController != null
}
fun attach(navController: NavController) {
this.navController = navController
}
/**
* Detaches the current navController only if it matches the one provided.
* This check ensures that if a new screen with a navController is attached,
* it doesn't lose its navController when the previous screen calls detach.
* By verifying equality, we prevent unintended detachment.
*/
fun detachNavController(navController: NavController) {
if (this.navController == navController) {
this.navController = null
}
}
fun detach() {
navController = null
}
fun finishApp() {
contextManager.getActivity()?.finish()
}
fun executeBack() {
val popped = navController!!.popBackStack()
if (!popped) {
contextManager.getActivity()!!.finish()
}
}
}
@@ -0,0 +1,5 @@
package io.novafoundation.nova.app.root.navigation.holders
import io.novafoundation.nova.common.resources.ContextManager
class RootNavigationHolder(contextManager: ContextManager) : NavigationHolder(contextManager)
@@ -0,0 +1,5 @@
package io.novafoundation.nova.app.root.navigation.holders
import io.novafoundation.nova.common.resources.ContextManager
class SplitScreenNavigationHolder(contextManager: ContextManager) : NavigationHolder(contextManager)
@@ -0,0 +1,8 @@
package io.novafoundation.nova.app.root.navigation.navigationFragment
import io.novafoundation.nova.app.R
class MainNavHostFragment : NovaNavHostFragment() {
override val containerId: Int = R.id.mainNavHost
}
@@ -0,0 +1,20 @@
package io.novafoundation.nova.app.root.navigation.navigationFragment
import android.annotation.SuppressLint
import androidx.navigation.NavController
import androidx.navigation.fragment.DialogFragmentNavigator
import androidx.navigation.fragment.NavHostFragment
import io.novafoundation.nova.app.root.navigation.navigators.AddFragmentNavigator
abstract class NovaNavHostFragment : NavHostFragment() {
abstract val containerId: Int
@SuppressLint("MissingSuperCall")
override fun onCreateNavController(navController: NavController) {
navController.navigatorProvider.addNavigator(DialogFragmentNavigator(requireContext(), childFragmentManager))
val addFragmentNavigator = AddFragmentNavigator(requireContext(), childFragmentManager, containerId)
navController.navigatorProvider.addNavigator(addFragmentNavigator)
}
}
@@ -0,0 +1,8 @@
package io.novafoundation.nova.app.root.navigation.navigationFragment
import io.novafoundation.nova.app.R
class RootNavHostFragment : NovaNavHostFragment() {
override val containerId: Int = R.id.rootNavHost
}
@@ -0,0 +1,352 @@
package io.novafoundation.nova.app.root.navigation.navigators;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.CallSuper;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.navigation.NavDestination;
import androidx.navigation.NavOptions;
import androidx.navigation.Navigator;
import androidx.navigation.NavigatorProvider;
import androidx.navigation.fragment.FragmentNavigator;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Map;
import io.novafoundation.nova.app.R;
/**
* This is an improved version (aka copy-paste with fixes) of
* {@link androidx.navigation.fragment.FragmentNavigator} which allows not only to replace old
* fragment with new one, but also add new and hide old one.
* The difference with original implementation from google library is in navigate() method (
* <code>if (destination.shouldUseAdd) ...</code> )and in modified Destination subclass
* which includes <code>shouldUseAdd</code> flag
*/
@Navigator.Name("fragment")
public class AddFragmentNavigator extends Navigator<AddFragmentNavigator.Destination> {
private static final String TAG = "FragmentNavigator";
private static final String KEY_BACK_STACK_IDS = "androidx-nav-fragment:navigator:backStackIds";
private final Context mContext;
private final FragmentManager mFragmentManager;
private final int mContainerId;
private ArrayDeque<Integer> mBackStack = new ArrayDeque<>();
public AddFragmentNavigator(@NonNull Context context, @NonNull FragmentManager manager,
int containerId) {
mContext = context;
mFragmentManager = manager;
mContainerId = containerId;
}
/**
* {@inheritDoc}
* <p>
* This method must call
* {@link FragmentTransaction#setPrimaryNavigationFragment(Fragment)}
* if the pop succeeded so that the newly visible Fragment can be retrieved with
* {@link FragmentManager#getPrimaryNavigationFragment()}.
* <p>
* Note that the default implementation pops the Fragment
* asynchronously, so the newly visible Fragment from the back stack
* is not instantly available after this call completes.
*/
@Override
public boolean popBackStack() {
if (mBackStack.isEmpty()) {
return false;
}
if (mFragmentManager.isStateSaved()) {
Log.i(TAG, "Ignoring popBackStack() call: FragmentManager has already"
+ " saved its state");
return false;
}
mFragmentManager.popBackStack(
generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
mBackStack.removeLast();
return true;
}
@NonNull
@Override
public AddFragmentNavigator.Destination createDestination() {
return new AddFragmentNavigator.Destination(this);
}
/**
* Instantiates the Fragment via the FragmentManager's
* {@link androidx.fragment.app.FragmentFactory}.
* Note that this method is <strong>not</strong> responsible for calling
* {@link Fragment#setArguments(Bundle)} on the returned Fragment instance.
*
* @param context Context providing the correct {@link ClassLoader}
* @param fragmentManager FragmentManager the Fragment will be added to
* @param className The Fragment to instantiate
* @param args The Fragment's arguments, if any
* @return A new fragment instance.
* @deprecated Set a custom {@link androidx.fragment.app.FragmentFactory} via
* {@link FragmentManager#setFragmentFactory(androidx.fragment.app.FragmentFactory)} to control
* instantiation of Fragments.
*/
@SuppressWarnings("DeprecatedIsStillUsed") // needed to maintain forward compatibility
@Deprecated
@NonNull
public Fragment instantiateFragment(@NonNull Context context,
@NonNull FragmentManager fragmentManager,
@NonNull String className, @SuppressWarnings("unused") @Nullable Bundle args) {
return fragmentManager.getFragmentFactory().instantiate(
context.getClassLoader(), className);
}
/**
* {@inheritDoc}
* <p>
* This method should always call
* {@link FragmentTransaction#setPrimaryNavigationFragment(Fragment)}
* so that the Fragment associated with the new destination can be retrieved with
* {@link FragmentManager#getPrimaryNavigationFragment()}.
* <p>
* Note that the default implementation commits the new Fragment
* asynchronously, so the new Fragment is not instantly available
* after this call completes.
*/
@SuppressWarnings("deprecation") /* Using instantiateFragment for forward compatibility */
@Nullable
@Override
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
if (mFragmentManager.isStateSaved()) {
Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
+ " saved its state");
return null;
}
String className = destination.getClassName();
if (className.charAt(0) == '.') {
className = mContext.getPackageName() + className;
}
final Fragment frag = instantiateFragment(mContext, mFragmentManager,
className, args);
frag.setArguments(args);
final FragmentTransaction ft = mFragmentManager.beginTransaction();
int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
enterAnim = enterAnim != -1 ? enterAnim : 0;
exitAnim = exitAnim != -1 ? exitAnim : 0;
popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
}
if (destination.shouldUseAdd) {
List<Fragment> fragments = mFragmentManager.getFragments();
for (Fragment fragment : fragments) {
if (fragment.isHidden()) continue;
ft.hide(fragment);
}
ft.add(mContainerId, frag);
} else {
ft.replace(mContainerId, frag);
}
ft.setPrimaryNavigationFragment(frag);
final @IdRes int destId = destination.getId();
final boolean initialNavigation = mBackStack.isEmpty();
// TODO Build first class singleTop behavior for fragments
final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& mBackStack.peekLast() == destId;
boolean isAdded;
if (initialNavigation) {
isAdded = true;
} else if (isSingleTopReplacement) {
// Single Top means we only want one instance on the back stack
if (mBackStack.size() > 1) {
// If the Fragment to be replaced is on the FragmentManager's
// back stack, a simple replace() isn't enough so we
// remove it from the back stack and put our replacement
// on the back stack in its place
mFragmentManager.popBackStack(
generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
}
isAdded = false;
} else {
ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
isAdded = true;
}
if (navigatorExtras instanceof FragmentNavigator.Extras) {
FragmentNavigator.Extras extras = (FragmentNavigator.Extras) navigatorExtras;
for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
}
}
ft.setReorderingAllowed(true);
ft.commit();
// The commit succeeded, update our view of the world
if (isAdded) {
mBackStack.add(destId);
return destination;
} else {
return null;
}
}
@Override
@Nullable
public Bundle onSaveState() {
Bundle b = new Bundle();
int[] backStack = new int[mBackStack.size()];
int index = 0;
for (Integer id : mBackStack) {
backStack[index++] = id;
}
b.putIntArray(KEY_BACK_STACK_IDS, backStack);
return b;
}
@Override
public void onRestoreState(@Nullable Bundle savedState) {
if (savedState != null) {
int[] backStack = savedState.getIntArray(KEY_BACK_STACK_IDS);
if (backStack != null) {
mBackStack.clear();
for (int destId : backStack) {
mBackStack.add(destId);
}
}
}
}
@NonNull
private String generateBackStackName(int backStackIndex, int destId) {
return backStackIndex + "-" + destId;
}
@NavDestination.ClassType(Fragment.class)
public static class Destination extends NavDestination {
private String mClassName;
private boolean shouldUseAdd;
/**
* Construct a new fragment destination. This destination is not valid until you set the
* Fragment via {@link #setClassName(String)}.
*
* @param navigatorProvider The {@link androidx.navigation.NavController} which this destination
* will be associated with.
*/
public Destination(@NonNull NavigatorProvider navigatorProvider) {
this(navigatorProvider.getNavigator(AddFragmentNavigator.class));
}
/**
* Construct a new fragment destination. This destination is not valid until you set the
* Fragment via {@link #setClassName(String)}.
*
* @param fragmentNavigator The {@link FragmentNavigator} which this destination
* will be associated with. Generally retrieved via a
* {@link androidx.navigation.NavController}'s
* {@link NavigatorProvider#getNavigator(Class)} method.
*/
public Destination(@NonNull Navigator<? extends Destination> fragmentNavigator) {
super(fragmentNavigator);
}
@CallSuper
@Override
public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs) {
super.onInflate(context, attrs);
TypedArray fragmentNavigatorArray = context.getResources().obtainAttributes(attrs,
R.styleable.FragmentNavigator);
String className = fragmentNavigatorArray.getString(R.styleable.FragmentNavigator_android_name);
if (className != null) {
setClassName(className);
}
fragmentNavigatorArray.recycle();
TypedArray novaNavigatorArray = context.getResources().obtainAttributes(attrs,
R.styleable.AddFragmentNavigator);
Boolean useAdd = novaNavigatorArray.getBoolean(R.styleable.AddFragmentNavigator_useAdd, false);
setUseAdd(useAdd);
novaNavigatorArray.recycle();
}
@NonNull
public final Destination setUseAdd(@NonNull Boolean useAdd) {
this.shouldUseAdd = useAdd;
return this;
}
/**
* Gets the Fragment's class name associated with this destination
*
* @throws IllegalStateException when no Fragment class was set.
*/
@NonNull
public final boolean shouldUseAdd() {
return shouldUseAdd;
}
/**
* Set the Fragment class name associated with this destination
*
* @param className The class name of the Fragment to show when you navigate to this
* destination
* @return this {@link androidx.navigation.fragment.FragmentNavigator.Destination}
*/
@NonNull
public final Destination setClassName(@NonNull String className) {
mClassName = className;
return this;
}
/**
* Gets the Fragment's class name associated with this destination
*
* @throws IllegalStateException when no Fragment class was set.
*/
@NonNull
public final String getClassName() {
if (mClassName == null) {
throw new IllegalStateException("Fragment class was not set");
}
return mClassName;
}
@NonNull
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
sb.append(" class=");
if (mClassName == null) {
sb.append("null");
} else {
sb.append(mClassName);
}
return sb.toString();
}
}
}
@@ -0,0 +1,33 @@
package io.novafoundation.nova.app.root.navigation.navigators
import io.novafoundation.nova.app.root.navigation.navigators.builder.NavigationBuilderRegistry
import io.novafoundation.nova.common.navigation.ReturnableRouter
abstract class BaseNavigator(
private val navigationHoldersRegistry: NavigationHoldersRegistry
) : ReturnableRouter {
val currentBackStackEntry
get() = navigationHoldersRegistry.firstAttachedNavController
?.currentBackStackEntry
val previousBackStackEntry
get() = navigationHoldersRegistry.firstAttachedNavController
?.previousBackStackEntry
val currentDestination
get() = navigationHoldersRegistry.firstAttachedNavController
?.currentDestination
override fun back() {
navigationHoldersRegistry.firstAttachedHolder.executeBack()
}
fun finishApp() {
navigationHoldersRegistry.firstAttachedHolder.finishApp()
}
fun navigationBuilder(): NavigationBuilderRegistry {
return navigationHoldersRegistry.navigationBuilder()
}
}
@@ -0,0 +1,25 @@
package io.novafoundation.nova.app.root.navigation.navigators
import androidx.navigation.NavController
import io.novafoundation.nova.app.root.navigation.holders.NavigationHolder
import io.novafoundation.nova.app.root.navigation.holders.RootNavigationHolder
import io.novafoundation.nova.app.root.navigation.holders.SplitScreenNavigationHolder
import io.novafoundation.nova.app.root.navigation.navigators.builder.NavigationBuilderRegistry
class NavigationHoldersRegistry(
val splitScreenNavigationHolder: SplitScreenNavigationHolder,
val rootNavigationHolder: RootNavigationHolder
) {
private val holders = listOf(splitScreenNavigationHolder, rootNavigationHolder)
val firstAttachedHolder: NavigationHolder
get() = holders.first { it.isControllerAttached() }
val firstAttachedNavController: NavController?
get() = firstAttachedHolder.navController
}
fun NavigationHoldersRegistry.navigationBuilder(): NavigationBuilderRegistry {
return NavigationBuilderRegistry(this)
}
@@ -0,0 +1,973 @@
package io.novafoundation.nova.app.root.navigation.navigators
import android.os.Bundle
import androidx.lifecycle.asFlow
import androidx.navigation.NavOptions
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.delayedNavigation.BackDelayedNavigation
import io.novafoundation.nova.app.root.navigation.delayedNavigation.NavComponentDelayedNavigation
import io.novafoundation.nova.app.root.navigation.openSplitScreenWithInstantAction
import io.novafoundation.nova.app.root.presentation.RootRouter
import io.novafoundation.nova.common.navigation.DelayedNavigation
import io.novafoundation.nova.common.navigation.DelayedNavigationRouter
import io.novafoundation.nova.common.utils.getParcelableCompat
import io.novafoundation.nova.common.utils.postToUiThread
import io.novafoundation.nova.feature_account_api.domain.model.PolkadotVaultVariant
import io.novafoundation.nova.feature_account_api.presenatation.account.add.AddAccountPayload
import io.novafoundation.nova.feature_account_api.presenatation.account.add.ImportAccountPayload
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
import io.novafoundation.nova.feature_account_impl.presentation.account.advancedEncryption.AdvancedEncryptionFragment
import io.novafoundation.nova.feature_account_impl.presentation.account.advancedEncryption.AdvancedEncryptionModePayload
import io.novafoundation.nova.feature_account_impl.presentation.account.details.WalletDetailsFragment
import io.novafoundation.nova.feature_account_impl.presentation.cloudBackup.createPassword.createWallet.CreateBackupPasswordPayload
import io.novafoundation.nova.feature_account_impl.presentation.cloudBackup.createPassword.createWallet.CreateWalletBackupPasswordFragment
import io.novafoundation.nova.feature_account_impl.presentation.exporting.ExportPayload
import io.novafoundation.nova.feature_account_impl.presentation.exporting.json.ExportJsonFragment
import io.novafoundation.nova.feature_account_impl.presentation.exporting.seed.ExportSeedFragment
import io.novafoundation.nova.feature_account_impl.presentation.importing.ImportAccountFragment
import io.novafoundation.nova.feature_account_impl.presentation.legacyAddress.ChainAddressSelectorFragment
import io.novafoundation.nova.feature_account_impl.presentation.legacyAddress.ChainAddressSelectorPayload
import io.novafoundation.nova.feature_account_impl.presentation.manualBackup.accounts.ManualBackupSelectAccountFragment
import io.novafoundation.nova.feature_account_impl.presentation.manualBackup.accounts.ManualBackupSelectAccountPayload
import io.novafoundation.nova.feature_account_impl.presentation.manualBackup.common.ManualBackupCommonPayload
import io.novafoundation.nova.feature_account_impl.presentation.manualBackup.secrets.advanced.ManualBackupAdvancedSecretsFragment
import io.novafoundation.nova.feature_account_impl.presentation.manualBackup.secrets.main.ManualBackupSecretsFragment
import io.novafoundation.nova.feature_account_impl.presentation.manualBackup.warning.ManualBackupWarningFragment
import io.novafoundation.nova.feature_account_impl.presentation.mnemonic.backup.BackupMnemonicFragment
import io.novafoundation.nova.feature_account_impl.presentation.mnemonic.backup.BackupMnemonicPayload
import io.novafoundation.nova.feature_account_impl.presentation.mnemonic.confirm.ConfirmMnemonicFragment
import io.novafoundation.nova.feature_account_impl.presentation.mnemonic.confirm.ConfirmMnemonicPayload
import io.novafoundation.nova.feature_account_impl.presentation.node.details.NodeDetailsFragment
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.connect.ParitySignerAccountPayload
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.connect.ParitySignerStartPayload
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.connect.finish.FinishImportParitySignerFragment
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.connect.preview.PreviewImportParitySignerFragment
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.connect.scan.ScanImportParitySignerFragment
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.connect.start.StartImportParitySignerFragment
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.scan.ScanSignParitySignerFragment
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.scan.model.ScanSignParitySignerPayload
import io.novafoundation.nova.feature_account_impl.presentation.pincode.PinCodeAction
import io.novafoundation.nova.feature_account_impl.presentation.pincode.PincodeFragment
import io.novafoundation.nova.feature_account_impl.presentation.pincode.ToolbarConfiguration
import io.novafoundation.nova.feature_account_impl.presentation.startCreateWallet.StartCreateWalletFragment
import io.novafoundation.nova.feature_account_impl.presentation.startCreateWallet.StartCreateWalletPayload
import io.novafoundation.nova.feature_account_impl.presentation.startCreateWallet.StartCreateWalletPayload.FlowType
import io.novafoundation.nova.feature_account_impl.presentation.watchOnly.change.ChangeWatchAccountFragment
import io.novafoundation.nova.feature_ahm_impl.presentation.migrationDetails.ChainMigrationDetailsFragment
import io.novafoundation.nova.feature_ahm_impl.presentation.migrationDetails.ChainMigrationDetailsPayload
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
import io.novafoundation.nova.feature_assets.presentation.balance.detail.BalanceDetailFragment
import io.novafoundation.nova.feature_assets.presentation.flow.network.NetworkFlowFragment
import io.novafoundation.nova.feature_assets.presentation.flow.network.NetworkFlowPayload
import io.novafoundation.nova.feature_assets.presentation.model.OperationParcelizeModel
import io.novafoundation.nova.feature_assets.presentation.receive.ReceiveFragment
import io.novafoundation.nova.feature_assets.presentation.send.TransferDraft
import io.novafoundation.nova.feature_assets.presentation.send.amount.SelectSendFragment
import io.novafoundation.nova.feature_assets.presentation.send.amount.SendPayload
import io.novafoundation.nova.feature_assets.presentation.send.confirm.ConfirmSendFragment
import io.novafoundation.nova.feature_assets.presentation.swap.asset.AssetSwapFlowFragment
import io.novafoundation.nova.feature_assets.presentation.swap.asset.SwapFlowPayload
import io.novafoundation.nova.feature_assets.presentation.swap.network.NetworkSwapFlowFragment
import io.novafoundation.nova.feature_assets.presentation.swap.network.NetworkSwapFlowPayload
import io.novafoundation.nova.feature_assets.presentation.tokens.add.enterInfo.AddTokenEnterInfoFragment
import io.novafoundation.nova.feature_assets.presentation.tokens.add.enterInfo.AddTokenEnterInfoPayload
import io.novafoundation.nova.feature_assets.presentation.tokens.manage.chain.ManageChainTokensFragment
import io.novafoundation.nova.feature_assets.presentation.tokens.manage.chain.ManageChainTokensPayload
import io.novafoundation.nova.feature_assets.presentation.trade.common.TradeProviderFlowType
import io.novafoundation.nova.feature_assets.presentation.trade.provider.TradeProviderListFragment
import io.novafoundation.nova.feature_assets.presentation.trade.provider.TradeProviderListPayload
import io.novafoundation.nova.feature_assets.presentation.trade.webInterface.OnSuccessfulTradeStrategyType
import io.novafoundation.nova.feature_assets.presentation.trade.webInterface.TradeWebFragment
import io.novafoundation.nova.feature_assets.presentation.trade.webInterface.TradeWebPayload
import io.novafoundation.nova.feature_assets.presentation.transaction.detail.extrinsic.ExtrinsicDetailFragment
import io.novafoundation.nova.feature_assets.presentation.transaction.detail.reward.direct.RewardDetailFragment
import io.novafoundation.nova.feature_assets.presentation.transaction.detail.reward.pool.PoolRewardDetailFragment
import io.novafoundation.nova.feature_assets.presentation.transaction.detail.swap.SwapDetailFragment
import io.novafoundation.nova.feature_assets.presentation.transaction.detail.transfer.TransferDetailFragment
import io.novafoundation.nova.feature_assets.presentation.transaction.filter.TransactionHistoryFilterFragment
import io.novafoundation.nova.feature_assets.presentation.transaction.filter.TransactionHistoryFilterPayload
import io.novafoundation.nova.feature_crowdloan_impl.presentation.CrowdloanRouter
import io.novafoundation.nova.feature_crowdloan_impl.presentation.contribute.confirm.ConfirmContributeFragment
import io.novafoundation.nova.feature_crowdloan_impl.presentation.contribute.confirm.parcel.ConfirmContributePayload
import io.novafoundation.nova.feature_crowdloan_impl.presentation.contribute.custom.BonusPayload
import io.novafoundation.nova.feature_crowdloan_impl.presentation.contribute.custom.CustomContributeFragment
import io.novafoundation.nova.feature_crowdloan_impl.presentation.contribute.custom.model.CustomContributePayload
import io.novafoundation.nova.feature_crowdloan_impl.presentation.contribute.custom.moonbeam.terms.MoonbeamCrowdloanTermsFragment
import io.novafoundation.nova.feature_crowdloan_impl.presentation.contribute.select.CrowdloanContributeFragment
import io.novafoundation.nova.feature_crowdloan_impl.presentation.contribute.select.parcel.ContributePayload
import io.novafoundation.nova.feature_gift_impl.presentation.amount.SelectGiftAmountFragment
import io.novafoundation.nova.feature_gift_impl.presentation.amount.SelectGiftAmountPayload
import io.novafoundation.nova.feature_gift_impl.presentation.gifts.GiftsFragment
import io.novafoundation.nova.feature_gift_impl.presentation.gifts.GiftsPayload
import io.novafoundation.nova.feature_ledger_impl.presentation.account.addChain.generic.selectLedger.AddEvmAccountSelectGenericLedgerFragment
import io.novafoundation.nova.feature_ledger_impl.presentation.account.addChain.generic.selectLedger.AddEvmAccountSelectGenericLedgerPayload
import io.novafoundation.nova.feature_ledger_impl.presentation.account.addChain.legacy.selectLedger.AddChainAccountSelectLedgerFragment
import io.novafoundation.nova.feature_ledger_impl.presentation.account.addChain.legacy.selectLedger.AddChainAccountSelectLedgerPayload
import io.novafoundation.nova.feature_ledger_impl.presentation.account.common.selectLedger.SelectLedgerPayload
import io.novafoundation.nova.feature_multisig_operations.presentation.created.MultisigCreatedBottomSheet
import io.novafoundation.nova.feature_multisig_operations.presentation.created.MultisigCreatedPayload
import io.novafoundation.nova.feature_onboarding_impl.OnboardingRouter
import io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.WelcomeFragment
import io.novafoundation.nova.feature_swap_api.presentation.model.SwapSettingsPayload
import io.novafoundation.nova.feature_swap_impl.presentation.main.SwapMainSettingsFragment
import io.novafoundation.nova.feature_wallet_api.presentation.model.AssetPayload
import io.novafoundation.nova.feature_wallet_connect_impl.WalletConnectRouter
import io.novafoundation.nova.feature_wallet_connect_impl.presentation.sessions.list.WalletConnectSessionsPayload
import io.novafoundation.nova.splash.SplashRouter
import kotlinx.coroutines.flow.Flow
class Navigator(
navigationHoldersRegistry: NavigationHoldersRegistry,
private val walletConnectDelegate: WalletConnectRouter
) : BaseNavigator(navigationHoldersRegistry),
SplashRouter,
OnboardingRouter,
AccountRouter,
AssetsRouter,
RootRouter,
CrowdloanRouter,
DelayedNavigationRouter {
override fun openWelcomeScreen() {
navigationBuilder().cases()
.addCase(R.id.accountsFragment, R.id.action_walletManagment_to_welcome)
.addCase(R.id.splashFragment, R.id.action_splash_to_onboarding)
.setArgs(WelcomeFragment.bundle(false))
.navigateInFirstAttachedContext()
}
override fun openInitialCheckPincode() {
val action = PinCodeAction.Check(NavComponentDelayedNavigation(R.id.action_open_split_screen), ToolbarConfiguration())
navigationBuilder().action(R.id.action_splash_to_pin)
.setArgs(PincodeFragment.getPinCodeBundle(action))
.navigateInRoot()
}
override fun openCreateFirstWallet() {
navigationBuilder().action(R.id.action_welcomeFragment_to_startCreateWallet)
.setArgs(StartCreateWalletFragment.bundle(StartCreateWalletPayload(FlowType.FIRST_WALLET)))
.navigateInFirstAttachedContext()
}
override fun openMain() {
navigationBuilder().action(R.id.action_open_split_screen)
.navigateInRoot()
}
override fun openAfterPinCode(delayedNavigation: DelayedNavigation) {
when (delayedNavigation) {
is NavComponentDelayedNavigation -> {
val navOptions = NavOptions.Builder()
.setPopUpTo(R.id.pincodeFragment, true)
.setEnterAnim(R.anim.fragment_open_enter)
.setExitAnim(R.anim.fragment_open_exit)
.setPopEnterAnim(R.anim.fragment_close_enter)
.setPopExitAnim(R.anim.fragment_close_exit)
.build()
navigationBuilder().action(delayedNavigation.globalActionId)
.setArgs(delayedNavigation.extras)
.setNavOptions(navOptions)
.navigateInFirstAttachedContext()
}
is BackDelayedNavigation -> back()
}
}
override fun openCreatePincode() {
val args = buildCreatePinBundle()
navigationBuilder().cases()
.addCase(R.id.splashFragment, R.id.action_splash_to_pin)
.addCase(R.id.importAccountFragment, R.id.action_importAccountFragment_to_pincodeFragment)
.addCase(R.id.confirmMnemonicFragment, R.id.action_confirmMnemonicFragment_to_pincodeFragment)
.addCase(R.id.createWatchWalletFragment, R.id.action_watchWalletFragment_to_pincodeFragment)
.addCase(R.id.finishImportParitySignerFragment, R.id.action_finishImportParitySignerFragment_to_pincodeFragment)
.addCase(R.id.finishImportLedgerFragment, R.id.action_finishImportLedgerFragment_to_pincodeFragment)
.addCase(R.id.createCloudBackupPasswordFragment, R.id.action_createCloudBackupPasswordFragment_to_pincodeFragment)
.addCase(R.id.restoreCloudBackupFragment, R.id.action_restoreCloudBackupFragment_to_pincodeFragment)
.addCase(R.id.finishImportGenericLedgerFragment, R.id.action_finishImportGenericLedgerFragment_to_pincodeFragment)
.setArgs(args)
.navigateInRoot()
}
override fun openAdvancedSettings(payload: AdvancedEncryptionModePayload) {
navigationBuilder().action(R.id.action_open_advancedEncryptionFragment)
.setArgs(AdvancedEncryptionFragment.getBundle(payload))
.navigateInFirstAttachedContext()
}
override fun openConfirmMnemonicOnCreate(confirmMnemonicPayload: ConfirmMnemonicPayload) {
navigationBuilder().action(R.id.action_backupMnemonicFragment_to_confirmMnemonicFragment)
.setArgs(ConfirmMnemonicFragment.getBundle(confirmMnemonicPayload))
.navigateInFirstAttachedContext()
}
override fun openImportAccountScreen(payload: ImportAccountPayload) {
navigationBuilder().cases()
.addCase(R.id.splashFragment, R.id.action_splashFragment_to_import_nav_graph)
.setFallbackCase(R.id.action_import_nav_graph)
.setArgs(ImportAccountFragment.getBundle(payload))
.navigateInFirstAttachedContext()
}
override fun openMnemonicScreen(accountName: String?, addAccountPayload: AddAccountPayload) {
val payload = BackupMnemonicPayload.Create(accountName, addAccountPayload)
navigationBuilder().cases()
.addCase(R.id.welcomeFragment, R.id.action_welcomeFragment_to_mnemonic_nav_graph)
.addCase(R.id.startCreateWalletFragment, R.id.action_startCreateWalletFragment_to_mnemonic_nav_graph)
.addCase(R.id.walletDetailsFragment, R.id.action_accountDetailsFragment_to_mnemonic_nav_graph)
.setArgs(BackupMnemonicFragment.getBundle(payload))
.navigateInFirstAttachedContext()
}
override fun openContribute(payload: ContributePayload) {
val bundle = CrowdloanContributeFragment.getBundle(payload)
navigationBuilder().cases()
.addCase(R.id.mainFragment, R.id.action_mainFragment_to_crowdloanContributeFragment)
.addCase(R.id.moonbeamCrowdloanTermsFragment, R.id.action_moonbeamCrowdloanTermsFragment_to_crowdloanContributeFragment)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
@Deprecated("TODO: Use communicator api instead")
override val customBonusFlow: Flow<BonusPayload?>
get() = currentBackStackEntry!!.savedStateHandle
.getLiveData<BonusPayload?>(CrowdloanContributeFragment.KEY_BONUS_LIVE_DATA)
.asFlow()
@Deprecated("TODO: Use communicator api instead")
override val latestCustomBonus: BonusPayload?
get() = currentBackStackEntry!!.savedStateHandle
.get(CrowdloanContributeFragment.KEY_BONUS_LIVE_DATA)
override fun openCustomContribute(payload: CustomContributePayload) {
navigationBuilder().action(R.id.action_crowdloanContributeFragment_to_customContributeFragment)
.setArgs(CustomContributeFragment.getBundle(payload))
.navigateInFirstAttachedContext()
}
@Deprecated("TODO: Use communicator api instead")
override fun setCustomBonus(payload: BonusPayload) {
previousBackStackEntry!!.savedStateHandle.set(CrowdloanContributeFragment.KEY_BONUS_LIVE_DATA, payload)
}
override fun openConfirmContribute(payload: ConfirmContributePayload) {
navigationBuilder().action(R.id.action_crowdloanContributeFragment_to_confirmContributeFragment)
.setArgs(ConfirmContributeFragment.getBundle(payload))
.navigateInFirstAttachedContext()
}
override fun returnToMain() {
navigationBuilder().action(R.id.back_to_main)
.navigateInFirstAttachedContext()
}
override fun openMoonbeamFlow(payload: ContributePayload) {
navigationBuilder().action(R.id.action_mainFragment_to_moonbeamCrowdloanTermsFragment)
.setArgs(MoonbeamCrowdloanTermsFragment.getBundle(payload))
.navigateInFirstAttachedContext()
}
override fun openAddAccount(payload: AddAccountPayload) {
navigationBuilder().action(R.id.action_open_onboarding)
.setArgs(WelcomeFragment.bundle(payload))
.navigateInFirstAttachedContext()
}
override fun openFilter(payload: TransactionHistoryFilterPayload) {
navigationBuilder().action(R.id.action_mainFragment_to_filterFragment)
.setArgs(TransactionHistoryFilterFragment.getBundle(payload))
.navigateInFirstAttachedContext()
}
override fun openSend(payload: SendPayload, initialRecipientAddress: String?) {
val extras = SelectSendFragment.getBundle(payload, initialRecipientAddress)
navigationBuilder().cases()
.addCase(R.id.sendFlowFragment, R.id.action_sendFlow_to_send)
.addCase(R.id.sendFlowNetworkFragment, R.id.action_sendFlowNetwork_to_send)
.setFallbackCase(R.id.action_open_send)
.setArgs(extras)
.navigateInFirstAttachedContext()
}
override fun openConfirmTransfer(transferDraft: TransferDraft) {
val bundle = ConfirmSendFragment.getBundle(transferDraft)
navigationBuilder().action(R.id.action_chooseAmountFragment_to_confirmTransferFragment)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openTransferDetail(transaction: OperationParcelizeModel.Transfer) {
val bundle = TransferDetailFragment.getBundle(transaction)
navigationBuilder().action(R.id.open_transfer_detail)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openRewardDetail(reward: OperationParcelizeModel.Reward) {
val bundle = RewardDetailFragment.getBundle(reward)
navigationBuilder().action(R.id.open_reward_detail)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openPoolRewardDetail(reward: OperationParcelizeModel.PoolReward) {
val bundle = PoolRewardDetailFragment.getBundle(reward)
navigationBuilder().action(R.id.open_pool_reward_detail)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openSwapDetail(swap: OperationParcelizeModel.Swap) {
val bundle = SwapDetailFragment.getBundle(swap)
navigationBuilder().action(R.id.open_swap_detail)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openExtrinsicDetail(extrinsic: OperationParcelizeModel.Extrinsic) {
navigationBuilder().action(R.id.open_extrinsic_detail)
.setArgs(ExtrinsicDetailFragment.getBundle(extrinsic))
.navigateInFirstAttachedContext()
}
override fun openWallets() {
navigationBuilder().action(R.id.action_open_accounts)
.navigateInFirstAttachedContext()
}
override fun openSwitchWallet() {
navigationBuilder().action(R.id.action_open_switch_wallet)
.navigateInFirstAttachedContext()
}
override fun openDelegatedAccountsUpdates() {
navigationBuilder().action(R.id.action_switchWalletFragment_to_delegatedAccountUpdates)
.navigateInFirstAttachedContext()
}
override fun openSelectAddress(arguments: Bundle) {
navigationBuilder().action(R.id.action_open_select_address)
.setArgs(arguments)
.navigateInFirstAttachedContext()
}
override fun openSelectSingleWallet(arguments: Bundle) {
navigationBuilder().action(R.id.action_open_select_single_wallet)
.setArgs(arguments)
.navigateInFirstAttachedContext()
}
override fun openSelectMultipleWallets(arguments: Bundle) {
navigationBuilder().action(R.id.action_open_select_multiple_wallets)
.setArgs(arguments)
.navigateInFirstAttachedContext()
}
override fun openNodes() {
navigationBuilder().action(R.id.action_mainFragment_to_nodesFragment)
.navigateInFirstAttachedContext()
}
override fun openReceive(assetPayload: AssetPayload) {
navigationBuilder().cases()
.addCase(R.id.receiveFlowFragment, R.id.action_receiveFlow_to_receive)
.addCase(R.id.receiveFlowNetworkFragment, R.id.action_receiveFlowNetwork_to_receive)
.setFallbackCase(R.id.action_open_receive)
.setArgs(ReceiveFragment.getBundle(assetPayload))
.navigateInFirstAttachedContext()
}
override fun openAssetSearch() {
navigationBuilder().action(R.id.action_mainFragment_to_assetSearchFragment)
.navigateInFirstAttachedContext()
}
override fun openManageTokens() {
navigationBuilder().action(R.id.action_mainFragment_to_manageTokensGraph)
.navigateInFirstAttachedContext()
}
override fun openManageChainTokens(payload: ManageChainTokensPayload) {
val args = ManageChainTokensFragment.getBundle(payload)
navigationBuilder().action(R.id.action_manageTokensFragment_to_manageChainTokensFragment)
.setArgs(args)
.navigateInFirstAttachedContext()
}
override fun openAddTokenSelectChain() {
navigationBuilder().action(R.id.action_manageTokensFragment_to_addTokenSelectChainFragment)
.navigateInFirstAttachedContext()
}
override fun openSendFlow() {
navigationBuilder().action(R.id.action_mainFragment_to_sendFlow)
.navigateInFirstAttachedContext()
}
override fun openReceiveFlow() {
navigationBuilder().action(R.id.action_mainFragment_to_receiveFlow)
.navigateInFirstAttachedContext()
}
override fun openBuyFlow() {
navigationBuilder().action(R.id.action_mainFragment_to_buyFlow)
.navigateInFirstAttachedContext()
}
override fun openSellFlow() {
navigationBuilder().action(R.id.action_mainFragment_to_sellFlow)
.navigateInFirstAttachedContext()
}
override fun openSelectGiftAmount(assetPayload: AssetPayload) {
navigationBuilder().action(R.id.action_selectGiftAmount)
.setArgs(SelectGiftAmountFragment.createPayload(SelectGiftAmountPayload(assetPayload)))
.navigateInFirstAttachedContext()
}
override fun openBuyFlowFromSendFlow() {
navigationBuilder().action(R.id.action_sendFlow_to_buyFlow)
.navigateInFirstAttachedContext()
}
override fun openAddTokenEnterInfo(payload: AddTokenEnterInfoPayload) {
val args = AddTokenEnterInfoFragment.getBundle(payload)
navigationBuilder().action(R.id.action_addTokenSelectChainFragment_to_addTokenEnterInfoFragment)
.setArgs(args)
.navigateInFirstAttachedContext()
}
override fun finishAddTokenFlow() {
navigationBuilder().action(R.id.finish_add_token_flow)
.navigateInFirstAttachedContext()
}
override fun openWalletConnectSessions(metaId: Long) {
walletConnectDelegate.openWalletConnectSessions(WalletConnectSessionsPayload(metaId = metaId))
}
override fun openWalletConnectScan() {
walletConnectDelegate.openScanPairingQrCode()
}
override fun closeSendFlow() {
navigationBuilder().action(R.id.action_close_send_flow)
.navigateInFirstAttachedContext()
}
override fun openNovaCard() {
navigationBuilder().action(R.id.action_open_novaCard)
.navigateInFirstAttachedContext()
}
override fun openAwaitingCardCreation() {
navigationBuilder().action(R.id.action_open_awaiting_card_creation)
.navigateInFirstAttachedContext()
}
override fun closeNovaCard() {
navigationBuilder().action(R.id.action_close_nova_card_from_waiting_dialog)
.navigateInFirstAttachedContext()
}
override fun openSendNetworks(payload: NetworkFlowPayload) {
navigationBuilder().action(R.id.action_sendFlow_to_sendFlowNetwork)
.setArgs(NetworkFlowFragment.createPayload(payload))
.navigateInFirstAttachedContext()
}
override fun openReceiveNetworks(payload: NetworkFlowPayload) {
navigationBuilder().action(R.id.action_receiveFlow_to_receiveFlowNetwork)
.setArgs(NetworkFlowFragment.createPayload(payload))
.navigateInFirstAttachedContext()
}
override fun openSwapNetworks(payload: NetworkSwapFlowPayload) {
navigationBuilder().action(R.id.action_selectAssetSwapFlowFragment_to_swapFlowNetworkFragment)
.setArgs(NetworkSwapFlowFragment.createPayload(payload))
.navigateInFirstAttachedContext()
}
override fun openBuyNetworks(payload: NetworkFlowPayload) {
navigationBuilder().action(R.id.action_buyFlow_to_buyFlowNetwork)
.setArgs(NetworkFlowFragment.createPayload(payload))
.navigateInFirstAttachedContext()
}
override fun openSellNetworks(payload: NetworkFlowPayload) {
navigationBuilder().action(R.id.action_sellFlow_to_sellFlowNetwork)
.setArgs(NetworkFlowFragment.createPayload(payload))
.navigateInFirstAttachedContext()
}
override fun openGiftsNetworks(payload: NetworkFlowPayload) {
navigationBuilder().action(R.id.action_giftsFlow_to_giftsFlowNetwork)
.setArgs(NetworkFlowFragment.createPayload(payload))
.navigateInFirstAttachedContext()
}
override fun openBuyProviders(
chainId: String,
chainAssetId: Int
) {
val payload = TradeProviderListPayload(chainId, chainAssetId, TradeProviderFlowType.BUY, OnSuccessfulTradeStrategyType.OPEN_ASSET)
navigationBuilder().cases()
.addCase(R.id.buyFlowFragment, R.id.action_buyFlow_to_tradeProvidersFragment)
.addCase(R.id.buyFlowNetworkFragment, R.id.action_buyFlowNetworks_to_tradeProvidersFragment)
.setFallbackCase(R.id.action_tradeProvidersFragment)
.setArgs(TradeProviderListFragment.createPayload(payload))
.navigateInFirstAttachedContext()
}
override fun openSellProviders(
chainId: String,
chainAssetId: Int
) {
val payload = TradeProviderListPayload(chainId, chainAssetId, TradeProviderFlowType.SELL, OnSuccessfulTradeStrategyType.OPEN_ASSET)
navigationBuilder().cases()
.addCase(R.id.sellFlowFragment, R.id.action_sellFlow_to_tradeProvidersFragment)
.addCase(R.id.sellFlowNetworkFragment, R.id.action_sellFlowNetworks_to_tradeProvidersFragment)
.setFallbackCase(R.id.action_tradeProvidersFragment)
.setArgs(TradeProviderListFragment.createPayload(payload))
.navigateInFirstAttachedContext()
}
override fun openTradeWebInterface(payload: TradeWebPayload) {
navigationBuilder().action(R.id.action_tradeWebFragment)
.setArgs(TradeWebFragment.createPayload(payload))
.navigateInFirstAttachedContext()
}
override fun openChainAddressSelector(chainId: String, accountId: ByteArray) {
val payload = ChainAddressSelectorPayload(chainId, accountId)
navigationBuilder().action(R.id.action_openUnifiedAddressDialog)
.setArgs(ChainAddressSelectorFragment.getBundle(payload))
.navigateInRoot()
}
override fun closeChainAddressesSelector() {
navigationBuilder().action(R.id.action_closeChainAddressesFragment)
.navigateInRoot()
}
override fun openAddGenericEvmAddressSelectLedger(metaId: Long) {
val payload = AddEvmAccountSelectGenericLedgerPayload(metaId)
navigationBuilder().action(R.id.action_accountDetailsFragment_to_addEvmAccountGenericLedgerGraph)
.setArgs(AddEvmAccountSelectGenericLedgerFragment.getBundle(payload))
.navigateInFirstAttachedContext()
}
override fun returnToMainSwapScreen() {
navigationBuilder().action(R.id.action_return_to_swap_settings)
.navigateInFirstAttachedContext()
}
override fun openSwapFlow() {
val payload = SwapFlowPayload.InitialSelecting
navigationBuilder().action(R.id.action_mainFragment_to_swapFlow)
.setArgs(AssetSwapFlowFragment.getBundle(payload))
.navigateInFirstAttachedContext()
}
override fun openSwapSetupAmount(swapSettingsPayload: SwapSettingsPayload) {
navigationBuilder().action(R.id.action_open_swapSetupAmount)
.setArgs(SwapMainSettingsFragment.getBundle(swapSettingsPayload))
.navigateInFirstAttachedContext()
}
override fun returnToMainScreen() {
navigationBuilder().action(R.id.action_returnToMainScreen)
.navigateInFirstAttachedContext()
}
override fun finishSelectAndOpenSwapSetupAmount(swapSettingsPayload: SwapSettingsPayload) {
navigationBuilder().action(R.id.action_finish_and_open_swap_settings)
.setArgs(SwapMainSettingsFragment.getBundle(swapSettingsPayload))
.navigateInFirstAttachedContext()
}
override fun openNfts() {
navigationBuilder().action(R.id.action_mainFragment_to_nfts_nav_graph)
.navigateInFirstAttachedContext()
}
override fun nonCancellableVerify() {
if (currentDestination?.id == R.id.splashFragment) {
return
}
val action = PinCodeAction.CheckAfterInactivity(BackDelayedNavigation, ToolbarConfiguration())
val bundle = PincodeFragment.getPinCodeBundle(action)
if (currentDestination?.id == R.id.pincodeFragment) {
val arguments = currentBackStackEntry!!.arguments!!.getParcelableCompat<PinCodeAction>(PincodeFragment.KEY_PINCODE_ACTION)
if (arguments is PinCodeAction.Change) {
navigationBuilder().action(R.id.action_pin_code_access_recovery)
.setArgs(bundle)
.navigateInRoot()
}
} else {
navigationBuilder().action(R.id.action_pin_code_access_recovery)
.setArgs(bundle)
.navigateInRoot()
}
}
override fun openUpdateNotifications() {
navigationBuilder().action(R.id.action_open_update_notifications)
.navigateInRoot()
}
override fun openPushWelcome() {
navigationBuilder().action(R.id.action_open_pushNotificationsWelcome)
.navigateInFirstAttachedContext()
}
override fun openCloudBackupSettings() {
navigationBuilder().action(R.id.action_open_cloudBackupSettings)
.navigateInFirstAttachedContext()
}
override fun openChainMigrationDetails(chainId: String) {
navigationBuilder().action(R.id.action_open_chain_migration_details)
.setArgs(ChainMigrationDetailsFragment.createPayload(ChainMigrationDetailsPayload(chainId)))
.navigateInRoot()
}
override fun returnToWallet() {
// to achieve smooth animation
postToUiThread {
navigationBuilder().action(R.id.action_return_to_wallet)
.navigateInFirstAttachedContext()
}
}
override fun openWalletDetails(metaId: Long) {
val extras = WalletDetailsFragment.getBundle(metaId)
navigationBuilder().action(R.id.action_open_account_details)
.setArgs(extras)
.navigateInFirstAttachedContext()
}
override fun openClaimContribution() {
navigationBuilder()
.action(R.id.action_userContributionsFragment_to_claimContributionFragment)
.navigateInFirstAttachedContext()
}
override fun openNodeDetails(nodeId: Int) {
val extras = NodeDetailsFragment.getBundle(nodeId)
navigationBuilder().action(R.id.action_nodesFragment_to_nodeDetailsFragment)
.setArgs(extras)
.navigateInFirstAttachedContext()
}
override fun openAssetDetails(assetPayload: AssetPayload) {
val bundle = BalanceDetailFragment.getBundle(assetPayload)
navigationBuilder().cases()
.addCase(R.id.mainFragment, R.id.action_mainFragment_to_balanceDetailFragment)
.addCase(R.id.assetSearchFragment, R.id.action_assetSearchFragment_to_balanceDetailFragment)
.addCase(R.id.confirmTransferFragment, R.id.action_confirmTransferFragment_to_balanceDetailFragment)
.addCase(R.id.tradeWebFragment, R.id.action_tradeWebFragment_to_balanceDetailFragment)
.addCase(R.id.balanceDetailFragment, R.id.action_balanceDetailFragment_to_balanceDetailFragment)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openAssetDetailsFromDeepLink(payload: AssetPayload) {
openSplitScreenWithInstantAction(R.id.action_mainFragment_to_balanceDetailFragment, BalanceDetailFragment.getBundle(payload))
}
override fun openGifts() {
navigationBuilder().action(R.id.action_open_gifts)
.setArgs(GiftsFragment.createPayload(GiftsPayload.AllAssets))
.navigateInFirstAttachedContext()
}
override fun openGiftsByAsset(assetPayload: AssetPayload) {
navigationBuilder().action(R.id.action_open_gifts)
.setArgs(GiftsFragment.createPayload(GiftsPayload.ByAsset(assetPayload)))
.navigateInFirstAttachedContext()
}
override fun finishTradeOperation() {
navigationBuilder().action(R.id.action_finishTradeOperation)
.navigateInFirstAttachedContext()
}
override fun openAddNode() {
navigationBuilder().action(R.id.action_nodesFragment_to_addNodeFragment)
.navigateInFirstAttachedContext()
}
override fun openChangeWatchAccount(payload: AddAccountPayload.ChainAccount) {
val bundle = ChangeWatchAccountFragment.getBundle(payload)
navigationBuilder().action(R.id.action_accountDetailsFragment_to_changeWatchAccountFragment)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openCreateWallet(payload: StartCreateWalletPayload) {
navigationBuilder().action(R.id.action_open_create_new_wallet)
.setArgs(StartCreateWalletFragment.bundle(payload))
.navigateInFirstAttachedContext()
}
override fun openUserContributions() {
navigationBuilder().action(R.id.action_mainFragment_to_userContributionsGraph)
.navigateInFirstAttachedContext()
}
override fun getExportMnemonicDelayedNavigation(exportPayload: ExportPayload.ChainAccount): DelayedNavigation {
val payload = BackupMnemonicPayload.Confirm(exportPayload.chainId, exportPayload.metaId)
val extras = BackupMnemonicFragment.getBundle(payload)
return NavComponentDelayedNavigation(R.id.action_open_mnemonic_nav_graph, extras)
}
override fun getExportSeedDelayedNavigation(exportPayload: ExportPayload.ChainAccount): DelayedNavigation {
val extras = ExportSeedFragment.getBundle(exportPayload)
return NavComponentDelayedNavigation(R.id.action_export_seed, extras)
}
override fun getExportJsonDelayedNavigation(exportPayload: ExportPayload): DelayedNavigation {
val extras = ExportJsonFragment.getBundle(exportPayload)
return NavComponentDelayedNavigation(R.id.action_export_json, extras)
}
override fun exportJsonAction(exportPayload: ExportPayload) {
val extras = ExportJsonFragment.getBundle(exportPayload)
navigationBuilder().action(R.id.action_export_json)
.setArgs(extras)
.navigateInFirstAttachedContext()
}
override fun finishExportFlow() {
navigationBuilder().action(R.id.finish_export_flow)
.navigateInFirstAttachedContext()
}
override fun openScanImportParitySigner(payload: ParitySignerStartPayload) {
val args = ScanImportParitySignerFragment.getBundle(payload)
navigationBuilder().action(R.id.action_startImportParitySignerFragment_to_scanImportParitySignerFragment)
.setArgs(args)
.navigateInFirstAttachedContext()
}
override fun openPreviewImportParitySigner(payload: ParitySignerAccountPayload) {
val bundle = PreviewImportParitySignerFragment.getBundle(payload)
navigationBuilder().action(R.id.action_scanImportParitySignerFragment_to_previewImportParitySignerFragment)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openFinishImportParitySigner(payload: ParitySignerAccountPayload) {
val bundle = FinishImportParitySignerFragment.getBundle(payload)
navigationBuilder().action(R.id.action_previewImportParitySignerFragment_to_finishImportParitySignerFragment)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openScanParitySignerSignature(payload: ScanSignParitySignerPayload) {
val bundle = ScanSignParitySignerFragment.getBundle(payload)
navigationBuilder().action(R.id.action_showSignParitySignerFragment_to_scanSignParitySignerFragment)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun finishParitySignerFlow() {
navigationBuilder().action(R.id.action_finish_parity_signer_flow)
.navigateInFirstAttachedContext()
}
override fun openAddLedgerChainAccountFlow(addAccountPayload: AddAccountPayload.ChainAccount) {
val payload = AddChainAccountSelectLedgerPayload(addAccountPayload, SelectLedgerPayload.ConnectionMode.ALL)
val bundle = AddChainAccountSelectLedgerFragment.getBundle(payload)
navigationBuilder().action(R.id.action_accountDetailsFragment_to_addLedgerAccountGraph)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openCreateCloudBackupPassword(walletName: String) {
val bundle = CreateWalletBackupPasswordFragment.getBundle(CreateBackupPasswordPayload(walletName))
navigationBuilder().action(R.id.action_startCreateWalletFragment_to_createCloudBackupPasswordFragment)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun restoreCloudBackup() {
navigationBuilder().cases()
.addCase(R.id.importWalletOptionsFragment, R.id.action_importWalletOptionsFragment_to_restoreCloudBackup)
.addCase(R.id.startCreateWalletFragment, R.id.action_startCreateWalletFragment_to_resotreCloudBackupFragment)
.navigateInFirstAttachedContext()
}
override fun openSyncWalletsBackupPassword() {
navigationBuilder().action(R.id.action_cloudBackupSettings_to_syncWalletsBackupPasswordFragment)
.navigateInFirstAttachedContext()
}
override fun openChangeBackupPasswordFlow() {
navigationBuilder().action(R.id.action_cloudBackupSettings_to_checkCloudBackupPasswordFragment)
.navigateInFirstAttachedContext()
}
override fun openRestoreBackupPassword() {
navigationBuilder().action(R.id.action_cloudBackupSettings_to_restoreCloudBackupPasswordFragment)
.navigateInFirstAttachedContext()
}
override fun openChangeBackupPassword() {
navigationBuilder().action(R.id.action_checkCloudBackupPasswordFragment_to_changeBackupPasswordFragment)
.navigateInFirstAttachedContext()
}
override fun openManualBackupSelectAccount(metaId: Long) {
val bundle = ManualBackupSelectAccountFragment.bundle(ManualBackupSelectAccountPayload(metaId))
navigationBuilder().action(R.id.action_manualBackupSelectWalletFragment_to_manualBackupSelectAccountFragment)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openManualBackupConditions(payload: ManualBackupCommonPayload) {
val bundle = ManualBackupWarningFragment.bundle(payload)
val pinCodePayload = PinCodeAction.Check(
NavComponentDelayedNavigation(R.id.action_manualBackupPincodeFragment_to_manualBackupWarning, bundle),
ToolbarConfiguration()
)
val pinCodeBundle = PincodeFragment.getPinCodeBundle(pinCodePayload)
navigationBuilder().cases()
.addCase(R.id.manualBackupSelectWallet, R.id.action_manualBackupSelectWallet_to_pincode_check)
.addCase(R.id.manualBackupSelectAccount, R.id.action_manualBackupSelectAccount_to_pincode_check)
.setArgs(pinCodeBundle)
.navigateInFirstAttachedContext()
}
override fun openManualBackupSecrets(payload: ManualBackupCommonPayload) {
val bundle = ManualBackupSecretsFragment.bundle(payload)
navigationBuilder().action(R.id.action_manualBackupWarning_to_manualBackupSecrets)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openManualBackupAdvancedSecrets(payload: ManualBackupCommonPayload) {
val bundle = ManualBackupAdvancedSecretsFragment.bundle(payload)
navigationBuilder().action(R.id.action_manualBackupSecrets_to_manualBackupAdvancedSecrets)
.setArgs(bundle)
.navigateInFirstAttachedContext()
}
override fun openCreateWatchWallet() {
navigationBuilder().action(R.id.action_importWalletOptionsFragment_to_createWatchWalletFragment)
.navigateInFirstAttachedContext()
}
override fun openStartImportParitySigner() {
openStartImportPolkadotVault(PolkadotVaultVariant.PARITY_SIGNER)
}
override fun openStartImportPolkadotVault() {
openStartImportPolkadotVault(PolkadotVaultVariant.POLKADOT_VAULT)
}
override fun openImportOptionsScreen() {
navigationBuilder().cases()
.addCase(R.id.welcomeFragment, R.id.action_welcomeFragment_to_importWalletOptionsFragment)
.setFallbackCase(R.id.action_importWalletOptionsFragment)
.navigateInFirstAttachedContext()
}
override fun openStartImportLegacyLedger() {
navigationBuilder().action(R.id.action_importWalletOptionsFragment_to_import_legacy_ledger_graph)
.navigateInFirstAttachedContext()
}
override fun openStartImportGenericLedger() {
navigationBuilder().action(R.id.action_importWalletOptionsFragment_to_import_generic_ledger_graph)
.navigateInFirstAttachedContext()
}
override fun withPinCodeCheckRequired(
delayedNavigation: DelayedNavigation,
createMode: Boolean,
pinCodeTitleRes: Int?,
) {
val action = if (createMode) {
PinCodeAction.Create(delayedNavigation)
} else {
PinCodeAction.Check(delayedNavigation, ToolbarConfiguration(pinCodeTitleRes, true))
}
navigationBuilder().action(R.id.open_pincode_check)
.setArgs(PincodeFragment.getPinCodeBundle(action))
.navigateInFirstAttachedContext()
}
private fun openStartImportPolkadotVault(variant: PolkadotVaultVariant) {
val args = StartImportParitySignerFragment.getBundle(ParitySignerStartPayload(variant))
navigationBuilder().action(R.id.action_importWalletOptionsFragment_to_import_parity_signer_graph)
.setArgs(args)
.navigateInFirstAttachedContext()
}
private fun buildCreatePinBundle(): Bundle {
val delayedNavigation = NavComponentDelayedNavigation(R.id.action_open_split_screen)
val action = PinCodeAction.Create(delayedNavigation)
return PincodeFragment.getPinCodeBundle(action)
}
override fun runDelayedNavigation(delayedNavigation: DelayedNavigation) {
when (delayedNavigation) {
BackDelayedNavigation -> back()
is NavComponentDelayedNavigation -> {
navigationBuilder().action(delayedNavigation.globalActionId)
.setArgs(delayedNavigation.extras)
.navigateInFirstAttachedContext()
}
}
}
override fun finishTopUp() {
navigationBuilder().action(R.id.action_finishTopUpFlow)
.navigateInFirstAttachedContext()
}
override fun openPendingMultisigOperations() {
navigationBuilder().action(R.id.action_mainFragment_to_multisigPendingOperationsFlow)
.navigateInFirstAttachedContext()
}
override fun openMainWithFinishMultisigTransaction(accountWasSwitched: Boolean) {
val payload = MultisigCreatedBottomSheet.createPayload(MultisigCreatedPayload(accountWasSwitched))
openSplitScreenWithInstantAction(R.id.action_open_multisigCreatedDialog, nestedActionExtras = payload)
}
}
@@ -0,0 +1,37 @@
package io.novafoundation.nova.app.root.navigation.navigators.account
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.NavStackInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.getBackStackEntryBefore
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_account_api.domain.model.PolkadotVaultVariant
import io.novafoundation.nova.feature_account_api.presenatation.sign.SignInterScreenCommunicator.Request
import io.novafoundation.nova.feature_account_api.presenatation.sign.SignInterScreenCommunicator.Response
import io.novafoundation.nova.feature_account_impl.data.signer.paritySigner.PolkadotVaultVariantSignCommunicator
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.show.ShowSignParitySignerFragment
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.show.ShowSignParitySignerPayload
class PolkadotVaultVariantSignCommunicatorImpl(
navigationHoldersRegistry: NavigationHoldersRegistry
) : NavStackInterScreenCommunicator<Request, Response>(navigationHoldersRegistry), PolkadotVaultVariantSignCommunicator {
private var usedPolkadotVaultVariant: PolkadotVaultVariant? = null
override fun respond(response: Response) {
val requester = navController.getBackStackEntryBefore(R.id.showSignParitySignerFragment)
saveResultTo(requester, response)
}
override fun setUsedVariant(variant: PolkadotVaultVariant) {
usedPolkadotVaultVariant = variant
}
override fun openRequest(request: Request) {
super.openRequest(request)
val payload = ShowSignParitySignerPayload(request, requireNotNull(usedPolkadotVaultVariant))
val bundle = ShowSignParitySignerFragment.getBundle(payload)
navController.navigate(R.id.action_open_sign_parity_signer, bundle)
}
}
@@ -0,0 +1,20 @@
package io.novafoundation.nova.app.root.navigation.navigators.account
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.NavStackInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.navigationBuilder
import io.novafoundation.nova.feature_account_impl.presentation.seedScan.ScanSeedCommunicator
class ScanSeedCommunicatorImpl(
private val navigationHoldersRegistry: NavigationHoldersRegistry
) : NavStackInterScreenCommunicator<ScanSeedCommunicator.Request, ScanSeedCommunicator.Response>(navigationHoldersRegistry),
ScanSeedCommunicator {
override fun openRequest(request: ScanSeedCommunicator.Request) {
super.openRequest(request)
navigationHoldersRegistry.navigationBuilder().action(R.id.action_scan_seed)
.navigateInFirstAttachedContext()
}
}
@@ -0,0 +1,20 @@
package io.novafoundation.nova.app.root.navigation.navigators.account
import io.novafoundation.nova.app.root.navigation.NavStackInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectAddress.SelectAddressCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectAddress.SelectAddressRequester
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectAddress.SelectAddressResponder
import io.novafoundation.nova.feature_account_impl.presentation.account.list.selectAddress.SelectAddressBottomSheet
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
class SelectAddressCommunicatorImpl(private val router: AssetsRouter, navigationHoldersRegistry: NavigationHoldersRegistry) :
NavStackInterScreenCommunicator<SelectAddressRequester.Request, SelectAddressResponder.Response>(navigationHoldersRegistry),
SelectAddressCommunicator {
override fun openRequest(request: SelectAddressRequester.Request) {
super.openRequest(request)
router.openSelectAddress(SelectAddressBottomSheet.getBundle(request))
}
}
@@ -0,0 +1,20 @@
package io.novafoundation.nova.app.root.navigation.navigators.account
import io.novafoundation.nova.app.root.navigation.NavStackInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_account_api.presenatation.account.wallet.list.SelectMultipleWalletsCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.account.wallet.list.SelectMultipleWalletsRequester
import io.novafoundation.nova.feature_account_api.presenatation.account.wallet.list.SelectMultipleWalletsResponder
import io.novafoundation.nova.feature_account_impl.presentation.account.list.multipleSelecting.SelectMultipleWalletsFragment
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
class SelectMultipleWalletsCommunicatorImpl(private val router: AssetsRouter, navigationHoldersRegistry: NavigationHoldersRegistry) :
NavStackInterScreenCommunicator<SelectMultipleWalletsRequester.Request, SelectMultipleWalletsResponder.Response>(navigationHoldersRegistry),
SelectMultipleWalletsCommunicator {
override fun openRequest(request: SelectMultipleWalletsRequester.Request) {
super.openRequest(request)
router.openSelectMultipleWallets(SelectMultipleWalletsFragment.getBundle(request))
}
}
@@ -0,0 +1,17 @@
package io.novafoundation.nova.app.root.navigation.navigators.account
import io.novafoundation.nova.app.root.navigation.FlowInterScreenCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectSingleWallet.SelectSingleWalletCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectSingleWallet.SelectSingleWalletRequester
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectSingleWallet.SelectSingleWalletResponder
import io.novafoundation.nova.feature_account_impl.presentation.account.list.singleSelecting.SelectSingleWalletFragment
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
class SelectSingleWalletCommunicatorImpl(private val router: AssetsRouter) :
FlowInterScreenCommunicator<SelectSingleWalletRequester.Request, SelectSingleWalletResponder.Response>(),
SelectSingleWalletCommunicator {
override fun dispatchRequest(request: SelectSingleWalletRequester.Request) {
router.openSelectSingleWallet(SelectSingleWalletFragment.createPayload(request))
}
}
@@ -0,0 +1,19 @@
package io.novafoundation.nova.app.root.navigation.navigators.account
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.NavStackInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectWallet.SelectWalletCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectWallet.SelectWalletCommunicator.Payload
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectWallet.SelectWalletCommunicator.Response
class SelectWalletCommunicatorImpl(
private val navigationHoldersRegistry: NavigationHoldersRegistry
) : NavStackInterScreenCommunicator<Payload, Response>(navigationHoldersRegistry), SelectWalletCommunicator {
override fun openRequest(request: Payload) {
super.openRequest(request)
navController.navigate(R.id.action_open_select_wallet)
}
}
@@ -0,0 +1,43 @@
package io.novafoundation.nova.app.root.navigation.navigators.accountmigration
import android.os.Bundle
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.delayedNavigation.NavComponentDelayedNavigation
import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_account_impl.presentation.pincode.PinCodeAction
import io.novafoundation.nova.feature_account_impl.presentation.pincode.PincodeFragment
import io.novafoundation.nova.feature_account_migration.presentation.AccountMigrationRouter
import io.novafoundation.nova.feature_account_migration.presentation.pairing.AccountMigrationPairingFragment
import io.novafoundation.nova.feature_account_migration.presentation.pairing.AccountMigrationPairingPayload
class AccountMigrationNavigator(
navigationHoldersRegistry: NavigationHoldersRegistry
) : BaseNavigator(navigationHoldersRegistry), AccountMigrationRouter {
override fun openAccountMigrationPairing(scheme: String) {
val payload = AccountMigrationPairingPayload(scheme)
navigationBuilder().action(R.id.action_open_accountMigrationPairing)
.setArgs(AccountMigrationPairingFragment.Companion.createPayload(payload))
.navigateInRoot()
}
override fun finishMigrationFlow() {
navigationBuilder().action(R.id.action_open_split_screen)
.navigateInRoot()
}
override fun openPinCodeSet() {
val args = buildCreatePinBundle()
navigationBuilder().action(R.id.action_migration_to_pin)
.setArgs(args)
.navigateInRoot()
}
private fun buildCreatePinBundle(): Bundle {
val delayedNavigation = NavComponentDelayedNavigation(R.id.action_open_split_screen)
val action = PinCodeAction.Create(delayedNavigation)
return PincodeFragment.getPinCodeBundle(action)
}
}
@@ -0,0 +1,14 @@
package io.novafoundation.nova.app.root.navigation.navigators.builder
import io.novafoundation.nova.app.root.navigation.holders.NavigationHolder
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
class ActionNavigationBuilder(
navigationHoldersRegistry: NavigationHoldersRegistry,
private val actionId: Int
) : NavigationBuilder(navigationHoldersRegistry) {
override fun performInternal(navigationHolder: NavigationHolder) {
performAction(navigationHolder, actionId)
}
}
@@ -0,0 +1,42 @@
package io.novafoundation.nova.app.root.navigation.navigators.builder
import androidx.navigation.NavDestination
import io.novafoundation.nova.app.root.navigation.holders.NavigationHolder
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
class CasesNavigationBuilder(
navigationHoldersRegistry: NavigationHoldersRegistry
) : NavigationBuilder(navigationHoldersRegistry) {
private class Case(val destination: Int, val actionId: Int)
private var cases = mutableListOf<Case>()
private var fallbackCaseActionId: Int? = null
fun addCase(currentDestination: Int, actionId: Int): CasesNavigationBuilder {
cases.add(Case(currentDestination, actionId))
return this
}
fun setFallbackCase(actionId: Int): CasesNavigationBuilder {
fallbackCaseActionId = actionId
return this
}
override fun performInternal(navigationHolder: NavigationHolder) {
val navController = navigationHolder.navController ?: return
val currentDestination = navController.currentDestination ?: return
val caseActionId = cases.find { case -> case.destination == currentDestination.id }
?.actionId
?: fallbackCaseActionId
?: throw IllegalArgumentException("Unknown case for ${currentDestination.label}")
performAction(navigationHolder, caseActionId)
}
private fun NavDestination.hasAction(actionId: Int): Boolean {
return this.getAction(actionId) != null
}
}
@@ -0,0 +1,82 @@
package io.novafoundation.nova.app.root.navigation.navigators.builder
import android.os.Bundle
import androidx.navigation.NavDestination
import androidx.navigation.NavOptions
import androidx.navigation.fragment.FragmentNavigator
import io.novafoundation.nova.app.root.navigation.holders.NavigationHolder
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
/**
* Class for building navigation.
* Currently, it has 2 navigators: split_screen and root
* - root-navigator - a navigator that opens fragments on top of others and is designed for fragments that do not require a split screen (For example, DAppBrowser needs to be opened in the root navigator)
* - split_screen-navigator - the main navigator of the application. All fragments opened in it will be opened in a split screen mode
*
* Let's look at the scenarios for building navigation:
* - In the normal case, you need to add a navigation node only to the split_screen_nav_graph, be it a dialog or a fragment.
* This can be used when you are sure that the fragment or dialog should not be launched in the browser or on top of a split screen.
* Use [navigateInFirstAttachedContext] and the fragment will be automatically attached to the SplitScreenNavigationHolder.
* - If you expect that the fragment can also be launched from the browser, you need to add it to both the root_navigation_graph and the split_screen_navigation_graph.
* Keep in mind that the actionId must be the same in both graphs.
* Use [navigateInFirstAttachedContext] and the fragment will be automatically attached to the desired holder.
* - In case of adding dialogs, you can add it only to the root_navigation_graph if you think that the dialog can be launched both from the browser and from the remote part of the application.
* To attach the dialog to the RootNavigationHolder, call [navigateInRoot]
* - In the latter case, we may need to add a screen that is strictly required to be opened on top of the split screen or only in the browser flow. (Such screens as entering a pin code).
* In this case, we need to add an action only to the root_navigation_graph
* To attach the fragment to the RootNavigationHolder, call [navigateInRoot]
**/
abstract class NavigationBuilder(
private val navigationHoldersRegistry: NavigationHoldersRegistry
) {
protected var navOptions: NavOptions? = null
protected var args: Bundle? = null
protected var extras: FragmentNavigator.Extras? = null
fun setArgs(args: Bundle?): NavigationBuilder {
this.args = args
return this
}
fun setNavOptions(navOptions: NavOptions): NavigationBuilder {
this.navOptions = navOptions
return this
}
fun setExtras(extras: FragmentNavigator.Extras?): NavigationBuilder {
this.extras = extras
return this
}
/**
* Opens a fragment in the first attached navigation holder (split_screen or root).
* If it is assumed that the fragment can be opened both in root and in split_screen, then it is necessary to add a navigation node both to split_screen_navigation_graph and to root_navigation_graph
*/
fun navigateInFirstAttachedContext() {
performInternal(navigationHoldersRegistry.firstAttachedHolder)
}
/**
* Always open fragment in root navigation holder.
* In this case, the node must be added to root_navigation_graph
**/
fun navigateInRoot() {
performInternal(navigationHoldersRegistry.rootNavigationHolder)
}
protected fun NavigationBuilder.performAction(navigationHolder: NavigationHolder, actionId: Int) {
val navController = navigationHolder.navController ?: return
val currentDestination = navController.currentDestination ?: return
if (currentDestination.hasAction(actionId)) {
navigationHolder.navController?.navigate(actionId, args, navOptions, extras)
}
}
protected abstract fun performInternal(navigationHolder: NavigationHolder)
}
private fun NavDestination.hasAction(actionId: Int): Boolean {
return this.getAction(actionId) != null
}
@@ -0,0 +1,12 @@
package io.novafoundation.nova.app.root.navigation.navigators.builder
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
class NavigationBuilderRegistry(private val registry: NavigationHoldersRegistry) {
fun action(actionId: Int) = ActionNavigationBuilder(registry, actionId)
fun cases() = CasesNavigationBuilder(registry)
fun graph(graphId: Int) = OpenGraphNavigationBuilder(registry, graphId)
}
@@ -0,0 +1,14 @@
package io.novafoundation.nova.app.root.navigation.navigators.builder
import io.novafoundation.nova.app.root.navigation.holders.NavigationHolder
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
class OpenGraphNavigationBuilder(
navigationHoldersRegistry: NavigationHoldersRegistry,
private val graphId: Int
) : NavigationBuilder(navigationHoldersRegistry) {
override fun performInternal(navigationHolder: NavigationHolder) {
navigationHolder.navController?.navigate(graphId, args, navOptions, extras)
}
}
@@ -0,0 +1,8 @@
package io.novafoundation.nova.app.root.navigation.navigators.buy
import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_buy_impl.presentation.BuyRouter
class BuyNavigator(navigationHoldersRegistry: NavigationHoldersRegistry) : BuyRouter,
BaseNavigator(navigationHoldersRegistry)
@@ -0,0 +1,17 @@
package io.novafoundation.nova.app.root.navigation.navigators.chainMigration
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_ahm_impl.presentation.ChainMigrationRouter
import io.novafoundation.nova.feature_ahm_impl.presentation.migrationDetails.ChainMigrationDetailsFragment
import io.novafoundation.nova.feature_ahm_impl.presentation.migrationDetails.ChainMigrationDetailsPayload
class ChainMigrationNavigator(navigationHoldersRegistry: NavigationHoldersRegistry) : ChainMigrationRouter, BaseNavigator(navigationHoldersRegistry) {
override fun openChainMigrationDetails(chainId: String) {
navigationBuilder().action(R.id.action_open_chain_migration_details)
.setArgs(ChainMigrationDetailsFragment.createPayload(ChainMigrationDetailsPayload(chainId)))
.navigateInRoot()
}
}
@@ -0,0 +1,19 @@
package io.novafoundation.nova.app.root.navigation.navigators.cloudBackup
import io.novafoundation.nova.app.root.navigation.NavStackInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.changePassword.ChangeBackupPasswordCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.changePassword.ChangeBackupPasswordRequester
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.changePassword.ChangeBackupPasswordResponder
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
class ChangeBackupPasswordCommunicatorImpl(private val router: AccountRouter, navigationHoldersRegistry: NavigationHoldersRegistry) :
NavStackInterScreenCommunicator<ChangeBackupPasswordRequester.EmptyRequest, ChangeBackupPasswordResponder.Success>(navigationHoldersRegistry),
ChangeBackupPasswordCommunicator {
override fun openRequest(request: ChangeBackupPasswordRequester.EmptyRequest) {
super.openRequest(request)
router.openChangeBackupPasswordFlow()
}
}
@@ -0,0 +1,8 @@
package io.novafoundation.nova.app.root.navigation.navigators.cloudBackup
import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_cloud_backup_impl.presentation.CloudBackupRouter
class CloudBackupNavigator(navigationHoldersRegistry: NavigationHoldersRegistry) : CloudBackupRouter,
BaseNavigator(navigationHoldersRegistry)
@@ -0,0 +1,19 @@
package io.novafoundation.nova.app.root.navigation.navigators.cloudBackup
import io.novafoundation.nova.app.root.navigation.NavStackInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.changePassword.RestoreBackupPasswordCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.changePassword.RestoreBackupPasswordRequester
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.changePassword.RestoreBackupPasswordResponder
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
class RestoreBackupPasswordCommunicatorImpl(private val router: AccountRouter, navigationHoldersRegistry: NavigationHoldersRegistry) :
NavStackInterScreenCommunicator<RestoreBackupPasswordRequester.EmptyRequest, RestoreBackupPasswordResponder.Success>(navigationHoldersRegistry),
RestoreBackupPasswordCommunicator {
override fun openRequest(request: RestoreBackupPasswordRequester.EmptyRequest) {
super.openRequest(request)
router.openRestoreBackupPassword()
}
}
@@ -0,0 +1,19 @@
package io.novafoundation.nova.app.root.navigation.navigators.cloudBackup
import io.novafoundation.nova.app.root.navigation.NavStackInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.createPassword.SyncWalletsBackupPasswordCommunicator
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.createPassword.SyncWalletsBackupPasswordRequester
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.createPassword.SyncWalletsBackupPasswordResponder
class SyncWalletsBackupPasswordCommunicatorImpl(private val router: AccountRouter, navigationHoldersRegistry: NavigationHoldersRegistry) :
NavStackInterScreenCommunicator<SyncWalletsBackupPasswordRequester.EmptyRequest, SyncWalletsBackupPasswordResponder.Response>(navigationHoldersRegistry),
SyncWalletsBackupPasswordCommunicator {
override fun openRequest(request: SyncWalletsBackupPasswordRequester.EmptyRequest) {
super.openRequest(request)
router.openSyncWalletsBackupPassword()
}
}
@@ -0,0 +1,109 @@
package io.novafoundation.nova.app.root.navigation.navigators.dApp
import androidx.navigation.NavOptions
import androidx.navigation.fragment.FragmentNavigator
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.builder.NavigationBuilder
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
import io.novafoundation.nova.feature_dapp_impl.presentation.addToFavourites.AddToFavouritesFragment
import io.novafoundation.nova.feature_dapp_api.presentation.addToFavorites.AddToFavouritesPayload
import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload
import io.novafoundation.nova.feature_dapp_impl.presentation.browser.main.DAppBrowserFragment
import io.novafoundation.nova.feature_dapp_impl.presentation.search.DappSearchFragment
import io.novafoundation.nova.feature_dapp_impl.presentation.search.SearchPayload
class DAppNavigator(
navigationHoldersRegistry: NavigationHoldersRegistry,
) : BaseNavigator(navigationHoldersRegistry), DAppRouter {
override fun openChangeAccount() {
navigationBuilder().action(R.id.action_open_switch_wallet)
.navigateInFirstAttachedContext()
}
override fun openDAppBrowser(payload: DAppBrowserPayload, extras: FragmentNavigator.Extras?) {
// Close dapp browser if it is already opened
// TODO it's better to provide new url to existing browser
navigationBuilder().graph(R.id.dapp_browser_graph)
.setDappAnimations()
.setExtras(extras)
.setArgs(DAppBrowserFragment.getBundle(payload))
.navigateInRoot()
}
override fun openDappSearch() {
openDappSearchWithCategory(categoryId = null)
}
override fun openDappSearchWithCategory(categoryId: String?) {
navigationBuilder().graph(R.id.dapp_search_graph)
.setDappAnimations()
.setArgs(DappSearchFragment.getBundle(SearchPayload(initialUrl = null, SearchPayload.Request.OPEN_NEW_URL, preselectedCategoryId = categoryId)))
.navigateInRoot()
}
override fun finishDappSearch() {
navigationBuilder().action(R.id.action_finish_dapp_search)
.navigateInRoot()
}
override fun openAddToFavourites(payload: AddToFavouritesPayload) {
navigationBuilder().action(R.id.action_DAppBrowserFragment_to_addToFavouritesFragment)
.setArgs(AddToFavouritesFragment.getBundle(payload))
.navigateInFirstAttachedContext()
}
override fun openAuthorizedDApps() {
navigationBuilder().action(R.id.action_mainFragment_to_authorizedDAppsFragment)
.navigateInFirstAttachedContext()
}
override fun openTabs() {
navigationBuilder().graph(R.id.dapp_tabs_graph)
.setDappAnimations()
.navigateInRoot()
}
override fun closeTabsScreen() {
navigationBuilder().action(R.id.action_finish_tabs_fragment)
.navigateInRoot()
}
override fun openDAppFavorites() {
navigationBuilder().action(R.id.action_open_dapp_favorites)
.navigateInFirstAttachedContext()
}
private fun NavigationBuilder.setDappAnimations(): NavigationBuilder {
val currentDestinationId = currentDestination?.id
// For this currentDestinations we will use default animation. And for other - slide_in, slide_out
val dappDestinations = listOf(
R.id.dappSearchFragment,
R.id.dappBrowserFragment,
R.id.dappTabsFragment
)
val navOptionsBuilder = if (currentDestinationId in dappDestinations) {
// Only slide out animation
NavOptions.Builder()
.setEnterAnim(R.anim.fragment_open_enter)
.setExitAnim(R.anim.fragment_open_exit)
.setPopEnterAnim(R.anim.fragment_close_enter)
.setPopExitAnim(R.anim.fragment_slide_out)
.setPopUpTo(R.id.splitScreenFragment, false)
} else {
// Slide in/out animations
NavOptions.Builder()
.setEnterAnim(R.anim.fragment_slide_in)
.setExitAnim(R.anim.fragment_open_exit)
.setPopEnterAnim(R.anim.fragment_close_enter)
.setPopExitAnim(R.anim.fragment_slide_out)
.setPopUpTo(R.id.splitScreenFragment, false)
}
return setNavOptions(navOptionsBuilder.build())
}
}
@@ -0,0 +1,22 @@
package io.novafoundation.nova.app.root.navigation.navigators.dApp
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.NavStackInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_dapp_impl.presentation.search.DAppSearchCommunicator
import io.novafoundation.nova.feature_dapp_impl.presentation.search.DAppSearchCommunicator.Response
import io.novafoundation.nova.feature_dapp_impl.presentation.search.DappSearchFragment
import io.novafoundation.nova.feature_dapp_impl.presentation.search.SearchPayload
class DAppSearchCommunicatorImpl(navigationHoldersRegistry: NavigationHoldersRegistry) :
NavStackInterScreenCommunicator<SearchPayload, Response>(navigationHoldersRegistry),
DAppSearchCommunicator {
override fun openRequest(request: SearchPayload) {
super.openRequest(request)
navigationBuilder().action(R.id.action_open_dappSearch_from_browser)
.setArgs(DappSearchFragment.getBundle(request))
.navigateInRoot()
}
}
@@ -0,0 +1,32 @@
package io.novafoundation.nova.app.root.navigation.navigators.externalSign
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.FlowInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.navigationBuilder
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.common.utils.sequrity.awaitInteractionAllowed
import io.novafoundation.nova.feature_external_sign_api.model.ExternalSignCommunicator
import io.novafoundation.nova.feature_external_sign_api.model.signPayload.ExternalSignPayload
import io.novafoundation.nova.feature_external_sign_impl.presentation.signExtrinsic.ExternalSignFragment
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class ExternalSignCommunicatorImpl(
private val navigationHoldersRegistry: NavigationHoldersRegistry,
private val automaticInteractionGate: AutomaticInteractionGate,
) : CoroutineScope by CoroutineScope(Dispatchers.Main),
FlowInterScreenCommunicator<ExternalSignPayload, ExternalSignCommunicator.Response>(),
ExternalSignCommunicator {
override fun dispatchRequest(request: ExternalSignPayload) {
launch {
automaticInteractionGate.awaitInteractionAllowed()
navigationHoldersRegistry.navigationBuilder().action(R.id.action_open_externalSignGraph)
.setArgs(ExternalSignFragment.getBundle(request))
.navigateInRoot()
}
}
}
@@ -0,0 +1,18 @@
package io.novafoundation.nova.app.root.navigation.navigators.externalSign
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_external_sign_impl.ExternalSignRouter
import io.novafoundation.nova.feature_external_sign_impl.presentation.extrinsicDetails.ExternalExtrinsicDetailsFragment
class ExternalSignNavigator(
navigationHoldersRegistry: NavigationHoldersRegistry
) : BaseNavigator(navigationHoldersRegistry), ExternalSignRouter {
override fun openExtrinsicDetails(extrinsicContent: String) {
navigationBuilder().action(R.id.action_ConfirmSignExtrinsicFragment_to_extrinsicDetailsFragment)
.setArgs(ExternalExtrinsicDetailsFragment.getBundle(extrinsicContent))
.navigateInFirstAttachedContext()
}
}
@@ -0,0 +1,61 @@
package io.novafoundation.nova.app.root.navigation.navigators.gift
import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
import io.novafoundation.nova.feature_gift_impl.domain.GiftId
import io.novafoundation.nova.feature_gift_impl.presentation.GiftRouter
import io.novafoundation.nova.feature_gift_impl.presentation.claim.ClaimGiftFragment
import io.novafoundation.nova.feature_gift_impl.presentation.claim.ClaimGiftPayload
import io.novafoundation.nova.feature_gift_impl.presentation.confirm.CreateGiftConfirmFragment
import io.novafoundation.nova.feature_gift_impl.presentation.confirm.CreateGiftConfirmPayload
import io.novafoundation.nova.feature_gift_impl.presentation.share.ShareGiftFragment
import io.novafoundation.nova.feature_gift_impl.presentation.share.ShareGiftPayload
import io.novafoundation.nova.feature_wallet_api.presentation.model.AssetPayload
class GiftNavigator(
private val commonDelegate: Navigator,
navigationHoldersRegistry: NavigationHoldersRegistry
) : GiftRouter, BaseNavigator(navigationHoldersRegistry) {
override fun finishCreateGift() {
navigationBuilder().action(R.id.action_finishCreateGift)
.navigateInFirstAttachedContext()
}
override fun openGiftsFlow() {
navigationBuilder().action(R.id.action_giftsFragment_to_giftsFlow)
.navigateInFirstAttachedContext()
}
override fun openSelectGiftAmount(assetPayload: AssetPayload) {
commonDelegate.openSelectGiftAmount(assetPayload)
}
override fun openConfirmCreateGift(payload: CreateGiftConfirmPayload) {
navigationBuilder().action(R.id.action_openConfirmCreateGift)
.setArgs(CreateGiftConfirmFragment.createPayload(payload))
.navigateInFirstAttachedContext()
}
override fun openGiftSharing(giftId: GiftId, isSecondOpen: Boolean) {
navigationBuilder().action(R.id.action_openShareGiftFragment)
.setArgs(ShareGiftFragment.createPayload(ShareGiftPayload(giftId, isSecondOpen)))
.navigateInFirstAttachedContext()
}
override fun openMainScreen() {
commonDelegate.openMain()
}
override fun openClaimGift(payload: ClaimGiftPayload) {
navigationBuilder().action(R.id.action_openClaimGiftFragment)
.setArgs(ClaimGiftFragment.createPayload(payload))
.navigateInFirstAttachedContext()
}
override fun openManageWallets() {
commonDelegate.openWallets()
}
}

Some files were not shown because too many files have changed in this diff Show More