From cc66f0389592825f0b673e629b3a175f32fb755b Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 May 2022 15:39:37 +0300 Subject: [PATCH 1/4] Updated on 2026-08-14 --- domain/src/main/java/com/tangem/domain/DomainException.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/domain/src/main/java/com/tangem/domain/DomainException.kt b/domain/src/main/java/com/tangem/domain/DomainException.kt index b6fc362990..a2661db781 100644 --- a/domain/src/main/java/com/tangem/domain/DomainException.kt +++ b/domain/src/main/java/com/tangem/domain/DomainException.kt @@ -6,7 +6,9 @@ import com.tangem.common.module.ModuleException /** [REDACTED_AUTHOR] */ -sealed class AddCustomTokenException(override val message: String) : Throwable(message), ModuleException { +sealed class DomainException(override val message: String) : Throwable(message), ModuleException + +sealed class AddCustomTokenException(message: String) : DomainException(message) { data class SelectTokeNetworkException(val networkId: String) : AddCustomTokenException( "Unknown network [$networkId] should not be included in the network selection dialog." From 097a377af378b7b71f9a3bc799e43f6c9124083d Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 May 2022 16:37:13 +0300 Subject: [PATCH 2/4] Updated on 2026-08-14 --- app/build.gradle | 24 +++++++++---------- .../tangem/tap/domain/configurable/Loader.kt | 6 ++--- dependencies.gradle | 6 +++++ domain/build.gradle | 10 ++++++++ 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8771cec1c3..550a83ec13 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,11 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' - -ext.configVar = [ - configEnvironment: "CONFIG_ENVIRONMENT", - testActionEnabled: "TEST_ACTION_ENABLED", -] +apply from: '../dependencies.gradle' android { compileSdkVersion 31 @@ -19,7 +15,8 @@ android { targetSdkVersion 31 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - buildConfigField 'Boolean', configVar.testActionEnabled, 'false' + buildConfigField 'Boolean', environmentConfig.testActionEnabled, 'false' + buildConfigField 'Boolean', environmentConfig.logEnabled, 'false' } buildFeatures { compose true @@ -31,8 +28,9 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.debug - buildConfigField 'String', configVar.configEnvironment, '\"prod\"' - buildConfigField 'Boolean', configVar.testActionEnabled, 'false' + buildConfigField 'String', environmentConfig.environment, '\"prod\"' + buildConfigField 'Boolean', environmentConfig.testActionEnabled, 'false' + buildConfigField 'Boolean', environmentConfig.logEnabled, 'false' } debug { debuggable true @@ -42,16 +40,18 @@ android { firebaseCrashlytics { mappingFileUploadEnabled false } - buildConfigField 'String', configVar.configEnvironment, '\"dev\"' - buildConfigField 'Boolean', configVar.testActionEnabled, 'true' + buildConfigField 'String', environmentConfig.environment, '\"dev\"' + buildConfigField 'Boolean', environmentConfig.testActionEnabled, 'true' + buildConfigField 'Boolean', environmentConfig.logEnabled, 'true' } debug_beta { initWith release debuggable false versionNameSuffix "-beta" applicationIdSuffix ".debug" - buildConfigField 'String', configVar.configEnvironment, '\"prod\"' - buildConfigField 'Boolean', configVar.testActionEnabled, 'false' + buildConfigField 'String', environmentConfig.environment, '\"prod\"' + buildConfigField 'Boolean', environmentConfig.testActionEnabled, 'false' + buildConfigField 'Boolean', environmentConfig.logEnabled, 'false' } } diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt b/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt index 873b71c500..5354d0b219 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt @@ -9,8 +9,8 @@ interface Loader { fun load(onComplete: (T) -> Unit) companion object { - const val featuresName = "features_${BuildConfig.CONFIG_ENVIRONMENT}" - const val configValuesName = "tangem-app-config/config_${BuildConfig.CONFIG_ENVIRONMENT}" - const val warnings = "warnings_${BuildConfig.CONFIG_ENVIRONMENT}" + const val featuresName = "features_${BuildConfig.ENVIRONMENT}" + const val configValuesName = "tangem-app-config/config_${BuildConfig.ENVIRONMENT}" + const val warnings = "warnings_${BuildConfig.ENVIRONMENT}" } } \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index 5ceeadcc6e..d463106208 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -2,3 +2,9 @@ ext.versions = [ kotlin : '1.6.10', build_gradle: '7.1.3', ] + +ext.environmentConfig = [ + environment: "ENVIRONMENT", + testActionEnabled: "TEST_ACTION_ENABLED", + logEnabled : "LOG_ENABLED", +] \ No newline at end of file diff --git a/domain/build.gradle b/domain/build.gradle index 4b5b520869..b156723a15 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -3,6 +3,8 @@ plugins { id 'org.jetbrains.kotlin.android' } +apply from: '../dependencies.gradle' + android { compileSdk 31 @@ -12,6 +14,8 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" + buildConfigField 'Boolean', environmentConfig.testActionEnabled, 'false' + buildConfigField 'Boolean', environmentConfig.logEnabled, 'false' } buildTypes { @@ -19,13 +23,19 @@ android { debuggable false minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + buildConfigField 'Boolean', environmentConfig.testActionEnabled, 'false' + buildConfigField 'Boolean', environmentConfig.logEnabled, 'false' } debug { debuggable true minifyEnabled false + buildConfigField 'Boolean', environmentConfig.testActionEnabled, 'true' + buildConfigField 'Boolean', environmentConfig.logEnabled, 'true' } debug_beta { initWith release + buildConfigField 'Boolean', environmentConfig.testActionEnabled, 'true' + buildConfigField 'Boolean', environmentConfig.logEnabled, 'true' } } kotlinOptions { From a085c2998c4717ebd1c664054c3e9b15f6a47540 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 May 2022 16:39:21 +0300 Subject: [PATCH 3/4] Updated on 2026-08-14 --- .../addCustomToken/redux/AddCustomTokenHub.kt | 27 ++++++++------- .../com/tangem/domain/redux/ReStoreHub.kt | 34 ++++++------------- .../domain/redux/global/DomainGlobalHub.kt | 30 ++++++++++------ 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt index 19e2dcc43c..73db7237db 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt @@ -18,11 +18,9 @@ import com.tangem.domain.common.form.* import com.tangem.domain.features.addCustomToken.* import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.* -import com.tangem.domain.redux.BaseStoreHub -import com.tangem.domain.redux.DomainState -import com.tangem.domain.redux.dispatchOnMain -import com.tangem.domain.redux.domainStore +import com.tangem.domain.redux.* import com.tangem.domain.redux.global.DomainGlobalAction +import com.tangem.domain.redux.global.DomainGlobalState import com.tangem.network.api.tangemTech.CoinsResponse import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -38,6 +36,8 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT private val hubState: AddCustomTokenState get() = domainStore.state.addCustomTokensState + override fun getReducer(): ReStoreReducer = AddCustomTokenReducer(globalState) + override fun getHubState(storeState: DomainState): AddCustomTokenState = hubState override fun updateStoreState(storeState: DomainState, newHubState: AddCustomTokenState): DomainState { @@ -515,10 +515,17 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT dispatchOnMain(Warning.Replace(setOf(this), setOf(to))) } -// private suspend fun Warning.replace(replace: Boolean, to: Warning) { -// if (replace) dispatchOnMain(Warning.Replace(setOf(this), setOf(to))) -// } + @Throws + private fun throwUnAppropriateInitialization(objName: String) { + throw AddCustomTokenException.UnAppropriateInitializationException( + "AddCustomTokenHub", "$objName must be not NULL" + ) + } +} +private class AddCustomTokenReducer( + private val globalState: DomainGlobalState, +) : ReStoreReducer { override fun reduceAction(action: Action, state: AddCustomTokenState): AddCustomTokenState { return when (action) { @@ -679,10 +686,4 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT return state.copy(form = Form(state.form.fieldList)) } - @Throws - private fun throwUnAppropriateInitialization(objName: String) { - throw AddCustomTokenException.UnAppropriateInitializationException( - "AddCustomTokenHub", "$objName must be not NULL" - ) - } } \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/redux/ReStoreHub.kt b/domain/src/main/java/com/tangem/domain/redux/ReStoreHub.kt index 72aa9ca605..06775baafd 100644 --- a/domain/src/main/java/com/tangem/domain/redux/ReStoreHub.kt +++ b/domain/src/main/java/com/tangem/domain/redux/ReStoreHub.kt @@ -17,14 +17,13 @@ import java.util.concurrent.Executors * a state behavior. * All ReStoreHub's must be marked as internal */ -internal interface ReStoreHub : HubMiddleware, HubReducer - -internal interface HubMiddleware { +internal interface ReStoreHub { fun getMiddleware(): Middleware + fun reduce(action: Action, domainState: StoreState): StoreState } -internal interface HubReducer { - fun reduce(action: Action, storeState: StoreState): StoreState +internal interface ReStoreReducer { + fun reduceAction(action: Action, state: State): State } /** @@ -87,13 +86,13 @@ internal abstract class BaseStoreHub( * Reduce the action and check it. If the action hasn't updated the hubState, then it doesn't need to update * storeState */ - override fun reduce(action: Action, storeState: DomainState): DomainState { - val oldState = getHubState(storeState) - val newState = reduceAction(action, oldState) - return if (oldState === newState) { - storeState + override fun reduce(action: Action, domainState: DomainState): DomainState { + val hubOldState = getHubState(domainState) + val hubNewState = getReducer().reduceAction(action, hubOldState) + return if (hubOldState === hubNewState) { + domainState } else { - updateStoreState(storeState, newState) + updateStoreState(domainState, hubNewState) } } @@ -102,24 +101,13 @@ internal abstract class BaseStoreHub( } protected abstract suspend fun handleAction(action: Action, storeState: DomainState, cancel: ValueCallback) - - @Deprecated( - replaceWith = ReplaceWith("ReStoreReducer"), - message = "must return a Reducer instance. The reducer must be a separate component - this closes " + - "access to the states that can be obtained from ReStoreHub" - ) - abstract fun reduceAction(action: Action, state: State): State -// protected abstract fun getReducer(): ReStoreReducer + protected abstract fun getReducer(): ReStoreReducer protected abstract fun getHubState(storeState: DomainState): State protected abstract fun updateStoreState(storeState: DomainState, newHubState: State): DomainState } -internal interface ReStoreReducer { - fun reduceAction(action: Action, state: State): State -} - internal suspend inline fun ReStoreHub<*, *>.dispatchOnMain(vararg actions: Action) { withMainContext { actions.forEach { domainStore.dispatch(it) } } } diff --git a/domain/src/main/java/com/tangem/domain/redux/global/DomainGlobalHub.kt b/domain/src/main/java/com/tangem/domain/redux/global/DomainGlobalHub.kt index 92fc243edd..d80b0bab73 100644 --- a/domain/src/main/java/com/tangem/domain/redux/global/DomainGlobalHub.kt +++ b/domain/src/main/java/com/tangem/domain/redux/global/DomainGlobalHub.kt @@ -4,6 +4,7 @@ import android.webkit.ValueCallback import com.tangem.common.extensions.toHexString import com.tangem.domain.redux.BaseStoreHub import com.tangem.domain.redux.DomainState +import com.tangem.domain.redux.ReStoreReducer import com.tangem.network.common.CardPublicKeyHttpInterceptor import org.rekotlin.Action @@ -34,17 +35,24 @@ internal class DomainGlobalHub : BaseStoreHub("DomainGlobalHu } } - override fun reduceAction(action: Action, state: DomainGlobalState): DomainGlobalState = when (action) { - is DomainGlobalAction.SaveScanNoteResponse -> { - val cardPublicKeyHex = action.scanResponse.card.cardPublicKey.toHexString() - state.networkServices.tangemTechService.addHeaderInterceptors( - listOf(CardPublicKeyHttpInterceptor(cardPublicKeyHex)) - ) - state.copy(scanResponse = action.scanResponse) + override fun getReducer(): ReStoreReducer = DomainGlobalReducer() +} + +private class DomainGlobalReducer : ReStoreReducer { + + override fun reduceAction(action: Action, state: DomainGlobalState): DomainGlobalState { + return when (action) { + is DomainGlobalAction.SaveScanNoteResponse -> { + val cardPublicKeyHex = action.scanResponse.card.cardPublicKey.toHexString() + state.networkServices.tangemTechService.addHeaderInterceptors( + listOf(CardPublicKeyHttpInterceptor(cardPublicKeyHex)) + ) + state.copy(scanResponse = action.scanResponse) + } + is DomainGlobalAction.ShowDialog -> { + state.copy(dialog = action.stateDialog) + } + else -> state } - is DomainGlobalAction.ShowDialog -> { - state.copy(dialog = action.stateDialog) - } - else -> state } } \ No newline at end of file From 853972903271cb994b30f68a32491d2c597b3b46 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 May 2022 16:39:56 +0300 Subject: [PATCH 4/4] 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