Updated on 2026-08-14
This commit is contained in:
commit
8a962212d5
159 changed files with 4492 additions and 3415 deletions
|
|
@ -84,8 +84,8 @@ private fun SegmentSeedBlock(state: MultiWalletSeedPhraseUM.GenerateSeedPhrase,
|
|||
Text(
|
||||
text = pluralStringResourceSafe(
|
||||
id = R.plurals.onboarding_seed_generate_words_count,
|
||||
count = state.option.length,
|
||||
state.option.length,
|
||||
count = it.length,
|
||||
it.length,
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing10)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
/** Domain models */
|
||||
implementation(projects.domain.qrScanning.models)
|
||||
|
||||
/** Core */
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
/** AndroidX */
|
||||
implementation(deps.androidx.fragment.ktx)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.feature.qrscanning
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
|
||||
interface QrScanningComponent : ComposableContentComponent {
|
||||
|
||||
data class Params(
|
||||
val source: SourceType,
|
||||
val networkName: String? = null,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, QrScanningComponent>
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.feature.qrscanning
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
interface QrScanningRouter {
|
||||
|
||||
fun getEntryFragment(): Fragment
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ dependencies {
|
|||
|
||||
/** Core */
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.navigation)
|
||||
implementation(projects.common.routing)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,142 @@
|
|||
package com.tangem.feature.qrscanning
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.compose.LifecycleEventEffect
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.essenty.lifecycle.doOnDestroy
|
||||
import com.google.mlkit.vision.common.InputImage
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.components.SystemBarsIconsDisposable
|
||||
import com.tangem.feature.qrscanning.inner.MLKitBarcodeAnalyzer
|
||||
import com.tangem.feature.qrscanning.model.QrScanningModel
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningContent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.delay
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class DefaultQrScanningComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted params: QrScanningComponent.Params,
|
||||
) : QrScanningComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: QrScanningModel = getOrCreateModel(params)
|
||||
|
||||
private val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
|
||||
// Camera requires its own analyzer instance due to flow of frames needed to be analyzed.
|
||||
// Each new frame can cancel previous analysis e.i. image from the gallery can be skipped.
|
||||
private val cameraAnalyzer: MLKitBarcodeAnalyzer by lazy(LazyThreadSafetyMode.NONE) {
|
||||
MLKitBarcodeAnalyzer(model::onQrScanned)
|
||||
}
|
||||
private val analyzer: MLKitBarcodeAnalyzer by lazy(LazyThreadSafetyMode.NONE) {
|
||||
MLKitBarcodeAnalyzer(model::onQrScanned)
|
||||
}
|
||||
|
||||
init {
|
||||
lifecycle.doOnDestroy { cameraExecutor.shutdown() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val cameraPermissionLauncher =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
if (isGranted.not()) {
|
||||
model.onCameraDeniedState()
|
||||
}
|
||||
}
|
||||
|
||||
val galleryLauncher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
|
||||
val selectedImage = it ?: Uri.EMPTY
|
||||
if (selectedImage != Uri.EMPTY) {
|
||||
val mimeType = context.contentResolver.getType(selectedImage)
|
||||
if (mimeType.isImageMimeType()) {
|
||||
try {
|
||||
val image = InputImage.fromFilePath(context, selectedImage)
|
||||
analyzer.analyze(image)
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e, "Unable to get image $selectedImage from gallery")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
model.launchGallery.collect {
|
||||
galleryLauncher.launch(GALLERY_IMAGE_FILTER)
|
||||
delay(timeMillis = 2000)
|
||||
}
|
||||
}
|
||||
|
||||
LifecycleEventEffect(
|
||||
event = Lifecycle.Event.ON_CREATE,
|
||||
) {
|
||||
if (
|
||||
ContextCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.CAMERA,
|
||||
) == PackageManager.PERMISSION_DENIED
|
||||
) {
|
||||
cameraPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||
}
|
||||
}
|
||||
|
||||
LifecycleEventEffect(
|
||||
event = Lifecycle.Event.ON_RESUME,
|
||||
) {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.CAMERA,
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
model.onDismissBottomSheetState()
|
||||
}
|
||||
}
|
||||
|
||||
ScreenContent(modifier)
|
||||
}
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
@Composable
|
||||
private fun ScreenContent(modifier: Modifier = Modifier) {
|
||||
SystemBarsIconsDisposable(darkIcons = false)
|
||||
|
||||
QrScanningContent(
|
||||
executor = { cameraExecutor },
|
||||
analyzer = { cameraAnalyzer },
|
||||
uiState = model.uiState.collectAsStateWithLifecycle().value,
|
||||
)
|
||||
}
|
||||
|
||||
private fun String?.isImageMimeType() = this?.startsWith(prefix = "$IMAGE_MIME_TYPE/") == true
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : QrScanningComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: QrScanningComponent.Params,
|
||||
): DefaultQrScanningComponent
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val IMAGE_MIME_TYPE = "image"
|
||||
private const val GALLERY_IMAGE_FILTER = "$IMAGE_MIME_TYPE/*"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
package com.tangem.feature.qrscanning
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.google.mlkit.vision.common.InputImage
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.components.SystemBarsIconsDisposable
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.feature.qrscanning.inner.MLKitBarcodeAnalyzer
|
||||
import com.tangem.feature.qrscanning.navigation.QrScanningInnerRouter
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningContent
|
||||
import com.tangem.feature.qrscanning.viewmodel.QrScanningViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@AndroidEntryPoint
|
||||
internal class QrScanningFragment : ComposeFragment() {
|
||||
|
||||
@Inject
|
||||
override lateinit var uiDependencies: UiDependencies
|
||||
|
||||
@Inject
|
||||
lateinit var router: QrScanningRouter
|
||||
|
||||
private val innerRouter: QrScanningInnerRouter
|
||||
get() = requireNotNull(router as? QrScanningInnerRouter) {
|
||||
"innerRouter should be instance of QrScanningInnerRouter"
|
||||
}
|
||||
|
||||
private val viewModel by viewModels<QrScanningViewModel>()
|
||||
|
||||
private var cameraExecutor: ExecutorService by Delegates.notNull()
|
||||
// Camera requires its own analyzer instance due to flow of frames needed to be analyzed.
|
||||
// Each new frame can cancel previous analysis e.i. image from the gallery can be skipped.
|
||||
private val cameraAnalyzer: MLKitBarcodeAnalyzer by lazy(LazyThreadSafetyMode.NONE) {
|
||||
MLKitBarcodeAnalyzer(viewModel::onQrScanned)
|
||||
}
|
||||
private val analyzer: MLKitBarcodeAnalyzer by lazy(LazyThreadSafetyMode.NONE) {
|
||||
MLKitBarcodeAnalyzer(viewModel::onQrScanned)
|
||||
}
|
||||
|
||||
private val cameraPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||
if (!it) viewModel.onCameraDeniedState()
|
||||
}
|
||||
private val galleryLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) {
|
||||
val selectedImage = it ?: Uri.EMPTY
|
||||
if (selectedImage != Uri.EMPTY) {
|
||||
val mimeType = requireContext().contentResolver.getType(selectedImage)
|
||||
if (mimeType.isImageMimeType()) {
|
||||
try {
|
||||
val image = InputImage.fromFilePath(requireContext(), selectedImage)
|
||||
analyzer.analyze(image)
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e, "Unable to get image $selectedImage from gallery")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
viewModel.setRouter(innerRouter)
|
||||
cameraExecutor = Executors.newSingleThreadExecutor()
|
||||
requestCameraPermission()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
viewModel.launchGalleryEvent
|
||||
.collect {
|
||||
galleryLauncher.launch(GALLERY_IMAGE_FILTER)
|
||||
delay(timeMillis = 2000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
checkPermissionGranted()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
cameraPermissionLauncher.unregister()
|
||||
cameraExecutor.shutdown()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
SystemBarsIconsDisposable(darkIcons = false)
|
||||
|
||||
QrScanningContent(
|
||||
executor = { cameraExecutor },
|
||||
analyzer = { cameraAnalyzer },
|
||||
uiState = viewModel.uiState.collectAsStateWithLifecycle().value,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for requesting permission if there isn't one.
|
||||
*/
|
||||
private fun requestCameraPermission() {
|
||||
if (
|
||||
ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.CAMERA,
|
||||
) == PackageManager.PERMISSION_DENIED
|
||||
) {
|
||||
cameraPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for checking if permission was granted after user opened Settings screen.
|
||||
* If permission was granted dismiss bottom sheet.
|
||||
*/
|
||||
private fun checkPermissionGranted() {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.CAMERA,
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
viewModel.onDismissBottomSheetState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun String?.isImageMimeType() = this?.startsWith(prefix = "$IMAGE_MIME_TYPE/") == true
|
||||
|
||||
companion object {
|
||||
|
||||
private const val IMAGE_MIME_TYPE = "image"
|
||||
private const val GALLERY_IMAGE_FILTER = "$IMAGE_MIME_TYPE/*"
|
||||
|
||||
fun create() = QrScanningFragment()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.feature.qrscanning.di
|
||||
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.feature.qrscanning.DefaultQrScanningComponent
|
||||
import com.tangem.feature.qrscanning.QrScanningComponent
|
||||
import com.tangem.feature.qrscanning.model.QrScanningModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface QrScanningFeatureModule {
|
||||
|
||||
@Binds
|
||||
fun bindComponentFactory(impl: DefaultQrScanningComponent.Factory): QrScanningComponent.Factory
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(QrScanningModel::class)
|
||||
fun bindModel(model: QrScanningModel): Model
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package com.tangem.feature.qrscanning.di
|
||||
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.feature.qrscanning.navigation.DefaultQrScanningRouter
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ActivityComponent
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
|
||||
@Module
|
||||
@InstallIn(ActivityComponent::class)
|
||||
internal object QrScanningRouterModule {
|
||||
|
||||
@Provides
|
||||
@ActivityScoped
|
||||
fun provideQrScanRouter(appRouter: AppRouter): QrScanningRouter {
|
||||
return DefaultQrScanningRouter(appRouter)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.feature.qrscanning.model
|
||||
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
internal interface QrScanningClickIntents {
|
||||
|
||||
val launchGallery: SharedFlow<Unit>
|
||||
|
||||
fun onBackClick()
|
||||
|
||||
fun onQrScanned(qrCode: String)
|
||||
|
||||
fun onGalleryClicked()
|
||||
|
||||
fun onSettingsClick()
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package com.tangem.feature.qrscanning.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.settings.SettingsManager
|
||||
import com.tangem.data.card.sdk.CardSdkProvider
|
||||
import com.tangem.domain.qrscanning.usecases.EmitQrScannedEventUseCase
|
||||
import com.tangem.feature.qrscanning.QrScanningComponent
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningState
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningStateController
|
||||
import com.tangem.feature.qrscanning.presentation.transformers.DismissBottomSheetTransformer
|
||||
import com.tangem.feature.qrscanning.presentation.transformers.InitializeQrScanningStateTransformer
|
||||
import com.tangem.feature.qrscanning.presentation.transformers.ShowCameraDeniedBottomSheetTransformer
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
internal class QrScanningModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
paramsContainer: ParamsContainer,
|
||||
private val stateHolder: QrScanningStateController,
|
||||
private val cardSdkProvider: CardSdkProvider,
|
||||
private val emitQrScannedEventUseCase: EmitQrScannedEventUseCase,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val appRouter: AppRouter,
|
||||
) : Model(), QrScanningClickIntents {
|
||||
|
||||
private val params = paramsContainer.require<QrScanningComponent.Params>()
|
||||
val uiState: StateFlow<QrScanningState> = stateHolder.uiState
|
||||
private var isScanned = false
|
||||
|
||||
override val launchGallery = MutableSharedFlow<Unit>(
|
||||
extraBufferCapacity = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_LATEST,
|
||||
)
|
||||
|
||||
init {
|
||||
// samsung for some reason disables reader mode, and then it works unstable
|
||||
// to prevent this disable ir manually before scan QR
|
||||
cardSdkProvider.sdk.forceDisableReaderMode()
|
||||
stateHolder.update(InitializeQrScanningStateTransformer(this, params.source, params.networkName))
|
||||
}
|
||||
|
||||
fun onCameraDeniedState() {
|
||||
stateHolder.update(ShowCameraDeniedBottomSheetTransformer(this))
|
||||
}
|
||||
|
||||
fun onDismissBottomSheetState() {
|
||||
stateHolder.update(DismissBottomSheetTransformer())
|
||||
}
|
||||
|
||||
override fun onBackClick() = appRouter.pop()
|
||||
|
||||
override fun onQrScanned(qrCode: String) {
|
||||
if (qrCode.isNotBlank()) {
|
||||
modelScope.launch(dispatchers.mainImmediate) {
|
||||
emitQrScannedEventUseCase.invoke(params.source, qrCode)
|
||||
}
|
||||
if (!isScanned) {
|
||||
appRouter.pop()
|
||||
isScanned = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGalleryClicked() {
|
||||
launchGallery.tryEmit(Unit)
|
||||
if (stateHolder.value.bottomSheetConfig != null) {
|
||||
stateHolder.update(DismissBottomSheetTransformer())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSettingsClick() {
|
||||
settingsManager.openAppSettings()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
// don't forget enable reader mode after scan complete
|
||||
cardSdkProvider.sdk.forceEnableReaderMode()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package com.tangem.feature.qrscanning.navigation
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.tangem.common.routing.AppRouter
|
||||
|
||||
import com.tangem.feature.qrscanning.QrScanningFragment
|
||||
|
||||
class DefaultQrScanningRouter(
|
||||
private val router: AppRouter,
|
||||
) : QrScanningInnerRouter {
|
||||
override fun getEntryFragment(): Fragment = QrScanningFragment.create()
|
||||
|
||||
override fun popBackStack() {
|
||||
router.pop()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.feature.qrscanning.navigation
|
||||
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
|
||||
interface QrScanningInnerRouter : QrScanningRouter {
|
||||
|
||||
fun popBackStack()
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import com.tangem.core.ui.extensions.wrappedList
|
|||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.feature.qrscanning.impl.R
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningState
|
||||
import com.tangem.feature.qrscanning.viewmodel.QrScanningClickIntents
|
||||
import com.tangem.feature.qrscanning.model.QrScanningClickIntents
|
||||
|
||||
internal class InitializeQrScanningStateTransformer(
|
||||
private val clickIntents: QrScanningClickIntents,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.feature.qrscanning.presentation.transformers
|
|||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.feature.qrscanning.presentation.CameraDeniedBottomSheetConfig
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningState
|
||||
import com.tangem.feature.qrscanning.viewmodel.QrScanningClickIntents
|
||||
import com.tangem.feature.qrscanning.model.QrScanningClickIntents
|
||||
|
||||
internal class ShowCameraDeniedBottomSheetTransformer(
|
||||
private val clickIntents: QrScanningClickIntents,
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
package com.tangem.feature.qrscanning.viewmodel
|
||||
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.feature.qrscanning.navigation.QrScanningInnerRouter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
internal open class BaseQrScanningClickIntents {
|
||||
|
||||
protected val router: QrScanningInnerRouter get() = _router
|
||||
protected val viewModelScope: CoroutineScope get() = _viewModelScope
|
||||
protected val source: SourceType get() = _source
|
||||
|
||||
private var _router: QrScanningInnerRouter by Delegates.notNull()
|
||||
private var _viewModelScope: CoroutineScope by Delegates.notNull()
|
||||
private var _source: SourceType by Delegates.notNull()
|
||||
|
||||
open fun initialize(router: QrScanningInnerRouter, source: SourceType, coroutineScope: CoroutineScope) {
|
||||
_router = router
|
||||
_viewModelScope = coroutineScope
|
||||
_source = source
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
package com.tangem.feature.qrscanning.viewmodel
|
||||
|
||||
import com.tangem.core.navigation.settings.SettingsManager
|
||||
import com.tangem.domain.qrscanning.usecases.EmitQrScannedEventUseCase
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningStateController
|
||||
import com.tangem.feature.qrscanning.presentation.transformers.DismissBottomSheetTransformer
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface QrScanningClickIntents {
|
||||
|
||||
val launchGallery: SharedFlow<Unit>
|
||||
|
||||
fun onBackClick()
|
||||
|
||||
fun onQrScanned(qrCode: String)
|
||||
|
||||
fun onGalleryClicked()
|
||||
|
||||
fun onSettingsClick()
|
||||
}
|
||||
|
||||
@ViewModelScoped
|
||||
internal class QrScanningClickIntentsImplementor @Inject constructor(
|
||||
private val stateHolder: QrScanningStateController,
|
||||
private val emitQrScannedEventUseCase: EmitQrScannedEventUseCase,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val dispatcher: CoroutineDispatcherProvider,
|
||||
) : BaseQrScanningClickIntents(), QrScanningClickIntents {
|
||||
|
||||
private var isScanned = false
|
||||
|
||||
override val launchGallery = MutableSharedFlow<Unit>(
|
||||
extraBufferCapacity = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_LATEST,
|
||||
)
|
||||
|
||||
override fun onBackClick() = router.popBackStack()
|
||||
|
||||
override fun onQrScanned(qrCode: String) {
|
||||
if (qrCode.isNotBlank()) {
|
||||
viewModelScope.launch(dispatcher.mainImmediate) {
|
||||
emitQrScannedEventUseCase.invoke(source, qrCode)
|
||||
}
|
||||
if (!isScanned) {
|
||||
router.popBackStack()
|
||||
isScanned = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGalleryClicked() {
|
||||
launchGallery.tryEmit(Unit)
|
||||
if (stateHolder.value.bottomSheetConfig != null) {
|
||||
stateHolder.update(DismissBottomSheetTransformer())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSettingsClick() {
|
||||
settingsManager.openAppSettings()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
package com.tangem.feature.qrscanning.viewmodel
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.data.card.sdk.CardSdkProvider
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.feature.qrscanning.navigation.QrScanningInnerRouter
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningState
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningStateController
|
||||
import com.tangem.feature.qrscanning.presentation.transformers.DismissBottomSheetTransformer
|
||||
import com.tangem.feature.qrscanning.presentation.transformers.InitializeQrScanningStateTransformer
|
||||
import com.tangem.feature.qrscanning.presentation.transformers.ShowCameraDeniedBottomSheetTransformer
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
internal class QrScanningViewModel @Inject constructor(
|
||||
private val stateHolder: QrScanningStateController,
|
||||
private val clickIntents: QrScanningClickIntentsImplementor,
|
||||
private val cardSdkProvider: CardSdkProvider,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
|
||||
private val source: SourceType = savedStateHandle.get<Int>(AppRoute.QrScanning.SOURCE_KEY)
|
||||
?.let { SourceType.entries[it] }
|
||||
?: error("Source is mandatory")
|
||||
private val network: String? = savedStateHandle[AppRoute.QrScanning.NETWORK_KEY]
|
||||
|
||||
val uiState: StateFlow<QrScanningState> = stateHolder.uiState
|
||||
val launchGalleryEvent: SharedFlow<Unit> = clickIntents.launchGallery
|
||||
|
||||
init {
|
||||
// samsung for some reason disables reader mode, and then it works unstable
|
||||
// to prevent this disable ir manually before scan QR
|
||||
cardSdkProvider.sdk.forceDisableReaderMode()
|
||||
}
|
||||
|
||||
fun setRouter(router: QrScanningInnerRouter) {
|
||||
clickIntents.initialize(
|
||||
router = router,
|
||||
source = source,
|
||||
coroutineScope = viewModelScope,
|
||||
)
|
||||
stateHolder.update(InitializeQrScanningStateTransformer(clickIntents, source, network))
|
||||
}
|
||||
|
||||
fun onQrScanned(qrCode: String) = clickIntents.onQrScanned(qrCode)
|
||||
|
||||
fun onCameraDeniedState() {
|
||||
stateHolder.update(ShowCameraDeniedBottomSheetTransformer(clickIntents))
|
||||
}
|
||||
|
||||
fun onDismissBottomSheetState() {
|
||||
stateHolder.update(DismissBottomSheetTransformer())
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
// don't forget enable reader mode after scan complete
|
||||
cardSdkProvider.sdk.forceEnableReaderMode()
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,13 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
/** Core */
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
/** Domain models */
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.tokens.models)
|
||||
|
||||
/** AndroidX */
|
||||
implementation(deps.androidx.fragment.ktx)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.send.api
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface SendComponent : ComposableContentComponent {
|
||||
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
val currency: CryptoCurrency,
|
||||
val transactionId: String? = null,
|
||||
val amount: String? = null,
|
||||
val tag: String? = null,
|
||||
val destinationAddress: String? = null,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, SendComponent>
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.features.send.api.navigation
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
interface SendRouter {
|
||||
|
||||
fun getEntryFragment(): Fragment
|
||||
}
|
||||
|
|
@ -50,6 +50,7 @@ dependencies {
|
|||
implementation(projects.core.analytics)
|
||||
implementation(projects.core.analytics.models)
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.decompose)
|
||||
|
||||
/** Common */
|
||||
implementation(projects.common.ui)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.features.send.impl
|
||||
|
||||
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.features.send.api.SendComponent
|
||||
import com.tangem.features.send.impl.presentation.model.SendModel
|
||||
import com.tangem.features.send.impl.presentation.ui.SendScreen
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultSendComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted params: SendComponent.Params,
|
||||
) : SendComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: SendModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val currentState = model.stateRouter.currentState.collectAsStateWithLifecycle()
|
||||
val uiState by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
SendScreen(uiState, currentState.value)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : SendComponent.Factory {
|
||||
override fun create(context: AppComponentContext, params: SendComponent.Params): DefaultSendComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.features.send.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.send.api.SendComponent
|
||||
import com.tangem.features.send.impl.DefaultSendComponent
|
||||
import com.tangem.features.send.impl.navigation.DefaultSendRouter
|
||||
import com.tangem.features.send.impl.navigation.InnerSendRouter
|
||||
import com.tangem.features.send.impl.presentation.model.SendModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface SendModule {
|
||||
|
||||
@Binds
|
||||
fun bindComponentFactory(factory: DefaultSendComponent.Factory): SendComponent.Factory
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(SendModel::class)
|
||||
fun bindModel(model: SendModel): Model
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
internal interface SendModelModule {
|
||||
|
||||
@Binds
|
||||
@ComponentScoped
|
||||
fun bindRouter(router: DefaultSendRouter): InnerSendRouter
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package com.tangem.features.send.impl.di
|
||||
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.features.send.api.navigation.SendRouter
|
||||
import com.tangem.features.send.impl.navigation.DefaultSendRouter
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ActivityComponent
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
|
||||
/**
|
||||
* DI module provides implementation of [SendRouter]
|
||||
*/
|
||||
@Module
|
||||
@InstallIn(ActivityComponent::class)
|
||||
internal object SendRouterModule {
|
||||
|
||||
@Provides
|
||||
@ActivityScoped
|
||||
fun provideSendRouter(appRouter: AppRouter, urlOpener: UrlOpener): SendRouter {
|
||||
return DefaultSendRouter(appRouter, urlOpener)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,20 @@
|
|||
package com.tangem.features.send.impl.navigation
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.send.impl.presentation.SendFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultSendRouter(
|
||||
@ComponentScoped
|
||||
internal class DefaultSendRouter @Inject constructor(
|
||||
private val router: AppRouter,
|
||||
private val urlOpener: UrlOpener,
|
||||
) : InnerSendRouter {
|
||||
|
||||
override fun getEntryFragment(): Fragment = SendFragment.create()
|
||||
|
||||
override fun openUrl(url: String) {
|
||||
urlOpener.openUrl(url)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@ package com.tangem.features.send.impl.navigation
|
|||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.send.api.navigation.SendRouter
|
||||
|
||||
interface InnerSendRouter : SendRouter {
|
||||
interface InnerSendRouter {
|
||||
|
||||
/** Open website by [url] */
|
||||
fun openUrl(url: String)
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
package com.tangem.features.send.impl.presentation
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.features.send.api.navigation.SendRouter
|
||||
import com.tangem.features.send.impl.navigation.InnerSendRouter
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.features.send.impl.presentation.ui.SendScreen
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Send fragment
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
internal class SendFragment : ComposeFragment() {
|
||||
|
||||
@Inject
|
||||
override lateinit var uiDependencies: UiDependencies
|
||||
|
||||
@Inject
|
||||
lateinit var router: SendRouter
|
||||
|
||||
@Inject
|
||||
lateinit var appRouter: AppRouter
|
||||
|
||||
@Inject
|
||||
lateinit var analyticsEventsHandler: AnalyticsEventHandler
|
||||
|
||||
private val viewModel by viewModels<SendViewModel>()
|
||||
private val innerSendRouter: InnerSendRouter
|
||||
get() = requireNotNull(router as? InnerSendRouter) {
|
||||
"innerSendRouter should be instance of InnerSendRouter"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycle.addObserver(viewModel)
|
||||
|
||||
val isEditingDisabled = arguments?.getString(AppRoute.Send.TRANSACTION_ID_KEY) != null
|
||||
viewModel.setRouter(
|
||||
innerSendRouter,
|
||||
StateRouter(
|
||||
appRouter = appRouter,
|
||||
isEditingDisabled = isEditingDisabled,
|
||||
analyticsEventsHandler = analyticsEventsHandler,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
val currentState = viewModel.stateRouter.currentState.collectAsStateWithLifecycle()
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
SendScreen(uiState, currentState.value)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
lifecycle.removeObserver(viewModel)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** Create send fragment instance */
|
||||
fun create(): SendFragment = SendFragment()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.send.impl.presentation.viewmodel
|
||||
package com.tangem.features.send.impl.presentation.model
|
||||
|
||||
import com.tangem.common.ui.amountScreen.AmountScreenClickIntents
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
|
|
@ -1,20 +1,21 @@
|
|||
package com.tangem.features.send.impl.presentation.viewmodel
|
||||
package com.tangem.features.send.impl.presentation.model
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
import androidx.lifecycle.*
|
||||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.left
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.bundle.unbundle
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
|
|
@ -47,6 +48,7 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.send.api.SendComponent
|
||||
import com.tangem.features.send.impl.navigation.InnerSendRouter
|
||||
import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource
|
||||
import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents
|
||||
|
|
@ -63,7 +65,6 @@ import com.tangem.utils.Provider
|
|||
import com.tangem.utils.coroutines.*
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.extensions.stripZeroPlainString
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import timber.log.Timber
|
||||
|
|
@ -73,9 +74,10 @@ import javax.inject.Inject
|
|||
import kotlin.properties.Delegates
|
||||
|
||||
@Suppress("LongParameterList", "TooManyFunctions", "LargeClass")
|
||||
@HiltViewModel
|
||||
internal class SendViewModel @Inject constructor(
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
internal class SendModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val getCryptoCurrencyStatusSyncUseCase: GetCryptoCurrencyStatusSyncUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
|
|
@ -109,31 +111,31 @@ internal class SendViewModel @Inject constructor(
|
|||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val shareManager: ShareManager,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
private val innerRouter: InnerSendRouter,
|
||||
private val appRouter: AppRouter,
|
||||
paramsContainer: ParamsContainer,
|
||||
validateTransactionUseCase: ValidateTransactionUseCase,
|
||||
getCurrencyCheckUseCase: GetCurrencyCheckUseCase,
|
||||
isFeeApproximateUseCase: IsFeeApproximateUseCase,
|
||||
getBalanceNotEnoughForFeeWarningUseCase: GetBalanceNotEnoughForFeeWarningUseCase,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), DefaultLifecycleObserver, SendClickIntents {
|
||||
) : Model(), SendClickIntents {
|
||||
|
||||
private val userWalletId: UserWalletId = savedStateHandle.get<Bundle>(AppRoute.Send.USER_WALLET_ID_KEY)
|
||||
?.unbundle(UserWalletId.serializer())
|
||||
?: error("This screen can't open without `UserWalletId`")
|
||||
private val params = paramsContainer.require<SendComponent.Params>()
|
||||
|
||||
private val cryptoCurrency: CryptoCurrency = savedStateHandle.get<Bundle>(AppRoute.Send.CRYPTO_CURRENCY_KEY)
|
||||
?.unbundle(CryptoCurrency.serializer())
|
||||
?: error("This screen can't open without `CryptoCurrency`")
|
||||
|
||||
private val transactionId: String? = savedStateHandle[AppRoute.Send.TRANSACTION_ID_KEY]
|
||||
private val amount: String? = savedStateHandle[AppRoute.Send.AMOUNT_KEY]
|
||||
private val destinationAddress: String? = savedStateHandle[AppRoute.Send.DESTINATION_ADDRESS_KEY]
|
||||
private val memo: String? = savedStateHandle[AppRoute.Send.TAG_KEY]
|
||||
private val userWalletId: UserWalletId = params.userWalletId
|
||||
private val cryptoCurrency: CryptoCurrency = params.currency
|
||||
private val transactionId: String? = params.transactionId
|
||||
private val amount: String? = params.amount
|
||||
private val destinationAddress: String? = params.destinationAddress
|
||||
private val memo: String? = params.tag
|
||||
|
||||
private val selectedAppCurrencyFlow: StateFlow<AppCurrency> = createSelectedAppCurrencyFlow()
|
||||
|
||||
private var innerRouter: InnerSendRouter by Delegates.notNull()
|
||||
var stateRouter: StateRouter by Delegates.notNull()
|
||||
private set
|
||||
val stateRouter = StateRouter(
|
||||
appRouter = appRouter,
|
||||
isEditingDisabled = transactionId != null,
|
||||
analyticsEventsHandler = analyticsEventHandler,
|
||||
)
|
||||
|
||||
private val stateFactory = SendStateFactory(
|
||||
clickIntents = this,
|
||||
|
|
@ -234,33 +236,26 @@ internal class SendViewModel @Inject constructor(
|
|||
subscribeOnCurrencyStatusUpdates()
|
||||
subscribeOnBalanceHidden()
|
||||
getTapHelpPreviewAvailability()
|
||||
}
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
onStateActive()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
balanceHidingJobHolder.cancel()
|
||||
balanceJobHolder.cancel()
|
||||
stateRouter.clear()
|
||||
}
|
||||
|
||||
fun setRouter(router: InnerSendRouter, stateRouter: StateRouter) {
|
||||
innerRouter = router
|
||||
this.stateRouter = stateRouter
|
||||
}
|
||||
|
||||
private fun subscribeOnQRScannerResult() {
|
||||
listenToQrScanningUseCase(SourceType.SEND)
|
||||
.getOrElse { emptyFlow() }
|
||||
.onEach(::onQrCodeScanned)
|
||||
.launchIn(viewModelScope)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnCurrencyStatusUpdates() {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
getUserWalletUseCase(userWalletId).fold(
|
||||
ifRight = { wallet ->
|
||||
userWallet = wallet
|
||||
|
|
@ -289,7 +284,7 @@ internal class SendViewModel @Inject constructor(
|
|||
.onEach {
|
||||
uiState.value = stateFactory.getOnHideBalanceState(isBalanceHidden = it.isBalanceHidden)
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(balanceHidingJobHolder)
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +305,7 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun getTapHelpPreviewAvailability() {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
isTapHelpPreviewEnabled = isSendTapHelpEnabledUseCase().getOrElse { false }
|
||||
}
|
||||
}
|
||||
|
|
@ -362,7 +357,7 @@ internal class SendViewModel @Inject constructor(
|
|||
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
scope = modelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = AppCurrency.Default,
|
||||
)
|
||||
|
|
@ -395,13 +390,13 @@ internal class SendViewModel @Inject constructor(
|
|||
|
||||
private fun getWalletsAndRecent() {
|
||||
getUserWallets()
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
getTxHistory()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUserWallets() {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
runCatching {
|
||||
waitForDelay(delay = RECENT_LOAD_DELAY) {
|
||||
getWalletsUseCase.invokeSync()
|
||||
|
|
@ -466,7 +461,7 @@ internal class SendViewModel @Inject constructor(
|
|||
else -> Unit
|
||||
}
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun updateNotifications() {
|
||||
|
|
@ -475,7 +470,7 @@ internal class SendViewModel @Inject constructor(
|
|||
.distinctUntilChanged()
|
||||
.onEach { uiState.value = stateFactory.getSendNotificationState(notifications = it) }
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(viewModelScope)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(sendNotificationsJobHolder)
|
||||
}
|
||||
|
||||
|
|
@ -485,7 +480,7 @@ internal class SendViewModel @Inject constructor(
|
|||
.distinctUntilChanged()
|
||||
.onEach { uiState.value = feeStateFactory.getFeeNotificationState(notifications = it) }
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(viewModelScope)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(feeNotificationsJobHolder)
|
||||
}
|
||||
|
||||
|
|
@ -573,7 +568,7 @@ internal class SendViewModel @Inject constructor(
|
|||
|
||||
val cardInfo = getCardInfoUseCase(userWallet.scanResponse).getOrNull() ?: return
|
||||
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo))
|
||||
}
|
||||
}
|
||||
|
|
@ -615,7 +610,7 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun cancelFeeRequest() {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
feeJobHolder.cancel()
|
||||
}
|
||||
}
|
||||
|
|
@ -660,7 +655,7 @@ internal class SendViewModel @Inject constructor(
|
|||
// region recipient state clicks
|
||||
|
||||
override fun onRecipientAddressValueChange(value: String, type: EnterAddressSource?) {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
if (!checkIfXrpAddressValue(value)) {
|
||||
uiState.value = recipientStateFactory.onRecipientAddressValueChange(value, isValuePasted = type != null)
|
||||
uiState.value = recipientStateFactory.getOnRecipientAddressValidationStarted()
|
||||
|
|
@ -680,7 +675,7 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onRecipientMemoValueChange(value: String, isValuePasted: Boolean) {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
if (!checkIfXrpAddressValue(value)) {
|
||||
uiState.value = recipientStateFactory.getOnRecipientMemoValueChange(value, isValuePasted)
|
||||
uiState.value = recipientStateFactory.getOnRecipientAddressValidationStarted()
|
||||
|
|
@ -758,7 +753,7 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun loadFee() {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
val isShowStatus = uiState.value.feeState?.fee == null
|
||||
if (isShowStatus) {
|
||||
uiState.value = feeStateFactory.onFeeOnLoadingState()
|
||||
|
|
@ -911,7 +906,7 @@ internal class SendViewModel @Inject constructor(
|
|||
reduceAmountBy = uiState.value.sendState?.reduceAmountBy ?: BigDecimal.ZERO,
|
||||
)
|
||||
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
createTransactionUseCase(
|
||||
amount = receivingAmount.convertToSdkAmount(cryptoCurrency),
|
||||
fee = fee,
|
||||
|
|
@ -969,7 +964,7 @@ internal class SendViewModel @Inject constructor(
|
|||
|
||||
val receivingUserWallet = userWallets.firstOrNull { it.address == destinationAddress } ?: return
|
||||
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
addCryptoCurrenciesUseCase(
|
||||
userWalletId = receivingUserWallet.userWalletId,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
|
|
@ -1031,7 +1026,7 @@ internal class SendViewModel @Inject constructor(
|
|||
val noErrorNotifications = sendState.notifications.none { it is NotificationUM.Error }
|
||||
|
||||
if (!isSuccess && noErrorNotifications) {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
val feeUpdatedState = callFeeUseCase()?.fold(
|
||||
ifRight = {
|
||||
uiState.value = stateFactory.getSendingStateUpdate(isSending = false)
|
||||
|
|
@ -1065,7 +1060,7 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun setNeverToShowTapHelp() {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
neverShowTapHelpUseCase()
|
||||
}
|
||||
uiState.value = stateFactory.getHiddenTapHelpState()
|
||||
|
|
@ -8,7 +8,7 @@ import com.tangem.domain.transaction.error.SendTransactionError
|
|||
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeStateFactory
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeType
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
|
|||
import com.tangem.features.send.impl.presentation.state.fee.SendFeeStateConverter
|
||||
import com.tangem.features.send.impl.presentation.state.fee.checkFeeCoverage
|
||||
import com.tangem.features.send.impl.presentation.state.recipient.SendRecipientStateConverter
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ internal class StateRouter(
|
|||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val isEditingDisabled: Boolean,
|
||||
) {
|
||||
private var mutableCurrentState: MutableStateFlow<SendUiCurrentScreen> = MutableStateFlow(getInitState())
|
||||
private val mutableCurrentState: MutableStateFlow<SendUiCurrentScreen> = MutableStateFlow(getInitState())
|
||||
|
||||
val currentState: StateFlow<SendUiCurrentScreen>
|
||||
get() = mutableCurrentState
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import com.tangem.features.send.impl.presentation.state.SendUiState
|
|||
import com.tangem.features.send.impl.presentation.state.SendUiStateType
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.features.send.impl.presentation.state.fee.*
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isTezos
|
||||
import com.tangem.utils.Provider
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import com.tangem.features.send.impl.presentation.state.StateRouter
|
|||
import com.tangem.features.send.impl.presentation.state.fee.custom.BitcoinCustomFeeConverter
|
||||
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter
|
||||
import com.tangem.features.send.impl.presentation.state.fee.custom.KaspaCustomFeeConverter
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.common.ui.notifications.NotificationsFactory.addFeeUnreachable
|
|||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiStateType
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase
|
|||
import com.tangem.features.send.impl.presentation.state.SendStates
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import com.tangem.features.send.impl.presentation.state.fee.custom.BitcoinCustom
|
|||
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter
|
||||
import com.tangem.features.send.impl.presentation.state.fee.custom.KaspaCustomFeeConverter
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import com.tangem.features.send.impl.R
|
|||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.features.send.impl.presentation.state.fee.checkExceedBalance
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isUseBitcoinFeeConverter
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import com.tangem.features.send.impl.R
|
|||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.features.send.impl.presentation.state.fee.checkExceedBalance
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCusto
|
|||
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter.Companion.GAS_DECIMALS
|
||||
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter.Companion.GIGA_DECIMALS
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCusto
|
|||
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter.Companion.GAS_DECIMALS
|
||||
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter.Companion.GIGA_DECIMALS
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.common.ui.notifications.NotificationUM
|
|||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeType
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class SendRecipientAddressFieldConverter(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
|||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.send.impl.presentation.state.recipient
|
|||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendStates
|
||||
import com.tangem.features.send.impl.presentation.state.recipient.utils.*
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import com.tangem.features.send.impl.presentation.state.fee.FeeType
|
|||
import com.tangem.features.send.impl.presentation.state.previewdata.FeeStatePreviewData
|
||||
import com.tangem.features.send.impl.presentation.state.previewdata.SendClickIntentsStub
|
||||
import com.tangem.features.send.impl.presentation.ui.common.notifications
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
|
||||
private const val FEE_SELECTOR_KEY = "FEE_SELECTOR_KEY"
|
||||
private const val FEE_CUSTOM_KEY = "FEE_CUSTOM_KEY"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import com.tangem.features.send.impl.presentation.state.SendStates
|
|||
import com.tangem.features.send.impl.presentation.state.fee.FeeType
|
||||
import com.tangem.features.send.impl.presentation.state.previewdata.FeeStatePreviewData
|
||||
import com.tangem.features.send.impl.presentation.state.previewdata.SendClickIntentsStub
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import com.tangem.features.send.impl.presentation.state.SendStates
|
|||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.state.previewdata.RecipientStatePreviewData
|
||||
import com.tangem.features.send.impl.presentation.state.previewdata.SendClickIntentsStub
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
private const val ADDRESS_FIELD_KEY = "ADDRESS_FIELD_KEY"
|
||||
|
|
|
|||
|
|
@ -989,6 +989,8 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
Bitrock, BitrockTestnet,
|
||||
Sonic, SonicTestnet,
|
||||
ApeChain, ApeChainTestnet,
|
||||
Scroll, ScrollTestnet,
|
||||
ZkLinkNova, ZkLinkNovaTestnet,
|
||||
-> Fee.Common(feeAmount)
|
||||
// endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,11 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.domain.tokens.models)
|
||||
/** Core */
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
/** AndroidX */
|
||||
implementation(deps.androidx.fragment.ktx)
|
||||
/** Domain models */
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.wallets.models)
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.features.tokendetails
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface TokenDetailsComponent : ComposableContentComponent {
|
||||
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
val currency: CryptoCurrency,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, TokenDetailsComponent>
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.features.tokendetails.navigation
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
interface TokenDetailsRouter {
|
||||
|
||||
fun getEntryFragment(): Fragment
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package com.tangem.feature.tokendetails
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.essenty.lifecycle.subscribe
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.deeplink.global.BuyCurrencyDeepLink
|
||||
import com.tangem.core.deeplink.utils.registerDeepLinks
|
||||
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsModel
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
|
||||
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
|
||||
import com.tangem.features.tokendetails.TokenDetailsComponent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultTokenDetailsComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted params: TokenDetailsComponent.Params,
|
||||
tokenMarketBlockComponentFactory: TokenMarketBlockComponent.Factory,
|
||||
deepLinksRegistry: DeepLinksRegistry,
|
||||
) : TokenDetailsComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: TokenDetailsModel = getOrCreateModel(params)
|
||||
|
||||
init {
|
||||
lifecycle.subscribe(
|
||||
onPause = model::onPause,
|
||||
onResume = model::onResume,
|
||||
)
|
||||
|
||||
registerDeepLinks(
|
||||
registry = deepLinksRegistry,
|
||||
BuyCurrencyDeepLink(
|
||||
onReceive = model::onBuyCurrencyDeepLink,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private val tokenMarketBlockComponent = params.currency.toTokenMarketParam()?.let { tokenMarketParams ->
|
||||
tokenMarketBlockComponentFactory.create(
|
||||
appComponentContext = child("tokenMarketBlockComponent"),
|
||||
params = tokenMarketParams,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
NavigationBar3ButtonsScrim()
|
||||
TokenDetailsScreen(
|
||||
state = state,
|
||||
tokenMarketBlockComponent = tokenMarketBlockComponent,
|
||||
)
|
||||
}
|
||||
|
||||
private fun CryptoCurrency.toTokenMarketParam(): TokenMarketBlockComponent.Params? {
|
||||
id.rawCurrencyId ?: return null // token price is not available
|
||||
|
||||
return TokenMarketBlockComponent.Params(cryptoCurrency = this)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : TokenDetailsComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: TokenDetailsComponent.Params,
|
||||
): DefaultTokenDetailsComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.feature.tokendetails.di
|
||||
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.feature.tokendetails.DefaultTokenDetailsComponent
|
||||
import com.tangem.feature.tokendetails.presentation.router.DefaultTokenDetailsRouter
|
||||
import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsModel
|
||||
import com.tangem.features.tokendetails.TokenDetailsComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface TokenDetailsModule {
|
||||
|
||||
@Binds
|
||||
fun bindComponentFactory(factory: DefaultTokenDetailsComponent.Factory): TokenDetailsComponent.Factory
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(TokenDetailsModel::class)
|
||||
fun bindModel(model: TokenDetailsModel): Model
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
internal interface StakingComponentModule {
|
||||
|
||||
@Binds
|
||||
@ComponentScoped
|
||||
fun bindRouter(impl: DefaultTokenDetailsRouter): InnerTokenDetailsRouter
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package com.tangem.feature.tokendetails.di
|
||||
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.feature.tokendetails.presentation.router.DefaultTokenDetailsRouter
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ActivityComponent
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
|
||||
@Module
|
||||
@InstallIn(ActivityComponent::class)
|
||||
internal object TokenDetailsRouterModule {
|
||||
|
||||
@Provides
|
||||
@ActivityScoped
|
||||
fun provideTokenDetailsRouter(
|
||||
appRouter: AppRouter,
|
||||
urlOpener: UrlOpener,
|
||||
shareManager: ShareManager,
|
||||
): TokenDetailsRouter {
|
||||
return DefaultTokenDetailsRouter(appRouter, urlOpener, shareManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
package com.tangem.feature.tokendetails.presentation
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.defaultComponentContext
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.routing.bundle.unbundle
|
||||
import com.tangem.common.routing.utils.asRouter
|
||||
import com.tangem.core.decompose.context.DefaultAppComponentContext
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsViewModel
|
||||
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
internal class TokenDetailsFragment : ComposeFragment() {
|
||||
|
||||
@Inject
|
||||
override lateinit var uiDependencies: UiDependencies
|
||||
|
||||
@Inject
|
||||
@GlobalUiMessageSender
|
||||
internal lateinit var messageSender: UiMessageSender
|
||||
|
||||
@Inject
|
||||
internal lateinit var tokenDetailsRouter: TokenDetailsRouter
|
||||
|
||||
@Inject
|
||||
internal lateinit var coroutineDispatcherProvider: CoroutineDispatcherProvider
|
||||
|
||||
@Inject
|
||||
internal lateinit var componentBuilder: DecomposeComponent.Builder
|
||||
|
||||
@Inject
|
||||
internal lateinit var tokenMarketBlockComponentFactory: TokenMarketBlockComponent.Factory
|
||||
|
||||
@Inject
|
||||
internal lateinit var appRouter: AppRouter
|
||||
|
||||
private val viewModel by viewModels<TokenDetailsViewModel>()
|
||||
|
||||
private var tokenMarketBlockComponent: TokenMarketBlockComponent? = null
|
||||
|
||||
private val internalTokenDetailsRouter: InnerTokenDetailsRouter
|
||||
get() = requireNotNull(tokenDetailsRouter as? InnerTokenDetailsRouter) {
|
||||
"internalTokenDetailsRouter should be instance of InnerTokenDetailsRouter"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewModel.router = internalTokenDetailsRouter
|
||||
lifecycle.addObserver(viewModel)
|
||||
|
||||
val cryptoCurrency: CryptoCurrency = arguments
|
||||
?.getBundle(AppRoute.CurrencyDetails.CRYPTO_CURRENCY_KEY)
|
||||
?.unbundle(CryptoCurrency.serializer())
|
||||
?: error("Token Details screen can't be opened without `CryptoCurrency`")
|
||||
|
||||
val param = cryptoCurrency.toParam() ?: return
|
||||
|
||||
val appContext = DefaultAppComponentContext(
|
||||
componentContext = defaultComponentContext(requireActivity().onBackPressedDispatcher),
|
||||
messageSender = messageSender,
|
||||
dispatchers = coroutineDispatcherProvider,
|
||||
hiltComponentBuilder = componentBuilder,
|
||||
replaceRouter = appRouter.asRouter(),
|
||||
)
|
||||
|
||||
tokenMarketBlockComponent = tokenMarketBlockComponentFactory.create(
|
||||
appComponentContext = appContext,
|
||||
params = param,
|
||||
)
|
||||
}
|
||||
|
||||
private fun CryptoCurrency.toParam(): TokenMarketBlockComponent.Params? {
|
||||
id.rawCurrencyId ?: return null // token price is not available
|
||||
|
||||
return TokenMarketBlockComponent.Params(cryptoCurrency = this)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
NavigationBar3ButtonsScrim()
|
||||
TokenDetailsScreen(
|
||||
state = viewModel.uiState.collectAsStateWithLifecycle().value,
|
||||
tokenMarketBlockComponent = tokenMarketBlockComponent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,22 @@
|
|||
package com.tangem.feature.tokendetails.presentation.router
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.tokendetails.presentation.TokenDetailsFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultTokenDetailsRouter(
|
||||
@ComponentScoped
|
||||
internal class DefaultTokenDetailsRouter @Inject constructor(
|
||||
private val router: AppRouter,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val shareManager: ShareManager,
|
||||
) : InnerTokenDetailsRouter {
|
||||
|
||||
override fun getEntryFragment(): Fragment = TokenDetailsFragment()
|
||||
|
||||
override fun popBackStack() {
|
||||
router.pop()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ package com.tangem.feature.tokendetails.presentation.router
|
|||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
|
||||
internal interface InnerTokenDetailsRouter : TokenDetailsRouter {
|
||||
internal interface InnerTokenDetailsRouter {
|
||||
|
||||
/** Pop back stack */
|
||||
fun popBackStack()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels
|
||||
package com.tangem.feature.tokendetails.presentation.tokendetails.model
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -1,21 +1,20 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels
|
||||
package com.tangem.feature.tokendetails.presentation.tokendetails.model
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.lifecycle.*
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.paging.cachedIn
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.routing.bundle.unbundle
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.deeplink.global.BuyCurrencyDeepLink
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
|
||||
|
|
@ -75,10 +74,10 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDeta
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express.ExpressStatusFactory
|
||||
import com.tangem.features.onramp.OnrampFeatureToggles
|
||||
import com.tangem.features.tokendetails.TokenDetailsComponent
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.*
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.async
|
||||
|
|
@ -89,9 +88,10 @@ import timber.log.Timber
|
|||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList", "LargeClass", "TooManyFunctions")
|
||||
@HiltViewModel
|
||||
internal class TokenDetailsViewModel @Inject constructor(
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
internal class TokenDetailsModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
|
||||
|
|
@ -122,26 +122,19 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val onrampFeatureToggles: OnrampFeatureToggles,
|
||||
private val shareManager: ShareManager,
|
||||
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
|
||||
paramsContainer: ParamsContainer,
|
||||
expressStatusFactory: ExpressStatusFactory.Factory,
|
||||
getUserWalletUseCase: GetUserWalletUseCase,
|
||||
getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase,
|
||||
deepLinksRegistry: DeepLinksRegistry,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val appRouter: AppRouter,
|
||||
) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents {
|
||||
private val router: InnerTokenDetailsRouter,
|
||||
) : Model(), TokenDetailsClickIntents {
|
||||
|
||||
private val userWalletId: UserWalletId = savedStateHandle.get<Bundle>(AppRoute.CurrencyDetails.USER_WALLET_ID_KEY)
|
||||
?.unbundle(UserWalletId.serializer())
|
||||
?: error("This screen can't be opened without `UserWalletId`")
|
||||
private val params = paramsContainer.require<TokenDetailsComponent.Params>()
|
||||
private val userWalletId: UserWalletId = params.userWalletId
|
||||
private val cryptoCurrency: CryptoCurrency = params.currency
|
||||
|
||||
private val cryptoCurrency: CryptoCurrency =
|
||||
savedStateHandle.get<Bundle>(AppRoute.CurrencyDetails.CRYPTO_CURRENCY_KEY)
|
||||
?.unbundle(CryptoCurrency.serializer())
|
||||
?: error("This screen can't be opened without `CryptoCurrency`")
|
||||
|
||||
private val userWallet: UserWallet
|
||||
|
||||
lateinit var router: InnerTokenDetailsRouter
|
||||
private val userWallet: UserWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: error("UserWallet not found")
|
||||
|
||||
private val marketPriceJobHolder = JobHolder()
|
||||
private val refreshStateJobHolder = JobHolder()
|
||||
|
|
@ -195,18 +188,16 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
val uiState: StateFlow<TokenDetailsState> = internalUiState
|
||||
|
||||
init {
|
||||
deepLinksRegistry.registerWithViewModel(
|
||||
viewModel = this,
|
||||
deepLinks = listOf(
|
||||
BuyCurrencyDeepLink(
|
||||
onReceive = ::onBuyCurrencyDeepLink,
|
||||
),
|
||||
),
|
||||
analyticsEventsHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.DetailsScreenOpened(token = cryptoCurrency.symbol),
|
||||
)
|
||||
userWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: error("UserWallet not found")
|
||||
updateTopBarMenu()
|
||||
initButtons()
|
||||
updateContent()
|
||||
handleBalanceHiding()
|
||||
}
|
||||
|
||||
private fun onBuyCurrencyDeepLink(externalTxId: String) {
|
||||
fun onBuyCurrencyDeepLink(externalTxId: String) {
|
||||
if (onrampFeatureToggles.isFeatureEnabled) {
|
||||
router.openOnrampSuccess(externalTxId)
|
||||
} else {
|
||||
|
|
@ -215,36 +206,24 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
analyticsEventsHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.DetailsScreenOpened(token = cryptoCurrency.symbol),
|
||||
)
|
||||
updateTopBarMenu()
|
||||
initButtons()
|
||||
updateContent()
|
||||
handleBalanceHiding(owner)
|
||||
}
|
||||
|
||||
override fun onPause(owner: LifecycleOwner) {
|
||||
fun onPause() {
|
||||
expressTxStatusTaskScheduler.cancelTask()
|
||||
expressTxJobHolder.cancel()
|
||||
super.onPause(owner)
|
||||
}
|
||||
|
||||
override fun onResume(owner: LifecycleOwner) {
|
||||
fun onResume() {
|
||||
subscribeOnExpressTransactionsUpdates()
|
||||
super.onResume(owner)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
override fun onDestroy() {
|
||||
expressTxStatusTaskScheduler.cancelTask()
|
||||
expressTxJobHolder.cancel()
|
||||
super.onCleared()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun initButtons() {
|
||||
// we need also init buttons before start all loading to avoid buttons blocking
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
val currentCryptoCurrencyStatus = getCryptoCurrencySyncUseCase.invoke(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = cryptoCurrency.id,
|
||||
|
|
@ -266,15 +245,14 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
updateStakingInfo()
|
||||
}
|
||||
|
||||
private fun handleBalanceHiding(owner: LifecycleOwner) {
|
||||
private fun handleBalanceHiding() {
|
||||
getBalanceHidingSettingsUseCase()
|
||||
.flowWithLifecycle(owner.lifecycle)
|
||||
.onEach {
|
||||
internalUiState.value = stateFactory.getStateWithUpdatedHidden(
|
||||
isBalanceHidden = it.isBalanceHidden,
|
||||
)
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private suspend fun updateButtons(currencyStatus: CryptoCurrencyStatus) {
|
||||
|
|
@ -288,11 +266,11 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
internalUiState.value = stateFactory.getManageButtonsState(actions = it.states)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(viewModelScope)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun updateWarnings(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
getCurrencyWarningsUseCase.invoke(
|
||||
userWalletId = userWalletId,
|
||||
currencyStatus = cryptoCurrencyStatus,
|
||||
|
|
@ -305,13 +283,13 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
notificationsAnalyticsSender.send(internalUiState.value, updatedState.notifications)
|
||||
internalUiState.value = updatedState
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(warningsJobHolder)
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnCurrencyStatusUpdates() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
getCurrencyStatusUpdatesUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = cryptoCurrency.id,
|
||||
|
|
@ -328,13 +306,13 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
currencyStatusAnalyticsSender.send(maybeCurrencyStatus)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(viewModelScope)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(marketPriceJobHolder)
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnExpressTransactionsUpdates() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
expressTxStatusTaskScheduler.cancelTask()
|
||||
expressStatusFactory
|
||||
.getExpressStatuses()
|
||||
|
|
@ -345,7 +323,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
::updateNetworkToSwapBalance,
|
||||
)
|
||||
expressTxStatusTaskScheduler.scheduleTask(
|
||||
viewModelScope,
|
||||
modelScope,
|
||||
PeriodicTask(
|
||||
isDelayFirst = false,
|
||||
delay = EXPRESS_STATUS_UPDATE_DELAY,
|
||||
|
|
@ -365,13 +343,13 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(viewModelScope)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(expressTxJobHolder)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateNetworkToSwapBalance(toCryptoCurrency: CryptoCurrency) {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
updateDelayedCurrencyStatusUseCase(
|
||||
userWalletId = userWalletId,
|
||||
network = toCryptoCurrency.network,
|
||||
|
|
@ -385,7 +363,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
* @param showItemsLoading - show loading items placeholder.
|
||||
*/
|
||||
private fun updateTxHistory(refresh: Boolean, showItemsLoading: Boolean) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currency = cryptoCurrency,
|
||||
|
|
@ -404,7 +382,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
userWalletId = userWalletId,
|
||||
currency = cryptoCurrency,
|
||||
refresh = refresh,
|
||||
).map { it.cachedIn(viewModelScope) }
|
||||
).map { it.cachedIn(modelScope) }
|
||||
|
||||
internalUiState.value = stateFactory.getLoadedTxHistoryState(maybeTxHistory)
|
||||
}
|
||||
|
|
@ -412,7 +390,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun updateStakingInfo() {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
val availability = getStakingAvailabilityUseCase(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
|
|
@ -432,7 +410,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun updateTopBarMenu() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
val hasDerivations =
|
||||
networkHasDerivationUseCase(userWallet.scanResponse, cryptoCurrency.network).getOrElse { false }
|
||||
|
||||
|
|
@ -452,7 +430,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
scope = modelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = AppCurrency.Default,
|
||||
)
|
||||
|
|
@ -476,7 +454,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
|
||||
val status = cryptoCurrencyStatus ?: return
|
||||
if (onrampFeatureToggles.isFeatureEnabled) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
reduxStateHolder.dispatch(
|
||||
TradeCryptoAction.Buy(
|
||||
userWallet = userWallet,
|
||||
|
|
@ -488,7 +466,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
} else {
|
||||
showErrorIfDemoModeOrElse {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
reduxStateHolder.dispatch(
|
||||
TradeCryptoAction.Buy(
|
||||
userWallet = userWallet,
|
||||
|
|
@ -554,7 +532,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
return
|
||||
}
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonReceive(cryptoCurrency.symbol))
|
||||
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrency.symbol))
|
||||
|
||||
|
|
@ -587,7 +565,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onGenerateExtendedKey() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
val extendedKey = getExtendedPublicKeyForCurrencyUseCase(
|
||||
userWalletId,
|
||||
cryptoCurrency.network,
|
||||
|
|
@ -661,7 +639,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
override fun onHideClick() {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonRemoveToken(cryptoCurrency.symbol))
|
||||
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
val hasLinkedTokens = removeCurrencyUseCase.hasLinkedTokens(userWalletId, cryptoCurrency)
|
||||
internalUiState.value = if (hasLinkedTokens) {
|
||||
stateFactory.getStateWithLinkedTokensDialog(cryptoCurrency)
|
||||
|
|
@ -672,7 +650,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onHideConfirmed() {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
removeCurrencyUseCase.invoke(userWalletId, cryptoCurrency)
|
||||
.onLeft { Timber.e(it) }
|
||||
.onRight { router.popBackStack() }
|
||||
|
|
@ -687,7 +665,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private fun openExplorer() {
|
||||
val currencyStatus = cryptoCurrencyStatus ?: return
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
when (val addresses = currencyStatus.value.networkAddress) {
|
||||
is NetworkAddress.Selectable -> {
|
||||
internalUiState.value = stateFactory.getStateWithChooseAddressBottomSheet(cryptoCurrency, addresses)
|
||||
|
|
@ -719,7 +697,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onAddressTypeSelected(addressModel: AddressModel) {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
router.openUrl(
|
||||
url = getExploreUrlUseCase(
|
||||
userWalletId = userWalletId,
|
||||
|
|
@ -744,7 +722,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
override fun onRefreshSwipe(isRefreshing: Boolean) {
|
||||
internalUiState.value = stateFactory.getRefreshingState()
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
listOf(
|
||||
async {
|
||||
fetchCurrencyStatusUseCase(
|
||||
|
|
@ -768,7 +746,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
override fun onDismissBottomSheet() {
|
||||
when (val bsContent = internalUiState.value.bottomSheetConfig?.content) {
|
||||
is ExpressStatusBottomSheetConfig -> {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
expressStatusFactory.removeTransactionOnBottomSheetClosed(bsContent.value)
|
||||
}
|
||||
}
|
||||
|
|
@ -798,7 +776,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onSwapPromoDismiss() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
shouldShowSwapPromoTokenUseCase.neverToShow()
|
||||
analyticsEventsHandler.send(
|
||||
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
|
||||
|
|
@ -811,7 +789,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onSwapPromoClick() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
shouldShowSwapPromoTokenUseCase.neverToShow()
|
||||
analyticsEventsHandler.send(
|
||||
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
|
||||
|
|
@ -842,7 +820,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
blockchain = cryptoCurrency.network.name,
|
||||
),
|
||||
)
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
retryIncompleteTransactionUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currency = cryptoCurrency,
|
||||
|
|
@ -883,13 +861,13 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
blockchain = cryptoCurrency.network.name,
|
||||
),
|
||||
)
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
internalUiState.value = stateFactory.getStateWithDismissIncompleteTransactionConfirmDialog()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfirmDismissIncompleteTransactionClick() {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
dismissIncompleteTransactionUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currency = cryptoCurrency,
|
||||
|
|
@ -914,7 +892,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
blockchain = cryptoCurrency.network.name,
|
||||
),
|
||||
)
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
modelScope.launch(dispatchers.io) {
|
||||
associateAssetUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currency = cryptoCurrency,
|
||||
|
|
@ -953,7 +931,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
override fun onDisposeExpressStatus() {
|
||||
val bottomSheetState = internalUiState.value.bottomSheetConfig?.content
|
||||
if (bottomSheetState is ExpressStatusBottomSheetConfig) {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
expressStatusFactory.removeTransactionOnBottomSheetClosed(
|
||||
expressState = bottomSheetState.value,
|
||||
isForceDispose = true,
|
||||
|
|
@ -985,7 +963,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun openStaking() {
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
val yield = getYieldUseCase.invoke(
|
||||
cryptoCurrencyId = cryptoCurrency.id,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
|
|
@ -4,7 +4,7 @@ import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
|||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsTxHistoryTransactionStateConverter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.utils.getBalance
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isBSC
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isSolana
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import com.tangem.domain.tokens.model.warnings.KaspaWarnings
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification.*
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.removeBy
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
|||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isBitcoin
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.component
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadedTxHistoryConverter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter.TokenDetailsLoadingTxHistoryModel
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionModel
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeStatusState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import com.tangem.feature.swap.domain.models.domain.*
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsSwapTransactionsStateConverter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.assisted.Assisted
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsOnrampTransactionStateConverter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
|||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
|||
import com.tangem.domain.txhistory.models.TxHistoryStateError
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter.TokenDetailsLoadingTxHistoryModel
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistory
|
|||
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import com.tangem.core.ui.format.bigdecimal.format
|
|||
import com.tangem.core.ui.utils.toTimeFormat
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem.*
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.StringsSigns.MINUS
|
||||
import com.tangem.utils.StringsSigns.PLUS
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
|||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import java.util.UUID
|
||||
|
|
@ -32,8 +33,7 @@ internal object WalletPreviewData {
|
|||
content = TextReference.Str("3 cards • Seed phrase3 cards • Seed phrasephrasephrasephrase"),
|
||||
),
|
||||
imageResId = R.drawable.ill_wallet2_cards3_120_106,
|
||||
onRenameClick = { _ -> },
|
||||
onDeleteClick = {},
|
||||
dropDownItems = persistentListOf(),
|
||||
cardCount = 1,
|
||||
isZeroBalance = false,
|
||||
isBalanceFlickering = false,
|
||||
|
|
@ -45,8 +45,7 @@ internal object WalletPreviewData {
|
|||
id = UserWalletId("321"),
|
||||
title = "Wallet 1",
|
||||
imageResId = R.drawable.ill_wallet2_cards3_120_106,
|
||||
onRenameClick = { _ -> },
|
||||
onDeleteClick = {},
|
||||
dropDownItems = persistentListOf(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -55,8 +54,7 @@ internal object WalletPreviewData {
|
|||
id = UserWalletId("24"),
|
||||
title = "Wallet 1",
|
||||
imageResId = R.drawable.ill_wallet2_cards3_120_106,
|
||||
onRenameClick = { _ -> },
|
||||
onDeleteClick = {},
|
||||
dropDownItems = persistentListOf(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,8 +87,7 @@ internal object WalletScreenPreviewData {
|
|||
content = TextReference.Str("Locked"),
|
||||
),
|
||||
imageResId = R.drawable.ill_note_btc_120_106,
|
||||
onRenameClick = { _ -> },
|
||||
onDeleteClick = {},
|
||||
dropDownItems = persistentListOf(),
|
||||
)
|
||||
}
|
||||
private val miltiUnreachableCard by lazy {
|
||||
|
|
@ -102,8 +101,7 @@ internal object WalletScreenPreviewData {
|
|||
imageResId = R.drawable.ill_wallet2_cards3_120_106,
|
||||
cardCount = 3,
|
||||
balance = DASH_SIGN,
|
||||
onRenameClick = { _ -> },
|
||||
onDeleteClick = {},
|
||||
dropDownItems = persistentListOf(),
|
||||
isZeroBalance = false,
|
||||
isBalanceFlickering = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.promo.GetStoryContentUseCase
|
|||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
|
||||
|
|
@ -13,6 +14,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletActionButtonsSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletTokenListSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber
|
||||
|
|
@ -34,6 +36,7 @@ internal class MultiWalletContentLoader(
|
|||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val swapFeatureToggles: SwapFeatureToggles,
|
||||
private val deepLinksRegistry: DeepLinksRegistry,
|
||||
|
|
@ -68,6 +71,11 @@ internal class MultiWalletContentLoader(
|
|||
getStoryContentUseCase = getStoryContentUseCase,
|
||||
).let(::add)
|
||||
}
|
||||
WalletDropDownItemsSubscriber(
|
||||
stateHolder = stateHolder,
|
||||
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
|
||||
clickIntents = clickIntents,
|
||||
).let(::add)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.promo.GetStoryContentUseCase
|
|||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
|
||||
|
|
@ -31,6 +32,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val swapFeatureToggles: SwapFeatureToggles,
|
||||
private val deepLinksRegistry: DeepLinksRegistry,
|
||||
|
|
@ -51,6 +53,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
getStoryContentUseCase = getStoryContentUseCase,
|
||||
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
|
||||
swapFeatureToggles = swapFeatureToggles,
|
||||
deepLinksRegistry = deepLinksRegistry,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
|
|||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
|
|
@ -31,6 +32,7 @@ internal class SingleWalletContentLoader(
|
|||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
|
||||
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
|
@ -59,6 +61,11 @@ internal class SingleWalletContentLoader(
|
|||
getSingleWalletWarningsFactory = getSingleWalletWarningsFactory,
|
||||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
),
|
||||
WalletDropDownItemsSubscriber(
|
||||
stateHolder = stateHolder,
|
||||
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
SingleWalletExpressStatusesSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateHolder = stateHolder,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
|
|||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
|
|
@ -30,6 +31,7 @@ internal class SingleWalletContentLoaderFactory @Inject constructor(
|
|||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
|
||||
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
) {
|
||||
|
|
@ -51,6 +53,7 @@ internal class SingleWalletContentLoaderFactory @Inject constructor(
|
|||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
getOnrampTransactionsUseCase = getOnrampTransactionsUseCase,
|
||||
onrampRemoveTransactionUseCase = onrampRemoveTransactionUseCase,
|
||||
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
|
||||
|
|
@ -12,9 +13,11 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletActionButtonsSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.SingleWalletWithTokenListSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletDropDownItemsSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.features.swap.SwapFeatureToggles
|
||||
|
|
@ -32,6 +35,7 @@ internal class SingleWalletWithTokenContentLoader(
|
|||
private val tokenListStore: MultiWalletTokenListStore,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val swapFeatureToggles: SwapFeatureToggles,
|
||||
private val deepLinksRegistry: DeepLinksRegistry,
|
||||
|
|
@ -65,6 +69,11 @@ internal class SingleWalletWithTokenContentLoader(
|
|||
getStoryContentUseCase = getStoryContentUseCase,
|
||||
).let(::add)
|
||||
}
|
||||
WalletDropDownItemsSubscriber(
|
||||
stateHolder = stateHolder,
|
||||
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
|
||||
clickIntents = clickIntents,
|
||||
).let(::add)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
|
||||
|
|
@ -28,6 +29,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
|
|||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val swapFeatureToggles: SwapFeatureToggles,
|
||||
private val deepLinksRegistry: DeepLinksRegistry,
|
||||
|
|
@ -47,6 +49,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
|
|||
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
getStoryContentUseCase = getStoryContentUseCase,
|
||||
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
|
||||
swapFeatureToggles = swapFeatureToggles,
|
||||
deepLinksRegistry = deepLinksRegistry,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ internal data class BalancesAndLimitsBottomSheetConfig(
|
|||
val availableBalance: String,
|
||||
val blockedBalance: String,
|
||||
val debit: String,
|
||||
val pending: String,
|
||||
val amlVerified: String,
|
||||
val onInfoClick: () -> Unit,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import androidx.compose.runtime.Immutable
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
/** Wallet card state */
|
||||
@Immutable
|
||||
|
|
@ -23,11 +24,8 @@ internal sealed interface WalletCardState {
|
|||
@get:DrawableRes
|
||||
val imageResId: Int?
|
||||
|
||||
/** Lambda be invoked when Rename button is clicked */
|
||||
val onRenameClick: (UserWalletId) -> Unit
|
||||
|
||||
/** Lambda be invoked when Delete button is clicked */
|
||||
val onDeleteClick: (UserWalletId) -> Unit
|
||||
/** Wallet drop down items */
|
||||
val dropDownItems: ImmutableList<WalletDropDownItems>
|
||||
|
||||
/**
|
||||
* Wallet card content state
|
||||
|
|
@ -35,8 +33,7 @@ internal sealed interface WalletCardState {
|
|||
* @property id wallet id
|
||||
* @property title wallet name
|
||||
* @property imageResId wallet image resource id
|
||||
* @property onRenameClick lambda be invoked when Rename button is clicked
|
||||
* @property onDeleteClick lambda be invoked when Delete button is clicked
|
||||
* @property dropDownItems wallet dropdown items
|
||||
* @property additionalInfo wallet additional info
|
||||
* @property cardCount number of cards in the wallet
|
||||
* @property balance wallet balance
|
||||
|
|
@ -46,8 +43,7 @@ internal sealed interface WalletCardState {
|
|||
override val title: String,
|
||||
override val additionalInfo: WalletAdditionalInfo,
|
||||
override val imageResId: Int?,
|
||||
override val onRenameClick: (UserWalletId) -> Unit,
|
||||
override val onDeleteClick: (UserWalletId) -> Unit,
|
||||
override val dropDownItems: ImmutableList<WalletDropDownItems>,
|
||||
val isBalanceFlickering: Boolean,
|
||||
val cardCount: Int?,
|
||||
val balance: String,
|
||||
|
|
@ -61,16 +57,14 @@ internal sealed interface WalletCardState {
|
|||
* @property title wallet name
|
||||
* @property additionalInfo wallet additional info
|
||||
* @property imageResId wallet image resource id
|
||||
* @property onRenameClick lambda be invoked when Rename button is clicked
|
||||
* @property onDeleteClick lambda be invoked when Delete button is clicked
|
||||
* @property dropDownItems wallet dropdown items
|
||||
*/
|
||||
data class LockedContent(
|
||||
override val id: UserWalletId,
|
||||
override val title: String,
|
||||
override val additionalInfo: WalletAdditionalInfo,
|
||||
override val imageResId: Int?,
|
||||
override val onRenameClick: (UserWalletId) -> Unit,
|
||||
override val onDeleteClick: (UserWalletId) -> Unit,
|
||||
override val dropDownItems: ImmutableList<WalletDropDownItems>,
|
||||
) : WalletCardState
|
||||
|
||||
/**
|
||||
|
|
@ -79,16 +73,14 @@ internal sealed interface WalletCardState {
|
|||
* @property id wallet id
|
||||
* @property title wallet name
|
||||
* @property imageResId wallet image resource id
|
||||
* @property onRenameClick lambda be invoked when Rename button is clicked
|
||||
* @property onDeleteClick lambda be invoked when Delete button is clicked
|
||||
* @property dropDownItems wallet dropdown items
|
||||
*/
|
||||
data class Error(
|
||||
override val id: UserWalletId,
|
||||
override val title: String,
|
||||
override val additionalInfo: WalletAdditionalInfo? = defaultAdditionalInfo,
|
||||
override val imageResId: Int?,
|
||||
override val onRenameClick: (UserWalletId) -> Unit,
|
||||
override val onDeleteClick: (UserWalletId) -> Unit,
|
||||
override val dropDownItems: ImmutableList<WalletDropDownItems>,
|
||||
) : WalletCardState {
|
||||
|
||||
private companion object {
|
||||
|
|
@ -103,24 +95,25 @@ internal sealed interface WalletCardState {
|
|||
* @property id wallet id
|
||||
* @property title wallet name
|
||||
* @property imageResId wallet image resource id
|
||||
* @property onRenameClick lambda be invoked when Rename button is clicked
|
||||
* @property onDeleteClick lambda be invoked when Delete button is clicked
|
||||
* @property dropDownItems wallet dropdown items
|
||||
*/
|
||||
data class Loading(
|
||||
override val id: UserWalletId,
|
||||
override val title: String,
|
||||
override val additionalInfo: WalletAdditionalInfo? = null,
|
||||
override val imageResId: Int?,
|
||||
override val onRenameClick: (UserWalletId) -> Unit,
|
||||
override val onDeleteClick: (UserWalletId) -> Unit,
|
||||
override val dropDownItems: ImmutableList<WalletDropDownItems>,
|
||||
) : WalletCardState
|
||||
|
||||
fun copySealed(title: String = this.title): WalletCardState {
|
||||
fun copySealed(
|
||||
title: String = this.title,
|
||||
dropDownItems: ImmutableList<WalletDropDownItems> = this.dropDownItems,
|
||||
): WalletCardState {
|
||||
return when (this) {
|
||||
is Content -> copy(title = title)
|
||||
is Error -> copy(title = title)
|
||||
is Loading -> copy(title = title)
|
||||
is LockedContent -> copy(title = title)
|
||||
is Content -> copy(title = title, dropDownItems = dropDownItems)
|
||||
is Error -> copy(title = title, dropDownItems = dropDownItems)
|
||||
is Loading -> copy(title = title, dropDownItems = dropDownItems)
|
||||
is LockedContent -> copy(title = title, dropDownItems = dropDownItems)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
internal data class WalletDropDownItems(
|
||||
val text: TextReference,
|
||||
val icon: ImageVector,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -91,8 +91,7 @@ internal class InitializeWalletsTransformer(
|
|||
title = name,
|
||||
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = this),
|
||||
imageResId = walletImageResolver.resolve(userWallet = this),
|
||||
onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
|
||||
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
|
||||
dropDownItems = persistentListOf(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,8 +58,7 @@ internal class SetBalancesAndLimitsTransformer(
|
|||
id = id,
|
||||
title = title,
|
||||
imageResId = imageResId,
|
||||
onRenameClick = onRenameClick,
|
||||
onDeleteClick = onDeleteClick,
|
||||
dropDownItems = dropDownItems,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -71,8 +70,7 @@ internal class SetBalancesAndLimitsTransformer(
|
|||
title = title,
|
||||
additionalInfo = createAdditionalInfo(visaCurrency),
|
||||
imageResId = imageResId,
|
||||
onRenameClick = onRenameClick,
|
||||
onDeleteClick = onDeleteClick,
|
||||
dropDownItems = dropDownItems,
|
||||
balance = visaCurrency.balances.available.format {
|
||||
crypto(visaCurrency.symbol, visaCurrency.decimals)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -61,8 +61,7 @@ internal class SetTokenListErrorTransformer(
|
|||
title = title,
|
||||
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = selectedWallet),
|
||||
imageResId = imageResId,
|
||||
onRenameClick = onRenameClick,
|
||||
onDeleteClick = onDeleteClick,
|
||||
dropDownItems = dropDownItems,
|
||||
balance = BigDecimal.ZERO.format {
|
||||
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDropDownItems
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletCardClickIntents
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
internal class SetWalletCardDropDownItemsTransformer(
|
||||
private val dropdownEnabled: Boolean,
|
||||
private val clickIntents: WalletCardClickIntents,
|
||||
) : WalletScreenStateTransformer {
|
||||
override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||
return prevState.copy(wallets = prevState.wallets.map(::transformWalletState).toImmutableList())
|
||||
}
|
||||
|
||||
private fun transformWalletState(prevState: WalletState): WalletState {
|
||||
return when (prevState) {
|
||||
is WalletState.MultiCurrency.Content -> prevState.copy(
|
||||
walletCardState = prevState.walletCardState.copySealed(
|
||||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
),
|
||||
)
|
||||
is WalletState.SingleCurrency.Content -> prevState.copy(
|
||||
walletCardState = prevState.walletCardState.copySealed(
|
||||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
),
|
||||
)
|
||||
is WalletState.Visa.Content -> prevState.copy(
|
||||
walletCardState = prevState.walletCardState.copySealed(
|
||||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
),
|
||||
)
|
||||
is WalletState.MultiCurrency.Locked -> prevState.copy(
|
||||
walletCardState = prevState.walletCardState.copySealed(
|
||||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
),
|
||||
)
|
||||
is WalletState.SingleCurrency.Locked -> prevState.copy(
|
||||
walletCardState = prevState.walletCardState.copySealed(
|
||||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
),
|
||||
)
|
||||
is WalletState.Visa.Locked -> prevState.copy(
|
||||
walletCardState = prevState.walletCardState.copySealed(
|
||||
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun constructDropDownItems(userWalletId: UserWalletId): ImmutableList<WalletDropDownItems> {
|
||||
return if (dropdownEnabled) {
|
||||
persistentListOf(
|
||||
WalletDropDownItems(
|
||||
text = resourceReference(id = R.string.common_rename),
|
||||
icon = Icons.Outlined.Edit,
|
||||
onClick = { clickIntents.onRenameBeforeConfirmationClick(userWalletId) },
|
||||
),
|
||||
WalletDropDownItems(
|
||||
text = resourceReference(id = R.string.common_delete),
|
||||
icon = Icons.Outlined.Delete,
|
||||
onClick = { clickIntents.onDeleteBeforeConfirmationClick(userWalletId) },
|
||||
),
|
||||
)
|
||||
} else {
|
||||
persistentListOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,6 @@ internal class BalancesAndLimitsBottomSheetConverter(
|
|||
availableBalance = value.balances.available.let(::formatAmount),
|
||||
blockedBalance = value.balances.blocked.let(::formatAmount),
|
||||
debit = value.balances.debt.let(::formatAmount),
|
||||
pending = value.balances.pendingRefund.let(::formatAmount),
|
||||
amlVerified = value.balances.verified.let(::formatAmount),
|
||||
onInfoClick = this::showBalanceInfo,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -32,8 +32,7 @@ internal class MultiWalletCardStateConverter(
|
|||
title = title,
|
||||
additionalInfo = additionalInfo,
|
||||
imageResId = imageResId,
|
||||
onRenameClick = onRenameClick,
|
||||
onDeleteClick = onDeleteClick,
|
||||
dropDownItems = dropDownItems,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -43,8 +42,7 @@ internal class MultiWalletCardStateConverter(
|
|||
title = title,
|
||||
additionalInfo = additionalInfo,
|
||||
imageResId = imageResId,
|
||||
onDeleteClick = onDeleteClick,
|
||||
onRenameClick = onRenameClick,
|
||||
dropDownItems = dropDownItems,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -54,8 +52,7 @@ internal class MultiWalletCardStateConverter(
|
|||
title = title,
|
||||
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = selectedWallet),
|
||||
imageResId = imageResId,
|
||||
onRenameClick = onRenameClick,
|
||||
onDeleteClick = onDeleteClick,
|
||||
dropDownItems = dropDownItems,
|
||||
balance = fiatBalance.amount.format {
|
||||
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -39,8 +39,7 @@ internal class SingleWalletCardStateConverter(
|
|||
id = id,
|
||||
title = title,
|
||||
imageResId = imageResId,
|
||||
onRenameClick = onRenameClick,
|
||||
onDeleteClick = onDeleteClick,
|
||||
dropDownItems = dropDownItems,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -49,8 +48,7 @@ internal class SingleWalletCardStateConverter(
|
|||
id = id,
|
||||
title = title,
|
||||
imageResId = imageResId,
|
||||
onRenameClick = onRenameClick,
|
||||
onDeleteClick = onDeleteClick,
|
||||
dropDownItems = dropDownItems,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -63,8 +61,7 @@ internal class SingleWalletCardStateConverter(
|
|||
currencyAmount = status.amount,
|
||||
),
|
||||
imageResId = imageResId,
|
||||
onRenameClick = onRenameClick,
|
||||
onDeleteClick = onDeleteClick,
|
||||
dropDownItems = dropDownItems,
|
||||
balance = formatFiatAmount(status = status, appCurrency = appCurrency),
|
||||
cardCount = selectedWallet.getCardsCount(),
|
||||
isZeroBalance = status.fiatAmount?.isZero(),
|
||||
|
|
|
|||
|
|
@ -90,8 +90,7 @@ internal class WalletLoadingStateFactory(
|
|||
title = name,
|
||||
additionalInfo = if (isMultiCurrency) WalletAdditionalInfoFactory.resolve(wallet = this) else null,
|
||||
imageResId = walletImageResolver.resolve(userWallet = this),
|
||||
onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
|
||||
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
|
||||
dropDownItems = persistentListOf(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWalletCardDropDownItemsTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
internal class WalletDropDownItemsSubscriber(
|
||||
private val stateHolder: WalletStateController,
|
||||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : WalletSubscriber() {
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
return flow<Any> {
|
||||
shouldSaveUserWalletsUseCase.invoke()
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
stateHolder.update(
|
||||
SetWalletCardDropDownItemsTransformer(
|
||||
dropdownEnabled = it,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
}
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Image
|
||||
|
|
@ -14,9 +13,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||
import androidx.compose.foundation.interaction.PressInteraction
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
|
|
@ -48,13 +44,14 @@ import com.tangem.core.ui.components.flicker
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemDimens
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDropDownItems
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.fastForEach
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
private const val HALF_OF_ITEM_WIDTH = 0.5
|
||||
|
||||
|
|
@ -71,8 +68,7 @@ private const val HALF_OF_ITEM_WIDTH = 0.5
|
|||
internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries")
|
||||
CardContainer(
|
||||
onDeleteClick = { state.onDeleteClick(state.id) },
|
||||
onRenameClick = { state.onRenameClick(state.id) },
|
||||
dropDownItems = state.dropDownItems,
|
||||
isLockedState = state is WalletCardState.LockedContent,
|
||||
modifier = modifier,
|
||||
) { itemSize ->
|
||||
|
|
@ -148,8 +144,7 @@ internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifi
|
|||
|
||||
@Composable
|
||||
private fun CardContainer(
|
||||
onDeleteClick: () -> Unit,
|
||||
onRenameClick: () -> Unit,
|
||||
dropDownItems: ImmutableList<WalletDropDownItems>,
|
||||
isLockedState: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable (ConstraintLayoutScope.(IntSize) -> Unit),
|
||||
|
|
@ -167,7 +162,7 @@ private fun CardContainer(
|
|||
.defaultMinSize(minHeight = TangemTheme.dimens.size108)
|
||||
.onSizeChanged { itemSize = it }
|
||||
.then(
|
||||
if (isLockedState) {
|
||||
if (isLockedState || dropDownItems.isEmpty()) {
|
||||
Modifier
|
||||
} else {
|
||||
Modifier
|
||||
|
|
@ -210,8 +205,7 @@ private fun CardContainer(
|
|||
pressOffset = pressOffset,
|
||||
itemHeight = itemHeight,
|
||||
onDismissRequest = { isMenuVisible = false },
|
||||
onShowRenameWalletDialogClick = onRenameClick,
|
||||
onDeleteClick = onDeleteClick,
|
||||
dropDownItems = dropDownItems,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -222,8 +216,7 @@ private fun ManageWalletContextMenu(
|
|||
pressOffset: DpOffset,
|
||||
itemHeight: Dp,
|
||||
onDismissRequest: () -> Unit,
|
||||
onShowRenameWalletDialogClick: () -> Unit,
|
||||
onDeleteClick: () -> Unit,
|
||||
dropDownItems: ImmutableList<WalletDropDownItems>,
|
||||
) {
|
||||
DropdownMenu(
|
||||
expanded = isMenuVisible,
|
||||
|
|
@ -231,29 +224,23 @@ private fun ManageWalletContextMenu(
|
|||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
offset = pressOffset.copy(y = pressOffset.y - itemHeight),
|
||||
) {
|
||||
MenuItem(
|
||||
textResId = R.string.common_rename,
|
||||
imageVector = Icons.Outlined.Edit,
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
onShowRenameWalletDialogClick()
|
||||
},
|
||||
)
|
||||
MenuItem(
|
||||
textResId = R.string.common_delete,
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
onDeleteClick()
|
||||
},
|
||||
)
|
||||
dropDownItems.fastForEach { item ->
|
||||
MenuItem(
|
||||
text = item.text,
|
||||
imageVector = item.icon,
|
||||
onClick = {
|
||||
onDismissRequest()
|
||||
item.onClick()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MenuItem(@StringRes textResId: Int, imageVector: ImageVector, onClick: () -> Unit) {
|
||||
private fun MenuItem(text: TextReference, imageVector: ImageVector, onClick: () -> Unit) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResourceSafe(id = textResId), style = TangemTheme.typography.subtitle2) },
|
||||
text = { Text(text = text.resolveReference(), style = TangemTheme.typography.subtitle2) },
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
trailingIcon = { Icon(imageVector = imageVector, contentDescription = null) },
|
||||
onClick = onClick,
|
||||
|
|
|
|||
|
|
@ -78,10 +78,6 @@ private fun BalancesBlock(balances: BalancesAndLimitsBottomSheetConfig.Balance,
|
|||
title = stringReference("Debit"),
|
||||
value = balances.debit,
|
||||
)
|
||||
BlockItem(
|
||||
title = stringReference("Pending refund"),
|
||||
value = balances.pending,
|
||||
)
|
||||
},
|
||||
description = {
|
||||
InfoButton(onClick = balances.onInfoClick)
|
||||
|
|
@ -180,7 +176,6 @@ private class BalancesAndLimitsBottomSheetParameterProvider :
|
|||
availableBalance = "392.45 USDT",
|
||||
blockedBalance = "36.00 USDT",
|
||||
debit = "00.00 USDT",
|
||||
pending = "20.99 USDT",
|
||||
amlVerified = "356.45 USDT",
|
||||
onInfoClick = {},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -299,12 +299,6 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun initializeWallets(action: WalletsUpdateActionResolver.Action.InitializeWallets) {
|
||||
walletScreenContentLoader.load(
|
||||
userWallet = action.selectedWallet,
|
||||
clickIntents = clickIntents,
|
||||
coroutineScope = viewModelScope,
|
||||
)
|
||||
|
||||
stateHolder.update(
|
||||
transformer = InitializeWalletsTransformer(
|
||||
selectedWalletIndex = action.selectedWalletIndex,
|
||||
|
|
@ -315,6 +309,12 @@ internal class WalletViewModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
|
||||
walletScreenContentLoader.load(
|
||||
userWallet = action.selectedWallet,
|
||||
clickIntents = clickIntents,
|
||||
coroutineScope = viewModelScope,
|
||||
)
|
||||
|
||||
if (action.wallets.size > 1 && isWalletsScrollPreviewEnabled()) {
|
||||
withContext(dispatchers.io) { delay(timeMillis = 1_800) }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue