Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-30 18:22:42 +05:00
parent c55274f40f
commit efcb0dea79
20 changed files with 477 additions and 17 deletions

View file

@ -1,10 +1,7 @@
package com.tangem.tap.di.domain
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.domain.notifications.GetApplicationIdUseCase
import com.tangem.domain.notifications.GetTronFeeNotificationShowCountUseCase
import com.tangem.domain.notifications.IncrementNotificationsShowCountUseCase
import com.tangem.domain.notifications.SendPushTokenUseCase
import com.tangem.domain.notifications.*
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.notifications.toggles.NotificationsFeatureToggles
import com.tangem.tap.domain.notifications.DefaultNotificationsFeatureToggles
@ -64,4 +61,12 @@ internal object NotificationsDomainModule {
fun provideNotificationsFeatureToggles(featureTogglesManager: FeatureTogglesManager): NotificationsFeatureToggles {
return DefaultNotificationsFeatureToggles(featureTogglesManager = featureTogglesManager)
}
@Provides
@Singleton
fun provideGetNetworksAvailableForNotifications(
notificationsRepository: NotificationsRepository,
): GetNetworksAvailableForNotificationsUseCase {
return GetNetworksAvailableForNotificationsUseCase(notificationsRepository = notificationsRepository)
}
}

View file

