Updated on 2026-08-14

This commit is contained in:
Tangem 2024-12-16 18:07:48 +03:00
commit 8232cc6e18
255 changed files with 2657 additions and 2338 deletions

View file

@ -35,7 +35,6 @@ import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.tap.common.log.TangemAppLoggerInitializer
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
import com.tangem.tap.features.home.featuretoggles.HomeFeatureToggles
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.EntryPoint
@ -112,8 +111,6 @@ interface ApplicationEntryPoint {
fun getTransactionSignerFactory(): TransactionSignerFactory
fun getHomeFeatureToggles(): HomeFeatureToggles
fun getGetUserCountryCodeUseCase(): GetUserCountryUseCase
fun getOnrampFeatureToggles(): OnrampFeatureToggles

View file

@ -87,6 +87,7 @@ import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.proxy.redux.DaggerGraphAction
import com.tangem.tap.routing.RoutingComponent
import com.tangem.tap.routing.configurator.AppRouterConfig
import com.tangem.tap.routing.toggle.RoutingFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
import com.tangem.wallet.R
@ -209,6 +210,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
@Inject
lateinit var dispatchers: CoroutineDispatcherProvider
@Inject
internal lateinit var routingFeatureToggles: RoutingFeatureToggles
internal val viewModel: MainViewModel by viewModels()
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode>
@ -255,8 +259,16 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
installActivityDependencies()
observeAppThemeModeUpdates()
setContentView(R.layout.activity_main)
installRouting()
if (routingFeatureToggles.isNavigationRefactoringEnabled) {
TODO(
"Will be implemented in [REDACTED_JIRA] " +
"and in [REDACTED_JIRA]",
)
} else {
setContentView(R.layout.activity_main)
installRouting()
}
initContent()
observeStateUpdates()

View file

@ -57,7 +57,6 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.appReducer
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
import com.tangem.tap.domain.tasks.product.DerivationsFinder
import com.tangem.tap.features.home.featuretoggles.HomeFeatureToggles
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -177,9 +176,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
private val transactionSignerFactory: TransactionSignerFactory
get() = entryPoint.getTransactionSignerFactory()
private val homeFeatureToggles: HomeFeatureToggles
get() = entryPoint.getHomeFeatureToggles()
private val getUserCountryUseCase: GetUserCountryUseCase
get() = entryPoint.getGetUserCountryCodeUseCase()
@ -272,7 +268,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
shareManager = shareManager,
appRouter = appRouter,
transactionSignerFactory = transactionSignerFactory,
homeFeatureToggles = homeFeatureToggles,
getUserCountryUseCase = getUserCountryUseCase,
onrampFeatureToggles = onrampFeatureToggles,
environmentConfigStorage = environmentConfigStorage,

View file

@ -0,0 +1,33 @@
package com.tangem.tap.common.analytics.handlers.firebase
internal class FirebaseAnalyticsEventConverter {
fun convertEventName(event: String): String {
return convertString(event, FIREBASE_EVENT_NAME_MAX_LENGTH)
}
fun convertEventParams(params: Map<String, String>): Map<String, String> {
return params.map { (key, value) ->
convertString(key, FIREBASE_EVENT_NAME_MAX_LENGTH) to convertString(value, FIREBASE_EVENT_VALUE_MAX_LENGTH)
}.toMap()
}
private fun convertString(string: String, maxLength: Int): String {
return string
.replace(REPLACING_PATTERN.toRegex(), WORD_SEPARATOR)
.trim { it in TRIMMING_CHARACTERS }
.trimToLength(maxLength)
}
private fun String.trimToLength(length: Int): String {
return if (this.length > length) this.substring(0, length) else this
}
private companion object {
const val REPLACING_PATTERN = "[^\\w]+"
const val WORD_SEPARATOR = "_"
const val TRIMMING_CHARACTERS = WORD_SEPARATOR
const val FIREBASE_EVENT_NAME_MAX_LENGTH = 40
const val FIREBASE_EVENT_VALUE_MAX_LENGTH = 100
}
}

View file

@ -18,12 +18,19 @@ internal class FirebaseClient : FirebaseAnalyticsClient {
private val fbAnalytics = Firebase.analytics
private val fbCrashlytics = Firebase.crashlytics
private val eventConverter = FirebaseAnalyticsEventConverter()
override fun logEvent(event: String, params: Map<String, String>) {
fbAnalytics.logEvent(event, params.toBundle())
fbAnalytics.logEvent(
eventConverter.convertEventName(event),
eventConverter.convertEventParams(params).toBundle(),
)
}
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
params.forEach { fbCrashlytics.setCustomKey(it.key, it.value) }
eventConverter.convertEventParams(params)
.forEach { fbCrashlytics.setCustomKey(it.key, it.value) }
fbCrashlytics.recordException(error)
}

View file

@ -1,3 +0,0 @@
package com.tangem.tap.common.extensions
fun StringBuilder.breakLine(count: Int = 1): StringBuilder = append("\n".repeat(count))

View file

@ -2,26 +2,21 @@
package com.tangem.tap.common.extensions
import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.ContextWrapper
import android.graphics.drawable.Drawable
import android.view.View
import androidx.annotation.*
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
fun Context.getDrawableCompat(@DrawableRes drawableResId: Int): Drawable? {
return ContextCompat.getDrawable(this, drawableResId)
}
@ColorInt
fun Fragment.getColor(@ColorRes colorRes: Int): Int {
return ContextCompat.getColor(requireContext(), colorRes)
}
@ColorInt
fun Context.getColorCompat(@ColorRes colorRes: Int): Int {
return ContextCompat.getColor(this, colorRes)
@ -40,10 +35,6 @@ fun View.getString(@StringRes id: Int, vararg formatArgs: String): String {
return context.getString(id, *formatArgs)
}
fun View.getQuantityString(@PluralsRes id: Int, quantity: Int): String {
return context.resources.getQuantityString(id, quantity, quantity)
}
fun View.show(show: Boolean, invokeBeforeStateChanged: (() -> Unit)? = null) {
return if (show) this.show(invokeBeforeStateChanged) else this.hide(invokeBeforeStateChanged)
}
@ -62,9 +53,6 @@ fun View.hide(invokeBeforeStateChanged: (() -> Unit)? = null) {
this.visibility = View.GONE
}
tailrec fun Context?.getActivity(): Activity? = this as? Activity
?: (this as? ContextWrapper)?.baseContext?.getActivity()
fun Context.copyToClipboard(value: Any, label: String = "") {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return

View file

@ -1,8 +1,5 @@
package com.tangem.tap.common.redux
import com.tangem.domain.redux.DomainState
import com.tangem.domain.redux.domainStore
import com.tangem.domain.redux.global.NetworkServices
import com.tangem.tap.common.redux.global.GlobalMiddleware
import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.common.redux.legacy.LegacyMiddleware
@ -28,7 +25,6 @@ import com.tangem.tap.features.welcome.redux.WelcomeMiddleware
import com.tangem.tap.features.welcome.redux.WelcomeState
import com.tangem.tap.proxy.redux.DaggerGraphMiddleware
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
import org.rekotlin.Middleware
import org.rekotlin.StateType
@ -46,18 +42,6 @@ data class AppState(
val daggerGraphState: DaggerGraphState = DaggerGraphState(),
) : StateType {
private val domainState: DomainState
get() = domainStore.state
private val domainNetworks: NetworkServices
get() = domainState.globalState.networkServices
val featureRepositoryProvider: FeatureRepositoryProvider
get() = FeatureRepositoryProvider(
tangemTechApi = domainNetworks.tangemTechService.api,
dispatchers = AppCoroutineDispatcherProvider(),
)
companion object {
fun getMiddleware(): List<Middleware<AppState>> {
return listOf(

View file

@ -1,11 +0,0 @@
package com.tangem.tap.common.redux
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.tap.features.home.data.HomeRepositoryImpl
import com.tangem.tap.features.home.domain.HomeRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
class FeatureRepositoryProvider(tangemTechApi: TangemTechApi, dispatchers: CoroutineDispatcherProvider) {
val homeRepository: HomeRepository = HomeRepositoryImpl(tangemTechApi, dispatchers)
}

View file

@ -82,8 +82,4 @@ sealed class GlobalAction : Action {
object Update : GlobalAction()
}
object FetchUserCountry : GlobalAction() {
data class Success(val countryCode: String) : GlobalAction()
}
}

View file

@ -25,7 +25,6 @@ import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.Middleware
import java.util.Locale
object GlobalMiddleware {
val handler = globalMiddlewareHandler
@ -87,27 +86,6 @@ private fun handleAction(action: Action, appState: () -> AppState?) {
}
scope.launch { exchangeManager.update() }
}
is GlobalAction.FetchUserCountry -> {
val homeFeatureToggles = store.inject(DaggerGraphState::homeFeatureToggles)
if (!homeFeatureToggles.isMigrateUserCountryCodeEnabled) {
scope.launch {
runCatching { store.state.featureRepositoryProvider.homeRepository.getUserCountryCode() }
.onSuccess {
store.dispatchOnMain(
GlobalAction.FetchUserCountry.Success(countryCode = it.code.lowercase()),
)
}
.onFailure {
store.dispatchOnMain(
GlobalAction.FetchUserCountry.Success(
countryCode = Locale.getDefault().country.lowercase(),
),
)
}
}
}
}
}
}

View file

@ -1,7 +1,5 @@
package com.tangem.tap.common.redux.global
import com.tangem.domain.redux.domainStore
import com.tangem.domain.redux.global.DomainGlobalAction
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.onboarding.OnboardingManager
import com.tangem.tap.proxy.AppStateHolder
@ -38,7 +36,6 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde
}
is GlobalAction.SaveScanResponse -> {
appStateHolder.scanResponse = action.scanResponse
domainStore.dispatch(DomainGlobalAction.SaveScanNoteResponse(action.scanResponse))
globalState.copy(scanResponse = action.scanResponse)
}
is GlobalAction.ChangeAppCurrency -> {
@ -77,9 +74,6 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde
}
is GlobalAction.SetIfCardVerifiedOnline ->
globalState.copy(cardVerifiedOnline = action.verified)
is GlobalAction.FetchUserCountry.Success -> {
globalState.copy(userCountryCode = action.countryCode)
}
else -> globalState
}
}

View file

@ -15,6 +15,7 @@ import com.tangem.common.services.secure.SecureStorage
import com.tangem.common.usersCode.UserCodeRepository
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.Basic
import com.tangem.core.res.getStringSafe
import com.tangem.crypto.bip39.DefaultMnemonic
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
@ -35,7 +36,10 @@ import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.sdk.api.CreateProductWalletTaskResponse
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.derivationsFinder
import com.tangem.tap.domain.tasks.product.*
import com.tangem.tap.domain.tasks.product.CreateProductWalletTask
import com.tangem.tap.domain.tasks.product.ResetBackupCardTask
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
import com.tangem.tap.domain.tasks.product.ScanProductTask
import com.tangem.tap.domain.twins.CreateFirstTwinWalletTask
import com.tangem.tap.domain.twins.CreateSecondTwinWalletTask
import com.tangem.tap.domain.twins.FinalizeTwinTask
@ -122,7 +126,7 @@ class DefaultTangemSdkManager(
messageRes: Int?,
allowsRequestAccessCodeFromRepository: Boolean,
): CompletionResult<ScanResponse> {
val message = Message(resources.getString(messageRes ?: R.string.initial_message_scan_header))
val message = Message(resources.getStringSafe(messageRes ?: R.string.initial_message_scan_header))
return runTaskAsyncReturnOnMain(
runnable = ScanProductTask(
card = null,
@ -154,9 +158,9 @@ class DefaultTangemSdkManager(
),
cardId = scanResponse.card.cardId,
initialMessage = if (scanResponse.cardTypesResolver.isRing()) {
Message(resources.getString(R.string.initial_message_create_wallet_body_ring))
Message(resources.getStringSafe(R.string.initial_message_create_wallet_body_ring))
} else {
Message(resources.getString(R.string.initial_message_create_wallet_body))
Message(resources.getStringSafe(R.string.initial_message_create_wallet_body))
},
iconScanRes = if (scanResponse.cardTypesResolver.isRing()) R.drawable.img_hand_scan_ring else null,
preflightReadFilter = null,
@ -194,9 +198,9 @@ class DefaultTangemSdkManager(
),
cardId = scanResponse.card.cardId,
initialMessage = if (scanResponse.cardTypesResolver.isRing()) {
Message(resources.getString(R.string.initial_message_create_wallet_body_ring))
Message(resources.getStringSafe(R.string.initial_message_create_wallet_body_ring))
} else {
Message(resources.getString(R.string.initial_message_create_wallet_body))
Message(resources.getStringSafe(R.string.initial_message_create_wallet_body))
},
preflightReadFilter = null,
)
@ -243,7 +247,7 @@ class DefaultTangemSdkManager(
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
),
cardId = cardId,
initialMessage = Message(resources.getString(R.string.card_settings_reset_card_to_factory)),
initialMessage = Message(resources.getStringSafe(R.string.card_settings_reset_card_to_factory)),
)
}
@ -251,7 +255,7 @@ class DefaultTangemSdkManager(
return runTaskAsyncReturnOnMain(
runnable = ResetBackupCardTask(userWalletId),
initialMessage = Message(
resources.getString(
resources.getStringSafe(
R.string.initial_message_reset_backup_card_header,
cardNumber.toString(),
),
@ -281,7 +285,7 @@ class DefaultTangemSdkManager(
return runTaskAsyncReturnOnMain(
SetUserCodeCommand.changePasscode(null),
cardId,
initialMessage = Message(resources.getString(R.string.initial_message_change_passcode_body)),
initialMessage = Message(resources.getStringSafe(R.string.initial_message_change_passcode_body)),
)
}
@ -289,7 +293,7 @@ class DefaultTangemSdkManager(
return runTaskAsyncReturnOnMain(
SetUserCodeCommand.changeAccessCode(null),
cardId,
initialMessage = Message(resources.getString(R.string.initial_message_change_access_code_body)),
initialMessage = Message(resources.getStringSafe(R.string.initial_message_change_access_code_body)),
)
}
@ -297,7 +301,7 @@ class DefaultTangemSdkManager(
return runTaskAsyncReturnOnMain(
SetUserCodeCommand.resetUserCodes(),
cardId,
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
initialMessage = Message(resources.getStringSafe(R.string.initial_message_tap_header)),
)
}
@ -308,7 +312,7 @@ class DefaultTangemSdkManager(
return runTaskAsyncReturnOnMain(
SetUserCodeRecoveryAllowedTask(enabled),
cardId,
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
initialMessage = Message(resources.getStringSafe(R.string.initial_message_tap_header)),
)
}
@ -319,7 +323,7 @@ class DefaultTangemSdkManager(
return runTaskAsyncReturnOnMain(
runnable = ScanTask(allowRequestAccessCodeFromRepository),
cardId = cardId,
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
initialMessage = Message(resources.getStringSafe(R.string.initial_message_tap_header)),
)
.map { CardDTO(it) }
}
@ -372,7 +376,9 @@ class DefaultTangemSdkManager(
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
override fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
return resources.getString(stringResId, *formatArgs)
val args = formatArgs.toSet().filterNotNull().toTypedArray()
return resources.getStringSafe(stringResId, *args)
}
override fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {

View file

@ -12,6 +12,7 @@ import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.UserCodeRequestPolicy
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.services.InMemoryStorage
import com.tangem.core.res.getStringSafe
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.models.scan.CardDTO
@ -150,7 +151,9 @@ class MockTangemSdkManager(
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
override fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
return resources.getString(stringResId, *formatArgs)
val args = formatArgs.toSet().filterNotNull().toTypedArray()
return resources.getStringSafe(stringResId, *args)
}
override fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {

View file

@ -18,7 +18,6 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
@ -27,8 +26,9 @@ import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.event.EventEffect
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.tap.features.details.ui.appcurrency.AppCurrencySelectorState.Currency
import com.tangem.wallet.R
import kotlinx.collections.immutable.ImmutableList
@ -114,7 +114,7 @@ private fun TopBar(
is AppCurrencySelectorState.Loading,
is AppCurrencySelectorState.Default,
-> Text(
text = stringResource(id = R.string.details_row_title_currency),
text = stringResourceSafe(id = R.string.details_row_title_currency),
style = TangemTheme.typography.subtitle1,
)
}
@ -163,7 +163,7 @@ private fun SearchBar(onInputChange: (String) -> Unit, modifier: Modifier = Modi
singleLine = true,
textStyle = TangemTheme.typography.subtitle2,
placeholder = {
Text(text = stringResource(id = R.string.common_search))
Text(text = stringResourceSafe(id = R.string.common_search))
},
colors = SearchBarColors,
)

View file

@ -2,13 +2,13 @@ package com.tangem.tap.features.details.ui.appsettings.components
import android.content.res.Configuration
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.tap.features.details.ui.appsettings.AppSettingsDialogsFactory
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Dialog
@ -26,7 +26,7 @@ internal fun SettingsAlertDialog(dialog: Dialog.Alert) {
onClick = dialog.onConfirm,
),
dismissButton = DialogButtonUM(
title = stringResource(id = R.string.common_cancel),
title = stringResourceSafe(id = R.string.common_cancel),
onClick = dialog.onDismiss,
),
onDismissDialog = dialog.onDismiss,

View file

@ -2,13 +2,13 @@ package com.tangem.tap.features.details.ui.appsettings.components
import android.content.res.Configuration
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.components.SelectorDialog
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.tap.features.details.ui.appsettings.AppSettingsDialogsFactory
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Dialog
@ -22,7 +22,7 @@ internal fun SettingsSelectorDialog(dialog: Dialog.Selector) {
selectedItemIndex = dialog.selectedItemIndex,
items = dialog.items.map { it.resolveReference() }.toImmutableList(),
confirmButton = DialogButtonUM(
title = stringResource(R.string.common_cancel),
title = stringResourceSafe(R.string.common_cancel),
onClick = dialog.onDismiss,
),
onSelect = dialog.onSelect,

View file

@ -14,8 +14,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.tap.features.details.ui.common.DetailsMainButton
@ -88,13 +88,13 @@ private fun CardSettingsReadCard(onScanCardClick: () -> Unit) {
),
) {
Text(
text = stringResource(id = R.string.scan_card_settings_title),
text = stringResourceSafe(id = R.string.scan_card_settings_title),
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.h3,
)
Spacer(modifier = Modifier.size(TangemTheme.dimens.size20))
Text(
text = stringResource(id = R.string.scan_card_settings_message),
text = stringResourceSafe(id = R.string.scan_card_settings_message),
color = TangemTheme.colors.text.secondary,
style = TangemTheme.typography.body1,
modifier = Modifier
@ -103,7 +103,7 @@ private fun CardSettingsReadCard(onScanCardClick: () -> Unit) {
)
Spacer(modifier = Modifier.size(TangemTheme.dimens.size32))
DetailsMainButton(
title = stringResource(id = R.string.scan_card_settings_button),
title = stringResourceSafe(id = R.string.scan_card_settings_button),
onClick = onScanCardClick,
)
}

View file

@ -3,7 +3,7 @@ package com.tangem.tap.features.details.ui.cardsettings
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.res.stringResource
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.tap.features.details.redux.SecurityOption
import com.tangem.tap.features.details.ui.securitymode.toTitleRes
import com.tangem.wallet.R
@ -77,7 +77,7 @@ internal sealed interface TextReference {
@ReadOnlyComposable
internal fun TextReference.resolveReference(): String {
return when (this) {
is TextReference.Res -> stringResource(this.id, *this.formatArgs.toTypedArray())
is TextReference.Res -> stringResourceSafe(this.id, *this.formatArgs.toTypedArray())
is TextReference.Str -> this.value
}
}

View file

@ -5,7 +5,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.features.details.ui.common.DetailsMainButton
import com.tangem.tap.features.details.ui.common.DetailsRadioButtonElement
@ -36,14 +36,14 @@ fun AccessCodeRecoveryOptions(state: AccessCodeRecoveryScreenState) {
)
DetailsRadioButtonElement(
title = stringResource(id = R.string.common_enabled),
subtitle = stringResource(id = R.string.card_settings_access_code_recovery_enabled_description),
title = stringResourceSafe(id = R.string.common_enabled),
subtitle = stringResourceSafe(id = R.string.card_settings_access_code_recovery_enabled_description),
selected = state.enabledSelection,
onClick = { state.onOptionClick(true) },
)
DetailsRadioButtonElement(
title = stringResource(id = R.string.common_disabled),
subtitle = stringResource(id = R.string.card_settings_access_code_recovery_disabled_description),
title = stringResourceSafe(id = R.string.common_disabled),
subtitle = stringResourceSafe(id = R.string.card_settings_access_code_recovery_disabled_description),
selected = !state.enabledSelection,
onClick = { state.onOptionClick(false) },
)
@ -51,7 +51,7 @@ fun AccessCodeRecoveryOptions(state: AccessCodeRecoveryScreenState) {
Spacer(modifier = Modifier.weight(1f))
DetailsMainButton(
title = stringResource(id = R.string.common_save_changes),
title = stringResourceSafe(id = R.string.common_save_changes),
enabled = state.isSaveChangesEnabled,
onClick = { state.onSaveChangesClick() },
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing20),

View file

@ -10,9 +10,9 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.PrimaryButtonIconEnd
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.WindowInsetsZero
import com.tangem.wallet.R
@ -64,7 +64,7 @@ internal fun SettingsScreensScaffold(
) {
if (titleRes != null) {
Text(
text = stringResource(id = titleRes),
text = stringResourceSafe(id = titleRes),
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing20)
.padding(bottom = TangemTheme.dimens.spacing36),
@ -82,7 +82,7 @@ internal fun SettingsScreensScaffold(
@Composable
internal fun ScreenTitle(titleRes: Int, modifier: Modifier = Modifier) {
Text(
text = stringResource(id = titleRes),
text = stringResourceSafe(id = titleRes),
modifier = modifier.padding(start = 20.dp, end = 20.dp),
style = TangemTheme.typography.h1,
color = TangemTheme.colors.text.primary1,

View file

@ -10,9 +10,9 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.*
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.tap.features.details.ui.cardsettings.TextReference
@ -71,7 +71,7 @@ private fun ResetCardView(state: ResetCardScreenState) {
@Composable
private fun Title() {
Text(
text = stringResource(id = R.string.card_settings_reset_card_to_factory),
text = stringResourceSafe(id = R.string.card_settings_reset_card_to_factory),
style = TangemTheme.typography.h1,
color = TangemTheme.colors.text.primary1,
)
@ -89,7 +89,7 @@ private fun AlertImage() {
@Composable
private fun Subtitle() {
Text(
text = stringResource(id = R.string.common_attention),
text = stringResourceSafe(id = R.string.common_attention),
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
)
@ -179,7 +179,7 @@ private fun ColumnScope.DynamicSpacer(scrollState: ScrollState) {
@Composable
private fun ResetButton(enabled: Boolean, onResetButtonClick: () -> Unit) {
DetailsMainButton(
title = stringResource(id = R.string.reset_card_to_factory_button_title),
title = stringResourceSafe(id = R.string.reset_card_to_factory_button_title),
onClick = onResetButtonClick,
enabled = enabled,
)
@ -188,14 +188,14 @@ private fun ResetButton(enabled: Boolean, onResetButtonClick: () -> Unit) {
@Composable
private fun CommonResetDialog(dialog: ResetCardScreenState.Dialog) {
BasicDialog(
title = stringResource(dialog.titleResId),
message = stringResource(dialog.messageResId),
title = stringResourceSafe(dialog.titleResId),
message = stringResourceSafe(dialog.messageResId),
dismissButton = DialogButtonUM(
title = stringResource(id = R.string.common_cancel),
title = stringResourceSafe(id = R.string.common_cancel),
onClick = dialog.onDismiss,
),
confirmButton = DialogButtonUM(
title = stringResource(id = R.string.card_settings_action_sheet_reset),
title = stringResourceSafe(id = R.string.card_settings_action_sheet_reset),
warning = true,
onClick = dialog.onConfirmClick,
),
@ -206,10 +206,10 @@ private fun CommonResetDialog(dialog: ResetCardScreenState.Dialog) {
@Composable
private fun CompletedResetDialog(dialog: ResetCardDialog) {
BasicDialog(
title = stringResource(id = dialog.titleResId),
message = stringResource(id = dialog.messageResId),
title = stringResourceSafe(id = dialog.titleResId),
message = stringResourceSafe(id = dialog.messageResId),
confirmButton = DialogButtonUM(
title = stringResource(id = R.string.common_ok),
title = stringResourceSafe(id = R.string.common_ok),
onClick = dialog.onConfirmClick,
),
onDismissDialog = {},

View file

@ -5,9 +5,9 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.tap.features.details.redux.SecurityOption
import com.tangem.tap.features.details.ui.common.DetailsMainButton
import com.tangem.tap.features.details.ui.common.DetailsRadioButtonElement
@ -47,7 +47,7 @@ private fun SecurityModeOptions(state: SecurityModeScreenState) {
Spacer(modifier = Modifier.weight(1f))
DetailsMainButton(
title = stringResource(id = R.string.common_save_changes),
title = stringResourceSafe(id = R.string.common_save_changes),
enabled = state.isSaveChangesEnabled,
onClick = state.onSaveChangesClicked,
modifier = Modifier.padding(start = 20.dp, end = 20.dp),
@ -68,8 +68,8 @@ private fun SecurityOption(option: SecurityOption, state: SecurityModeScreenStat
}
DetailsRadioButtonElement(
title = stringResource(id = title),
subtitle = stringResource(id = subtitle),
title = stringResourceSafe(id = title),
subtitle = stringResourceSafe(id = subtitle),
selected = selected,
onClick = { state.onNewModeSelected(option) },
)
@ -80,7 +80,7 @@ private fun SecurityOption(option: SecurityOption, state: SecurityModeScreenStat
private fun SecurityModeScreenPreview() {
SecurityModeScreen(
state = SecurityModeScreenState(
availableOptions = SecurityOption.values().toList(),
availableOptions = SecurityOption.entries,
selectedSecurityMode = SecurityOption.LongTap,
isSaveChangesEnabled = false,
onNewModeSelected = {},

View file

@ -13,10 +13,10 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.analytics.Analytics
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.common.analytics.events.Settings
import com.tangem.tap.common.extensions.getFromClipboard
@ -96,7 +96,7 @@ private fun EmptyScreen(state: WalletConnectScreenState) {
)
Spacer(modifier = Modifier.size(24.dp))
Text(
text = stringResource(id = R.string.wallet_connect_subtitle),
text = stringResourceSafe(id = R.string.wallet_connect_subtitle),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)

View file

@ -2,20 +2,23 @@ package com.tangem.tap.features.home.compose.content
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerH16
import com.tangem.core.ui.components.SpacerH32
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.features.home.compose.StoriesBottomImageAnimation
@ -27,8 +30,8 @@ fun StoriesRevolutionaryWallet() {
SplitContent(
topContent = {
TopContent(
titleText = stringResource(id = R.string.story_awe_title),
subtitleText = stringResource(id = R.string.story_awe_description),
titleText = stringResourceSafe(id = R.string.story_awe_title),
subtitleText = stringResourceSafe(id = R.string.story_awe_description),
)
},
bottomContent = {
@ -46,8 +49,8 @@ fun StoriesUltraSecureBackup(isPaused: Boolean, stepDuration: Int) {
SplitContent(
topContent = {
TopContent(
titleText = stringResource(id = R.string.story_backup_title),
subtitleText = stringResource(id = R.string.story_backup_description),
titleText = stringResourceSafe(id = R.string.story_backup_title),
subtitleText = stringResourceSafe(id = R.string.story_backup_description),
)
},
bottomContent = {
@ -65,8 +68,8 @@ fun StoriesCurrencies(isPaused: Boolean, stepDuration: Int) {
SplitContent(
topContent = {
TopContent(
titleText = stringResource(id = R.string.story_currencies_title),
subtitleText = stringResource(id = R.string.story_currencies_description),
titleText = stringResourceSafe(id = R.string.story_currencies_title),
subtitleText = stringResourceSafe(id = R.string.story_currencies_description),
)
},
bottomContent = {
@ -81,8 +84,8 @@ fun StoriesWeb3(isPaused: Boolean, stepDuration: Int) {
SplitContent(
topContent = {
TopContent(
titleText = stringResource(id = R.string.story_web3_title),
subtitleText = stringResource(id = R.string.story_web3_description),
titleText = stringResourceSafe(id = R.string.story_web3_title),
subtitleText = stringResourceSafe(id = R.string.story_web3_description),
)
},
bottomContent = {
@ -97,8 +100,8 @@ fun StoriesWalletForEveryone(stepDuration: Int) {
SplitContent(
topContent = {
TopContent(
titleText = stringResource(id = R.string.story_finish_title),
subtitleText = stringResource(id = R.string.story_finish_description),
titleText = stringResourceSafe(id = R.string.story_finish_title),
subtitleText = stringResourceSafe(id = R.string.story_finish_description),
)
},
bottomContent = {

View file

@ -16,13 +16,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.features.home.compose.StoriesTextAnimation
@ -66,7 +66,7 @@ fun FirstStoriesContent(isPaused: Boolean, duration: Int) {
) { modifier ->
Text(
modifier = modifier,
text = stringResource(R.string.story_meet_title),
text = stringResourceSafe(R.string.story_meet_title),
style = style,
color = TangemColorPalette.White,
textAlign = TextAlign.Center,

View file

@ -9,12 +9,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.SpacerW12
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.TestTags
@ -52,7 +52,7 @@ internal fun HomeButtons(
private fun ScanCardButton(showProgress: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
StoriesButton(
modifier = modifier,
text = stringResource(id = R.string.home_button_scan),
text = stringResourceSafe(id = R.string.home_button_scan),
useDarkerColors = false,
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_tangem_24),
onClick = onClick,
@ -64,7 +64,7 @@ private fun ScanCardButton(showProgress: Boolean, onClick: () -> Unit, modifier:
private fun OrderCardButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
StoriesButton(
modifier = modifier,
text = stringResource(id = R.string.home_button_order),
text = stringResourceSafe(id = R.string.home_button_order),
useDarkerColors = true,
onClick = onClick,
)

View file

@ -7,9 +7,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.wallet.R
@ -18,7 +18,7 @@ import com.tangem.wallet.R
internal fun SearchCurrenciesButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
StoriesButton(
modifier = modifier,
text = stringResource(id = R.string.common_search_tokens),
text = stringResourceSafe(id = R.string.common_search_tokens),
icon = TangemButtonIconPosition.Start(R.drawable.ic_search_24),
showProgress = false,
useDarkerColors = true,

View file

@ -1,23 +0,0 @@
package com.tangem.tap.features.home.data
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.GeoResponse
import com.tangem.tap.features.home.domain.HomeRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
/**
* Implementation of repository for Home feature
*
* @property tangemTechApi API for server requests
* @property dispatchers coroutine dispatcher provider
*/
class HomeRepositoryImpl(
private val tangemTechApi: TangemTechApi,
private val dispatchers: CoroutineDispatcherProvider,
) : HomeRepository {
override suspend fun getUserCountryCode(): GeoResponse = withContext(dispatchers.io) {
tangemTechApi.getUserCountryCode()
}
}

View file

@ -1,10 +0,0 @@
package com.tangem.tap.features.home.domain
import com.tangem.datasource.api.tangemTech.models.GeoResponse
/** Repository for Home feature */
interface HomeRepository {
/** Get user's country code */
suspend fun getUserCountryCode(): GeoResponse
}

View file

@ -1,12 +0,0 @@
package com.tangem.tap.features.home.featuretoggles
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import javax.inject.Inject
class HomeFeatureToggles @Inject constructor(
private val featureTogglesManager: FeatureTogglesManager,
) {
val isMigrateUserCountryCodeEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "MIGRATE_USER_COUNTRY_CODE_ENABLED")
}

View file

@ -53,7 +53,6 @@ private fun handleHomeAction(action: Action) {
store.dispatch(GlobalAction.RestoreAppCurrency)
store.dispatch(GlobalAction.ExchangeManager.Init)
store.dispatch(GlobalAction.FetchUserCountry)
}
is HomeAction.ReadCard -> {
action.scope.launch {

View file

@ -18,7 +18,6 @@ import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase
import com.tangem.domain.staking.FetchStakingTokensUseCase
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.features.home.featuretoggles.HomeFeatureToggles
import com.tangem.tap.features.main.model.MainScreenState
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.lifecycle.HiltViewModel
@ -41,7 +40,6 @@ internal class MainViewModel @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
private val fetchStakingTokensUseCase: FetchStakingTokensUseCase,
private val apiConfigsManager: ApiConfigsManager,
homeFeatureToggles: HomeFeatureToggles,
private val fetchUserCountryUseCase: FetchUserCountryUseCase,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
) : ViewModel(), MainIntents {
@ -63,11 +61,9 @@ internal class MainViewModel @Inject constructor(
viewModelScope.launch(dispatchers.main) { incrementAppLaunchCounterUseCase() }
if (homeFeatureToggles.isMigrateUserCountryCodeEnabled) {
viewModelScope.launch {
fetchUserCountryUseCase().onLeft {
Timber.e("Unable to fetch the user country code $it")
}
viewModelScope.launch {
fetchUserCountryUseCase().onLeft {
Timber.e("Unable to fetch the user country code $it")
}
}

View file

@ -22,11 +22,9 @@ import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletAction
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletAction
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
import com.tangem.tap.mainScope
import com.tangem.tap.proxy.redux.DaggerGraphState
@ -50,7 +48,7 @@ object OnboardingHelper {
if (!response.twinsIsTwinned()) {
true
} else {
onboardingManager.isActivationInProgress(cardId) ?: false
onboardingManager.isActivationInProgress(cardId)
}
}
@ -62,7 +60,7 @@ object OnboardingHelper {
emptyWallets || activationInProgress || isNoBackup
}
response.card.wallets.isNotEmpty() -> onboardingManager.isActivationInProgress(cardId) ?: false
response.card.wallets.isNotEmpty() -> onboardingManager.isActivationInProgress(cardId)
else -> true
}
}
@ -70,7 +68,7 @@ object OnboardingHelper {
fun whereToNavigate(scanResponse: ScanResponse): AppRoute {
val newOnboardingSupportTypes = scanResponse.productType == ProductType.Wallet2 ||
scanResponse.productType == ProductType.Ring ||
scanResponse.productType == ProductType.Wallet
scanResponse.productType == ProductType.Wallet // AppRoute.OnboardingOther is also supported
if (store.inject(DaggerGraphState::onboardingV2FeatureToggles).isOnboardingV2Enabled &&
newOnboardingSupportTypes
) {
@ -222,7 +220,7 @@ object OnboardingHelper {
}
}
fun handleTopUpAction(walletManager: WalletManager, scanResponse: ScanResponse, globalState: GlobalState) {
fun handleTopUpAction(walletManager: WalletManager, scanResponse: ScanResponse) {
val blockchain = walletManager.wallet.blockchain
val excludedBlockchains = store.inject(DaggerGraphState::excludedBlockchains)
@ -238,17 +236,10 @@ object OnboardingHelper {
Analytics.send(Onboarding.Topup.ButtonBuyCrypto(currencyType))
scope.launch {
val homeFeatureToggles = store.inject(DaggerGraphState::homeFeatureToggles)
val getUserCountryCodeUseCase = store.inject(DaggerGraphState::getUserCountryUseCase)
val isRussia = getUserCountryCodeUseCase().isRight { it is UserCountry.Russia }
val onrampFeatureToggles = store.inject(DaggerGraphState::onrampFeatureToggles)
val isRussia = if (homeFeatureToggles.isMigrateUserCountryCodeEnabled) {
val getUserCountryCodeUseCase = store.inject(DaggerGraphState::getUserCountryUseCase)
getUserCountryCodeUseCase().isRight { it is UserCountry.Russia }
} else {
globalState.userCountryCode == RUSSIA_COUNTRY_CODE
}
if (isRussia && !onrampFeatureToggles.isFeatureEnabled) {
val dialogData = AppDialog.RussianCardholdersWarningDialog.Data(topUpUrl)
store.dispatchDialogShow(AppDialog.RussianCardholdersWarningDialog(dialogData))

View file

@ -176,7 +176,6 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
OnboardingHelper.handleTopUpAction(
walletManager = walletManager,
scanResponse = scanResponse,
globalState = globalState,
)
}
is OnboardingNoteAction.Done -> {

View file

@ -299,7 +299,6 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
OnboardingHelper.handleTopUpAction(
walletManager = walletManager,
scanResponse = onboardingManager?.scanResponse ?: return,
globalState = globalState,
)
}
TwinCardsAction.Done -> {

View file

@ -5,11 +5,11 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.tap.features.saveWallet.ui.models.EnrollBiometricsDialog
@ -17,10 +17,10 @@ import com.tangem.tap.features.saveWallet.ui.models.EnrollBiometricsDialog
@Composable
fun EnrollBiometricsDialogContent(dialog: EnrollBiometricsDialog) {
BasicDialog(
title = stringResource(R.string.save_user_wallet_agreement_enroll_biometrics_title),
message = stringResource(R.string.save_user_wallet_agreement_enroll_biometrics_description),
title = stringResourceSafe(R.string.save_user_wallet_agreement_enroll_biometrics_title),
message = stringResourceSafe(R.string.save_user_wallet_agreement_enroll_biometrics_description),
confirmButton = DialogButtonUM(
title = stringResource(R.string.common_enable),
title = stringResourceSafe(R.string.common_enable),
onClick = dialog.onEnroll,
),
dismissButton = DialogButtonUM(

View file

@ -11,11 +11,11 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.atoms.Hand
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.wallet.R
@ -63,7 +63,7 @@ private fun Header(onCloseClick: () -> Unit) {
modifier = Modifier.size(TangemTheme.dimens.size24),
painter = painterResource(id = R.drawable.ic_close_24),
tint = TangemTheme.colors.icon.secondary,
contentDescription = stringResource(id = R.string.common_cancel),
contentDescription = stringResourceSafe(id = R.string.common_cancel),
)
}
SpacerW8()
@ -85,7 +85,7 @@ private fun Title(modifier: Modifier = Modifier) {
contentDescription = null,
)
Text(
text = stringResource(id = R.string.save_user_wallet_agreement_header_biometrics),
text = stringResourceSafe(id = R.string.save_user_wallet_agreement_header_biometrics),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
@ -102,13 +102,13 @@ private fun Description(modifier: Modifier = Modifier) {
) {
DescriptionItem(
iconPainter = painterResource(id = R.drawable.ic_face_recognition_24),
title = stringResource(id = R.string.save_user_wallet_agreement_access_title),
description = stringResource(id = R.string.save_user_wallet_agreement_access_description),
title = stringResourceSafe(id = R.string.save_user_wallet_agreement_access_title),
description = stringResourceSafe(id = R.string.save_user_wallet_agreement_access_description),
)
DescriptionItem(
iconPainter = painterResource(id = R.drawable.ic_lock_24),
title = stringResource(id = R.string.save_user_wallet_agreement_code_title),
description = stringResource(id = R.string.save_user_wallet_agreement_code_description_biometrics),
title = stringResourceSafe(id = R.string.save_user_wallet_agreement_code_title),
description = stringResourceSafe(id = R.string.save_user_wallet_agreement_code_description_biometrics),
)
}
}
@ -125,12 +125,12 @@ private fun Footer(showProgress: Boolean, onSaveWalletClick: () -> Unit) {
PrimaryButton(
modifier = Modifier.fillMaxWidth(),
showProgress = showProgress,
text = stringResource(id = R.string.save_user_wallet_agreement_allow_biometrics),
text = stringResourceSafe(id = R.string.save_user_wallet_agreement_allow_biometrics),
onClick = onSaveWalletClick,
)
Text(
modifier = Modifier.fillMaxWidth(fraction = .7f),
text = stringResource(R.string.save_user_wallet_agreement_notice),
text = stringResourceSafe(R.string.save_user_wallet_agreement_notice),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.Center,

View file

@ -17,7 +17,6 @@ import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.network.exchangeServices.buyErc20TestnetTokens
import com.tangem.tap.proxy.redux.DaggerGraphState
@ -26,7 +25,6 @@ import com.tangem.tap.store
import kotlinx.coroutines.launch
import org.rekotlin.Middleware
@Suppress("LargeClass")
@Deprecated("Will be removed soon")
object TradeCryptoMiddleware {
@ -41,25 +39,24 @@ object TradeCryptoMiddleware {
}
}
@Suppress("LongMethod", "CyclomaticComplexMethod")
private fun handle(state: () -> AppState?, action: TradeCryptoAction) {
if (DemoHelper.tryHandle(state, action)) return
when (action) {
is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId)
is TradeCryptoAction.Buy -> proceedBuyAction(state, action)
is TradeCryptoAction.Buy -> proceedBuyAction(action)
is TradeCryptoAction.Sell -> proceedSellAction(action)
is TradeCryptoAction.SendToken -> handleNewSendToken(action = action)
is TradeCryptoAction.SendCoin -> handleNewSendCoin(action = action)
}
}
private fun proceedBuyAction(state: () -> AppState?, action: TradeCryptoAction.Buy) {
private fun proceedBuyAction(action: TradeCryptoAction.Buy) {
val isOnrampEnabled = store.inject(DaggerGraphState::onrampFeatureToggles).isFeatureEnabled
if (isOnrampEnabled) {
proceedWithOnramp(action.userWallet.walletId, action.cryptoCurrencyStatus.currency, action.source)
} else {
proceedWithLegacyBuyAction(state, action)
proceedWithLegacyBuyAction(action)
}
}
@ -75,7 +72,7 @@ object TradeCryptoMiddleware {
}
}
private fun proceedWithLegacyBuyAction(state: () -> AppState?, action: TradeCryptoAction.Buy) {
private fun proceedWithLegacyBuyAction(action: TradeCryptoAction.Buy) {
val networkAddress = action.cryptoCurrencyStatus.value.networkAddress
?.defaultAddress
?.let(NetworkAddress.Address::value)
@ -94,15 +91,10 @@ object TradeCryptoMiddleware {
)
scope.launch {
val homeFeatureToggles = store.inject(DaggerGraphState::homeFeatureToggles)
val getUserCountryCodeUseCase = store.inject(DaggerGraphState::getUserCountryUseCase)
val onrampFeatureToggles = store.inject(DaggerGraphState::onrampFeatureToggles)
val isRussia = if (homeFeatureToggles.isMigrateUserCountryCodeEnabled) {
val getUserCountryCodeUseCase = store.inject(DaggerGraphState::getUserCountryUseCase)
getUserCountryCodeUseCase().isRight { it is UserCountry.Russia }
} else {
state()?.globalState?.userCountryCode == RUSSIA_COUNTRY_CODE
}
val isRussia = getUserCountryCodeUseCase().isRight { it is UserCountry.Russia }
if (action.checkUserLocation && isRussia && !onrampFeatureToggles.isFeatureEnabled) {
val dialogData = topUrl?.let {

View file

@ -130,6 +130,5 @@ internal class WelcomeViewModel @Inject constructor(
private fun initGlobalState() {
store.dispatch(GlobalAction.RestoreAppCurrency)
store.dispatch(GlobalAction.ExchangeManager.Init)
store.dispatch(GlobalAction.FetchUserCountry)
}
}

View file

@ -4,10 +4,10 @@ import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.tap.features.welcome.ui.model.WarningModel
import com.tangem.wallet.R
@ -18,8 +18,8 @@ internal fun WarningDialog(warning: WarningModel?) {
null -> Unit
is WarningModel.BiometricsLockoutWarning -> {
BasicDialog(
title = stringResource(id = R.string.biometric_lockout_warning_title),
message = stringResource(
title = stringResourceSafe(id = R.string.biometric_lockout_warning_title),
message = stringResourceSafe(
id = if (warning.isPermanent) {
R.string.biometric_lockout_permanent_warning_description
} else {
@ -28,30 +28,30 @@ internal fun WarningDialog(warning: WarningModel?) {
),
onDismissDialog = warning.onDismiss,
confirmButton = DialogButtonUM(
title = stringResource(id = R.string.common_ok),
title = stringResourceSafe(id = R.string.common_ok),
onClick = warning.onDismiss,
),
)
}
is WarningModel.KeyInvalidatedWarning -> {
BasicDialog(
title = stringResource(id = R.string.common_attention),
message = stringResource(id = R.string.key_invalidated_warning_description),
title = stringResourceSafe(id = R.string.common_attention),
message = stringResourceSafe(id = R.string.key_invalidated_warning_description),
onDismissDialog = warning.onDismiss,
confirmButton = DialogButtonUM(
title = stringResource(id = R.string.common_ok),
title = stringResourceSafe(id = R.string.common_ok),
onClick = warning.onDismiss,
),
)
}
is WarningModel.BiometricsDisabledWarning -> {
BasicDialog(
title = stringResource(id = R.string.common_warning),
message = stringResource(id = R.string.biometric_unavailable_warning),
title = stringResourceSafe(id = R.string.common_warning),
message = stringResourceSafe(id = R.string.biometric_unavailable_warning),
onDismissDialog = warning.onDismiss,
isDismissable = false,
confirmButton = DialogButtonUM(
title = stringResource(id = R.string.common_ok),
title = stringResourceSafe(id = R.string.common_ok),
onClick = warning.onDismiss,
),
)

View file

@ -9,10 +9,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.*
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.wallet.R
@ -38,7 +38,7 @@ internal fun WelcomeScreenContent(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
text = stringResource(R.string.welcome_unlock_title),
text = stringResourceSafe(R.string.welcome_unlock_title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
@ -48,9 +48,9 @@ internal fun WelcomeScreenContent(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing44)
.fillMaxWidth(),
text = stringResource(
text = stringResourceSafe(
id = R.string.welcome_unlock_description,
stringResource(id = R.string.common_biometric_authentication),
stringResourceSafe(id = R.string.common_biometric_authentication),
),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.secondary,
@ -61,9 +61,9 @@ internal fun WelcomeScreenContent(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
text = stringResource(
text = stringResourceSafe(
id = R.string.welcome_unlock,
stringResource(id = R.string.common_biometrics),
stringResourceSafe(id = R.string.common_biometrics),
),
showProgress = showUnlockProgress,
onClick = onUnlockClick,
@ -73,7 +73,7 @@ internal fun WelcomeScreenContent(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
text = stringResource(R.string.welcome_unlock_card),
text = stringResourceSafe(R.string.welcome_unlock_card),
showProgress = showScanCardProgress,
iconResId = R.drawable.ic_tangem_24,
onClick = onScanCardClick,

View file

@ -35,7 +35,6 @@ import com.tangem.features.staking.api.navigation.StakingRouter
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
import com.tangem.tap.features.home.featuretoggles.HomeFeatureToggles
import com.tangem.tap.proxy.AppStateHolder
import org.rekotlin.StateType
@ -69,7 +68,6 @@ data class DaggerGraphState(
val appRouter: AppRouter? = null,
val pushNotificationsRouter: PushNotificationsRouter? = null,
val transactionSignerFactory: TransactionSignerFactory? = null,
val homeFeatureToggles: HomeFeatureToggles? = null,
val getUserCountryUseCase: GetUserCountryUseCase? = null,
val onrampFeatureToggles: OnrampFeatureToggles? = null,
val environmentConfigStorage: EnvironmentConfigStorage? = null,

View file

@ -0,0 +1,14 @@
package com.tangem.tap.routing.toggle
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
internal class RoutingFeatureToggles @Inject constructor(
private val featureTogglesManager: FeatureTogglesManager,
) {
val isNavigationRefactoringEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "NAVIGATION_REFACTORING")
}

View file

@ -35,6 +35,7 @@ import com.tangem.tap.features.onboarding.products.wallet.ui.OnboardingWalletFra
import com.tangem.tap.features.saveWallet.ui.SaveWalletBottomSheetFragment
import com.tangem.tap.features.welcome.ui.WelcomeFragment
import com.tangem.tap.routing.RoutingComponent.Child
import com.tangem.tap.routing.toggle.RoutingFeatureToggles
import com.tangem.utils.Provider
import dagger.hilt.android.scopes.ActivityScoped
import java.util.WeakHashMap
@ -61,10 +62,19 @@ internal class ChildFactory @Inject constructor(
private val stakingRouter: StakingRouter,
private val testerRouter: TesterRouter,
private val pushNotificationRouter: PushNotificationsRouter,
private val routingFeatureToggles: RoutingFeatureToggles,
) {
@Suppress("LongMethod", "CyclomaticComplexMethod")
fun createChild(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
return if (routingFeatureToggles.isNavigationRefactoringEnabled) {
TODO("Will be implemented in [REDACTED_JIRA]")
} else {
createChildLegacy(route, contextFactory)
}
}
@Suppress("LongMethod", "CyclomaticComplexMethod")
private fun createChildLegacy(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
componentContexts[route] = contextFactory(route)
return when (route) {