Updated on 2026-08-14
This commit is contained in:
parent
6c41eefe0b
commit
044efe36c5
19 changed files with 540 additions and 35 deletions
|
|
@ -0,0 +1,96 @@
|
|||
package com.tangem.features.pushnotificationsettings.impl.component
|
||||
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.arkivanov.essenty.lifecycle.doOnResume
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.feature.walletsettings.component.NetworksAvailableForNotificationsComponent
|
||||
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
|
||||
import com.tangem.features.pushnotificationsettings.component.PushNotificationSettingsComponent
|
||||
import com.tangem.features.pushnotificationsettings.impl.entity.NetworksAvailableForNotificationBSConfig
|
||||
import com.tangem.features.pushnotificationsettings.impl.model.PushNotificationSettingsModel
|
||||
import com.tangem.features.pushnotificationsettings.impl.ui.PushNotificationSettingsScreen
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultPushNotificationSettingsComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted private val params: PushNotificationSettingsComponent.Params,
|
||||
private val networksAvailableForNotificationsComponentFactory: NetworksAvailableForNotificationsComponent.Factory,
|
||||
) : PushNotificationSettingsComponent, AppComponentContext by context {
|
||||
|
||||
private val model: PushNotificationSettingsModel = getOrCreateModel(params)
|
||||
|
||||
private val bottomSheetSlot = childSlot(
|
||||
source = model.bottomSheetNavigation,
|
||||
serializer = null,
|
||||
handleBackButton = false,
|
||||
key = "moreInfoBottomSheet",
|
||||
childFactory = ::bottomSheetChild,
|
||||
)
|
||||
|
||||
init {
|
||||
lifecycle.doOnResume { model.onResume() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
val bottomSheet by bottomSheetSlot.subscribeAsState()
|
||||
|
||||
val permissionLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.RequestPermission(),
|
||||
onResult = model::onPermissionResult,
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
model.requestPushPermission.collect {
|
||||
val permission = getPushPermissionOrNull()
|
||||
if (permission != null) {
|
||||
permissionLauncher.launch(permission)
|
||||
} else {
|
||||
model.onPermissionResult(isGranted = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PushNotificationSettingsScreen(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
onBackClick = router::pop,
|
||||
)
|
||||
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
}
|
||||
|
||||
private fun bottomSheetChild(
|
||||
@Suppress("UNUSED_PARAMETER") config: NetworksAvailableForNotificationBSConfig,
|
||||
componentContext: ComponentContext,
|
||||
): ComposableBottomSheetComponent = networksAvailableForNotificationsComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = NetworksAvailableForNotificationsComponent.Params(
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : PushNotificationSettingsComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: PushNotificationSettingsComponent.Params,
|
||||
): DefaultPushNotificationSettingsComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.pushnotificationsettings.impl.di
|
||||
|
||||
import com.tangem.features.pushnotificationsettings.component.PushNotificationSettingsComponent
|
||||
import com.tangem.features.pushnotificationsettings.impl.component.DefaultPushNotificationSettingsComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface PushNotificationSettingsComponentModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindPushNotificationSettingsComponentFactory(
|
||||
factory: DefaultPushNotificationSettingsComponent.Factory,
|
||||
): PushNotificationSettingsComponent.Factory
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.features.pushnotificationsettings.impl.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
|
||||
@Immutable
|
||||
|
|
@ -12,7 +11,6 @@ internal sealed interface PushNotificationSettingsUM {
|
|||
data class Content(
|
||||
val banner: AllowPushNotificationsBannerUM?,
|
||||
val toggles: PersistentList<ToggleUM>,
|
||||
val requestPermissionEvent: StateEvent<Unit>,
|
||||
val onMoreInfoClick: () -> Unit,
|
||||
) : PushNotificationSettingsUM
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ import com.tangem.core.decompose.model.ParamsContainer
|
|||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.notifications.SystemNotificationsStateProvider
|
||||
import com.tangem.core.navigation.settings.SettingsManager
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.event.triggeredEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
|
|
@ -38,6 +35,8 @@ import com.tangem.utils.coroutines.saveIn
|
|||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -45,6 +44,7 @@ import kotlinx.coroutines.flow.catch
|
|||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -69,7 +69,6 @@ internal class PushNotificationSettingsModel @Inject constructor(
|
|||
|
||||
private val loadState = MutableStateFlow<LoadState>(LoadState.Loading)
|
||||
private val osNotificationsEnabled = MutableStateFlow(systemNotificationsStateProvider.areNotificationsEnabled())
|
||||
private val pendingRequest = MutableStateFlow<StateEvent<Unit>>(consumedEvent())
|
||||
|
||||
private var pendingPermissionToggle: ToggleSpec? = null
|
||||
private val preferencesJobHolder = JobHolder()
|
||||
|
|
@ -77,15 +76,19 @@ internal class PushNotificationSettingsModel @Inject constructor(
|
|||
private val cachedPrefs: WalletPushNotificationPreferences?
|
||||
get() = (loadState.value as? LoadState.Content)?.prefs
|
||||
|
||||
private val requestPushPermissionChannel = Channel<Unit>(Channel.BUFFERED)
|
||||
|
||||
/** One-shot requests to launch the system push permission prompt, consumed by the component. */
|
||||
val requestPushPermission: Flow<Unit> = requestPushPermissionChannel.receiveAsFlow()
|
||||
|
||||
val uiState: StateFlow<PushNotificationSettingsUM> = combine(
|
||||
loadState,
|
||||
osNotificationsEnabled,
|
||||
pendingRequest,
|
||||
) { load, osEnabled, request ->
|
||||
) { load, osEnabled ->
|
||||
when (load) {
|
||||
is LoadState.Failed -> PushNotificationSettingsUM.Error(onRetryClick = ::onRetry)
|
||||
is LoadState.Loading -> PushNotificationSettingsUM.Loading
|
||||
is LoadState.Content -> buildContent(prefs = load.prefs, osEnabled = osEnabled, request = request)
|
||||
is LoadState.Content -> buildContent(prefs = load.prefs, osEnabled = osEnabled)
|
||||
}
|
||||
}.stateIn(
|
||||
scope = modelScope,
|
||||
|
|
@ -117,7 +120,6 @@ internal class PushNotificationSettingsModel @Inject constructor(
|
|||
}
|
||||
|
||||
fun onPermissionResult(isGranted: Boolean) {
|
||||
pendingRequest.value = consumedEvent()
|
||||
val tapped = pendingPermissionToggle
|
||||
pendingPermissionToggle = null
|
||||
modelScope.launch {
|
||||
|
|
@ -145,12 +147,10 @@ internal class PushNotificationSettingsModel @Inject constructor(
|
|||
private fun buildContent(
|
||||
prefs: WalletPushNotificationPreferences,
|
||||
osEnabled: Boolean,
|
||||
request: StateEvent<Unit>,
|
||||
): PushNotificationSettingsUM.Content {
|
||||
return PushNotificationSettingsUM.Content(
|
||||
banner = buildBanner(prefs = prefs, osEnabled = osEnabled),
|
||||
toggles = buildToggles(prefs),
|
||||
requestPermissionEvent = request,
|
||||
onMoreInfoClick = ::onMoreInfoClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -190,7 +190,7 @@ internal class PushNotificationSettingsModel @Inject constructor(
|
|||
|
||||
private fun requestPermission(tapped: ToggleSpec? = null) {
|
||||
pendingPermissionToggle = tapped
|
||||
pendingRequest.value = triggeredEvent(data = Unit, onConsume = ::onPermissionEventConsumed)
|
||||
requestPushPermissionChannel.trySend(Unit)
|
||||
}
|
||||
|
||||
private fun onBannerCtaClick() {
|
||||
|
|
@ -219,10 +219,6 @@ internal class PushNotificationSettingsModel @Inject constructor(
|
|||
applyOptimisticToggle(spec, newValue)
|
||||
}
|
||||
|
||||
private fun onPermissionEventConsumed() {
|
||||
pendingRequest.value = consumedEvent()
|
||||
}
|
||||
|
||||
private fun applyOptimisticToggle(spec: ToggleSpec, newValue: Boolean) {
|
||||
val current = cachedPrefs ?: return
|
||||
loadState.value = LoadState.Content(current.withCategory(spec.category, newValue))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.features.pushnotificationsettings.impl.ui
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.R as CoreUiR
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.pushnotificationsettings.impl.R
|
||||
import com.tangem.features.pushnotificationsettings.impl.entity.AllowPushNotificationsBannerUM
|
||||
|
||||
@Composable
|
||||
internal fun AllowPushNotificationsBanner(state: AllowPushNotificationsBannerUM, modifier: Modifier = Modifier) {
|
||||
Notification(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.push_notification_settings_banner_title),
|
||||
subtitle = resourceReference(R.string.push_notification_settings_banner_description),
|
||||
iconResId = CoreUiR.drawable.ic_alert_circle_24,
|
||||
iconTint = NotificationConfig.IconTint.Warning,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.common_open_settings_button_title),
|
||||
onClick = state.onOpenSettingsClick,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.features.pushnotificationsettings.impl.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.TangemSwitch
|
||||
import com.tangem.core.ui.components.block.BlockCard
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resolveAnnotatedReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.extensions.styledResourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.pushnotificationsettings.impl.R
|
||||
import com.tangem.features.pushnotificationsettings.impl.entity.ToggleUM
|
||||
|
||||
@Composable
|
||||
internal fun NotificationSettingRow(
|
||||
toggle: ToggleUM,
|
||||
showInlineMoreInfoLink: Boolean,
|
||||
onMoreInfoClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
BlockCard(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResourceSafe(id = toggle.titleRes),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
TangemSwitch(
|
||||
checked = toggle.isOn,
|
||||
onCheckedChange = toggle.onCheckedChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SubtitleText(
|
||||
base = toggle.subtitle,
|
||||
showMoreInfo = showInlineMoreInfoLink,
|
||||
onMoreInfoClick = onMoreInfoClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubtitleText(base: TextReference, showMoreInfo: Boolean, onMoreInfoClick: () -> Unit) {
|
||||
val reference: TextReference = if (showMoreInfo) {
|
||||
combinedReference(
|
||||
base,
|
||||
stringReference(" "),
|
||||
styledResourceReference(
|
||||
id = R.string.push_notifications_more_info,
|
||||
spanStyleReference = {
|
||||
TangemTheme.typography.caption2
|
||||
.copy(color = TangemTheme.colors.text.accent)
|
||||
.toSpanStyle()
|
||||
},
|
||||
onClick = onMoreInfoClick,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
base
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
text = reference.resolveAnnotatedReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package com.tangem.features.pushnotificationsettings.impl.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.pushnotificationsettings.impl.entity.AllowPushNotificationsBannerUM
|
||||
import com.tangem.features.pushnotificationsettings.impl.entity.PushNotificationSettingsUM
|
||||
import com.tangem.features.pushnotificationsettings.impl.entity.ToggleId
|
||||
|
||||
@Composable
|
||||
internal fun PushNotificationSettingsContent(
|
||||
state: PushNotificationSettingsUM.Content,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding(),
|
||||
contentPadding = PaddingValues(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
item(key = "banner") {
|
||||
AnimatedBannerSlot(banner = state.banner)
|
||||
}
|
||||
|
||||
items(items = state.toggles, key = { it.id.name }) { toggle ->
|
||||
NotificationSettingRow(
|
||||
modifier = Modifier.animateItem(),
|
||||
toggle = toggle,
|
||||
showInlineMoreInfoLink = toggle.id == ToggleId.TransactionAlerts,
|
||||
onMoreInfoClick = state.onMoreInfoClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AnimatedBannerSlot(banner: AllowPushNotificationsBannerUM?) {
|
||||
var lastVisible by remember { mutableStateOf(banner) }
|
||||
if (banner != null) {
|
||||
lastVisible = banner
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = banner != null,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
lastVisible?.let { AllowPushNotificationsBanner(state = it) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.features.pushnotificationsettings.impl.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.UnableToLoadData
|
||||
import com.tangem.features.pushnotificationsettings.impl.entity.PushNotificationSettingsUM
|
||||
|
||||
@Composable
|
||||
internal fun PushNotificationSettingsError(state: PushNotificationSettingsUM.Error, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
UnableToLoadData(onRetryClick = state.onRetryClick)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package com.tangem.features.pushnotificationsettings.impl.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.block.BlockCard
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun PushNotificationSettingsLoading(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
repeat(SHIMMER_ROW_COUNT) {
|
||||
ShimmerRow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShimmerRow(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
BlockCard(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(SHIMMER_TITLE_WIDTH)
|
||||
.height(SHIMMER_TITLE_HEIGHT),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(SHIMMER_SWITCH_WIDTH)
|
||||
.height(SHIMMER_SWITCH_HEIGHT),
|
||||
radius = TangemTheme.dimens.radius12,
|
||||
)
|
||||
}
|
||||
}
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.width(SHIMMER_SUBTITLE_WIDTH)
|
||||
.height(SHIMMER_SUBTITLE_HEIGHT),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val SHIMMER_ROW_COUNT = 3
|
||||
private val SHIMMER_TITLE_WIDTH = 160.dp
|
||||
private val SHIMMER_TITLE_HEIGHT = 18.dp
|
||||
private val SHIMMER_SWITCH_WIDTH = 40.dp
|
||||
private val SHIMMER_SWITCH_HEIGHT = 22.dp
|
||||
private val SHIMMER_SUBTITLE_WIDTH = 220.dp
|
||||
private val SHIMMER_SUBTITLE_HEIGHT = 14.dp
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.features.pushnotificationsettings.impl.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.pushnotificationsettings.impl.R
|
||||
import com.tangem.features.pushnotificationsettings.impl.entity.PushNotificationSettingsUM
|
||||
|
||||
@Composable
|
||||
internal fun PushNotificationSettingsScreen(
|
||||
state: PushNotificationSettingsUM,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
topBar = {
|
||||
TangemTopAppBar(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
title = resourceReference(R.string.push_notification_settings_title),
|
||||
startButton = TopAppBarButtonUM.Back(onBackClicked = onBackClick),
|
||||
)
|
||||
},
|
||||
) { padding ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding),
|
||||
) {
|
||||
when (state) {
|
||||
is PushNotificationSettingsUM.Loading -> PushNotificationSettingsLoading()
|
||||
is PushNotificationSettingsUM.Content -> PushNotificationSettingsContent(state = state)
|
||||
is PushNotificationSettingsUM.Error -> PushNotificationSettingsError(state = state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
class PushNotificationSettingsModelTest {
|
||||
|
|
@ -204,13 +204,15 @@ class PushNotificationSettingsModelTest {
|
|||
val model = model(osEnabled = false, preferencesFlow = flow)
|
||||
advanceUntilIdle()
|
||||
|
||||
val offers = (model.uiState.value as PushNotificationSettingsUM.Content)
|
||||
.toggles.first { it.id == ToggleId.OffersUpdates }
|
||||
offers.onCheckedChange(true)
|
||||
advanceUntilIdle()
|
||||
model.requestPushPermission.test {
|
||||
val offers = (model.uiState.value as PushNotificationSettingsUM.Content)
|
||||
.toggles.first { it.id == ToggleId.OffersUpdates }
|
||||
offers.onCheckedChange(true)
|
||||
advanceUntilIdle()
|
||||
|
||||
val content = model.uiState.value as PushNotificationSettingsUM.Content
|
||||
assertThat(content.requestPermissionEvent.javaClass.simpleName).isEqualTo("Triggered")
|
||||
awaitItem()
|
||||
expectNoEvents()
|
||||
}
|
||||
coVerify(exactly = 0) { updatePreference(any(), any(), any()) }
|
||||
}
|
||||
|
||||
|
|
@ -224,12 +226,14 @@ class PushNotificationSettingsModelTest {
|
|||
val banner = requireNotNull(
|
||||
(model.uiState.value as PushNotificationSettingsUM.Content).banner,
|
||||
)
|
||||
banner.onOpenSettingsClick()
|
||||
advanceUntilIdle()
|
||||
model.requestPushPermission.test {
|
||||
banner.onOpenSettingsClick()
|
||||
advanceUntilIdle()
|
||||
|
||||
// The banner CTA opens system settings directly and never asks for the permission.
|
||||
expectNoEvents()
|
||||
}
|
||||
verify(exactly = 1) { settingsManager.openAppNotificationSettings() }
|
||||
val refreshed = model.uiState.value as PushNotificationSettingsUM.Content
|
||||
assertThat(refreshed.requestPermissionEvent.javaClass.simpleName).isEqualTo("Consumed")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue