Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-03 21:07:28 +04:00
parent 5aff097e3e
commit 84802cb6e2
14 changed files with 132 additions and 18 deletions

View file

@ -562,6 +562,10 @@
<string name="nft_traits_title">Eigenschaften</string>
<string name="nft_untitled_collection">Unbetitelte Sammlung</string>
<string name="nft_wallet_count">%1$d NFTs in der %2$d Sammlung</string>
<plurals name="nft_wallet_count_android">
<item quantity="one"></item>
<item quantity="other">%1$d NFTs in der %2$d Sammlung</item>
</plurals>
<string name="nft_wallet_receive_nft">Tippe hier, um das erste NFT zu erhalten</string>
<string name="nft_wallet_title">NFT-Sammlungen</string>
<string name="nft_wallet_unable_to_load">Die Daten konnten nicht geladen werden</string>

View file

@ -520,6 +520,10 @@
<string name="nft_collections_warning_subtitle">Certaines données peuvent ne pas se charger</string>
<string name="nft_collections_warning_title">Problèmes de chargement temporaires</string>
<string name="nft_wallet_count">%1$d NFT dans la collection %2$d</string>
<plurals name="nft_wallet_count_android">
<item quantity="one"></item>
<item quantity="other">%1$d NFT dans la collection %2$d</item>
</plurals>
<string name="nft_wallet_receive_nft">Appuyez ici pour recevoir le premier NFT</string>
<string name="nft_wallet_title">Collections NFT</string>
<string name="nft_wallet_unable_to_load">Impossible de charger les données</string>

View file

@ -560,6 +560,9 @@
<string name="nft_traits_title">特徴</string>
<string name="nft_untitled_collection">無題のコレクション</string>
<string name="nft_wallet_count">%2$dコレクションの%1$dNFT</string>
<plurals name="nft_wallet_count_android">
<item quantity="other">%2$dコレクションの%1$dNFT</item>
</plurals>
<string name="nft_wallet_receive_nft">ここをタップして最初のNFTを受け取ります</string>
<string name="nft_wallet_title">NFTコレクション</string>
<string name="nft_wallet_unable_to_load">データを読み込めません</string>
@ -1085,6 +1088,7 @@
<string name="unlock_wallet_description_full">%sを使用するか、カード / リングをスキャンしてウォレットにアクセスしてください</string>
<string name="unsupported_wc_version">接続に失敗しましたこのdAppは、サポートされていないWallet Connectバージョン1.0を使用しています。正常に接続するには、dAppがWallet Connectバージョン2.0をサポートしていることを確認してください。</string>
<string name="user_push_notification_agreement_argument_one">最新の機能とニュースをお届けします</string>
<string name="user_push_notification_agreement_argument_three">受信取引の通知を受け取る</string>
<string name="user_push_notification_agreement_argument_two">新しいプロモーション情報をいち早く入手しましょう</string>
<string name="user_push_notification_agreement_header">プッシュ通知を使用しますか?</string>
<string name="user_wallet_list_add_button">新しいウォレットを追加</string>

View file

@ -569,6 +569,12 @@
<string name="nft_send">Отправить NFT</string>
<string name="nft_traits_title">Черты</string>
<string name="nft_wallet_count">%1$d NFTs в %2$d коллекциях</string>
<plurals name="nft_wallet_count_android">
<item quantity="one"></item>
<item quantity="few"></item>
<item quantity="many"></item>
<item quantity="other">%1$d NFTs в %2$d коллекциях</item>
</plurals>
<string name="nft_wallet_receive_nft">Нажмите здесь, чтобы получить первую NFT</string>
<string name="nft_wallet_title">NFT коллекции</string>
<string name="nft_wallet_unable_to_load">Невозможно загрузить данные</string>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="_warning_stelar_token_required_min_coin_reserve">Required minimum XLM reserve %1$s</string>
<string name="action_buttons_buy_empty_search_message">Cant find your token? Go to the Market section on the main page and add it to your portfolio for purchase</string>
<string name="action_buttons_sell_empty_search_message">Cant find your token? Go to the Market section on the main page and add it to your portfolio for selling.</string>
<string name="action_buttons_sell_navigation_bar_title">Sell</string>
@ -271,6 +272,7 @@
<string name="details_row_subtitle_signed_hashes_format">%s hashes</string>
<string name="details_row_title_cid">Device ID</string>
<string name="details_row_title_contact_to_support">Contact support</string>
<string name="details_row_title_contact_to_support_chat">Open support chat</string>
<string name="details_row_title_create_backup">Link More Cards</string>
<string name="details_row_title_currency">App Currency</string>
<string name="details_row_title_flip_to_hide">Flip-to-Hide Balances</string>
@ -569,6 +571,10 @@
<string name="nft_traits_title">Traits</string>
<string name="nft_untitled_collection">Untitled collection</string>
<string name="nft_wallet_count">%1$d NFTs in %2$d collections</string>
<plurals name="nft_wallet_count_android">
<item quantity="one"></item>
<item quantity="other">%1$d NFTs in %2$d collections</item>
</plurals>
<string name="nft_wallet_receive_nft">Tap here to receive first NFT</string>
<string name="nft_wallet_title">NFT collections</string>
<string name="nft_wallet_unable_to_load">Unable to load the data</string>
@ -1077,6 +1083,8 @@
<string name="transaction_history_transaction_from_address">from: %s</string>
<string name="transaction_history_transaction_to_address">to: %s</string>
<string name="transaction_history_transaction_validator">validator: %s</string>
<string name="transaction_notifications_warning_description">Notifications are enabled but won\'t work until you allow notifications in your device settings.</string>
<string name="transaction_notifications_warning_title">Transaction Notifications</string>
<string name="transfer_min_amount_error">Minimum %s</string>
<string name="transfer_notification_invalid_minimum_transaction_amount_text">The minimum transaction amount is %1$s.</string>
<string name="tron_will_be_send_token_fee_description">Tron network fees for popular tokens can be higher. Staking TRX may help reduce transaction costs.</string>
@ -1293,6 +1301,7 @@
<string name="warning_rate_app_message">Your review keeps us motivated to make Tangem Wallet even better</string>
<string name="warning_rate_app_title">Enjoying Tangem?</string>
<string name="warning_receive_blocked_hedera_token_association_required_message">You must associate your token before receiving tokens</string>
<string name="warning_receive_blocked_stelar_token_trustline_required_message">You must open trustline before receiving tokens</string>
<string name="warning_rent_fee_title">Network rent fee required</string>
<string name="warning_seedphrase_action_required_title">Action required</string>
<string name="warning_seedphrase_contacted_support">Did you contact support via the app within 7 days of creating your wallet? If yes, or if you\'re unsure, click \'Yes\' and follow the instructions.</string>
@ -1308,6 +1317,9 @@
<string name="warning_some_networks_unreachable_message">Swipe down to refresh or try again later.</string>
<string name="warning_some_networks_unreachable_title">Some networks are unreachable</string>
<string name="warning_some_token_balances_not_updated">Some token balances could not be updated</string>
<string name="warning_stellar_token_trustline_button_title">Open trustline</string>
<string name="warning_stellar_token_trustline_subtiile">Open trustline subtiile</string>
<string name="warning_stellar_token_trustline_tiile">Open trustline title</string>
<string name="warning_testnet_card_message">This is a Testnet card. It cannot process transactions and should only be used for testing and development purposes.</string>
<string name="warning_testnet_card_title">For testing purposes only</string>
<string name="warning_token_balance_not_updated">Balance may be outdated. Refresh the page.</string>

View file

@ -1,5 +1,8 @@
package com.tangem.data.settings
import android.content.Context
import android.content.pm.PackageManager
import androidx.core.content.ContextCompat
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys.getShouldShowInitialPermissionScreen
import com.tangem.datasource.local.preferences.PreferencesKeys.getShouldShowPermission
@ -9,6 +12,7 @@ import com.tangem.domain.settings.repositories.PermissionRepository
internal class DefaultPermissionRepository(
private val appPreferencesStore: AppPreferencesStore,
private val context: Context,
) : PermissionRepository {
override suspend fun shouldInitiallyShowPermissionScreen(permission: String): Boolean {
@ -32,4 +36,12 @@ internal class DefaultPermissionRepository(
override suspend fun neverAskPermission(permission: String) {
appPreferencesStore.store(key = getShouldShowPermission(permission), value = false)
}
override fun hasRuntimePermission(permission: String): Boolean {
return context.isPermissionGranted(permission)
}
private fun Context.isPermissionGranted(permission: String): Boolean {
return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.data.settings.di
import android.content.Context
import com.tangem.data.settings.DefaultAppRatingRepository
import com.tangem.data.settings.DefaultPermissionRepository
import com.tangem.data.settings.DefaultSettingsRepository
@ -13,6 +14,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@ -44,7 +46,13 @@ internal object SettingsDataModule {
@Provides
@Singleton
fun providePushPermissionRepository(appPreferencesStore: AppPreferencesStore): PermissionRepository {
return DefaultPermissionRepository(appPreferencesStore = appPreferencesStore)
fun providePushPermissionRepository(
appPreferencesStore: AppPreferencesStore,
@ApplicationContext context: Context,
): PermissionRepository {
return DefaultPermissionRepository(
appPreferencesStore = appPreferencesStore,
context = context,
)
}
}

View file

@ -24,4 +24,9 @@ interface PermissionRepository {
* Permanently deny [permission] and never request again
*/
suspend fun neverAskPermission(permission: String)
/**
* Return is [permission] granted or not.
*/
fun hasRuntimePermission(permission: String): Boolean
}

View file

@ -36,6 +36,7 @@ dependencies {
implementation(projects.domain.wallets.models)
implementation(projects.domain.demo)
implementation(projects.domain.nft)
implementation(projects.domain.settings)
implementation(projects.domain.notifications.toggles)
implementation(projects.domain.notifications.models)
implementation(projects.domain.notifications)

View file

@ -35,6 +35,7 @@ internal class PreviewWalletSettingsComponent : WalletSettingsComponent {
isNotificationsFeatureEnabled = true,
onCheckedNotificationsChanged = {},
onNotificationsDescriptionClick = {},
isNotificationsPermissionGranted = false,
),
requestPushNotificationsPermission = false,
onPushNotificationPermissionGranted = {},

View file

@ -37,4 +37,10 @@ internal sealed class WalletSettingsItemUM {
val more: TextReference,
val onClick: () -> Unit,
) : WalletSettingsItemUM()
data class NotificationPermission(
override val id: String,
val title: TextReference,
val description: TextReference,
) : WalletSettingsItemUM()
}

View file

@ -1,5 +1,6 @@
package com.tangem.feature.walletsettings.model
import android.os.Build
import arrow.core.getOrElse
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.activate
@ -22,6 +23,7 @@ import com.tangem.domain.nft.DisableWalletNFTUseCase
import com.tangem.domain.nft.EnableWalletNFTUseCase
import com.tangem.domain.nft.GetWalletNFTEnabledUseCase
import com.tangem.domain.notifications.toggles.NotificationsFeatureToggles
import com.tangem.domain.settings.repositories.PermissionRepository
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.domain.wallets.models.requireColdWallet
@ -65,6 +67,7 @@ internal class WalletSettingsModel @Inject constructor(
private val getWalletNotificationsEnabledUseCase: GetWalletNotificationsEnabledUseCase,
private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase,
private val settingsManager: SettingsManager,
private val permissionsRepository: PermissionRepository,
) : Model() {
val params: WalletSettingsComponent.Params = paramsContainer.require()
@ -98,6 +101,7 @@ internal class WalletSettingsModel @Inject constructor(
isNFTEnabled = nftEnabled,
isNotificationsEnabled = notificationsEnabled,
isNotificationsFeatureEnabled = notificationsToggles.isNotificationsEnabled,
isNotificationsPermissionGranted = isNotificationsPermissionGranted(),
),
)
}
@ -105,6 +109,16 @@ internal class WalletSettingsModel @Inject constructor(
.launchIn(modelScope)
}
private fun isNotificationsPermissionGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissionsRepository.hasRuntimePermission(
android.Manifest.permission.POST_NOTIFICATIONS,
)
} else {
true
}
}
private fun buildItems(
userWallet: UserWallet,
dialogNavigation: SlotNavigation<DialogConfig>,
@ -113,6 +127,7 @@ internal class WalletSettingsModel @Inject constructor(
isNFTEnabled: Boolean,
isNotificationsFeatureEnabled: Boolean,
isNotificationsEnabled: Boolean,
isNotificationsPermissionGranted: Boolean,
): PersistentList<WalletSettingsItemUM> = itemsBuilder.buildItems(
userWalletId = userWallet.walletId,
userWalletName = userWallet.name,
@ -147,6 +162,7 @@ internal class WalletSettingsModel @Inject constructor(
onReferralClick = { onReferralClick(userWallet) },
isNotificationsEnabled = isNotificationsEnabled,
isNotificationsFeatureEnabled = isNotificationsFeatureEnabled,
isNotificationsPermissionGranted = isNotificationsPermissionGranted,
onCheckedNotificationsChanged = ::onCheckedNotificationsChange,
onNotificationsDescriptionClick = ::onNotificationsDescriptionClick,
)

View file

@ -21,6 +21,8 @@ import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.components.block.BlockItem
import com.tangem.core.ui.components.items.DescriptionItem
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
@ -109,6 +111,10 @@ private fun Content(state: WalletSettingsUM, modifier: Modifier = Modifier) {
yOffset = DESCRIPTION_OFFSET,
onDescriptionClick = item.onClick,
)
is WalletSettingsItemUM.NotificationPermission -> NotificationAlertBlock(
modifier = itemModifier,
model = item,
)
}
}
}
@ -216,6 +222,18 @@ private fun SwitchBlock(model: WalletSettingsItemUM.WithSwitch, modifier: Modifi
}
}
@Composable
private fun NotificationAlertBlock(model: WalletSettingsItemUM.NotificationPermission, modifier: Modifier = Modifier) {
Notification(
config = NotificationConfig(
title = model.title,
subtitle = model.description,
iconResId = R.drawable.ic_alert_triangle_20,
),
modifier = modifier,
)
}
@Composable
private fun DescriptionWithMoreBlock(
model: WalletSettingsItemUM.DescriptionWithMore,

View file

@ -36,6 +36,7 @@ internal class ItemsBuilder @Inject constructor(
onCheckedNFTChange: (Boolean) -> Unit,
isNotificationsFeatureEnabled: Boolean,
isNotificationsEnabled: Boolean,
isNotificationsPermissionGranted: Boolean,
onCheckedNotificationsChanged: (Boolean) -> Unit,
onNotificationsDescriptionClick: () -> Unit,
forgetWallet: () -> Unit,
@ -61,24 +62,34 @@ internal class ItemsBuilder @Inject constructor(
onReferralClick = onReferralClick,
),
)
.run {
if (isNotificationsFeatureEnabled) {
add(
buildNotificationsSwitchItem(
isNotificationsEnabled,
onCheckedNotificationsChanged,
),
).add(
buildNotificationsDescriptionItem(
onDescriptionClick = onNotificationsDescriptionClick,
),
)
} else {
this
}
}
.addAll(
buildNotificationItems(
isNotificationsFeatureEnabled = isNotificationsFeatureEnabled,
isNotificationsPermissionGranted = isNotificationsPermissionGranted,
isNotificationsEnabled = isNotificationsEnabled,
onCheckedNotificationsChanged = onCheckedNotificationsChanged,
onNotificationsDescriptionClick = onNotificationsDescriptionClick,
),
)
.add(buildForgetItem(forgetWallet))
private fun buildNotificationItems(
isNotificationsFeatureEnabled: Boolean,
isNotificationsPermissionGranted: Boolean,
isNotificationsEnabled: Boolean,
onCheckedNotificationsChanged: (Boolean) -> Unit,
onNotificationsDescriptionClick: () -> Unit,
): List<WalletSettingsItemUM> {
if (!isNotificationsFeatureEnabled) return emptyList()
return buildList {
if (!isNotificationsPermissionGranted) {
add(buildNotificationsPermissionItem())
}
add(buildNotificationsSwitchItem(isNotificationsEnabled, onCheckedNotificationsChanged))
add(buildNotificationsDescriptionItem(onNotificationsDescriptionClick))
}
}
private fun buildNameItem(walletName: String, isRenameWalletAvailable: Boolean, renameWallet: () -> Unit) =
WalletSettingsItemUM.WithText(
id = "wallet_name",
@ -96,6 +107,12 @@ internal class ItemsBuilder @Inject constructor(
onCheckedChange = onCheckedNFTChange,
)
private fun buildNotificationsPermissionItem() = WalletSettingsItemUM.NotificationPermission(
id = "notifications_permission",
title = resourceReference(id = R.string.transaction_notifications_warning_title),
description = resourceReference(id = R.string.transaction_notifications_warning_description),
)
private fun buildNotificationsSwitchItem(isNFTEnabled: Boolean, onCheckedNFTChange: (Boolean) -> Unit) =
WalletSettingsItemUM.WithSwitch(
id = "notifications",