From a7fc5e10363b2e0788888c6a3a9326db949f13ba Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 23 Sep 2021 12:49:34 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/common/extensions/Navigation.kt | 10 +- .../com/tangem/tap/common/redux/AppReducer.kt | 6 + .../com/tangem/tap/common/redux/AppState.kt | 19 +- .../tap/common/redux/global/GlobalAction.kt | 11 ++ .../common/redux/global/GlobalMidlleware.kt | 27 ++- .../redux/navigation/NavigationAction.kt | 15 +- .../redux/navigation/NavigationState.kt | 15 +- .../disclaimer/redux/DisclaimerMiddleware.kt | 17 +- .../tangem/tap/features/home/HomeFragment.kt | 77 +++++--- .../tap/features/home/redux/HomeAction.kt | 16 +- .../tap/features/home/redux/HomeMiddleware.kt | 126 ++++++------- .../tap/features/home/redux/HomeReducer.kt | 21 ++- .../tap/features/home/redux/HomeState.kt | 14 +- .../features/onboarding/OnboardingService.kt | 168 +++++++++++++++++ .../products/note/OnboardingNoteFragment.kt | 133 ++++++++++++++ .../note/redux/OnboardingNoteAction.kt | 16 ++ .../note/redux/OnboardingNoteMiddleware.kt | 72 ++++++++ .../note/redux/OnboardingNoteReducer.kt | 38 ++++ .../note/redux/OnboardingNoteState.kt | 18 ++ .../old/OnboardingOtherCardsFragment.kt | 50 ++++++ .../wallet/OnboardingWalletFragment.kt | 52 ++++++ .../wallet/redux/OnboardingWalletAction.kt | 19 ++ .../redux/OnboardingWalletMiddleware.kt | 18 ++ .../wallet/redux/OnboardingWalletReducer.kt | 20 +++ .../wallet/redux/OnboardingWalletState.kt | 15 ++ .../onboarding/redux/OnboardingAction.kt | 22 +++ .../onboarding/redux/OnboardingMiddleware.kt | 22 +++ .../onboarding/redux/OnboardingReducer.kt | 24 +++ .../onboarding/redux/OnboardingState.kt | 11 ++ .../onboarding/redux/StateSupportClasses.kt | 26 +++ ...Fragment.kt => OnboardingTwinsFragment.kt} | 2 +- .../wallet/ui/wallet/SingleWalletView.kt | 2 +- app/src/main/res/drawable/bg_circle_gray.xml | 9 + .../main/res/drawable/ic_home_card_back.xml | 108 +++++++++++ .../main/res/drawable/ic_home_card_front.xml | 126 +++++++++++++ app/src/main/res/layout/fragment_home.xml | 169 +++++++++++------- .../main/res/layout/fragment_onboarding.xml | 161 +++++++++++++++++ .../res/layout/fragment_onboarding_wallet.xml | 6 + 38 files changed, 1486 insertions(+), 195 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/old/OnboardingOtherCardsFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt rename app/src/main/java/com/tangem/tap/features/wallet/ui/{TwinsOnboardingFragment.kt => OnboardingTwinsFragment.kt} (97%) create mode 100644 app/src/main/res/drawable/bg_circle_gray.xml create mode 100644 app/src/main/res/drawable/ic_home_card_back.xml create mode 100644 app/src/main/res/drawable/ic_home_card_front.xml create mode 100644 app/src/main/res/layout/fragment_onboarding.xml create mode 100644 app/src/main/res/layout/fragment_onboarding_wallet.xml diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index b3657a070c..c7e04cf44f 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -16,9 +16,12 @@ import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment import com.tangem.tap.features.details.ui.walletconnect.WalletConnectSessionsFragment import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment import com.tangem.tap.features.home.HomeFragment +import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment +import com.tangem.tap.features.onboarding.products.old.OnboardingOtherCardsFragment +import com.tangem.tap.features.onboarding.products.wallet.OnboardingWalletFragment import com.tangem.tap.features.send.ui.SendFragment import com.tangem.tap.features.tokens.ui.AddTokensFragment -import com.tangem.tap.features.wallet.ui.TwinsOnboardingFragment +import com.tangem.tap.features.wallet.ui.OnboardingTwinsFragment import com.tangem.tap.features.wallet.ui.WalletDetailsFragment import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.wallet.R @@ -67,6 +70,10 @@ fun FragmentActivity.addOnBackPressedDispatcher( private fun fragmentFactory(screen: AppScreen): Fragment { return when (screen) { AppScreen.Home -> HomeFragment() + AppScreen.OnboardingNote -> OnboardingNoteFragment() + AppScreen.OnboardingWallet -> OnboardingWalletFragment() + AppScreen.OnboardingTwins -> OnboardingTwinsFragment() + AppScreen.OnboardingOther -> OnboardingOtherCardsFragment() AppScreen.Wallet -> WalletFragment() AppScreen.Send -> SendFragment() AppScreen.Details -> DetailsFragment() @@ -75,7 +82,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment { AppScreen.Disclaimer -> DisclaimerFragment() AppScreen.CreateTwinWalletWarning -> TwinWalletWarningFragment() AppScreen.CreateTwinWallet -> CreateTwinWalletFragment() - AppScreen.TwinsOnboarding -> TwinsOnboardingFragment() AppScreen.AddTokens -> AddTokensFragment() AppScreen.WalletDetails -> WalletDetailsFragment() AppScreen.WalletConnectSessions -> WalletConnectSessionsFragment() diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 57ed1eaf2c..e60b9cade1 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -6,6 +6,9 @@ import com.tangem.tap.features.details.redux.DetailsReducer import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer import com.tangem.tap.features.home.redux.HomeReducer +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer +import com.tangem.tap.features.onboarding.redux.OnboardingReducer import com.tangem.tap.features.send.redux.reducers.SendScreenReducer import com.tangem.tap.features.tokens.redux.TokensReducer import com.tangem.tap.features.wallet.redux.reducers.WalletReducer @@ -19,6 +22,9 @@ fun appReducer(action: Action, state: AppState?): AppState { navigationState = NavigationReducer.reduce(action, state), globalState = globalReducer(action, state), homeState = HomeReducer.reduce(action, state), + onboardingState = OnboardingReducer.reduce(action, state), + onboardingNoteState = OnboardingNoteReducer.reduce(action, state), + onboardingWalletState = OnboardingWalletReducer.reduce(action, state), walletState = WalletReducer.reduce(action, state), sendState = SendScreenReducer.reduce(action, state.sendState), detailsState = DetailsReducer.reduce(action, state), diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 79400725e3..985a2a75ba 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -1,7 +1,7 @@ package com.tangem.tap.common.redux +import com.tangem.tap.common.redux.global.GlobalMiddleware import com.tangem.tap.common.redux.global.GlobalState -import com.tangem.tap.common.redux.global.globalMiddleware import com.tangem.tap.common.redux.navigation.NavigationState import com.tangem.tap.common.redux.navigation.navigationMiddleware import com.tangem.tap.features.details.redux.DetailsMiddleware @@ -12,6 +12,12 @@ import com.tangem.tap.features.disclaimer.redux.DisclaimerMiddleware import com.tangem.tap.features.disclaimer.redux.DisclaimerState import com.tangem.tap.features.home.redux.HomeMiddleware import com.tangem.tap.features.home.redux.HomeState +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMiddleware +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState +import com.tangem.tap.features.onboarding.redux.OnboardingMiddleware +import com.tangem.tap.features.onboarding.redux.OnboardingState import com.tangem.tap.features.send.redux.middlewares.SendMiddleware import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.tokens.redux.TokensMiddleware @@ -25,6 +31,9 @@ data class AppState( val navigationState: NavigationState = NavigationState(), val globalState: GlobalState = GlobalState(), val homeState: HomeState = HomeState(), + val onboardingState: OnboardingState = OnboardingState(), + val onboardingNoteState: OnboardingNoteState = OnboardingNoteState(), + val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(), val walletState: WalletState = WalletState(), val sendState: SendState = SendState(), val detailsState: DetailsState = DetailsState(), @@ -36,8 +45,12 @@ data class AppState( companion object { fun getMiddleware(): List> { return listOf( - logMiddleware, navigationMiddleware, notificationsMiddleware, globalMiddleware, - HomeMiddleware().homeMiddleware, + logMiddleware, navigationMiddleware, notificationsMiddleware, + GlobalMiddleware.handler, + HomeMiddleware.handler, + OnboardingMiddleware.handler, + OnboardingNoteMiddleware.handler, + OnboardingWalletMiddleware.handler, WalletMiddleware().walletMiddleware, SendMiddleware().sendMiddleware, DetailsMiddleware().detailsMiddleware, diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index c1c9137aec..928cb7b341 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -2,6 +2,9 @@ package com.tangem.tap.common.redux.global import com.tangem.blockchain.common.WalletManager import com.tangem.common.CompletionResult +import com.tangem.common.core.TangemError +import com.tangem.tap.common.redux.DebugErrorAction +import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager @@ -14,6 +17,14 @@ import org.rekotlin.Action sealed class GlobalAction : Action { + data class DebugShowError(override val error: TapError) : GlobalAction(), DebugErrorAction + + data class ReadCard( + val onSuccess: (suspend (ScanNoteResponse) -> Unit)? = null, + val onFailure: (suspend (TangemError) -> Unit)? = null, + val messageResId: Int? = null, + ) : GlobalAction() + object ScanFailsCounter { data class ChooseBehavior(val result: CompletionResult) : GlobalAction() object Reset : GlobalAction() diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index d90e263263..115329af90 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -3,6 +3,7 @@ package com.tangem.tap.common.redux.global import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.services.Result +import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager @@ -13,10 +14,19 @@ import com.tangem.tap.network.moonpay.MoonpayService import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.rekotlin.Middleware -val globalMiddleware: Middleware = { dispatch, appState -> +class GlobalMiddleware { + companion object { + val handler = globalMiddlewareHandler + } +} + +private val globalMiddlewareHandler: Middleware = { dispatch, appState -> { nextDispatch -> { action -> when (action) { @@ -78,6 +88,21 @@ val globalMiddleware: Middleware = { dispatch, appState -> } } } + is GlobalAction.ReadCard -> { + scope.launch { + val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler, action.messageResId) + withContext(Dispatchers.Main) { + store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) + when (result) { + is CompletionResult.Success -> { + tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card) + action.onSuccess?.invoke(result.data) + } + is CompletionResult.Failure -> action.onFailure?.invoke(result.error) + } + } + } + } } nextDispatch(action) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt index 9a4794b29d..375b16e785 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt @@ -2,6 +2,9 @@ package com.tangem.tap.common.redux.navigation import android.view.View import androidx.fragment.app.FragmentActivity +import androidx.transition.ChangeBounds +import androidx.transition.ChangeImageTransform +import androidx.transition.ChangeTransform import androidx.transition.TransitionSet import org.rekotlin.Action import java.lang.ref.WeakReference @@ -27,4 +30,14 @@ data class FragmentShareTransition( val exitTransitionSet: TransitionSet ) -data class ShareElement(val wView: WeakReference, val name: String) \ No newline at end of file +data class ShareElement(val wView: WeakReference, val name: String) + +//TODO: remove transition example +class DetailsTransition : TransitionSet() { + init { + ordering = ORDERING_TOGETHER; + addTransition(ChangeBounds()) + .addTransition(ChangeTransform()) + .addTransition(ChangeImageTransform()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt index ed59b9b2d5..4fa02150f0 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt @@ -5,12 +5,19 @@ import org.rekotlin.StateType import java.lang.ref.WeakReference data class NavigationState( - val backStack: List = listOf(AppScreen.Home), - val activity: WeakReference? = null + val backStack: List = listOf(AppScreen.Home), + val activity: WeakReference? = null ) : StateType enum class AppScreen { - Home, Wallet, WalletDetails, Send, Details, DetailsConfirm, DetailsSecurity, Disclaimer, - CreateTwinWalletWarning, CreateTwinWallet, TwinsOnboarding, AddTokens, WalletConnectSessions, + Home, + Disclaimer, + OnboardingNote, OnboardingWallet, OnboardingTwins, OnboardingOther, + Wallet, WalletDetails, + Send, + Details, DetailsConfirm, DetailsSecurity, + CreateTwinWalletWarning, CreateTwinWallet, + AddTokens, + WalletConnectSessions, QrScan } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt index 066b2b83db..12a1cfaccd 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt @@ -1,8 +1,9 @@ package com.tangem.tap.features.disclaimer.redux +import com.tangem.tap.common.post import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.preferencesStorage import com.tangem.tap.store import org.rekotlin.Middleware @@ -14,16 +15,10 @@ class DisclaimerMiddleware { when (action) { is DisclaimerAction.AcceptDisclaimer -> { preferencesStorage.saveDisclaimerAccepted() - if (store.state.walletState.twinCardsState != null) { - val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() - if (showOnboarding) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) - } else { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - } else { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } + store.dispatch(NavigationAction.PopBackTo()) + + //delayed sending used for better animation + post(550) { store.dispatch(HomeAction.ReadCard) } } } next(action) diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index 29cd04dc73..168941d2c5 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -1,12 +1,20 @@ package com.tangem.tap.features.home import android.app.Dialog +import android.content.Intent +import android.net.Uri import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater -import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.extensions.safeStartActivity +import com.tangem.tap.common.extensions.toast import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.navigation.DetailsTransition +import com.tangem.tap.common.redux.navigation.FragmentShareTransition +import com.tangem.tap.common.redux.navigation.ShareElement +import com.tangem.tap.common.toggleWidget.IndeterminateProgressButtonWidget +import com.tangem.tap.common.toggleWidget.ViewStateWidget import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.home.redux.HomeDialog import com.tangem.tap.features.home.redux.HomeState @@ -15,11 +23,36 @@ import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_home.* import org.rekotlin.StoreSubscriber +import java.lang.ref.WeakReference class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber { + lateinit var btnScanCard: ViewStateWidget + private var dialog: Dialog? = null + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val inflater = TransitionInflater.from(requireContext()) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val transitionViews = mutableListOf() + transitionViews.add(ShareElement(WeakReference(imv_front_card), "imv_front_card")) + val shareTransition = FragmentShareTransition(transitionViews, DetailsTransition(), DetailsTransition()) + + store.dispatch(HomeAction.SetFragmentShareTransition(shareTransition)) + store.dispatch(HomeAction.Init) + + btn_scan_card?.setOnClickListener { store.dispatch(HomeAction.ReadCard) } + btn_get_new_card?.setOnClickListener { store.dispatch(HomeAction.GoToShop) } + + btnScanCard = IndeterminateProgressButtonWidget(btn_scan_card, progress) + } + override fun onStart() { super.onStart() store.subscribe(this) { state -> @@ -37,16 +70,23 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber = { dispatch, state -> - { next -> - { action -> - when (action) { - is HomeAction.CheckIfFirstLaunch -> { - store.dispatch( - HomeAction.CheckIfFirstLaunch.Result(preferencesStorage.getCountOfLaunches() == 1) - ) - } - is HomeAction.ReadCard -> { - scope.launch { - val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler) - withContext(Dispatchers.Main) { - store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) - when (result) { - is CompletionResult.Success -> { - tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card) - store.dispatch(GlobalAction.RestoreAppCurrency) - store.state.globalState.tapWalletManager.onCardScanned(result.data) - showDisclaimerOrNavigateToWallet() - } - } - } - } - } - is HomeAction.GoToShop -> { - val uri = Uri.parse(CARD_SHOP_URI) - val intent = Intent(Intent.ACTION_VIEW, uri) - startActivity(action.context, intent, null) - } - } - next(action) - } - } - } - - private fun showDisclaimerOrNavigateToWallet() { - if (!preferencesStorage.wasDisclaimerAccepted()) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) - return - } - if (store.state.walletState.twinCardsState != null) { - val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() - if (showOnboarding) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) - return - } - } - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - companion object { - private const val CARD_SHOP_URI = + val handler = homeMiddleware + + const val CARD_SHOP_URI = "https://shop.tangem.com/?afmc=1i&utm_campaign=1i&utm_source=leaddyno&utm_medium=affiliate" } } +private val homeMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + when (action) { + is HomeAction.Init -> { + store.dispatch(GlobalAction.RestoreAppCurrency) + store.dispatch(GlobalAction.GetMoonPayUserStatus) + store.dispatch(HomeAction.SetTermsOfUseState(preferencesStorage.wasDisclaimerAccepted())) + } + is HomeAction.ReadCard -> { + if (!preferencesStorage.wasDisclaimerAccepted()) { + store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) + } else { + launchOnboardingService() + } + } + is HomeAction.GoToShop -> store.dispatch(HomeAction.SetOpenUrl(HomeMiddleware.CARD_SHOP_URI)) + } + next(action) + } + } +} +private fun launchOnboardingService() { + fun changeButtonState(state: ButtonState) { + val btnState = IndeterminateProgressButton(state) + store.dispatch(HomeAction.ChangeScanCardButtonState(btnState)) + } + changeButtonState(ButtonState.PROGRESS) + val walletManager = store.state.globalState.tapWalletManager + val service = OnboardingService( + walletManager, + preferencesStorage.usedCardsPrefStorage + ) + + service.onReadyToProceed = { + store.dispatch(OnboardingAction.SetData(it)) + changeButtonState(ButtonState.ENABLED) + store.dispatch(NavigationAction.NavigateTo(it.onboardingScreen)) + } + + service.onFailedToProceedToOnboardingCase = { + changeButtonState(ButtonState.ENABLED) + walletManager.onCardScanned(it) + store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + } + + service.onError = { + changeButtonState(ButtonState.ENABLED) + store.dispatch(GlobalAction.DebugShowError(TapError.CustomError(it))) + } + + store.dispatch(GlobalAction.ReadCard(service::onSuccess, { + changeButtonState(ButtonState.ENABLED) + store.dispatch(GlobalAction.DebugShowError(TapError.ScanCardError)) + })) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt index 3c031ed7b2..6fe2462f08 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt @@ -13,18 +13,27 @@ private fun internalReduce(action: Action, state: AppState): HomeState { if (action !is HomeAction) return state.homeState - var homeState = state.homeState + var state = state.homeState when (action) { - is HomeAction.CheckIfFirstLaunch.Result -> { - homeState = homeState.copy(firstLaunch = action.firstLaunch) + is HomeAction.SetFragmentShareTransition -> { + state = state.copy(shareTransition = action.shareTransition) + } + is HomeAction.SetTermsOfUseState -> { + state = state.copy(isDisclaimerAccepted = action.isDisclaimerAccepted) + } + is HomeAction.SetOpenUrl -> { + state = state.copy(openUrl = action.url) + } + is HomeAction.ChangeScanCardButtonState -> { + state = state.copy(btnScanState = action.state) } is HomeAction.ShowDialog.ScanFails -> { - homeState = homeState.copy(dialog = HomeDialog.ScanFailsDialog) + state = state.copy(dialog = HomeDialog.ScanFailsDialog) } is HomeAction.HideDialog -> { - homeState = homeState.copy(dialog = null) + state = state.copy(dialog = null) } } - return homeState + return state } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt index c9fce9b492..9a261cd1bf 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt @@ -1,13 +1,19 @@ package com.tangem.tap.features.home.redux +import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.navigation.FragmentShareTransition +import com.tangem.tap.features.send.redux.states.ButtonState import org.rekotlin.StateType data class HomeState( - val firstLaunch: Boolean = true, - val dialog: StateDialog? = null + val openUrl: String? = null, + val isDisclaimerAccepted: Boolean = false, + val shareTransition: FragmentShareTransition? = null, + val btnScanState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.ENABLED), + val dialog: StateDialog? = null, ) : StateType -sealed class HomeDialog: StateDialog { - object ScanFailsDialog: HomeDialog() +sealed class HomeDialog : StateDialog { + object ScanFailsDialog : HomeDialog() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt new file mode 100644 index 0000000000..c54cfc75e8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingService.kt @@ -0,0 +1,168 @@ +package com.tangem.tap.features.onboarding + +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.common.card.Card +import com.tangem.common.extensions.guard +import com.tangem.common.services.Result +import com.tangem.operations.attestation.OnlineCardVerifier +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.TapWalletManager +import com.tangem.tap.domain.TapWorkarounds.isTangemNote +import com.tangem.tap.domain.TapWorkarounds.isTangemWallet +import com.tangem.tap.domain.UrlBitmapLoader +import com.tangem.tap.domain.extensions.getArtworkUrl +import com.tangem.tap.domain.extensions.hasWallets +import com.tangem.tap.domain.extensions.makePrimaryWalletManager +import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.twins.isTangemTwin +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction +import com.tangem.tap.features.onboarding.redux.OnboardingData +import com.tangem.tap.features.wallet.redux.Artwork +import com.tangem.tap.persistence.UsedCardsPrefStorage +import com.tangem.tap.store +import java.math.BigDecimal + +/** +[REDACTED_AUTHOR] + */ +class OnboardingService( + private val tapWalletManager: TapWalletManager, + private val cardInfoStorage: UsedCardsPrefStorage +) { + + var onReadyToProceed: ((OnboardingData) -> Unit)? = null + set(value) { + field = value + onboardingDataLinker.onReady = { value?.invoke(it) } + } + var onFailedToProceedToOnboardingCase: (suspend (ScanNoteResponse) -> Unit)? = null + var onError: ((String) -> Unit)? = null + + private val onboardingDataLinker = OnboardingDataLinker() + + suspend fun onSuccess(response: ScanNoteResponse) { + onboardingDataLinker.scanNoteResponse = response + val card = response.card + val hasWallets = card.hasWallets() + + if (!card.isTangemNote() && !card.isTangemWallet() && !card.isTangemTwin()) { + if (hasWallets) { + cardInfoStorage.activated(card.cardId) + onFailedToProceedToOnboardingCase?.invoke(response) + return + } else { + cardInfoStorage.activated(card.cardId) // вызвать только после того, как был создан кошелек + onboardingDataLinker.navigateTo = AppScreen.OnboardingOther + } + } + if (cardInfoStorage.wasActivated(card.cardId)) { + onFailedToProceedToOnboardingCase?.invoke(response) + return + } + + loadCardImage(card) { onboardingDataLinker.cardArtwork = it } + + if (hasWallets) { + val walletManagerFactory = tapWalletManager.walletManagerFactory + when { + card.isTangemNote() -> loadBalanceNote(walletManagerFactory, response) + card.isTangemWallet() -> loadBalanceWallet(walletManagerFactory, response) + card.isTangemTwin() -> loadBalanceTwins(walletManagerFactory, response) + } + } else { + onboardingDataLinker.valueOfWalletAmount = BigDecimal.ZERO + } + + onboardingDataLinker.navigateTo = when { + card.isTangemNote() -> AppScreen.OnboardingNote + card.isTangemWallet() -> AppScreen.OnboardingWallet + card.isTangemTwin() -> AppScreen.OnboardingTwins + else -> AppScreen.OnboardingOther + } + } + + private suspend fun loadBalanceNote(walletManagerFactory: WalletManagerFactory, response: ScanNoteResponse) { + val walletManager: WalletManager = walletManagerFactory.makePrimaryWalletManager(response).guard { + onError?.invoke("Can't proceed: primary wallet manager didn't create") + return + } + walletManager.update() + store.dispatch(OnboardingNoteAction.SetWalletManager(walletManager)) + + val wallet = walletManager.wallet + val valueOfAmount = wallet.amounts[AmountType.Coin]?.value + if (valueOfAmount == null) { + onError?.invoke("Amount can't be NULL") + return + } + + onboardingDataLinker.valueOfWalletAmount = valueOfAmount + } + + private suspend fun loadBalanceWallet(walletManagerFactory: WalletManagerFactory, response: ScanNoteResponse) { + + } + + private suspend fun loadBalanceTwins(walletManagerFactory: WalletManagerFactory, response: ScanNoteResponse) { + + } + + private suspend fun loadCardImage(card: Card, callback: (Artwork?) -> Unit) { + fun loadBitmap(url: String, callback: (Artwork?) -> Unit) { + UrlBitmapLoader().loadBitmap(url) { + when (it) { + is Result.Success -> callback(Artwork(url, it.data)) + is Result.Failure -> callback(null) + } + } + } + + when (val cardInfoResult = OnlineCardVerifier().getCardInfo(card.cardId, card.cardPublicKey)) { + is Result.Success -> { + val artworkUrl = card.getArtworkUrl(cardInfoResult.data.artwork?.id) ?: Artwork.DEFAULT_IMG_URL + loadBitmap(artworkUrl, callback) + } + is Result.Failure -> loadBitmap(Artwork.DEFAULT_IMG_URL, callback) + } + } + +} + +class OnboardingDataLinker { + var onReady: ((OnboardingData) -> Unit)? = null + + internal var scanNoteResponse: ScanNoteResponse? = null + set(value) { + field = value + tryToProceed() + } + internal var cardArtwork: Artwork? = null + set(value) { + field = value + tryToProceed() + } + internal var valueOfWalletAmount: BigDecimal? = null + set(value) { + field = value + tryToProceed() + } + internal var navigateTo: AppScreen? = null + set(value) { + field = value + tryToProceed() + } + + private fun tryToProceed() { + if (!isReady()) return + + val data = OnboardingData(scanNoteResponse!!, cardArtwork!!, valueOfWalletAmount, navigateTo!!) + onReady?.invoke(data) + } + + private fun isReady(): Boolean { + val objList = mutableListOf(scanNoteResponse, cardArtwork, valueOfWalletAmount, navigateTo) + return !objList.any { it == null } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt new file mode 100644 index 0000000000..730c8760fc --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -0,0 +1,133 @@ +package com.tangem.tap.features.onboarding.products.note + +import android.os.Bundle +import android.view.View +import androidx.core.view.isVisible +import androidx.transition.TransitionInflater +import com.tangem.tap.common.extensions.addOnBackPressedDispatcher +import com.tangem.tap.common.extensions.beginDelayedTransition +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState +import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep +import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.fragment_onboarding.* +import org.rekotlin.StoreSubscriber + +/** +[REDACTED_AUTHOR] + */ +class OnboardingNoteFragment : BaseStoreFragment(R.layout.fragment_onboarding), StoreSubscriber { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + activity?.addOnBackPressedDispatcher { + store.dispatch(NavigationAction.PopBackTo()) + store.dispatch(OnboardingAction.SetInitialStepOfScreen(OnboardingNoteStep.None)) + } + + val inflater = TransitionInflater.from(requireContext()) + enterTransition = inflater.inflateTransition(R.transition.fade) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + store.dispatch(OnboardingNoteAction.Init) + + imv_front_card.setImageBitmap(store.state.onboardingState.onboardingData?.cardArtwork?.artwork) + } + + override fun subscribeToStore() { + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.onboardingNoteState == newState.onboardingNoteState + }.select { it.onboardingNoteState } + } + } + + override fun newState(state: OnboardingNoteState) { + if (activity == null) return + + pb_state.max = state.steps.size - 1 + + onboarding_main_container.beginDelayedTransition() + when (state.currentStep) { + OnboardingNoteStep.None -> setupNoneState(state) + OnboardingNoteStep.CreateWallet -> setupCreateWalletState(state) + OnboardingNoteStep.TopUpWallet -> setupTopUpWalletState(state) + OnboardingNoteStep.Done -> setupSuccessState(state) + } + } + + private fun setupNoneState(state: OnboardingNoteState) { + pb_state.progress = state.progress + + btn_main_action.isVisible = true + btn_alternative_action.isVisible = true + btn_check_balance.isVisible = true + tv_header.isVisible = true + tv_body.isVisible = true + + btn_main_action.text = "NULL" + btn_alternative_action.text = "NULL" + btn_check_balance.text = "NULL balance" + tv_header.text = "NULL" + tv_body.text = "NULL" + } + + private fun setupCreateWalletState(state: OnboardingNoteState) { + pb_state.progress = state.progress + + btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet) + btn_main_action.setOnClickListener { store.dispatch(OnboardingNoteAction.CreateWallet) } + + btn_alternative_action.setText(R.string.onboarding_button_what_does_it_mean) + btn_alternative_action.setOnClickListener { } + + btn_check_balance.isVisible = false + btn_check_balance.setOnClickListener { } + + tv_header.setText(R.string.onboarding_create_wallet_header) + tv_body.setText(R.string.onboarding_create_wallet_body) + } + + private fun setupTopUpWalletState(state: OnboardingNoteState) { + pb_state.progress = state.progress + + btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto) + btn_main_action.setOnClickListener { store.dispatch(OnboardingNoteAction.TopUp) } + + btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address) + btn_alternative_action.setOnClickListener { } + + btn_check_balance.isVisible = false + btn_check_balance.setOnClickListener { } + + tv_header.setText(R.string.onboarding_top_up_header) + tv_body.setText(R.string.onboarding_top_up_body) + } + + private fun setupSuccessState(state: OnboardingNoteState) { + pb_state.progress = state.progress + + btn_main_action.setText(R.string.onboarding_done_button_continue) + btn_main_action.setOnClickListener { store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } + + btn_alternative_action.isVisible = false + btn_alternative_action.setText("") + btn_alternative_action.setOnClickListener { } + + btn_check_balance.isVisible = false + btn_check_balance.setOnClickListener { } + + tv_header.setText(R.string.onboarding_done_header) + tv_body.setText(R.string.onboarding_done_body) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt new file mode 100644 index 0000000000..bab556d68b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.blockchain.common.WalletManager +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class OnboardingNoteAction : Action { + object Init : OnboardingNoteAction() + object CreateWallet : OnboardingNoteAction() + object TopUp : OnboardingNoteAction() + object Done : OnboardingNoteAction() + + data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt new file mode 100644 index 0000000000..7f5ee16c3f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -0,0 +1,72 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.common.CompletionResult +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep +import com.tangem.tap.scope +import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager +import kotlinx.coroutines.launch +import org.rekotlin.Action +import org.rekotlin.Middleware + +class OnboardingNoteMiddleware { + companion object { + private const val CARD_SHOP_URI = + "https://shop.tangem.com/?afmc=1i&utm_campaign=1i&utm_source=leaddyno&utm_medium=affiliate" + val handler = onboardingNoteMiddleware + } +} + +private val onboardingNoteMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + handleMainOnboardingAction(action, ::handleNoteAction) + next(action) + } + } +} + +private fun handleMainOnboardingAction(action: Action, isNotMainAction: (Action) -> Unit) { + val action = action as? OnboardingAction ?: isNotMainAction(action) + + when (action) { + } +} + +private fun handleNoteAction(action: Action) { + val onboardingState = store.state.onboardingState + val noteState = store.state.onboardingNoteState + + when (action) { + is OnboardingNoteAction.SetWalletManager -> { + } + is OnboardingNoteAction.Init -> { + val step = if (noteState.walletManager == null) { + OnboardingNoteStep.CreateWallet + } else { + OnboardingNoteStep.TopUpWallet + } + store.dispatch(OnboardingAction.SetInitialStepOfScreen(step)) + } + is OnboardingNoteAction.CreateWallet -> { + scope.launch { + val scanNoteResponse = onboardingState.onboardingData?.scanNoteResponse + val result = tangemSdkManager.createWallet(scanNoteResponse?.card?.cardId) + when (result) { + is CompletionResult.Success -> { +// val scanNoteResponse = scanNoteResponse!!.copy(card = result.data) +// store.dispatch(OnboardingAction.SetScanNoteResponse(scanNoteResponse)) + } + is CompletionResult.Failure -> { + store.dispatch(OnboardingAction.Error("Failed to create wallet")) + } + } + } + } + is OnboardingNoteAction.TopUp -> { + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt new file mode 100644 index 0000000000..b6a50be846 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt @@ -0,0 +1,38 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep +import org.rekotlin.Action + +class OnboardingNoteReducer { + companion object { + fun reduce(action: Action, state: AppState): OnboardingNoteState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, appState: AppState): OnboardingNoteState { + var state = appState.onboardingNoteState + + when (action) { + is OnboardingAction.SetInitialStepOfScreen -> { + (action.step as? OnboardingNoteStep)?.let { + if (it != state.currentStep && state.steps.contains(it)) { + state = state.copy(currentStep = it) + } + } + } + OnboardingAction.SwitchToNextStep -> { + val progress = state.progress + if (progress >= 0 && progress != state.steps.size - 1) { + state = state.copy(currentStep = state.steps[progress + 1]) + } + } + is OnboardingNoteAction.SetWalletManager -> { + state = state.copy(walletManager = action.walletManager) + } + + } + + return state +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt new file mode 100644 index 0000000000..ae5677779f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -0,0 +1,18 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.features.onboarding.redux.OnboardingNoteStep +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class OnboardingNoteState( + val walletManager: WalletManager? = null, + val currentStep: OnboardingNoteStep = OnboardingNoteStep.None, + val steps: List = OnboardingNoteStep.values().toList(), +) : StateType { + + val progress: Int + get() = steps.indexOf(currentStep) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/old/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/old/OnboardingOtherCardsFragment.kt new file mode 100644 index 0000000000..cada557578 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/old/OnboardingOtherCardsFragment.kt @@ -0,0 +1,50 @@ +package com.tangem.tap.features.onboarding.products.old + +import android.os.Bundle +import android.view.View +import androidx.transition.TransitionInflater +import com.tangem.tap.common.extensions.addOnBackPressedDispatcher +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.onboarding.redux.OnboardingOtherCardsState +import com.tangem.tap.features.onboarding.redux.OnboardingOtherStep +import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.store +import com.tangem.wallet.R +import org.rekotlin.StoreSubscriber + +/** +[REDACTED_AUTHOR] + */ +class OnboardingOtherCardsFragment : BaseStoreFragment(R.layout.fragment_onboarding), StoreSubscriber { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + activity?.addOnBackPressedDispatcher { + store.dispatch(NavigationAction.PopBackTo()) + store.dispatch(OnboardingAction.SetInitialStepOfScreen(OnboardingOtherStep.Done)) + } + + val inflater = TransitionInflater.from(requireContext()) + enterTransition = inflater.inflateTransition(R.transition.fade) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } + + override fun subscribeToStore() { +// store.subscribe(this) { state -> +// state.skipRepeats { oldState, newState -> +// oldState.onboardingOtherState == newState.onboardingOtherState +// }.select { it.onboardingOtherState } +// } + } + + override fun newState(state: OnboardingOtherCardsState) { + if (activity == null) return + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt new file mode 100644 index 0000000000..5b2eba2423 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt @@ -0,0 +1,52 @@ +package com.tangem.tap.features.onboarding.products.wallet + +import android.os.Bundle +import android.view.View +import androidx.transition.TransitionInflater +import com.tangem.tap.common.extensions.addOnBackPressedDispatcher +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState +import com.tangem.tap.features.onboarding.redux.OnboardingAction +import com.tangem.tap.features.onboarding.redux.OnboardingOtherStep +import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.store +import com.tangem.wallet.R +import org.rekotlin.StoreSubscriber + +/** +[REDACTED_AUTHOR] + */ +class OnboardingWalletFragment : BaseStoreFragment(R.layout.fragment_onboarding_wallet), StoreSubscriber { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + activity?.addOnBackPressedDispatcher { + store.dispatch(NavigationAction.PopBackTo()) + store.dispatch(OnboardingAction.SetInitialStepOfScreen(OnboardingOtherStep.Done)) + } + + val inflater = TransitionInflater.from(requireContext()) + enterTransition = inflater.inflateTransition(R.transition.fade) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + store.dispatch(OnboardingAction.OnboardingOtherAction.Init) + } + + override fun subscribeToStore() { + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.onboardingWalletState == newState.onboardingWalletState + }.select { it.onboardingWalletState } + } + } + + override fun newState(state: OnboardingWalletState) { + if (activity == null) return + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt new file mode 100644 index 0000000000..5cfd213d57 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt @@ -0,0 +1,19 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class OnboardingWalletAction : Action { + object Init : OnboardingWalletAction() + object CreateWallet : OnboardingWalletAction() + object TopUp : OnboardingWalletAction() + object Done : OnboardingWalletAction() + + object ProceedBackup : OnboardingWalletAction() + + sealed class Backup : Action { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt new file mode 100644 index 0000000000..c442090de4 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -0,0 +1,18 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import com.tangem.tap.common.redux.AppState +import org.rekotlin.Middleware + +class OnboardingWalletMiddleware { + companion object { + val handler = onboardingWalletMiddleware + } +} + +private val onboardingWalletMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + next(action) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt new file mode 100644 index 0000000000..427f44048e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt @@ -0,0 +1,20 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import com.tangem.tap.common.redux.AppState +import org.rekotlin.Action + +class OnboardingWalletReducer { + companion object { + fun reduce(action: Action, state: AppState): OnboardingWalletState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, appState: AppState): OnboardingWalletState { + var state = appState.onboardingWalletState + + when (action) { + + } + + return state +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt new file mode 100644 index 0000000000..9b8579171a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt @@ -0,0 +1,15 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import com.tangem.tap.features.onboarding.redux.OnboardingStep +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class OnboardingWalletState( + val any: String? = null +) : StateType + +enum class OnboardingWalletStep : OnboardingStep { + None, CreateWallet, TopUpWallet, Backup, Done +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt new file mode 100644 index 0000000000..7a904b47ee --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingAction.kt @@ -0,0 +1,22 @@ +package com.tangem.tap.features.onboarding.redux + +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class OnboardingAction : Action { + + data class SetInitialStepOfScreen(val step: OnboardingStep) : OnboardingAction() + object SwitchToNextStep : OnboardingAction() + + data class SetData(val data: OnboardingData) : OnboardingAction() + + sealed class OnboardingOtherAction : Action { + object Init : OnboardingOtherAction() + object CreateWallet : OnboardingOtherAction() + object Done : OnboardingOtherAction() + } + + class Error(val message: String) : OnboardingAction() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt new file mode 100644 index 0000000000..ea373a5f41 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingMiddleware.kt @@ -0,0 +1,22 @@ +package com.tangem.tap.features.onboarding.redux + +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.store +import org.rekotlin.Middleware + +class OnboardingMiddleware { + companion object { + val handler = onboardingMiddleware + } +} + +private val onboardingMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + val onboardingState = store.state.onboardingState + when (action) { + } + next(action) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt new file mode 100644 index 0000000000..5ee024922d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingReducer.kt @@ -0,0 +1,24 @@ +package com.tangem.tap.features.onboarding.redux + +import com.tangem.tap.common.redux.AppState +import org.rekotlin.Action + +class OnboardingReducer { + companion object { + fun reduce(action: Action, state: AppState): OnboardingState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, appState: AppState): OnboardingState { + + if (action !is OnboardingAction) return appState.onboardingState + + var state = appState.onboardingState + when (action) { + is OnboardingAction.SetData -> { + state = state.copy(onboardingData = action.data) + } + } + + return state +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt new file mode 100644 index 0000000000..2a4909de43 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/redux/OnboardingState.kt @@ -0,0 +1,11 @@ +package com.tangem.tap.features.onboarding.redux + +import org.rekotlin.StateType + +data class OnboardingState( + val onboardingData: OnboardingData? = null, +) : StateType + +data class OnboardingOtherCardsState( + val any: String? = null +) : StateType \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt b/app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt new file mode 100644 index 0000000000..cc422410a5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/redux/StateSupportClasses.kt @@ -0,0 +1,26 @@ +package com.tangem.tap.features.onboarding.redux + +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.features.wallet.redux.Artwork +import java.math.BigDecimal + +/** +[REDACTED_AUTHOR] + */ +data class OnboardingData( + val scanNoteResponse: ScanNoteResponse, + val cardArtwork: Artwork, + val balance: BigDecimal?, + val onboardingScreen: AppScreen +) + +interface OnboardingStep + +enum class OnboardingNoteStep : OnboardingStep { + None, CreateWallet, TopUpWallet, Done +} + +enum class OnboardingOtherStep : OnboardingStep { + CreateWallet, Done +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/TwinsOnboardingFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt similarity index 97% rename from app/src/main/java/com/tangem/tap/features/wallet/ui/TwinsOnboardingFragment.kt rename to app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt index 0d3abe02e2..6bfd2f28df 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/TwinsOnboardingFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt @@ -16,7 +16,7 @@ import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_twin_cards.* import kotlinx.android.synthetic.main.layout_twin_cards.* -class TwinsOnboardingFragment : Fragment(R.layout.fragment_twin_cards) { +class OnboardingTwinsFragment : Fragment(R.layout.fragment_twin_cards) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index b80b989b70..aac75ef60b 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -107,7 +107,7 @@ class SingleWalletView : WalletView { tv_twin_card_number.hide() } if (twinCardsState?.showTwinOnboarding == true) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) + store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins)) } diff --git a/app/src/main/res/drawable/bg_circle_gray.xml b/app/src/main/res/drawable/bg_circle_gray.xml new file mode 100644 index 0000000000..94a585da83 --- /dev/null +++ b/app/src/main/res/drawable/bg_circle_gray.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_home_card_back.xml b/app/src/main/res/drawable/ic_home_card_back.xml new file mode 100644 index 0000000000..8a71b20ce5 --- /dev/null +++ b/app/src/main/res/drawable/ic_home_card_back.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_home_card_front.xml b/app/src/main/res/drawable/ic_home_card_front.xml new file mode 100644 index 0000000000..3bbf5a93dd --- /dev/null +++ b/app/src/main/res/drawable/ic_home_card_front.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 33157a5fe6..1ebc971c29 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -2,95 +2,126 @@ + android:layout_height="match_parent" + app:layoutDescription="@xml/fragment_welcome_scene"> - - - + android:src="@drawable/ic_home_card_back" + android:translationY="-55dp" + app:layout_constraintEnd_toEndOf="@+id/imv_front_card" + app:layout_constraintStart_toStartOf="@+id/imv_front_card" + app:layout_constraintTop_toBottomOf="@+id/imv_front_card" /> - - - - + android:src="@drawable/ic_home_card_front" + android:transitionName="imv_front_card" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.496" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/view" /> + android:layout_marginBottom="14dp" + android:fontFamily="sans-serif-medium" + android:text="@string/home_welcome_header" + android:textColor="#060606" + android:textSize="24sp" + app:layout_constraintBottom_toTopOf="@+id/tv_home_body" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.508" + app:layout_constraintStart_toStartOf="parent" /> + + + + + + + + + + + app:layout_constraintGuide_percent="0.5" + app:layout_constraintTop_toTopOf="parent" /> - + app:layout_constraintHorizontal_bias="0.498" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - \ No newline at end of file + + diff --git a/app/src/main/res/layout/fragment_onboarding.xml b/app/src/main/res/layout/fragment_onboarding.xml new file mode 100644 index 0000000000..710cf8a7c5 --- /dev/null +++ b/app/src/main/res/layout/fragment_onboarding.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_onboarding_wallet.xml b/app/src/main/res/layout/fragment_onboarding_wallet.xml new file mode 100644 index 0000000000..77d9ef65f8 --- /dev/null +++ b/app/src/main/res/layout/fragment_onboarding_wallet.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file