Updated on 2026-08-14
This commit is contained in:
commit
d314c8733a
21 changed files with 57 additions and 40 deletions
|
|
@ -108,11 +108,11 @@ internal var lockUserWalletsTimer: LockUserWalletsTimer? = null
|
|||
var notificationsHandler: NotificationsHandler? = null
|
||||
|
||||
private val coroutineContext: CoroutineContext
|
||||
get() = Job() + Dispatchers.IO + FeatureCoroutineExceptionHandler.create("scope")
|
||||
get() = SupervisorJob() + Dispatchers.IO + FeatureCoroutineExceptionHandler.create("scope")
|
||||
val scope = CoroutineScope(coroutineContext)
|
||||
|
||||
private val mainCoroutineContext: CoroutineContext
|
||||
get() = Job() + Dispatchers.Main + FeatureCoroutineExceptionHandler.create("mainScope")
|
||||
get() = SupervisorJob() + Dispatchers.Main + FeatureCoroutineExceptionHandler.create("mainScope")
|
||||
val mainScope = CoroutineScope(mainCoroutineContext)
|
||||
|
||||
@Suppress("LargeClass")
|
||||
|
|
|
|||
|
|
@ -623,7 +623,12 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
if (action.unfinishedBackupScanResponse != null) {
|
||||
// onboarding V2
|
||||
store.dispatchNavigationAction {
|
||||
push(AppRoute.Onboarding(scanResponse = action.unfinishedBackupScanResponse))
|
||||
push(
|
||||
AppRoute.Onboarding(
|
||||
scanResponse = action.unfinishedBackupScanResponse,
|
||||
mode = AppRoute.Onboarding.Mode.ContinueFinalize,
|
||||
),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
store.dispatch(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import com.tangem.wallet.databinding.LayoutBackupAccessCodeBinding
|
|||
|
||||
class AccessCodeDialog(context: Context) : BottomSheetDialog(context) {
|
||||
|
||||
var binding: LayoutBackupAccessCodeBinding? = null
|
||||
private var binding: LayoutBackupAccessCodeBinding? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -33,7 +33,7 @@ class AccessCodeDialog(context: Context) : BottomSheetDialog(context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun showInfoScreen() = with(binding!!) {
|
||||
fun showInfoScreen() = binding?.run {
|
||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
accessCodeTitle.text = context.getText(R.string.onboarding_access_code_intro_title)
|
||||
layoutBackupAccessCodeInfo.root.show()
|
||||
|
|
@ -43,7 +43,7 @@ class AccessCodeDialog(context: Context) : BottomSheetDialog(context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun showEnterAccessCode() = with(binding!!) {
|
||||
fun showEnterAccessCode() = binding?.run {
|
||||
layoutBackupAccessCodeInfo.root.hide()
|
||||
layoutBackupAccessCodeSubmit.root.show()
|
||||
accessCodeTitle.text = context.getText(R.string.onboarding_access_code_intro_title)
|
||||
|
|
@ -56,7 +56,7 @@ class AccessCodeDialog(context: Context) : BottomSheetDialog(context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun showReenterAccessCode() = with(binding!!) {
|
||||
fun showReenterAccessCode() = binding?.run {
|
||||
layoutBackupAccessCodeInfo.root.hide()
|
||||
layoutBackupAccessCodeSubmit.root.show()
|
||||
accessCodeTitle.text = context.getText(R.string.onboarding_access_code_repeat_code_title)
|
||||
|
|
@ -69,7 +69,7 @@ class AccessCodeDialog(context: Context) : BottomSheetDialog(context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun showError(error: AccessCodeError?) = with(binding!!.layoutBackupAccessCodeSubmit) {
|
||||
fun showError(error: AccessCodeError?) = binding?.layoutBackupAccessCodeSubmit?.run {
|
||||
when (error) {
|
||||
AccessCodeError.CodeTooShort ->
|
||||
tilAccessCode.error = context.getString(R.string.onboarding_access_code_too_short)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.welcome.model
|
|||
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.finisher.AppFinisher
|
||||
|
|
@ -14,7 +15,6 @@ import com.tangem.tap.features.welcome.redux.WelcomeAction
|
|||
import com.tangem.tap.features.welcome.redux.WelcomeState
|
||||
import com.tangem.tap.features.welcome.ui.WelcomeScreenState
|
||||
import com.tangem.tap.features.welcome.ui.model.WarningModel
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -23,6 +23,7 @@ import org.rekotlin.StoreSubscriber
|
|||
import javax.inject.Inject
|
||||
|
||||
// FIXME: Remove redux: [REDACTED_JIRA]
|
||||
@ModelScoped
|
||||
internal class WelcomeModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val appFinisher: AppFinisher,
|
||||
|
|
@ -40,8 +41,6 @@ internal class WelcomeModel @Inject constructor(
|
|||
val state: MutableStateFlow<WelcomeScreenState> = MutableStateFlow(initialState)
|
||||
|
||||
init {
|
||||
store.dispatch(WelcomeAction.SetCoroutineScope(mainScope))
|
||||
|
||||
subscribeToStoreChanges()
|
||||
initGlobalState()
|
||||
|
||||
|
|
@ -87,7 +86,6 @@ internal class WelcomeModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
store.dispatch(WelcomeAction.ClearCoroutineScope)
|
||||
store.unsubscribe(subscriber = this)
|
||||
|
||||
super.onDestroy()
|
||||
|
|
|
|||
|
|
@ -2,15 +2,10 @@ package com.tangem.tap.features.welcome.redux
|
|||
|
||||
import android.content.Intent
|
||||
import com.tangem.common.core.TangemError
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.rekotlin.Action
|
||||
|
||||
internal sealed interface WelcomeAction : Action {
|
||||
|
||||
data class SetCoroutineScope(val scope: CoroutineScope) : WelcomeAction
|
||||
|
||||
object ClearCoroutineScope : WelcomeAction
|
||||
|
||||
data class ProceedWithBiometrics(val afterUnlockIntent: Intent? = null) : WelcomeAction {
|
||||
object Success : WelcomeAction
|
||||
data class Error(val error: TangemError) : WelcomeAction
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import com.tangem.tap.common.extensions.*
|
|||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler
|
||||
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -44,7 +45,7 @@ internal class WelcomeMiddleware {
|
|||
}
|
||||
|
||||
private fun handleAction(action: WelcomeAction, state: WelcomeState) {
|
||||
state.scope?.launch {
|
||||
mainScope.launch {
|
||||
when (action) {
|
||||
is WelcomeAction.ProceedWithIntent -> proceedWithIntent(action.intent, scope = this)
|
||||
is WelcomeAction.ProceedWithBiometrics -> proceedWithBiometrics(
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ internal object WelcomeReducer {
|
|||
|
||||
private fun internalReduce(action: WelcomeAction, state: WelcomeState): WelcomeState {
|
||||
return when (action) {
|
||||
is WelcomeAction.SetCoroutineScope -> state.copy(scope = action.scope)
|
||||
is WelcomeAction.ClearCoroutineScope -> state.copy(scope = null)
|
||||
is WelcomeAction.ProceedWithIntent -> state.copy(intent = action.intent)
|
||||
is WelcomeAction.ProceedWithBiometrics -> state.copy(isUnlockWithBiometricsInProgress = true)
|
||||
is WelcomeAction.ProceedWithCard -> state.copy(isUnlockWithCardInProgress = true)
|
||||
|
|
|
|||
|
|
@ -2,11 +2,9 @@ package com.tangem.tap.features.welcome.redux
|
|||
|
||||
import android.content.Intent
|
||||
import com.tangem.common.core.TangemError
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.rekotlin.StateType
|
||||
|
||||
data class WelcomeState(
|
||||
val scope: CoroutineScope? = null,
|
||||
val isUnlockWithBiometricsInProgress: Boolean = false,
|
||||
val isUnlockWithCardInProgress: Boolean = false,
|
||||
val intent: Intent? = null,
|
||||
|
|
|
|||
|
|
@ -216,6 +216,8 @@ internal class ChildFactory @Inject constructor(
|
|||
AppRoute.Onboarding.Mode.WelcomeOnlyTwin -> OnboardingEntryComponent.Mode.WelcomeOnlyTwin
|
||||
AppRoute.Onboarding.Mode.RecreateWalletTwin ->
|
||||
OnboardingEntryComponent.Mode.RecreateWalletTwin
|
||||
AppRoute.Onboarding.Mode.ContinueFinalize ->
|
||||
OnboardingEntryComponent.Mode.ContinueFinalize
|
||||
},
|
||||
),
|
||||
componentFactory = onboardingEntryComponentFactory,
|
||||
|
|
@ -729,6 +731,8 @@ internal class ChildFactory @Inject constructor(
|
|||
AppRoute.Onboarding.Mode.WelcomeOnlyTwin -> OnboardingEntryComponent.Mode.WelcomeOnlyTwin
|
||||
AppRoute.Onboarding.Mode.RecreateWalletTwin ->
|
||||
OnboardingEntryComponent.Mode.RecreateWalletTwin
|
||||
AppRoute.Onboarding.Mode.ContinueFinalize ->
|
||||
OnboardingEntryComponent.Mode.ContinueFinalize
|
||||
},
|
||||
),
|
||||
componentFactory = onboardingEntryComponentFactory,
|
||||
|
|
|
|||
|
|
@ -268,6 +268,7 @@ sealed class AppRoute(val path: String) : Route {
|
|||
AddBackupWallet1, // continue backup process for existing wallet 1
|
||||
WelcomeOnlyTwin, // show welcome screen and then navigate to wallet for twins
|
||||
RecreateWalletTwin, // reset twins
|
||||
ContinueFinalize, // continue finalize process (unfinished backup dialog)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,14 @@ import dagger.Module
|
|||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object TxHistoryItemsStoreModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTxHistoryItemsStore(): TxHistoryItemsStore {
|
||||
return DefaultTxHistoryItemsStore(
|
||||
dataStore = RuntimeDataStore(),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.core.ui.utils
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
|
|
@ -16,7 +15,7 @@ import com.google.accompanist.permissions.rememberPermissionState
|
|||
@Composable
|
||||
fun requestPushPermission(
|
||||
pushPermission: String?,
|
||||
isClicked: MutableState<Boolean>,
|
||||
isClicked: Boolean,
|
||||
onAllow: () -> Unit,
|
||||
onDeny: () -> Unit,
|
||||
): () -> Unit {
|
||||
|
|
@ -26,7 +25,7 @@ fun requestPushPermission(
|
|||
|
||||
// Check if user granted permission and close bottom sheet
|
||||
LaunchedEffect(key1 = permissionState?.status, isClicked) {
|
||||
if (!isClicked.value) return@LaunchedEffect
|
||||
if (!isClicked) return@LaunchedEffect
|
||||
if (permissionState?.status?.isGranted == true) {
|
||||
onAllow()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ interface OnboardingEntryComponent : ComposableContentComponent {
|
|||
AddBackupWallet1,
|
||||
WelcomeOnlyTwin,
|
||||
RecreateWalletTwin,
|
||||
ContinueFinalize,
|
||||
}
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnboardingEntryComponent>
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ internal class OnboardingEntryModel @Inject constructor(
|
|||
val multiWalletNavigationMode = when (params.mode) {
|
||||
Mode.Onboarding -> OnboardingMultiWalletComponent.Mode.Onboarding
|
||||
Mode.AddBackupWallet1 -> OnboardingMultiWalletComponent.Mode.AddBackup
|
||||
Mode.ContinueFinalize -> OnboardingMultiWalletComponent.Mode.ContinueFinalize
|
||||
else -> error("Incorrect onboarding type")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ interface OnboardingMultiWalletComponent : ComposableContentComponent, InnerNavi
|
|||
enum class Mode {
|
||||
Onboarding,
|
||||
AddBackup,
|
||||
ContinueFinalize,
|
||||
}
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnboardingMultiWalletComponent>
|
||||
|
|
|
|||
|
|
@ -39,9 +39,8 @@ fun Wallet1ChooseOption(
|
|||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.weight(1f)
|
||||
.padding(top = 32.dp, bottom = 16.dp),
|
||||
.padding(vertical = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
val pagerState = rememberPagerState { CarouselItems.size }
|
||||
|
|
@ -49,6 +48,8 @@ fun Wallet1ChooseOption(
|
|||
HorizontalPager(pagerState) { selectedIndex ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(start = 32.dp, end = 32.dp, bottom = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -226,7 +226,9 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
val userWalletCreated = createUserWallet(scanResponse)
|
||||
|
||||
val userWallet = when (params.parentParams.mode) {
|
||||
OnboardingMultiWalletComponent.Mode.Onboarding -> {
|
||||
OnboardingMultiWalletComponent.Mode.Onboarding,
|
||||
OnboardingMultiWalletComponent.Mode.ContinueFinalize,
|
||||
-> {
|
||||
userWalletsListManager.save(
|
||||
userWallet = userWalletCreated.copy(
|
||||
scanResponse = scanResponse.updateScanResponseAfterBackup(),
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
|||
currentStep = getInitialStep(),
|
||||
currentScanResponse = params.scanResponse,
|
||||
accessCode = null,
|
||||
isThreeCards = true,
|
||||
isThreeCards = isThreeCardsInit(),
|
||||
resultUserWallet = null,
|
||||
startFromFinalize = getInitialStartFromFinalize(),
|
||||
),
|
||||
|
|
@ -102,6 +102,8 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
|||
val card = scanResponse.card
|
||||
|
||||
return when {
|
||||
params.mode == OnboardingMultiWalletComponent.Mode.ContinueFinalize ->
|
||||
OnboardingMultiWalletState.Step.Finalize
|
||||
// Add backup button
|
||||
// Wallet1 without backup and userwallet's scanResponse doesn't contain primary card.
|
||||
card.wallets.isNotEmpty() && card.backupStatus == CardDTO.BackupStatus.NoBackup &&
|
||||
|
|
@ -134,6 +136,18 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun isThreeCardsInit(): Boolean {
|
||||
val backupService = backupServiceHolder.backupService.get() ?: return true
|
||||
return when (backupService.currentState) {
|
||||
BackupService.State.FinalizingPrimaryCard,
|
||||
is BackupService.State.FinalizingBackupCard,
|
||||
-> {
|
||||
backupService.addedBackupCardsCount > 1
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
private fun initScreenTitle() {
|
||||
val title = screenTitleByStep(getInitialStep())
|
||||
params.titleProvider.changeTitle(title)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import javax.inject.Inject
|
|||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
class PushNotificationsModel @Inject constructor(
|
||||
internal class PushNotificationsModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase,
|
||||
private val neverToInitiallyAskPermissionUseCase: NeverToInitiallyAskPermissionUseCase,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package com.tangem.features.pushnotifications.impl.presentation.ui
|
||||
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.showcase.Showcase
|
||||
import com.tangem.core.ui.components.showcase.model.ShowcaseButtonModel
|
||||
|
|
@ -21,7 +19,7 @@ internal fun PushNotificationsScreen(
|
|||
onAllowPermission: () -> Unit,
|
||||
onDenyPermission: () -> Unit,
|
||||
) {
|
||||
val isClicked = remember { mutableStateOf(false) }
|
||||
var isClicked by remember { mutableStateOf(false) }
|
||||
val requestPushPermission = requestPushPermission(
|
||||
isClicked = isClicked,
|
||||
onAllow = onAllowPermission,
|
||||
|
|
@ -45,7 +43,7 @@ internal fun PushNotificationsScreen(
|
|||
primaryButton = ShowcaseButtonModel(
|
||||
buttonText = resourceReference(R.string.common_allow),
|
||||
onClick = {
|
||||
isClicked.value = true
|
||||
isClicked = true
|
||||
onRequest()
|
||||
requestPushPermission()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ import androidx.compose.foundation.layout.Arrangement
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
|
|
@ -36,7 +34,7 @@ internal fun PushNotificationsBottomSheet(config: TangemBottomSheetConfig) {
|
|||
|
||||
@Composable
|
||||
private fun PushNotificationsSheetContent(content: PushNotificationsBottomSheetConfig, onDismiss: () -> Unit) {
|
||||
val isClicked = remember { mutableStateOf(false) }
|
||||
var isClicked by remember { mutableStateOf(false) }
|
||||
val requestPushPermission = requestPushPermission(
|
||||
pushPermission = getPushPermissionOrNull(),
|
||||
isClicked = isClicked,
|
||||
|
|
@ -86,7 +84,7 @@ private fun PushNotificationsSheetContent(content: PushNotificationsBottomSheetC
|
|||
PrimaryButton(
|
||||
text = stringResourceSafe(R.string.common_allow),
|
||||
onClick = {
|
||||
isClicked.value = true
|
||||
isClicked = true
|
||||
content.onRequest()
|
||||
requestPushPermission()
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue