Updated on 2026-08-14
This commit is contained in:
commit
8232cc6e18
255 changed files with 2657 additions and 2338 deletions
44
Dockerfile
Normal file
44
Dockerfile
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
FROM ubuntu:24.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV ANDROID_HOME=/opt/android-sdk
|
||||
ENV BUNDLE_PATH=vendor/bundle
|
||||
ENV PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/34.0.0:$BUNDLE_PATH/bin
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
openjdk-17-jdk \
|
||||
wget \
|
||||
unzip \
|
||||
curl \
|
||||
git \
|
||||
ruby \
|
||||
ruby-dev \
|
||||
build-essential \
|
||||
&& apt-get clean
|
||||
|
||||
RUN mkdir -p $ANDROID_HOME/cmdline-tools/latest && \
|
||||
wget https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -O /tmp/cmdline-tools.zip && \
|
||||
unzip /tmp/cmdline-tools.zip -d $ANDROID_HOME/cmdline-tools/latest && \
|
||||
mv $ANDROID_HOME/cmdline-tools/latest/cmdline-tools/* $ANDROID_HOME/cmdline-tools/latest/ && \
|
||||
rm -rf $ANDROID_HOME/cmdline-tools/latest/cmdline-tools && \
|
||||
rm -f /tmp/cmdline-tools.zip && \
|
||||
yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses && \
|
||||
$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "platform-tools" "platforms;android-31" "platforms;android-34" "build-tools;34.0.0"
|
||||
|
||||
RUN wget https://github.com/lzhiyong/android-sdk-tools/releases/download/34.0.3/android-sdk-tools-static-aarch64.zip -O /tmp/android-sdk-tools-static-aarch64.zip && \
|
||||
unzip /tmp/android-sdk-tools-static-aarch64.zip -d /tmp/android-sdk-tools-static-arm && \
|
||||
cp -r /tmp/android-sdk-tools-static-arm/build-tools/* $ANDROID_HOME/build-tools/34.0.0/ && \
|
||||
rm -rf /tmp/android-sdk-tools-static-arm /tmp/android-sdk-tools-static-aarch64.zip
|
||||
|
||||
RUN gem install bundler:2.5.23
|
||||
RUN gem install fastlane -v 2.211.0 -N -V
|
||||
RUN gem install fastlane-plugin-firebase_app_distribution -v 0.9.1 -N -V
|
||||
|
||||
COPY Gemfile Gemfile.lock ./
|
||||
RUN bundle install --jobs=4 --retry=3 --verbose
|
||||
|
||||
RUN bundle exec fastlane -v
|
||||
|
||||
CMD ["bash"]
|
||||
|
|
@ -166,6 +166,7 @@ dependencies {
|
|||
implementation(deps.androidx.browser)
|
||||
implementation(deps.androidx.paging.runtime)
|
||||
implementation(deps.androidx.swipeRefreshLayout)
|
||||
implementation(deps.androidx.fragment.compose)
|
||||
implementation(deps.lifecycle.runtime.ktx)
|
||||
implementation(deps.lifecycle.common.java8)
|
||||
implementation(deps.lifecycle.viewModel.ktx)
|
||||
|
|
@ -226,7 +227,6 @@ dependencies {
|
|||
implementation(deps.xmlShimmer)
|
||||
implementation(deps.viewBindingDelegate)
|
||||
implementation(deps.armadillo)
|
||||
implementation(deps.mviCore.watcher)
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(deps.reownCore)
|
||||
implementation(deps.reownWeb3)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
fun StringBuilder.breakLine(count: Int = 1): StringBuilder = append("\n".repeat(count))
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -82,8 +82,4 @@ sealed class GlobalAction : Action {
|
|||
|
||||
object Update : GlobalAction()
|
||||
}
|
||||
|
||||
object FetchUserCountry : GlobalAction() {
|
||||
data class Success(val countryCode: String) : GlobalAction()
|
||||
}
|
||||
}
|
||||
|
|
@ -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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 = {},
|
||||
|
|
|
|||
|
|
@ -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 = {},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -176,7 +176,6 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
|
|||
OnboardingHelper.handleTopUpAction(
|
||||
walletManager = walletManager,
|
||||
scanResponse = scanResponse,
|
||||
globalState = globalState,
|
||||
)
|
||||
}
|
||||
is OnboardingNoteAction.Done -> {
|
||||
|
|
|
|||
|
|
@ -299,7 +299,6 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
OnboardingHelper.handleTopUpAction(
|
||||
walletManager = walletManager,
|
||||
scanResponse = onboardingManager?.scanResponse ?: return,
|
||||
globalState = globalState,
|
||||
)
|
||||
}
|
||||
TwinCardsAction.Done -> {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
14
ci_resources/build_8gb_ram_ci_gradle.properties
Normal file
14
ci_resources/build_8gb_ram_ci_gradle.properties
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
org.gradle.jvmargs=-Xmx7g -XX:MaxMetaspaceSize=512m -XX:+UseParallelGC -XX:ParallelGCThreads=3 -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.parallel=true
|
||||
org.gradle.workers.max=3
|
||||
org.gradle.daemon=false
|
||||
org.gradle.unsafe.configuration-cache=false
|
||||
org.gradle.console=plain
|
||||
org.gradle.caching=false
|
||||
|
||||
android.useAndroidX=true
|
||||
android.nonTransitiveRClass=false
|
||||
android.aapt2FromMavenOverride=/opt/android-sdk/build-tools/34.0.0/aapt2
|
||||
|
||||
kotlin.incremental=false
|
||||
kotlin.compiler.execution.strategy=in-process
|
||||
13
ci_resources/tests_8gb_ram_ci_gradle.properties
Normal file
13
ci_resources/tests_8gb_ram_ci_gradle.properties
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
org.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=512m -XX:+UseParallelGC -XX:ParallelGCThreads=3 -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.parallel=true
|
||||
org.gradle.workers.max=3
|
||||
org.gradle.daemon=false
|
||||
org.gradle.unsafe.configuration-cache=false
|
||||
org.gradle.console=plain
|
||||
org.gradle.caching=false
|
||||
|
||||
android.useAndroidX=true
|
||||
android.nonTransitiveRClass=false
|
||||
android.aapt2FromMavenOverride=/opt/android-sdk/build-tools/34.0.0/aapt2
|
||||
|
||||
kotlin.incremental=false
|
||||
BIN
ci_resources/x86_64_libs/ld-linux-x86-64.so.2
Executable file
BIN
ci_resources/x86_64_libs/ld-linux-x86-64.so.2
Executable file
Binary file not shown.
BIN
ci_resources/x86_64_libs/libc.so.6
Executable file
BIN
ci_resources/x86_64_libs/libc.so.6
Executable file
Binary file not shown.
BIN
ci_resources/x86_64_libs/libdl.so.2
Normal file
BIN
ci_resources/x86_64_libs/libdl.so.2
Normal file
Binary file not shown.
BIN
ci_resources/x86_64_libs/libgcc_s.so.1
Normal file
BIN
ci_resources/x86_64_libs/libgcc_s.so.1
Normal file
Binary file not shown.
BIN
ci_resources/x86_64_libs/libm.so.6
Normal file
BIN
ci_resources/x86_64_libs/libm.so.6
Normal file
Binary file not shown.
BIN
ci_resources/x86_64_libs/libpthread.so.0
Normal file
BIN
ci_resources/x86_64_libs/libpthread.so.0
Normal file
Binary file not shown.
BIN
ci_resources/x86_64_libs/librt.so.1
Normal file
BIN
ci_resources/x86_64_libs/librt.so.1
Normal file
Binary file not shown.
|
|
@ -12,7 +12,6 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.amountScreen.AmountScreenClickIntents
|
||||
import com.tangem.common.ui.amountScreen.models.AmountSegmentedButtonsConfig
|
||||
|
|
@ -21,6 +20,7 @@ import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
|
|||
import com.tangem.core.ui.components.currency.fiaticon.FiatIcon
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ internal fun LazyListScope.buttons(
|
|||
SpacerWMax()
|
||||
}
|
||||
Text(
|
||||
text = stringResource(R.string.send_max_amount),
|
||||
text = stringResourceSafe(R.string.send_max_amount),
|
||||
style = TangemTheme.typography.button,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.components.SecondaryButtonIconStart
|
||||
import com.tangem.core.ui.components.SpacerW12
|
||||
import com.tangem.core.ui.extensions.shareText
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
@Composable
|
||||
|
|
@ -38,14 +38,14 @@ fun SendDoneButtons(
|
|||
) {
|
||||
Row(modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12)) {
|
||||
SecondaryButtonIconStart(
|
||||
text = stringResource(id = R.string.common_explore),
|
||||
text = stringResourceSafe(id = R.string.common_explore),
|
||||
iconResId = R.drawable.ic_web_24,
|
||||
onClick = onExploreClick,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
SpacerW12()
|
||||
SecondaryButtonIconStart(
|
||||
text = stringResource(id = R.string.common_share),
|
||||
text = stringResourceSafe(id = R.string.common_share),
|
||||
iconResId = R.drawable.ic_share_24,
|
||||
onClick = {
|
||||
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -54,7 +53,7 @@ fun GiveTxPermissionBottomSheet(config: TangemBottomSheetConfig) {
|
|||
if (isPermissionAlertShow) {
|
||||
BasicDialog(
|
||||
message = content.data.dialogText.resolveReference(),
|
||||
title = stringResource(id = R.string.common_approve),
|
||||
title = stringResourceSafe(id = R.string.common_approve),
|
||||
confirmButton = DialogButtonUM { isPermissionAlertShow = false },
|
||||
onDismissDialog = {},
|
||||
)
|
||||
|
|
@ -87,7 +86,7 @@ private fun GiveTxPermissionBottomSheetContent(content: GiveTxPermissionBottomSh
|
|||
SpacerH(height = TangemTheme.dimens.spacing20)
|
||||
|
||||
PrimaryButtonIconEnd(
|
||||
text = stringResource(id = R.string.common_approve),
|
||||
text = stringResourceSafe(id = R.string.common_approve),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
showProgress = data.approveButton.loading,
|
||||
modifier = Modifier
|
||||
|
|
@ -100,7 +99,7 @@ private fun GiveTxPermissionBottomSheetContent(content: GiveTxPermissionBottomSh
|
|||
SpacerH12()
|
||||
|
||||
SecondaryButton(
|
||||
text = stringResource(id = R.string.common_cancel),
|
||||
text = stringResourceSafe(id = R.string.common_cancel),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
|
|
@ -180,7 +179,7 @@ private fun AmountItem(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.give_permission_rows_amount, currency),
|
||||
text = stringResourceSafe(id = R.string.give_permission_rows_amount, currency),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
maxLines = 1,
|
||||
|
|
@ -259,10 +258,10 @@ private fun DropdownSelector(
|
|||
Row {
|
||||
Text(
|
||||
text = when (item) {
|
||||
ApproveType.LIMITED -> stringResource(
|
||||
ApproveType.LIMITED -> stringResourceSafe(
|
||||
id = R.string.give_permission_current_transaction,
|
||||
)
|
||||
ApproveType.UNLIMITED -> stringResource(id = R.string.give_permission_unlimited)
|
||||
ApproveType.UNLIMITED -> stringResourceSafe(id = R.string.give_permission_unlimited)
|
||||
},
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.body1,
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ sealed class NavigationButtonsState {
|
|||
data class NavigationButton(
|
||||
val textReference: TextReference,
|
||||
@DrawableRes val iconRes: Int? = null,
|
||||
val isSecondary: Boolean,
|
||||
val isIconVisible: Boolean,
|
||||
val showProgress: Boolean,
|
||||
val isEnabled: Boolean,
|
||||
val isSecondary: Boolean = false,
|
||||
val isIconVisible: Boolean = false,
|
||||
val showProgress: Boolean = false,
|
||||
val isEnabled: Boolean = true,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.common.ui.notifications
|
||||
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.amountScreen.models.AmountFieldModel
|
||||
import com.tangem.common.ui.amountScreen.utils.getFiatString
|
||||
|
|
@ -149,10 +148,19 @@ object NotificationsFactory {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Existential Warning
|
||||
*
|
||||
* @param existentialDeposit existential deposit of blockchain
|
||||
* @param feeAmount amount of fee spending for transaction
|
||||
* @param sendingAmount amount sending by user (excluding fee for coins)
|
||||
* @param cryptoCurrencyStatus blockchain currency status
|
||||
* @param onReduceClick action to leave existential amount in balance after transaction
|
||||
*/
|
||||
fun MutableList<NotificationUM>.addExistentialWarningNotification(
|
||||
existentialDeposit: BigDecimal?,
|
||||
feeAmount: BigDecimal,
|
||||
receivedAmount: BigDecimal,
|
||||
sendingAmount: BigDecimal,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
onReduceClick: (
|
||||
reduceAmountBy: BigDecimal,
|
||||
|
|
@ -165,7 +173,7 @@ object NotificationsFactory {
|
|||
val spendingAmount = if (cryptoCurrency is CryptoCurrency.Token) {
|
||||
feeAmount
|
||||
} else {
|
||||
receivedAmount
|
||||
sendingAmount
|
||||
}
|
||||
val diff = balance.minus(spendingAmount)
|
||||
if (existentialDeposit != null && diff >= BigDecimal.ZERO && existentialDeposit > diff) {
|
||||
|
|
@ -283,9 +291,9 @@ object NotificationsFactory {
|
|||
|
||||
fun MutableList<NotificationUM>.addValidateTransactionNotifications(
|
||||
dustValue: BigDecimal,
|
||||
fee: Fee?,
|
||||
validationError: Throwable?,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
minAdaValue: BigDecimal?, // TODO revert to Fee, after swap TxFee refactored
|
||||
onReduceClick: (
|
||||
reduceAmountTo: BigDecimal,
|
||||
notification: Class<out NotificationUM>,
|
||||
|
|
@ -301,11 +309,11 @@ object NotificationsFactory {
|
|||
error = validationError,
|
||||
onReduceClick = onReduceClick,
|
||||
)
|
||||
null -> (fee as? Fee.CardanoToken)?.let {
|
||||
null -> minAdaValue?.let {
|
||||
add(
|
||||
NotificationUM.Cardano.MinAdaValueCharged(
|
||||
tokenName = cryptoCurrency.name,
|
||||
minAdaValue = it.minAdaValue.parseBigDecimal(cryptoCurrency.decimals),
|
||||
minAdaValue = minAdaValue.parseBigDecimal(cryptoCurrency.decimals),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,10 +11,6 @@
|
|||
"name": "IS_ETHEREUM_EIP_1559_ENABLED",
|
||||
"version": "5.17.0"
|
||||
},
|
||||
{
|
||||
"name": "MIGRATE_USER_COUNTRY_CODE_ENABLED",
|
||||
"version": "5.17.0"
|
||||
},
|
||||
{
|
||||
"name": "ONRAMP_ENABLED",
|
||||
"version": "5.19.0"
|
||||
|
|
@ -26,5 +22,9 @@
|
|||
{
|
||||
"name": "ONBOARDING_CODE_REFACTORING_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "NAVIGATION_REFACTORING",
|
||||
"version": "undefined"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
package com.tangem.datasource.api.tangemTech
|
||||
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.datasource.api.common.response.ApiResponseCallAdapterFactory
|
||||
import com.tangem.datasource.utils.RequestHeader
|
||||
import com.tangem.datasource.utils.RequestHeader.AuthenticationHeader
|
||||
import com.tangem.datasource.utils.RequestHeader.CacheControlHeader
|
||||
import com.tangem.datasource.utils.addHeaders
|
||||
import com.tangem.datasource.utils.addLoggers
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// TODO("Remove after removing Redux")
|
||||
@Deprecated("Use TangemTechApi")
|
||||
object TangemTechService {
|
||||
|
||||
var api: TangemTechApi = createApi()
|
||||
private set
|
||||
|
||||
private const val TANGEM_TECH_BASE_URL = "https://api.tangem-tech.com/v1/"
|
||||
|
||||
fun addAuthenticationHeader(header: AuthenticationHeader) {
|
||||
api = createApi(header)
|
||||
}
|
||||
|
||||
private fun createApi(header: RequestHeader? = null): TangemTechApi {
|
||||
val headers = mutableListOf<RequestHeader>(CacheControlHeader).apply { header?.let(::add) }
|
||||
return Retrofit.Builder()
|
||||
.addConverterFactory(MoshiConverter.networkMoshiConverter)
|
||||
.addCallAdapterFactory(ApiResponseCallAdapterFactory.create())
|
||||
.baseUrl(TANGEM_TECH_BASE_URL)
|
||||
.client(
|
||||
OkHttpClient.Builder()
|
||||
.addHeaders(*headers.toTypedArray())
|
||||
.addLoggers()
|
||||
.build(),
|
||||
)
|
||||
.build()
|
||||
.create(TangemTechApi::class.java)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,4 +6,17 @@ plugins {
|
|||
|
||||
android {
|
||||
namespace = "com.tangem.core.res"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(projects.core.utils)
|
||||
|
||||
implementation(deps.timber)
|
||||
|
||||
// region Firebase libraries
|
||||
implementation(platform(deps.firebase.bom))
|
||||
implementation(deps.firebase.analytics)
|
||||
implementation(deps.firebase.crashlytics)
|
||||
// endregion
|
||||
}
|
||||
82
core/res/src/main/java/com/tangem/core/res/Resources.kt
Normal file
82
core/res/src/main/java/com/tangem/core/res/Resources.kt
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package com.tangem.core.res
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.annotation.StringRes
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.utils.SupportedLanguages
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Get a string resource safely or the resource name if an exception is thrown
|
||||
*
|
||||
* @param id resource id
|
||||
*/
|
||||
fun Resources.getStringSafe(@StringRes id: Int): String {
|
||||
return runCatching { getString(id) }
|
||||
.getOrResourceName(resources = this, id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string resource safely or the resource name if an exception is thrown
|
||||
*
|
||||
* @param id resource id
|
||||
* @param formatArgs format args
|
||||
*/
|
||||
fun Resources.getStringSafe(@StringRes id: Int, vararg formatArgs: Any): String {
|
||||
return runCatching { getString(id, *formatArgs) }
|
||||
.recoverCatching {
|
||||
// If something goes wrong, returns the resource without arguments
|
||||
val string = getString(id)
|
||||
|
||||
reportIssue(resources = this, id, *formatArgs)
|
||||
|
||||
string
|
||||
}
|
||||
.getOrResourceName(resources = this, id, *formatArgs)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a plural string resource safely or the resource name if an exception is thrown
|
||||
*
|
||||
* @param id resource id
|
||||
* @param count count
|
||||
*/
|
||||
fun Resources.getPluralStringSafe(@PluralsRes id: Int, count: Int): String {
|
||||
return runCatching { getQuantityString(id, count) }
|
||||
.getOrResourceName(resources = this, id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a plural string resource safely or the resource name if an exception is thrown
|
||||
*
|
||||
* @param id resource id
|
||||
* @param count count
|
||||
* @param formatArgs format args
|
||||
*/
|
||||
fun Resources.getPluralStringSafe(@PluralsRes id: Int, count: Int, vararg formatArgs: Any): String {
|
||||
return runCatching { getQuantityString(id, count, *formatArgs) }
|
||||
.getOrResourceName(resources = this, id, *formatArgs)
|
||||
}
|
||||
|
||||
private fun Result<String>.getOrResourceName(resources: Resources, id: Int, vararg formatArgs: Any): String {
|
||||
return getOrElse {
|
||||
reportIssue(resources, id, formatArgs)
|
||||
|
||||
// If something still goes wrong, returns the resource name
|
||||
resources.getResourceEntryName(id)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportIssue(resources: Resources, id: Int, vararg formatArgs: Any) {
|
||||
val exception = IllegalStateException(
|
||||
"An error occurred while parsing the string:\n" +
|
||||
"\tname: R.string.${resources.getResourceEntryName(id)}\n" +
|
||||
"\targs: ${formatArgs.joinToString(prefix = "[", postfix = "]") { it.toString() }}\n" +
|
||||
"\tlocale: ${SupportedLanguages.getCurrentSupportedLanguageCode()}\n",
|
||||
)
|
||||
|
||||
Timber.tag("Resources").e(exception)
|
||||
|
||||
FirebaseCrashlytics.getInstance().recordException(exception)
|
||||
}
|
||||
|
|
@ -16,7 +16,6 @@ import androidx.compose.runtime.Immutable
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
|
|
@ -27,6 +26,7 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.components.SelctorDialogParamsProvider.SelectorDialogParams
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
|
||||
import com.tangem.core.ui.components.fields.SimpleDialogTextField
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -318,14 +318,14 @@ private fun DialogButtons(
|
|||
) {
|
||||
if (dismissButton != null) {
|
||||
DialogButton(
|
||||
text = dismissButton.title ?: stringResource(id = R.string.common_cancel),
|
||||
text = dismissButton.title ?: stringResourceSafe(id = R.string.common_cancel),
|
||||
warning = dismissButton.warning,
|
||||
enabled = dismissButton.enabled,
|
||||
onClick = dismissButton.onClick,
|
||||
)
|
||||
}
|
||||
DialogButton(
|
||||
text = confirmButton.title ?: stringResource(id = R.string.common_ok),
|
||||
text = confirmButton.title ?: stringResourceSafe(id = R.string.common_ok),
|
||||
warning = confirmButton.warning,
|
||||
enabled = confirmButton.enabled,
|
||||
onClick = confirmButton.onClick,
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ import androidx.compose.ui.layout.ContentScale
|
|||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.res.getStringSafe
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SecondaryButtonIconStart
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
|
|
@ -96,7 +96,7 @@ private fun Info(currency: String, network: String, showMemoDisclaimer: Boolean,
|
|||
modifier = Modifier
|
||||
.padding(horizontal = 18.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResource(R.string.receive_bottom_sheet_no_memo_required_message),
|
||||
text = stringResourceSafe(R.string.receive_bottom_sheet_no_memo_required_message),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
|
|
@ -191,7 +191,7 @@ private fun QrCodePage(content: TokenReceiveBottomSheetConfig, qrCodePainter: Pa
|
|||
verticalArrangement = Arrangement.spacedBy(24.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(
|
||||
text = stringResourceSafe(
|
||||
R.string.receive_bottom_sheet_warning_message,
|
||||
getName(content = content, index = currentIndex),
|
||||
content.symbol,
|
||||
|
|
@ -247,7 +247,7 @@ private fun Buttons(
|
|||
) {
|
||||
SecondaryButtonIconStart(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResource(id = R.string.common_copy),
|
||||
text = stringResourceSafe(id = R.string.common_copy),
|
||||
iconResId = R.drawable.ic_copy_24,
|
||||
onClick = {
|
||||
onCopyClick()
|
||||
|
|
@ -257,7 +257,7 @@ private fun Buttons(
|
|||
|
||||
coroutineScope.launch {
|
||||
snackbarHostState.showSnackbar(
|
||||
message = resources.getString(R.string.wallet_notification_address_copied),
|
||||
message = resources.getStringSafe(R.string.wallet_notification_address_copied),
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
@ -265,7 +265,7 @@ private fun Buttons(
|
|||
|
||||
SecondaryButtonIconStart(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResource(id = R.string.common_share),
|
||||
text = stringResourceSafe(id = R.string.common_share),
|
||||
iconResId = R.drawable.ic_share_24,
|
||||
onClick = {
|
||||
onShareClick()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import android.content.res.Configuration
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -14,7 +17,6 @@ import androidx.compose.runtime.remember
|
|||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
|
|
@ -24,8 +26,9 @@ import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
|||
import com.tangem.core.ui.components.inputrow.inner.InputRowAsyncImage
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
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
|
||||
|
||||
/**
|
||||
* [Input Row Best Rate](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-889&mode=dev)
|
||||
|
|
@ -112,7 +115,7 @@ private fun InnerTitle(title: TextReference, titleExtra: TextReference, showTag:
|
|||
)
|
||||
if (showTag) {
|
||||
Text(
|
||||
text = stringResource(R.string.express_provider_best_rate),
|
||||
text = stringResourceSafe(R.string.express_provider_best_rate),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.icon.accent,
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
|||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
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.utils.DEFAULT_ANIMATION_DURATION
|
||||
|
|
@ -51,7 +51,7 @@ fun PasteButton(isPasteButtonVisible: Boolean, onClick: (String) -> Unit, modifi
|
|||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.common_paste),
|
||||
text = stringResourceSafe(R.string.common_paste),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.primary2,
|
||||
modifier = Modifier
|
||||
|
|
@ -86,7 +86,7 @@ fun CrossIcon(onClick: (String) -> Unit, modifier: Modifier = Modifier) {
|
|||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_close_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = stringResource(R.string.common_close),
|
||||
contentDescription = stringResourceSafe(R.string.common_close),
|
||||
modifier = modifier
|
||||
.size(TangemTheme.dimens.size24)
|
||||
.clickable(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
|
|
@ -18,6 +17,7 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter
|
|||
import androidx.compose.ui.unit.Dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
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.utils.BigDecimalFormatter
|
||||
|
|
@ -61,7 +61,7 @@ private fun Title(currencyName: String, modifier: Modifier = Modifier) {
|
|||
contentAlignment = Alignment.CenterStart,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.wallet_marketplace_block_title, currencyName),
|
||||
text = stringResourceSafe(id = R.string.wallet_marketplace_block_title, currencyName),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
|
@ -157,7 +157,7 @@ private fun LoadingContent() {
|
|||
@Composable
|
||||
private fun QuoteTimeStatus() {
|
||||
Text(
|
||||
text = stringResource(id = R.string.wallet_marketprice_block_update_time),
|
||||
text = stringResourceSafe(id = R.string.wallet_marketprice_block_update_time),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import androidx.compose.runtime.getValue
|
|||
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.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -23,8 +22,9 @@ import com.tangem.core.ui.components.atoms.text.EllipsisText
|
|||
import com.tangem.core.ui.components.atoms.text.TextEllipsis
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
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.utils.StringsSigns
|
||||
|
||||
@Composable
|
||||
|
|
@ -77,7 +77,7 @@ fun SelectorRowItem(
|
|||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(titleRes),
|
||||
text = stringResourceSafe(titleRes),
|
||||
style = textStyle,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing8),
|
||||
|
|
|
|||
|
|
@ -10,10 +10,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.TextOverflow
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.detectReorder
|
||||
|
|
@ -67,7 +67,7 @@ private fun DraggableImage(reorderableTokenListState: ReorderableLazyListState?)
|
|||
@Composable
|
||||
private fun NonFiatContentText(@StringRes text: Int) {
|
||||
Text(
|
||||
text = stringResource(id = text),
|
||||
text = stringResourceSafe(id = text),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.audits.AuditLabel
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState.Subtitle2State as TokenCryptoAmountState
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ internal fun TokenCryptoAmount(
|
|||
AuditLabel(state = state.auditLabelUM, modifier = modifier)
|
||||
}
|
||||
is TokenCryptoAmountState.Unreachable -> {
|
||||
CryptoAmountText(amount = stringResource(id = R.string.common_unreachable), modifier = modifier)
|
||||
CryptoAmountText(amount = stringResourceSafe(id = R.string.common_unreachable), modifier = modifier)
|
||||
}
|
||||
is TokenCryptoAmountState.Loading -> {
|
||||
RectangleShimmer(modifier = modifier.placeholderSize(), radius = TangemTheme.dimens.radius4)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import androidx.compose.ui.unit.dp
|
|||
import com.tangem.core.ui.components.fields.SearchBar
|
||||
import com.tangem.core.ui.components.token.TokenItem
|
||||
import com.tangem.core.ui.components.tokenlist.internal.GroupTitleItem
|
||||
import com.tangem.core.ui.components.tokenlist.internal.NetworkTitleItem
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -25,9 +24,6 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
@Composable
|
||||
fun TokenListItem(state: TokensListItemUM, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is TokensListItemUM.NetworkGroupTitle -> {
|
||||
NetworkTitleItem(networkName = state.name.resolveReference(), modifier = modifier)
|
||||
}
|
||||
is TokensListItemUM.GroupTitle -> {
|
||||
GroupTitleItem(state, modifier)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,32 @@
|
|||
package com.tangem.core.ui.components.tokenlist.internal
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
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.R
|
||||
import com.tangem.core.ui.components.rows.NetworkTitle
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.detectReorder
|
||||
import org.burnoutcrew.reorderable.rememberReorderableLazyListState
|
||||
|
||||
/**
|
||||
* Group title item
|
||||
|
|
@ -18,14 +38,90 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
*/
|
||||
@Composable
|
||||
fun GroupTitleItem(state: TokensListItemUM.GroupTitle, modifier: Modifier = Modifier) {
|
||||
BaseGroupTitleItem(text = state.text, modifier = modifier)
|
||||
}
|
||||
|
||||
/**
|
||||
* Draggable network group item
|
||||
*
|
||||
* @param state state
|
||||
* @param reorderableTokenListState reorderable token list state
|
||||
* @param modifier modifier
|
||||
*/
|
||||
@Composable
|
||||
fun DraggableGroupTitleItem(
|
||||
state: TokensListItemUM.GroupTitle,
|
||||
reorderableTokenListState: ReorderableLazyListState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BaseGroupTitleItem(
|
||||
text = state.text,
|
||||
modifier = modifier,
|
||||
action = { DraggableIcon(reorderableTokenListState = reorderableTokenListState) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BaseGroupTitleItem(
|
||||
text: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
action: (@Composable BoxScope.() -> Unit)? = null,
|
||||
) {
|
||||
NetworkTitle(
|
||||
title = {
|
||||
Text(
|
||||
text = state.text.resolveReference(),
|
||||
text = text.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
action = action,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DraggableIcon(reorderableTokenListState: ReorderableLazyListState) {
|
||||
Box(
|
||||
modifier = Modifier.detectReorder(reorderableTokenListState),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(
|
||||
image = ImageVector.vectorResource(id = R.drawable.ic_group_drop_24),
|
||||
),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun NetworkTitleItemPreview(@PreviewParameter(GroupTitleItemProvider::class) isDraggable: Boolean) {
|
||||
TangemThemePreview {
|
||||
val state = TokensListItemUM.GroupTitle(
|
||||
id = 1,
|
||||
text = resourceReference(
|
||||
id = R.string.wallet_network_group_title,
|
||||
formatArgs = wrappedList(resourceReference(id = R.string.main_tokens)),
|
||||
),
|
||||
)
|
||||
|
||||
if (isDraggable) {
|
||||
DraggableGroupTitleItem(
|
||||
state = state,
|
||||
reorderableTokenListState = rememberReorderableLazyListState(onMove = { _, _ -> }),
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.primary),
|
||||
)
|
||||
} else {
|
||||
GroupTitleItem(
|
||||
state = state,
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.primary),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object GroupTitleItemProvider : CollectionPreviewParameterProvider<Boolean>(collection = listOf(true, false))
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
package com.tangem.core.ui.components.tokenlist.internal
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
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.R
|
||||
import com.tangem.core.ui.components.rows.NetworkTitle
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import org.burnoutcrew.reorderable.ReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.detectReorder
|
||||
import org.burnoutcrew.reorderable.rememberReorderableLazyListState
|
||||
|
||||
/**
|
||||
* Network title item
|
||||
*
|
||||
* @param networkName network name
|
||||
* @param modifier modifier
|
||||
*/
|
||||
@Composable
|
||||
internal fun NetworkTitleItem(networkName: String, modifier: Modifier = Modifier) {
|
||||
BaseNetworkTitleItem(networkName = networkName, modifier = modifier)
|
||||
}
|
||||
|
||||
/**
|
||||
* Draggable network title item
|
||||
*
|
||||
* @param networkName network name
|
||||
* @param reorderableTokenListState reorderable token list state
|
||||
* @param modifier modifier
|
||||
*/
|
||||
@Composable
|
||||
fun DraggableNetworkTitleItem(
|
||||
networkName: String,
|
||||
reorderableTokenListState: ReorderableLazyListState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BaseNetworkTitleItem(
|
||||
networkName = networkName,
|
||||
modifier = modifier,
|
||||
action = { DraggableIcon(reorderableTokenListState = reorderableTokenListState) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BaseNetworkTitleItem(
|
||||
networkName: String,
|
||||
modifier: Modifier = Modifier,
|
||||
action: (@Composable BoxScope.() -> Unit)? = null,
|
||||
) {
|
||||
NetworkTitle(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.wallet_network_group_title, networkName),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
action = action,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DraggableIcon(reorderableTokenListState: ReorderableLazyListState) {
|
||||
Box(
|
||||
modifier = Modifier.detectReorder(reorderableTokenListState),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(
|
||||
image = ImageVector.vectorResource(id = R.drawable.ic_group_drop_24),
|
||||
),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun NetworkTitleItemPreview(@PreviewParameter(NetworkTitleItemProvider::class) isDraggable: Boolean) {
|
||||
TangemThemePreview {
|
||||
if (isDraggable) {
|
||||
DraggableNetworkTitleItem(
|
||||
networkName = "Ethereum",
|
||||
reorderableTokenListState = rememberReorderableLazyListState(onMove = { _, _ -> }),
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.primary),
|
||||
)
|
||||
} else {
|
||||
NetworkTitleItem(
|
||||
networkName = "Ethereum",
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.primary),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object NetworkTitleItemProvider : CollectionPreviewParameterProvider<Boolean>(collection = listOf(true, false))
|
||||
|
|
@ -23,15 +23,6 @@ sealed interface TokensListItemUM {
|
|||
val searchBarUM: SearchBarUM,
|
||||
) : TokensListItemUM
|
||||
|
||||
/**
|
||||
* Network group title
|
||||
*
|
||||
* @property id id
|
||||
* @property name network group name
|
||||
*/
|
||||
@Deprecated("Use GroupTitle instead") // TODO: [REDACTED_JIRA]
|
||||
data class NetworkGroupTitle(override val id: Int, val name: TextReference) : TokensListItemUM
|
||||
|
||||
/**
|
||||
* Group title
|
||||
*
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ 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.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -234,7 +233,7 @@ private fun Subtitle(state: TransactionState, modifier: Modifier = Modifier) {
|
|||
is TransactionState.Content -> {
|
||||
Text(
|
||||
text = when (state.status) {
|
||||
is Status.Failed -> stringResource(id = R.string.common_transaction_failed)
|
||||
is Status.Failed -> stringResourceSafe(id = R.string.common_transaction_failed)
|
||||
else -> state.subtitle.resolveReference()
|
||||
},
|
||||
modifier = modifier,
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ 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.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
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
|
||||
|
||||
/**
|
||||
* Transactions block title
|
||||
|
|
@ -35,7 +35,7 @@ internal fun TxHistoryTitle(onExploreClick: () -> Unit, modifier: Modifier = Mod
|
|||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.common_transactions),
|
||||
text = stringResourceSafe(id = R.string.common_transactions),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
|
@ -52,7 +52,7 @@ internal fun TxHistoryTitle(onExploreClick: () -> Unit, modifier: Modifier = Mod
|
|||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.common_explorer),
|
||||
text = stringResourceSafe(id = R.string.common_explorer),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,12 +2,56 @@ package com.tangem.core.ui.extensions
|
|||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.EncodeHintType
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
|
||||
import com.tangem.core.res.getPluralStringSafe
|
||||
import com.tangem.core.res.getStringSafe
|
||||
import java.util.Hashtable
|
||||
|
||||
/**
|
||||
* Get a string resource safely or the resource name if an exception is thrown
|
||||
*
|
||||
* @param id resource id
|
||||
* @param formatArgs format args
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
fun stringResourceSafe(@StringRes id: Int, vararg formatArgs: Any): String {
|
||||
val resources = LocalContext.current.resources
|
||||
|
||||
return if (formatArgs.isEmpty()) {
|
||||
resources.getStringSafe(id)
|
||||
} else {
|
||||
resources.getStringSafe(id, *formatArgs)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a plural string resource safely or the resource name if an exception is thrown
|
||||
*
|
||||
* @param id resource id
|
||||
* @param count count
|
||||
* @param formatArgs format args
|
||||
*/
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
fun pluralStringResourceSafe(@PluralsRes id: Int, count: Int, vararg formatArgs: Any): String {
|
||||
val resources = LocalContext.current.resources
|
||||
|
||||
return if (formatArgs.isEmpty()) {
|
||||
resources.getPluralStringSafe(id, count)
|
||||
} else {
|
||||
resources.getPluralStringSafe(id, count, *formatArgs)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun String.toQrCode(sizePx: Int = 256, paddingPx: Int = 0): Bitmap {
|
||||
val hintMap = Hashtable<EncodeHintType, Any>()
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@ import androidx.annotation.StringRes
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.AnnotatedString.Builder
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.decapitalize
|
||||
import com.tangem.core.res.getPluralStringSafe
|
||||
import com.tangem.core.res.getStringSafe
|
||||
import org.intellij.markdown.MarkdownElementTypes
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
|
@ -172,7 +171,7 @@ fun TextReference.resolveReference(): String {
|
|||
.map { if (it is TextReference) it.resolveReference() else it }
|
||||
.toTypedArray()
|
||||
|
||||
val resolvedReference = stringResource(id = id, *args)
|
||||
val resolvedReference = stringResourceSafe(id = id, *args)
|
||||
|
||||
if (decapitalize) {
|
||||
resolvedReference.replaceFirstChar { char -> char.lowercase() }
|
||||
|
|
@ -180,7 +179,7 @@ fun TextReference.resolveReference(): String {
|
|||
resolvedReference
|
||||
}
|
||||
}
|
||||
is TextReference.PluralRes -> pluralStringResource(id, count, *formatArgs.toTypedArray())
|
||||
is TextReference.PluralRes -> pluralStringResourceSafe(id, count, *formatArgs.toTypedArray())
|
||||
is TextReference.Str -> value
|
||||
is TextReference.Annotated -> value.text
|
||||
is TextReference.Combined -> {
|
||||
|
|
@ -201,9 +200,9 @@ fun TextReference.resolveReference(resources: Resources): String {
|
|||
.map { if (it is TextReference) it.resolveReference(resources) else it }
|
||||
.toTypedArray()
|
||||
|
||||
resources.getString(id, *args)
|
||||
resources.getStringSafe(id, *args)
|
||||
}
|
||||
is TextReference.PluralRes -> resources.getQuantityString(id, count, *formatArgs.toTypedArray())
|
||||
is TextReference.PluralRes -> resources.getPluralStringSafe(id, count, *formatArgs.toTypedArray())
|
||||
is TextReference.Str -> value
|
||||
is TextReference.Annotated -> value.text
|
||||
is TextReference.Combined -> {
|
||||
|
|
@ -225,10 +224,10 @@ fun TextReference.resolveAnnotatedReference(): AnnotatedString {
|
|||
.map { if (it is TextReference) it.resolveReference() else it }
|
||||
.toTypedArray()
|
||||
|
||||
formatAnnotated(stringResource(id = id, *args))
|
||||
formatAnnotated(stringResourceSafe(id = id, *args))
|
||||
}
|
||||
is TextReference.PluralRes -> formatAnnotated(
|
||||
pluralStringResource(id, count, *formatArgs.toTypedArray()),
|
||||
pluralStringResourceSafe(id, count, *formatArgs.toTypedArray()),
|
||||
)
|
||||
is TextReference.Str -> formatAnnotated(value)
|
||||
is TextReference.Annotated -> value
|
||||
|
|
|
|||
BIN
core/ui/src/main/res/drawable/img_card_visa.webp
Normal file
BIN
core/ui/src/main/res/drawable/img_card_visa.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
|
|
@ -25,11 +25,12 @@ class Debouncer {
|
|||
coroutineScope: CoroutineScope,
|
||||
waitMs: Long = 300L,
|
||||
context: CoroutineContext = EmptyCoroutineContext,
|
||||
forceUpdate: Boolean = false,
|
||||
destinationFunction: suspend () -> Unit,
|
||||
) {
|
||||
debounceJob?.cancel()
|
||||
debounceJob = coroutineScope.launch(context) {
|
||||
delay(waitMs)
|
||||
if (!forceUpdate) delay(waitMs)
|
||||
destinationFunction.invoke()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.feedback
|
||||
|
||||
import android.content.res.Resources
|
||||
import com.tangem.core.res.getStringSafe
|
||||
import com.tangem.domain.feedback.models.CardInfo
|
||||
import com.tangem.domain.feedback.models.FeedbackEmail
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
|
|
@ -65,7 +66,7 @@ class SendFeedbackEmailUseCase(
|
|||
is FeedbackEmailType.SwapProblem,
|
||||
is FeedbackEmailType.TransactionSendingProblem,
|
||||
-> {
|
||||
append(resources.getString(R.string.feedback_data_collection_message))
|
||||
append(resources.getStringSafe(R.string.feedback_data_collection_message))
|
||||
skipLine()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.feedback.utils
|
||||
|
||||
import android.content.res.Resources
|
||||
import com.tangem.core.res.getStringSafe
|
||||
import com.tangem.domain.feedback.R
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
|
||||
|
|
@ -27,6 +28,6 @@ internal class EmailMessageTitleResolver(private val resources: Resources) {
|
|||
-> R.string.feedback_preface_tx_failed
|
||||
is FeedbackEmailType.PreActivatedWallet -> R.string.feedback_preface_support
|
||||
}
|
||||
.let(resources::getString)
|
||||
.let(resources::getStringSafe)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.feedback.utils
|
||||
|
||||
import android.content.res.Resources
|
||||
import com.tangem.core.res.getStringSafe
|
||||
import com.tangem.domain.feedback.R
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
|
||||
|
|
@ -32,6 +33,6 @@ internal class EmailSubjectResolver(private val resources: Resources) {
|
|||
is FeedbackEmailType.PreActivatedWallet -> R.string.feedback_subject_pre_activated_wallet
|
||||
is FeedbackEmailType.CurrencyDescriptionError -> R.string.feedback_token_description_error
|
||||
}
|
||||
.let(resources::getString)
|
||||
.let(resources::getStringSafe)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package com.tangem.domain.redux
|
||||
|
||||
import com.tangem.domain.redux.global.DomainGlobalState
|
||||
import org.rekotlin.StateType
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class DomainState(val globalState: DomainGlobalState = DomainGlobalState()) : StateType
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package com.tangem.domain.redux
|
||||
|
||||
import com.tangem.domain.redux.global.DomainGlobalHub
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Store
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
private val RE_STORE_HUBS: List<ReStoreHub<DomainState, *>> = listOf(DomainGlobalHub())
|
||||
|
||||
val domainStore = Store(
|
||||
state = DomainState(),
|
||||
middleware = RE_STORE_HUBS.map { it.getMiddleware() },
|
||||
reducer = { action, state -> reduce(action, state) },
|
||||
)
|
||||
|
||||
private fun reduce(action: Action, domainState: DomainState?): DomainState {
|
||||
requireNotNull(domainState)
|
||||
|
||||
// we can examine the store state after each change by reducer
|
||||
var assembleReducedDomainState: DomainState = domainState
|
||||
val reducedStatesByAction = mutableListOf<Pair<Action, DomainState>>()
|
||||
|
||||
RE_STORE_HUBS.forEach {
|
||||
val reducedState = it.reduce(action, assembleReducedDomainState)
|
||||
|
||||
assembleReducedDomainState = if (reducedState != assembleReducedDomainState) {
|
||||
reducedStatesByAction.add(action to assembleReducedDomainState)
|
||||
reducedState
|
||||
} else {
|
||||
assembleReducedDomainState
|
||||
}
|
||||
}
|
||||
|
||||
return assembleReducedDomainState
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
package com.tangem.domain.redux
|
||||
|
||||
import android.webkit.ValueCallback
|
||||
import com.tangem.domain.redux.global.DomainGlobalState
|
||||
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.job
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.DispatchFunction
|
||||
import org.rekotlin.Middleware
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
* ReStoreHub's should not store the <StoreState> or the <State>, because this can lead to destabilization of
|
||||
* a state behavior.
|
||||
* All ReStoreHub's must be marked as internal
|
||||
*/
|
||||
internal interface ReStoreHub<StoreState, State> {
|
||||
fun getMiddleware(): Middleware<StoreState>
|
||||
fun reduce(action: Action, domainState: StoreState): StoreState
|
||||
}
|
||||
|
||||
internal interface ReStoreReducer<State> {
|
||||
fun reduceAction(action: Action, state: State): State
|
||||
}
|
||||
|
||||
/**
|
||||
* ReStoreHub is the entry point for actions. It processes it through middleware and reducer.
|
||||
* Actions handled by ReStoreHub go into coroutine scope, which can be canceled while the action is being processed.
|
||||
* All action went from the middleware must be dispatched through ReStoreHub.dispatchOnMain(Actions) to prevent
|
||||
* concurrent modification in the Store
|
||||
* Only the changed hub State will change its state in the DomainState
|
||||
* Do not implement other states like as DomainGlobalState. Because it can dilute the responsibility of
|
||||
* states.
|
||||
* @param name - name of the Hub
|
||||
* @param dispatcher - main coroutine dispatcher for actions
|
||||
* @property globalState - state witch produce accessibility to global variables
|
||||
*/
|
||||
internal abstract class BaseStoreHub<State>(
|
||||
private val name: String,
|
||||
private val dispatcher: CoroutineDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher(),
|
||||
) : ReStoreHub<DomainState, State> {
|
||||
|
||||
val globalState: DomainGlobalState
|
||||
get() = domainStore.state.globalState
|
||||
|
||||
val hubScope = CoroutineScope(
|
||||
Job() + dispatcher + CoroutineName(name) + FeatureCoroutineExceptionHandler.create(name),
|
||||
)
|
||||
|
||||
private val actionsAndJobs = mutableMapOf<Action, Job>()
|
||||
|
||||
override fun getMiddleware(): Middleware<DomainState> {
|
||||
return { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
handle(state, action, dispatch)
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches new coroutine and stores the action with it's coroutine job. (Coroutine can be cancelled
|
||||
* through invoking the cancelActionJob() function inside a middleware).
|
||||
* Removes the action when job is completed.
|
||||
*/
|
||||
protected open fun handle(storeStateHolder: () -> DomainState?, action: Action, dispatch: DispatchFunction) {
|
||||
val storeState = storeStateHolder()
|
||||
?: throw UnsupportedOperationException("StoreState for the $name can't be NULL")
|
||||
|
||||
hubScope.launch {
|
||||
actionsAndJobs[action] = this.coroutineContext.job
|
||||
actionsAndJobs[action]?.invokeOnCompletion { actionsAndJobs.remove(action) }
|
||||
|
||||
handleAction(action, storeState) {
|
||||
actionsAndJobs.remove(it)?.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the action and check it. If the action hasn't updated the hubState, then it doesn't need to update
|
||||
* storeState
|
||||
*/
|
||||
override fun reduce(action: Action, domainState: DomainState): DomainState {
|
||||
val hubOldState = getHubState(domainState)
|
||||
val hubNewState = getReducer().reduceAction(action, hubOldState)
|
||||
return if (hubOldState === hubNewState) {
|
||||
domainState
|
||||
} else {
|
||||
updateStoreState(domainState, hubNewState)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun cancelAll() {
|
||||
actionsAndJobs.forEach { (_, job) -> job.cancel() }
|
||||
}
|
||||
|
||||
protected abstract suspend fun handleAction(action: Action, storeState: DomainState, cancel: ValueCallback<Action>)
|
||||
protected abstract fun getReducer(): ReStoreReducer<State>
|
||||
|
||||
protected abstract fun getHubState(storeState: DomainState): State
|
||||
protected abstract fun updateStoreState(storeState: DomainState, newHubState: State): DomainState
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.domain.redux.extensions
|
||||
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.domain.redux.domainStore
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal suspend inline fun dispatchOnMain(vararg actions: Action) {
|
||||
withMainContext { actions.forEach { domainStore.dispatch(it) } }
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.domain.redux.global
|
||||
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// TODO: refactoring: is alias for the GlobalAction
|
||||
sealed class DomainGlobalAction : Action {
|
||||
data class SaveScanNoteResponse(val scanResponse: ScanResponse) : DomainGlobalAction()
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
package com.tangem.domain.redux.global
|
||||
|
||||
import android.webkit.ValueCallback
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.datasource.api.common.AuthProvider
|
||||
import com.tangem.datasource.utils.RequestHeader
|
||||
import com.tangem.domain.redux.BaseStoreHub
|
||||
import com.tangem.domain.redux.DomainState
|
||||
import com.tangem.domain.redux.ReStoreReducer
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// TODO: refactoring: is alias for the GlobalMiddleware and the GlobalReducer
|
||||
internal class DomainGlobalHub : BaseStoreHub<DomainGlobalState>("DomainGlobalHub") {
|
||||
|
||||
override fun getHubState(storeState: DomainState): DomainGlobalState {
|
||||
return storeState.globalState
|
||||
}
|
||||
|
||||
override fun updateStoreState(storeState: DomainState, newHubState: DomainGlobalState): DomainState {
|
||||
return storeState.copy(globalState = newHubState)
|
||||
}
|
||||
|
||||
override suspend fun handleAction(action: Action, storeState: DomainState, cancel: ValueCallback<Action>) {
|
||||
if (action !is DomainGlobalAction) return
|
||||
}
|
||||
|
||||
override fun getReducer(): ReStoreReducer<DomainGlobalState> = DomainGlobalReducer()
|
||||
}
|
||||
|
||||
private class DomainGlobalReducer : ReStoreReducer<DomainGlobalState> {
|
||||
|
||||
override fun reduceAction(action: Action, state: DomainGlobalState): DomainGlobalState {
|
||||
return when (action) {
|
||||
is DomainGlobalAction.SaveScanNoteResponse -> {
|
||||
val card = action.scanResponse.card
|
||||
// TODO: AuthHeaders: try to remove it, because now we can use headers with dynamic values
|
||||
state.networkServices.tangemTechService.addAuthenticationHeader(
|
||||
RequestHeader.AuthenticationHeader(
|
||||
object : AuthProvider {
|
||||
override fun getCardPublicKey(): String = card.cardPublicKey.toHexString()
|
||||
override fun getCardId(): String = card.cardId
|
||||
},
|
||||
),
|
||||
)
|
||||
state.copy(scanResponse = action.scanResponse)
|
||||
}
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package com.tangem.domain.redux.global
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechService
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class DomainGlobalState(
|
||||
// there is a part of mirrors from the GlobalState.
|
||||
// It updates on GlobalAction.SaveScanNoteResponse -> DomainGlobalAction.SaveScanNoteResponse(scanResponse)
|
||||
val scanResponse: ScanResponse? = null,
|
||||
//
|
||||
val networkServices: NetworkServices = NetworkServices(),
|
||||
)
|
||||
|
||||
data class NetworkServices(
|
||||
val tangemTechService: TangemTechService = TangemTechService,
|
||||
)
|
||||
|
|
@ -21,9 +21,17 @@ platform :android do
|
|||
FileUtils.cp("../app/src/main/assets/tangem-app-config/android/google-services.json", "../app")
|
||||
end
|
||||
|
||||
desc "Runs all the tests"
|
||||
lane :test do
|
||||
desc "Run detekt"
|
||||
lane :detekt do
|
||||
FileUtils.cp("../ci_resources/tests_8gb_ram_ci_gradle.properties", "../gradle.properties")
|
||||
puts File.read("../gradle.properties")
|
||||
gradle(task: "detekt")
|
||||
end
|
||||
|
||||
desc "Run tests"
|
||||
lane :test do
|
||||
FileUtils.cp("../ci_resources/tests_8gb_ram_ci_gradle.properties", "../gradle.properties")
|
||||
puts File.read("../gradle.properties")
|
||||
gradle(task: "testDebugUnitTest")
|
||||
end
|
||||
|
||||
|
|
@ -79,19 +87,22 @@ platform :android do
|
|||
|
||||
desc "Publish internal and external builds to Firebase App Distribution"
|
||||
lane :publishToFirebase do |options|
|
||||
FileUtils.cp("../ci_resources/build_8gb_ram_ci_gradle.properties", "../gradle.properties")
|
||||
puts File.read("../gradle.properties")
|
||||
gradle(
|
||||
task: "clean assemble",
|
||||
build_type: "Internal",
|
||||
properties: {
|
||||
'versionCode' => ENV['versionCode'],
|
||||
'versionName' => ENV['versionName'],
|
||||
'versionCode' => ENV['version_code'],
|
||||
'versionName' => ENV['version_name'],
|
||||
}
|
||||
)
|
||||
firebase_app_distribution(
|
||||
firebase_cli_token: ENV['firebase_cli_token'],
|
||||
app: ENV['app_id_internal'],
|
||||
apk_path: ENV['apk_path_internal'],
|
||||
groups: ENV['groups'],
|
||||
release_notes: ENV['releaseNotes']
|
||||
release_notes: ENV['release_notes'],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
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.appbar.TangemTopAppBar
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.block.BlockItem
|
||||
import com.tangem.core.ui.components.snackbar.TangemSnackbarHost
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalSnackbarHostState
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
|
@ -86,7 +86,7 @@ private fun Content(
|
|||
item {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
text = stringResource(R.string.details_title),
|
||||
text = stringResourceSafe(R.string.details_title),
|
||||
style = TangemTheme.typography.h1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
|
|
@ -127,7 +127,9 @@ private fun Block(
|
|||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
) {
|
||||
val itemModifier = Modifier.fillMaxWidth().testTag(TestTags.DETAILS_SCREEN_ITEM)
|
||||
val itemModifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(TestTags.DETAILS_SCREEN_ITEM)
|
||||
|
||||
when (model) {
|
||||
is DetailsItemUM.Basic -> {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ 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 com.tangem.core.ui.components.block.BlockCard
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.details.impl.R
|
||||
|
|
@ -37,12 +37,12 @@ internal fun WalletConnectBlock(onClick: () -> Unit, modifier: Modifier = Modifi
|
|||
verticalArrangement = Arrangement.SpaceAround,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.wallet_connect_title),
|
||||
text = stringResourceSafe(id = R.string.wallet_connect_title),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
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.secondary,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.features.details.utils
|
||||
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.Raise
|
||||
import arrow.core.raise.ensureNotNull
|
||||
|
|
@ -13,10 +12,7 @@ import com.tangem.core.decompose.navigation.Router
|
|||
import com.tangem.core.decompose.navigation.popTo
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.components.SimpleOkDialog
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.isNullOrEmpty
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.message.ContentMessage
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.card.ScanCardProcessor
|
||||
|
|
@ -107,7 +103,7 @@ internal class UserWalletSaver @Inject constructor(
|
|||
messageSender.send(
|
||||
message = ContentMessage { onDismiss ->
|
||||
SimpleOkDialog(
|
||||
message = stringResource(id = R.string.user_wallet_list_error_wallet_already_saved),
|
||||
message = stringResourceSafe(id = R.string.user_wallet_list_error_wallet_already_saved),
|
||||
onDismissDialog = onDismiss,
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
|
|
@ -31,6 +30,7 @@ import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
|||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonColors
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
|
@ -144,7 +144,7 @@ private fun BoxScope.DisclaimerButton(onAccept: (Boolean) -> Unit) {
|
|||
rememberPermissionState(permission = permission).status.isGranted
|
||||
} ?: true
|
||||
PrimaryButton(
|
||||
text = stringResource(id = R.string.common_accept),
|
||||
text = stringResourceSafe(id = R.string.common_accept),
|
||||
onClick = { onAccept(!isPermissionGranted) },
|
||||
colors = TangemButtonColors(
|
||||
backgroundColor = TangemColorPalette.Light4,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.features.managetokens.model
|
||||
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
|
|
@ -10,6 +9,7 @@ import com.tangem.core.decompose.ui.UiMessageSender
|
|||
import com.tangem.core.ui.components.SimpleOkDialog
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.message.ContentMessage
|
||||
import com.tangem.domain.card.DerivePublicKeysUseCase
|
||||
import com.tangem.domain.card.HasMissedDerivationsUseCase
|
||||
|
|
@ -231,7 +231,7 @@ internal class CustomTokenFormModel @Inject constructor(
|
|||
private fun showErrorDialog() {
|
||||
val dialog = ContentMessage { onDismiss ->
|
||||
SimpleOkDialog(
|
||||
message = stringResource(R.string.common_unknown_error),
|
||||
message = stringResourceSafe(R.string.common_unknown_error),
|
||||
onDismissDialog = onDismiss,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
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.PreviewParameterProvider
|
||||
|
|
@ -33,6 +32,7 @@ import com.tangem.core.ui.components.keyboardAsState
|
|||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.managetokens.component.preview.PreviewCustomTokenFormComponent
|
||||
|
|
@ -89,7 +89,7 @@ internal fun CustomTokenFormContent(model: CustomTokenFormUM, modifier: Modifier
|
|||
.align(Alignment.BottomCenter)
|
||||
.padding(bottom = TangemTheme.dimens.spacing16 + bottomBarHeight)
|
||||
.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.custom_token_add_token),
|
||||
text = stringResourceSafe(id = R.string.custom_token_add_token),
|
||||
colors = TangemButtonsDefaults.primaryButtonColors,
|
||||
enabled = model.canAddToken,
|
||||
showProgress = model.isValidating,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue