Updated on 2026-08-14
This commit is contained in:
parent
cf7d5814d6
commit
10ba20b045
30 changed files with 358 additions and 264 deletions
|
|
@ -4,7 +4,7 @@ import com.tangem.TangemSdkLogger
|
|||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.datasource.local.token.UserTokensStore
|
||||
|
|
@ -47,7 +47,7 @@ interface ApplicationEntryPoint {
|
|||
|
||||
fun getAppStateHolder(): AppStateHolder
|
||||
|
||||
fun getAssetReader(): AssetReader
|
||||
fun getAssetLoader(): AssetLoader
|
||||
|
||||
fun getFeatureTogglesManager(): FeatureTogglesManager
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import com.tangem.core.analytics.filter.OneTimeEventFilter
|
|||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.config.FeaturesLocalLoader
|
||||
import com.tangem.datasource.config.models.Config
|
||||
|
|
@ -92,8 +92,8 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
private val configManager: ConfigManager
|
||||
get() = entryPoint.getConfigManager()
|
||||
|
||||
private val assetReader: AssetReader
|
||||
get() = entryPoint.getAssetReader()
|
||||
private val assetLoader: AssetLoader
|
||||
get() = entryPoint.getAssetLoader()
|
||||
|
||||
private val featureTogglesManager: FeatureTogglesManager
|
||||
get() = entryPoint.getFeatureTogglesManager()
|
||||
|
|
@ -217,10 +217,12 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
// [REDACTED_JIRA]
|
||||
runBlocking {
|
||||
featureTogglesManager.init()
|
||||
initConfigManager(
|
||||
loader = FeaturesLocalLoader(assetLoader, BuildConfig.ENVIRONMENT),
|
||||
onComplete = ::initWithConfigDependency,
|
||||
)
|
||||
}
|
||||
|
||||
val configLoader = FeaturesLocalLoader(assetReader, MoshiConverter.sdkMoshi, BuildConfig.ENVIRONMENT)
|
||||
initConfigManager(configLoader, ::initWithConfigDependency)
|
||||
initWarningMessagesManager()
|
||||
|
||||
loadNativeLibraries()
|
||||
|
|
@ -290,7 +292,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
System.loadLibrary("TrustWalletCore")
|
||||
}
|
||||
|
||||
private fun initConfigManager(loader: FeaturesLocalLoader, onComplete: (Config) -> Unit) {
|
||||
private suspend fun initConfigManager(loader: FeaturesLocalLoader, onComplete: (Config) -> Unit) {
|
||||
configManager.load(loader) { config ->
|
||||
store.dispatch(GlobalAction.SetConfigManager(configManager))
|
||||
onComplete(config)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.datasource.di.SdkMoshi
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||
import com.tangem.domain.walletmanager.DefaultWalletManagersFacade
|
||||
|
|
@ -23,15 +21,13 @@ internal object WalletManagersFacadeModule {
|
|||
fun provideWalletManagersFacade(
|
||||
walletManagersStore: WalletManagersStore,
|
||||
userWalletsStore: UserWalletsStore,
|
||||
assetReader: AssetReader,
|
||||
@SdkMoshi moshi: Moshi,
|
||||
assetLoader: AssetLoader,
|
||||
blockchainSDKFactory: BlockchainSDKFactory,
|
||||
): WalletManagersFacade {
|
||||
return DefaultWalletManagersFacade(
|
||||
walletManagersStore = walletManagersStore,
|
||||
userWalletsStore = userWalletsStore,
|
||||
assetReader = assetReader,
|
||||
moshi = moshi,
|
||||
assetLoader = assetLoader,
|
||||
blockchainSDKFactory = blockchainSDKFactory,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,40 +1,36 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.Message
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.KeyPair
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class TwinCardsManager(
|
||||
card: CardDTO,
|
||||
assetReader: AssetReader,
|
||||
) {
|
||||
class TwinCardsManager(card: CardDTO) {
|
||||
private val firstCardId: String = card.cardId
|
||||
private val publicKey: String = card.issuer.publicKey.toHexString()
|
||||
|
||||
private var currentCardPublicKey: String? = null
|
||||
private var secondCardPublicKey: String? = null
|
||||
|
||||
private val issuerKeyPair: KeyPair = getIssuerKeys(assetReader, card.issuer.publicKey.toHexString())
|
||||
private val issuerKeyPairFlow = MutableStateFlow<KeyPair?>(value = null)
|
||||
|
||||
suspend fun createFirstWallet(message: Message): CompletionResult<CreateWalletResponse> {
|
||||
val response = tangemSdkManager.createFirstTwinWallet(
|
||||
cardId = firstCardId,
|
||||
initialMessage = message,
|
||||
)
|
||||
when (response) {
|
||||
is CompletionResult.Success -> currentCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
is CompletionResult.Failure -> {}
|
||||
val response = tangemSdkManager.createFirstTwinWallet(cardId = firstCardId, initialMessage = message)
|
||||
|
||||
if (response is CompletionResult.Success) {
|
||||
currentCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
|
@ -46,58 +42,51 @@ class TwinCardsManager(
|
|||
val response = tangemSdkManager.createSecondTwinWallet(
|
||||
firstPublicKey = currentCardPublicKey!!,
|
||||
firstCardId = firstCardId,
|
||||
issuerKeys = issuerKeyPair,
|
||||
issuerKeys = getIssuerKeys(),
|
||||
preparingMessage = preparingMessage,
|
||||
creatingWalletMessage = creatingWalletMessage,
|
||||
initialMessage = initialMessage,
|
||||
)
|
||||
|
||||
when (response) {
|
||||
is CompletionResult.Success -> {
|
||||
secondCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
}
|
||||
is CompletionResult.Failure -> {}
|
||||
if (response is CompletionResult.Success) {
|
||||
secondCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
suspend fun complete(message: Message): Result<ScanResponse> {
|
||||
val response = tangemSdkManager.finalizeTwin(
|
||||
secondCardPublicKey = secondCardPublicKey!!.hexToBytes(),
|
||||
issuerKeyPair = issuerKeyPair,
|
||||
issuerKeyPair = getIssuerKeys(),
|
||||
cardId = firstCardId,
|
||||
initialMessage = message,
|
||||
)
|
||||
|
||||
return when (response) {
|
||||
is CompletionResult.Success -> Result.Success(response.data)
|
||||
is CompletionResult.Failure -> Result.fromTangemSdkError(response.error)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun getIssuerKeys(reader: AssetReader, publicKey: String): KeyPair {
|
||||
val issuer = getIssuers(reader).first { it.publicKey == publicKey }
|
||||
return KeyPair(
|
||||
publicKey = issuer.publicKey.hexToBytes(),
|
||||
privateKey = issuer.privateKey.hexToBytes(),
|
||||
)
|
||||
}
|
||||
private suspend fun getIssuerKeys(): KeyPair {
|
||||
issuerKeyPairFlow.value?.let { return it }
|
||||
|
||||
private fun getAdapter(): JsonAdapter<List<Issuer>> {
|
||||
return MoshiConverter.sdkMoshi.adapter(
|
||||
Types.newParameterizedType(List::class.java, Issuer::class.java),
|
||||
)
|
||||
}
|
||||
val assetLoader = store.inject(DaggerGraphState::assetLoader)
|
||||
val issuer = assetLoader.loadList<Issuer>(fileName = ISSUERS_FILE_NAME)
|
||||
.first { it.publicKey == publicKey }
|
||||
|
||||
@Deprecated(message = "Use AssetReader instead")
|
||||
private fun getIssuers(reader: AssetReader): List<Issuer> {
|
||||
val file = reader.readJson(fileName = "tangem-app-config/issuers")
|
||||
return getAdapter().fromJson(file)!!
|
||||
return KeyPair(
|
||||
publicKey = issuer.publicKey.hexToBytes(),
|
||||
privateKey = issuer.privateKey.hexToBytes(),
|
||||
).also {
|
||||
issuerKeyPairFlow.value = it
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val ISSUERS_FILE_NAME = "tangem-app-config/issuers"
|
||||
}
|
||||
}
|
||||
|
||||
private class Issuer(
|
||||
val privateKey: String,
|
||||
val publicKey: String,
|
||||
)
|
||||
private class Issuer(val privateKey: String, val publicKey: String)
|
||||
|
|
@ -3,7 +3,6 @@ package com.tangem.tap.features.onboarding.products.twins.redux
|
|||
import com.tangem.Message
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
|
|
@ -27,7 +26,7 @@ sealed class TwinCardsAction : Action {
|
|||
}
|
||||
|
||||
sealed class Wallet : TwinCardsAction() {
|
||||
data class LaunchFirstStep(val initialMessage: Message, val reader: AssetReader) : TwinCardsAction()
|
||||
data class LaunchFirstStep(val initialMessage: Message) : TwinCardsAction()
|
||||
data class LaunchSecondStep(
|
||||
val initialMessage: Message,
|
||||
val preparingMessage: Message,
|
||||
|
|
|
|||
|
|
@ -165,10 +165,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
}
|
||||
}
|
||||
is TwinCardsAction.Wallet.LaunchFirstStep -> {
|
||||
val manager = TwinCardsManager(
|
||||
card = getScanResponse().card,
|
||||
assetReader = action.reader,
|
||||
)
|
||||
val manager = TwinCardsManager(card = getScanResponse().card)
|
||||
store.dispatch(TwinCardsAction.CardsManager.Set(manager))
|
||||
|
||||
scope.launch {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import com.tangem.common.extensions.VoidCallback
|
|||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.ShareElement
|
||||
import com.tangem.core.ui.extensions.setStatusBarColor
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.models.Artwork
|
||||
|
|
@ -35,15 +34,11 @@ import com.tangem.tap.store
|
|||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.LayoutOnboardingContainerTopBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@AndroidEntryPoint
|
||||
internal class OnboardingTwinsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
||||
|
||||
@Inject
|
||||
lateinit var assetReader: AssetReader
|
||||
|
||||
private val mainBinding by lazy { binding.vMain }
|
||||
private var previousStep: TwinCardsStep = TwinCardsStep.None
|
||||
|
||||
|
|
@ -260,7 +255,6 @@ internal class OnboardingTwinsFragment : BaseOnboardingFragment<TwinCardsState>(
|
|||
store.dispatch(
|
||||
TwinCardsAction.Wallet.LaunchFirstStep(
|
||||
initialMessage = Message(getString(R.string.twins_recreate_title_format, twinIndexNumber)),
|
||||
reader = assetReader,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.TangemSdkLogger
|
|||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.navigation.email.EmailSender
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
|
||||
import com.tangem.domain.apptheme.repository.AppThemeModeRepository
|
||||
|
|
@ -77,6 +78,7 @@ data class DaggerGraphState(
|
|||
val blockchainSDKFactory: BlockchainSDKFactory? = null,
|
||||
val emailSender: EmailSender? = null,
|
||||
val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase? = null,
|
||||
val assetLoader: AssetLoader? = null,
|
||||
val detailsFeatureToggles: DetailsFeatureToggles? = null,
|
||||
val detailsEntryPoint: DetailsEntryPoint? = null,
|
||||
) : StateType
|
||||
|
|
@ -69,4 +69,9 @@ dependencies {
|
|||
implementation(deps.room.runtime)
|
||||
implementation(deps.room.ktx)
|
||||
kapt(deps.room.compiler)
|
||||
|
||||
testImplementation(deps.test.coroutine)
|
||||
testImplementation(deps.test.junit)
|
||||
testImplementation(deps.test.mockk)
|
||||
testImplementation(deps.test.truth)
|
||||
}
|
||||
|
|
@ -5,27 +5,31 @@ import com.squareup.moshi.Types
|
|||
import com.squareup.moshi.adapter
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.runCatching
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Asset file loader
|
||||
*
|
||||
* @property assetReader asset reader
|
||||
* @property moshi moshi
|
||||
*
|
||||
* @see <a href = "https://www.notion.so/tangem/Assets-e045dd890413413faf34ce07ae47ff56">Documentation</a>
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Singleton
|
||||
class AssetLoader @Inject constructor(
|
||||
val assetReader: AssetReader,
|
||||
@NetworkMoshi val moshi: Moshi,
|
||||
val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
/** Load content [Content] of asset file [fileName] */
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
suspend inline fun <reified Content> load(fileName: String): Content? {
|
||||
return runCatching(dispatchers.io) {
|
||||
val json = assetReader.readJson(fileName = fileName)
|
||||
return runCatching {
|
||||
val json = assetReader.read(fullFileName = "$fileName.json")
|
||||
|
||||
moshi.adapter<Content>().fromJson(json)
|
||||
}
|
||||
|
|
@ -43,8 +47,8 @@ class AssetLoader @Inject constructor(
|
|||
|
||||
/** Load list [V] values of asset file [fileName] */
|
||||
suspend inline fun <reified V> loadList(fileName: String): List<V> {
|
||||
return runCatching(dispatchers.io) {
|
||||
val json = assetReader.readJson(fileName = fileName)
|
||||
return runCatching {
|
||||
val json = assetReader.read(fullFileName = "$fileName.json")
|
||||
|
||||
val type = Types.newParameterizedType(List::class.java, V::class.java)
|
||||
val adapter = moshi.adapter<List<V>>(type)
|
||||
|
|
@ -65,8 +69,8 @@ class AssetLoader @Inject constructor(
|
|||
|
||||
/** Load map [String] keys and [V] values of asset file [fileName] */
|
||||
suspend inline fun <reified V> loadMap(fileName: String): Map<String, V> {
|
||||
return runCatching(dispatchers.io) {
|
||||
val json = assetReader.readJson(fileName = fileName)
|
||||
return runCatching {
|
||||
val json = assetReader.read(fullFileName = "$fileName.json")
|
||||
|
||||
val type = Types.newParameterizedType(Map::class.java, String::class.java, V::class.java)
|
||||
val adapter = moshi.adapter<Map<String, V>>(type)
|
||||
|
|
|
|||
|
|
@ -1,27 +1,23 @@
|
|||
package com.tangem.datasource.asset.reader
|
||||
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import android.content.res.AssetManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStream
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Implementation of asset file reader
|
||||
*
|
||||
* @property context application context
|
||||
* @property assetManager asset manager
|
||||
* @property dispatchers dispatchers
|
||||
*/
|
||||
internal class AndroidAssetReader @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
internal class AndroidAssetReader(
|
||||
private val assetManager: AssetManager,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : AssetReader {
|
||||
|
||||
override fun readJson(fileName: String): String {
|
||||
return openFile("$fileName.json")
|
||||
.bufferedReader()
|
||||
override suspend fun read(fullFileName: String): String = withContext(dispatchers.io) {
|
||||
assetManager.open(fullFileName).bufferedReader()
|
||||
.use(BufferedReader::readText)
|
||||
}
|
||||
|
||||
override fun openFile(file: String): InputStream {
|
||||
return context.assets.open(file)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
package com.tangem.datasource.asset.reader
|
||||
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* Asset file reader
|
||||
*
|
||||
* @see <a href = "https://www.notion.so/tangem/Assets-e045dd890413413faf34ce07ae47ff56">Documentation</a>
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface AssetReader {
|
||||
|
||||
/** Read content of json file [fileName] from asset */
|
||||
fun readJson(fileName: String): String
|
||||
|
||||
/** Open a file [file] from asset as InputStream */
|
||||
fun openFile(file: String): InputStream
|
||||
/**
|
||||
* Read content of file from assets
|
||||
*
|
||||
* @param fullFileName name of file with extension. Example: file.json
|
||||
*/
|
||||
suspend fun read(fullFileName: String): String
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ interface ConfigManager {
|
|||
|
||||
val config: Config
|
||||
|
||||
fun load(configLoader: Loader<ConfigModel>, onComplete: ((config: Config) -> Unit)? = null)
|
||||
suspend fun load(configLoader: Loader<ConfigModel>, onComplete: ((config: Config) -> Unit)? = null)
|
||||
|
||||
fun turnOff(name: String)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager {
|
|||
|
||||
private var defaultConfig = Config()
|
||||
|
||||
override fun load(configLoader: Loader<ConfigModel>, onComplete: ((config: Config) -> Unit)?) {
|
||||
override suspend fun load(configLoader: Loader<ConfigModel>, onComplete: ((config: Config) -> Unit)?) {
|
||||
configLoader.load { configModel ->
|
||||
setupFeature(configModel.features)
|
||||
setupConfigValues(configModel.configValues)
|
||||
|
|
|
|||
|
|
@ -1,39 +1,25 @@
|
|||
package com.tangem.datasource.config
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.config.models.ConfigModel
|
||||
import com.tangem.datasource.config.models.ConfigValueModel
|
||||
import com.tangem.datasource.config.models.FeatureModel
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Deprecated(message = "Use AssetReader instead")
|
||||
class FeaturesLocalLoader(
|
||||
private val assetReader: AssetReader,
|
||||
private val moshi: Moshi,
|
||||
private val assetLoader: AssetLoader,
|
||||
buildEnvironment: String,
|
||||
) : Loader<ConfigModel> {
|
||||
|
||||
private val featuresName = "features_$buildEnvironment"
|
||||
private val configValuesName = "tangem-app-config/config_$buildEnvironment"
|
||||
|
||||
override fun load(onComplete: (ConfigModel) -> Unit) {
|
||||
val config = try {
|
||||
val featureAdapter: JsonAdapter<FeatureModel> = moshi.adapter(FeatureModel::class.java)
|
||||
val valuesAdapter: JsonAdapter<ConfigValueModel> = moshi.adapter(ConfigValueModel::class.java)
|
||||
|
||||
val jsonFeatures = assetReader.readJson(featuresName)
|
||||
val jsonConfigValues = assetReader.readJson(configValuesName)
|
||||
|
||||
ConfigModel(featureAdapter.fromJson(jsonFeatures), valuesAdapter.fromJson(jsonConfigValues))
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex)
|
||||
ConfigModel.empty()
|
||||
}
|
||||
onComplete(config)
|
||||
override suspend fun load(onComplete: (ConfigModel) -> Unit) {
|
||||
ConfigModel(
|
||||
features = assetLoader.load<FeatureModel>(fileName = featuresName),
|
||||
configValues = assetLoader.load<ConfigValueModel>(fileName = configValuesName),
|
||||
).also(onComplete)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,5 +4,5 @@ package com.tangem.datasource.config
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Loader<T> {
|
||||
fun load(onComplete: (T) -> Unit)
|
||||
suspend fun load(onComplete: (T) -> Unit)
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.asset.reader.AndroidAssetReader
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface AssetModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindAsserReader(androidAssetReader: AndroidAssetReader): AssetReader
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.datasource.asset.reader.AndroidAssetReader
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object AssetReaderModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun providesAsserReader(
|
||||
@ApplicationContext context: Context,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): AssetReader {
|
||||
return AndroidAssetReader(context.assets, dispatchers)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapter
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.testnet.DefaultTestnetTokensStorage
|
||||
import com.tangem.datasource.local.testnet.TestnetTokensStorage
|
||||
import dagger.Module
|
||||
|
|
@ -16,12 +14,11 @@ import javax.inject.Singleton
|
|||
*/
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object TestnetTokensStorageModule {
|
||||
internal object TestnetTokensStorageModule {
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesTestnetTokensStorage(assetReader: AssetReader, @SdkMoshi moshi: Moshi): TestnetTokensStorage {
|
||||
return DefaultTestnetTokensStorage(assetReader = assetReader, adapter = moshi.adapter())
|
||||
fun providesTestnetTokensStorage(assetLoader: AssetLoader): TestnetTokensStorage {
|
||||
return DefaultTestnetTokensStorage(assetLoader)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +1,21 @@
|
|||
package com.tangem.datasource.local.testnet
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.testnet.models.TestnetTokensConfig
|
||||
|
||||
/**
|
||||
* Default implementation for storing testnet tokens data
|
||||
*
|
||||
* @property assetReader file reader from assets
|
||||
* @property adapter adapter for parsing testnet tokens config
|
||||
* @property assetLoader asset loader
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultTestnetTokensStorage(
|
||||
private val assetReader: AssetReader,
|
||||
private val adapter: JsonAdapter<TestnetTokensConfig>,
|
||||
private val assetLoader: AssetLoader,
|
||||
) : TestnetTokensStorage {
|
||||
|
||||
@Deprecated(message = "Use AssetReader instead")
|
||||
override fun getConfig(): TestnetTokensConfig {
|
||||
return requireNotNull(
|
||||
value = adapter.fromJson(
|
||||
assetReader.readJson(fileName = LOCAL_CONFIG_PATH),
|
||||
),
|
||||
)
|
||||
override suspend fun getConfig(): TestnetTokensConfig {
|
||||
return requireNotNull(assetLoader.load<TestnetTokensConfig>(fileName = LOCAL_CONFIG_PATH))
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -10,5 +10,5 @@ import com.tangem.datasource.local.testnet.models.TestnetTokensConfig
|
|||
interface TestnetTokensStorage {
|
||||
|
||||
/** Get a testnet tokens data */
|
||||
fun getConfig(): TestnetTokensConfig
|
||||
suspend fun getConfig(): TestnetTokensConfig
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package com.tangem.datasource.asset.loader
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import com.squareup.moshi.adapter
|
||||
import com.tangem.datasource.api.express.models.response.Asset
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerifyOrder
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
class AssetLoaderTest {
|
||||
|
||||
private val assetReader = mockk<AssetReader>()
|
||||
private val moshi = mockk<Moshi>()
|
||||
private val assetLoader = AssetLoader(assetReader = assetReader, moshi = moshi)
|
||||
|
||||
@Test
|
||||
fun load() = runTest {
|
||||
everyReadingJson() returns assetObjectJson
|
||||
|
||||
val jsonAdapter = mockk<JsonAdapter<Asset>>()
|
||||
every { moshi.adapter<Asset>() } returns jsonAdapter
|
||||
every { jsonAdapter.fromJson(assetObjectJson) } returns assetObject
|
||||
|
||||
val actual = assetLoader.load<Asset>(fileName = JSON_FILE_NAME)
|
||||
|
||||
coVerifyOrder {
|
||||
assetReader.read("$JSON_FILE_NAME.json")
|
||||
jsonAdapter.fromJson(assetObjectJson)
|
||||
}
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(assetObject)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadList() = runTest {
|
||||
everyReadingJson() returns assetListJson
|
||||
|
||||
val jsonAdapter = mockk<JsonAdapter<List<Asset>>>()
|
||||
val types = Types.newParameterizedType(List::class.java, Asset::class.java)
|
||||
every { moshi.adapter<List<Asset>>(types) } returns jsonAdapter
|
||||
every { jsonAdapter.fromJson(assetListJson) } returns assetList
|
||||
|
||||
val actual = assetLoader.loadList<Asset>(fileName = JSON_FILE_NAME)
|
||||
|
||||
coVerifyOrder {
|
||||
assetReader.read("$JSON_FILE_NAME.json")
|
||||
jsonAdapter.fromJson(assetListJson)
|
||||
}
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(assetList)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadMap() = runTest {
|
||||
everyReadingJson() returns assetMapJson
|
||||
|
||||
val jsonAdapter = mockk<JsonAdapter<Map<String, Asset>>>()
|
||||
val types = Types.newParameterizedType(Map::class.java, String::class.java, Asset::class.java)
|
||||
every { moshi.adapter<Map<String, Asset>>(types) } returns jsonAdapter
|
||||
every { jsonAdapter.fromJson(assetMapJson) } returns assetMap
|
||||
|
||||
val actual = assetLoader.loadMap<Asset>(fileName = JSON_FILE_NAME)
|
||||
|
||||
coVerifyOrder {
|
||||
assetReader.read("$JSON_FILE_NAME.json")
|
||||
jsonAdapter.fromJson(assetMapJson)
|
||||
}
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(assetMap)
|
||||
}
|
||||
|
||||
private fun everyReadingJson() = coEvery { assetReader.read("$JSON_FILE_NAME.json") }
|
||||
|
||||
private companion object {
|
||||
|
||||
const val JSON_FILE_NAME = "config"
|
||||
|
||||
val assetObject = Asset(
|
||||
contractAddress = "0x0000000000000000000000000000000000000000",
|
||||
network = "Network",
|
||||
exchangeAvailable = true,
|
||||
)
|
||||
|
||||
val assetList = listOf(assetObject, assetObject)
|
||||
|
||||
val assetMap = mapOf("key1" to assetObject, "key2" to assetObject)
|
||||
|
||||
val assetObjectJson = """
|
||||
{
|
||||
"contractAddress": "${assetObject.contractAddress}",
|
||||
"network": "${assetObject.network}",
|
||||
"exchangeAvailable": ${assetObject.exchangeAvailable}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val assetListJson = """
|
||||
$assetObjectJson,
|
||||
$assetObjectJson
|
||||
""".trimIndent()
|
||||
|
||||
val assetMapJson = """
|
||||
{
|
||||
"key1": $assetObjectJson,
|
||||
"key2": $assetObjectJson
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.datasource.asset.reader
|
||||
|
||||
import android.content.res.AssetManager
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class AndroidAssetReaderTest {
|
||||
|
||||
private val assetManager = mockk<AssetManager>()
|
||||
private val assetReader = AndroidAssetReader(assetManager, TestingCoroutineDispatcherProvider())
|
||||
|
||||
@Test
|
||||
fun read_content() = runTest {
|
||||
every { assetManager.open(FILE_NAME) } returns json.byteInputStream()
|
||||
|
||||
val actual = assetReader.read(fullFileName = FILE_NAME)
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(json)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun read_error() = runTest {
|
||||
val exception = IOException("Error")
|
||||
every { assetManager.open(FILE_NAME) } throws exception
|
||||
|
||||
runCatching { assetReader.read(fullFileName = FILE_NAME) }
|
||||
.onSuccess { throw IllegalStateException("Error should be thrown") }
|
||||
.onFailure {
|
||||
Truth.assertThat(it).isEqualTo(exception)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
const val FILE_NAME = "file.json"
|
||||
|
||||
val json = """
|
||||
{
|
||||
"key": "value"
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ dependencies {
|
|||
|
||||
/** Core modules */
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.utils)
|
||||
|
||||
testImplementation(deps.test.coroutine)
|
||||
testImplementation(deps.test.junit)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
package com.tangem.core.featuretoggle.di
|
||||
|
||||
import android.content.Context
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapter
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.core.featuretoggle.manager.DevFeatureTogglesManager
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.core.featuretoggle.manager.ProdFeatureTogglesManager
|
||||
import com.tangem.core.featuretoggle.storage.LocalFeatureTogglesStorage
|
||||
import com.tangem.core.featuretoggle.version.DefaultVersionProvider
|
||||
import com.tangem.core.featuretoggles.BuildConfig
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -25,17 +22,12 @@ internal object FeatureTogglesManagerModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
fun provideFeatureTogglesManager(
|
||||
@ApplicationContext context: Context,
|
||||
assetReader: AssetReader,
|
||||
assetLoader: AssetLoader,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
): FeatureTogglesManager {
|
||||
val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build()
|
||||
val localFeatureTogglesStorage = LocalFeatureTogglesStorage(
|
||||
assetReader = assetReader,
|
||||
jsonAdapter = moshi.adapter(),
|
||||
)
|
||||
val localFeatureTogglesStorage = LocalFeatureTogglesStorage(assetLoader)
|
||||
val versionProvider = DefaultVersionProvider(context)
|
||||
|
||||
return if (BuildConfig.TESTER_MENU_ENABLED) {
|
||||
|
|
|
|||
|
|
@ -1,42 +1,28 @@
|
|||
package com.tangem.core.featuretoggle.storage
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.tangem.core.featuretoggle.storage.LocalFeatureTogglesStorage.Companion.LOCAL_CONFIG_PATH
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import timber.log.Timber
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/**
|
||||
* Storage implementation for storing local feature toggles.
|
||||
* Feature toggles are declared in file [LOCAL_CONFIG_PATH].
|
||||
*
|
||||
* @property assetReader asset reader
|
||||
* @property jsonAdapter adapter for parsing local json config
|
||||
* @property assetLoader asset loader
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class LocalFeatureTogglesStorage(
|
||||
private val assetReader: AssetReader,
|
||||
private val jsonAdapter: JsonAdapter<List<FeatureToggle>>,
|
||||
private val assetLoader: AssetLoader,
|
||||
) : FeatureTogglesStorage {
|
||||
|
||||
override var featureToggles: List<FeatureToggle> by Delegates.notNull()
|
||||
private set
|
||||
|
||||
@Deprecated(message = "Use AssetReader instead")
|
||||
override suspend fun init() {
|
||||
runCatching { requireNotNull(jsonAdapter.fromJson(assetReader.readJson(LOCAL_CONFIG_PATH))) }
|
||||
.onSuccess { featureToggles = it }
|
||||
.onFailure { Timber.e(LocalFeatureTogglesStorage::class.java.name, "Failed to parse $LOCAL_CONFIG_PATH") }
|
||||
featureToggles = assetLoader.loadList<FeatureToggle>(LOCAL_CONFIG_PATH)
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
fun getConfigPath() = LOCAL_CONFIG_PATH
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
fun getFeatureToggles(): Iterable<FeatureToggle> = featureToggles
|
||||
|
||||
private companion object {
|
||||
const val LOCAL_CONFIG_PATH: String = "configs/feature_toggles_config"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ package com.tangem.core.featuretoggle.storage
|
|||
import android.annotation.SuppressLint
|
||||
import com.google.common.truth.Truth
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verifyAll
|
||||
import io.mockk.verifyOrder
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import java.io.IOException
|
||||
|
|
@ -19,70 +19,69 @@ import java.io.IOException
|
|||
internal class LocalFeatureTogglesStorageTest {
|
||||
|
||||
private val assetReader = mockk<AssetReader>()
|
||||
private val moshi = mockk<Moshi>()
|
||||
private val jsonAdapter = mockk<JsonAdapter<List<FeatureToggle>>>()
|
||||
private val storage = LocalFeatureTogglesStorage(assetReader, jsonAdapter)
|
||||
|
||||
// Impossible to mockk AssetLoader because it implement inline functions
|
||||
private val assetLoader = AssetLoader(assetReader = assetReader, moshi = moshi)
|
||||
|
||||
private val storage = LocalFeatureTogglesStorage(assetLoader)
|
||||
|
||||
@Test
|
||||
fun `successfully initialize storage`() = runTest {
|
||||
coEvery { assetReader.readJson(storage.getConfigPath()) } returns json
|
||||
coEvery { jsonAdapter.fromJson(json) } returns featureToggles
|
||||
everyReadingJson() returns json
|
||||
everyCreatingMoshiAdapter() returns jsonAdapter
|
||||
everyMappingJson() returns featureToggles
|
||||
|
||||
storage.init()
|
||||
|
||||
verifyOrder {
|
||||
assetReader.readJson(storage.getConfigPath())
|
||||
coVerifyOrder {
|
||||
assetReader.read(CONFIG_FILE_NAME)
|
||||
jsonAdapter.fromJson(json)
|
||||
}
|
||||
|
||||
Truth.assertThat(storage.getFeatureToggles()).containsExactlyElementsIn(featureToggles)
|
||||
Truth.assertThat(storage.featureToggles).containsExactlyElementsIn(featureToggles)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `failure initialize storage if assetReader throws exception`() = runTest {
|
||||
coEvery { assetReader.readJson(storage.getConfigPath()) } returns json
|
||||
coEvery { jsonAdapter.fromJson(json) } throws IOException()
|
||||
everyReadingJson() returns json
|
||||
everyCreatingMoshiAdapter() returns jsonAdapter
|
||||
everyMappingJson() throws IOException()
|
||||
|
||||
storage.init()
|
||||
|
||||
verifyOrder {
|
||||
assetReader.readJson(storage.getConfigPath())
|
||||
coVerifyOrder {
|
||||
assetReader.read(CONFIG_FILE_NAME)
|
||||
jsonAdapter.fromJson(json)
|
||||
}
|
||||
|
||||
runCatching { storage.getFeatureToggles() }
|
||||
.onSuccess { throw IllegalStateException("featureToggles shouldn't be initialized") }
|
||||
.onFailure {
|
||||
Truth
|
||||
.assertThat(it)
|
||||
.hasMessageThat()
|
||||
.contains("Property featureToggles should be initialized before get.")
|
||||
|
||||
Truth.assertThat(it).isInstanceOf(IllegalStateException::class.java)
|
||||
}
|
||||
Truth.assertThat(storage.featureToggles).containsExactlyElementsIn(emptyList<FeatureToggle>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `failure initialize storage if jsonAdapter throws exception`() = runTest {
|
||||
coEvery { assetReader.readJson(storage.getConfigPath()) } throws IOException()
|
||||
everyReadingJson() throws IOException()
|
||||
|
||||
storage.init()
|
||||
|
||||
verifyOrder { assetReader.readJson(storage.getConfigPath()) }
|
||||
coVerifyOrder { assetReader.read(CONFIG_FILE_NAME) }
|
||||
verifyAll(inverse = true) { jsonAdapter.fromJson(any<String>()) }
|
||||
|
||||
runCatching { storage.getFeatureToggles() }
|
||||
.onSuccess { throw IllegalStateException("featureToggles shouldn't be initialized") }
|
||||
.onFailure {
|
||||
Truth
|
||||
.assertThat(it)
|
||||
.hasMessageThat()
|
||||
.contains("Property featureToggles should be initialized before get.")
|
||||
|
||||
Truth.assertThat(it).isInstanceOf(IllegalStateException::class.java)
|
||||
}
|
||||
Truth.assertThat(storage.featureToggles).containsExactlyElementsIn(emptyList<FeatureToggle>())
|
||||
}
|
||||
|
||||
private fun everyReadingJson() = coEvery { assetReader.read(CONFIG_FILE_NAME) }
|
||||
|
||||
private fun everyCreatingMoshiAdapter() = every {
|
||||
val types = Types.newParameterizedType(List::class.java, FeatureToggle::class.java)
|
||||
moshi.adapter<List<FeatureToggle>>(types)
|
||||
}
|
||||
|
||||
private fun everyMappingJson() = every { jsonAdapter.fromJson(json) }
|
||||
|
||||
private companion object {
|
||||
const val CONFIG_FILE_NAME = "configs/feature_toggles_config.json"
|
||||
|
||||
val json = """
|
||||
[
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import arrow.core.Either
|
|||
import arrow.core.raise.either
|
||||
import arrow.core.raise.ensureNotNull
|
||||
import arrow.core.right
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.blockchains.solana.RentProvider
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.address.Address
|
||||
|
|
@ -19,7 +18,7 @@ import com.tangem.blockchain.extensions.Result
|
|||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||
import com.tangem.domain.common.util.hasDerivation
|
||||
|
|
@ -31,6 +30,7 @@ import com.tangem.domain.transaction.models.AssetRequirementsCondition
|
|||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryState
|
||||
import com.tangem.domain.walletmanager.model.SmartContractMethod
|
||||
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
||||
import com.tangem.domain.walletmanager.utils.*
|
||||
import com.tangem.domain.walletmanager.utils.WalletManagerFactory
|
||||
|
|
@ -47,8 +47,7 @@ import java.util.EnumSet
|
|||
class DefaultWalletManagersFacade(
|
||||
private val walletManagersStore: WalletManagersStore,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
assetReader: AssetReader,
|
||||
moshi: Moshi,
|
||||
private val assetLoader: AssetLoader,
|
||||
blockchainSDKFactory: BlockchainSDKFactory,
|
||||
) : WalletManagersFacade {
|
||||
|
||||
|
|
@ -57,7 +56,6 @@ class DefaultWalletManagersFacade(
|
|||
private val walletManagerFactory by lazy { WalletManagerFactory(blockchainSDKFactory) }
|
||||
private val sdkTokenConverter by lazy { SdkTokenConverter() }
|
||||
private val txHistoryStateConverter by lazy { SdkTransactionHistoryStateConverter() }
|
||||
private val txHistoryItemConverter by lazy { SdkTransactionHistoryItemConverter(assetReader, moshi) }
|
||||
private val sdkPageConverter by lazy { SdkPageConverter() }
|
||||
private val cryptoCurrencyTypeConverter by lazy { CryptoCurrencyTypeConverter() }
|
||||
private val requirementsConditionConverter by lazy { SdkRequirementsConditionConverter() }
|
||||
|
|
@ -236,7 +234,8 @@ class DefaultWalletManagersFacade(
|
|||
is Result.Success -> PaginationWrapper(
|
||||
currentPage = sdkPageConverter.convert(page),
|
||||
nextPage = sdkPageConverter.convert(itemsResult.data.nextPage),
|
||||
items = txHistoryItemConverter.convertList(itemsResult.data.items),
|
||||
items = SdkTransactionHistoryItemConverter(smartContractMethods = readSmartContractMethods())
|
||||
.convertList(itemsResult.data.items),
|
||||
)
|
||||
is Result.Failure -> error(itemsResult.error.message ?: itemsResult.error.customMessage)
|
||||
}
|
||||
|
|
@ -616,4 +615,8 @@ class DefaultWalletManagersFacade(
|
|||
|
||||
walletManager.addTokens(tokensToAdd)
|
||||
}
|
||||
|
||||
private suspend fun readSmartContractMethods(): Map<String, SmartContractMethod> {
|
||||
return assetLoader.loadMap<SmartContractMethod>(fileName = "contract_methods")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,16 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.common.txhistory.TransactionHistoryItem
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.walletmanager.model.SmartContractMethod
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.common.txhistory.TransactionHistoryItem as SdkTransactionHistoryItem
|
||||
|
||||
internal class SdkTransactionHistoryItemConverter(
|
||||
assetReader: AssetReader,
|
||||
moshi: Moshi,
|
||||
smartContractMethods: Map<String, SmartContractMethod>,
|
||||
) : Converter<SdkTransactionHistoryItem, TxHistoryItem> {
|
||||
|
||||
private val typeConverter by lazy {
|
||||
SdkTransactionTypeConverter(assetReader, moshi)
|
||||
}
|
||||
private val typeConverter by lazy { SdkTransactionTypeConverter(smartContractMethods) }
|
||||
|
||||
override fun convert(value: SdkTransactionHistoryItem): TxHistoryItem = TxHistoryItem(
|
||||
txHash = value.txHash,
|
||||
|
|
|
|||
|
|
@ -1,30 +1,14 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.blockchain.common.txhistory.TransactionHistoryItem
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.walletmanager.model.SmartContractMethod
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class SdkTransactionTypeConverter(
|
||||
private val assetReader: AssetReader,
|
||||
private val moshi: Moshi,
|
||||
private val smartContractMethods: Map<String, SmartContractMethod>,
|
||||
) : Converter<TransactionHistoryItem.TransactionType, TxHistoryItem.TransactionType> {
|
||||
|
||||
private val adapter: JsonAdapter<Map<String, SmartContractMethod>> by lazy {
|
||||
moshi.adapter(
|
||||
Types.newParameterizedType(
|
||||
Map::class.java,
|
||||
String::class.java,
|
||||
SmartContractMethod::class.java,
|
||||
),
|
||||
)
|
||||
}
|
||||
private val smartContractMethods by lazy { readSmartContractMethods() }
|
||||
|
||||
override fun convert(value: TransactionHistoryItem.TransactionType): TxHistoryItem.TransactionType {
|
||||
return when (value) {
|
||||
is TransactionHistoryItem.TransactionType.ContractMethod ->
|
||||
|
|
@ -43,9 +27,4 @@ internal class SdkTransactionTypeConverter(
|
|||
else -> TxHistoryItem.TransactionType.Operation(name = methodName.replaceFirstChar { it.titlecase() })
|
||||
}
|
||||
}
|
||||
|
||||
private fun readSmartContractMethods(): Map<String, SmartContractMethod> {
|
||||
val json = assetReader.readJson("contract_methods")
|
||||
return adapter.fromJson(json) ?: emptyMap()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue