From 853972903271cb994b30f68a32491d2c597b3b46 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 May 2022 16:39:56 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 2 + .../java/com/tangem/domain/DomainLayer.kt | 26 ++++++++ .../AddCustomTokenActionStateConverter.kt | 61 ------------------- .../redux/AddCustomTokenState.kt | 56 +++++++++++++++++ .../com/tangem/domain/redux/DomainStore.kt | 38 ++++++++---- .../domain/redux/state/StateConverter.kt | 23 ++++++- .../tangem/domain/redux/state/StateLogger.kt | 35 +++++++++++ .../domain/redux/state/StateObservables.kt | 36 ----------- 8 files changed, 166 insertions(+), 111 deletions(-) create mode 100644 domain/src/main/java/com/tangem/domain/DomainLayer.kt delete mode 100644 domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenActionStateConverter.kt create mode 100644 domain/src/main/java/com/tangem/domain/redux/state/StateLogger.kt delete mode 100644 domain/src/main/java/com/tangem/domain/redux/state/StateObservables.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index e4254151fc..28bf6c2485 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -7,6 +7,7 @@ import com.google.firebase.remoteconfig.ktx.remoteConfig import com.google.firebase.remoteconfig.ktx.remoteConfigSettings import com.tangem.Log import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder +import com.tangem.domain.DomainLayer import com.tangem.network.common.MoshiConverter import com.tangem.tap.common.analytics.GlobalAnalyticsHandler import com.tangem.tap.common.images.PicassoHelper @@ -46,6 +47,7 @@ class TapApplication : Application() { override fun onCreate() { super.onCreate() + DomainLayer.init() if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) Firebase.remoteConfig.setConfigSettingsAsync(remoteConfigSettings { diff --git a/domain/src/main/java/com/tangem/domain/DomainLayer.kt b/domain/src/main/java/com/tangem/domain/DomainLayer.kt new file mode 100644 index 0000000000..f83049dbf8 --- /dev/null +++ b/domain/src/main/java/com/tangem/domain/DomainLayer.kt @@ -0,0 +1,26 @@ +package com.tangem.domain + +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState +import com.tangem.domain.redux.state.ActionStateLoggerImpl + +/** +[REDACTED_AUTHOR] + */ +object DomainLayer { + internal val actionStateLogger = ActionStateLoggerImpl() + + var onInitComplete: ((DomainException?) -> Unit)? = null + + fun init(data: Map? = null) { + initActionStateLogger() + + onInitComplete?.invoke(null) + } + + private fun initActionStateLogger() { + val factory = actionStateLogger.actionStateConvertersFactory + + factory.addConverter(AddCustomTokenAction::class.java, AddCustomTokenState.Converter()) + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenActionStateConverter.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenActionStateConverter.kt deleted file mode 100644 index 196d7eafc1..0000000000 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenActionStateConverter.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.tangem.domain.features.addCustomToken - -import com.tangem.common.json.MoshiJsonConverter -import com.tangem.domain.common.form.FieldToJsonConverter -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState -import com.tangem.domain.redux.DomainState -import com.tangem.domain.redux.state.StringActionConverter -import org.rekotlin.Action - -class AddCustomTokenActionStateConverter : StringActionConverter { - private val jsonConverter: MoshiJsonConverter = MoshiJsonConverter.INSTANCE - private var builder: StringBuilder = StringBuilder() - - override fun convert(action: Action, stateHolder: DomainState): String? { - val action = (action as? AddCustomTokenAction) ?: return null - - val state = stateHolder.addCustomTokensState - val fieldConverter = FieldToJsonConverter(listOf( - CustomTokenFieldId.ContractAddress, - CustomTokenFieldId.Network, - CustomTokenFieldId.Name, - CustomTokenFieldId.Symbol, - CustomTokenFieldId.Decimals, - CustomTokenFieldId.DerivationPath, - ), jsonConverter) - state.visitDataConverter(fieldConverter) - val errors = state.formErrors.map { - "${it.key}: ${it.value::class.java.simpleName}" - } - val warnings = state.warnings.map { it::class.java.simpleName } - - printAction(action, state) - printStateValue("fields", fieldConverter.getConvertedData()) - printStateValue("fieldErrors", toJson(errors)) - printStateValue("warnings", toJson(warnings)) - printStateValue("screenState", toJson(state.screenState)) - printMessage("------------------------------------------------------") - - val printed = builder.toString() - builder = StringBuilder() - - return printed - } - - private fun printStateValue(name: String, value: String) { - printMessage("$name: $value") - } - - private fun printAction(action: AddCustomTokenAction, state: AddCustomTokenState) { - printMessage("action: $action, state: ${state::class.java.simpleName}") - } - - private fun toJson(value: Any): String { - return jsonConverter.prettyPrint(value) - } - - private fun printMessage(message: String) { - builder.append("$message\n") - } -} \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt index 2e19da5307..8d9714ce91 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt @@ -3,6 +3,7 @@ package com.tangem.domain.features.addCustomToken.redux import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.DerivationStyle import com.tangem.common.card.FirmwareVersion +import com.tangem.common.json.MoshiJsonConverter import com.tangem.domain.AddCustomTokenError import com.tangem.domain.DomainWrapped import com.tangem.domain.common.extensions.SolanaAvailable @@ -10,7 +11,10 @@ import com.tangem.domain.common.extensions.SolanaTokensAvailable import com.tangem.domain.common.form.* import com.tangem.domain.features.addCustomToken.* import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* +import com.tangem.domain.redux.DomainState import com.tangem.domain.redux.domainStore +import com.tangem.domain.redux.state.StringActionStateConverter +import org.rekotlin.Action import org.rekotlin.StateType data class AddCustomTokenState( @@ -239,6 +243,58 @@ data class AddCustomTokenState( ) } } + + class Converter : StringActionStateConverter { + private val jsonConverter: MoshiJsonConverter = MoshiJsonConverter.INSTANCE + private var builder: StringBuilder = StringBuilder() + + override fun convert(action: Action, stateHolder: DomainState): String? { + val action = (action as? AddCustomTokenAction) ?: return null + + val state = stateHolder.addCustomTokensState + val fieldConverter = FieldToJsonConverter(listOf( + ContractAddress, + Network, + Name, + Symbol, + Decimals, + DerivationPath, + ), jsonConverter) + state.visitDataConverter(fieldConverter) + val errors = state.formErrors.map { + "${it.key}: ${it.value::class.java.simpleName}" + } + val warnings = state.warnings.map { it::class.java.simpleName } + + printAction(action, state) + printStateValue("fields", fieldConverter.getConvertedData()) + printStateValue("fieldErrors", toJson(errors)) + printStateValue("warnings", toJson(warnings)) + printStateValue("screenState", toJson(state.screenState)) + printMessage("------------------------------------------------------") + + val printed = builder.toString() + builder = StringBuilder() + + return printed + } + + private fun printStateValue(name: String, value: String) { + printMessage("$name: $value") + } + + private fun printAction(action: AddCustomTokenAction, state: AddCustomTokenState) { + printMessage("action: $action, state: ${state::class.java.simpleName}") + } + + private fun toJson(value: Any): String { + return jsonConverter.prettyPrint(value) + } + + private fun printMessage(message: String) { + builder.append("$message\n") + } + } } enum class CustomTokenType { diff --git a/domain/src/main/java/com/tangem/domain/redux/DomainStore.kt b/domain/src/main/java/com/tangem/domain/redux/DomainStore.kt index 78bc7cf624..703ca18a88 100644 --- a/domain/src/main/java/com/tangem/domain/redux/DomainStore.kt +++ b/domain/src/main/java/com/tangem/domain/redux/DomainStore.kt @@ -1,8 +1,9 @@ package com.tangem.domain.redux +import com.tangem.domain.DomainLayer import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenHub import com.tangem.domain.redux.global.DomainGlobalHub -import com.tangem.domain.redux.state.observeReducedStates +import org.rekotlin.Action import org.rekotlin.Store /** @@ -18,16 +19,27 @@ private val RE_STORE_HUBS: List> = listOf( val domainStore = Store( state = DomainState(), middleware = RE_STORE_HUBS.map { it.getMiddleware() }, - reducer = { action, state -> - requireNotNull(state) - - // we can examine the store state after each change by reducer - val reducedSates = RE_STORE_HUBS.mapNotNull { - val reducedState = it.reduce(action, state) - if (reducedState == state) null else Pair(action, reducedState) - } - observeReducedStates(reducedSates) - - if (reducedSates.isEmpty()) state else reducedSates.last().second - } + reducer = { action, state -> reduce(action, state) } ) + +private fun reduce(action: Action, domainState: DomainState?): DomainState { + requireNotNull(domainState) + + // we can examine the store state after each change by reducer + var assembleReducedDomainState: DomainState = domainState + val reducedStatesByAction = mutableListOf>() + + RE_STORE_HUBS.forEach { + val reducedState = it.reduce(action, assembleReducedDomainState) + + assembleReducedDomainState = if (reducedState != assembleReducedDomainState) { + reducedStatesByAction.add(action to assembleReducedDomainState) + reducedState + } else { + assembleReducedDomainState + } + } + DomainLayer.actionStateLogger.log(reducedStatesByAction) + + return assembleReducedDomainState +} diff --git a/domain/src/main/java/com/tangem/domain/redux/state/StateConverter.kt b/domain/src/main/java/com/tangem/domain/redux/state/StateConverter.kt index f6a13aa10f..8e99e0f538 100644 --- a/domain/src/main/java/com/tangem/domain/redux/state/StateConverter.kt +++ b/domain/src/main/java/com/tangem/domain/redux/state/StateConverter.kt @@ -1,5 +1,6 @@ package com.tangem.domain.redux.state +import com.tangem.domain.redux.DomainState import org.rekotlin.Action /** @@ -9,6 +10,26 @@ interface StringStateConverter { fun convert(stateHolder: StateHolder): String } -interface StringActionConverter { +interface StringActionStateConverter { fun convert(action: Action, stateHolder: StateHolder): String? +} + +class ActionStateConvertersFactory { + private val stateConverters = mutableMapOf, StringActionStateConverter>() + + fun addConverter(classOfAction: Class, converter: StringActionStateConverter) { + stateConverters[classOfAction] = converter + } + + fun getConverter(action: Action): StringActionStateConverter? { + val converter = stateConverters.firstNotNullOfOrNull { (classOfAction, converter) -> + if (classOfAction.isAssignableFrom(action::class.java)) { + converter + } else { + null + } + } ?: return null + + return converter + } } \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/redux/state/StateLogger.kt b/domain/src/main/java/com/tangem/domain/redux/state/StateLogger.kt new file mode 100644 index 0000000000..576d398ea8 --- /dev/null +++ b/domain/src/main/java/com/tangem/domain/redux/state/StateLogger.kt @@ -0,0 +1,35 @@ +package com.tangem.domain.redux.state + +import com.tangem.domain.features.BuildConfig +import com.tangem.domain.redux.DomainState +import org.rekotlin.Action +import timber.log.Timber + +/** +[REDACTED_AUTHOR] + * Use it only in debug mode! + */ +internal interface ActionStateLogger { + fun log(reducedSates: List>) +} + +internal class ActionStateLoggerImpl : ActionStateLogger { + + val actionStateConvertersFactory = ActionStateConvertersFactory() + + override fun log(reducedSates: List>) { + if (!BuildConfig.LOG_ENABLED) return + + logStates(reducedSates) + } + + private fun logStates(reducedSates: List>) { + reducedSates.forEach { (action, domainState) -> + val messageToPrint = actionStateConvertersFactory.getConverter(action) + ?.convert(action, domainState) + ?: return@forEach + + Timber.d(messageToPrint) + } + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/redux/state/StateObservables.kt b/domain/src/main/java/com/tangem/domain/redux/state/StateObservables.kt deleted file mode 100644 index 2195ec8645..0000000000 --- a/domain/src/main/java/com/tangem/domain/redux/state/StateObservables.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.tangem.domain.redux.state - -import com.tangem.domain.features.addCustomToken.AddCustomTokenActionStateConverter -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction -import com.tangem.domain.redux.DomainState -import org.rekotlin.Action -import timber.log.Timber - -/** -[REDACTED_AUTHOR] - * Use it only in debug mode! - */ -internal fun observeReducedStates(reducedSates: List>) { - // we can add any logic to watch for changes of actions, states, etc. - val isDebugMode = true - if (!isDebugMode) return - - logStates(reducedSates) -} - -private fun logStates(reducedSates: List>) { - reducedSates.forEach { - val converter = stateConverters.firstNotNullOfOrNull { entry -> - if (entry.key.isAssignableFrom(it.first::class.java)) entry.value else null - } ?: return@forEach - - val messageToPrint = converter.convert(it.first, it.second) ?: return@forEach - - Timber.d(messageToPrint) - } -} - -// TODO: refactoring: mutate to factory -private val stateConverters = mutableMapOf( - AddCustomTokenAction::class.java to AddCustomTokenActionStateConverter() -) \ No newline at end of file