Updated on 2026-08-14
This commit is contained in:
commit
fbb99bcda0
317 changed files with 3800 additions and 2894 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.feedback
|
||||
|
||||
import android.content.res.Resources
|
||||
import com.tangem.core.res.getStringSafe
|
||||
import com.tangem.domain.feedback.models.CardInfo
|
||||
import com.tangem.domain.feedback.models.FeedbackEmail
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
|
|
@ -65,7 +66,7 @@ class SendFeedbackEmailUseCase(
|
|||
is FeedbackEmailType.SwapProblem,
|
||||
is FeedbackEmailType.TransactionSendingProblem,
|
||||
-> {
|
||||
append(resources.getString(R.string.feedback_data_collection_message))
|
||||
append(resources.getStringSafe(R.string.feedback_data_collection_message))
|
||||
skipLine()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.feedback.utils
|
||||
|
||||
import android.content.res.Resources
|
||||
import com.tangem.core.res.getStringSafe
|
||||
import com.tangem.domain.feedback.R
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
|
||||
|
|
@ -27,6 +28,6 @@ internal class EmailMessageTitleResolver(private val resources: Resources) {
|
|||
-> R.string.feedback_preface_tx_failed
|
||||
is FeedbackEmailType.PreActivatedWallet -> R.string.feedback_preface_support
|
||||
}
|
||||
.let(resources::getString)
|
||||
.let(resources::getStringSafe)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.feedback.utils
|
||||
|
||||
import android.content.res.Resources
|
||||
import com.tangem.core.res.getStringSafe
|
||||
import com.tangem.domain.feedback.R
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
|
||||
|
|
@ -32,6 +33,6 @@ internal class EmailSubjectResolver(private val resources: Resources) {
|
|||
is FeedbackEmailType.PreActivatedWallet -> R.string.feedback_subject_pre_activated_wallet
|
||||
is FeedbackEmailType.CurrencyDescriptionError -> R.string.feedback_token_description_error
|
||||
}
|
||||
.let(resources::getString)
|
||||
.let(resources::getStringSafe)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package com.tangem.domain.redux
|
||||
|
||||
import com.tangem.domain.redux.global.DomainGlobalState
|
||||
import org.rekotlin.StateType
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class DomainState(val globalState: DomainGlobalState = DomainGlobalState()) : StateType
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package com.tangem.domain.redux
|
||||
|
||||
import com.tangem.domain.redux.global.DomainGlobalHub
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Store
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
private val RE_STORE_HUBS: List<ReStoreHub<DomainState, *>> = listOf(DomainGlobalHub())
|
||||
|
||||
val domainStore = Store(
|
||||
state = DomainState(),
|
||||
middleware = RE_STORE_HUBS.map { it.getMiddleware() },
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return assembleReducedDomainState
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
package com.tangem.domain.redux
|
||||
|
||||
import android.webkit.ValueCallback
|
||||
import com.tangem.domain.redux.global.DomainGlobalState
|
||||
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.job
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.DispatchFunction
|
||||
import org.rekotlin.Middleware
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
* ReStoreHub's should not store the <StoreState> or the <State>, because this can lead to destabilization of
|
||||
* a state behavior.
|
||||
* All ReStoreHub's must be marked as internal
|
||||
*/
|
||||
internal interface ReStoreHub<StoreState, State> {
|
||||
fun getMiddleware(): Middleware<StoreState>
|
||||
fun reduce(action: Action, domainState: StoreState): StoreState
|
||||
}
|
||||
|
||||
internal interface ReStoreReducer<State> {
|
||||
fun reduceAction(action: Action, state: State): State
|
||||
}
|
||||
|
||||
/**
|
||||
* ReStoreHub is the entry point for actions. It processes it through middleware and reducer.
|
||||
* Actions handled by ReStoreHub go into coroutine scope, which can be canceled while the action is being processed.
|
||||
* All action went from the middleware must be dispatched through ReStoreHub.dispatchOnMain(Actions) to prevent
|
||||
* concurrent modification in the Store
|
||||
* Only the changed hub State will change its state in the DomainState
|
||||
* Do not implement other states like as DomainGlobalState. Because it can dilute the responsibility of
|
||||
* states.
|
||||
* @param name - name of the Hub
|
||||
* @param dispatcher - main coroutine dispatcher for actions
|
||||
* @property globalState - state witch produce accessibility to global variables
|
||||
*/
|
||||
internal abstract class BaseStoreHub<State>(
|
||||
private val name: String,
|
||||
private val dispatcher: CoroutineDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher(),
|
||||
) : ReStoreHub<DomainState, State> {
|
||||
|
||||
val globalState: DomainGlobalState
|
||||
get() = domainStore.state.globalState
|
||||
|
||||
val hubScope = CoroutineScope(
|
||||
Job() + dispatcher + CoroutineName(name) + FeatureCoroutineExceptionHandler.create(name),
|
||||
)
|
||||
|
||||
private val actionsAndJobs = mutableMapOf<Action, Job>()
|
||||
|
||||
override fun getMiddleware(): Middleware<DomainState> {
|
||||
return { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
handle(state, action, dispatch)
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches new coroutine and stores the action with it's coroutine job. (Coroutine can be cancelled
|
||||
* through invoking the cancelActionJob() function inside a middleware).
|
||||
* Removes the action when job is completed.
|
||||
*/
|
||||
protected open fun handle(storeStateHolder: () -> DomainState?, action: Action, dispatch: DispatchFunction) {
|
||||
val storeState = storeStateHolder()
|
||||
?: throw UnsupportedOperationException("StoreState for the $name can't be NULL")
|
||||
|
||||
hubScope.launch {
|
||||
actionsAndJobs[action] = this.coroutineContext.job
|
||||
actionsAndJobs[action]?.invokeOnCompletion { actionsAndJobs.remove(action) }
|
||||
|
||||
handleAction(action, storeState) {
|
||||
actionsAndJobs.remove(it)?.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, domainState: DomainState): DomainState {
|
||||
val hubOldState = getHubState(domainState)
|
||||
val hubNewState = getReducer().reduceAction(action, hubOldState)
|
||||
return if (hubOldState === hubNewState) {
|
||||
domainState
|
||||
} else {
|
||||
updateStoreState(domainState, hubNewState)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun cancelAll() {
|
||||
actionsAndJobs.forEach { (_, job) -> job.cancel() }
|
||||
}
|
||||
|
||||
protected abstract suspend fun handleAction(action: Action, storeState: DomainState, cancel: ValueCallback<Action>)
|
||||
protected abstract fun getReducer(): ReStoreReducer<State>
|
||||
|
||||
protected abstract fun getHubState(storeState: DomainState): State
|
||||
protected abstract fun updateStoreState(storeState: DomainState, newHubState: State): DomainState
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.domain.redux.extensions
|
||||
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.domain.redux.domainStore
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal suspend inline fun dispatchOnMain(vararg actions: Action) {
|
||||
withMainContext { actions.forEach { domainStore.dispatch(it) } }
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.domain.redux.global
|
||||
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// TODO: refactoring: is alias for the GlobalAction
|
||||
sealed class DomainGlobalAction : Action {
|
||||
data class SaveScanNoteResponse(val scanResponse: ScanResponse) : DomainGlobalAction()
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
package com.tangem.domain.redux.global
|
||||
|
||||
import android.webkit.ValueCallback
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.datasource.api.common.AuthProvider
|
||||
import com.tangem.datasource.utils.RequestHeader
|
||||
import com.tangem.domain.redux.BaseStoreHub
|
||||
import com.tangem.domain.redux.DomainState
|
||||
import com.tangem.domain.redux.ReStoreReducer
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// TODO: refactoring: is alias for the GlobalMiddleware and the GlobalReducer
|
||||
internal class DomainGlobalHub : BaseStoreHub<DomainGlobalState>("DomainGlobalHub") {
|
||||
|
||||
override fun getHubState(storeState: DomainState): DomainGlobalState {
|
||||
return storeState.globalState
|
||||
}
|
||||
|
||||
override fun updateStoreState(storeState: DomainState, newHubState: DomainGlobalState): DomainState {
|
||||
return storeState.copy(globalState = newHubState)
|
||||
}
|
||||
|
||||
override suspend fun handleAction(action: Action, storeState: DomainState, cancel: ValueCallback<Action>) {
|
||||
if (action !is DomainGlobalAction) return
|
||||
}
|
||||
|
||||
override fun getReducer(): ReStoreReducer<DomainGlobalState> = DomainGlobalReducer()
|
||||
}
|
||||
|
||||
private class DomainGlobalReducer : ReStoreReducer<DomainGlobalState> {
|
||||
|
||||
override fun reduceAction(action: Action, state: DomainGlobalState): DomainGlobalState {
|
||||
return when (action) {
|
||||
is DomainGlobalAction.SaveScanNoteResponse -> {
|
||||
val card = action.scanResponse.card
|
||||
// TODO: AuthHeaders: try to remove it, because now we can use headers with dynamic values
|
||||
state.networkServices.tangemTechService.addAuthenticationHeader(
|
||||
RequestHeader.AuthenticationHeader(
|
||||
object : AuthProvider {
|
||||
override fun getCardPublicKey(): String = card.cardPublicKey.toHexString()
|
||||
override fun getCardId(): String = card.cardId
|
||||
},
|
||||
),
|
||||
)
|
||||
state.copy(scanResponse = action.scanResponse)
|
||||
}
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package com.tangem.domain.redux.global
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechService
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class DomainGlobalState(
|
||||
// there is a part of mirrors from the GlobalState.
|
||||
// It updates on GlobalAction.SaveScanNoteResponse -> DomainGlobalAction.SaveScanNoteResponse(scanResponse)
|
||||
val scanResponse: ScanResponse? = null,
|
||||
//
|
||||
val networkServices: NetworkServices = NetworkServices(),
|
||||
)
|
||||
|
||||
data class NetworkServices(
|
||||
val tangemTechService: TangemTechService = TangemTechService,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue