Updated on 2026-08-14
This commit is contained in:
commit
fbb99bcda0
317 changed files with 3800 additions and 2894 deletions
|
|
@ -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,11 +227,11 @@ dependencies {
|
|||
implementation(deps.xmlShimmer)
|
||||
implementation(deps.viewBindingDelegate)
|
||||
implementation(deps.armadillo)
|
||||
implementation(deps.mviCore.watcher)
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(deps.reownCore)
|
||||
implementation(deps.reownWeb3)
|
||||
implementation(deps.prettyLogger)
|
||||
implementation(deps.decompose.ext.compose)
|
||||
|
||||
/** Testing libraries */
|
||||
testImplementation(deps.test.coroutine)
|
||||
|
|
|
|||
|
|
@ -10,11 +10,6 @@
|
|||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit f1aa2f2cbf374ecba5da753d9f51c9df81749ef2
|
||||
Subproject commit 1f1c301f4e0286cf6ab4cd8ee9323c85de3dfaab
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
|
|||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import com.tangem.datasource.local.config.issuers.IssuersConfigStorage
|
||||
|
|
@ -35,7 +36,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 +112,6 @@ interface ApplicationEntryPoint {
|
|||
|
||||
fun getTransactionSignerFactory(): TransactionSignerFactory
|
||||
|
||||
fun getHomeFeatureToggles(): HomeFeatureToggles
|
||||
|
||||
fun getGetUserCountryCodeUseCase(): GetUserCountryUseCase
|
||||
|
||||
fun getOnrampFeatureToggles(): OnrampFeatureToggles
|
||||
|
|
@ -125,4 +123,6 @@ interface ApplicationEntryPoint {
|
|||
fun getCoroutineDispatcherProvider(): CoroutineDispatcherProvider
|
||||
|
||||
fun getExcludedBlockchains(): ExcludedBlockchains
|
||||
|
||||
fun getClipboardManager(): ClipboardManager
|
||||
}
|
||||
|
|
@ -10,12 +10,15 @@ import android.view.MotionEvent
|
|||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
|
|
@ -85,8 +88,9 @@ import com.tangem.tap.features.main.model.Toast
|
|||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphAction
|
||||
import com.tangem.tap.routing.RoutingComponent
|
||||
import com.tangem.tap.routing.component.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 +213,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 +262,13 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
installActivityDependencies()
|
||||
observeAppThemeModeUpdates()
|
||||
|
||||
setContentView(R.layout.activity_main)
|
||||
installRouting()
|
||||
if (routingFeatureToggles.isNavigationRefactoringEnabled) {
|
||||
setRootContent()
|
||||
} else {
|
||||
setContentView(R.layout.activity_main)
|
||||
installRouting()
|
||||
}
|
||||
|
||||
initContent()
|
||||
|
||||
observeStateUpdates()
|
||||
|
|
@ -271,6 +283,16 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
lifecycle.addObserver(WindowObscurationObserver)
|
||||
}
|
||||
|
||||
private fun setRootContent() {
|
||||
val routingComponent = routingComponentFactory.create(
|
||||
context = rootComponentContext,
|
||||
)
|
||||
|
||||
setContent {
|
||||
routingComponent.Content(Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
|
||||
private fun installRouting() {
|
||||
val routingComponent = routingComponentFactory.create(
|
||||
context = rootComponentContext,
|
||||
|
|
@ -297,6 +319,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
is RoutingComponent.Child.LegacyIntent -> {
|
||||
startActivity(child.intent)
|
||||
}
|
||||
is RoutingComponent.Child.ComposableComponent -> error("Unsupported child: $child")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -400,8 +423,11 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
// TODO: RESEARCH! NotificationsHandler is created in onResume and destroyed in onStop
|
||||
notificationsHandler = NotificationsHandler(binding.fragmentContainer)
|
||||
|
||||
if (!routingFeatureToggles.isNavigationRefactoringEnabled) {
|
||||
// TODO: RESEARCH! NotificationsHandler is created in onResume and destroyed in onStop
|
||||
notificationsHandler = NotificationsHandler(binding.fragmentContainer)
|
||||
}
|
||||
|
||||
navigateToInitialScreenIfNeeded(intent)
|
||||
}
|
||||
|
|
@ -481,22 +507,22 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
}
|
||||
}
|
||||
|
||||
override fun showSnackbar(
|
||||
@StringRes text: Int,
|
||||
length: Int,
|
||||
@StringRes buttonTitle: Int?,
|
||||
action: View.OnClickListener?,
|
||||
) {
|
||||
showSnackbar(getString(text), length, buttonTitle?.let(::getString), action)
|
||||
override fun showSnackbar(@StringRes text: Int, length: Int, @StringRes buttonTitle: Int?, action: (() -> Unit)?) {
|
||||
showSnackbar(
|
||||
getString(text),
|
||||
length,
|
||||
buttonTitle?.let(::getString),
|
||||
action = { action?.invoke() },
|
||||
)
|
||||
}
|
||||
|
||||
override fun showSnackbar(
|
||||
text: TextReference,
|
||||
length: Int,
|
||||
buttonTitle: TextReference?,
|
||||
action: View.OnClickListener?,
|
||||
) {
|
||||
showSnackbar(text.resolveReference(resources), length, buttonTitle?.resolveReference(resources), action)
|
||||
override fun showSnackbar(text: TextReference, length: Int, buttonTitle: TextReference?, action: (() -> Unit)?) {
|
||||
showSnackbar(
|
||||
text = text.resolveReference(resources),
|
||||
length = length,
|
||||
buttonTitle = buttonTitle?.resolveReference(resources),
|
||||
action = { action?.invoke() },
|
||||
)
|
||||
}
|
||||
|
||||
override fun dismissSnackbar() {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import com.tangem.core.analytics.models.Basic
|
|||
import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm
|
||||
import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
|
|
@ -57,7 +58,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 +177,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()
|
||||
|
||||
|
|
@ -197,6 +194,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
|
||||
private val excludedBlockchains: ExcludedBlockchains
|
||||
get() = entryPoint.getExcludedBlockchains()
|
||||
|
||||
private val clipboardManager: ClipboardManager
|
||||
get() = entryPoint.getClipboardManager()
|
||||
// endregion
|
||||
|
||||
override fun onCreate() {
|
||||
|
|
@ -272,7 +272,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
shareManager = shareManager,
|
||||
appRouter = appRouter,
|
||||
transactionSignerFactory = transactionSignerFactory,
|
||||
homeFeatureToggles = homeFeatureToggles,
|
||||
getUserCountryUseCase = getUserCountryUseCase,
|
||||
onrampFeatureToggles = onrampFeatureToggles,
|
||||
environmentConfigStorage = environmentConfigStorage,
|
||||
|
|
@ -280,6 +279,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
|||
onboardingRepository = onboardingRepository,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
clipboardManager = clipboardManager,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.common
|
||||
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -11,14 +10,14 @@ interface SnackbarHandler {
|
|||
@StringRes text: Int,
|
||||
length: Int = Snackbar.LENGTH_INDEFINITE,
|
||||
@StringRes buttonTitle: Int? = null,
|
||||
action: View.OnClickListener? = null,
|
||||
action: (() -> Unit)? = null,
|
||||
)
|
||||
|
||||
fun showSnackbar(
|
||||
text: TextReference,
|
||||
length: Int = Snackbar.LENGTH_INDEFINITE,
|
||||
buttonTitle: TextReference? = null,
|
||||
action: View.OnClickListener? = null,
|
||||
action: (() -> Unit)? = null,
|
||||
)
|
||||
|
||||
fun dismissSnackbar()
|
||||
|
|
|
|||
|
|
@ -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,12 +1,52 @@
|
|||
package com.tangem.tap.common.clipboard
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipDescription
|
||||
import android.content.ClipDescription.MIMETYPE_TEXT_PLAIN
|
||||
import android.os.Build
|
||||
import android.os.PersistableBundle
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import timber.log.Timber
|
||||
import android.content.ClipboardManager as AndroidClipboardManager
|
||||
|
||||
internal class DefaultClipboardManager(private val clipboardManager: AndroidClipboardManager) : ClipboardManager {
|
||||
|
||||
override fun setText(label: String, text: String) {
|
||||
clipboardManager.setPrimaryClip(ClipData.newPlainText(label, text))
|
||||
override fun setText(text: String, isSensitive: Boolean, label: String) {
|
||||
val clip = ClipData.newPlainText(label, text).apply {
|
||||
if (isSensitive) description.setAsSensitive()
|
||||
}
|
||||
|
||||
clipboardManager.setPrimaryClip(clip)
|
||||
}
|
||||
|
||||
override fun getText(default: String?): String? {
|
||||
val clip = clipboardManager.primaryClip
|
||||
|
||||
if (clip == null || clip.itemCount == 0) {
|
||||
Timber.d("Clipboard is empty")
|
||||
return default
|
||||
}
|
||||
|
||||
val clipDescription = clipboardManager.primaryClipDescription
|
||||
if (clipDescription?.hasMimeType(MIMETYPE_TEXT_PLAIN) == false) {
|
||||
Timber.d("Clipboard doesn't contain text")
|
||||
return default
|
||||
}
|
||||
|
||||
return clip.getItemAt(0).text?.toString()
|
||||
}
|
||||
|
||||
private fun ClipDescription.setAsSensitive() {
|
||||
extras = PersistableBundle().apply {
|
||||
putBoolean(getExtraIsSensitiveFlag(), true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getExtraIsSensitiveFlag(): String {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
ClipDescription.EXTRA_IS_SENSITIVE
|
||||
} else {
|
||||
"android.content.extra.IS_SENSITIVE"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,10 @@ import com.tangem.core.ui.clipboard.ClipboardManager
|
|||
import timber.log.Timber
|
||||
|
||||
internal object MockClipboardManager : ClipboardManager {
|
||||
override fun setText(label: String, text: String) {
|
||||
/** Intentionnaly do nothing */
|
||||
|
||||
override fun setText(text: String, isSensitive: Boolean, label: String) {
|
||||
Timber.w("Clipboard Manager not available")
|
||||
}
|
||||
|
||||
override fun getText(default: String?): String? = null
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* required since targetAndroid=30
|
||||
* <queries>
|
||||
* <intent>
|
||||
* <action android:name="android.intent.action.SENDTO" />
|
||||
* <data android:scheme="mailto" />
|
||||
* </intent>
|
||||
* </queries>
|
||||
*/
|
||||
@Deprecated("Use EmailSender instead")
|
||||
fun Activity.sendEmail(
|
||||
email: String,
|
||||
subject: String,
|
||||
message: String,
|
||||
file: File? = null,
|
||||
onFail: ((Exception) -> Unit)? = null,
|
||||
) {
|
||||
fun createEmailShareIntent(recipient: String, subject: String, text: String, file: File? = null): Intent {
|
||||
val builder = ShareCompat.IntentBuilder.from(this)
|
||||
.setType("message/rfc822")
|
||||
.setEmailTo(arrayOf(recipient))
|
||||
.setSubject(subject)
|
||||
.setText(text)
|
||||
file?.let { builder.setStream(FileProvider.getUriForFile(this, "$packageName.provider", it)) }
|
||||
return builder.intent
|
||||
}
|
||||
|
||||
val originalIntent = createEmailShareIntent(email, subject, message, file)
|
||||
val emailFilterIntent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:"))
|
||||
|
||||
val originalIntentResults = packageManager.queryIntentActivities(originalIntent, 0)
|
||||
val emailFilterIntentResults = packageManager.queryIntentActivities(emailFilterIntent, 0)
|
||||
|
||||
val targetedIntents = originalIntentResults
|
||||
.filter { originalResult ->
|
||||
emailFilterIntentResults.any {
|
||||
originalResult.activityInfo.packageName == it.activityInfo.packageName
|
||||
}
|
||||
}
|
||||
.map {
|
||||
createEmailShareIntent(email, subject, message, file).apply {
|
||||
setPackage(it.activityInfo.packageName)
|
||||
}
|
||||
}
|
||||
.toMutableList()
|
||||
try {
|
||||
val chooserIntent = Intent.createChooser(targetedIntents.removeAt(0), "Send mail...")
|
||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toTypedArray())
|
||||
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
ContextCompat.startActivity(this, chooserIntent, null)
|
||||
} catch (ex: Exception) {
|
||||
onFail?.invoke(ex)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
fun StringBuilder.breakLine(count: Int = 1): StringBuilder = append("\n".repeat(count))
|
||||
|
|
@ -2,26 +2,19 @@
|
|||
|
||||
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 +33,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,25 +51,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
|
||||
|
||||
val clip: ClipData = ClipData.newPlainText(label, value.toString())
|
||||
clipboard.setPrimaryClip(clip)
|
||||
}
|
||||
|
||||
fun Context.getFromClipboard(default: CharSequence? = null): CharSequence? {
|
||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
|
||||
?: return default
|
||||
val clipData = clipboard.primaryClip ?: return default
|
||||
if (clipData.itemCount == 0) return default
|
||||
|
||||
return clipData.getItemAt(0).text
|
||||
}
|
||||
|
||||
fun View.getString(resId: Int, vararg formatArgs: Any?): String {
|
||||
return context.getString(resId, *formatArgs)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.common.ui
|
|||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.Toast
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.tangem.core.analytics.Analytics
|
||||
|
|
@ -10,6 +11,7 @@ import com.tangem.tap.common.analytics.events.Token
|
|||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.domain.model.WalletAddressData
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding
|
||||
|
|
@ -51,7 +53,12 @@ internal class AddressInfoBottomSheetDialog(
|
|||
tvAddress.text = data.address
|
||||
btnFlCopyAddress.setOnClickListener {
|
||||
Analytics.send(Token.Receive.ButtonCopyAddress())
|
||||
context.copyToClipboard(data.address)
|
||||
|
||||
Toast
|
||||
.makeText(context, R.string.wallet_notification_address_copied, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
|
||||
store.inject(DaggerGraphState::clipboardManager).setText(text = data.address, isSensitive = true)
|
||||
}
|
||||
btnFlShare.setOnClickListener {
|
||||
Analytics.send(Token.Receive.ButtonShareAddress())
|
||||
|
|
|
|||
|
|
@ -68,5 +68,6 @@ internal class AndroidEmailSender : EmailSender {
|
|||
}
|
||||
|
||||
return builder.intent
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.di.routing
|
||||
|
||||
import com.tangem.tap.routing.RoutingComponent
|
||||
import com.tangem.tap.routing.impl.DefaultRoutingComponent
|
||||
import com.tangem.tap.routing.component.RoutingComponent
|
||||
import com.tangem.tap.routing.component.impl.DefaultRoutingComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ class WalletConnectMiddleware {
|
|||
is WalletConnectAction.StartWalletConnect -> {
|
||||
val uri = action.copiedUri
|
||||
if (uri != null && isWalletConnectUri(uri)) {
|
||||
// TODO check
|
||||
store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri))
|
||||
} else {
|
||||
store.dispatchNavigationAction { push(AppRoute.QrScanning(SourceType.WALLET_CONNECT)) }
|
||||
|
|
|
|||
|
|
@ -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 = {},
|
||||
|
|
|
|||
|
|
@ -5,21 +5,19 @@ import androidx.compose.foundation.layout.*
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -30,8 +28,6 @@ internal fun WalletConnectScreen(
|
|||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
SettingsScreensScaffold(
|
||||
modifier = modifier,
|
||||
content = {
|
||||
|
|
@ -46,7 +42,7 @@ internal fun WalletConnectScreen(
|
|||
AddSessionFab(
|
||||
onAddSession = {
|
||||
Analytics.send(Settings.ButtonStartWalletConnectSession())
|
||||
state.onAddSession(context.getFromClipboard()?.toString())
|
||||
state.onAddSession()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -60,7 +56,7 @@ internal fun WalletConnectScreen(
|
|||
private fun AddSessionFab(onAddSession: () -> Unit, modifier: Modifier = Modifier) {
|
||||
FloatingActionButton(
|
||||
onClick = onAddSession,
|
||||
backgroundColor = TangemTheme.colors.button.primary,
|
||||
containerColor = TangemTheme.colors.button.primary,
|
||||
contentColor = TangemTheme.colors.icon.primary2,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
modifier = modifier,
|
||||
|
|
@ -96,7 +92,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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ internal data class WalletConnectScreenState(
|
|||
val sessions: ImmutableList<WcSessionForScreen>,
|
||||
val isLoading: Boolean = false,
|
||||
val onRemoveSession: (String) -> Unit = {},
|
||||
val onAddSession: (String?) -> Unit = {},
|
||||
val onAddSession: () -> Unit = {},
|
||||
)
|
||||
|
||||
data class WcSessionForScreen(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.details.ui.walletconnect
|
|||
|
||||
import androidx.lifecycle.*
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
|
|
@ -17,6 +18,7 @@ import javax.inject.Inject
|
|||
@HiltViewModel
|
||||
internal class WalletConnectViewModel @Inject constructor(
|
||||
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
) : ViewModel(), DefaultLifecycleObserver {
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
|
|
@ -35,7 +37,11 @@ internal class WalletConnectViewModel @Inject constructor(
|
|||
sessions.toImmutableList(),
|
||||
isLoading = state.loading,
|
||||
onRemoveSession = { sessionUri -> onRemoveSession(sessionUri, sessions) },
|
||||
onAddSession = { copiedUri -> store.dispatch(WalletConnectAction.StartWalletConnect(copiedUri)) },
|
||||
onAddSession = {
|
||||
store.dispatch(
|
||||
WalletConnectAction.StartWalletConnect(copiedUri = clipboardManager.getText()),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,23 +39,22 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +70,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)
|
||||
|
|
@ -92,15 +89,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 {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.tap.features.welcome.component
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.tap.features.welcome.model.WelcomeModel
|
||||
import com.tangem.tap.features.welcome.ui.components.WelcomeScreen
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultWelcomeComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted params: WelcomeComponent.Params,
|
||||
) : WelcomeComponent, AppComponentContext by context {
|
||||
|
||||
private val model: WelcomeModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
|
||||
WelcomeScreen(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : WelcomeComponent.Factory {
|
||||
override fun create(context: AppComponentContext, params: WelcomeComponent.Params): DefaultWelcomeComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.tap.features.welcome.component
|
||||
|
||||
import com.tangem.common.routing.entity.SerializableIntent
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
|
||||
interface WelcomeComponent : ComposableContentComponent {
|
||||
|
||||
data class Params(
|
||||
val intent: SerializableIntent?,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, WelcomeComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.tap.features.welcome.component.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.tap.features.welcome.component.WelcomeComponent
|
||||
import com.tangem.tap.features.welcome.ui.WelcomeScreenState
|
||||
import com.tangem.tap.features.welcome.ui.components.WelcomeScreen
|
||||
|
||||
internal class PreviewWelcomeComponent(
|
||||
private val initialState: WelcomeScreenState = WelcomeScreenState(),
|
||||
) : WelcomeComponent {
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
WelcomeScreen(
|
||||
modifier = modifier,
|
||||
state = initialState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.tap.features.welcome.di
|
||||
|
||||
import com.tangem.tap.features.welcome.component.DefaultWelcomeComponent
|
||||
import com.tangem.tap.features.welcome.component.WelcomeComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ComponentModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindWelcomeComponentFactory(factory: DefaultWelcomeComponent.Factory): WelcomeComponent.Factory
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.tap.features.welcome.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.tap.features.welcome.model.WelcomeModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(WelcomeModel::class)
|
||||
fun bindWelcomeModel(model: WelcomeModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
package com.tangem.tap.features.welcome.model
|
||||
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.finisher.AppFinisher
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListError
|
||||
import com.tangem.tap.common.analytics.events.SignIn
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
import com.tangem.tap.features.welcome.component.WelcomeComponent
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeAction
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeState
|
||||
import com.tangem.tap.features.welcome.ui.WelcomeScreenState
|
||||
import com.tangem.tap.features.welcome.ui.model.WarningModel
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.rekotlin.StoreSubscriber
|
||||
import javax.inject.Inject
|
||||
|
||||
// FIXME: Remove redux: [REDACTED_JIRA]
|
||||
internal class WelcomeModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val appFinisher: AppFinisher,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model(), StoreSubscriber<WelcomeState> {
|
||||
|
||||
private val params: WelcomeComponent.Params = paramsContainer.require()
|
||||
private val initialState: WelcomeScreenState = WelcomeScreenState(
|
||||
onPopBack = appFinisher::finish,
|
||||
onUnlockClick = this::unlockWallets,
|
||||
onScanCardClick = this::scanCard,
|
||||
onCloseError = this::closeError,
|
||||
)
|
||||
|
||||
val state: MutableStateFlow<WelcomeScreenState> = MutableStateFlow(initialState)
|
||||
|
||||
init {
|
||||
store.dispatch(WelcomeAction.SetCoroutineScope(modelScope))
|
||||
|
||||
subscribeToStoreChanges()
|
||||
initGlobalState()
|
||||
|
||||
val welcomeAction = if (params.intent != null) {
|
||||
WelcomeAction.ProceedWithIntent(params.intent.toIntent())
|
||||
} else {
|
||||
WelcomeAction.ProceedWithBiometrics()
|
||||
}
|
||||
|
||||
store.dispatch(welcomeAction)
|
||||
}
|
||||
|
||||
private fun unlockWallets() {
|
||||
Analytics.send(SignIn.ButtonBiometricSignIn())
|
||||
store.dispatch(WelcomeAction.ProceedWithBiometrics())
|
||||
}
|
||||
|
||||
private fun scanCard() {
|
||||
Analytics.send(SignIn.ButtonCardSignIn())
|
||||
store.dispatch(WelcomeAction.ProceedWithCard)
|
||||
}
|
||||
|
||||
private fun closeError() {
|
||||
store.dispatch(WelcomeAction.CloseError)
|
||||
}
|
||||
|
||||
override fun newState(state: WelcomeState) {
|
||||
val warning = createWarningIfNeeded(state.error)
|
||||
|
||||
this.state.update { prevState ->
|
||||
prevState.copy(
|
||||
showUnlockWithBiometricsProgress = state.isUnlockWithBiometricsInProgress,
|
||||
showUnlockWithCardProgress = state.isUnlockWithCardInProgress,
|
||||
warning = warning,
|
||||
error = state.error
|
||||
?.takeIf { !it.silent && warning == null }
|
||||
?.let { e ->
|
||||
e.messageResId?.let { TextReference.Res(it) }
|
||||
?: TextReference.Str(e.customMessage)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
store.dispatch(WelcomeAction.ClearCoroutineScope)
|
||||
store.unsubscribe(subscriber = this)
|
||||
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun createWarningIfNeeded(error: TangemError?): WarningModel? {
|
||||
return when (error) {
|
||||
is UserWalletsListError.BiometricsAuthenticationLockout -> WarningModel.BiometricsLockoutWarning(
|
||||
isPermanent = error.isPermanent,
|
||||
onDismiss = this::dismissWarning,
|
||||
)
|
||||
is UserWalletsListError.AllKeysInvalidated,
|
||||
is UserWalletsListError.NoUserWalletSelected,
|
||||
-> WarningModel.KeyInvalidatedWarning(
|
||||
onDismiss = this::dismissWarning,
|
||||
)
|
||||
is UserWalletsListError.BiometricsAuthenticationDisabled -> WarningModel.BiometricsDisabledWarning(
|
||||
onDismiss = this::clearUserWallets,
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun dismissWarning() {
|
||||
state.update { prevState ->
|
||||
prevState.copy(
|
||||
warning = null,
|
||||
)
|
||||
}
|
||||
closeError()
|
||||
}
|
||||
|
||||
private fun clearUserWallets() {
|
||||
store.dispatch(WelcomeAction.ClearUserWallets)
|
||||
}
|
||||
|
||||
private fun subscribeToStoreChanges() {
|
||||
store.subscribe(this) { appState ->
|
||||
appState.skip { old, new -> old.welcomeState == new.welcomeState }
|
||||
.select { it.welcomeState }
|
||||
}
|
||||
}
|
||||
|
||||
private fun initGlobalState() {
|
||||
store.dispatch(GlobalAction.RestoreAppCurrency)
|
||||
store.dispatch(GlobalAction.ExchangeManager.Init)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,15 @@
|
|||
package com.tangem.tap.features.welcome.ui
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
import com.tangem.tap.features.welcome.ui.model.WarningModel
|
||||
|
||||
@Immutable
|
||||
internal data class WelcomeScreenState(
|
||||
val onPopBack: () -> Unit = {},
|
||||
val showUnlockWithBiometricsProgress: Boolean = false,
|
||||
val showUnlockWithCardProgress: Boolean = false,
|
||||
val warning: WarningModel? = null,
|
||||
val error: TextReference? = null,
|
||||
val onUnlockClick: () -> Unit = {},
|
||||
val onScanCardClick: () -> Unit = {},
|
||||
val onCloseError: () -> Unit = {},
|
||||
)
|
||||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
package com.tangem.tap.features.welcome.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.material.SnackbarHost
|
||||
import androidx.compose.material.SnackbarHostState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||
import com.tangem.tap.features.details.ui.cardsettings.resolveReference
|
||||
import com.tangem.tap.features.welcome.component.WelcomeComponent
|
||||
import com.tangem.tap.features.welcome.component.impl.PreviewWelcomeComponent
|
||||
import com.tangem.tap.features.welcome.ui.WelcomeScreenState
|
||||
import com.tangem.tap.features.welcome.ui.model.WarningModel
|
||||
|
||||
@Composable
|
||||
internal fun WelcomeScreen(state: WelcomeScreenState, modifier: Modifier = Modifier) {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val errorMessage by rememberUpdatedState(newValue = state.error?.resolveReference())
|
||||
val warning by rememberUpdatedState(newValue = state.warning)
|
||||
|
||||
BackHandler(onBack = state.onPopBack)
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.systemBarsPadding(),
|
||||
) {
|
||||
WelcomeScreenContent(
|
||||
showUnlockProgress = state.showUnlockWithBiometricsProgress,
|
||||
showScanCardProgress = state.showUnlockWithCardProgress,
|
||||
onUnlockClick = state.onUnlockClick,
|
||||
onScanCardClick = state.onScanCardClick,
|
||||
)
|
||||
|
||||
SnackbarHost(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(vertical = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
hostState = snackbarHostState,
|
||||
)
|
||||
}
|
||||
|
||||
WarningDialog(warning)
|
||||
|
||||
LaunchedEffect(errorMessage, state.onCloseError) {
|
||||
errorMessage?.let {
|
||||
snackbarHostState.showSnackbar(it)
|
||||
state.onCloseError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun Preview_WelcomeScreen(
|
||||
@PreviewParameter(WelcomeComponentPreviewProvider::class) component: WelcomeComponent,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
component.Content(Modifier)
|
||||
}
|
||||
}
|
||||
|
||||
private class WelcomeComponentPreviewProvider : PreviewParameterProvider<WelcomeComponent> {
|
||||
override val values: Sequence<WelcomeComponent>
|
||||
get() = sequenceOf(
|
||||
PreviewWelcomeComponent(),
|
||||
PreviewWelcomeComponent(
|
||||
initialState = WelcomeScreenState(
|
||||
showUnlockWithBiometricsProgress = true,
|
||||
showUnlockWithCardProgress = true,
|
||||
),
|
||||
),
|
||||
PreviewWelcomeComponent(
|
||||
initialState = WelcomeScreenState(
|
||||
error = TextReference.Str(value = "Error"),
|
||||
),
|
||||
),
|
||||
PreviewWelcomeComponent(
|
||||
initialState = WelcomeScreenState(
|
||||
warning = WarningModel.KeyInvalidatedWarning(onDismiss = {}),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -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
|
||||
|
|
@ -24,8 +24,12 @@ internal fun WelcomeScreenContent(
|
|||
showScanCardProgress: Boolean,
|
||||
onUnlockClick: () -> Unit,
|
||||
onScanCardClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
SpacerHMax()
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size96),
|
||||
|
|
@ -38,7 +42,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 +52,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 +65,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 +77,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,
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ internal val Blockchain.mercuryoNetwork: String?
|
|||
Blockchain.PolygonZkEVM -> null
|
||||
Blockchain.PolygonZkEVMTestnet -> null
|
||||
Blockchain.Radiant -> null
|
||||
Blockchain.Fact0rn -> null
|
||||
Blockchain.BaseTestnet -> null
|
||||
Blockchain.Moonriver -> null
|
||||
Blockchain.MoonriverTestnet -> null
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ internal val Blockchain.moonPaySupportedCurrency: MoonPaySupportedCurrency?
|
|||
PolygonZkEVM -> null
|
||||
PolygonZkEVMTestnet -> null
|
||||
Radiant -> null
|
||||
Fact0rn -> null
|
||||
BaseTestnet -> null
|
||||
Moonriver -> null
|
||||
MoonriverTestnet -> null
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import com.tangem.datasource.local.config.issuers.IssuersConfigStorage
|
||||
|
|
@ -35,7 +36,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 +69,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,
|
||||
|
|
@ -77,4 +76,5 @@ data class DaggerGraphState(
|
|||
val onboardingRepository: OnboardingRepository? = null,
|
||||
val excludedBlockchains: ExcludedBlockchains? = null,
|
||||
val appPreferencesStore: AppPreferencesStore? = null,
|
||||
val clipboardManager: ClipboardManager? = null,
|
||||
) : StateType
|
||||
74
app/src/main/java/com/tangem/tap/routing/RootContent.kt
Normal file
74
app/src/main/java/com/tangem/tap/routing/RootContent.kt
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package com.tangem.tap.routing
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.Children
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.components.snackbar.TangemSnackbarHost
|
||||
import com.tangem.core.ui.message.EventMessageEffect
|
||||
import com.tangem.core.ui.res.LocalSnackbarHostState
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||
import com.tangem.tap.routing.component.RoutingComponent
|
||||
|
||||
@Composable
|
||||
internal fun RootContent(
|
||||
stack: ChildStack<AppRoute, RoutingComponent.Child>,
|
||||
uiDependencies: UiDependencies,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
TangemTheme(
|
||||
activity = context as Activity,
|
||||
uiDependencies = uiDependencies,
|
||||
) {
|
||||
val snackbarHostState = LocalSnackbarHostState.current
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
snackbarHost = {
|
||||
TangemSnackbarHost(
|
||||
modifier = Modifier.padding(all = 16.dp),
|
||||
hostState = snackbarHostState,
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsetsZero,
|
||||
content = { paddingValues ->
|
||||
Children(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
stack = stack,
|
||||
) { child ->
|
||||
when (val instance = child.instance) {
|
||||
is RoutingComponent.Child.Initial -> Unit
|
||||
is RoutingComponent.Child.ComposableComponent -> {
|
||||
instance.component.Content(Modifier.fillMaxSize())
|
||||
}
|
||||
is RoutingComponent.Child.LegacyIntent -> {
|
||||
// TODO: Remove and use it's own router: [REDACTED_JIRA]
|
||||
startActivity(context, instance.intent, Bundle.EMPTY)
|
||||
}
|
||||
is RoutingComponent.Child.LegacyFragment,
|
||||
-> error("Unsupported child: $instance")
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
EventMessageEffect(
|
||||
messageHandler = uiDependencies.eventMessageHandler,
|
||||
snackbarHostState = snackbarHostState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,28 @@
|
|||
package com.tangem.tap.routing
|
||||
package com.tangem.tap.routing.component
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.utils.Provider
|
||||
|
||||
internal interface RoutingComponent {
|
||||
internal interface RoutingComponent : ComposableContentComponent {
|
||||
|
||||
// TODO: Remove after full navigation refactoring
|
||||
val router: Router
|
||||
|
||||
// TODO: Remove after full navigation refactoring
|
||||
val stack: Value<ChildStack<AppRoute, Child>>
|
||||
|
||||
@Immutable
|
||||
sealed class Child {
|
||||
|
||||
// TODO: Remove and find initial intent in RoutingComponent: [REDACTED_JIRA]
|
||||
data object Initial : Child()
|
||||
|
||||
data class LegacyFragment(
|
||||
|
|
@ -25,6 +31,10 @@ internal interface RoutingComponent {
|
|||
) : Child()
|
||||
|
||||
data class LegacyIntent(val intent: Intent) : Child()
|
||||
|
||||
data class ComposableComponent(
|
||||
val component: ComposableContentComponent,
|
||||
) : Child()
|
||||
}
|
||||
|
||||
interface Factory {
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
package com.tangem.tap.routing.component.impl
|
||||
|
||||
import androidx.compose.material3.SnackbarDuration
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.decompose.value.observe
|
||||
import com.arkivanov.essenty.backhandler.BackCallback
|
||||
import com.arkivanov.essenty.lifecycle.doOnDestroy
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.navigation.getOrCreateTyped
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.routing.RootContent
|
||||
import com.tangem.tap.routing.component.RoutingComponent
|
||||
import com.tangem.tap.routing.component.RoutingComponent.Child
|
||||
import com.tangem.tap.routing.configurator.AppRouterConfig
|
||||
import com.tangem.tap.routing.toggle.RoutingFeatureToggles
|
||||
import com.tangem.tap.routing.utils.ChildFactory
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultRoutingComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
private val childFactory: ChildFactory,
|
||||
private val appRouterConfig: AppRouterConfig,
|
||||
private val uiDependencies: UiDependencies,
|
||||
routingFeatureToggles: RoutingFeatureToggles,
|
||||
) : RoutingComponent,
|
||||
AppComponentContext by context,
|
||||
SnackbarHandler {
|
||||
|
||||
private val backCallback = BackCallback(priority = Int.MIN_VALUE, onBack = router::pop)
|
||||
|
||||
override val stack: Value<ChildStack<AppRoute, Child>> = childStack(
|
||||
source = navigationProvider.getOrCreateTyped(),
|
||||
serializer = AppRoute.serializer(),
|
||||
initialConfiguration = getInitialRoute(),
|
||||
handleBackButton = false,
|
||||
childFactory = ::child,
|
||||
)
|
||||
|
||||
init {
|
||||
backHandler.register(backCallback)
|
||||
|
||||
lifecycle.doOnDestroy {
|
||||
childFactory.doOnDestroy()
|
||||
}
|
||||
|
||||
if (routingFeatureToggles.isNavigationRefactoringEnabled) {
|
||||
appRouterConfig.routerScope = componentScope
|
||||
appRouterConfig.componentRouter = router
|
||||
appRouterConfig.snackbarHandler = this
|
||||
|
||||
stack.observe(lifecycle) { stack ->
|
||||
val stackItems = stack.items.map { it.configuration }
|
||||
|
||||
if (appRouterConfig.stack != stackItems) {
|
||||
appRouterConfig.stack = stackItems
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val stack by this.stack.subscribeAsState()
|
||||
|
||||
RootContent(
|
||||
modifier = modifier,
|
||||
stack = stack,
|
||||
uiDependencies = uiDependencies,
|
||||
)
|
||||
}
|
||||
|
||||
override fun showSnackbar(text: Int, length: Int, buttonTitle: Int?, action: (() -> Unit)?) {
|
||||
showSnackbar(
|
||||
text = resourceReference(text),
|
||||
length = length,
|
||||
buttonTitle = buttonTitle?.let(::resourceReference),
|
||||
action = action,
|
||||
)
|
||||
}
|
||||
|
||||
override fun showSnackbar(text: TextReference, length: Int, buttonTitle: TextReference?, action: (() -> Unit)?) {
|
||||
val message = SnackbarMessage(
|
||||
message = text,
|
||||
duration = when (length) {
|
||||
Snackbar.LENGTH_SHORT -> SnackbarDuration.Short
|
||||
Snackbar.LENGTH_LONG -> SnackbarDuration.Long
|
||||
Snackbar.LENGTH_INDEFINITE -> SnackbarDuration.Indefinite
|
||||
else -> SnackbarDuration.Short
|
||||
},
|
||||
actionLabel = buttonTitle,
|
||||
action = action,
|
||||
)
|
||||
|
||||
messageSender.send(message)
|
||||
}
|
||||
|
||||
override fun dismissSnackbar() {
|
||||
messageSender.send(SnackbarMessage(message = TextReference.EMPTY))
|
||||
}
|
||||
|
||||
// TODO: Find correct initial route here: [REDACTED_JIRA]
|
||||
private fun getInitialRoute(): AppRoute = AppRoute.Initial
|
||||
|
||||
private fun child(route: AppRoute, context: ComponentContext): Child {
|
||||
return childFactory.createChild(route) { childByContext(context) }
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : RoutingComponent.Factory {
|
||||
override fun create(context: AppComponentContext): DefaultRoutingComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
package com.tangem.tap.routing.impl
|
||||
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.essenty.backhandler.BackCallback
|
||||
import com.arkivanov.essenty.lifecycle.doOnDestroy
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.navigation.getOrCreateTyped
|
||||
import com.tangem.tap.routing.RoutingComponent
|
||||
import com.tangem.tap.routing.RoutingComponent.Child
|
||||
import com.tangem.tap.routing.utils.ChildFactory
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultRoutingComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
private val childFactory: ChildFactory,
|
||||
) : RoutingComponent, AppComponentContext by context {
|
||||
|
||||
private val backCallback = BackCallback(priority = Int.MIN_VALUE, onBack = router::pop)
|
||||
|
||||
override val stack: Value<ChildStack<AppRoute, Child>> = childStack(
|
||||
source = navigationProvider.getOrCreateTyped(),
|
||||
serializer = AppRoute.serializer(), // TODO Maybe set this to null for AppRoute.Onboarding case. Need to check
|
||||
initialConfiguration = getInitialRoute(),
|
||||
handleBackButton = false,
|
||||
childFactory = ::child,
|
||||
)
|
||||
|
||||
init {
|
||||
backHandler.register(backCallback)
|
||||
|
||||
lifecycle.doOnDestroy {
|
||||
childFactory.doOnDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInitialRoute(): AppRoute = AppRoute.Initial
|
||||
|
||||
private fun child(route: AppRoute, context: ComponentContext): Child {
|
||||
return childFactory.createChild(route) { childByContext(context) }
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : RoutingComponent.Factory {
|
||||
override fun create(context: AppComponentContext): DefaultRoutingComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -33,15 +33,17 @@ import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCar
|
|||
import com.tangem.tap.features.onboarding.products.twins.ui.OnboardingTwinsFragment
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.OnboardingWalletFragment
|
||||
import com.tangem.tap.features.saveWallet.ui.SaveWalletBottomSheetFragment
|
||||
import com.tangem.tap.features.welcome.component.WelcomeComponent
|
||||
import com.tangem.tap.features.welcome.ui.WelcomeFragment
|
||||
import com.tangem.tap.routing.RoutingComponent.Child
|
||||
import com.tangem.tap.routing.component.RoutingComponent.Child
|
||||
import com.tangem.tap.routing.toggle.RoutingFeatureToggles
|
||||
import com.tangem.utils.Provider
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import java.util.WeakHashMap
|
||||
import javax.inject.Inject
|
||||
|
||||
@ActivityScoped
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
internal class ChildFactory @Inject constructor(
|
||||
private val detailsComponentFactory: DetailsComponent.Factory,
|
||||
private val walletSettingsComponentFactory: WalletSettingsComponent.Factory,
|
||||
|
|
@ -54,6 +56,7 @@ internal class ChildFactory @Inject constructor(
|
|||
private val sellCryptoComponentFactory: SellCryptoComponent.Factory,
|
||||
private val swapSelectTokensComponentFactory: SwapSelectTokensComponent.Factory,
|
||||
private val onboardingEntryComponentFactory: OnboardingEntryComponent.Factory,
|
||||
private val welcomeComponentFactory: WelcomeComponent.Factory,
|
||||
private val sendRouter: SendRouter,
|
||||
private val tokenDetailsRouter: TokenDetailsRouter,
|
||||
private val walletRouter: WalletRouter,
|
||||
|
|
@ -61,12 +64,174 @@ 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) {
|
||||
createChildNew(route, contextFactory)
|
||||
} else {
|
||||
createChildLegacy(route, contextFactory)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
private fun createChildNew(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
|
||||
componentContexts[route] = contextFactory(route)
|
||||
|
||||
// region Child creation
|
||||
return when (route) {
|
||||
is AppRoute.Initial -> {
|
||||
Child.Initial
|
||||
}
|
||||
is AppRoute.Details -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = DetailsComponent.Params(route.userWalletId),
|
||||
componentFactory = detailsComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.Disclaimer -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = DisclaimerComponent.Params(route.isTosAccepted),
|
||||
componentFactory = disclaimerComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.ManageTokens -> {
|
||||
val source = when (route.source) {
|
||||
AppRoute.ManageTokens.Source.SETTINGS -> ManageTokensSource.SETTINGS
|
||||
AppRoute.ManageTokens.Source.ONBOARDING -> ManageTokensSource.ONBOARDING
|
||||
AppRoute.ManageTokens.Source.STORIES -> ManageTokensSource.STORIES
|
||||
}
|
||||
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = ManageTokensComponent.Params(route.userWalletId, source),
|
||||
componentFactory = manageTokensComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.Welcome -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = WelcomeComponent.Params(
|
||||
intent = route.intent,
|
||||
),
|
||||
componentFactory = welcomeComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.TesterMenu -> {
|
||||
Child.LegacyIntent(testerRouter.getEntryIntent())
|
||||
}
|
||||
is AppRoute.WalletSettings -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = WalletSettingsComponent.Params(route.userWalletId),
|
||||
componentFactory = walletSettingsComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.MarketsTokenDetails -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = MarketsTokenDetailsComponent.Params(
|
||||
token = route.token,
|
||||
appCurrency = route.appCurrency,
|
||||
showPortfolio = route.showPortfolio,
|
||||
analyticsParams = route.analyticsParams?.let {
|
||||
MarketsTokenDetailsComponent.AnalyticsParams(
|
||||
blockchain = it.blockchain,
|
||||
source = it.source,
|
||||
)
|
||||
},
|
||||
),
|
||||
componentFactory = marketsTokenDetailsComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.Onramp -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = OnrampComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
cryptoCurrency = route.currency,
|
||||
source = route.source,
|
||||
),
|
||||
componentFactory = onrampComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.OnrampSuccess -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = OnrampSuccessComponent.Params(route.externalTxId),
|
||||
componentFactory = onrampSuccessComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.BuyCrypto -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = BuyCryptoComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = buyCryptoComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.SellCrypto -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = SellCryptoComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = sellCryptoComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.SwapCrypto -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = SwapSelectTokensComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = swapSelectTokensComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.Onboarding -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = OnboardingEntryComponent.Params(
|
||||
scanResponse = route.scanResponse,
|
||||
startBackupFlow = route.startFromBackup,
|
||||
multiWalletMode = when (route.mode) {
|
||||
AppRoute.Onboarding.Mode.Onboarding -> OnboardingEntryComponent.MultiWalletMode.Onboarding
|
||||
AppRoute.Onboarding.Mode.AddBackup -> OnboardingEntryComponent.MultiWalletMode.AddBackup
|
||||
},
|
||||
),
|
||||
componentFactory = onboardingEntryComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.AccessCodeRecovery,
|
||||
is AppRoute.AppCurrencySelector,
|
||||
is AppRoute.ModalNotification,
|
||||
is AppRoute.SaveWallet,
|
||||
is AppRoute.Send,
|
||||
is AppRoute.AppSettings,
|
||||
is AppRoute.CardSettings,
|
||||
is AppRoute.DetailsSecurity,
|
||||
is AppRoute.Home,
|
||||
is AppRoute.OnboardingNote,
|
||||
is AppRoute.OnboardingOther,
|
||||
is AppRoute.OnboardingTwins,
|
||||
is AppRoute.OnboardingWallet,
|
||||
is AppRoute.QrScanning,
|
||||
is AppRoute.ReferralProgram,
|
||||
is AppRoute.ResetToFactory,
|
||||
is AppRoute.Swap,
|
||||
is AppRoute.Wallet,
|
||||
is AppRoute.WalletConnectSessions,
|
||||
is AppRoute.CurrencyDetails,
|
||||
is AppRoute.Staking,
|
||||
is AppRoute.PushNotification,
|
||||
-> error("Unsupported route: $route")
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
private fun createChildLegacy(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
|
||||
componentContexts[route] = contextFactory(route)
|
||||
|
||||
// region Child creation
|
||||
return when (route) {
|
||||
is AppRoute.Initial -> {
|
||||
Child.Initial
|
||||
|
|
@ -248,6 +413,7 @@ internal class ChildFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
||||
fun doOnDestroy() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.core.decompose.context.AppComponentContext
|
|||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.tap.DecomposeFragment
|
||||
import com.tangem.tap.routing.RoutingComponent.Child
|
||||
import com.tangem.tap.routing.component.RoutingComponent.Child
|
||||
import com.tangem.utils.Provider
|
||||
|
||||
internal fun AppRoute.asFragmentChild(fragmentProvider: Provider<Fragment>): Child {
|
||||
|
|
@ -37,4 +37,14 @@ internal fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<P, C
|
|||
}
|
||||
|
||||
return Child.LegacyFragment(path, fragmentProvider)
|
||||
}
|
||||
|
||||
internal fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<P, C>> createComponentChild(
|
||||
contextProvider: Provider<AppComponentContext>,
|
||||
params: P,
|
||||
componentFactory: F,
|
||||
): Child {
|
||||
val context = contextProvider()
|
||||
|
||||
return Child.ComposableComponent(componentFactory.create(context, params))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue