Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-07 10:37:12 +03:00
commit 9488319315
29 changed files with 110 additions and 366 deletions

View file

@ -1,8 +1,6 @@
package com.tangem.tap.common.url
import android.content.Context
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.Intent.FLAG_ACTIVITY_NO_HISTORY
import android.net.Uri
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
@ -36,9 +34,6 @@ internal class CustomTabsUrlOpener : UrlOpener {
)
.build()
// Open CustomTabsActivity as new task without saving into the stack
customTabsIntent.intent.setFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_NO_HISTORY)
customTabsIntent.launchUrl(context, Uri.parse(url))
}
}

View file

@ -184,30 +184,6 @@ internal object SettingsDomainModule {
return NeverToInitiallyAskPermissionUseCase(repository = permissionRepository)
}
@Provides
@Singleton
fun provideIsFirstTimeAskingPermissionUseCase(
permissionRepository: PermissionRepository,
): IsFirstTimeAskingPermissionUseCase {
return IsFirstTimeAskingPermissionUseCase(repository = permissionRepository)
}
@Provides
@Singleton
fun provideSetFirstTimeAskingPushPermissionUseCase(
permissionRepository: PermissionRepository,
): SetFirstTimeAskingPermissionUseCase {
return SetFirstTimeAskingPermissionUseCase(repository = permissionRepository)
}
@Provides
@Singleton
fun provideDelayPermissionRequestUseCase(
permissionRepository: PermissionRepository,
): DelayPermissionRequestUseCase {
return DelayPermissionRequestUseCase(repository = permissionRepository)
}
@Provides
@Singleton
fun provideShouldAskPermissionUseCase(permissionRepository: PermissionRepository): ShouldAskPermissionUseCase {

View file

@ -161,6 +161,8 @@ internal class CardSettingsViewModel @Inject constructor(
if (scanResponse.cardTypesResolver.isTangemTwins()) {
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet(scanResponse)))
cardSettingsInteractor.clear()
store.dispatchNavigationAction { push(AppRoute.OnboardingTwins) }
} else {
val card = scanResponse.card

View file

@ -6,14 +6,13 @@ import android.net.Uri
[REDACTED_AUTHOR]
*/
interface Disclaimer {
fun type(): DisclaimerType
fun getUri(): Uri
suspend fun accept()
suspend fun isAccepted(): Boolean
}
abstract class BaseDisclaimer(
protected val dataProvider: DisclaimerDataProvider,
private val dataProvider: DisclaimerDataProvider,
) : Disclaimer {
val baseUrl = "https://tangem.com"
@ -26,46 +25,11 @@ abstract class BaseDisclaimer(
}
class DummyDisclaimer : Disclaimer {
override fun type(): DisclaimerType = DisclaimerType.Tangem
override fun getUri(): Uri = Uri.parse("https://tangem.com/tangem_tos.html")
override suspend fun accept() {}
override suspend fun isAccepted(): Boolean = false
}
class TangemDisclaimer(dataProvider: DisclaimerDataProvider) : BaseDisclaimer(dataProvider) {
override fun type(): DisclaimerType = DisclaimerType.Tangem
override fun getUri(): Uri = Uri.parse("$baseUrl/tangem_tos.html")
}
class Start2CoinDisclaimer(dataProvider: DisclaimerDataProvider) : BaseDisclaimer(dataProvider) {
override fun type(): DisclaimerType = DisclaimerType.Start2Coin
override fun getUri(): Uri = Uri.parse("$baseUrl/" + filename(dataProvider.getLanguage(), getRegion()))
@Suppress("ComplexMethod")
private fun filename(languageCode: String, regionCode: String?): String {
return when {
languageCode == "fr" && regionCode == "ch" -> "start2coin-fr-ch-tangem.html"
languageCode == "de" && regionCode == "ch" -> "start2coin-de-ch-tangem.html"
languageCode == "en" && regionCode == "ch" -> "start2coin-en-ch-tangem.html"
languageCode == "it" && regionCode == "ch" -> "start2coin-it-ch-tangem.html"
languageCode == "fr" && regionCode == "fr" -> "start2coin-fr-fr-tangem.html"
languageCode == "de" && regionCode == "at" -> "start2coin-de-at-tangem.html"
regionCode == "fr" -> "start2coin-fr-fr-tangem.html"
regionCode == "ch" -> "start2coin-en-ch-tangem.html"
regionCode == "at" -> "start2coin-de-at-tangem.html"
else -> "start2coin-fr-fr-tangem.html"
}
}
private fun getRegion(): String? {
val cardId = dataProvider.getCardId()
if (cardId.isEmpty()) return null
return when (cardId[1]) {
'0' -> "fr"
'1' -> "ch"
'2' -> "at"
else -> null
}
}
}

View file

@ -1,58 +1,28 @@
package com.tangem.tap.features.disclaimer
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.models.scan.CardDTO
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import java.util.Locale
/**
[REDACTED_AUTHOR]
*/
enum class DisclaimerType {
Tangem,
Start2Coin,
;
companion object {
fun get(cardDTO: CardDTO): DisclaimerType {
return when {
cardDTO.isStart2Coin -> Start2Coin
else -> Tangem
}
}
}
fun CardDTO.createDisclaimer(): Disclaimer {
val dataProvider = provideDisclaimerDataProvider(cardId)
return TangemDisclaimer(dataProvider)
}
fun DisclaimerType.createDisclaimer(cardDTO: CardDTO): Disclaimer {
val dataProvider = provideDisclaimerDataProvider(cardDTO.cardId, this)
return when (this) {
DisclaimerType.Tangem -> TangemDisclaimer(dataProvider)
DisclaimerType.Start2Coin -> Start2CoinDisclaimer(dataProvider)
}
}
fun CardDTO.createDisclaimer(): Disclaimer = DisclaimerType.get(this).createDisclaimer(this)
private fun provideDisclaimerDataProvider(cardId: String, disclaimerType: DisclaimerType): DisclaimerDataProvider {
private fun provideDisclaimerDataProvider(cardId: String): DisclaimerDataProvider {
val cardRepository = store.inject(DaggerGraphState::cardRepository)
return object : DisclaimerDataProvider {
override fun getLanguage(): String = Locale.getDefault().language
override fun getCardId(): String = cardId
override suspend fun accept() {
when (disclaimerType) {
DisclaimerType.Tangem -> cardRepository.acceptTangemTOS()
DisclaimerType.Start2Coin -> cardRepository.acceptStart2CoinTOS(cardId)
}
cardRepository.acceptTangemTOS()
}
override suspend fun isAccepted(): Boolean {
return when (disclaimerType) {
DisclaimerType.Tangem -> cardRepository.isTangemTOSAccepted()
DisclaimerType.Start2Coin -> cardRepository.isStart2CoinTOSAccepted(cardId)
}
return cardRepository.isTangemTOSAccepted()
}
}
}

View file

@ -65,9 +65,9 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
fun getScanResponse(): ScanResponse {
return when (val mode = twinCardsState.mode) {
return when (twinCardsState.mode) {
is CreateTwinWalletMode.CreateWallet -> onboardingManager?.scanResponse
is CreateTwinWalletMode.RecreateWallet -> mode.scanResponse
is CreateTwinWalletMode.RecreateWallet -> globalState.scanResponse
} ?: throw NullPointerException("ScanResponse can't be NULL")
}
@ -105,6 +105,10 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
mainScope.launch {
if (twinCardsState.currentStep is TwinCardsStep.WelcomeOnly) return@launch
if (twinCardsState.mode is CreateTwinWalletMode.RecreateWallet) {
store.dispatch(GlobalAction.SaveScanResponse(twinCardsState.mode.scanResponse))
}
val scanResponse = getScanResponse()
onboardingManager?.apply {
if (!isActivationStarted(scanResponse.card.cardId)) {
@ -368,8 +372,7 @@ private fun getPopBackScreen(): KClass<out AppRoute> {
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
return if (userWalletsListManager.hasUserWallets) {
val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync }
.fold(onSuccess = { true }, onFailure = { false })
val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync!! }.getOrElse { false }
if (isLocked) {
AppRoute.Welcome::class

View file

@ -35,6 +35,7 @@ import com.tangem.tap.features.onboarding.OnboardingDialog
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.wallet.R
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.Middleware
@ -44,6 +45,8 @@ object OnboardingWalletMiddleware {
val handler = onboardingWalletMiddleware
}
private const val HIDE_PROGRESS_DELAY = 400L
private val onboardingWalletMiddleware: Middleware<AppState> = { dispatch, state ->
{ next ->
{ action ->
@ -161,17 +164,13 @@ private fun handleWalletAction(action: Action) {
store.dispatch(GlobalAction.Onboarding.Stop)
if (scanResponse == null) {
store.dispatchNavigationAction(AppRouter::pop)
store.dispatch(HomeAction.ReadCard(scope = action.scope))
action.scope.launch {
readCard { newScanResponse ->
handleFinishOnboardind(newScanResponse)
}
}
} else {
val backupState = store.state.onboardingWalletState.backupState
val updatedScanResponse = updateScanResponseAfterBackup(scanResponse, backupState)
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(
scanResponse = updatedScanResponse,
accessCode = backupState.accessCode,
backupCardsIds = backupState.backupCardIds,
hasBackupError = backupState.hasBackupError,
)
handleFinishOnboardind(scanResponse)
}
}
is OnboardingWalletAction.ResumeBackup -> {
@ -196,6 +195,43 @@ private fun handleWalletAction(action: Action) {
}
}
private fun handleFinishOnboardind(scanResponse: ScanResponse) {
val backupState = store.state.onboardingWalletState.backupState
val updatedScanResponse = updateScanResponseAfterBackup(scanResponse, backupState)
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(
scanResponse = updatedScanResponse,
accessCode = backupState.accessCode,
backupCardsIds = backupState.backupCardIds,
hasBackupError = backupState.hasBackupError,
)
}
private suspend fun readCard(onSuccess: (ScanResponse) -> Unit) {
val shouldSaveAccessCodes = store.inject(DaggerGraphState::settingsRepository).shouldSaveAccessCodes()
store.inject(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
isBiometricsRequestPolicy = shouldSaveAccessCodes,
)
store.inject(DaggerGraphState::scanCardProcessor).scan(
analyticsSource = com.tangem.core.analytics.models.AnalyticsParam.ScreensSources.Intro,
onProgressStateChange = { showProgress ->
if (showProgress) {
store.dispatch(HomeAction.ScanInProgress(scanInProgress = true))
} else {
delay(HIDE_PROGRESS_DELAY)
store.dispatch(HomeAction.ScanInProgress(scanInProgress = false))
}
},
onFailure = {
Timber.e(it, "Unable to scan card")
delay(HIDE_PROGRESS_DELAY)
store.dispatch(HomeAction.ScanInProgress(scanInProgress = false))
},
onSuccess = onSuccess,
)
}
private suspend fun loadArtworkForUnfinishedBackup(
cardId: String,
cardPublicKey: ByteArray,

View file

@ -4,7 +4,10 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material.Text
@ -123,14 +126,14 @@ internal fun HasMoreItem(moreCount: Int) {
) {
Text(
modifier = Modifier
.padding(TangemTheme.dimens.spacing4)
.align(Alignment.Center)
.drawWithContent { if (readyToDraw) drawContent() },
text = "+$count",
style = textStyle,
color = TangemTheme.colors.text.tertiary,
overflow = TextOverflow.Clip,
onTextLayout = { textLayoutResult ->
if (textLayoutResult.didOverflowHeight) {
if (textLayoutResult.hasVisualOverflow) {
textStyle = textStyle.copy(fontSize = textStyle.fontSize * 0.9)
} else {
readyToDraw = true