Updated on 2026-08-14
This commit is contained in:
commit
8e42589a00
456 changed files with 6580 additions and 3738 deletions
44
Dockerfile
Normal file
44
Dockerfile
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
ENV ANDROID_HOME=/opt/android-sdk
|
||||||
|
ENV BUNDLE_PATH=vendor/bundle
|
||||||
|
ENV PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/34.0.0:$BUNDLE_PATH/bin
|
||||||
|
ENV LC_ALL=en_US.UTF-8
|
||||||
|
ENV LANG=en_US.UTF-8
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
openjdk-17-jdk \
|
||||||
|
wget \
|
||||||
|
unzip \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
ruby \
|
||||||
|
ruby-dev \
|
||||||
|
build-essential \
|
||||||
|
&& apt-get clean
|
||||||
|
|
||||||
|
RUN mkdir -p $ANDROID_HOME/cmdline-tools/latest && \
|
||||||
|
wget https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -O /tmp/cmdline-tools.zip && \
|
||||||
|
unzip /tmp/cmdline-tools.zip -d $ANDROID_HOME/cmdline-tools/latest && \
|
||||||
|
mv $ANDROID_HOME/cmdline-tools/latest/cmdline-tools/* $ANDROID_HOME/cmdline-tools/latest/ && \
|
||||||
|
rm -rf $ANDROID_HOME/cmdline-tools/latest/cmdline-tools && \
|
||||||
|
rm -f /tmp/cmdline-tools.zip && \
|
||||||
|
yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses && \
|
||||||
|
$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "platform-tools" "platforms;android-31" "platforms;android-34" "build-tools;34.0.0"
|
||||||
|
|
||||||
|
RUN wget https://github.com/lzhiyong/android-sdk-tools/releases/download/34.0.3/android-sdk-tools-static-aarch64.zip -O /tmp/android-sdk-tools-static-aarch64.zip && \
|
||||||
|
unzip /tmp/android-sdk-tools-static-aarch64.zip -d /tmp/android-sdk-tools-static-arm && \
|
||||||
|
cp -r /tmp/android-sdk-tools-static-arm/build-tools/* $ANDROID_HOME/build-tools/34.0.0/ && \
|
||||||
|
rm -rf /tmp/android-sdk-tools-static-arm /tmp/android-sdk-tools-static-aarch64.zip
|
||||||
|
|
||||||
|
RUN gem install bundler:2.5.23
|
||||||
|
RUN gem install fastlane -v 2.211.0 -N -V
|
||||||
|
RUN gem install fastlane-plugin-firebase_app_distribution -v 0.9.1 -N -V
|
||||||
|
|
||||||
|
COPY Gemfile Gemfile.lock ./
|
||||||
|
RUN bundle install --jobs=4 --retry=3 --verbose
|
||||||
|
|
||||||
|
RUN bundle exec fastlane -v
|
||||||
|
|
||||||
|
CMD ["bash"]
|
||||||
|
|
@ -75,6 +75,7 @@ dependencies {
|
||||||
implementation(projects.domain.markets)
|
implementation(projects.domain.markets)
|
||||||
implementation(projects.domain.manageTokens)
|
implementation(projects.domain.manageTokens)
|
||||||
implementation(projects.domain.onramp)
|
implementation(projects.domain.onramp)
|
||||||
|
implementation(projects.domain.promo)
|
||||||
|
|
||||||
implementation(projects.common)
|
implementation(projects.common)
|
||||||
implementation(projects.common.routing)
|
implementation(projects.common.routing)
|
||||||
|
|
@ -166,6 +167,7 @@ dependencies {
|
||||||
implementation(deps.androidx.browser)
|
implementation(deps.androidx.browser)
|
||||||
implementation(deps.androidx.paging.runtime)
|
implementation(deps.androidx.paging.runtime)
|
||||||
implementation(deps.androidx.swipeRefreshLayout)
|
implementation(deps.androidx.swipeRefreshLayout)
|
||||||
|
implementation(deps.androidx.fragment.compose)
|
||||||
implementation(deps.lifecycle.runtime.ktx)
|
implementation(deps.lifecycle.runtime.ktx)
|
||||||
implementation(deps.lifecycle.common.java8)
|
implementation(deps.lifecycle.common.java8)
|
||||||
implementation(deps.lifecycle.viewModel.ktx)
|
implementation(deps.lifecycle.viewModel.ktx)
|
||||||
|
|
@ -226,11 +228,11 @@ dependencies {
|
||||||
implementation(deps.xmlShimmer)
|
implementation(deps.xmlShimmer)
|
||||||
implementation(deps.viewBindingDelegate)
|
implementation(deps.viewBindingDelegate)
|
||||||
implementation(deps.armadillo)
|
implementation(deps.armadillo)
|
||||||
implementation(deps.mviCore.watcher)
|
|
||||||
implementation(deps.kotlin.serialization)
|
implementation(deps.kotlin.serialization)
|
||||||
implementation(deps.reownCore)
|
implementation(deps.reownCore)
|
||||||
implementation(deps.reownWeb3)
|
implementation(deps.reownWeb3)
|
||||||
implementation(deps.prettyLogger)
|
implementation(deps.prettyLogger)
|
||||||
|
implementation(deps.decompose.ext.compose)
|
||||||
|
|
||||||
/** Testing libraries */
|
/** Testing libraries */
|
||||||
testImplementation(deps.test.coroutine)
|
testImplementation(deps.test.coroutine)
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,6 @@
|
||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<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-permission android:name="com.google.android.gms.permission.AD_ID" />
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" />
|
<uses-feature android:name="android.hardware.camera" />
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit f1aa2f2cbf374ecba5da753d9f51c9df81749ef2
|
Subproject commit 1f1c301f4e0286cf6ab4cd8ee9323c85de3dfaab
|
||||||
|
|
@ -759,6 +759,16 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "vanar-chain",
|
||||||
|
"name": "Vanar Chain",
|
||||||
|
"symbol": "VANRY",
|
||||||
|
"networks": [
|
||||||
|
{
|
||||||
|
"networkId": "vanar-chain/test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "casper-network",
|
"id": "casper-network",
|
||||||
"name": "Casper",
|
"name": "Casper",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
|
||||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||||
import com.tangem.core.navigation.share.ShareManager
|
import com.tangem.core.navigation.share.ShareManager
|
||||||
import com.tangem.core.navigation.url.UrlOpener
|
import com.tangem.core.navigation.url.UrlOpener
|
||||||
|
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||||
import com.tangem.datasource.local.config.issuers.IssuersConfigStorage
|
import com.tangem.datasource.local.config.issuers.IssuersConfigStorage
|
||||||
|
|
@ -36,7 +37,6 @@ import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
|
||||||
import com.tangem.features.onramp.OnrampFeatureToggles
|
import com.tangem.features.onramp.OnrampFeatureToggles
|
||||||
import com.tangem.tap.common.log.TangemAppLoggerInitializer
|
import com.tangem.tap.common.log.TangemAppLoggerInitializer
|
||||||
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
|
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
|
||||||
import com.tangem.tap.features.home.featuretoggles.HomeFeatureToggles
|
|
||||||
import com.tangem.tap.proxy.AppStateHolder
|
import com.tangem.tap.proxy.AppStateHolder
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.hilt.EntryPoint
|
import dagger.hilt.EntryPoint
|
||||||
|
|
@ -113,8 +113,6 @@ interface ApplicationEntryPoint {
|
||||||
|
|
||||||
fun getTransactionSignerFactory(): TransactionSignerFactory
|
fun getTransactionSignerFactory(): TransactionSignerFactory
|
||||||
|
|
||||||
fun getHomeFeatureToggles(): HomeFeatureToggles
|
|
||||||
|
|
||||||
fun getGetUserCountryCodeUseCase(): GetUserCountryUseCase
|
fun getGetUserCountryCodeUseCase(): GetUserCountryUseCase
|
||||||
|
|
||||||
fun getOnrampFeatureToggles(): OnrampFeatureToggles
|
fun getOnrampFeatureToggles(): OnrampFeatureToggles
|
||||||
|
|
@ -128,4 +126,6 @@ interface ApplicationEntryPoint {
|
||||||
fun getExcludedBlockchains(): ExcludedBlockchains
|
fun getExcludedBlockchains(): ExcludedBlockchains
|
||||||
|
|
||||||
fun getAppLogsStore(): AppLogsStore
|
fun getAppLogsStore(): AppLogsStore
|
||||||
|
|
||||||
|
fun getClipboardManager(): ClipboardManager
|
||||||
}
|
}
|
||||||
|
|
@ -8,7 +8,6 @@ import com.tangem.core.decompose.context.AppComponentContext
|
||||||
import com.tangem.core.decompose.factory.ComponentFactory
|
import com.tangem.core.decompose.factory.ComponentFactory
|
||||||
import com.tangem.core.ui.UiDependencies
|
import com.tangem.core.ui.UiDependencies
|
||||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||||
import com.tangem.core.ui.message.EventMessageEffect
|
|
||||||
import com.tangem.core.ui.screen.ComposeFragment
|
import com.tangem.core.ui.screen.ComposeFragment
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
@ -33,11 +32,6 @@ internal class DecomposeFragment : ComposeFragment() {
|
||||||
@Composable
|
@Composable
|
||||||
override fun ScreenContent(modifier: Modifier) {
|
override fun ScreenContent(modifier: Modifier) {
|
||||||
component.Content(modifier)
|
component.Content(modifier)
|
||||||
|
|
||||||
EventMessageEffect(
|
|
||||||
messageHandler = uiDependencies.eventMessageHandler,
|
|
||||||
snackbarHostState = uiDependencies.globalSnackbarHostState,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ComponentBuilder<C : ComposableContentComponent, P : Any, F : ComponentFactory<P, C>>(
|
private class ComponentBuilder<C : ComposableContentComponent, P : Any, F : ComponentFactory<P, C>>(
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,20 @@ import android.content.res.Configuration
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.activity.SystemBarStyle
|
import androidx.activity.SystemBarStyle
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
|
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.Color
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
|
@ -36,10 +39,14 @@ import com.tangem.core.decompose.context.AppComponentContext
|
||||||
import com.tangem.core.decompose.di.RootAppComponentContext
|
import com.tangem.core.decompose.di.RootAppComponentContext
|
||||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||||
import com.tangem.core.navigation.email.EmailSender
|
import com.tangem.core.navigation.email.EmailSender
|
||||||
|
import com.tangem.core.ui.UiDependencies
|
||||||
import com.tangem.core.ui.event.StateEvent
|
import com.tangem.core.ui.event.StateEvent
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.core.ui.extensions.resolveReference
|
import com.tangem.core.ui.extensions.resolveReference
|
||||||
|
import com.tangem.core.ui.message.EventMessageEffect
|
||||||
|
import com.tangem.core.ui.message.SnackbarMessage
|
||||||
import com.tangem.core.ui.res.TangemColorPalette
|
import com.tangem.core.ui.res.TangemColorPalette
|
||||||
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.data.card.sdk.CardSdkOwner
|
import com.tangem.data.card.sdk.CardSdkOwner
|
||||||
import com.tangem.domain.apptheme.model.AppThemeMode
|
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||||
import com.tangem.domain.card.ScanCardUseCase
|
import com.tangem.domain.card.ScanCardUseCase
|
||||||
|
|
@ -85,8 +92,9 @@ import com.tangem.tap.features.main.model.Toast
|
||||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
||||||
import com.tangem.tap.proxy.AppStateHolder
|
import com.tangem.tap.proxy.AppStateHolder
|
||||||
import com.tangem.tap.proxy.redux.DaggerGraphAction
|
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.configurator.AppRouterConfig
|
||||||
|
import com.tangem.tap.routing.toggle.RoutingFeatureToggles
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
|
|
@ -209,6 +217,12 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var dispatchers: CoroutineDispatcherProvider
|
lateinit var dispatchers: CoroutineDispatcherProvider
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
internal lateinit var routingFeatureToggles: RoutingFeatureToggles
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
internal lateinit var uiDependencies: UiDependencies
|
||||||
|
|
||||||
internal val viewModel: MainViewModel by viewModels()
|
internal val viewModel: MainViewModel by viewModels()
|
||||||
|
|
||||||
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode>
|
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode>
|
||||||
|
|
@ -255,8 +269,14 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
||||||
installActivityDependencies()
|
installActivityDependencies()
|
||||||
observeAppThemeModeUpdates()
|
observeAppThemeModeUpdates()
|
||||||
|
|
||||||
setContentView(R.layout.activity_main)
|
if (routingFeatureToggles.isNavigationRefactoringEnabled) {
|
||||||
installRouting()
|
setRootContent()
|
||||||
|
} else {
|
||||||
|
setContentView(R.layout.activity_main)
|
||||||
|
installRouting()
|
||||||
|
installEventMessageEffect()
|
||||||
|
}
|
||||||
|
|
||||||
initContent()
|
initContent()
|
||||||
|
|
||||||
observeStateUpdates()
|
observeStateUpdates()
|
||||||
|
|
@ -271,6 +291,43 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
||||||
lifecycle.addObserver(WindowObscurationObserver)
|
lifecycle.addObserver(WindowObscurationObserver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun installEventMessageEffect() {
|
||||||
|
binding.composeView.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||||
|
binding.composeView.setContent {
|
||||||
|
TangemTheme(
|
||||||
|
activity = this,
|
||||||
|
uiDependencies = uiDependencies,
|
||||||
|
overrideSystemBarColors = false,
|
||||||
|
) {
|
||||||
|
EventMessageEffect(
|
||||||
|
onShowSnackbar = { message, _ ->
|
||||||
|
showSnackbar(
|
||||||
|
text = message.message.resolveReference(resources),
|
||||||
|
length = when (message.duration) {
|
||||||
|
SnackbarMessage.Duration.Short -> Snackbar.LENGTH_SHORT
|
||||||
|
SnackbarMessage.Duration.Long -> Snackbar.LENGTH_LONG
|
||||||
|
SnackbarMessage.Duration.Indefinite -> Snackbar.LENGTH_INDEFINITE
|
||||||
|
},
|
||||||
|
buttonTitle = message.actionLabel?.resolveReference(resources),
|
||||||
|
action = message.action,
|
||||||
|
onDismiss = message.onDismissRequest,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setRootContent() {
|
||||||
|
val routingComponent = routingComponentFactory.create(
|
||||||
|
context = rootComponentContext,
|
||||||
|
)
|
||||||
|
|
||||||
|
setContent {
|
||||||
|
routingComponent.Content(Modifier.fillMaxSize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun installRouting() {
|
private fun installRouting() {
|
||||||
val routingComponent = routingComponentFactory.create(
|
val routingComponent = routingComponentFactory.create(
|
||||||
context = rootComponentContext,
|
context = rootComponentContext,
|
||||||
|
|
@ -297,6 +354,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
||||||
is RoutingComponent.Child.LegacyIntent -> {
|
is RoutingComponent.Child.LegacyIntent -> {
|
||||||
startActivity(child.intent)
|
startActivity(child.intent)
|
||||||
}
|
}
|
||||||
|
is RoutingComponent.Child.ComposableComponent -> error("Unsupported child: $child")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -400,8 +458,11 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.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)
|
navigateToInitialScreenIfNeeded(intent)
|
||||||
}
|
}
|
||||||
|
|
@ -481,22 +542,22 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showSnackbar(
|
override fun showSnackbar(@StringRes text: Int, length: Int, @StringRes buttonTitle: Int?, action: (() -> Unit)?) {
|
||||||
@StringRes text: Int,
|
showSnackbar(
|
||||||
length: Int,
|
getString(text),
|
||||||
@StringRes buttonTitle: Int?,
|
length,
|
||||||
action: View.OnClickListener?,
|
buttonTitle?.let(::getString),
|
||||||
) {
|
action = { action?.invoke() },
|
||||||
showSnackbar(getString(text), length, buttonTitle?.let(::getString), action)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showSnackbar(
|
override fun showSnackbar(text: TextReference, length: Int, buttonTitle: TextReference?, action: (() -> Unit)?) {
|
||||||
text: TextReference,
|
showSnackbar(
|
||||||
length: Int,
|
text = text.resolveReference(resources),
|
||||||
buttonTitle: TextReference?,
|
length = length,
|
||||||
action: View.OnClickListener?,
|
buttonTitle = buttonTitle?.resolveReference(resources),
|
||||||
) {
|
action = { action?.invoke() },
|
||||||
showSnackbar(text.resolveReference(resources), length, buttonTitle?.resolveReference(resources), action)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dismissSnackbar() {
|
override fun dismissSnackbar() {
|
||||||
|
|
@ -531,7 +592,13 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
||||||
return if (result) super.dispatchTouchEvent(event) else false
|
return if (result) super.dispatchTouchEvent(event) else false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showSnackbar(text: String, length: Int, buttonTitle: String?, action: View.OnClickListener?) {
|
private fun showSnackbar(
|
||||||
|
text: String,
|
||||||
|
length: Int,
|
||||||
|
buttonTitle: String?,
|
||||||
|
action: (() -> Unit)? = null,
|
||||||
|
onDismiss: () -> Unit = {},
|
||||||
|
) {
|
||||||
if (snackbar != null) return
|
if (snackbar != null) return
|
||||||
|
|
||||||
snackbar = Snackbar.make(binding.fragmentContainer, text, length).apply {
|
snackbar = Snackbar.make(binding.fragmentContainer, text, length).apply {
|
||||||
|
|
@ -542,12 +609,13 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
||||||
setTextColor(textColor)
|
setTextColor(textColor)
|
||||||
|
|
||||||
if (buttonTitle != null && action != null) {
|
if (buttonTitle != null && action != null) {
|
||||||
setAction(buttonTitle, action)
|
setAction(buttonTitle, { action() })
|
||||||
}
|
}
|
||||||
|
|
||||||
addCallback(
|
addCallback(
|
||||||
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||||
|
onDismiss()
|
||||||
snackbar = null
|
snackbar = null
|
||||||
removeCallback(this)
|
removeCallback(this)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import com.tangem.core.analytics.models.Basic
|
||||||
import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm
|
import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm
|
||||||
import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
|
import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
|
||||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
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.MoshiConverter
|
||||||
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
|
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
|
||||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||||
|
|
@ -58,7 +59,6 @@ import com.tangem.tap.common.redux.AppState
|
||||||
import com.tangem.tap.common.redux.appReducer
|
import com.tangem.tap.common.redux.appReducer
|
||||||
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
|
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
|
||||||
import com.tangem.tap.domain.tasks.product.DerivationsFinder
|
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.AppStateHolder
|
||||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
|
@ -179,9 +179,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
||||||
private val transactionSignerFactory: TransactionSignerFactory
|
private val transactionSignerFactory: TransactionSignerFactory
|
||||||
get() = entryPoint.getTransactionSignerFactory()
|
get() = entryPoint.getTransactionSignerFactory()
|
||||||
|
|
||||||
private val homeFeatureToggles: HomeFeatureToggles
|
|
||||||
get() = entryPoint.getHomeFeatureToggles()
|
|
||||||
|
|
||||||
private val getUserCountryUseCase: GetUserCountryUseCase
|
private val getUserCountryUseCase: GetUserCountryUseCase
|
||||||
get() = entryPoint.getGetUserCountryCodeUseCase()
|
get() = entryPoint.getGetUserCountryCodeUseCase()
|
||||||
|
|
||||||
|
|
@ -202,6 +199,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
||||||
|
|
||||||
private val appLogsStore: AppLogsStore
|
private val appLogsStore: AppLogsStore
|
||||||
get() = entryPoint.getAppLogsStore()
|
get() = entryPoint.getAppLogsStore()
|
||||||
|
|
||||||
|
private val clipboardManager: ClipboardManager
|
||||||
|
get() = entryPoint.getClipboardManager()
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
|
@ -292,7 +292,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
||||||
shareManager = shareManager,
|
shareManager = shareManager,
|
||||||
appRouter = appRouter,
|
appRouter = appRouter,
|
||||||
transactionSignerFactory = transactionSignerFactory,
|
transactionSignerFactory = transactionSignerFactory,
|
||||||
homeFeatureToggles = homeFeatureToggles,
|
|
||||||
getUserCountryUseCase = getUserCountryUseCase,
|
getUserCountryUseCase = getUserCountryUseCase,
|
||||||
onrampFeatureToggles = onrampFeatureToggles,
|
onrampFeatureToggles = onrampFeatureToggles,
|
||||||
environmentConfigStorage = environmentConfigStorage,
|
environmentConfigStorage = environmentConfigStorage,
|
||||||
|
|
@ -300,6 +299,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
||||||
onboardingRepository = onboardingRepository,
|
onboardingRepository = onboardingRepository,
|
||||||
excludedBlockchains = excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
appPreferencesStore = appPreferencesStore,
|
appPreferencesStore = appPreferencesStore,
|
||||||
|
clipboardManager = clipboardManager,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.tangem.tap.common
|
package com.tangem.tap.common
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
|
|
@ -11,14 +10,14 @@ interface SnackbarHandler {
|
||||||
@StringRes text: Int,
|
@StringRes text: Int,
|
||||||
length: Int = Snackbar.LENGTH_INDEFINITE,
|
length: Int = Snackbar.LENGTH_INDEFINITE,
|
||||||
@StringRes buttonTitle: Int? = null,
|
@StringRes buttonTitle: Int? = null,
|
||||||
action: View.OnClickListener? = null,
|
action: (() -> Unit)? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun showSnackbar(
|
fun showSnackbar(
|
||||||
text: TextReference,
|
text: TextReference,
|
||||||
length: Int = Snackbar.LENGTH_INDEFINITE,
|
length: Int = Snackbar.LENGTH_INDEFINITE,
|
||||||
buttonTitle: TextReference? = null,
|
buttonTitle: TextReference? = null,
|
||||||
action: View.OnClickListener? = null,
|
action: (() -> Unit)? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun dismissSnackbar()
|
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 fbAnalytics = Firebase.analytics
|
||||||
private val fbCrashlytics = Firebase.crashlytics
|
private val fbCrashlytics = Firebase.crashlytics
|
||||||
|
|
||||||
|
private val eventConverter = FirebaseAnalyticsEventConverter()
|
||||||
|
|
||||||
override fun logEvent(event: String, params: Map<String, String>) {
|
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>) {
|
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)
|
fbCrashlytics.recordException(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,52 @@
|
||||||
package com.tangem.tap.common.clipboard
|
package com.tangem.tap.common.clipboard
|
||||||
|
|
||||||
import android.content.ClipData
|
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 com.tangem.core.ui.clipboard.ClipboardManager
|
||||||
|
import timber.log.Timber
|
||||||
import android.content.ClipboardManager as AndroidClipboardManager
|
import android.content.ClipboardManager as AndroidClipboardManager
|
||||||
|
|
||||||
internal class DefaultClipboardManager(private val clipboardManager: AndroidClipboardManager) : ClipboardManager {
|
internal class DefaultClipboardManager(private val clipboardManager: AndroidClipboardManager) : ClipboardManager {
|
||||||
|
|
||||||
override fun setText(label: String, text: String) {
|
override fun setText(text: String, isSensitive: Boolean, label: String) {
|
||||||
clipboardManager.setPrimaryClip(ClipData.newPlainText(label, text))
|
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
|
import timber.log.Timber
|
||||||
|
|
||||||
internal object MockClipboardManager : ClipboardManager {
|
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")
|
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
|
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.Context
|
||||||
import android.content.ContextWrapper
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.view.View
|
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.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
|
|
||||||
fun Context.getDrawableCompat(@DrawableRes drawableResId: Int): Drawable? {
|
fun Context.getDrawableCompat(@DrawableRes drawableResId: Int): Drawable? {
|
||||||
return ContextCompat.getDrawable(this, drawableResId)
|
return ContextCompat.getDrawable(this, drawableResId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorInt
|
|
||||||
fun Fragment.getColor(@ColorRes colorRes: Int): Int {
|
|
||||||
return ContextCompat.getColor(requireContext(), colorRes)
|
|
||||||
}
|
|
||||||
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
fun Context.getColorCompat(@ColorRes colorRes: Int): Int {
|
fun Context.getColorCompat(@ColorRes colorRes: Int): Int {
|
||||||
return ContextCompat.getColor(this, colorRes)
|
return ContextCompat.getColor(this, colorRes)
|
||||||
|
|
@ -40,10 +33,6 @@ fun View.getString(@StringRes id: Int, vararg formatArgs: String): String {
|
||||||
return context.getString(id, *formatArgs)
|
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) {
|
fun View.show(show: Boolean, invokeBeforeStateChanged: (() -> Unit)? = null) {
|
||||||
return if (show) this.show(invokeBeforeStateChanged) else this.hide(invokeBeforeStateChanged)
|
return if (show) this.show(invokeBeforeStateChanged) else this.hide(invokeBeforeStateChanged)
|
||||||
}
|
}
|
||||||
|
|
@ -62,25 +51,6 @@ fun View.hide(invokeBeforeStateChanged: (() -> Unit)? = null) {
|
||||||
this.visibility = View.GONE
|
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 {
|
fun View.getString(resId: Int, vararg formatArgs: Any?): String {
|
||||||
return context.getString(resId, *formatArgs)
|
return context.getString(resId, *formatArgs)
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
package com.tangem.tap.common.redux
|
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.GlobalMiddleware
|
||||||
import com.tangem.tap.common.redux.global.GlobalState
|
import com.tangem.tap.common.redux.global.GlobalState
|
||||||
import com.tangem.tap.common.redux.legacy.LegacyMiddleware
|
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.features.welcome.redux.WelcomeState
|
||||||
import com.tangem.tap.proxy.redux.DaggerGraphMiddleware
|
import com.tangem.tap.proxy.redux.DaggerGraphMiddleware
|
||||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||||
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
|
|
||||||
import org.rekotlin.Middleware
|
import org.rekotlin.Middleware
|
||||||
import org.rekotlin.StateType
|
import org.rekotlin.StateType
|
||||||
|
|
||||||
|
|
@ -46,18 +42,6 @@ data class AppState(
|
||||||
val daggerGraphState: DaggerGraphState = DaggerGraphState(),
|
val daggerGraphState: DaggerGraphState = DaggerGraphState(),
|
||||||
) : StateType {
|
) : 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 {
|
companion object {
|
||||||
fun getMiddleware(): List<Middleware<AppState>> {
|
fun getMiddleware(): List<Middleware<AppState>> {
|
||||||
return listOf(
|
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 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 kotlinx.coroutines.launch
|
||||||
import org.rekotlin.Action
|
import org.rekotlin.Action
|
||||||
import org.rekotlin.Middleware
|
import org.rekotlin.Middleware
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
object GlobalMiddleware {
|
object GlobalMiddleware {
|
||||||
val handler = globalMiddlewareHandler
|
val handler = globalMiddlewareHandler
|
||||||
|
|
@ -87,27 +86,6 @@ private fun handleAction(action: Action, appState: () -> AppState?) {
|
||||||
}
|
}
|
||||||
scope.launch { exchangeManager.update() }
|
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
|
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.common.redux.AppState
|
||||||
import com.tangem.tap.features.onboarding.OnboardingManager
|
import com.tangem.tap.features.onboarding.OnboardingManager
|
||||||
import com.tangem.tap.proxy.AppStateHolder
|
import com.tangem.tap.proxy.AppStateHolder
|
||||||
|
|
@ -38,7 +36,6 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde
|
||||||
}
|
}
|
||||||
is GlobalAction.SaveScanResponse -> {
|
is GlobalAction.SaveScanResponse -> {
|
||||||
appStateHolder.scanResponse = action.scanResponse
|
appStateHolder.scanResponse = action.scanResponse
|
||||||
domainStore.dispatch(DomainGlobalAction.SaveScanNoteResponse(action.scanResponse))
|
|
||||||
globalState.copy(scanResponse = action.scanResponse)
|
globalState.copy(scanResponse = action.scanResponse)
|
||||||
}
|
}
|
||||||
is GlobalAction.ChangeAppCurrency -> {
|
is GlobalAction.ChangeAppCurrency -> {
|
||||||
|
|
@ -77,9 +74,6 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde
|
||||||
}
|
}
|
||||||
is GlobalAction.SetIfCardVerifiedOnline ->
|
is GlobalAction.SetIfCardVerifiedOnline ->
|
||||||
globalState.copy(cardVerifiedOnline = action.verified)
|
globalState.copy(cardVerifiedOnline = action.verified)
|
||||||
is GlobalAction.FetchUserCountry.Success -> {
|
|
||||||
globalState.copy(userCountryCode = action.countryCode)
|
|
||||||
}
|
|
||||||
else -> globalState
|
else -> globalState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.common.ui
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.Toast
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.tangem.core.analytics.Analytics
|
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.extensions.*
|
||||||
import com.tangem.tap.common.redux.AppDialog
|
import com.tangem.tap.common.redux.AppDialog
|
||||||
import com.tangem.tap.domain.model.WalletAddressData
|
import com.tangem.tap.domain.model.WalletAddressData
|
||||||
|
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||||
import com.tangem.tap.store
|
import com.tangem.tap.store
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding
|
import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding
|
||||||
|
|
@ -51,7 +53,12 @@ internal class AddressInfoBottomSheetDialog(
|
||||||
tvAddress.text = data.address
|
tvAddress.text = data.address
|
||||||
btnFlCopyAddress.setOnClickListener {
|
btnFlCopyAddress.setOnClickListener {
|
||||||
Analytics.send(Token.Receive.ButtonCopyAddress())
|
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 {
|
btnFlShare.setOnClickListener {
|
||||||
Analytics.send(Token.Receive.ButtonShareAddress())
|
Analytics.send(Token.Receive.ButtonShareAddress())
|
||||||
|
|
|
||||||
|
|
@ -68,5 +68,6 @@ internal class AndroidEmailSender : EmailSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.intent
|
return builder.intent
|
||||||
|
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,8 +6,9 @@ import com.arkivanov.decompose.defaultComponentContext
|
||||||
import com.tangem.core.decompose.context.AppComponentContext
|
import com.tangem.core.decompose.context.AppComponentContext
|
||||||
import com.tangem.core.decompose.context.DefaultAppComponentContext
|
import com.tangem.core.decompose.context.DefaultAppComponentContext
|
||||||
import com.tangem.core.decompose.di.DecomposeComponent
|
import com.tangem.core.decompose.di.DecomposeComponent
|
||||||
|
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||||
import com.tangem.core.decompose.di.RootAppComponentContext
|
import com.tangem.core.decompose.di.RootAppComponentContext
|
||||||
import com.tangem.core.ui.UiDependencies
|
import com.tangem.core.decompose.ui.UiMessageSender
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
|
|
@ -27,13 +28,13 @@ internal object RootAppComponentContextModule {
|
||||||
@ActivityContext context: Context,
|
@ActivityContext context: Context,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
componentBuilder: DecomposeComponent.Builder,
|
componentBuilder: DecomposeComponent.Builder,
|
||||||
uiDependencies: UiDependencies,
|
@GlobalUiMessageSender messageSender: UiMessageSender,
|
||||||
): AppComponentContext {
|
): AppComponentContext {
|
||||||
return DefaultAppComponentContext(
|
return DefaultAppComponentContext(
|
||||||
componentContext = (context as AppCompatActivity).defaultComponentContext(),
|
componentContext = (context as AppCompatActivity).defaultComponentContext(),
|
||||||
messageHandler = uiDependencies.eventMessageHandler,
|
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
hiltComponentBuilder = componentBuilder,
|
hiltComponentBuilder = componentBuilder,
|
||||||
|
messageSender = messageSender,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package com.tangem.tap.di
|
package com.tangem.tap.di
|
||||||
|
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||||
|
import com.tangem.core.decompose.ui.DefaultUiMessageSender
|
||||||
|
import com.tangem.core.decompose.ui.UiMessageSender
|
||||||
import com.tangem.core.ui.UiDependencies
|
import com.tangem.core.ui.UiDependencies
|
||||||
import com.tangem.core.ui.haptic.VibratorHapticManager
|
import com.tangem.core.ui.haptic.VibratorHapticManager
|
||||||
import com.tangem.core.ui.message.EventMessageHandler
|
import com.tangem.core.ui.message.EventMessageHandler
|
||||||
|
|
@ -28,4 +31,11 @@ internal object UiDependenciesModule {
|
||||||
override val eventMessageHandler: EventMessageHandler = EventMessageHandler()
|
override val eventMessageHandler: EventMessageHandler = EventMessageHandler()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@GlobalUiMessageSender
|
||||||
|
fun provideUiMessageSender(uiDependencies: UiDependencies): UiMessageSender {
|
||||||
|
return DefaultUiMessageSender(uiDependencies.eventMessageHandler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.tangem.tap.di.domain
|
||||||
|
|
||||||
|
import com.tangem.domain.promo.PromoRepository
|
||||||
|
import com.tangem.domain.promo.ShouldShowRingPromoUseCase
|
||||||
|
import com.tangem.domain.promo.ShouldShowSwapPromoTokenUseCase
|
||||||
|
import com.tangem.domain.promo.ShouldShowSwapPromoWalletUseCase
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
internal object PromoDomainModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideShouldShowSwapPromoWalletUseCase(
|
||||||
|
promoSettingsRepository: PromoRepository,
|
||||||
|
): ShouldShowSwapPromoWalletUseCase {
|
||||||
|
return ShouldShowSwapPromoWalletUseCase(promoSettingsRepository)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideShouldShowRingPromoUseCase(promoRepository: PromoRepository): ShouldShowRingPromoUseCase {
|
||||||
|
return ShouldShowRingPromoUseCase(promoRepository)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideShouldShowSwapPromoTokenUseCase(promoRepository: PromoRepository): ShouldShowSwapPromoTokenUseCase {
|
||||||
|
return ShouldShowSwapPromoTokenUseCase(promoRepository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,6 @@ import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
|
||||||
import com.tangem.domain.settings.*
|
import com.tangem.domain.settings.*
|
||||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||||
import com.tangem.domain.settings.repositories.PermissionRepository
|
import com.tangem.domain.settings.repositories.PermissionRepository
|
||||||
import com.tangem.domain.settings.repositories.PromoSettingsRepository
|
|
||||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||||
import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase
|
import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase
|
||||||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||||
|
|
@ -117,30 +116,6 @@ internal object SettingsDomainModule {
|
||||||
return IsWalletsScrollPreviewEnabled(settingsRepository = settingsRepository)
|
return IsWalletsScrollPreviewEnabled(settingsRepository = settingsRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideShouldShowSwapPromoWalletUseCase(
|
|
||||||
promoSettingsRepository: PromoSettingsRepository,
|
|
||||||
): ShouldShowSwapPromoWalletUseCase {
|
|
||||||
return ShouldShowSwapPromoWalletUseCase(promoSettingsRepository)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideShouldShowRingPromoUseCase(
|
|
||||||
promoSettingsRepository: PromoSettingsRepository,
|
|
||||||
): ShouldShowRingPromoUseCase {
|
|
||||||
return ShouldShowRingPromoUseCase(promoSettingsRepository)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideShouldShowSwapPromoTokenUseCase(
|
|
||||||
promoSettingsRepository: PromoSettingsRepository,
|
|
||||||
): ShouldShowSwapPromoTokenUseCase {
|
|
||||||
return ShouldShowSwapPromoTokenUseCase(promoSettingsRepository)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideDeleteDeprecatedLogsUseCase(settingsRepository: SettingsRepository): DeleteDeprecatedLogsUseCase {
|
fun provideDeleteDeprecatedLogsUseCase(settingsRepository: SettingsRepository): DeleteDeprecatedLogsUseCase {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
package com.tangem.tap.di.domain
|
package com.tangem.tap.di.domain
|
||||||
|
|
||||||
import com.tangem.domain.exchange.RampStateManager
|
import com.tangem.domain.exchange.RampStateManager
|
||||||
import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase
|
|
||||||
import com.tangem.domain.staking.repositories.StakingRepository
|
import com.tangem.domain.staking.repositories.StakingRepository
|
||||||
import com.tangem.domain.tokens.*
|
import com.tangem.domain.tokens.*
|
||||||
import com.tangem.domain.tokens.repository.*
|
import com.tangem.domain.tokens.repository.*
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
import com.tangem.feature.swap.domain.api.SwapRepository
|
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
|
|
@ -118,13 +116,9 @@ internal object TokensDomainModule {
|
||||||
currenciesRepository: CurrenciesRepository,
|
currenciesRepository: CurrenciesRepository,
|
||||||
quotesRepository: QuotesRepository,
|
quotesRepository: QuotesRepository,
|
||||||
networksRepository: NetworksRepository,
|
networksRepository: NetworksRepository,
|
||||||
swapRepository: SwapRepository,
|
|
||||||
currencyChecksRepository: CurrencyChecksRepository,
|
currencyChecksRepository: CurrencyChecksRepository,
|
||||||
showSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase,
|
|
||||||
promoRepository: PromoRepository,
|
|
||||||
stakingRepository: StakingRepository,
|
stakingRepository: StakingRepository,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
rampStateManager: RampStateManager,
|
|
||||||
): GetCurrencyWarningsUseCase {
|
): GetCurrencyWarningsUseCase {
|
||||||
return GetCurrencyWarningsUseCase(
|
return GetCurrencyWarningsUseCase(
|
||||||
walletManagersFacade = walletManagersFacade,
|
walletManagersFacade = walletManagersFacade,
|
||||||
|
|
@ -132,12 +126,8 @@ internal object TokensDomainModule {
|
||||||
quotesRepository = quotesRepository,
|
quotesRepository = quotesRepository,
|
||||||
networksRepository = networksRepository,
|
networksRepository = networksRepository,
|
||||||
currencyChecksRepository = currencyChecksRepository,
|
currencyChecksRepository = currencyChecksRepository,
|
||||||
swapRepository = swapRepository,
|
|
||||||
showSwapPromoTokenUseCase = showSwapPromoTokenUseCase,
|
|
||||||
promoRepository = promoRepository,
|
|
||||||
stakingRepository = stakingRepository,
|
stakingRepository = stakingRepository,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
rampStateManager = rampStateManager,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.tangem.tap.di.routing
|
package com.tangem.tap.di.routing
|
||||||
|
|
||||||
import com.tangem.tap.routing.RoutingComponent
|
import com.tangem.tap.routing.component.RoutingComponent
|
||||||
import com.tangem.tap.routing.impl.DefaultRoutingComponent
|
import com.tangem.tap.routing.component.impl.DefaultRoutingComponent
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import com.tangem.common.services.secure.SecureStorage
|
||||||
import com.tangem.common.usersCode.UserCodeRepository
|
import com.tangem.common.usersCode.UserCodeRepository
|
||||||
import com.tangem.core.analytics.Analytics
|
import com.tangem.core.analytics.Analytics
|
||||||
import com.tangem.core.analytics.models.Basic
|
import com.tangem.core.analytics.models.Basic
|
||||||
|
import com.tangem.core.res.getStringSafe
|
||||||
import com.tangem.crypto.bip39.DefaultMnemonic
|
import com.tangem.crypto.bip39.DefaultMnemonic
|
||||||
import com.tangem.crypto.hdWallet.DerivationPath
|
import com.tangem.crypto.hdWallet.DerivationPath
|
||||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
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.CreateProductWalletTaskResponse
|
||||||
import com.tangem.sdk.api.TangemSdkManager
|
import com.tangem.sdk.api.TangemSdkManager
|
||||||
import com.tangem.tap.derivationsFinder
|
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.CreateFirstTwinWalletTask
|
||||||
import com.tangem.tap.domain.twins.CreateSecondTwinWalletTask
|
import com.tangem.tap.domain.twins.CreateSecondTwinWalletTask
|
||||||
import com.tangem.tap.domain.twins.FinalizeTwinTask
|
import com.tangem.tap.domain.twins.FinalizeTwinTask
|
||||||
|
|
@ -122,7 +126,7 @@ class DefaultTangemSdkManager(
|
||||||
messageRes: Int?,
|
messageRes: Int?,
|
||||||
allowsRequestAccessCodeFromRepository: Boolean,
|
allowsRequestAccessCodeFromRepository: Boolean,
|
||||||
): CompletionResult<ScanResponse> {
|
): 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(
|
return runTaskAsyncReturnOnMain(
|
||||||
runnable = ScanProductTask(
|
runnable = ScanProductTask(
|
||||||
card = null,
|
card = null,
|
||||||
|
|
@ -154,9 +158,9 @@ class DefaultTangemSdkManager(
|
||||||
),
|
),
|
||||||
cardId = scanResponse.card.cardId,
|
cardId = scanResponse.card.cardId,
|
||||||
initialMessage = if (scanResponse.cardTypesResolver.isRing()) {
|
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 {
|
} 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,
|
iconScanRes = if (scanResponse.cardTypesResolver.isRing()) R.drawable.img_hand_scan_ring else null,
|
||||||
preflightReadFilter = null,
|
preflightReadFilter = null,
|
||||||
|
|
@ -194,9 +198,9 @@ class DefaultTangemSdkManager(
|
||||||
),
|
),
|
||||||
cardId = scanResponse.card.cardId,
|
cardId = scanResponse.card.cardId,
|
||||||
initialMessage = if (scanResponse.cardTypesResolver.isRing()) {
|
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 {
|
} else {
|
||||||
Message(resources.getString(R.string.initial_message_create_wallet_body))
|
Message(resources.getStringSafe(R.string.initial_message_create_wallet_body))
|
||||||
},
|
},
|
||||||
preflightReadFilter = null,
|
preflightReadFilter = null,
|
||||||
)
|
)
|
||||||
|
|
@ -243,7 +247,7 @@ class DefaultTangemSdkManager(
|
||||||
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
|
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
|
||||||
),
|
),
|
||||||
cardId = cardId,
|
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(
|
return runTaskAsyncReturnOnMain(
|
||||||
runnable = ResetBackupCardTask(userWalletId),
|
runnable = ResetBackupCardTask(userWalletId),
|
||||||
initialMessage = Message(
|
initialMessage = Message(
|
||||||
resources.getString(
|
resources.getStringSafe(
|
||||||
R.string.initial_message_reset_backup_card_header,
|
R.string.initial_message_reset_backup_card_header,
|
||||||
cardNumber.toString(),
|
cardNumber.toString(),
|
||||||
),
|
),
|
||||||
|
|
@ -281,7 +285,7 @@ class DefaultTangemSdkManager(
|
||||||
return runTaskAsyncReturnOnMain(
|
return runTaskAsyncReturnOnMain(
|
||||||
SetUserCodeCommand.changePasscode(null),
|
SetUserCodeCommand.changePasscode(null),
|
||||||
cardId,
|
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(
|
return runTaskAsyncReturnOnMain(
|
||||||
SetUserCodeCommand.changeAccessCode(null),
|
SetUserCodeCommand.changeAccessCode(null),
|
||||||
cardId,
|
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(
|
return runTaskAsyncReturnOnMain(
|
||||||
SetUserCodeCommand.resetUserCodes(),
|
SetUserCodeCommand.resetUserCodes(),
|
||||||
cardId,
|
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(
|
return runTaskAsyncReturnOnMain(
|
||||||
SetUserCodeRecoveryAllowedTask(enabled),
|
SetUserCodeRecoveryAllowedTask(enabled),
|
||||||
cardId,
|
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(
|
return runTaskAsyncReturnOnMain(
|
||||||
runnable = ScanTask(allowRequestAccessCodeFromRepository),
|
runnable = ScanTask(allowRequestAccessCodeFromRepository),
|
||||||
cardId = cardId,
|
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) }
|
.map { CardDTO(it) }
|
||||||
}
|
}
|
||||||
|
|
@ -372,7 +376,9 @@ class DefaultTangemSdkManager(
|
||||||
|
|
||||||
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
||||||
override fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
|
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) {
|
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.core.UserCodeRequestPolicy
|
||||||
import com.tangem.common.extensions.ByteArrayKey
|
import com.tangem.common.extensions.ByteArrayKey
|
||||||
import com.tangem.common.services.InMemoryStorage
|
import com.tangem.common.services.InMemoryStorage
|
||||||
|
import com.tangem.core.res.getStringSafe
|
||||||
import com.tangem.crypto.hdWallet.DerivationPath
|
import com.tangem.crypto.hdWallet.DerivationPath
|
||||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||||
import com.tangem.domain.models.scan.CardDTO
|
import com.tangem.domain.models.scan.CardDTO
|
||||||
|
|
@ -150,7 +151,9 @@ class MockTangemSdkManager(
|
||||||
|
|
||||||
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
||||||
override fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
|
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) {
|
override fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,6 @@ class WalletConnectMiddleware {
|
||||||
is WalletConnectAction.StartWalletConnect -> {
|
is WalletConnectAction.StartWalletConnect -> {
|
||||||
val uri = action.copiedUri
|
val uri = action.copiedUri
|
||||||
if (uri != null && isWalletConnectUri(uri)) {
|
if (uri != null && isWalletConnectUri(uri)) {
|
||||||
// TODO check
|
|
||||||
store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri))
|
store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri))
|
||||||
} else {
|
} else {
|
||||||
store.dispatchNavigationAction { push(AppRoute.QrScanning(SourceType.WALLET_CONNECT)) }
|
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.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.painterResource
|
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.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
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.components.SpacerW
|
||||||
import com.tangem.core.ui.event.EventEffect
|
import com.tangem.core.ui.event.EventEffect
|
||||||
import com.tangem.core.ui.event.consumedEvent
|
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.TangemTheme
|
||||||
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.tap.features.details.ui.appcurrency.AppCurrencySelectorState.Currency
|
import com.tangem.tap.features.details.ui.appcurrency.AppCurrencySelectorState.Currency
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
@ -114,7 +114,7 @@ private fun TopBar(
|
||||||
is AppCurrencySelectorState.Loading,
|
is AppCurrencySelectorState.Loading,
|
||||||
is AppCurrencySelectorState.Default,
|
is AppCurrencySelectorState.Default,
|
||||||
-> Text(
|
-> Text(
|
||||||
text = stringResource(id = R.string.details_row_title_currency),
|
text = stringResourceSafe(id = R.string.details_row_title_currency),
|
||||||
style = TangemTheme.typography.subtitle1,
|
style = TangemTheme.typography.subtitle1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +163,7 @@ private fun SearchBar(onInputChange: (String) -> Unit, modifier: Modifier = Modi
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
textStyle = TangemTheme.typography.subtitle2,
|
textStyle = TangemTheme.typography.subtitle2,
|
||||||
placeholder = {
|
placeholder = {
|
||||||
Text(text = stringResource(id = R.string.common_search))
|
Text(text = stringResourceSafe(id = R.string.common_search))
|
||||||
},
|
},
|
||||||
colors = SearchBarColors,
|
colors = SearchBarColors,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ package com.tangem.tap.features.details.ui.appsettings.components
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||||
import com.tangem.core.ui.components.BasicDialog
|
import com.tangem.core.ui.components.BasicDialog
|
||||||
import com.tangem.core.ui.components.DialogButtonUM
|
import com.tangem.core.ui.components.DialogButtonUM
|
||||||
import com.tangem.core.ui.extensions.resolveReference
|
import com.tangem.core.ui.extensions.resolveReference
|
||||||
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsDialogsFactory
|
import com.tangem.tap.features.details.ui.appsettings.AppSettingsDialogsFactory
|
||||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Dialog
|
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Dialog
|
||||||
|
|
@ -26,7 +26,7 @@ internal fun SettingsAlertDialog(dialog: Dialog.Alert) {
|
||||||
onClick = dialog.onConfirm,
|
onClick = dialog.onConfirm,
|
||||||
),
|
),
|
||||||
dismissButton = DialogButtonUM(
|
dismissButton = DialogButtonUM(
|
||||||
title = stringResource(id = R.string.common_cancel),
|
title = stringResourceSafe(id = R.string.common_cancel),
|
||||||
onClick = dialog.onDismiss,
|
onClick = dialog.onDismiss,
|
||||||
),
|
),
|
||||||
onDismissDialog = dialog.onDismiss,
|
onDismissDialog = dialog.onDismiss,
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ package com.tangem.tap.features.details.ui.appsettings.components
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||||
import com.tangem.core.ui.components.DialogButtonUM
|
import com.tangem.core.ui.components.DialogButtonUM
|
||||||
import com.tangem.core.ui.components.SelectorDialog
|
import com.tangem.core.ui.components.SelectorDialog
|
||||||
import com.tangem.core.ui.extensions.resolveReference
|
import com.tangem.core.ui.extensions.resolveReference
|
||||||
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsDialogsFactory
|
import com.tangem.tap.features.details.ui.appsettings.AppSettingsDialogsFactory
|
||||||
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Dialog
|
import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Dialog
|
||||||
|
|
@ -22,7 +22,7 @@ internal fun SettingsSelectorDialog(dialog: Dialog.Selector) {
|
||||||
selectedItemIndex = dialog.selectedItemIndex,
|
selectedItemIndex = dialog.selectedItemIndex,
|
||||||
items = dialog.items.map { it.resolveReference() }.toImmutableList(),
|
items = dialog.items.map { it.resolveReference() }.toImmutableList(),
|
||||||
confirmButton = DialogButtonUM(
|
confirmButton = DialogButtonUM(
|
||||||
title = stringResource(R.string.common_cancel),
|
title = stringResourceSafe(R.string.common_cancel),
|
||||||
onClick = dialog.onDismiss,
|
onClick = dialog.onDismiss,
|
||||||
),
|
),
|
||||||
onSelect = dialog.onSelect,
|
onSelect = dialog.onSelect,
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.painterResource
|
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.Preview
|
||||||
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
||||||
|
|
@ -88,13 +88,13 @@ private fun CardSettingsReadCard(onScanCardClick: () -> Unit) {
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.scan_card_settings_title),
|
text = stringResourceSafe(id = R.string.scan_card_settings_title),
|
||||||
color = TangemTheme.colors.text.primary1,
|
color = TangemTheme.colors.text.primary1,
|
||||||
style = TangemTheme.typography.h3,
|
style = TangemTheme.typography.h3,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.size(TangemTheme.dimens.size20))
|
Spacer(modifier = Modifier.size(TangemTheme.dimens.size20))
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.scan_card_settings_message),
|
text = stringResourceSafe(id = R.string.scan_card_settings_message),
|
||||||
color = TangemTheme.colors.text.secondary,
|
color = TangemTheme.colors.text.secondary,
|
||||||
style = TangemTheme.typography.body1,
|
style = TangemTheme.typography.body1,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -103,7 +103,7 @@ private fun CardSettingsReadCard(onScanCardClick: () -> Unit) {
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.size(TangemTheme.dimens.size32))
|
Spacer(modifier = Modifier.size(TangemTheme.dimens.size32))
|
||||||
DetailsMainButton(
|
DetailsMainButton(
|
||||||
title = stringResource(id = R.string.scan_card_settings_button),
|
title = stringResourceSafe(id = R.string.scan_card_settings_button),
|
||||||
onClick = onScanCardClick,
|
onClick = onScanCardClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package com.tangem.tap.features.details.ui.cardsettings
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.ReadOnlyComposable
|
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.redux.SecurityOption
|
||||||
import com.tangem.tap.features.details.ui.securitymode.toTitleRes
|
import com.tangem.tap.features.details.ui.securitymode.toTitleRes
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
|
|
@ -77,7 +77,7 @@ internal sealed interface TextReference {
|
||||||
@ReadOnlyComposable
|
@ReadOnlyComposable
|
||||||
internal fun TextReference.resolveReference(): String {
|
internal fun TextReference.resolveReference(): String {
|
||||||
return when (this) {
|
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
|
is TextReference.Str -> this.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
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.core.ui.res.TangemTheme
|
||||||
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
||||||
import com.tangem.tap.features.details.ui.common.DetailsRadioButtonElement
|
import com.tangem.tap.features.details.ui.common.DetailsRadioButtonElement
|
||||||
|
|
@ -36,14 +36,14 @@ fun AccessCodeRecoveryOptions(state: AccessCodeRecoveryScreenState) {
|
||||||
)
|
)
|
||||||
|
|
||||||
DetailsRadioButtonElement(
|
DetailsRadioButtonElement(
|
||||||
title = stringResource(id = R.string.common_enabled),
|
title = stringResourceSafe(id = R.string.common_enabled),
|
||||||
subtitle = stringResource(id = R.string.card_settings_access_code_recovery_enabled_description),
|
subtitle = stringResourceSafe(id = R.string.card_settings_access_code_recovery_enabled_description),
|
||||||
selected = state.enabledSelection,
|
selected = state.enabledSelection,
|
||||||
onClick = { state.onOptionClick(true) },
|
onClick = { state.onOptionClick(true) },
|
||||||
)
|
)
|
||||||
DetailsRadioButtonElement(
|
DetailsRadioButtonElement(
|
||||||
title = stringResource(id = R.string.common_disabled),
|
title = stringResourceSafe(id = R.string.common_disabled),
|
||||||
subtitle = stringResource(id = R.string.card_settings_access_code_recovery_disabled_description),
|
subtitle = stringResourceSafe(id = R.string.card_settings_access_code_recovery_disabled_description),
|
||||||
selected = !state.enabledSelection,
|
selected = !state.enabledSelection,
|
||||||
onClick = { state.onOptionClick(false) },
|
onClick = { state.onOptionClick(false) },
|
||||||
)
|
)
|
||||||
|
|
@ -51,7 +51,7 @@ fun AccessCodeRecoveryOptions(state: AccessCodeRecoveryScreenState) {
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
DetailsMainButton(
|
DetailsMainButton(
|
||||||
title = stringResource(id = R.string.common_save_changes),
|
title = stringResourceSafe(id = R.string.common_save_changes),
|
||||||
enabled = state.isSaveChangesEnabled,
|
enabled = state.isSaveChangesEnabled,
|
||||||
onClick = { state.onSaveChangesClick() },
|
onClick = { state.onSaveChangesClick() },
|
||||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing20),
|
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.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
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.res.TangemTheme
|
||||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
|
|
@ -64,7 +64,7 @@ internal fun SettingsScreensScaffold(
|
||||||
) {
|
) {
|
||||||
if (titleRes != null) {
|
if (titleRes != null) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = titleRes),
|
text = stringResourceSafe(id = titleRes),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = TangemTheme.dimens.spacing20)
|
.padding(horizontal = TangemTheme.dimens.spacing20)
|
||||||
.padding(bottom = TangemTheme.dimens.spacing36),
|
.padding(bottom = TangemTheme.dimens.spacing36),
|
||||||
|
|
@ -82,7 +82,7 @@ internal fun SettingsScreensScaffold(
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ScreenTitle(titleRes: Int, modifier: Modifier = Modifier) {
|
internal fun ScreenTitle(titleRes: Int, modifier: Modifier = Modifier) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = titleRes),
|
text = stringResourceSafe(id = titleRes),
|
||||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||||
style = TangemTheme.typography.h1,
|
style = TangemTheme.typography.h1,
|
||||||
color = TangemTheme.colors.text.primary1,
|
color = TangemTheme.colors.text.primary1,
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
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.Preview
|
||||||
import com.tangem.core.ui.components.*
|
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.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
import com.tangem.tap.features.details.ui.cardsettings.TextReference
|
||||||
|
|
@ -71,7 +71,7 @@ private fun ResetCardView(state: ResetCardScreenState) {
|
||||||
@Composable
|
@Composable
|
||||||
private fun Title() {
|
private fun Title() {
|
||||||
Text(
|
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,
|
style = TangemTheme.typography.h1,
|
||||||
color = TangemTheme.colors.text.primary1,
|
color = TangemTheme.colors.text.primary1,
|
||||||
)
|
)
|
||||||
|
|
@ -89,7 +89,7 @@ private fun AlertImage() {
|
||||||
@Composable
|
@Composable
|
||||||
private fun Subtitle() {
|
private fun Subtitle() {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.common_attention),
|
text = stringResourceSafe(id = R.string.common_attention),
|
||||||
style = TangemTheme.typography.h3,
|
style = TangemTheme.typography.h3,
|
||||||
color = TangemTheme.colors.text.primary1,
|
color = TangemTheme.colors.text.primary1,
|
||||||
)
|
)
|
||||||
|
|
@ -179,7 +179,7 @@ private fun ColumnScope.DynamicSpacer(scrollState: ScrollState) {
|
||||||
@Composable
|
@Composable
|
||||||
private fun ResetButton(enabled: Boolean, onResetButtonClick: () -> Unit) {
|
private fun ResetButton(enabled: Boolean, onResetButtonClick: () -> Unit) {
|
||||||
DetailsMainButton(
|
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,
|
onClick = onResetButtonClick,
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
)
|
)
|
||||||
|
|
@ -188,14 +188,14 @@ private fun ResetButton(enabled: Boolean, onResetButtonClick: () -> Unit) {
|
||||||
@Composable
|
@Composable
|
||||||
private fun CommonResetDialog(dialog: ResetCardScreenState.Dialog) {
|
private fun CommonResetDialog(dialog: ResetCardScreenState.Dialog) {
|
||||||
BasicDialog(
|
BasicDialog(
|
||||||
title = stringResource(dialog.titleResId),
|
title = stringResourceSafe(dialog.titleResId),
|
||||||
message = stringResource(dialog.messageResId),
|
message = stringResourceSafe(dialog.messageResId),
|
||||||
dismissButton = DialogButtonUM(
|
dismissButton = DialogButtonUM(
|
||||||
title = stringResource(id = R.string.common_cancel),
|
title = stringResourceSafe(id = R.string.common_cancel),
|
||||||
onClick = dialog.onDismiss,
|
onClick = dialog.onDismiss,
|
||||||
),
|
),
|
||||||
confirmButton = DialogButtonUM(
|
confirmButton = DialogButtonUM(
|
||||||
title = stringResource(id = R.string.card_settings_action_sheet_reset),
|
title = stringResourceSafe(id = R.string.card_settings_action_sheet_reset),
|
||||||
warning = true,
|
warning = true,
|
||||||
onClick = dialog.onConfirmClick,
|
onClick = dialog.onConfirmClick,
|
||||||
),
|
),
|
||||||
|
|
@ -206,10 +206,10 @@ private fun CommonResetDialog(dialog: ResetCardScreenState.Dialog) {
|
||||||
@Composable
|
@Composable
|
||||||
private fun CompletedResetDialog(dialog: ResetCardDialog) {
|
private fun CompletedResetDialog(dialog: ResetCardDialog) {
|
||||||
BasicDialog(
|
BasicDialog(
|
||||||
title = stringResource(id = dialog.titleResId),
|
title = stringResourceSafe(id = dialog.titleResId),
|
||||||
message = stringResource(id = dialog.messageResId),
|
message = stringResourceSafe(id = dialog.messageResId),
|
||||||
confirmButton = DialogButtonUM(
|
confirmButton = DialogButtonUM(
|
||||||
title = stringResource(id = R.string.common_ok),
|
title = stringResourceSafe(id = R.string.common_ok),
|
||||||
onClick = dialog.onConfirmClick,
|
onClick = dialog.onConfirmClick,
|
||||||
),
|
),
|
||||||
onDismissDialog = {},
|
onDismissDialog = {},
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
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.redux.SecurityOption
|
||||||
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
||||||
import com.tangem.tap.features.details.ui.common.DetailsRadioButtonElement
|
import com.tangem.tap.features.details.ui.common.DetailsRadioButtonElement
|
||||||
|
|
@ -47,7 +47,7 @@ private fun SecurityModeOptions(state: SecurityModeScreenState) {
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
DetailsMainButton(
|
DetailsMainButton(
|
||||||
title = stringResource(id = R.string.common_save_changes),
|
title = stringResourceSafe(id = R.string.common_save_changes),
|
||||||
enabled = state.isSaveChangesEnabled,
|
enabled = state.isSaveChangesEnabled,
|
||||||
onClick = state.onSaveChangesClicked,
|
onClick = state.onSaveChangesClicked,
|
||||||
modifier = Modifier.padding(start = 20.dp, end = 20.dp),
|
modifier = Modifier.padding(start = 20.dp, end = 20.dp),
|
||||||
|
|
@ -68,8 +68,8 @@ private fun SecurityOption(option: SecurityOption, state: SecurityModeScreenStat
|
||||||
}
|
}
|
||||||
|
|
||||||
DetailsRadioButtonElement(
|
DetailsRadioButtonElement(
|
||||||
title = stringResource(id = title),
|
title = stringResourceSafe(id = title),
|
||||||
subtitle = stringResource(id = subtitle),
|
subtitle = stringResourceSafe(id = subtitle),
|
||||||
selected = selected,
|
selected = selected,
|
||||||
onClick = { state.onNewModeSelected(option) },
|
onClick = { state.onNewModeSelected(option) },
|
||||||
)
|
)
|
||||||
|
|
@ -80,7 +80,7 @@ private fun SecurityOption(option: SecurityOption, state: SecurityModeScreenStat
|
||||||
private fun SecurityModeScreenPreview() {
|
private fun SecurityModeScreenPreview() {
|
||||||
SecurityModeScreen(
|
SecurityModeScreen(
|
||||||
state = SecurityModeScreenState(
|
state = SecurityModeScreenState(
|
||||||
availableOptions = SecurityOption.values().toList(),
|
availableOptions = SecurityOption.entries,
|
||||||
selectedSecurityMode = SecurityOption.LongTap,
|
selectedSecurityMode = SecurityOption.LongTap,
|
||||||
isSaveChangesEnabled = false,
|
isSaveChangesEnabled = false,
|
||||||
onNewModeSelected = {},
|
onNewModeSelected = {},
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,19 @@ import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.painterResource
|
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.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.tangem.core.analytics.Analytics
|
import com.tangem.core.analytics.Analytics
|
||||||
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.tap.common.analytics.events.Settings
|
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.tap.features.details.ui.common.SettingsScreensScaffold
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
@ -30,8 +28,6 @@ internal fun WalletConnectScreen(
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
SettingsScreensScaffold(
|
SettingsScreensScaffold(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
content = {
|
content = {
|
||||||
|
|
@ -46,7 +42,7 @@ internal fun WalletConnectScreen(
|
||||||
AddSessionFab(
|
AddSessionFab(
|
||||||
onAddSession = {
|
onAddSession = {
|
||||||
Analytics.send(Settings.ButtonStartWalletConnectSession())
|
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) {
|
private fun AddSessionFab(onAddSession: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
onClick = onAddSession,
|
onClick = onAddSession,
|
||||||
backgroundColor = TangemTheme.colors.button.primary,
|
containerColor = TangemTheme.colors.button.primary,
|
||||||
contentColor = TangemTheme.colors.icon.primary2,
|
contentColor = TangemTheme.colors.icon.primary2,
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(16.dp),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -96,7 +92,7 @@ private fun EmptyScreen(state: WalletConnectScreenState) {
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.size(24.dp))
|
Spacer(modifier = Modifier.size(24.dp))
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.wallet_connect_subtitle),
|
text = stringResourceSafe(id = R.string.wallet_connect_subtitle),
|
||||||
style = TangemTheme.typography.body2,
|
style = TangemTheme.typography.body2,
|
||||||
color = TangemTheme.colors.text.tertiary,
|
color = TangemTheme.colors.text.tertiary,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ internal data class WalletConnectScreenState(
|
||||||
val sessions: ImmutableList<WcSessionForScreen>,
|
val sessions: ImmutableList<WcSessionForScreen>,
|
||||||
val isLoading: Boolean = false,
|
val isLoading: Boolean = false,
|
||||||
val onRemoveSession: (String) -> Unit = {},
|
val onRemoveSession: (String) -> Unit = {},
|
||||||
val onAddSession: (String?) -> Unit = {},
|
val onAddSession: () -> Unit = {},
|
||||||
)
|
)
|
||||||
|
|
||||||
data class WcSessionForScreen(
|
data class WcSessionForScreen(
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.details.ui.walletconnect
|
||||||
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import arrow.core.getOrElse
|
import arrow.core.getOrElse
|
||||||
|
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||||
import com.tangem.domain.qrscanning.models.SourceType
|
import com.tangem.domain.qrscanning.models.SourceType
|
||||||
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
|
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
|
||||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||||
|
|
@ -17,6 +18,7 @@ import javax.inject.Inject
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class WalletConnectViewModel @Inject constructor(
|
internal class WalletConnectViewModel @Inject constructor(
|
||||||
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
|
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
|
||||||
|
private val clipboardManager: ClipboardManager,
|
||||||
) : ViewModel(), DefaultLifecycleObserver {
|
) : ViewModel(), DefaultLifecycleObserver {
|
||||||
|
|
||||||
override fun onCreate(owner: LifecycleOwner) {
|
override fun onCreate(owner: LifecycleOwner) {
|
||||||
|
|
@ -35,7 +37,11 @@ internal class WalletConnectViewModel @Inject constructor(
|
||||||
sessions.toImmutableList(),
|
sessions.toImmutableList(),
|
||||||
isLoading = state.loading,
|
isLoading = state.loading,
|
||||||
onRemoveSession = { sessionUri -> onRemoveSession(sessionUri, sessions) },
|
onRemoveSession = { sessionUri -> onRemoveSession(sessionUri, sessions) },
|
||||||
onAddSession = { copiedUri -> store.dispatch(WalletConnectAction.StartWalletConnect(copiedUri)) },
|
onAddSession = {
|
||||||
|
store.dispatch(
|
||||||
|
WalletConnectAction.StartWalletConnect(copiedUri = clipboardManager.getText()),
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ internal fun StoriesScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo refactor [REDACTED_TASK_KEY]
|
||||||
StoriesScreenContent(
|
StoriesScreenContent(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -75,6 +76,7 @@ internal fun StoriesScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("Use StoriesContainer from core/ui")
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
@Composable
|
@Composable
|
||||||
private fun StoriesScreenContent(config: StoriesScreenContentConfig, modifier: Modifier = Modifier) {
|
private fun StoriesScreenContent(config: StoriesScreenContentConfig, modifier: Modifier = Modifier) {
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,23 @@ package com.tangem.tap.features.home.compose.content
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.compose.foundation.Image
|
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.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.tangem.core.ui.components.SpacerH
|
import com.tangem.core.ui.components.SpacerH
|
||||||
import com.tangem.core.ui.components.SpacerH16
|
import com.tangem.core.ui.components.SpacerH16
|
||||||
import com.tangem.core.ui.components.SpacerH32
|
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.TangemColorPalette
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.tap.features.home.compose.StoriesBottomImageAnimation
|
import com.tangem.tap.features.home.compose.StoriesBottomImageAnimation
|
||||||
|
|
@ -27,8 +30,8 @@ fun StoriesRevolutionaryWallet() {
|
||||||
SplitContent(
|
SplitContent(
|
||||||
topContent = {
|
topContent = {
|
||||||
TopContent(
|
TopContent(
|
||||||
titleText = stringResource(id = R.string.story_awe_title),
|
titleText = stringResourceSafe(id = R.string.story_awe_title),
|
||||||
subtitleText = stringResource(id = R.string.story_awe_description),
|
subtitleText = stringResourceSafe(id = R.string.story_awe_description),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomContent = {
|
bottomContent = {
|
||||||
|
|
@ -46,8 +49,8 @@ fun StoriesUltraSecureBackup(isPaused: Boolean, stepDuration: Int) {
|
||||||
SplitContent(
|
SplitContent(
|
||||||
topContent = {
|
topContent = {
|
||||||
TopContent(
|
TopContent(
|
||||||
titleText = stringResource(id = R.string.story_backup_title),
|
titleText = stringResourceSafe(id = R.string.story_backup_title),
|
||||||
subtitleText = stringResource(id = R.string.story_backup_description),
|
subtitleText = stringResourceSafe(id = R.string.story_backup_description),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomContent = {
|
bottomContent = {
|
||||||
|
|
@ -65,8 +68,8 @@ fun StoriesCurrencies(isPaused: Boolean, stepDuration: Int) {
|
||||||
SplitContent(
|
SplitContent(
|
||||||
topContent = {
|
topContent = {
|
||||||
TopContent(
|
TopContent(
|
||||||
titleText = stringResource(id = R.string.story_currencies_title),
|
titleText = stringResourceSafe(id = R.string.story_currencies_title),
|
||||||
subtitleText = stringResource(id = R.string.story_currencies_description),
|
subtitleText = stringResourceSafe(id = R.string.story_currencies_description),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomContent = {
|
bottomContent = {
|
||||||
|
|
@ -81,8 +84,8 @@ fun StoriesWeb3(isPaused: Boolean, stepDuration: Int) {
|
||||||
SplitContent(
|
SplitContent(
|
||||||
topContent = {
|
topContent = {
|
||||||
TopContent(
|
TopContent(
|
||||||
titleText = stringResource(id = R.string.story_web3_title),
|
titleText = stringResourceSafe(id = R.string.story_web3_title),
|
||||||
subtitleText = stringResource(id = R.string.story_web3_description),
|
subtitleText = stringResourceSafe(id = R.string.story_web3_description),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomContent = {
|
bottomContent = {
|
||||||
|
|
@ -97,8 +100,8 @@ fun StoriesWalletForEveryone(stepDuration: Int) {
|
||||||
SplitContent(
|
SplitContent(
|
||||||
topContent = {
|
topContent = {
|
||||||
TopContent(
|
TopContent(
|
||||||
titleText = stringResource(id = R.string.story_finish_title),
|
titleText = stringResourceSafe(id = R.string.story_finish_title),
|
||||||
subtitleText = stringResource(id = R.string.story_finish_description),
|
subtitleText = stringResourceSafe(id = R.string.story_finish_description),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomContent = {
|
bottomContent = {
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.tangem.core.ui.components.SpacerH
|
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.TangemColorPalette
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.tap.features.home.compose.StoriesTextAnimation
|
import com.tangem.tap.features.home.compose.StoriesTextAnimation
|
||||||
|
|
@ -66,7 +66,7 @@ fun FirstStoriesContent(isPaused: Boolean, duration: Int) {
|
||||||
) { modifier ->
|
) { modifier ->
|
||||||
Text(
|
Text(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
text = stringResource(R.string.story_meet_title),
|
text = stringResourceSafe(R.string.story_meet_title),
|
||||||
style = style,
|
style = style,
|
||||||
color = TangemColorPalette.White,
|
color = TangemColorPalette.White,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.testTag
|
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.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||||
import com.tangem.core.ui.components.SpacerW12
|
import com.tangem.core.ui.components.SpacerW12
|
||||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
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.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.core.ui.test.TestTags
|
import com.tangem.core.ui.test.TestTags
|
||||||
|
|
@ -52,7 +52,7 @@ internal fun HomeButtons(
|
||||||
private fun ScanCardButton(showProgress: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
private fun ScanCardButton(showProgress: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
StoriesButton(
|
StoriesButton(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
text = stringResource(id = R.string.home_button_scan),
|
text = stringResourceSafe(id = R.string.home_button_scan),
|
||||||
useDarkerColors = false,
|
useDarkerColors = false,
|
||||||
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_tangem_24),
|
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_tangem_24),
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
|
|
@ -64,7 +64,7 @@ private fun ScanCardButton(showProgress: Boolean, onClick: () -> Unit, modifier:
|
||||||
private fun OrderCardButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
private fun OrderCardButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
StoriesButton(
|
StoriesButton(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
text = stringResource(id = R.string.home_button_order),
|
text = stringResourceSafe(id = R.string.home_button_order),
|
||||||
useDarkerColors = true,
|
useDarkerColors = true,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
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.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
|
|
@ -18,7 +18,7 @@ import com.tangem.wallet.R
|
||||||
internal fun SearchCurrenciesButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
internal fun SearchCurrenciesButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
StoriesButton(
|
StoriesButton(
|
||||||
modifier = modifier,
|
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),
|
icon = TangemButtonIconPosition.Start(R.drawable.ic_search_24),
|
||||||
showProgress = false,
|
showProgress = false,
|
||||||
useDarkerColors = true,
|
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.RestoreAppCurrency)
|
||||||
store.dispatch(GlobalAction.ExchangeManager.Init)
|
store.dispatch(GlobalAction.ExchangeManager.Init)
|
||||||
store.dispatch(GlobalAction.FetchUserCountry)
|
|
||||||
}
|
}
|
||||||
is HomeAction.ReadCard -> {
|
is HomeAction.ReadCard -> {
|
||||||
action.scope.launch {
|
action.scope.launch {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import org.rekotlin.StateType
|
import org.rekotlin.StateType
|
||||||
|
|
||||||
|
// todo refactor [REDACTED_TASK_KEY]
|
||||||
data class HomeState(
|
data class HomeState(
|
||||||
val scanInProgress: Boolean = false,
|
val scanInProgress: Boolean = false,
|
||||||
val stories: ImmutableList<Stories> = Stories.entries.toImmutableList(),
|
val stories: ImmutableList<Stories> = Stories.entries.toImmutableList(),
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ internal class MainScreenStateHolder(private val intents: MainIntents) {
|
||||||
stateFlowInternal.update { state ->
|
stateFlowInternal.update { state ->
|
||||||
state.copy(
|
state.copy(
|
||||||
modalNotification = TangemBottomSheetConfig(
|
modalNotification = TangemBottomSheetConfig(
|
||||||
isShow = true,
|
isShown = true,
|
||||||
onDismissRequest = intents::onDismissBottomSheet,
|
onDismissRequest = intents::onDismissBottomSheet,
|
||||||
content = notificationsFactory.createBalancesAreHiddenModalNotification(),
|
content = notificationsFactory.createBalancesAreHiddenModalNotification(),
|
||||||
),
|
),
|
||||||
|
|
@ -45,7 +45,7 @@ internal class MainScreenStateHolder(private val intents: MainIntents) {
|
||||||
|
|
||||||
fun updateWithoutModalNotification() {
|
fun updateWithoutModalNotification() {
|
||||||
stateFlowInternal.update { state ->
|
stateFlowInternal.update { state ->
|
||||||
state.copy(modalNotification = state.modalNotification?.copy(isShow = false))
|
state.copy(modalNotification = state.modalNotification?.copy(isShown = false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase
|
||||||
import com.tangem.domain.staking.FetchStakingTokensUseCase
|
import com.tangem.domain.staking.FetchStakingTokensUseCase
|
||||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||||
import com.tangem.tap.common.extensions.setContext
|
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.tap.features.main.model.MainScreenState
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
|
@ -41,7 +40,6 @@ internal class MainViewModel @Inject constructor(
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val fetchStakingTokensUseCase: FetchStakingTokensUseCase,
|
private val fetchStakingTokensUseCase: FetchStakingTokensUseCase,
|
||||||
private val apiConfigsManager: ApiConfigsManager,
|
private val apiConfigsManager: ApiConfigsManager,
|
||||||
homeFeatureToggles: HomeFeatureToggles,
|
|
||||||
private val fetchUserCountryUseCase: FetchUserCountryUseCase,
|
private val fetchUserCountryUseCase: FetchUserCountryUseCase,
|
||||||
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||||
) : ViewModel(), MainIntents {
|
) : ViewModel(), MainIntents {
|
||||||
|
|
@ -63,11 +61,9 @@ internal class MainViewModel @Inject constructor(
|
||||||
|
|
||||||
viewModelScope.launch(dispatchers.main) { incrementAppLaunchCounterUseCase() }
|
viewModelScope.launch(dispatchers.main) { incrementAppLaunchCounterUseCase() }
|
||||||
|
|
||||||
if (homeFeatureToggles.isMigrateUserCountryCodeEnabled) {
|
viewModelScope.launch {
|
||||||
viewModelScope.launch {
|
fetchUserCountryUseCase().onLeft {
|
||||||
fetchUserCountryUseCase().onLeft {
|
Timber.e("Unable to fetch the user country code $it")
|
||||||
Timber.e("Unable to fetch the user country code $it")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,7 +125,7 @@ internal class MainViewModel @Inject constructor(
|
||||||
it.isBalanceHidingNotificationEnabled && it.isBalanceHidden
|
it.isBalanceHidingNotificationEnabled && it.isBalanceHidden
|
||||||
}
|
}
|
||||||
.onEach {
|
.onEach {
|
||||||
if (state.value.modalNotification?.isShow != true && !it.isUpdateFromToast) {
|
if (state.value.modalNotification?.isShown != true && !it.isUpdateFromToast) {
|
||||||
listenToFlipsUseCase.changeUpdateEnabled(false)
|
listenToFlipsUseCase.changeUpdateEnabled(false)
|
||||||
stateHolder.updateWithHiddenBalancesNotification()
|
stateHolder.updateWithHiddenBalancesNotification()
|
||||||
router.push(AppRoute.ModalNotification)
|
router.push(AppRoute.ModalNotification)
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ internal class ModalNotificationBottomSheetFragment : ComposeBottomSheetFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(notification) {
|
LaunchedEffect(notification) {
|
||||||
if (!notification.isShow) {
|
if (!notification.isShown) {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.analytics.events.Onboarding
|
||||||
import com.tangem.tap.common.extensions.*
|
import com.tangem.tap.common.extensions.*
|
||||||
import com.tangem.tap.common.redux.AppDialog
|
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.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.BackupStartedSource
|
||||||
|
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletAction
|
||||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
|
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
|
||||||
import com.tangem.tap.mainScope
|
import com.tangem.tap.mainScope
|
||||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||||
|
|
@ -50,7 +48,7 @@ object OnboardingHelper {
|
||||||
if (!response.twinsIsTwinned()) {
|
if (!response.twinsIsTwinned()) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
onboardingManager.isActivationInProgress(cardId) ?: false
|
onboardingManager.isActivationInProgress(cardId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,7 +60,7 @@ object OnboardingHelper {
|
||||||
emptyWallets || activationInProgress || isNoBackup
|
emptyWallets || activationInProgress || isNoBackup
|
||||||
}
|
}
|
||||||
|
|
||||||
response.card.wallets.isNotEmpty() -> onboardingManager.isActivationInProgress(cardId) ?: false
|
response.card.wallets.isNotEmpty() -> onboardingManager.isActivationInProgress(cardId)
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -70,7 +68,7 @@ object OnboardingHelper {
|
||||||
fun whereToNavigate(scanResponse: ScanResponse): AppRoute {
|
fun whereToNavigate(scanResponse: ScanResponse): AppRoute {
|
||||||
val newOnboardingSupportTypes = scanResponse.productType == ProductType.Wallet2 ||
|
val newOnboardingSupportTypes = scanResponse.productType == ProductType.Wallet2 ||
|
||||||
scanResponse.productType == ProductType.Ring ||
|
scanResponse.productType == ProductType.Ring ||
|
||||||
scanResponse.productType == ProductType.Wallet
|
scanResponse.productType == ProductType.Wallet // AppRoute.OnboardingOther is also supported
|
||||||
if (store.inject(DaggerGraphState::onboardingV2FeatureToggles).isOnboardingV2Enabled &&
|
if (store.inject(DaggerGraphState::onboardingV2FeatureToggles).isOnboardingV2Enabled &&
|
||||||
newOnboardingSupportTypes
|
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 blockchain = walletManager.wallet.blockchain
|
||||||
val excludedBlockchains = store.inject(DaggerGraphState::excludedBlockchains)
|
val excludedBlockchains = store.inject(DaggerGraphState::excludedBlockchains)
|
||||||
|
|
||||||
|
|
@ -238,17 +236,10 @@ object OnboardingHelper {
|
||||||
Analytics.send(Onboarding.Topup.ButtonBuyCrypto(currencyType))
|
Analytics.send(Onboarding.Topup.ButtonBuyCrypto(currencyType))
|
||||||
|
|
||||||
scope.launch {
|
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 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) {
|
if (isRussia && !onrampFeatureToggles.isFeatureEnabled) {
|
||||||
val dialogData = AppDialog.RussianCardholdersWarningDialog.Data(topUpUrl)
|
val dialogData = AppDialog.RussianCardholdersWarningDialog.Data(topUpUrl)
|
||||||
store.dispatchDialogShow(AppDialog.RussianCardholdersWarningDialog(dialogData))
|
store.dispatchDialogShow(AppDialog.RussianCardholdersWarningDialog(dialogData))
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,6 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
|
||||||
OnboardingHelper.handleTopUpAction(
|
OnboardingHelper.handleTopUpAction(
|
||||||
walletManager = walletManager,
|
walletManager = walletManager,
|
||||||
scanResponse = scanResponse,
|
scanResponse = scanResponse,
|
||||||
globalState = globalState,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is OnboardingNoteAction.Done -> {
|
is OnboardingNoteAction.Done -> {
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,6 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
||||||
OnboardingHelper.handleTopUpAction(
|
OnboardingHelper.handleTopUpAction(
|
||||||
walletManager = walletManager,
|
walletManager = walletManager,
|
||||||
scanResponse = onboardingManager?.scanResponse ?: return,
|
scanResponse = onboardingManager?.scanResponse ?: return,
|
||||||
globalState = globalState,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TwinCardsAction.Done -> {
|
TwinCardsAction.Done -> {
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ import com.tangem.core.decompose.context.childByContext
|
||||||
import com.tangem.core.decompose.di.RootAppComponentContext
|
import com.tangem.core.decompose.di.RootAppComponentContext
|
||||||
import com.tangem.core.ui.UiDependencies
|
import com.tangem.core.ui.UiDependencies
|
||||||
import com.tangem.core.ui.extensions.setStatusBarColor
|
import com.tangem.core.ui.extensions.setStatusBarColor
|
||||||
import com.tangem.core.ui.message.EventMessageEffect
|
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.windowsize.rememberWindowSize
|
import com.tangem.core.ui.windowsize.rememberWindowSize
|
||||||
import com.tangem.datasource.utils.isNullOrEmpty
|
import com.tangem.datasource.utils.isNullOrEmpty
|
||||||
|
|
@ -301,10 +300,6 @@ class OnboardingWalletFragment :
|
||||||
windowSize = rememberWindowSize(activity = requireActivity()),
|
windowSize = rememberWindowSize(activity = requireActivity()),
|
||||||
) {
|
) {
|
||||||
onboardingManageTokensComponent?.Content(modifier = Modifier.fillMaxSize())
|
onboardingManageTokensComponent?.Content(modifier = Modifier.fillMaxSize())
|
||||||
EventMessageEffect(
|
|
||||||
messageHandler = uiDependencies.eventMessageHandler,
|
|
||||||
snackbarHostState = uiDependencies.globalSnackbarHostState,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.tangem.core.ui.R
|
import com.tangem.core.ui.R
|
||||||
import com.tangem.core.ui.components.BasicDialog
|
import com.tangem.core.ui.components.BasicDialog
|
||||||
import com.tangem.core.ui.components.DialogButtonUM
|
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.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.tap.features.saveWallet.ui.models.EnrollBiometricsDialog
|
import com.tangem.tap.features.saveWallet.ui.models.EnrollBiometricsDialog
|
||||||
|
|
@ -17,10 +17,10 @@ import com.tangem.tap.features.saveWallet.ui.models.EnrollBiometricsDialog
|
||||||
@Composable
|
@Composable
|
||||||
fun EnrollBiometricsDialogContent(dialog: EnrollBiometricsDialog) {
|
fun EnrollBiometricsDialogContent(dialog: EnrollBiometricsDialog) {
|
||||||
BasicDialog(
|
BasicDialog(
|
||||||
title = stringResource(R.string.save_user_wallet_agreement_enroll_biometrics_title),
|
title = stringResourceSafe(R.string.save_user_wallet_agreement_enroll_biometrics_title),
|
||||||
message = stringResource(R.string.save_user_wallet_agreement_enroll_biometrics_description),
|
message = stringResourceSafe(R.string.save_user_wallet_agreement_enroll_biometrics_description),
|
||||||
confirmButton = DialogButtonUM(
|
confirmButton = DialogButtonUM(
|
||||||
title = stringResource(R.string.common_enable),
|
title = stringResourceSafe(R.string.common_enable),
|
||||||
onClick = dialog.onEnroll,
|
onClick = dialog.onEnroll,
|
||||||
),
|
),
|
||||||
dismissButton = DialogButtonUM(
|
dismissButton = DialogButtonUM(
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.tangem.core.ui.components.*
|
import com.tangem.core.ui.components.*
|
||||||
import com.tangem.core.ui.components.atoms.Hand
|
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.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
|
|
@ -63,7 +63,7 @@ private fun Header(onCloseClick: () -> Unit) {
|
||||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||||
painter = painterResource(id = R.drawable.ic_close_24),
|
painter = painterResource(id = R.drawable.ic_close_24),
|
||||||
tint = TangemTheme.colors.icon.secondary,
|
tint = TangemTheme.colors.icon.secondary,
|
||||||
contentDescription = stringResource(id = R.string.common_cancel),
|
contentDescription = stringResourceSafe(id = R.string.common_cancel),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SpacerW8()
|
SpacerW8()
|
||||||
|
|
@ -85,7 +85,7 @@ private fun Title(modifier: Modifier = Modifier) {
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
Text(
|
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,
|
style = TangemTheme.typography.h2,
|
||||||
color = TangemTheme.colors.text.primary1,
|
color = TangemTheme.colors.text.primary1,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
|
|
@ -102,13 +102,13 @@ private fun Description(modifier: Modifier = Modifier) {
|
||||||
) {
|
) {
|
||||||
DescriptionItem(
|
DescriptionItem(
|
||||||
iconPainter = painterResource(id = R.drawable.ic_face_recognition_24),
|
iconPainter = painterResource(id = R.drawable.ic_face_recognition_24),
|
||||||
title = stringResource(id = R.string.save_user_wallet_agreement_access_title),
|
title = stringResourceSafe(id = R.string.save_user_wallet_agreement_access_title),
|
||||||
description = stringResource(id = R.string.save_user_wallet_agreement_access_description),
|
description = stringResourceSafe(id = R.string.save_user_wallet_agreement_access_description),
|
||||||
)
|
)
|
||||||
DescriptionItem(
|
DescriptionItem(
|
||||||
iconPainter = painterResource(id = R.drawable.ic_lock_24),
|
iconPainter = painterResource(id = R.drawable.ic_lock_24),
|
||||||
title = stringResource(id = R.string.save_user_wallet_agreement_code_title),
|
title = stringResourceSafe(id = R.string.save_user_wallet_agreement_code_title),
|
||||||
description = stringResource(id = R.string.save_user_wallet_agreement_code_description_biometrics),
|
description = stringResourceSafe(id = R.string.save_user_wallet_agreement_code_description_biometrics),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -125,12 +125,12 @@ private fun Footer(showProgress: Boolean, onSaveWalletClick: () -> Unit) {
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
showProgress = showProgress,
|
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,
|
onClick = onSaveWalletClick,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.fillMaxWidth(fraction = .7f),
|
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,
|
style = TangemTheme.typography.caption2,
|
||||||
color = TangemTheme.colors.text.tertiary,
|
color = TangemTheme.colors.text.tertiary,
|
||||||
textAlign = TextAlign.Center,
|
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.AppDialog
|
||||||
import com.tangem.tap.common.redux.AppState
|
import com.tangem.tap.common.redux.AppState
|
||||||
import com.tangem.tap.features.demo.DemoHelper
|
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.CurrencyExchangeManager
|
||||||
import com.tangem.tap.network.exchangeServices.buyErc20TestnetTokens
|
import com.tangem.tap.network.exchangeServices.buyErc20TestnetTokens
|
||||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||||
|
|
@ -26,7 +25,6 @@ import com.tangem.tap.store
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.rekotlin.Middleware
|
import org.rekotlin.Middleware
|
||||||
|
|
||||||
@Suppress("LargeClass")
|
|
||||||
@Deprecated("Will be removed soon")
|
@Deprecated("Will be removed soon")
|
||||||
object TradeCryptoMiddleware {
|
object TradeCryptoMiddleware {
|
||||||
|
|
||||||
|
|
@ -41,23 +39,22 @@ object TradeCryptoMiddleware {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
|
||||||
private fun handle(state: () -> AppState?, action: TradeCryptoAction) {
|
private fun handle(state: () -> AppState?, action: TradeCryptoAction) {
|
||||||
if (DemoHelper.tryHandle(state, action)) return
|
if (DemoHelper.tryHandle(state, action)) return
|
||||||
|
|
||||||
when (action) {
|
when (action) {
|
||||||
is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId)
|
is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId)
|
||||||
is TradeCryptoAction.Buy -> proceedBuyAction(state, action)
|
is TradeCryptoAction.Buy -> proceedBuyAction(action)
|
||||||
is TradeCryptoAction.Sell -> proceedSellAction(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
|
val isOnrampEnabled = store.inject(DaggerGraphState::onrampFeatureToggles).isFeatureEnabled
|
||||||
if (isOnrampEnabled) {
|
if (isOnrampEnabled) {
|
||||||
proceedWithOnramp(action.userWallet.walletId, action.cryptoCurrencyStatus.currency, action.source)
|
proceedWithOnramp(action.userWallet.walletId, action.cryptoCurrencyStatus.currency, action.source)
|
||||||
} else {
|
} 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
|
val networkAddress = action.cryptoCurrencyStatus.value.networkAddress
|
||||||
?.defaultAddress
|
?.defaultAddress
|
||||||
?.let(NetworkAddress.Address::value)
|
?.let(NetworkAddress.Address::value)
|
||||||
|
|
@ -92,15 +89,10 @@ object TradeCryptoMiddleware {
|
||||||
)
|
)
|
||||||
|
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val homeFeatureToggles = store.inject(DaggerGraphState::homeFeatureToggles)
|
val getUserCountryCodeUseCase = store.inject(DaggerGraphState::getUserCountryUseCase)
|
||||||
val onrampFeatureToggles = store.inject(DaggerGraphState::onrampFeatureToggles)
|
val onrampFeatureToggles = store.inject(DaggerGraphState::onrampFeatureToggles)
|
||||||
val isRussia = if (homeFeatureToggles.isMigrateUserCountryCodeEnabled) {
|
|
||||||
val getUserCountryCodeUseCase = store.inject(DaggerGraphState::getUserCountryUseCase)
|
|
||||||
|
|
||||||
getUserCountryCodeUseCase().isRight { it is UserCountry.Russia }
|
val isRussia = getUserCountryCodeUseCase().isRight { it is UserCountry.Russia }
|
||||||
} else {
|
|
||||||
state()?.globalState?.userCountryCode == RUSSIA_COUNTRY_CODE
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.checkUserLocation && isRussia && !onrampFeatureToggles.isFeatureEnabled) {
|
if (action.checkUserLocation && isRussia && !onrampFeatureToggles.isFeatureEnabled) {
|
||||||
val dialogData = topUrl?.let {
|
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
|
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.details.ui.cardsettings.TextReference
|
||||||
import com.tangem.tap.features.welcome.ui.model.WarningModel
|
import com.tangem.tap.features.welcome.ui.model.WarningModel
|
||||||
|
|
||||||
@Immutable
|
|
||||||
internal data class WelcomeScreenState(
|
internal data class WelcomeScreenState(
|
||||||
|
val onPopBack: () -> Unit = {},
|
||||||
val showUnlockWithBiometricsProgress: Boolean = false,
|
val showUnlockWithBiometricsProgress: Boolean = false,
|
||||||
val showUnlockWithCardProgress: Boolean = false,
|
val showUnlockWithCardProgress: Boolean = false,
|
||||||
val warning: WarningModel? = null,
|
val warning: WarningModel? = null,
|
||||||
val error: TextReference? = 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() {
|
private fun initGlobalState() {
|
||||||
store.dispatch(GlobalAction.RestoreAppCurrency)
|
store.dispatch(GlobalAction.RestoreAppCurrency)
|
||||||
store.dispatch(GlobalAction.ExchangeManager.Init)
|
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.foundation.layout.Column
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.tangem.core.ui.components.BasicDialog
|
import com.tangem.core.ui.components.BasicDialog
|
||||||
import com.tangem.core.ui.components.DialogButtonUM
|
import com.tangem.core.ui.components.DialogButtonUM
|
||||||
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.tap.features.welcome.ui.model.WarningModel
|
import com.tangem.tap.features.welcome.ui.model.WarningModel
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
|
|
@ -18,8 +18,8 @@ internal fun WarningDialog(warning: WarningModel?) {
|
||||||
null -> Unit
|
null -> Unit
|
||||||
is WarningModel.BiometricsLockoutWarning -> {
|
is WarningModel.BiometricsLockoutWarning -> {
|
||||||
BasicDialog(
|
BasicDialog(
|
||||||
title = stringResource(id = R.string.biometric_lockout_warning_title),
|
title = stringResourceSafe(id = R.string.biometric_lockout_warning_title),
|
||||||
message = stringResource(
|
message = stringResourceSafe(
|
||||||
id = if (warning.isPermanent) {
|
id = if (warning.isPermanent) {
|
||||||
R.string.biometric_lockout_permanent_warning_description
|
R.string.biometric_lockout_permanent_warning_description
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -28,30 +28,30 @@ internal fun WarningDialog(warning: WarningModel?) {
|
||||||
),
|
),
|
||||||
onDismissDialog = warning.onDismiss,
|
onDismissDialog = warning.onDismiss,
|
||||||
confirmButton = DialogButtonUM(
|
confirmButton = DialogButtonUM(
|
||||||
title = stringResource(id = R.string.common_ok),
|
title = stringResourceSafe(id = R.string.common_ok),
|
||||||
onClick = warning.onDismiss,
|
onClick = warning.onDismiss,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is WarningModel.KeyInvalidatedWarning -> {
|
is WarningModel.KeyInvalidatedWarning -> {
|
||||||
BasicDialog(
|
BasicDialog(
|
||||||
title = stringResource(id = R.string.common_attention),
|
title = stringResourceSafe(id = R.string.common_attention),
|
||||||
message = stringResource(id = R.string.key_invalidated_warning_description),
|
message = stringResourceSafe(id = R.string.key_invalidated_warning_description),
|
||||||
onDismissDialog = warning.onDismiss,
|
onDismissDialog = warning.onDismiss,
|
||||||
confirmButton = DialogButtonUM(
|
confirmButton = DialogButtonUM(
|
||||||
title = stringResource(id = R.string.common_ok),
|
title = stringResourceSafe(id = R.string.common_ok),
|
||||||
onClick = warning.onDismiss,
|
onClick = warning.onDismiss,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is WarningModel.BiometricsDisabledWarning -> {
|
is WarningModel.BiometricsDisabledWarning -> {
|
||||||
BasicDialog(
|
BasicDialog(
|
||||||
title = stringResource(id = R.string.common_warning),
|
title = stringResourceSafe(id = R.string.common_warning),
|
||||||
message = stringResource(id = R.string.biometric_unavailable_warning),
|
message = stringResourceSafe(id = R.string.biometric_unavailable_warning),
|
||||||
onDismissDialog = warning.onDismiss,
|
onDismissDialog = warning.onDismiss,
|
||||||
isDismissable = false,
|
isDismissable = false,
|
||||||
confirmButton = DialogButtonUM(
|
confirmButton = DialogButtonUM(
|
||||||
title = stringResource(id = R.string.common_ok),
|
title = stringResourceSafe(id = R.string.common_ok),
|
||||||
onClick = warning.onDismiss,
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.tangem.core.ui.components.*
|
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.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.wallet.R
|
import com.tangem.wallet.R
|
||||||
|
|
@ -24,8 +24,12 @@ internal fun WelcomeScreenContent(
|
||||||
showScanCardProgress: Boolean,
|
showScanCardProgress: Boolean,
|
||||||
onUnlockClick: () -> Unit,
|
onUnlockClick: () -> Unit,
|
||||||
onScanCardClick: () -> Unit,
|
onScanCardClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
SpacerHMax()
|
SpacerHMax()
|
||||||
Icon(
|
Icon(
|
||||||
modifier = Modifier.size(TangemTheme.dimens.size96),
|
modifier = Modifier.size(TangemTheme.dimens.size96),
|
||||||
|
|
@ -38,7 +42,7 @@ internal fun WelcomeScreenContent(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
text = stringResource(R.string.welcome_unlock_title),
|
text = stringResourceSafe(R.string.welcome_unlock_title),
|
||||||
style = TangemTheme.typography.h2,
|
style = TangemTheme.typography.h2,
|
||||||
color = TangemTheme.colors.text.primary1,
|
color = TangemTheme.colors.text.primary1,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
|
|
@ -48,9 +52,9 @@ internal fun WelcomeScreenContent(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = TangemTheme.dimens.spacing44)
|
.padding(horizontal = TangemTheme.dimens.spacing44)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
text = stringResource(
|
text = stringResourceSafe(
|
||||||
id = R.string.welcome_unlock_description,
|
id = R.string.welcome_unlock_description,
|
||||||
stringResource(id = R.string.common_biometric_authentication),
|
stringResourceSafe(id = R.string.common_biometric_authentication),
|
||||||
),
|
),
|
||||||
style = TangemTheme.typography.body1,
|
style = TangemTheme.typography.body1,
|
||||||
color = TangemTheme.colors.text.secondary,
|
color = TangemTheme.colors.text.secondary,
|
||||||
|
|
@ -61,9 +65,9 @@ internal fun WelcomeScreenContent(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
text = stringResource(
|
text = stringResourceSafe(
|
||||||
id = R.string.welcome_unlock,
|
id = R.string.welcome_unlock,
|
||||||
stringResource(id = R.string.common_biometrics),
|
stringResourceSafe(id = R.string.common_biometrics),
|
||||||
),
|
),
|
||||||
showProgress = showUnlockProgress,
|
showProgress = showUnlockProgress,
|
||||||
onClick = onUnlockClick,
|
onClick = onUnlockClick,
|
||||||
|
|
@ -73,7 +77,7 @@ internal fun WelcomeScreenContent(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
text = stringResource(R.string.welcome_unlock_card),
|
text = stringResourceSafe(R.string.welcome_unlock_card),
|
||||||
showProgress = showScanCardProgress,
|
showProgress = showScanCardProgress,
|
||||||
iconResId = R.drawable.ic_tangem_24,
|
iconResId = R.drawable.ic_tangem_24,
|
||||||
onClick = onScanCardClick,
|
onClick = onScanCardClick,
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ internal val Blockchain.mercuryoNetwork: String?
|
||||||
Blockchain.PolygonZkEVM -> null
|
Blockchain.PolygonZkEVM -> null
|
||||||
Blockchain.PolygonZkEVMTestnet -> null
|
Blockchain.PolygonZkEVMTestnet -> null
|
||||||
Blockchain.Radiant -> null
|
Blockchain.Radiant -> null
|
||||||
|
Blockchain.Fact0rn -> null
|
||||||
Blockchain.BaseTestnet -> null
|
Blockchain.BaseTestnet -> null
|
||||||
Blockchain.Moonriver -> null
|
Blockchain.Moonriver -> null
|
||||||
Blockchain.MoonriverTestnet -> null
|
Blockchain.MoonriverTestnet -> null
|
||||||
|
|
@ -140,6 +141,8 @@ internal val Blockchain.mercuryoNetwork: String?
|
||||||
Blockchain.CoreTestnet -> null
|
Blockchain.CoreTestnet -> null
|
||||||
Blockchain.Chiliz -> null
|
Blockchain.Chiliz -> null
|
||||||
Blockchain.ChilizTestnet -> null
|
Blockchain.ChilizTestnet -> null
|
||||||
|
Blockchain.VanarChain -> null
|
||||||
|
Blockchain.VanarChainTestnet -> null
|
||||||
Blockchain.Unknown -> null
|
Blockchain.Unknown -> null
|
||||||
Blockchain.Xodex -> null
|
Blockchain.Xodex -> null
|
||||||
Blockchain.Canxium -> null
|
Blockchain.Canxium -> null
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ internal val Blockchain.moonPaySupportedCurrency: MoonPaySupportedCurrency?
|
||||||
PolygonZkEVM -> null
|
PolygonZkEVM -> null
|
||||||
PolygonZkEVMTestnet -> null
|
PolygonZkEVMTestnet -> null
|
||||||
Radiant -> null
|
Radiant -> null
|
||||||
|
Fact0rn -> null
|
||||||
BaseTestnet -> null
|
BaseTestnet -> null
|
||||||
Moonriver -> null
|
Moonriver -> null
|
||||||
MoonriverTestnet -> null
|
MoonriverTestnet -> null
|
||||||
|
|
@ -145,4 +146,6 @@ internal val Blockchain.moonPaySupportedCurrency: MoonPaySupportedCurrency?
|
||||||
Xodex -> null
|
Xodex -> null
|
||||||
Canxium -> null
|
Canxium -> null
|
||||||
Clore -> null
|
Clore -> null
|
||||||
|
VanarChain -> null
|
||||||
|
VanarChainTestnet -> null
|
||||||
}
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||||
import com.tangem.common.routing.AppRouter
|
import com.tangem.common.routing.AppRouter
|
||||||
import com.tangem.core.navigation.share.ShareManager
|
import com.tangem.core.navigation.share.ShareManager
|
||||||
import com.tangem.core.navigation.url.UrlOpener
|
import com.tangem.core.navigation.url.UrlOpener
|
||||||
|
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||||
import com.tangem.datasource.local.config.issuers.IssuersConfigStorage
|
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.scanCard.CardScanningFeatureToggles
|
||||||
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
|
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
|
||||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||||
import com.tangem.tap.features.home.featuretoggles.HomeFeatureToggles
|
|
||||||
import com.tangem.tap.proxy.AppStateHolder
|
import com.tangem.tap.proxy.AppStateHolder
|
||||||
import org.rekotlin.StateType
|
import org.rekotlin.StateType
|
||||||
|
|
||||||
|
|
@ -69,7 +69,6 @@ data class DaggerGraphState(
|
||||||
val appRouter: AppRouter? = null,
|
val appRouter: AppRouter? = null,
|
||||||
val pushNotificationsRouter: PushNotificationsRouter? = null,
|
val pushNotificationsRouter: PushNotificationsRouter? = null,
|
||||||
val transactionSignerFactory: TransactionSignerFactory? = null,
|
val transactionSignerFactory: TransactionSignerFactory? = null,
|
||||||
val homeFeatureToggles: HomeFeatureToggles? = null,
|
|
||||||
val getUserCountryUseCase: GetUserCountryUseCase? = null,
|
val getUserCountryUseCase: GetUserCountryUseCase? = null,
|
||||||
val onrampFeatureToggles: OnrampFeatureToggles? = null,
|
val onrampFeatureToggles: OnrampFeatureToggles? = null,
|
||||||
val environmentConfigStorage: EnvironmentConfigStorage? = null,
|
val environmentConfigStorage: EnvironmentConfigStorage? = null,
|
||||||
|
|
@ -77,4 +76,5 @@ data class DaggerGraphState(
|
||||||
val onboardingRepository: OnboardingRepository? = null,
|
val onboardingRepository: OnboardingRepository? = null,
|
||||||
val excludedBlockchains: ExcludedBlockchains? = null,
|
val excludedBlockchains: ExcludedBlockchains? = null,
|
||||||
val appPreferencesStore: AppPreferencesStore? = null,
|
val appPreferencesStore: AppPreferencesStore? = null,
|
||||||
|
val clipboardManager: ClipboardManager? = null,
|
||||||
) : StateType
|
) : StateType
|
||||||
71
app/src/main/java/com/tangem/tap/routing/RootContent.kt
Normal file
71
app/src/main/java/com/tangem/tap/routing/RootContent.kt
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,22 +1,28 @@
|
||||||
package com.tangem.tap.routing
|
package com.tangem.tap.routing.component
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
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.Value
|
||||||
import com.tangem.common.routing.AppRoute
|
import com.tangem.common.routing.AppRoute
|
||||||
import com.tangem.core.decompose.context.AppComponentContext
|
import com.tangem.core.decompose.context.AppComponentContext
|
||||||
import com.tangem.core.decompose.navigation.Router
|
import com.tangem.core.decompose.navigation.Router
|
||||||
|
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
|
|
||||||
internal interface RoutingComponent {
|
internal interface RoutingComponent : ComposableContentComponent {
|
||||||
|
|
||||||
|
// TODO: Remove after full navigation refactoring
|
||||||
val router: Router
|
val router: Router
|
||||||
|
|
||||||
|
// TODO: Remove after full navigation refactoring
|
||||||
val stack: Value<ChildStack<AppRoute, Child>>
|
val stack: Value<ChildStack<AppRoute, Child>>
|
||||||
|
|
||||||
|
@Immutable
|
||||||
sealed class Child {
|
sealed class Child {
|
||||||
|
|
||||||
|
// TODO: Remove and find initial intent in RoutingComponent: [REDACTED_JIRA]
|
||||||
data object Initial : Child()
|
data object Initial : Child()
|
||||||
|
|
||||||
data class LegacyFragment(
|
data class LegacyFragment(
|
||||||
|
|
@ -25,6 +31,10 @@ internal interface RoutingComponent {
|
||||||
) : Child()
|
) : Child()
|
||||||
|
|
||||||
data class LegacyIntent(val intent: Intent) : Child()
|
data class LegacyIntent(val intent: Intent) : Child()
|
||||||
|
|
||||||
|
data class ComposableComponent(
|
||||||
|
val component: ComposableContentComponent,
|
||||||
|
) : Child()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
package com.tangem.tap.routing.component.impl
|
||||||
|
|
||||||
|
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 -> SnackbarMessage.Duration.Short
|
||||||
|
Snackbar.LENGTH_LONG -> SnackbarMessage.Duration.Long
|
||||||
|
Snackbar.LENGTH_INDEFINITE -> SnackbarMessage.Duration.Indefinite
|
||||||
|
else -> SnackbarMessage.Duration.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.twins.ui.OnboardingTwinsFragment
|
||||||
import com.tangem.tap.features.onboarding.products.wallet.ui.OnboardingWalletFragment
|
import com.tangem.tap.features.onboarding.products.wallet.ui.OnboardingWalletFragment
|
||||||
import com.tangem.tap.features.saveWallet.ui.SaveWalletBottomSheetFragment
|
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.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 com.tangem.utils.Provider
|
||||||
import dagger.hilt.android.scopes.ActivityScoped
|
import dagger.hilt.android.scopes.ActivityScoped
|
||||||
import java.util.WeakHashMap
|
import java.util.WeakHashMap
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ActivityScoped
|
@ActivityScoped
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList", "LargeClass")
|
||||||
internal class ChildFactory @Inject constructor(
|
internal class ChildFactory @Inject constructor(
|
||||||
private val detailsComponentFactory: DetailsComponent.Factory,
|
private val detailsComponentFactory: DetailsComponent.Factory,
|
||||||
private val walletSettingsComponentFactory: WalletSettingsComponent.Factory,
|
private val walletSettingsComponentFactory: WalletSettingsComponent.Factory,
|
||||||
|
|
@ -54,6 +56,7 @@ internal class ChildFactory @Inject constructor(
|
||||||
private val sellCryptoComponentFactory: SellCryptoComponent.Factory,
|
private val sellCryptoComponentFactory: SellCryptoComponent.Factory,
|
||||||
private val swapSelectTokensComponentFactory: SwapSelectTokensComponent.Factory,
|
private val swapSelectTokensComponentFactory: SwapSelectTokensComponent.Factory,
|
||||||
private val onboardingEntryComponentFactory: OnboardingEntryComponent.Factory,
|
private val onboardingEntryComponentFactory: OnboardingEntryComponent.Factory,
|
||||||
|
private val welcomeComponentFactory: WelcomeComponent.Factory,
|
||||||
private val sendRouter: SendRouter,
|
private val sendRouter: SendRouter,
|
||||||
private val tokenDetailsRouter: TokenDetailsRouter,
|
private val tokenDetailsRouter: TokenDetailsRouter,
|
||||||
private val walletRouter: WalletRouter,
|
private val walletRouter: WalletRouter,
|
||||||
|
|
@ -61,12 +64,174 @@ internal class ChildFactory @Inject constructor(
|
||||||
private val stakingRouter: StakingRouter,
|
private val stakingRouter: StakingRouter,
|
||||||
private val testerRouter: TesterRouter,
|
private val testerRouter: TesterRouter,
|
||||||
private val pushNotificationRouter: PushNotificationsRouter,
|
private val pushNotificationRouter: PushNotificationsRouter,
|
||||||
|
private val routingFeatureToggles: RoutingFeatureToggles,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
|
||||||
fun createChild(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
|
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)
|
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) {
|
return when (route) {
|
||||||
is AppRoute.Initial -> {
|
is AppRoute.Initial -> {
|
||||||
Child.Initial
|
Child.Initial
|
||||||
|
|
@ -248,6 +413,7 @@ internal class ChildFactory @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
fun doOnDestroy() {
|
fun doOnDestroy() {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import com.tangem.core.decompose.context.AppComponentContext
|
||||||
import com.tangem.core.decompose.factory.ComponentFactory
|
import com.tangem.core.decompose.factory.ComponentFactory
|
||||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||||
import com.tangem.tap.DecomposeFragment
|
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
|
import com.tangem.utils.Provider
|
||||||
|
|
||||||
internal fun AppRoute.asFragmentChild(fragmentProvider: Provider<Fragment>): Child {
|
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)
|
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))
|
||||||
}
|
}
|
||||||
|
|
@ -6,4 +6,10 @@
|
||||||
android:background="@color/background_primary"
|
android:background="@color/background_primary"
|
||||||
android:filterTouchesWhenObscured="true">
|
android:filterTouchesWhenObscured="true">
|
||||||
|
|
||||||
|
<androidx.compose.ui.platform.ComposeView
|
||||||
|
android:id="@+id/compose_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentBottom="true" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
13
ci_resources/build_ci_gradle.properties
Normal file
13
ci_resources/build_ci_gradle.properties
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
org.gradle.jvmargs=-Xmx7g -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
|
org.gradle.parallel=true
|
||||||
|
org.gradle.workers.max=8
|
||||||
|
org.gradle.daemon=false
|
||||||
|
org.gradle.unsafe.configuration-cache=false
|
||||||
|
org.gradle.console=plain
|
||||||
|
org.gradle.caching=false
|
||||||
|
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.nonTransitiveRClass=false
|
||||||
|
android.aapt2FromMavenOverride=/opt/android-sdk/build-tools/34.0.0/aapt2
|
||||||
|
|
||||||
|
kotlin.incremental=true
|
||||||
13
ci_resources/tests_8gb_ram_ci_gradle.properties
Normal file
13
ci_resources/tests_8gb_ram_ci_gradle.properties
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
org.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=512m -XX:+UseParallelGC -XX:ParallelGCThreads=3 -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
|
org.gradle.parallel=true
|
||||||
|
org.gradle.workers.max=3
|
||||||
|
org.gradle.daemon=false
|
||||||
|
org.gradle.unsafe.configuration-cache=false
|
||||||
|
org.gradle.console=plain
|
||||||
|
org.gradle.caching=false
|
||||||
|
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.nonTransitiveRClass=false
|
||||||
|
android.aapt2FromMavenOverride=/opt/android-sdk/build-tools/34.0.0/aapt2
|
||||||
|
|
||||||
|
kotlin.incremental=false
|
||||||
BIN
ci_resources/x86_64_libs/ld-linux-x86-64.so.2
Executable file
BIN
ci_resources/x86_64_libs/ld-linux-x86-64.so.2
Executable file
Binary file not shown.
BIN
ci_resources/x86_64_libs/libc.so.6
Executable file
BIN
ci_resources/x86_64_libs/libc.so.6
Executable file
Binary file not shown.
BIN
ci_resources/x86_64_libs/libdl.so.2
Normal file
BIN
ci_resources/x86_64_libs/libdl.so.2
Normal file
Binary file not shown.
BIN
ci_resources/x86_64_libs/libgcc_s.so.1
Normal file
BIN
ci_resources/x86_64_libs/libgcc_s.so.1
Normal file
Binary file not shown.
BIN
ci_resources/x86_64_libs/libm.so.6
Normal file
BIN
ci_resources/x86_64_libs/libm.so.6
Normal file
Binary file not shown.
BIN
ci_resources/x86_64_libs/libpthread.so.0
Normal file
BIN
ci_resources/x86_64_libs/libpthread.so.0
Normal file
Binary file not shown.
BIN
ci_resources/x86_64_libs/librt.so.1
Normal file
BIN
ci_resources/x86_64_libs/librt.so.1
Normal file
Binary file not shown.
|
|
@ -1,7 +1,28 @@
|
||||||
plugins {
|
plugins {
|
||||||
alias(deps.plugins.kotlin.jvm)
|
alias(deps.plugins.android.library)
|
||||||
|
alias(deps.plugins.kotlin.android)
|
||||||
id("configuration")
|
id("configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.tangem.common"
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation(projects.core.utils)
|
implementation(projects.core.utils)
|
||||||
|
|
||||||
|
// region Firebase libraries
|
||||||
|
implementation(platform(deps.firebase.bom))
|
||||||
|
implementation(deps.firebase.analytics)
|
||||||
|
implementation(deps.firebase.crashlytics)
|
||||||
|
implementation(deps.firebase.messaging)
|
||||||
|
// end
|
||||||
|
|
||||||
|
implementation(deps.timber)
|
||||||
|
|
||||||
|
implementation(deps.arrow.core)
|
||||||
|
|
||||||
|
implementation(deps.test.junit)
|
||||||
|
implementation(deps.test.truth)
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue