Updated on 2026-08-14
This commit is contained in:
parent
9b1c90cf12
commit
2f7bcb9cbb
15 changed files with 316 additions and 88 deletions
|
|
@ -1,16 +0,0 @@
|
||||||
package com.tangem.tap.common
|
|
||||||
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.HandlerThread
|
|
||||||
import android.os.Looper
|
|
||||||
|
|
||||||
private val uiHandler = Handler(Looper.getMainLooper())
|
|
||||||
private val backgroundHandler = Handler(HandlerThread("AppMainHandlerThread").apply { start() }.looper)
|
|
||||||
|
|
||||||
fun postUi(ms: Long = 0, func: Runnable) {
|
|
||||||
if (ms == 0L) uiHandler.post { func.run() } else uiHandler.postDelayed(func, ms)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun postUiDelayBg(ms: Long, func: Runnable) {
|
|
||||||
backgroundHandler.postDelayed({ uiHandler.post(func) }, ms)
|
|
||||||
}
|
|
||||||
|
|
@ -11,7 +11,7 @@ class ScanFailsDialogAnalytics(button: Buttons, source: AnalyticsParam.ScreensSo
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
enum class Buttons(val event: String) {
|
enum class Buttons(val event: String) {
|
||||||
TRY_AGAIN("Try again button"),
|
|
||||||
HOW_TO_SCAN("Button blog"),
|
HOW_TO_SCAN("Button blog"),
|
||||||
|
TRY_AGAIN("Button try again"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package com.tangem.tap.common.entities
|
|
||||||
|
|
||||||
import com.tangem.tap.common.toggleWidget.WidgetState
|
|
||||||
|
|
||||||
enum class ProgressState : WidgetState { Loading, Done, Error }
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
package com.tangem.tap.common.extensions
|
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.transition.AutoTransition
|
|
||||||
import androidx.transition.Transition
|
|
||||||
import androidx.transition.TransitionManager
|
|
||||||
|
|
||||||
/**
|
|
||||||
[REDACTED_AUTHOR]
|
|
||||||
*/
|
|
||||||
fun ViewGroup.inflate(viewToInflate: Int, attachToRoot: Boolean = false): View {
|
|
||||||
return LayoutInflater.from(context).inflate(viewToInflate, this, attachToRoot)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ViewGroup.beginDelayedTransition(transition: Transition = AutoTransition()) {
|
|
||||||
TransitionManager.beginDelayedTransition(this, transition)
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
package com.tangem.tap.common.toggleWidget
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
|
|
||||||
/**
|
|
||||||
[REDACTED_AUTHOR]
|
|
||||||
*/
|
|
||||||
interface WidgetState
|
|
||||||
|
|
||||||
interface ViewStateWidget {
|
|
||||||
val mainView: View
|
|
||||||
fun changeState(state: WidgetState)
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
package com.tangem.tap.common.transitions
|
|
||||||
|
|
||||||
import androidx.transition.ChangeBounds
|
|
||||||
import androidx.transition.ChangeTransform
|
|
||||||
import androidx.transition.Fade
|
|
||||||
import androidx.transition.TransitionSet
|
|
||||||
|
|
||||||
class HomeToOnboardingTransition : TransitionSet() {
|
|
||||||
init {
|
|
||||||
ordering = ORDERING_TOGETHER
|
|
||||||
addTransition(Fade())
|
|
||||||
addTransition(ChangeTransform())
|
|
||||||
addTransition(ChangeBounds())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InternalNoteLayoutTransition : TransitionSet() {
|
|
||||||
init {
|
|
||||||
ordering = ORDERING_TOGETHER
|
|
||||||
addTransition(ChangeTransform())
|
|
||||||
addTransition(ChangeBounds())
|
|
||||||
addTransition(Fade())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.tangem.tap.features.scanfails
|
||||||
|
|
||||||
|
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.factory.ComponentFactory
|
||||||
|
import com.tangem.core.decompose.model.getOrCreateModel
|
||||||
|
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||||
|
import com.tangem.domain.card.ScanFailsRequester
|
||||||
|
import com.tangem.tap.features.scanfails.ui.ScanFailsDialogContent
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
|
||||||
|
internal class ScanFailsComponent @AssistedInject constructor(
|
||||||
|
@Assisted appComponentContext: AppComponentContext,
|
||||||
|
@Assisted params: Unit,
|
||||||
|
) : AppComponentContext by appComponentContext, ComposableContentComponent, ScanFailsRequester {
|
||||||
|
|
||||||
|
private val model: ScanFailsModel = getOrCreateModel(params)
|
||||||
|
|
||||||
|
override suspend fun show(source: ScanFailsRequester.Source): ScanFailsRequester.Result {
|
||||||
|
model.show(source)
|
||||||
|
return model.waitResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Content(modifier: Modifier) {
|
||||||
|
val state by model.uiState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
if (state.isShown) {
|
||||||
|
ScanFailsDialogContent(state = state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory : ComponentFactory<Unit, ScanFailsComponent> {
|
||||||
|
override fun create(context: AppComponentContext, params: Unit): ScanFailsComponent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
package com.tangem.tap.features.scanfails
|
||||||
|
|
||||||
|
import com.tangem.common.TangemBlogUrlBuilder
|
||||||
|
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||||
|
import com.tangem.core.analytics.models.AnalyticsParam
|
||||||
|
import com.tangem.core.analytics.models.Basic
|
||||||
|
import com.tangem.core.decompose.di.ModelScoped
|
||||||
|
import com.tangem.core.decompose.model.Model
|
||||||
|
import com.tangem.core.navigation.url.UrlOpener
|
||||||
|
import com.tangem.domain.card.ScanFailsRequester
|
||||||
|
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||||
|
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||||
|
import com.tangem.tap.common.analytics.events.ScanFailsDialogAnalytics
|
||||||
|
import com.tangem.tap.features.scanfails.ui.ScanFailsUM
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ModelScoped
|
||||||
|
internal class ScanFailsModel @Inject constructor(
|
||||||
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||||
|
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||||
|
private val urlOpener: UrlOpener,
|
||||||
|
) : Model() {
|
||||||
|
|
||||||
|
private val result = MutableStateFlow<ScanFailsRequester.Result?>(null)
|
||||||
|
|
||||||
|
val uiState: StateFlow<ScanFailsUM>
|
||||||
|
field = MutableStateFlow(ScanFailsUM(onDismiss = ::dismiss))
|
||||||
|
|
||||||
|
fun show(source: ScanFailsRequester.Source) {
|
||||||
|
val analyticsSource = source.toAnalyticsSource()
|
||||||
|
result.value = null
|
||||||
|
uiState.update {
|
||||||
|
ScanFailsUM(
|
||||||
|
isShown = true,
|
||||||
|
onHowToScan = { onHowToScan(analyticsSource) },
|
||||||
|
onRequestSupport = { onRequestSupport(analyticsSource) },
|
||||||
|
onDismiss = ::dismiss,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun waitResult(): ScanFailsRequester.Result {
|
||||||
|
return result.filterNotNull().first().also { result.value = null }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismiss() {
|
||||||
|
result.value = ScanFailsRequester.Result.Dismissed
|
||||||
|
uiState.update { it.copy(isShown = false) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onHowToScan(source: AnalyticsParam.ScreensSources) {
|
||||||
|
analyticsEventHandler.send(
|
||||||
|
ScanFailsDialogAnalytics(
|
||||||
|
button = ScanFailsDialogAnalytics.Buttons.HOW_TO_SCAN,
|
||||||
|
source = source,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
modelScope.launch {
|
||||||
|
urlOpener.openUrl(TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.HowToScan))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onRequestSupport(source: AnalyticsParam.ScreensSources) {
|
||||||
|
analyticsEventHandler.send(Basic.ButtonSupport(source))
|
||||||
|
modelScope.launch {
|
||||||
|
sendFeedbackEmailUseCase(type = FeedbackEmailType.ScanningProblem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ScanFailsRequester.Source.toAnalyticsSource(): AnalyticsParam.ScreensSources = when (this) {
|
||||||
|
ScanFailsRequester.Source.MAIN -> AnalyticsParam.ScreensSources.Main
|
||||||
|
ScanFailsRequester.Source.SIGN_IN -> AnalyticsParam.ScreensSources.SignIn
|
||||||
|
ScanFailsRequester.Source.SETTINGS -> AnalyticsParam.ScreensSources.Settings
|
||||||
|
ScanFailsRequester.Source.INTRO -> AnalyticsParam.ScreensSources.Intro
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.tangem.tap.features.scanfails
|
||||||
|
|
||||||
|
import com.tangem.domain.card.ScanFailsRequester
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.withTimeout
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class ScanFailsRequesterProxy @Inject constructor() : ScanFailsRequester {
|
||||||
|
|
||||||
|
val componentRequester = MutableStateFlow<ScanFailsRequester?>(null)
|
||||||
|
|
||||||
|
override suspend fun show(source: ScanFailsRequester.Source): ScanFailsRequester.Result {
|
||||||
|
return withTimeout(timeMillis = 1000) {
|
||||||
|
componentRequester.filterNotNull().first()
|
||||||
|
}.show(source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.tangem.tap.features.scanfails.di
|
||||||
|
|
||||||
|
import com.tangem.core.decompose.factory.ComponentFactory
|
||||||
|
import com.tangem.core.decompose.model.Model
|
||||||
|
import com.tangem.domain.card.ScanFailsRequester
|
||||||
|
import com.tangem.tap.features.scanfails.ScanFailsComponent
|
||||||
|
import com.tangem.tap.features.scanfails.ScanFailsModel
|
||||||
|
import com.tangem.tap.features.scanfails.ScanFailsRequesterProxy
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import dagger.multibindings.ClassKey
|
||||||
|
import dagger.multibindings.IntoMap
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
internal interface ScanFailsModule {
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
fun bindComponentFactory(impl: ScanFailsComponent.Factory): ComponentFactory<Unit, ScanFailsComponent>
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@ClassKey(ScanFailsModel::class)
|
||||||
|
fun bindModel(model: ScanFailsModel): Model
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
fun bindRequester(impl: ScanFailsRequesterProxy): ScanFailsRequester
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
package com.tangem.tap.features.scanfails.ui
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import com.tangem.core.ui.extensions.LocalUserInteractionTracker
|
||||||
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
|
import com.tangem.core.ui.extensions.trackUserInteraction
|
||||||
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
|
import com.tangem.wallet.R
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ScanFailsDialogContent(state: ScanFailsUM) {
|
||||||
|
val userInteractionTracker = LocalUserInteractionTracker.current
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = state.onDismiss,
|
||||||
|
properties = DialogProperties(dismissOnClickOutside = true),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.trackUserInteraction(userInteractionTracker)
|
||||||
|
.background(
|
||||||
|
color = TangemTheme.colors.background.primary,
|
||||||
|
shape = RoundedCornerShape(16.dp),
|
||||||
|
)
|
||||||
|
.padding(vertical = 16.dp, horizontal = 38.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(24.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResourceSafe(R.string.common_warning),
|
||||||
|
color = TangemTheme.colors.text.primary1,
|
||||||
|
style = TangemTheme.typography.h3,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResourceSafe(R.string.alert_troubleshooting_scan_card_title),
|
||||||
|
color = TangemTheme.colors.text.secondary,
|
||||||
|
style = TangemTheme.typography.body2,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
|
||||||
|
DialogTextButton(
|
||||||
|
text = stringResourceSafe(R.string.alert_button_how_to_scan),
|
||||||
|
onClick = state.onHowToScan,
|
||||||
|
color = TangemTheme.colors.text.accent,
|
||||||
|
)
|
||||||
|
|
||||||
|
DialogTextButton(
|
||||||
|
text = stringResourceSafe(R.string.alert_button_request_support),
|
||||||
|
onClick = state.onRequestSupport,
|
||||||
|
color = TangemTheme.colors.text.accent,
|
||||||
|
)
|
||||||
|
|
||||||
|
DialogTextButton(
|
||||||
|
text = stringResourceSafe(R.string.common_cancel),
|
||||||
|
onClick = state.onDismiss,
|
||||||
|
color = TangemTheme.colors.text.warning,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DialogTextButton(text: String, onClick: () -> Unit, color: Color) {
|
||||||
|
Text(
|
||||||
|
text = text.uppercase(),
|
||||||
|
color = color,
|
||||||
|
style = TangemTheme.typography.button,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.clickable(onClick = onClick),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Preview
|
||||||
|
@Preview(showBackground = true, widthDp = 360)
|
||||||
|
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Preview(showBackground = true, widthDp = 360, fontScale = 2f)
|
||||||
|
@Composable
|
||||||
|
private fun ScanFailsDialogContentPreview() {
|
||||||
|
TangemThemePreview {
|
||||||
|
ScanFailsDialogContent(
|
||||||
|
state = ScanFailsUM(
|
||||||
|
isShown = true,
|
||||||
|
onHowToScan = {},
|
||||||
|
onRequestSupport = {},
|
||||||
|
onDismiss = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion Preview
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.tangem.tap.features.scanfails.ui
|
||||||
|
|
||||||
|
internal data class ScanFailsUM(
|
||||||
|
val isShown: Boolean = false,
|
||||||
|
val onHowToScan: () -> Unit = {},
|
||||||
|
val onRequestSupport: () -> Unit = {},
|
||||||
|
val onDismiss: () -> Unit = {},
|
||||||
|
)
|
||||||
|
|
@ -35,5 +35,9 @@ object TangemBlogUrlBuilder {
|
||||||
data object WhatIsTransactionFee : Post {
|
data object WhatIsTransactionFee : Post {
|
||||||
override val path: String = "what-is-a-transaction-fee-and-why-do-we-need-it"
|
override val path: String = "what-is-a-transaction-fee-and-why-do-we-need-it"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data object HowToScan : Post {
|
||||||
|
override val path: String = "scan-tangem-card"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.tangem.domain.card
|
||||||
|
|
||||||
|
interface ScanFailsRequester {
|
||||||
|
|
||||||
|
suspend fun show(source: Source): Result
|
||||||
|
|
||||||
|
enum class Source { MAIN, SIGN_IN, SETTINGS, INTRO }
|
||||||
|
|
||||||
|
sealed class Result {
|
||||||
|
data object Dismissed : Result()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
package com.tangem.features.hotwallet.accesscoderequest.ui
|
package com.tangem.features.hotwallet.accesscoderequest.ui
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
|
||||||
import androidx.compose.animation.fadeOut
|
|
||||||
import androidx.compose.animation.slideInVertically
|
|
||||||
import androidx.compose.animation.slideOutVertically
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
|
@ -26,9 +22,7 @@ import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||||
import com.tangem.core.ui.components.fields.PinTextColor
|
import com.tangem.core.ui.components.fields.PinTextColor
|
||||||
import com.tangem.core.ui.components.fields.PinTextField
|
import com.tangem.core.ui.components.fields.PinTextField
|
||||||
import com.tangem.core.ui.extensions.resolveReference
|
import com.tangem.core.ui.extensions.*
|
||||||
import com.tangem.core.ui.extensions.stringReference
|
|
||||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
|
||||||
import com.tangem.core.ui.haptic.TangemHapticEffect
|
import com.tangem.core.ui.haptic.TangemHapticEffect
|
||||||
import com.tangem.core.ui.res.LocalHapticManager
|
import com.tangem.core.ui.res.LocalHapticManager
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
|
@ -42,12 +36,11 @@ internal fun HotAccessCodeRequestFullScreenContent(state: HotAccessCodeRequestUM
|
||||||
val userInteractionTracker = LocalUserInteractionTracker.current
|
val userInteractionTracker = LocalUserInteractionTracker.current
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.trackUserInteraction(userInteractionTracker),
|
.trackUserInteraction(userInteractionTracker),
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
modifier = modifier,
|
|
||||||
visible = state.isShown,
|
visible = state.isShown,
|
||||||
enter = fadeIn(),
|
enter = fadeIn(),
|
||||||
exit = fadeOut(),
|
exit = fadeOut(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue