Updated on 2026-08-14
This commit is contained in:
parent
a085c2998c
commit
8539729032
8 changed files with 166 additions and 111 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
26
domain/src/main/java/com/tangem/domain/DomainLayer.kt
Normal file
26
domain/src/main/java/com/tangem/domain/DomainLayer.kt
Normal file
|
|
@ -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<String, Any?>? = null) {
|
||||
initActionStateLogger()
|
||||
|
||||
onInitComplete?.invoke(null)
|
||||
}
|
||||
|
||||
private fun initActionStateLogger() {
|
||||
val factory = actionStateLogger.actionStateConvertersFactory
|
||||
|
||||
factory.addConverter(AddCustomTokenAction::class.java, AddCustomTokenState.Converter())
|
||||
}
|
||||
}
|
||||
|
|
@ -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<DomainState> {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
|
@ -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<DomainState> {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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<ReStoreHub<DomainState, *>> = 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<Pair<Action, DomainState>>()
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<StateHolder> {
|
|||
fun convert(stateHolder: StateHolder): String
|
||||
}
|
||||
|
||||
interface StringActionConverter<StateHolder> {
|
||||
interface StringActionStateConverter<StateHolder> {
|
||||
fun convert(action: Action, stateHolder: StateHolder): String?
|
||||
}
|
||||
|
||||
class ActionStateConvertersFactory {
|
||||
private val stateConverters = mutableMapOf<Class<out Action>, StringActionStateConverter<DomainState>>()
|
||||
|
||||
fun addConverter(classOfAction: Class<out Action>, converter: StringActionStateConverter<DomainState>) {
|
||||
stateConverters[classOfAction] = converter
|
||||
}
|
||||
|
||||
fun getConverter(action: Action): StringActionStateConverter<DomainState>? {
|
||||
val converter = stateConverters.firstNotNullOfOrNull { (classOfAction, converter) ->
|
||||
if (classOfAction.isAssignableFrom(action::class.java)) {
|
||||
converter
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} ?: return null
|
||||
|
||||
return converter
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Pair<Action, DomainState>>)
|
||||
}
|
||||
|
||||
internal class ActionStateLoggerImpl : ActionStateLogger {
|
||||
|
||||
val actionStateConvertersFactory = ActionStateConvertersFactory()
|
||||
|
||||
override fun log(reducedSates: List<Pair<Action, DomainState>>) {
|
||||
if (!BuildConfig.LOG_ENABLED) return
|
||||
|
||||
logStates(reducedSates)
|
||||
}
|
||||
|
||||
private fun logStates(reducedSates: List<Pair<Action, DomainState>>) {
|
||||
reducedSates.forEach { (action, domainState) ->
|
||||
val messageToPrint = actionStateConvertersFactory.getConverter(action)
|
||||
?.convert(action, domainState)
|
||||
?: return@forEach
|
||||
|
||||
Timber.d(messageToPrint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Pair<Action, DomainState>>) {
|
||||
// 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<Pair<Action, DomainState>>) {
|
||||
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()
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue