diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 89ca7c3734..2d3445cf24 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,6 +13,8 @@ dependencies { implementation(files("libs/walletconnect-1.5.6.aar")) implementation(project(":domain:legacy")) implementation(project(":domain:models")) + implementation(project(":domain:core")) + implementation(project(":domain:card")) implementation(project(":common")) implementation(project(":core:analytics")) implementation(project(":core:featuretoggles")) diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/AnalyticsChain.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/AnalyticsChain.kt new file mode 100644 index 0000000000..febc6a1525 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/AnalyticsChain.kt @@ -0,0 +1,36 @@ +package com.tangem.tap.domain.scanCard.chains + +import arrow.core.Either +import arrow.core.right +import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.AnalyticsEvent +import com.tangem.domain.card.ScanCardException +import com.tangem.domain.core.chain.Chain +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor + +/** + * Handles the analytics event interception and sending event after the scan card operation. Always returns result of + * previous chain. + * + * @param event the analytics event to be handled by this chain. + * + * @see Chain for more information about the Chain interface. + */ +class AnalyticsChain( + private val event: AnalyticsEvent, +) : Chain { + + override suspend fun invoke( + previousChainResult: ScanResponse, + ): Either { + val interceptor = CardContextInterceptor(previousChainResult) + val params = event.params.toMutableMap() + interceptor.intercept(params) + event.params = params.toMap() + + Analytics.send(event) + + return previousChainResult.right() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForOnboardingChain.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForOnboardingChain.kt new file mode 100644 index 0000000000..21d0e75290 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForOnboardingChain.kt @@ -0,0 +1,126 @@ +package com.tangem.tap.domain.scanCard.chains + +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import com.tangem.common.services.Result +import com.tangem.core.analytics.Analytics +import com.tangem.data.source.preferences.PreferencesDataSource +import com.tangem.domain.card.ScanCardException +import com.tangem.domain.common.extensions.withMainContext +import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.common.util.twinsIsTwinned +import com.tangem.domain.core.chain.Chain +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE +import com.tangem.tap.common.extensions.addContext +import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.extensions.setContext +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.TapWalletManager +import com.tangem.tap.features.onboarding.OnboardingHelper +import com.tangem.tap.features.onboarding.OnboardingSaltPayHelper +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep +import com.tangem.tap.features.onboarding.products.wallet.saltPay.SaltPayActivationManagerFactory +import com.tangem.tap.features.onboarding.products.wallet.saltPay.SaltPayExceptionHandler +import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayActivationError +import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayAction +import kotlinx.coroutines.delay +import org.rekotlin.Store + +/** + * Handles the verification process to determine if the scanned card requires onboarding. + * + * Returns: + * - [ScanChainException.OnboardingNeeded] if onboarding required for none SaltPay cards. + * - [ScanChainException.CheckForSaltPayOnboardingCaseException] if unable to check for SaltPay onboarding case. + * - [ScanChainException.PutSaltPayVisaCard] if scanned SaltPay card is not activated and onboarding required. + * + * @param store the [Store] that holds the state of the app. + * @param tapWalletManager manager responsible for handling operations related to the Wallet. + * @param preferencesStore data source for user preferences. + * + * @see Chain for more information about the Chain interface. + */ +class CheckForOnboardingChain( + private val store: Store, + private val tapWalletManager: TapWalletManager, + private val preferencesStore: PreferencesDataSource, +) : Chain { + + override suspend fun invoke( + previousChainResult: ScanResponse, + ): Either { + val cardTypesResolver = previousChainResult.cardTypesResolver + + tapWalletManager.updateConfigManager(previousChainResult) + store.dispatchOnMain(TwinCardsAction.IfTwinsPrepareState(previousChainResult)) + + return when { + cardTypesResolver.isSaltPayVisa() -> { + val manager = SaltPayActivationManagerFactory( + blockchain = previousChainResult.cardTypesResolver.getBlockchain(), + card = previousChainResult.card, + ).create() + val result = OnboardingSaltPayHelper.isOnboardingCase(previousChainResult, manager) + delay(timeMillis = 500) + withMainContext { + when (result) { + is Result.Success -> { + Analytics.addContext(previousChainResult) + val isOnboardingCase = result.data + if (isOnboardingCase) { + store.dispatch( + GlobalAction.Onboarding.Start(previousChainResult, canSkipBackup = false), + ) + store.dispatch(OnboardingSaltPayAction.SetDependencies(manager)) + store.dispatch(OnboardingSaltPayAction.Update(withAnalytics = false)) + ScanChainException.OnboardingNeeded(AppScreen.OnboardingWallet).left() + } else { + delay(DELAY_SDK_DIALOG_CLOSE) + previousChainResult.right() + } + } + is Result.Failure -> { + delay(DELAY_SDK_DIALOG_CLOSE) + SaltPayExceptionHandler.handle(result.error) + ScanChainException.CheckForSaltPayOnboardingCaseException(result.error).left() + } + } + } + } + cardTypesResolver.isSaltPayWallet() -> { + delay(DELAY_SDK_DIALOG_CLOSE) + if (previousChainResult.card.backupStatus?.isActive == false) { + SaltPayExceptionHandler.handle(SaltPayActivationError.PutVisaCard) + ScanChainException.PutSaltPayVisaCard.left() + } else { + Analytics.setContext(previousChainResult) + previousChainResult.right() + } + } + OnboardingHelper.isOnboardingCase(previousChainResult) -> { + Analytics.addContext(previousChainResult) + store.dispatchOnMain(GlobalAction.Onboarding.Start(previousChainResult, canSkipBackup = true)) + val appScreen = OnboardingHelper.whereToNavigate(previousChainResult) + ScanChainException.OnboardingNeeded(appScreen).left() + } + else -> { + Analytics.setContext(previousChainResult) + // If twins was twinned previously but twins welcome not shown + if (previousChainResult.twinsIsTwinned() && !preferencesStore.wasTwinsOnboardingShown()) { + store.dispatchOnMain( + TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(previousChainResult)), + ) + ScanChainException.OnboardingNeeded(AppScreen.OnboardingTwins).left() + } else { + delay(DELAY_SDK_DIALOG_CLOSE) + previousChainResult.right() + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForUnfinishedSaltPayBackupChain.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForUnfinishedSaltPayBackupChain.kt new file mode 100644 index 0000000000..f01f9f76d7 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForUnfinishedSaltPayBackupChain.kt @@ -0,0 +1,44 @@ +package com.tangem.tap.domain.scanCard.chains + +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import com.tangem.domain.card.ScanCardException +import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.core.chain.Chain +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.operations.backup.BackupService +import com.tangem.tap.common.extensions.primaryCardIsSaltPayVisa + +/** + * Handles the verification for unfinished SaltPay backup processes after the card scanning operation. + * + * Returns [ScanChainException.PutSaltPayVisaCard] if backup is not finished. + * + * @param backupService a service responsible for managing backup operations, used here to check for unfinished backups + * and verify the primary card type. + * + * @see Chain for more information about the Chain interface. + */ +internal class CheckForUnfinishedSaltPayBackupChain( + private val backupService: BackupService, +) : Chain { + + override suspend fun invoke( + previousChainResult: ScanResponse, + ): Either { + if (!backupService.hasIncompletedBackup || !backupService.primaryCardIsSaltPayVisa()) { + return previousChainResult.right() + } + + val isTheSamePrimaryCard = backupService.primaryCardId + ?.let { it == previousChainResult.card.cardId } + ?: false + + return if (previousChainResult.cardTypesResolver.isSaltPayWallet() || !isTheSamePrimaryCard) { + ScanChainException.PutSaltPayVisaCard.left() + } else { + previousChainResult.right() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/DisclaimerChain.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/DisclaimerChain.kt new file mode 100644 index 0000000000..b1addc1718 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/DisclaimerChain.kt @@ -0,0 +1,75 @@ +package com.tangem.tap.domain.scanCard.chains + +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import com.tangem.domain.card.ScanCardException +import com.tangem.domain.core.chain.Chain +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.features.disclaimer.Disclaimer +import com.tangem.tap.features.disclaimer.createDisclaimer +import com.tangem.tap.features.disclaimer.redux.DisclaimerAction +import com.tangem.tap.features.disclaimer.redux.DisclaimerCallback +import kotlinx.coroutines.suspendCancellableCoroutine +import org.rekotlin.Store +import kotlin.coroutines.resume + +/** + * Handles disclaimer display after the card scanning operation. + * + * Returns [ScanChainException.DisclaimerWasCanceled] if the disclaimer is dismissed by the user. + * + * @param store the [Store] that holds the state of the app, used here to dispatch actions related to disclaimers. + * @param disclaimerWillShow an optional function to be invoked when a disclaimer is about to be shown. Default is an + * empty function. + * + * @see Chain for more information about the Chain interface. + */ +internal class DisclaimerChain( + private val store: Store, + private val disclaimerWillShow: () -> Unit = {}, +) : Chain { + + override suspend fun invoke( + previousChainResult: ScanResponse, + ): Either { + val disclaimer = previousChainResult.card.createDisclaimer() + + return if (disclaimer.isAccepted()) { + previousChainResult.right() + } else { + disclaimerWillShow() + showDisclaimer(disclaimer, previousChainResult) + } + } + + private suspend fun showDisclaimer( + disclaimer: Disclaimer, + response: ScanResponse, + ): Either { + store.dispatchOnMain(DisclaimerAction.SetDisclaimer(disclaimer)) + + return suspendCancellableCoroutine { continuation -> + store.dispatchOnMain( + DisclaimerAction.Show( + fromScreen = AppScreen.Home, + callback = DisclaimerCallback( + onAccept = { + if (continuation.isActive) { + continuation.resume(response.right()) + } + }, + onDismiss = { + if (continuation.isActive) { + continuation.resume(ScanChainException.DisclaimerWasCanceled.left()) + } + }, + ), + ), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanChainException.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanChainException.kt new file mode 100644 index 0000000000..626465c7ee --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanChainException.kt @@ -0,0 +1,31 @@ +package com.tangem.tap.domain.scanCard.chains + +import com.tangem.domain.card.ScanCardException +import com.tangem.tap.common.redux.navigation.AppScreen + +sealed class ScanChainException : ScanCardException.ChainException() { + + /** + * May be returned from [CheckForOnboardingChain] and [CheckForUnfinishedSaltPayBackupChain] + * */ + object PutSaltPayVisaCard : ScanChainException() + + /** + * May be returned from [DisclaimerChain] + * */ + object DisclaimerWasCanceled : ScanChainException() + + /** + * May be returned from [CheckForOnboardingChain] + * + * @param onboardingRoute route where to navigate + * */ + data class OnboardingNeeded(val onboardingRoute: AppScreen) : ScanChainException() + + /** + * May be returned from [CheckForOnboardingChain] + * + * @param cause cause of exception + * */ + data class CheckForSaltPayOnboardingCaseException(override val cause: Throwable?) : ScanChainException() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanningFinishedChain.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanningFinishedChain.kt new file mode 100644 index 0000000000..a8c116cbe8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanningFinishedChain.kt @@ -0,0 +1,25 @@ +package com.tangem.tap.domain.scanCard.chains + +import arrow.core.Either +import arrow.core.right +import com.tangem.domain.card.ScanCardException +import com.tangem.domain.core.chain.Chain +import com.tangem.domain.models.scan.ScanResponse + +/** + * Responsible for invoking the callback at the end of the card scanning operation. Should be passed as last chain in + * after card scanning chains. Always returns result of previous chain. + * + * @param onScanningFinished a suspending function to be called when the scanning process has finished. + * + * @see Chain for more information about the Chain interface. + */ +internal class ScanningFinishedChain( + private val onScanningFinished: suspend () -> Unit, +) : Chain { + + override suspend fun invoke(previousChainResult: ScanResponse): Either { + onScanningFinished() + return previousChainResult.right() + } +} \ No newline at end of file diff --git a/domain/card/build.gradle.kts b/domain/card/build.gradle.kts index 0da92b17b4..213bd024b6 100644 --- a/domain/card/build.gradle.kts +++ b/domain/card/build.gradle.kts @@ -1,6 +1,5 @@ plugins { alias(deps.plugins.kotlin.jvm) - alias(deps.plugins.kotlin.kapt) id("configuration") } @@ -10,9 +9,4 @@ dependencies { implementation(project(":domain:models")) implementation(deps.tangem.card.core) - - implementation(deps.hilt.core) - kapt(deps.hilt.kapt) - - implementation(deps.moshi.kotlin) } \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardException.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardException.kt index b1000aa283..b9d3e287ac 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardException.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardException.kt @@ -1,14 +1,14 @@ package com.tangem.domain.card -import com.tangem.common.core.TangemSdkError - // TODO: May be add new error types sealed class ScanCardException : Exception() { object WrongCardId : ScanCardException() object UserCancelled : ScanCardException() + object WrongAccessCode : ScanCardException() + open class ChainException : ScanCardException() - class SdkException(override val cause: TangemSdkError) : ScanCardException() + class UnknownException(override val cause: Exception) : ScanCardException() } \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardUseCase.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardUseCase.kt index 88de647d8d..0365ac58ae 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardUseCase.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/ScanCardUseCase.kt @@ -1,16 +1,15 @@ package com.tangem.domain.card +import arrow.core.Either import arrow.core.EitherNel import arrow.core.flatMap -import arrow.core.toEitherNel import com.tangem.TangemSdk import com.tangem.common.core.CardIdDisplayFormat -import com.tangem.domain.card.model.ScanCardParams import com.tangem.domain.card.repository.ScanCardRepository +import com.tangem.domain.core.chain.Chain import com.tangem.domain.core.chain.ChainProcessor import com.tangem.domain.models.scan.ProductType import com.tangem.domain.models.scan.ScanResponse -import javax.inject.Inject /** * Use case responsible for scanning a card and returning a [ScanResponse] object. @@ -18,7 +17,7 @@ import javax.inject.Inject * @property tangemSdk An instance of [TangemSdk] to configure the display format of the card ID. * @constructor Create a new instance of [ScanCardUseCase] with the given dependencies. */ -class ScanCardUseCase @Inject internal constructor( +class ScanCardUseCase( private val scanCardRepository: ScanCardRepository, private val tangemSdk: TangemSdk, ) { @@ -32,23 +31,31 @@ class ScanCardUseCase @Inject internal constructor( /** * Scan a card and return a [ScanResponse] object. - * @param params An instance of [ScanCardParams] to configure the scan process. + * @param cardId an optional card ID to scan. If null, can scan any card present. + * Defaults to null. + * @param allowRequestAccessCodeFromStorage whether to prompt the user for an access code if needed. + * Defaults to false. + * @param afterScanChains An array of chains that should be executed after a successful card scan operation. + * Defaults to an empty array. * @return A [EitherNel] object with either a non-empty list of [ScanCardException] or a [ScanResponse]. */ - suspend operator fun invoke(params: ScanCardParams = ScanCardParams()): EitherNel { + suspend operator fun invoke( + cardId: String? = null, + allowRequestAccessCodeFromStorage: Boolean = false, + afterScanChains: Array> = emptyArray(), + ): Either { resetCardIdDisplayFormat() - scanChainProcessor.addChains(*params.afterScanChains) + scanChainProcessor.addChains(afterScanChains) return scanCardRepository.scanCard( - cardId = params.cardId, - allowRequestAccessCodeFromRepository = params.allowRequestAccessCodeFromRepository, + cardId = cardId, + allowRequestAccessCodeFromStorage = allowRequestAccessCodeFromStorage, ) .onRight { scanResponse -> updateCardIdDisplayFormat(scanResponse.productType) } - .toEitherNel() - .flatMap { scanResponse -> - scanChainProcessor.launchChains(initial = scanResponse) + .flatMap { response -> + scanChainProcessor.launchChains(initial = response) } } diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/di/CardDomainModule.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/di/CardDomainModule.kt deleted file mode 100644 index 592df6a876..0000000000 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/di/CardDomainModule.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.tangem.domain.card.di - -import com.tangem.TangemSdk -import com.tangem.domain.card.ScanCardUseCase -import com.tangem.domain.card.repository.ScanCardRepository -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -class CardDomainModule { - - @Provides - fun provideScanCardUseCase(scanCardRepository: ScanCardRepository, tangemSdk: TangemSdk) = - ScanCardUseCase(scanCardRepository, tangemSdk) -} \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/model/ScanCardParams.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/model/ScanCardParams.kt deleted file mode 100644 index 2fa7fd798d..0000000000 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/model/ScanCardParams.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.domain.card.model - -import com.tangem.domain.card.ScanCardException -import com.tangem.domain.core.chain.Chain -import com.tangem.domain.models.scan.ScanResponse - -/** - * Represents the parameters to be used during a card scanning operation. - * - * @property cardId an optional card ID to scan. If null, can scan any card present. - * Defaults to null. - * @property allowRequestAccessCodeFromRepository whether to prompt the user for an access code if needed. - * Defaults to false. - * @property afterScanChains An array of chains that should be executed after a successful card scan operation. - * Defaults to an empty array. - */ -class ScanCardParams( - val cardId: String? = null, - val allowRequestAccessCodeFromRepository: Boolean = false, - val afterScanChains: Array> = emptyArray(), -) \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/repository/ScanCardRepository.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/repository/ScanCardRepository.kt index 0fce5103c1..2de00090cf 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/repository/ScanCardRepository.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/repository/ScanCardRepository.kt @@ -10,15 +10,15 @@ import com.tangem.domain.models.scan.ScanResponse interface ScanCardRepository { /** - * Scans the card with the given [cardId] and returns a [ScanResponse]. If [allowRequestAccessCodeFromRepository] - * is true, the repository may prompt the user for an access code. + * Scans the card with the given [cardId] and returns a [ScanResponse] * * @param cardId an optional card ID to scan. If null, the repository should scan any card present. - * @param allowRequestAccessCodeFromRepository whether to prompt the user for an access code if needed. + * @param allowRequestAccessCodeFromStorage whether the access code can be requested from + * internal storage during the scan process * @return an [Either] that contains a [ScanCardException] in case of an error or a [ScanResponse]. */ suspend fun scanCard( cardId: String?, - allowRequestAccessCodeFromRepository: Boolean, + allowRequestAccessCodeFromStorage: Boolean, ): Either } \ No newline at end of file diff --git a/domain/core/src/main/kotlin/com/tangem/domain/core/chain/Chain.kt b/domain/core/src/main/kotlin/com/tangem/domain/core/chain/Chain.kt index 57ec9f59b2..a7e0d5bc38 100644 --- a/domain/core/src/main/kotlin/com/tangem/domain/core/chain/Chain.kt +++ b/domain/core/src/main/kotlin/com/tangem/domain/core/chain/Chain.kt @@ -1,7 +1,6 @@ package com.tangem.domain.core.chain import arrow.core.Either -import arrow.core.EitherNel /** * A chain in the [ChainProcessor] class for processing a chain of operations with the ability to handle errors. @@ -12,8 +11,8 @@ interface Chain { /** * Invokes the chain with the previous chain result as input and returns an [Either] result. - * @param previousChainResult the previous chain result as an [EitherNel] + * @param previousChainResult the previous chain result as an [R] * @return the result of the chain processing as an [Either] */ - suspend operator fun invoke(previousChainResult: EitherNel): Either + suspend operator fun invoke(previousChainResult: R): Either } \ No newline at end of file diff --git a/domain/core/src/main/kotlin/com/tangem/domain/core/chain/ChainProcessor.kt b/domain/core/src/main/kotlin/com/tangem/domain/core/chain/ChainProcessor.kt index 334af39b0e..13101be7fd 100644 --- a/domain/core/src/main/kotlin/com/tangem/domain/core/chain/ChainProcessor.kt +++ b/domain/core/src/main/kotlin/com/tangem/domain/core/chain/ChainProcessor.kt @@ -1,12 +1,10 @@ package com.tangem.domain.core.chain import arrow.core.Either -import arrow.core.EitherNel -import arrow.core.left -import arrow.core.nel -import arrow.core.right -import arrow.core.toEitherNel +import arrow.core.raise.either +// TODO: Create Ior implementation +// TODO: Create accumulate implementation /** * A class for processing a chain of operations with the ability to handle errors. * @param E the type of error @@ -23,41 +21,15 @@ class ChainProcessor { * Adds chains to the existing list of chains to be executed. * @param chains the chains to be added to the list */ - fun addChains(vararg chains: Chain) { + fun addChains(chains: Array>) { this.chains.addAll(chains) } - /** - * Launches the chains with the given initial value as input. - * @param initial the initial value to start the chain processing. - * @param accumulateExceptions determines whether to accumulate exceptions or break on first exception. - * Defaults to true. - * @return the result of the chain processing as an EitherNel, which is a disjunction that may contain multiple errors. - */ - private suspend fun launchChains(initial: Either, accumulateExceptions: Boolean = true): EitherNel { - return chains.fold(initial.toEitherNel()) { previousChainResult, chain -> - chain.invoke(previousChainResult) - .mapLeft { e -> - previousChainResult.leftOrNull()?.plus(e) - ?: e.nel() - } - .onLeft { e -> - if (!accumulateExceptions) { - return e.left() - } - } + suspend fun launchChains(initial: R): Either { + return either { + chains.fold(initial) { prevChainResult, chain -> + chain.invoke(prevChainResult).bind() + } } } - - /** - * Launches the chains with the given initial value as input. - * This function is a convenience function that assumes the initial value does not contain any errors. - * @param initial the initial value to start the chain processing. - * @param accumulateExceptions determines whether to accumulate exceptions or break on first exception. - * Defaults to true. - * @return the result of the chain processing as an EitherNel, which is a disjunction that may contain multiple errors. - */ - suspend fun launchChains(initial: R, accumulateExceptions: Boolean = true): EitherNel { - return launchChains(initial.right(), accumulateExceptions) - } } \ No newline at end of file