Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-04 13:54:22 +04:00
parent 14df650f1a
commit be160483d2
25 changed files with 519 additions and 268 deletions

View file

@ -3,6 +3,7 @@ package com.tangem.tap.di.domain
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.domain.notifications.*
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.notifications.repository.PushNotificationsRepository
import com.tangem.domain.notifications.toggles.NotificationsFeatureToggles
import com.tangem.tap.domain.notifications.DefaultNotificationsFeatureToggles
import com.tangem.utils.notifications.PushNotificationsTokenProvider
@ -18,20 +19,22 @@ internal object NotificationsDomainModule {
@Provides
@Singleton
fun providesGetApplicationIdUseCase(notificationsRepository: NotificationsRepository): GetApplicationIdUseCase {
fun providesGetApplicationIdUseCase(
pushNotificationsRepository: PushNotificationsRepository,
): GetApplicationIdUseCase {
return GetApplicationIdUseCase(
notificationsRepository = notificationsRepository,
pushNotificationsRepository = pushNotificationsRepository,
)
}
@Provides
@Singleton
fun providesSendPushTokenUseCase(
notificationsRepository: NotificationsRepository,
pushNotificationsRepository: PushNotificationsRepository,
pushNotificationsTokenProvider: PushNotificationsTokenProvider,
): SendPushTokenUseCase {
return SendPushTokenUseCase(
notificationsRepository = notificationsRepository,
pushNotificationsRepository = pushNotificationsRepository,
pushNotificationsTokenProvider = pushNotificationsTokenProvider,
)
}
@ -56,6 +59,26 @@ internal object NotificationsDomainModule {
)
}
@Provides
@Singleton
fun providesShouldShowNotificationUseCase(
notificationsRepository: NotificationsRepository,
): ShouldShowNotificationUseCase {
return ShouldShowNotificationUseCase(
notificationsRepository = notificationsRepository,
)
}
@Provides
@Singleton
fun providesSetShouldShowNotificationUseCase(
notificationsRepository: NotificationsRepository,
): SetShouldShowNotificationUseCase {
return SetShouldShowNotificationUseCase(
notificationsRepository = notificationsRepository,
)
}
@Provides
@Singleton
fun provideNotificationsFeatureToggles(featureTogglesManager: FeatureTogglesManager): NotificationsFeatureToggles {
@ -65,8 +88,8 @@ internal object NotificationsDomainModule {
@Provides
@Singleton
fun provideGetNetworksAvailableForNotifications(
notificationsRepository: NotificationsRepository,
pushNotificationsRepository: PushNotificationsRepository,
): GetNetworksAvailableForNotificationsUseCase {
return GetNetworksAvailableForNotificationsUseCase(notificationsRepository = notificationsRepository)
return GetNetworksAvailableForNotificationsUseCase(pushNotificationsRepository = pushNotificationsRepository)
}
}

View file

@ -434,6 +434,7 @@ internal class ChildFactory @Inject constructor(
initialCurrency = route.initialCurrency,
selectedCurrency = route.selectedCurrency,
source = ChooseManagedTokensComponent.Source.valueOf(route.source.name),
showSendViaSwapNotification = route.showSendViaSwapNotification,
),
componentFactory = chooseManagedTokensComponentFactory,
)

View file

@ -133,6 +133,7 @@ sealed class AppRoute(val path: String) : Route {
val initialCurrency: CryptoCurrency,
val selectedCurrency: CryptoCurrency?,
val source: Source,
val showSendViaSwapNotification: Boolean,
) : AppRoute(path = "/$source/choose_managed_tokens/$userWalletId/${initialCurrency.id.value}") {
enum class Source {
SendViaSwap,

View file

@ -0,0 +1,11 @@
package com.tangem.common.ui.notifications
/**
* NotificationId represents unique identifiers for notifications in the app.
*
* These ids can be used with [ShouldShowNotificationUseCase] and [SetShouldShowNotificationUseCase]
* to check or update the visibility state of notifications.
*/
enum class NotificationId(val key: String) {
SendViaSwapTokenSelectorNotification("SendViaSwapTokenSelectorNotificationKey"),
}

View file

@ -137,6 +137,8 @@ object PreferencesKeys {
val TRON_NETWORK_FEE_NOTIFICATION_SHOW_COUNT_KEY by lazy {
intPreferencesKey(name = "tronNetworkFeeNotificationShowCount")
}
fun getShouldShowNotificationKey(key: String) = booleanPreferencesKey("showShowNotificationUM_$key")
// endregion
// region Promo

View file

@ -1,48 +1,22 @@
package com.tangem.data.notifications
import com.tangem.data.notifications.converters.NotificationsEligibleNetworkConverter
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.NotificationApplicationCreateBody
import com.tangem.utils.info.AppInfoProvider
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.*
import com.tangem.domain.notifications.models.ApplicationId
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.store
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.notifications.models.NotificationsEligibleNetwork
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import javax.inject.Inject
internal class DefaultNotificationsRepository @Inject constructor(
private val tangemTechApi: TangemTechApi,
private val appInfoProvider: AppInfoProvider,
class DefaultNotificationsRepository @Inject constructor(
private val appPreferencesStore: AppPreferencesStore,
private val dispatchers: CoroutineDispatcherProvider,
) : NotificationsRepository {
override suspend fun createApplicationId(pushToken: String?): ApplicationId = withContext(dispatchers.io) {
tangemTechApi.createApplicationId(
NotificationApplicationCreateBody(
platform = appInfoProvider.platform.lowercase(),
device = appInfoProvider.device,
systemVersion = appInfoProvider.osVersion,
language = appInfoProvider.language,
timezone = appInfoProvider.timezone,
version = appInfoProvider.appVersion,
pushToken = pushToken,
),
).getOrThrow().appId.let(::ApplicationId)
override suspend fun shouldShowNotification(key: String): Boolean {
return appPreferencesStore.getSyncOrDefault(PreferencesKeys.getShouldShowNotificationKey(key), true)
}
override suspend fun saveApplicationId(appId: ApplicationId) {
appPreferencesStore.store(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY, appId.value)
}
override suspend fun getApplicationId(): ApplicationId? {
return appPreferencesStore.getSyncOrNull(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY)
?.let(::ApplicationId)
override suspend fun setShouldShowNotifications(key: String, value: Boolean) {
appPreferencesStore.store(PreferencesKeys.getShouldShowNotificationKey(key), value)
}
override suspend fun incrementTronTokenFeeNotificationShowCounter() {
@ -61,25 +35,4 @@ internal class DefaultNotificationsRepository @Inject constructor(
default = 0,
)
}
override suspend fun sendPushToken(appId: ApplicationId, pushToken: String) {
withContext(dispatchers.io) {
tangemTechApi.updatePushTokenForApplicationId(
appId.value,
NotificationApplicationCreateBody(
pushToken = pushToken,
systemVersion = appInfoProvider.osVersion,
language = appInfoProvider.language,
timezone = appInfoProvider.timezone,
version = appInfoProvider.appVersion,
),
).getOrThrow()
}
}
override suspend fun getEligibleNetworks(): List<NotificationsEligibleNetwork> = withContext(dispatchers.io) {
tangemTechApi.getEligibleNetworksForPushNotifications().getOrThrow().mapNotNull {
NotificationsEligibleNetworkConverter.convert(it)
}
}
}

View file

@ -0,0 +1,68 @@
package com.tangem.data.notifications
import com.tangem.data.notifications.converters.NotificationsEligibleNetworkConverter
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.NotificationApplicationCreateBody
import com.tangem.utils.info.AppInfoProvider
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.*
import com.tangem.domain.notifications.models.ApplicationId
import com.tangem.domain.notifications.repository.PushNotificationsRepository
import com.tangem.domain.notifications.models.NotificationsEligibleNetwork
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import javax.inject.Inject
internal class DefaultPushNotificationsRepository @Inject constructor(
private val tangemTechApi: TangemTechApi,
private val appInfoProvider: AppInfoProvider,
private val appPreferencesStore: AppPreferencesStore,
private val dispatchers: CoroutineDispatcherProvider,
) : PushNotificationsRepository {
override suspend fun createApplicationId(pushToken: String?): ApplicationId = withContext(dispatchers.io) {
tangemTechApi.createApplicationId(
NotificationApplicationCreateBody(
platform = appInfoProvider.platform.lowercase(),
device = appInfoProvider.device,
systemVersion = appInfoProvider.osVersion,
language = appInfoProvider.language,
timezone = appInfoProvider.timezone,
version = appInfoProvider.appVersion,
pushToken = pushToken,
),
).getOrThrow().appId.let(::ApplicationId)
}
override suspend fun saveApplicationId(appId: ApplicationId) {
appPreferencesStore.store(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY, appId.value)
}
override suspend fun getApplicationId(): ApplicationId? {
return appPreferencesStore.getSyncOrNull(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY)
?.let(::ApplicationId)
}
override suspend fun sendPushToken(appId: ApplicationId, pushToken: String) {
withContext(dispatchers.io) {
tangemTechApi.updatePushTokenForApplicationId(
appId.value,
NotificationApplicationCreateBody(
pushToken = pushToken,
systemVersion = appInfoProvider.osVersion,
language = appInfoProvider.language,
timezone = appInfoProvider.timezone,
version = appInfoProvider.appVersion,
),
).getOrThrow()
}
}
override suspend fun getEligibleNetworks(): List<NotificationsEligibleNetwork> = withContext(dispatchers.io) {
tangemTechApi.getEligibleNetworksForPushNotifications().getOrThrow().mapNotNull {
NotificationsEligibleNetworkConverter.convert(it)
}
}
}

View file

@ -1,7 +1,9 @@
package com.tangem.data.notifications.di
import com.tangem.data.notifications.DefaultNotificationsRepository
import com.tangem.data.notifications.DefaultPushNotificationsRepository
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.notifications.repository.PushNotificationsRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@ -12,6 +14,10 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
internal interface NotificationsModule {
@Binds
@Singleton
fun bindPushNotificationsRepository(repository: DefaultPushNotificationsRepository): PushNotificationsRepository
@Binds
@Singleton
fun bindNotificationsRepository(repository: DefaultNotificationsRepository): NotificationsRepository

View file

@ -1,30 +1,21 @@
package com.tangem.data.notifications
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.utils.info.AppInfoProvider
import com.google.common.truth.Truth.assertThat
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.google.common.truth.Truth.assertThat
import com.squareup.moshi.Moshi
import com.tangem.data.notifications.converters.NotificationsEligibleNetworkConverter
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.tangemTech.models.*
import com.tangem.domain.notifications.models.ApplicationId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
import androidx.datastore.preferences.core.Preferences
import com.squareup.moshi.Moshi
import androidx.datastore.core.DataStore
import kotlinx.coroutines.flow.flowOf
class DefaultNotificationsRepositoryTest {
private val tangemTechApi: TangemTechApi = mockk()
private val appInfoProvider: AppInfoProvider = mockk()
private val preferencesDataStore: DataStore<Preferences> = mockk()
private val appPreferencesStore = AppPreferencesStore(
moshi = Moshi.Builder().build(),
@ -32,147 +23,77 @@ class DefaultNotificationsRepositoryTest {
preferencesDataStore = preferencesDataStore,
)
private val repository = DefaultNotificationsRepository(
tangemTechApi = tangemTechApi,
appInfoProvider = appInfoProvider,
appPreferencesStore = appPreferencesStore,
dispatchers = TestingCoroutineDispatcherProvider(),
)
@Test
fun `GIVEN valid push token WHEN createApplicationId THEN returns application id`() = runTest {
fun `GIVEN shouldShowNotification returns true WHEN called THEN returns true`() = runTest {
// GIVEN
val pushToken = "test-push-token"
val expectedAppId = ApplicationId("test-app-id")
val expectedAppIdResponse = NotificationApplicationIdResponse(
appId = expectedAppId.value,
)
coEvery { appInfoProvider.platform } returns "android"
coEvery { appInfoProvider.device } returns "test-device"
coEvery { appInfoProvider.osVersion } returns "11"
coEvery { appInfoProvider.language } returns "en"
coEvery { appInfoProvider.appVersion } returns "5.21.1"
coEvery { appInfoProvider.timezone } returns "UTC"
coEvery { tangemTechApi.createApplicationId(any()) } returns ApiResponse.Success(
expectedAppIdResponse,
)
val key = "test-key"
val preferences = mockk<Preferences>(relaxed = true)
every { preferences[PreferencesKeys.getShouldShowNotificationKey(key)] } returns true
coEvery { preferencesDataStore.data } returns flowOf(preferences)
// WHEN
val result = repository.createApplicationId(pushToken)
val result = repository.shouldShowNotification(key)
// THEN
assertThat(result).isEqualTo(expectedAppId)
coVerify {
tangemTechApi.createApplicationId(
NotificationApplicationCreateBody(
platform = "android",
device = "test-device",
systemVersion = "11",
language = "en",
timezone = "UTC",
version = "5.21.1",
pushToken = pushToken,
),
)
}
assertThat(result).isTrue()
}
@Test
fun `GIVEN application id WHEN saveApplicationId THEN stores it in preferences`() = runTest {
fun `GIVEN shouldShowNotification returns false WHEN called THEN returns false`() = runTest {
// GIVEN
val appId = ApplicationId("test-app-id")
val key = "test-key"
val preferences = mockk<Preferences>(relaxed = true)
coEvery { preferencesDataStore.updateData(any()) } returns preferences
every { preferences[PreferencesKeys.getShouldShowNotificationKey(key)] } returns false
coEvery { preferencesDataStore.data } returns flowOf(preferences)
// WHEN
repository.saveApplicationId(appId)
val result = repository.shouldShowNotification(key)
// THEN
assertThat(result).isFalse()
}
@Test
fun `GIVEN setShouldShowNotifications WHEN called THEN stores value in preferences`() = runTest {
// GIVEN
val key = "test-key"
val value = false
coEvery { preferencesDataStore.updateData(any()) } returns mockk(relaxed = true)
// WHEN
repository.setShouldShowNotifications(key, value)
// THEN
coVerify { preferencesDataStore.updateData(any()) }
}
@Test
fun `GIVEN stored application id WHEN getApplicationId THEN returns it`() = runTest {
fun `GIVEN incrementTronTokenFeeNotificationShowCounter WHEN called THEN increments counter`() = runTest {
// GIVEN
val expectedAppId = ApplicationId("test-app-id")
coEvery { preferencesDataStore.updateData(any()) } returns mockk(relaxed = true)
// WHEN
repository.incrementTronTokenFeeNotificationShowCounter()
// THEN
coVerify { preferencesDataStore.updateData(any()) }
}
@Test
fun `GIVEN getTronTokenFeeNotificationShowCounter WHEN called THEN returns counter value`() = runTest {
// GIVEN
val expectedCount = 5
val preferences = mockk<Preferences>(relaxed = true)
val key = stringPreferencesKey(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY.name)
every { preferences[key] } returns expectedAppId.value
every { preferences[PreferencesKeys.TRON_NETWORK_FEE_NOTIFICATION_SHOW_COUNT_KEY] } returns expectedCount
coEvery { preferencesDataStore.data } returns flowOf(preferences)
// WHEN
val result = repository.getApplicationId()
val result = repository.getTronTokenFeeNotificationShowCounter()
// THEN
assertThat(result).isEqualTo(expectedAppId)
}
@Test
fun `GIVEN application id and push token WHEN sendPushToken THEN updates push token`() = runTest {
// GIVEN
val appId = ApplicationId("test-app-id")
val pushToken = "test-push-token"
coEvery { appInfoProvider.device } returns "test-device"
coEvery { appInfoProvider.osVersion } returns "11"
coEvery { appInfoProvider.language } returns "en"
coEvery { appInfoProvider.appVersion } returns "5.21.1"
coEvery { appInfoProvider.timezone } returns "UTC"
coEvery {
tangemTechApi.updatePushTokenForApplicationId(
appId.value,
NotificationApplicationCreateBody(
pushToken = pushToken,
systemVersion = "11",
language = "en",
timezone = "UTC",
version = "5.21.1",
),
)
} returns ApiResponse.Success(Unit)
// WHEN
repository.sendPushToken(appId, pushToken)
// THEN
coVerify {
tangemTechApi.updatePushTokenForApplicationId(
appId.value,
NotificationApplicationCreateBody(
pushToken = pushToken,
systemVersion = "11",
language = "en",
timezone = "UTC",
version = "5.21.1",
),
)
}
}
@Test
fun `GIVEN eligible networks WHEN getEligibleNetworks THEN returns converted networks`() = runTest {
// GIVEN
val expectedNetworks = listOf(
CryptoNetworkResponse(
id = 1,
name = "Ethereum",
networkId = "ethereum",
),
CryptoNetworkResponse(
id = 2,
name = "Bitcoin",
networkId = "bitcoin",
),
)
coEvery { tangemTechApi.getEligibleNetworksForPushNotifications() } returns ApiResponse.Success(
expectedNetworks,
)
// WHEN
val result = repository.getEligibleNetworks()
// THEN
assertThat(result).hasSize(2)
assertThat(result[0]).isEqualTo(NotificationsEligibleNetworkConverter.convert(expectedNetworks[0]))
assertThat(result[1]).isEqualTo(NotificationsEligibleNetworkConverter.convert(expectedNetworks[1]))
coVerify { tangemTechApi.getEligibleNetworksForPushNotifications() }
assertThat(result).isEqualTo(expectedCount)
}
}

View file

@ -0,0 +1,178 @@
package com.tangem.data.notifications
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.utils.info.AppInfoProvider
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.google.common.truth.Truth.assertThat
import com.squareup.moshi.Moshi
import com.tangem.data.notifications.converters.NotificationsEligibleNetworkConverter
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.tangemTech.models.*
import com.tangem.domain.notifications.models.ApplicationId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
class DefaultPushNotificationsRepositoryTest {
private val tangemTechApi: TangemTechApi = mockk()
private val appInfoProvider: AppInfoProvider = mockk()
private val preferencesDataStore: DataStore<Preferences> = mockk()
private val appPreferencesStore = AppPreferencesStore(
moshi = Moshi.Builder().build(),
dispatchers = TestingCoroutineDispatcherProvider(),
preferencesDataStore = preferencesDataStore,
)
private val repository = DefaultPushNotificationsRepository(
tangemTechApi = tangemTechApi,
appInfoProvider = appInfoProvider,
appPreferencesStore = appPreferencesStore,
dispatchers = TestingCoroutineDispatcherProvider(),
)
@Test
fun `GIVEN valid push token WHEN createApplicationId THEN returns application id`() = runTest {
// GIVEN
val pushToken = "test-push-token"
val expectedAppId = ApplicationId("test-app-id")
val expectedAppIdResponse = NotificationApplicationIdResponse(
appId = expectedAppId.value,
)
coEvery { appInfoProvider.platform } returns "android"
coEvery { appInfoProvider.device } returns "test-device"
coEvery { appInfoProvider.osVersion } returns "11"
coEvery { appInfoProvider.language } returns "en"
coEvery { appInfoProvider.appVersion } returns "5.21.1"
coEvery { appInfoProvider.timezone } returns "UTC"
coEvery { tangemTechApi.createApplicationId(any()) } returns ApiResponse.Success(
expectedAppIdResponse,
)
// WHEN
val result = repository.createApplicationId(pushToken)
// THEN
assertThat(result).isEqualTo(expectedAppId)
coVerify {
tangemTechApi.createApplicationId(
NotificationApplicationCreateBody(
platform = "android",
device = "test-device",
systemVersion = "11",
language = "en",
timezone = "UTC",
version = "5.21.1",
pushToken = pushToken,
),
)
}
}
@Test
fun `GIVEN application id WHEN saveApplicationId THEN stores it in preferences`() = runTest {
// GIVEN
val appId = ApplicationId("test-app-id")
val preferences = mockk<Preferences>(relaxed = true)
coEvery { preferencesDataStore.updateData(any()) } returns preferences
// WHEN
repository.saveApplicationId(appId)
// THEN
coVerify { preferencesDataStore.updateData(any()) }
}
@Test
fun `GIVEN stored application id WHEN getApplicationId THEN returns it`() = runTest {
// GIVEN
val expectedAppId = ApplicationId("test-app-id")
val preferences = mockk<Preferences>(relaxed = true)
val key = stringPreferencesKey(PreferencesKeys.NOTIFICATIONS_APPLICATION_ID_KEY.name)
every { preferences[key] } returns expectedAppId.value
coEvery { preferencesDataStore.data } returns flowOf(preferences)
// WHEN
val result = repository.getApplicationId()
// THEN
assertThat(result).isEqualTo(expectedAppId)
}
@Test
fun `GIVEN application id and push token WHEN sendPushToken THEN updates push token`() = runTest {
// GIVEN
val appId = ApplicationId("test-app-id")
val pushToken = "test-push-token"
coEvery { appInfoProvider.device } returns "test-device"
coEvery { appInfoProvider.osVersion } returns "11"
coEvery { appInfoProvider.language } returns "en"
coEvery { appInfoProvider.appVersion } returns "5.21.1"
coEvery { appInfoProvider.timezone } returns "UTC"
coEvery {
tangemTechApi.updatePushTokenForApplicationId(
appId.value,
NotificationApplicationCreateBody(
pushToken = pushToken,
systemVersion = "11",
language = "en",
timezone = "UTC",
version = "5.21.1",
),
)
} returns ApiResponse.Success(Unit)
// WHEN
repository.sendPushToken(appId, pushToken)
// THEN
coVerify {
tangemTechApi.updatePushTokenForApplicationId(
appId.value,
NotificationApplicationCreateBody(
pushToken = pushToken,
systemVersion = "11",
language = "en",
timezone = "UTC",
version = "5.21.1",
),
)
}
}
@Test
fun `GIVEN eligible networks WHEN getEligibleNetworks THEN returns converted networks`() = runTest {
// GIVEN
val expectedNetworks = listOf(
CryptoNetworkResponse(
id = 1,
name = "Ethereum",
networkId = "ethereum",
),
CryptoNetworkResponse(
id = 2,
name = "Bitcoin",
networkId = "bitcoin",
),
)
coEvery { tangemTechApi.getEligibleNetworksForPushNotifications() } returns ApiResponse.Success(
expectedNetworks,
)
// WHEN
val result = repository.getEligibleNetworks()
// THEN
assertThat(result).hasSize(2)
assertThat(result[0]).isEqualTo(NotificationsEligibleNetworkConverter.convert(expectedNetworks[0]))
assertThat(result[1]).isEqualTo(NotificationsEligibleNetworkConverter.convert(expectedNetworks[1]))
coVerify { tangemTechApi.getEligibleNetworksForPushNotifications() }
}
}

View file

@ -2,25 +2,25 @@ package com.tangem.domain.notifications
import arrow.core.Either
import com.tangem.domain.notifications.models.ApplicationId
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.notifications.repository.PushNotificationsRepository
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
class GetApplicationIdUseCase(
private val notificationsRepository: NotificationsRepository,
private val pushNotificationsRepository: PushNotificationsRepository,
) {
private val mutex = Mutex()
suspend operator fun invoke(): Either<Throwable, ApplicationId> = Either.catch {
val localApplicationId = notificationsRepository.getApplicationId()
val localApplicationId = pushNotificationsRepository.getApplicationId()
if (localApplicationId != null) return@catch localApplicationId
mutex.withLock {
val doubleCheckedId = notificationsRepository.getApplicationId()
val doubleCheckedId = pushNotificationsRepository.getApplicationId()
if (doubleCheckedId != null) return@withLock doubleCheckedId
val newApplicationId = notificationsRepository.createApplicationId()
notificationsRepository.saveApplicationId(newApplicationId)
val newApplicationId = pushNotificationsRepository.createApplicationId()
pushNotificationsRepository.saveApplicationId(newApplicationId)
newApplicationId
}
}

View file

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

View file

@ -2,16 +2,16 @@ package com.tangem.domain.notifications
import arrow.core.Either
import com.tangem.domain.notifications.models.ApplicationId
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.notifications.repository.PushNotificationsRepository
import com.tangem.utils.notifications.PushNotificationsTokenProvider
class SendPushTokenUseCase(
private val notificationsRepository: NotificationsRepository,
private val pushNotificationsRepository: PushNotificationsRepository,
private val pushNotificationsTokenProvider: PushNotificationsTokenProvider,
) {
suspend operator fun invoke(applicationId: ApplicationId): Either<Throwable, Unit> = Either.catch {
val token = pushNotificationsTokenProvider.getToken()
notificationsRepository.sendPushToken(applicationId, token)
pushNotificationsRepository.sendPushToken(applicationId, token)
}
}

View file

@ -0,0 +1,12 @@
package com.tangem.domain.notifications
import com.tangem.domain.notifications.repository.NotificationsRepository
class SetShouldShowNotificationUseCase(
private val notificationsRepository: NotificationsRepository,
) {
suspend operator fun invoke(key: String, value: Boolean) {
notificationsRepository.setShouldShowNotifications(key, value)
}
}

View file

@ -0,0 +1,12 @@
package com.tangem.domain.notifications
import com.tangem.domain.notifications.repository.NotificationsRepository
class ShouldShowNotificationUseCase(
private val notificationsRepository: NotificationsRepository,
) {
suspend operator fun invoke(key: String): Boolean {
return notificationsRepository.shouldShowNotification(key)
}
}

View file

@ -1,24 +1,40 @@
package com.tangem.domain.notifications.repository
import com.tangem.domain.notifications.models.ApplicationId
import com.tangem.domain.notifications.models.NotificationsEligibleNetwork
/**
* Repository interface for managing local notification logic and state.
*
* This interface provides methods to check and update whether specific notifications should be shown,
* as well as to track the display count for certain notifications (e.g., Tron token fee).
*
* Note: This repository is responsible only for the local logic and state (such as preferences and counters)
* regarding notifications. It does **not** directly show or hide notifications to the user.
* The actual display and hiding of notifications in the UI is handled by [NotificationsUM],
* which uses this repository to determine the appropriate behavior.
*/
interface NotificationsRepository {
@Throws
suspend fun createApplicationId(pushToken: String? = null): ApplicationId
/**
* Checks whether a notification with the given [key] should be shown to the user.
* @param key The unique identifier for the notification.
* @return true if the notification should be shown, false otherwise.
*/
suspend fun shouldShowNotification(key: String): Boolean
suspend fun saveApplicationId(appId: ApplicationId)
suspend fun getApplicationId(): ApplicationId?
/**
* Sets whether a notification with the given [key] should be shown to the user.
* @param key The unique identifier for the notification.
* @param value true if the notification should be shown, false otherwise.
*/
suspend fun setShouldShowNotifications(key: String, value: Boolean)
/**
* Gets the number of times the Tron token fee notification has been shown.
* @return The current show counter for the Tron token fee notification.
*/
suspend fun getTronTokenFeeNotificationShowCounter(): Int
/**
* Increments the counter tracking how many times the Tron token fee notification has been shown.
*/
suspend fun incrementTronTokenFeeNotificationShowCounter()
@Throws
suspend fun sendPushToken(appId: ApplicationId, pushToken: String)
@Throws
suspend fun getEligibleNetworks(): List<NotificationsEligibleNetwork>
}

View file

@ -0,0 +1,20 @@
package com.tangem.domain.notifications.repository
import com.tangem.domain.notifications.models.ApplicationId
import com.tangem.domain.notifications.models.NotificationsEligibleNetwork
interface PushNotificationsRepository {
@Throws
suspend fun createApplicationId(pushToken: String? = null): ApplicationId
suspend fun saveApplicationId(appId: ApplicationId)
suspend fun getApplicationId(): ApplicationId?
@Throws
suspend fun sendPushToken(appId: ApplicationId, pushToken: String)
@Throws
suspend fun getEligibleNetworks(): List<NotificationsEligibleNetwork>
}

View file

@ -3,7 +3,7 @@ package com.tangem.domain.notifications
import arrow.core.Either
import com.google.common.truth.Truth.assertThat
import com.tangem.domain.notifications.models.ApplicationId
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.notifications.repository.PushNotificationsRepository
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.coVerifyOrder
@ -15,14 +15,14 @@ import java.net.SocketTimeoutException
class GetApplicationIdUseCaseTest {
private val notificationsRepository: NotificationsRepository = mockk()
private val useCase = GetApplicationIdUseCase(notificationsRepository)
private val pushNotificationsRepository: PushNotificationsRepository = mockk()
private val useCase = GetApplicationIdUseCase(pushNotificationsRepository)
@Test
fun `GIVEN local application ID exists WHEN invoke THEN return local application ID`() = runTest {
// GIVEN
val expectedApplicationId = ApplicationId("test-app-id")
coEvery { notificationsRepository.getApplicationId() } returns expectedApplicationId
coEvery { pushNotificationsRepository.getApplicationId() } returns expectedApplicationId
// WHEN
val result = useCase()
@ -30,10 +30,10 @@ class GetApplicationIdUseCaseTest {
// THEN
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isEqualTo(expectedApplicationId)
coVerify(exactly = 1) { notificationsRepository.getApplicationId() }
coVerify(exactly = 1) { pushNotificationsRepository.getApplicationId() }
coVerify(inverse = true) {
notificationsRepository.createApplicationId()
notificationsRepository.saveApplicationId(any())
pushNotificationsRepository.createApplicationId()
pushNotificationsRepository.saveApplicationId(any())
}
}
@ -41,9 +41,9 @@ class GetApplicationIdUseCaseTest {
fun `GIVEN local application ID does not exist WHEN invoke THEN create and save new application ID`() = runTest {
// GIVEN
val newApplicationId = ApplicationId("new-app-id")
coEvery { notificationsRepository.getApplicationId() } returns null
coEvery { notificationsRepository.createApplicationId() } returns newApplicationId
coEvery { notificationsRepository.saveApplicationId(newApplicationId) } returns Unit
coEvery { pushNotificationsRepository.getApplicationId() } returns null
coEvery { pushNotificationsRepository.createApplicationId() } returns newApplicationId
coEvery { pushNotificationsRepository.saveApplicationId(newApplicationId) } returns Unit
// WHEN
val result = useCase()
@ -52,10 +52,10 @@ class GetApplicationIdUseCaseTest {
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isEqualTo(newApplicationId)
coVerifyOrder {
notificationsRepository.getApplicationId()
notificationsRepository.getApplicationId()
notificationsRepository.createApplicationId()
notificationsRepository.saveApplicationId(newApplicationId)
pushNotificationsRepository.getApplicationId()
pushNotificationsRepository.getApplicationId()
pushNotificationsRepository.createApplicationId()
pushNotificationsRepository.saveApplicationId(newApplicationId)
}
}
@ -63,7 +63,7 @@ class GetApplicationIdUseCaseTest {
fun `GIVEN repository throws exception WHEN invoke THEN return Either Left with error`() = runTest {
// GIVEN
val expectedError = SocketTimeoutException("Test error")
coEvery { notificationsRepository.getApplicationId() } throws expectedError
coEvery { pushNotificationsRepository.getApplicationId() } throws expectedError
// WHEN
val result = useCase()
@ -71,10 +71,10 @@ class GetApplicationIdUseCaseTest {
// THEN
assertThat(result).isInstanceOf(Either.Left::class.java)
assertThat((result as Either.Left).value).isEqualTo(expectedError)
coVerify(exactly = 1) { notificationsRepository.getApplicationId() }
coVerify(exactly = 1) { pushNotificationsRepository.getApplicationId() }
coVerify(inverse = true) {
notificationsRepository.createApplicationId()
notificationsRepository.saveApplicationId(any())
pushNotificationsRepository.createApplicationId()
pushNotificationsRepository.saveApplicationId(any())
}
}
@ -85,14 +85,14 @@ class GetApplicationIdUseCaseTest {
val newApplicationId = ApplicationId("new-app-id")
var isIdCreated = false
coEvery { notificationsRepository.getApplicationId() } answers {
coEvery { pushNotificationsRepository.getApplicationId() } answers {
if (!isIdCreated) null else newApplicationId
}
coEvery { notificationsRepository.createApplicationId() } answers {
coEvery { pushNotificationsRepository.createApplicationId() } answers {
isIdCreated = true
newApplicationId
}
coEvery { notificationsRepository.saveApplicationId(newApplicationId) } returns Unit
coEvery { pushNotificationsRepository.saveApplicationId(newApplicationId) } returns Unit
// WHEN
val results = coroutineScope {
@ -108,9 +108,9 @@ class GetApplicationIdUseCaseTest {
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isEqualTo(newApplicationId)
}
coVerify(exactly = PARALLEL_COUNT + 1) { notificationsRepository.getApplicationId() }
coVerify(exactly = 1) { notificationsRepository.createApplicationId() }
coVerify(exactly = 1) { notificationsRepository.saveApplicationId(newApplicationId) }
coVerify(exactly = PARALLEL_COUNT + 1) { pushNotificationsRepository.getApplicationId() }
coVerify(exactly = 1) { pushNotificationsRepository.createApplicationId() }
coVerify(exactly = 1) { pushNotificationsRepository.saveApplicationId(newApplicationId) }
}
@Test
@ -119,14 +119,14 @@ class GetApplicationIdUseCaseTest {
val newApplicationId = ApplicationId("new-app-id")
var isIdCreated = false
coEvery { notificationsRepository.getApplicationId() } answers {
coEvery { pushNotificationsRepository.getApplicationId() } answers {
if (!isIdCreated) null else newApplicationId
}
coEvery { notificationsRepository.createApplicationId() } answers {
coEvery { pushNotificationsRepository.createApplicationId() } answers {
isIdCreated = true
newApplicationId
}
coEvery { notificationsRepository.saveApplicationId(newApplicationId) } returns Unit
coEvery { pushNotificationsRepository.saveApplicationId(newApplicationId) } returns Unit
// WHEN
val results = coroutineScope {
@ -143,9 +143,9 @@ class GetApplicationIdUseCaseTest {
assertThat(result).isInstanceOf(Either.Right::class.java)
assertThat((result as Either.Right).value).isEqualTo(newApplicationId)
}
coVerify(exactly = PARALLEL_COUNT + 1) { notificationsRepository.getApplicationId() }
coVerify(exactly = 1) { notificationsRepository.createApplicationId() }
coVerify(exactly = 1) { notificationsRepository.saveApplicationId(newApplicationId) }
coVerify(exactly = PARALLEL_COUNT + 1) { pushNotificationsRepository.getApplicationId() }
coVerify(exactly = 1) { pushNotificationsRepository.createApplicationId() }
coVerify(exactly = 1) { pushNotificationsRepository.saveApplicationId(newApplicationId) }
}
companion object {

View file

@ -3,7 +3,7 @@ package com.tangem.domain.notifications
import arrow.core.Either
import com.google.common.truth.Truth.assertThat
import com.tangem.domain.notifications.models.ApplicationId
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.notifications.repository.PushNotificationsRepository
import com.tangem.utils.notifications.PushNotificationsTokenProvider
import io.mockk.coEvery
import io.mockk.coVerify
@ -14,16 +14,16 @@ import org.junit.Test
class SendPushTokenUseCaseTest {
private lateinit var notificationsRepository: NotificationsRepository
private lateinit var pushNotificationsRepository: PushNotificationsRepository
private lateinit var pushNotificationsTokenProvider: PushNotificationsTokenProvider
private lateinit var sendPushTokenUseCase: SendPushTokenUseCase
@Before
fun setup() {
notificationsRepository = mockk()
pushNotificationsRepository = mockk()
pushNotificationsTokenProvider = mockk()
sendPushTokenUseCase = SendPushTokenUseCase(
notificationsRepository = notificationsRepository,
pushNotificationsRepository = pushNotificationsRepository,
pushNotificationsTokenProvider = pushNotificationsTokenProvider,
)
}
@ -34,14 +34,14 @@ class SendPushTokenUseCaseTest {
val applicationId = ApplicationId("test-app-id")
val token = "test-token"
coEvery { pushNotificationsTokenProvider.getToken() } returns token
coEvery { notificationsRepository.sendPushToken(applicationId, token) } returns Unit
coEvery { pushNotificationsRepository.sendPushToken(applicationId, token) } returns Unit
// WHEN
val result = sendPushTokenUseCase(applicationId)
// THEN
assertThat(result).isEqualTo(Either.Right(Unit))
coVerify(exactly = 1) { notificationsRepository.sendPushToken(applicationId, token) }
coVerify(exactly = 1) { pushNotificationsRepository.sendPushToken(applicationId, token) }
}
@Test
@ -51,13 +51,13 @@ class SendPushTokenUseCaseTest {
val token = "test-token"
val expectedError = RuntimeException("Network error")
coEvery { pushNotificationsTokenProvider.getToken() } returns token
coEvery { notificationsRepository.sendPushToken(applicationId, token) } throws expectedError
coEvery { pushNotificationsRepository.sendPushToken(applicationId, token) } throws expectedError
// WHEN
val result = sendPushTokenUseCase(applicationId)
// THEN
assertThat(result).isEqualTo(Either.Left(expectedError))
coVerify(exactly = 1) { notificationsRepository.sendPushToken(applicationId, token) }
coVerify(exactly = 1) { pushNotificationsRepository.sendPushToken(applicationId, token) }
}
}

View file

@ -12,6 +12,7 @@ interface ChooseManagedTokensComponent : ComposableContentComponent {
val initialCurrency: CryptoCurrency,
val selectedCurrency: CryptoCurrency?,
val source: Source,
val showSendViaSwapNotification: Boolean,
)
enum class Source {

View file

@ -33,6 +33,7 @@ dependencies {
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
implementation(projects.domain.swap.models)
implementation(projects.domain.notifications)
/* AndroidX */
implementation(deps.androidx.activity.compose)

View file

@ -3,6 +3,7 @@ package com.tangem.features.managetokens.choosetoken.model
import androidx.annotation.StringRes
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.activate
import com.tangem.common.ui.notifications.NotificationId
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
@ -15,6 +16,7 @@ import com.tangem.core.ui.event.triggeredEvent
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.notifications.SetShouldShowNotificationUseCase
import com.tangem.features.managetokens.choosetoken.entity.ChooseManageTokensBottomSheetConfig
import com.tangem.features.managetokens.choosetoken.entity.ChooseManagedTokenUM
import com.tangem.features.managetokens.component.ChooseManagedTokensComponent
@ -43,6 +45,7 @@ internal class ChooseManagedTokensModel @Inject constructor(
private val router: Router,
override val dispatchers: CoroutineDispatcherProvider,
private val uiMessageSender: UiMessageSender,
private val setShouldShowNotificationUseCase: SetShouldShowNotificationUseCase,
paramsContainer: ParamsContainer,
manageTokensListManagerFactory: ManageTokensListManager.Factory,
) : Model() {
@ -107,18 +110,21 @@ internal class ChooseManagedTokensModel @Inject constructor(
}
private fun getNotification(): NotificationUM? {
return when (params.source) {
Source.SendViaSwap -> ChooseManagedTokensNotificationUM.SendViaSwap(
onCloseClick = ::removeNotification,
)
return if (params.source == Source.SendViaSwap && params.showSendViaSwapNotification) {
ChooseManagedTokensNotificationUM.SendViaSwap(onCloseClick = ::removeNotification)
} else {
null
}
}
private fun removeNotification() {
uiState.update {
it.copy(
notificationUM = null,
)
modelScope.launch {
setShouldShowNotificationUseCase(NotificationId.SendViaSwapTokenSelectorNotification.key, false)
uiState.update {
it.copy(
notificationUM = null,
)
}
}
}

View file

@ -2,9 +2,11 @@ package com.tangem.features.send.v2.entrypoint.model
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.notifications.NotificationId
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.notifications.ShouldShowNotificationUseCase
import com.tangem.features.send.v2.api.SendComponent
import com.tangem.features.send.v2.api.SendEntryPointComponent
import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateTrigger
@ -18,6 +20,7 @@ import jakarta.inject.Inject
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
@Suppress("LongParameterList")
@ModelScoped
internal class SendEntryPointModel @Inject constructor(
paramsContainer: ParamsContainer,
@ -26,6 +29,7 @@ internal class SendEntryPointModel @Inject constructor(
private val swapChooseTokenNetworkListener: SwapChooseTokenNetworkListener,
private val sendAmountUpdateTrigger: SendAmountUpdateTrigger,
private val swapAmountUpdateTrigger: SwapAmountUpdateTrigger,
private val shouldShowNotificationUseCase: ShouldShowNotificationUseCase,
) : Model(), SendComponent.ModelCallback, SendWithSwapComponent.ModelCallback {
private val params: SendEntryPointComponent.Params = paramsContainer.require()
@ -36,15 +40,21 @@ internal class SendEntryPointModel @Inject constructor(
private var swapChooseTokenListenerJobHolder = JobHolder()
override fun onConvertToAnotherToken(lastAmount: String) {
appRouter.push(
AppRoute.ChooseManagedTokens(
userWalletId = params.userWalletId,
initialCurrency = params.cryptoCurrency,
selectedCurrency = null,
source = AppRoute.ChooseManagedTokens.Source.SendViaSwap,
),
)
observeChooseSelectToken(lastAmount)
modelScope.launch {
val showSendViaSwapNotification = shouldShowNotificationUseCase(
NotificationId.SendViaSwapTokenSelectorNotification.key,
)
appRouter.push(
AppRoute.ChooseManagedTokens(
userWalletId = params.userWalletId,
initialCurrency = params.cryptoCurrency,
selectedCurrency = null,
source = AppRoute.ChooseManagedTokens.Source.SendViaSwap,
showSendViaSwapNotification = showSendViaSwapNotification,
),
)
observeChooseSelectToken(lastAmount)
}
}
override fun onCloseSwap(lastAmount: String) {

View file

@ -64,6 +64,7 @@ dependencies {
implementation(projects.domain.settings)
implementation(projects.domain.txhistory.models)
implementation(projects.domain.txhistory)
implementation(projects.domain.notifications)
implementation(projects.domain.feedback.models)
implementation(projects.domain.feedback)

View file

@ -9,6 +9,7 @@ import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.common.ui.navigationButtons.NavigationButton
import com.tangem.common.ui.navigationButtons.NavigationUM
import com.tangem.common.ui.notifications.NotificationId
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@ -22,6 +23,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.swap.models.SwapCurrencies
import com.tangem.domain.notifications.ShouldShowNotificationUseCase
import com.tangem.domain.swap.models.SwapDirection
import com.tangem.domain.swap.models.SwapDirection.Companion.withSwapDirection
import com.tangem.domain.swap.models.SwapQuoteModel
@ -81,6 +83,7 @@ internal class SwapAmountModel @Inject constructor(
private val swapAmountUpdateListener: SwapAmountUpdateListener,
private val swapAmountReduceListener: SwapAmountReduceListener,
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
private val shouldShowNotificationUseCase: ShouldShowNotificationUseCase,
) : Model(), SwapAmountClickIntents, SwapChooseProviderComponent.ModelCallback {
private val params: SwapAmountComponentParams = paramsContainer.require()
@ -221,6 +224,9 @@ internal class SwapAmountModel @Inject constructor(
override fun onSelectTokenClick() {
val amountParams = params as? SwapAmountComponentParams.AmountParams ?: return
modelScope.launch {
val showSendViaSwapNotification = shouldShowNotificationUseCase(
NotificationId.SendViaSwapTokenSelectorNotification.key,
)
val isEditMode = amountParams.currentRoute.firstOrNull()?.isEditMode == true
val selectedCurrency = (uiState.value as? SwapAmountUM.Content)?.secondaryCryptoCurrencyStatus?.currency
appRouter.push(
@ -229,6 +235,7 @@ internal class SwapAmountModel @Inject constructor(
initialCurrency = primaryCryptoCurrency,
selectedCurrency = selectedCurrency.takeIf { isEditMode },
source = AppRoute.ChooseManagedTokens.Source.SendViaSwap,
showSendViaSwapNotification = showSendViaSwapNotification,
),
)
}
@ -259,6 +266,7 @@ internal class SwapAmountModel @Inject constructor(
private fun confirmSendWithSwapClose() {
val amountParams = params as? SwapAmountComponentParams.AmountParams ?: return
val amountFieldData = uiState.value.primaryAmount.amountField as? AmountState.Data
val callback = (params as? SwapAmountComponentParams.AmountParams)?.callback ?: return
val primaryCryptoCurrencyStatus = (uiState.value as? SwapAmountUM.Content)?.primaryCryptoCurrencyStatus
if (primaryCryptoCurrencyStatus != null) {