@ -0,0 +1,77 @@
package com.tangem.core.ui.components.items
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.CircleShimmer
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@Composable
fun NetworkNameAndSymbolItem(icon: Int, name: String, symbol: String, modifier: Modifier = Modifier) {
Row(
modifier = modifier.padding(14.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Image(
modifier = Modifier
.padding(end = 8.dp)
.size(24.dp),
painter = painterResource(icon),
contentDescription = null,
)
Text(
modifier = Modifier.weight(1f, fill = false),
text = name,
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(text = symbol, style = TangemTheme.typography.caption1, color = TangemTheme.colors.text.tertiary)
}
}
@Composable
fun NetworkNameAndSymbolItemShimmer(modifier: Modifier = Modifier) {
Row(
modifier = modifier.padding(12.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
CircleShimmer(
modifier = Modifier
.padding(end = 8.dp)
.size(24.dp),
)
TextShimmer(
style = TangemTheme.typography.subtitle2,
modifier = Modifier.width(70.dp),
)
}
}
@Preview
@Composable
private fun ContentPreview() {
TangemThemePreview {
Column {
NetworkNameAndSymbolItemShimmer()
NetworkNameAndSymbolItem(
icon = R.drawable.ic_solana_16,
name = "Solana",
symbol = "SOL",
)
}
}
}

View file

@ -0,0 +1,31 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:pathData="M25,6H23C14.043,6 9.565,6 6.782,8.782C4,11.565 4,16.043 4,25C4,33.957 4,38.435 6.782,41.217C9.565,44 14.043,44 23,44C31.957,44 36.435,44 39.217,41.217C42,38.435 42,33.957 42,25V23"
android:strokeWidth="3"
android:fillColor="#00000000"
android:strokeColor="#0099FF"
android:strokeLineCap="round"/>
<path
android:pathData="M44,11C44,14.866 40.866,18 37,18C33.134,18 30,14.866 30,11C30,7.134 33.134,4 37,4C40.866,4 44,7.134 44,11Z"
android:strokeWidth="3"
android:fillColor="#00000000"
android:strokeColor="#0099FF"/>
<path
android:pathData="M14,22H22"
android:strokeLineJoin="round"
android:strokeWidth="3"
android:fillColor="#00000000"
android:strokeColor="#0099FF"
android:strokeLineCap="round"/>
<path
android:pathData="M14,32H30"
android:strokeLineJoin="round"
android:strokeWidth="3"
android:fillColor="#00000000"
android:strokeColor="#0099FF"
android:strokeLineCap="round"/>
</vector>

View file

@ -10,6 +10,9 @@ android {
}
dependencies {
// region sdk
implementation(tangemDeps.blockchain)
// endregion
// region AndroidX libraries
implementation(deps.androidx.datastore)
@ -28,6 +31,8 @@ dependencies {
// region Core modules
implementation(projects.core.datasource)
implementation(projects.core.utils)
implementation(projects.core.ui)
implementation(projects.libs.blockchainSdk)
// endregion
// region Domain modules

View file

@ -73,7 +73,7 @@ internal class DefaultNotificationsRepository @Inject constructor(
}
override suspend fun getEligibleNetworks(): List<NotificationsEligibleNetwork> = withContext(dispatchers.io) {
tangemTechApi.getEligibleNetworksForPushNotifications().getOrThrow().map {
tangemTechApi.getEligibleNetworksForPushNotifications().getOrThrow().mapNotNull {
NotificationsEligibleNetworkConverter.convert(it)
}
}

View file

@ -1,15 +1,19 @@
package com.tangem.data.notifications.converters
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.core.ui.extensions.getActiveIconRes
import com.tangem.datasource.api.tangemTech.models.CryptoNetworkResponse
import com.tangem.domain.notifications.models.NotificationsEligibleNetwork
import com.tangem.utils.converter.Converter
internal object NotificationsEligibleNetworkConverter : Converter<CryptoNetworkResponse, NotificationsEligibleNetwork> {
override fun convert(value: CryptoNetworkResponse): NotificationsEligibleNetwork {
internal object NotificationsEligibleNetworkConverter {
fun convert(value: CryptoNetworkResponse): NotificationsEligibleNetwork? {
val blockchain: Blockchain = Blockchain.fromNetworkId(value.networkId) ?: return null
return NotificationsEligibleNetwork(
id = value.id.toString(),
networkId = value.networkId,
name = value.name,
id = value.networkId,
name = blockchain.fullName,
symbol = blockchain.currency,
icon = getActiveIconRes(blockchain.id),
)
}
}

View file

@ -149,7 +149,7 @@ class DefaultNotificationsRepositoryTest {
CryptoNetworkResponse(
id = 1,
name = "Ethereum",
networkId = "Ethereum",
networkId = "ethereum",
),
CryptoNetworkResponse(
id = 2,

View file

@ -2,6 +2,7 @@ package com.tangem.domain.notifications.models
data class NotificationsEligibleNetwork(
val id: String,
val networkId: String,
val name: String,
val symbol: String,
val icon: Int,
)

View file

@ -0,0 +1,14 @@
package com.tangem.domain.notifications
import arrow.core.Either
import com.tangem.domain.notifications.models.NotificationsEligibleNetwork
import com.tangem.domain.notifications.repository.NotificationsRepository
class GetNetworksAvailableForNotificationsUseCase(
private val notificationsRepository: NotificationsRepository,
) {
suspend operator fun invoke(): Either<Throwable, List<NotificationsEligibleNetwork>> = Either.catch {
notificationsRepository.getEligibleNetworks()
}
}

View file

@ -0,0 +1,13 @@
package com.tangem.feature.walletsettings.component
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
interface NetworksAvailableForNotificationsComponent : ComposableBottomSheetComponent {
data class Params(
val onDismiss: () -> Unit,
)
interface Factory : ComponentFactory<Params, NetworksAvailableForNotificationsComponent>
}

View file

@ -37,6 +37,8 @@ dependencies {
implementation(projects.domain.demo)
implementation(projects.domain.nft)
implementation(projects.domain.notifications.toggles)
implementation(projects.domain.notifications.models)
implementation(projects.domain.notifications)
/* AndroidX */
implementation(deps.androidx.fragment.ktx)

View file

@ -0,0 +1,40 @@
package com.tangem.feature.walletsettings.component.impl
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.feature.walletsettings.component.NetworksAvailableForNotificationsComponent
import com.tangem.feature.walletsettings.component.impl.model.NetworksAvailableForNotificationsModel
import com.tangem.feature.walletsettings.ui.NetworksAvailableForNotificationsListBS
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultNetworksAvailableForNotificationsComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted private val params: NetworksAvailableForNotificationsComponent.Params,
) : NetworksAvailableForNotificationsComponent, AppComponentContext by context {
private val model: NetworksAvailableForNotificationsModel = getOrCreateModel(params)
override fun dismiss() {
params.onDismiss()
}
@Composable
override fun BottomSheet() {
val state by model.state.collectAsStateWithLifecycle()
NetworksAvailableForNotificationsListBS(state = state, onBack = ::dismiss, onDismiss = ::dismiss)
}
@AssistedFactory
interface Factory : NetworksAvailableForNotificationsComponent.Factory {
override fun create(
context: AppComponentContext,
params: NetworksAvailableForNotificationsComponent.Params,
): DefaultNetworksAvailableForNotificationsComponent
}
}

View file

@ -11,10 +11,13 @@ import com.arkivanov.decompose.router.slot.dismiss
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.core.ui.decompose.ComposableDialogComponent
import com.tangem.feature.walletsettings.component.NetworksAvailableForNotificationsComponent
import com.tangem.feature.walletsettings.component.RenameWalletComponent
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
import com.tangem.feature.walletsettings.entity.DialogConfig
import com.tangem.feature.walletsettings.entity.NetworksAvailableForNotificationBSConfig
import com.tangem.feature.walletsettings.model.WalletSettingsModel
import com.tangem.feature.walletsettings.ui.WalletSettingsScreen
import dagger.assisted.Assisted
@ -25,14 +28,24 @@ internal class DefaultWalletSettingsComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted params: WalletSettingsComponent.Params,
private val renameWalletComponentFactory: RenameWalletComponent.Factory,
private val networksAvailableForNotificationsComponent: NetworksAvailableForNotificationsComponent.Factory,
) : WalletSettingsComponent, AppComponentContext by context {
private val model: WalletSettingsModel = getOrCreateModel(params)
private val bottomSheetSlot = childSlot(
source = model.bottomSheetNavigation,
serializer = null,
handleBackButton = false,
key = "bottomSheetSlot",
childFactory = ::bottomSheetChild,
)
private val dialog = childSlot(
source = model.dialogNavigation,
serializer = DialogConfig.serializer(),
handleBackButton = true,
key = "dialogSlot",
childFactory = ::dialogChild,
)
@ -40,12 +53,15 @@ internal class DefaultWalletSettingsComponent @AssistedInject constructor(
override fun Content(modifier: Modifier) {
val state by model.state.collectAsStateWithLifecycle()
val dialog by dialog.subscribeAsState()
val bottomSheet by bottomSheetSlot.subscribeAsState()
WalletSettingsScreen(
modifier = modifier,
state = state,
dialog = { dialog.child?.instance?.Dialog() },
)
bottomSheet.child?.instance?.BottomSheet()
}
private fun dialogChild(
@ -64,6 +80,18 @@ internal class DefaultWalletSettingsComponent @AssistedInject constructor(
}
}
@Suppress("UnusedPrivateMember")
private fun bottomSheetChild(
config: NetworksAvailableForNotificationBSConfig,
componentContext: ComponentContext,
): ComposableBottomSheetComponent = networksAvailableForNotificationsComponent.create(
context = childByContext(componentContext),
params = NetworksAvailableForNotificationsComponent.Params(
onDismiss = model
.bottomSheetNavigation::dismiss,
),
)
@AssistedFactory
interface Factory : WalletSettingsComponent.Factory {
override fun create(

View file

@ -0,0 +1,40 @@
package com.tangem.feature.walletsettings.component.impl.model
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.domain.notifications.GetNetworksAvailableForNotificationsUseCase
import com.tangem.feature.walletsettings.ui.state.NetworksAvailableForNotificationsUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
@ModelScoped
internal class NetworksAvailableForNotificationsModel @Inject constructor(
private val getNetworksAvailableForNotificationsUseCase: GetNetworksAvailableForNotificationsUseCase,
override val dispatchers: CoroutineDispatcherProvider,
) : Model() {
val state: StateFlow<NetworksAvailableForNotificationsUM> get() = _state
private val _state = MutableStateFlow(
value = NetworksAvailableForNotificationsUM(
networks = persistentListOf(),
isLoading = true,
),
)
init {
modelScope.launch {
getNetworksAvailableForNotificationsUseCase().onRight { networks ->
_state.update {
it.copy(networks = networks.toImmutableList(), isLoading = false)
}
}
}
}
}

View file

@ -2,8 +2,10 @@ package com.tangem.feature.walletsettings.di
import com.tangem.feature.walletsettings.component.RenameWalletComponent
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
import com.tangem.feature.walletsettings.component.impl.DefaultNetworksAvailableForNotificationsComponent
import com.tangem.feature.walletsettings.component.impl.DefaultRenameWalletComponent
import com.tangem.feature.walletsettings.component.impl.DefaultWalletSettingsComponent
import com.tangem.feature.walletsettings.component.NetworksAvailableForNotificationsComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@ -23,4 +25,10 @@ internal interface WalletSettingsComponentModule {
@Binds
@Singleton
fun bindRenameWalletComponentFactory(factory: DefaultRenameWalletComponent.Factory): RenameWalletComponent.Factory
@Binds
@Singleton
fun bindNetworksComponentFactory(
factory: DefaultNetworksAvailableForNotificationsComponent.Factory,
): NetworksAvailableForNotificationsComponent.Factory
}

View file

@ -2,6 +2,7 @@ package com.tangem.feature.walletsettings.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
import com.tangem.feature.walletsettings.component.impl.model.NetworksAvailableForNotificationsModel
import com.tangem.feature.walletsettings.component.impl.model.RenameWalletModel
import com.tangem.feature.walletsettings.model.WalletSettingsModel
import dagger.Binds
@ -23,4 +24,9 @@ internal interface WalletSettingsModelModule {
@IntoMap
@ClassKey(RenameWalletModel::class)
fun bindRenameWalletModel(model: RenameWalletModel): Model
@Binds
@IntoMap
@ClassKey(NetworksAvailableForNotificationsModel::class)
fun bindNetworksAvailableForNotificationsModel(model: NetworksAvailableForNotificationsModel): Model
}

View file

@ -0,0 +1,6 @@
package com.tangem.feature.walletsettings.entity
import kotlinx.serialization.Serializable
@Serializable
internal object NetworksAvailableForNotificationBSConfig

View file

@ -13,9 +13,7 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.settings.SettingsManager
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.core.ui.message.*
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.models.scan.CardDTO
@ -31,6 +29,7 @@ import com.tangem.domain.wallets.usecase.*
import com.tangem.feature.walletsettings.analytics.Settings
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
import com.tangem.feature.walletsettings.entity.DialogConfig
import com.tangem.feature.walletsettings.entity.NetworksAvailableForNotificationBSConfig
import com.tangem.feature.walletsettings.entity.WalletSettingsItemUM
import com.tangem.feature.walletsettings.entity.WalletSettingsUM
import com.tangem.feature.walletsettings.impl.R
@ -70,6 +69,7 @@ internal class WalletSettingsModel @Inject constructor(
val params: WalletSettingsComponent.Params = paramsContainer.require()
val dialogNavigation = SlotNavigation<DialogConfig>()
val bottomSheetNavigation: SlotNavigation<NetworksAvailableForNotificationBSConfig> = SlotNavigation()
val state: MutableStateFlow<WalletSettingsUM> = MutableStateFlow(
value = WalletSettingsUM(
@ -215,7 +215,7 @@ internal class WalletSettingsModel @Inject constructor(
}
private fun onNotificationsDescriptionClick() {
// TODO [REDACTED_JIRA]
bottomSheetNavigation.activate(NetworksAvailableForNotificationBSConfig)
}
private fun openPushSystemSettings() {

View file

@ -0,0 +1,171 @@
package com.tangem.feature.walletsettings.ui
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.SpacerH24
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetWithFooter
import com.tangem.core.ui.components.items.NetworkNameAndSymbolItem
import com.tangem.core.ui.components.items.NetworkNameAndSymbolItemShimmer
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.domain.notifications.models.NotificationsEligibleNetwork
import com.tangem.feature.walletsettings.ui.state.NetworksAvailableForNotificationsUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun NetworksAvailableForNotificationsListBS(
state: NetworksAvailableForNotificationsUM,
onBack: () -> Unit,
onDismiss: () -> Unit,
) {
TangemModalBottomSheetWithFooter<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = onDismiss,
content = TangemBottomSheetConfigContent.Empty,
),
containerColor = TangemTheme.colors.background.tertiary,
onBack = onBack,
title = {
TangemModalBottomSheetTitle(
endIconRes = R.drawable.ic_close_24,
onEndClick = onDismiss,
)
},
content = {
NetworksAvailableForNotificationsListBottomSheetContent(
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
state = state,
)
},
footer = {
SecondaryButton(
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.balance_hidden_got_it_button),
onClick = onDismiss,
)
},
)
}
@Composable
private fun NetworksAvailableForNotificationsListBottomSheetContent(
state: NetworksAvailableForNotificationsUM,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier.background(TangemTheme.colors.background.primary),
) {
Header()
SpacerH24()
Networks(networks = state.networks, isLoading = state.isLoading)
}
}
@Composable
private fun ColumnScope.Header() {
Icon(
painter = painterResource(id = R.drawable.ic_notification_48),
contentDescription = null,
tint = TangemTheme.colors.icon.accent,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.size(TangemTheme.dimens.size56),
)
Text(
text = stringResourceSafe(R.string.push_transactions_notifications_title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(
start = TangemTheme.dimens.spacing34,
end = TangemTheme.dimens.spacing34,
top = TangemTheme.dimens.spacing28,
),
)
Text(
text = stringResourceSafe(R.string.push_transactions_notifications_description),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(
start = TangemTheme.dimens.spacing34,
end = TangemTheme.dimens.spacing34,
top = TangemTheme.dimens.spacing8,
),
)
}
@Composable
private fun Networks(
networks: ImmutableList<NotificationsEligibleNetwork>,
modifier: Modifier = Modifier,
isLoading: Boolean = false,
) {
Column(modifier = modifier) {
Text(
modifier = Modifier.padding(top = 12.dp, bottom = 4.dp, start = 12.dp, end = 12.dp),
text = stringResourceSafe(R.string.common_supported_networks),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
if (isLoading) {
repeat(SHIMMERS_COUNT) {
NetworkNameAndSymbolItemShimmer()
}
} else {
networks.fastForEach { network ->
key(network.id) {
NetworkNameAndSymbolItem(
icon = network.icon,
name = network.name,
symbol = network.symbol,
)
}
}
}
}
}
private const val SHIMMERS_COUNT = 10
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun Preview_CustomTokenNetworkSelectorContent() {
TangemThemePreview {
NetworksAvailableForNotificationsListBS(
state = NetworksAvailableForNotificationsUM(
networks = persistentListOf(),
isLoading = true,
),
onDismiss = {},
onBack = {},
)
}
}

View file

@ -0,0 +1,9 @@
package com.tangem.feature.walletsettings.ui.state
import com.tangem.domain.notifications.models.NotificationsEligibleNetwork
import kotlinx.collections.immutable.ImmutableList
data class NetworksAvailableForNotificationsUM(
val networks: ImmutableList<NotificationsEligibleNetwork>,
val isLoading: Boolean,
)