Updated on 2026-08-14
This commit is contained in:
parent
4c903fc654
commit
1485cebb6b
14 changed files with 420 additions and 397 deletions
|
|
@ -177,7 +177,7 @@ private fun Footer(
|
|||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(
|
||||
id = R.string.user_wallet_list_unlock_all,
|
||||
id = R.string.user_wallet_list_unlock_all_with,
|
||||
stringResource(id = R.string.common_biometrics),
|
||||
),
|
||||
showProgress = showUnlockProgress,
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@
|
|||
<string name="common_biometric_authentication">биометрическую аутентификацию</string>
|
||||
<string name="common_biometrics">биометрией</string>
|
||||
<string name="common_buy">Купить</string>
|
||||
<string name="common_buy_currency">Купить %1$s</string>
|
||||
<string name="common_camera_denied_alert_message">Вы не предоставили доступ к камере, пожалуйста, измените настройки конфиденциальности.</string>
|
||||
<string name="common_cancel">Отмена</string>
|
||||
<string name="common_close">Закрыть</string>
|
||||
|
|
@ -192,6 +193,7 @@
|
|||
<string name="initial_message_tap_header">Приложите карту</string>
|
||||
<string name="internal_error_wallet_manager_not_found">Внутренняя ошибка: не удается найти менеджер кошельков</string>
|
||||
<string name="key_invalidated_warning_description">Вы обновили данные биометрии, отсканируйте свою карту для входа</string>
|
||||
<string name="main_empty_tokens_list_message">Чтобы начать отслеживать свои криптоактивы и транзакции, добавьте токены</string>
|
||||
<string name="main_get_bonus_subtitle">Вы успешно прошли все уроки и теперь можете получить 1INCH токены</string>
|
||||
<plurals name="main_learn_subtitle">
|
||||
<item quantity="one">Пройдите 3 урока и получите %d 1INCH токен на свой кошелек</item>
|
||||
|
|
@ -215,6 +217,8 @@
|
|||
<item quantity="other">Вам надо сгенерировать адреса для %d новых сетей, используя вашу карту</item>
|
||||
</plurals>
|
||||
<string name="main_warning_missing_derivation_title">Некоторые адреса отсутствуют</string>
|
||||
<string name="manage_tokens_add">Добавить</string>
|
||||
<string name="manage_tokens_edit">Изменить</string>
|
||||
<string name="notification_title_not_enough_funds">Невозможно покрыть %1$s комиссию</string>
|
||||
<string name="onboarding_access_code_feature_1_description">Вам необходимо установить единый код доступа для защиты всех ваших карт</string>
|
||||
<string name="onboarding_access_code_feature_1_title">Защита</string>
|
||||
|
|
@ -325,6 +329,8 @@
|
|||
<item quantity="other">за %d кошельков</item>
|
||||
</plurals>
|
||||
<string name="referral_point_currencies_description">Получите ^^%1$s^^ на ваш адрес в сети %2$s %3$s ^^спустя 30 дней^^ за каждый кошелек, который купит ваш друг</string>
|
||||
<string name="referral_point_currencies_description_prefix">Получите</string>
|
||||
<string name="referral_point_currencies_description_suffix">на ваш адрес в сети %1$s%2$s за каждый кошелек, который купит ваш друг</string>
|
||||
<string name="referral_point_currencies_title">Вы</string>
|
||||
<string name="referral_point_discount_description_prefix">Получит</string>
|
||||
<string name="referral_point_discount_description_suffix">при покупке кошелька на сайте tangem.com</string>
|
||||
|
|
@ -491,10 +497,11 @@
|
|||
<string name="user_wallet_list_rename_popup_title">Переименование кошелька</string>
|
||||
<string name="user_wallet_list_single_header">Одновалютные</string>
|
||||
<string name="user_wallet_list_title">Мои кошельки</string>
|
||||
<string name="user_wallet_list_unlock_all">Разблокировать все с %s</string>
|
||||
<string name="user_wallet_list_unlock_all">Разблокировать все</string>
|
||||
<string name="user_wallet_list_unlock_all_with">Разблокировать все с %s</string>
|
||||
<string name="wallet_address_button_explore">История транзакций</string>
|
||||
<string name="wallet_balance_blockchain_unreachable">Сеть недоступна</string>
|
||||
<string name="wallet_balance_blockchain_unreachable_try_later">Блокчейн недоступен. Попробуй позже.</string>
|
||||
<string name="wallet_balance_blockchain_unreachable_try_later">Блокчейн недоступен. Попробуйте позже.</string>
|
||||
<string name="wallet_balance_loading">Баланс загружается…</string>
|
||||
<string name="wallet_balance_missing_derivation">Отсканируйте карту</string>
|
||||
<string name="wallet_balance_tx_in_progress">Транзакция подтверждается…</string>
|
||||
|
|
@ -577,5 +584,5 @@
|
|||
<string name="welcome_unlock">Войти с %s</string>
|
||||
<string name="welcome_unlock_card">Сканировать карту</string>
|
||||
<string name="welcome_unlock_description">Используйте %s или отсканируйте карту для входа в приложение</string>
|
||||
<string name="welcome_unlock_title">С возвращением!</string>
|
||||
<string name="welcome_unlock_title">C возвращением!</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
<string name="card_settings_access_code_recovery_enabled_description">允許您使用此卡重置此錢包中其他卡上的訪問密碼</string>
|
||||
<string name="card_settings_access_code_recovery_footer">禁用重置此卡或此錢包中其他卡上的訪問密碼的功能</string>
|
||||
<string name="card_settings_access_code_recovery_title">恢復訪問密碼</string>
|
||||
<string name="card_settings_action_sheet_reset">重置</string>
|
||||
<string name="card_settings_action_sheet_title">您確定要這麼做嗎?</string>
|
||||
<string name="card_settings_change_access_code">更改訪問密碼</string>
|
||||
<string name="card_settings_change_access_code_footer">訪問密碼將僅在此卡上更改</string>
|
||||
|
|
@ -258,6 +259,8 @@
|
|||
<string name="referral_error_failed_to_load_info">無法加載有關推薦計劃的消息。請稍後再試</string>
|
||||
<string name="referral_error_failed_to_load_info_with_reason">無法加載有關推薦計劃的消息。原因:%s。請稍後再試</string>
|
||||
<string name="referral_friends_bought_title">您的朋友買</string>
|
||||
<string name="referral_point_currencies_description_prefix">會得到</string>
|
||||
<string name="referral_point_currencies_description_suffix">對於你的朋友在你的 %1$s 網絡地址%2$s上購買的每個錢包</string>
|
||||
<string name="referral_point_currencies_title">你</string>
|
||||
<string name="referral_point_discount_description_prefix">得到</string>
|
||||
<string name="referral_point_discount_description_value">%s 折扣</string>
|
||||
|
|
@ -277,7 +280,6 @@
|
|||
<string name="reset_card_to_factory_warning_message">我了解執行此操作後,我將無法再訪問當前錢包</string>
|
||||
<string name="reset_card_with_backup_to_factory_message">恢復原廠設置將從所選卡中完全刪除錢包。您將無法恢復當前錢包或使用卡恢復訪問密碼</string>
|
||||
<string name="reset_card_without_backup_to_factory_message">恢復原廠設置將從所選卡中完全刪除錢包並將其從應用程序中刪除。您將無法恢復當前錢包</string>
|
||||
<string name="russian_bank_card_warning_subtitle">您有其他國家的銀行卡或銀聯卡嗎?</string>
|
||||
<string name="russian_bank_card_warning_title">目前不接受俄羅斯銀行卡</string>
|
||||
<string name="save_user_wallet_agreement_access_description">登錄應用程序並在不掃描卡片的情況下檢查您的資產</string>
|
||||
<string name="save_user_wallet_agreement_access_title">訪問應用程序</string>
|
||||
|
|
@ -402,7 +404,7 @@
|
|||
<string name="user_wallet_list_rename_popup_title">重新命名錢包</string>
|
||||
<string name="user_wallet_list_single_header">單一幣種</string>
|
||||
<string name="user_wallet_list_title">我的錢包</string>
|
||||
<string name="user_wallet_list_unlock_all">用 %s 解鎖全部</string>
|
||||
<string name="user_wallet_list_unlock_all_with">用 %s 解鎖全部</string>
|
||||
<string name="wallet_address_button_explore">交易記錄</string>
|
||||
<string name="wallet_balance_blockchain_unreachable">網路無法使用</string>
|
||||
<string name="wallet_balance_blockchain_unreachable_try_later">區塊鍊無法使用。稍後再試</string>
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@
|
|||
<string name="common_biometric_authentication">biometric authentication</string>
|
||||
<string name="common_biometrics">biometrics</string>
|
||||
<string name="common_buy">Buy</string>
|
||||
<string name="common_buy_currency">Buy %1$s</string>
|
||||
<string name="common_camera_denied_alert_message">You have not given access to your camera, please adjust your privacy settings</string>
|
||||
<string name="common_cancel">Cancel</string>
|
||||
<string name="common_close">Close</string>
|
||||
|
|
@ -134,7 +135,10 @@
|
|||
<string name="custom_token_creation_error_required_field">Required field</string>
|
||||
<string name="custom_token_creation_error_wrong_decimals">Decimal number must be a valid integer, no higher than %d</string>
|
||||
<string name="custom_token_custom_derivation">Custom derivation</string>
|
||||
<string name="custom_token_custom_derivation_placeholder">E. g. m/00\'/0000\'/0\'/0/0</string>
|
||||
<string name="custom_token_custom_derivation_title">Enter custom derivation</string>
|
||||
<string name="custom_token_decimals_input_title">Decimals</string>
|
||||
<string name="custom_token_derivation_path">Derivation Path</string>
|
||||
<string name="custom_token_derivation_path_default">Default</string>
|
||||
<string name="custom_token_derivation_path_input_title">BIP44 coin type</string>
|
||||
<string name="custom_token_invalid_derivation_path">The derivation path you\'ve entered is not valid</string>
|
||||
|
|
@ -142,6 +146,8 @@
|
|||
<string name="custom_token_name_input_title">Name</string>
|
||||
<string name="custom_token_network_input_not_selected">Not selected</string>
|
||||
<string name="custom_token_network_input_title">Network</string>
|
||||
<string name="custom_token_network_selector_title">Token network</string>
|
||||
<string name="custom_token_subtitle">You can manually add a token that is not natively supported by Tangem</string>
|
||||
<string name="custom_token_token_symbol_input_placeholder">E.g. USDC</string>
|
||||
<string name="custom_token_token_symbol_input_title">Token symbol</string>
|
||||
<string name="custom_token_validation_error_already_added">This token/network has already been added to your list</string>
|
||||
|
|
@ -193,7 +199,7 @@
|
|||
<string name="initial_message_tap_header">Tap the card</string>
|
||||
<string name="internal_error_wallet_manager_not_found">Internal error: wallet manager not found</string>
|
||||
<string name="key_invalidated_warning_description">You have updated biometrics, scan your card to enter</string>
|
||||
<string name="main_empty_tokens_list_message">To begin tracking your crypto assets and transactions, add tokens.</string>
|
||||
<string name="main_empty_tokens_list_message">To begin tracking your crypto assets and transactions, add tokens</string>
|
||||
<string name="main_get_bonus_subtitle">You have completed all of the lessons, and are now eligible to receive your 1INCH tokens</string>
|
||||
<plurals name="main_learn_subtitle">
|
||||
<item quantity="one">Complete three lessons and receive %d 1INCH token to your wallet</item>
|
||||
|
|
@ -213,6 +219,29 @@
|
|||
<item quantity="other">You need to generate addresses for %d new networks using your card</item>
|
||||
</plurals>
|
||||
<string name="main_warning_missing_derivation_title">Some addresses are missing</string>
|
||||
<string name="manage_tokens_add">Add</string>
|
||||
<string name="manage_tokens_custom">Custom</string>
|
||||
<string name="manage_tokens_edit">Edit</string>
|
||||
<string name="manage_tokens_network_selector_native_subtitle">Blockchain the cryptocurrency was initially created</string>
|
||||
<string name="manage_tokens_network_selector_native_title">Native network</string>
|
||||
<string name="manage_tokens_network_selector_non_native_info">Using non-native networks for tokens enables cross-blockchain interoperability, allowing assets to be utilized in diverse decentralized applications and smart contracts across platforms. However, this often involves a custodian or smart contract to hold the original asset securely, introducing centralization and counterparty risk.</string>
|
||||
<string name="manage_tokens_network_selector_non_native_subtitle">Not original or primary blockchain the token is hosted</string>
|
||||
<string name="manage_tokens_network_selector_non_native_title">Non-native networks</string>
|
||||
<string name="manage_tokens_network_selector_other_subtitle">Blockchain the cryptocurrency was initially created</string>
|
||||
<string name="manage_tokens_network_selector_other_title">Networks</string>
|
||||
<string name="manage_tokens_network_selector_title">Choose networks</string>
|
||||
<string name="manage_tokens_network_selector_wallet">Wallet</string>
|
||||
<string name="manage_tokens_nothing_found">Couldn’t find this token, you can add it manually</string>
|
||||
<string name="manage_tokens_number_of_wallets">%d of %#@total_wallets@</string>
|
||||
<plurals name="manage_tokens_number_of_walletstotal_wallets">
|
||||
<item quantity="one">%d wallet</item>
|
||||
<item quantity="other">%d wallets</item>
|
||||
</plurals>
|
||||
<string name="manage_tokens_search_placeholder">e.g. BTC I trust, hodl I must</string>
|
||||
<string name="manage_tokens_title">Coin market cap</string>
|
||||
<string name="manage_tokens_unavailable_description">The selected token is currently unavailable for actions within the crypto wallet. But worry not, you can express your interest by upvoting it.</string>
|
||||
<string name="manage_tokens_unavailable_vote">Upvote</string>
|
||||
<string name="manage_tokens_wallet_selector_title">Choose wallet</string>
|
||||
<string name="notification_title_not_enough_funds">Unable to cover %1$s fee</string>
|
||||
<string name="onboarding_access_code_feature_1_description">You have to set up a single access code to protect all your wallets</string>
|
||||
<string name="onboarding_access_code_feature_1_title">Protect</string>
|
||||
|
|
@ -322,6 +351,8 @@
|
|||
<item quantity="other">for %d wallets</item>
|
||||
</plurals>
|
||||
<string name="referral_point_currencies_description">Will get ^^%1$s^^ for each wallet bought by your friend on your %2$s network address %3$s ^^30 days after^^ that</string>
|
||||
<string name="referral_point_currencies_description_prefix">Will get</string>
|
||||
<string name="referral_point_currencies_description_suffix">for each wallet bought by your friend on your %1$s network address%2$s</string>
|
||||
<string name="referral_point_currencies_title">You</string>
|
||||
<string name="referral_point_discount_description_prefix">Will get a</string>
|
||||
<string name="referral_point_discount_description_suffix">when buying a wallet on tangem.com</string>
|
||||
|
|
@ -484,7 +515,8 @@
|
|||
<string name="user_wallet_list_rename_popup_title">Rename Wallet</string>
|
||||
<string name="user_wallet_list_single_header">Single-currency</string>
|
||||
<string name="user_wallet_list_title">My Wallets</string>
|
||||
<string name="user_wallet_list_unlock_all">Unlock all with %s</string>
|
||||
<string name="user_wallet_list_unlock_all">Unlock all</string>
|
||||
<string name="user_wallet_list_unlock_all_with">Unlock all with %s</string>
|
||||
<string name="wallet_address_button_explore">Transaction history</string>
|
||||
<string name="wallet_balance_blockchain_unreachable">Network is unreachable</string>
|
||||
<string name="wallet_balance_blockchain_unreachable_try_later">Blockchain is unreachable. Try later</string>
|
||||
|
|
@ -549,6 +581,7 @@
|
|||
<string name="wallet_title">Tangem</string>
|
||||
<string name="warning_button_can_be_better">Can be better</string>
|
||||
<string name="warning_button_learn_more">Learn more</string>
|
||||
<string name="warning_button_love_it">Love it!</string>
|
||||
<string name="warning_button_ok">Ok, Got it!</string>
|
||||
<string name="warning_button_really_cool">Really cool!</string>
|
||||
<string name="warning_existential_deposit_message">%1$s network has a concept of Existential Deposit. If your account drops below %2$s it will be deactivated and any remaining funds will be destroyed.</string>
|
||||
|
|
@ -559,7 +592,11 @@
|
|||
<string name="warning_rate_app_message">How do you like Tangem?</string>
|
||||
<string name="warning_rate_app_title">One question</string>
|
||||
<string name="warning_signed_tx_previously">This card has signed transactions in the past</string>
|
||||
<string name="warning_subtitle_network_unreachable">Network currently is unreachable. Please try again later.</string>
|
||||
<string name="warning_subtitle_some_networks_unreachable">Some networks currently are unreachable. Please try again later.</string>
|
||||
<string name="warning_testnet_card_message">This is a Testnet card. Don\'t accept it as a payment. This card must only be used for testing and development purposes.</string>
|
||||
<string name="warning_title_note_top_up">Note top up</string>
|
||||
<string name="warning_title_some_networks_unreachable">Some networks are unreachable</string>
|
||||
<string name="welcome_interrupted_backup_alert_discard">Discard</string>
|
||||
<string name="welcome_interrupted_backup_alert_message">You have an interrupted backup. Do you want to resume?</string>
|
||||
<string name="welcome_interrupted_backup_alert_resume">Yes, resume</string>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package com.tangem.core.ui.components.notifications
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.clickable
|
||||
|
|
@ -25,9 +23,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -46,7 +42,7 @@ import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsSta
|
|||
*/
|
||||
@Composable
|
||||
fun Notification(config: NotificationConfig, modifier: Modifier = Modifier, iconTint: Color? = null) {
|
||||
BaseContainer(buttonsState = config.buttonsState, modifier = modifier) {
|
||||
BaseContainer(buttonsState = config.buttonsState, onClick = config.onClick, modifier = modifier) {
|
||||
Column(
|
||||
modifier = Modifier.padding(all = TangemTheme.dimens.spacing12),
|
||||
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing12),
|
||||
|
|
@ -56,6 +52,7 @@ fun Notification(config: NotificationConfig, modifier: Modifier = Modifier, icon
|
|||
iconTint = iconTint,
|
||||
title = config.title,
|
||||
subtitle = config.subtitle,
|
||||
isClickableComponent = config.onClick != null,
|
||||
)
|
||||
|
||||
Buttons(state = config.buttonsState)
|
||||
|
|
@ -71,11 +68,12 @@ fun Notification(config: NotificationConfig, modifier: Modifier = Modifier, icon
|
|||
@Composable
|
||||
private fun BaseContainer(
|
||||
buttonsState: NotificationConfig.ButtonsState?,
|
||||
onClick: (() -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable BoxScope.() -> Unit,
|
||||
) {
|
||||
val containerColor by rememberUpdatedState(
|
||||
newValue = if (buttonsState != null) {
|
||||
newValue = if (buttonsState != null || onClick != null) {
|
||||
TangemTheme.colors.background.primary
|
||||
} else {
|
||||
TangemTheme.colors.button.disabled
|
||||
|
|
@ -83,9 +81,11 @@ private fun BaseContainer(
|
|||
)
|
||||
|
||||
Surface(
|
||||
onClick = onClick ?: {},
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = TangemTheme.dimens.size62)
|
||||
.fillMaxWidth(),
|
||||
enabled = onClick != null,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
color = containerColor,
|
||||
) {
|
||||
|
|
@ -94,15 +94,36 @@ private fun BaseContainer(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun MainContent(iconResId: Int, iconTint: Color?, title: TextReference, subtitle: TextReference) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing10)) {
|
||||
private fun MainContent(
|
||||
iconResId: Int,
|
||||
iconTint: Color?,
|
||||
title: TextReference,
|
||||
subtitle: TextReference,
|
||||
isClickableComponent: Boolean,
|
||||
) {
|
||||
Row {
|
||||
Icon(
|
||||
iconResId = iconResId,
|
||||
tint = iconTint,
|
||||
modifier = Modifier.align(alignment = Alignment.CenterVertically),
|
||||
)
|
||||
|
||||
SpacerW(width = TangemTheme.dimens.spacing10)
|
||||
|
||||
TextsBlock(title = title, subtitle = subtitle)
|
||||
|
||||
if (isClickableComponent) {
|
||||
SpacerWMax()
|
||||
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_chevron_right_24),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(size = TangemTheme.dimens.size20)
|
||||
.align(alignment = Alignment.CenterVertically),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +151,7 @@ private fun TextsBlock(title: TextReference, subtitle: TextReference) {
|
|||
Text(
|
||||
text = title.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.body2,
|
||||
style = TangemTheme.typography.button,
|
||||
)
|
||||
|
||||
Text(
|
||||
|
|
@ -141,16 +162,13 @@ private fun TextsBlock(title: TextReference, subtitle: TextReference) {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
private fun Buttons(state: NotificationButtonsState?) {
|
||||
AnimatedContent(targetState = state, label = "Update the buttons content") { animatedState ->
|
||||
when (animatedState) {
|
||||
is NotificationButtonsState.SecondaryButtonConfig -> SingleSecondaryButton(config = animatedState)
|
||||
is NotificationButtonsState.PrimaryButtonConfig -> SinglePrimaryButton(config = animatedState)
|
||||
is NotificationButtonsState.PairButtonsConfig -> PairButtons(config = animatedState)
|
||||
null -> Unit
|
||||
}
|
||||
when (state) {
|
||||
is NotificationButtonsState.SecondaryButtonConfig -> SingleSecondaryButton(config = state)
|
||||
is NotificationButtonsState.PrimaryButtonConfig -> SinglePrimaryButton(config = state)
|
||||
is NotificationButtonsState.PairButtonsConfig -> PairButtons(config = state)
|
||||
null -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,6 +284,12 @@ private class NotificationConfigProvider : CollectionPreviewParameterProvider<No
|
|||
subtitle = TextReference.Str(value = "Check your network connection"),
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
),
|
||||
NotificationConfig(
|
||||
title = TextReference.Str(value = "Used card"),
|
||||
subtitle = TextReference.Str(value = "The card signed transactions in the past"),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
onClick = {},
|
||||
),
|
||||
NotificationConfig(
|
||||
title = TextReference.Str(value = "Your wallet hasn’t been backed up"),
|
||||
subtitle = TextReference.Str(value = "To protect your assets, we advise you to carry out this procedure"),
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
* @property subtitle subtitle
|
||||
* @property iconResId icon resource id
|
||||
* @property buttonsState buttons state
|
||||
* @property onClick lambda be invoked when notification is clicked
|
||||
* @property onCloseClick lambda be invoked when close button is clicked
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -19,6 +20,7 @@ data class NotificationConfig(
|
|||
val subtitle: TextReference,
|
||||
@DrawableRes val iconResId: Int,
|
||||
val buttonsState: ButtonsState? = null,
|
||||
val onClick: (() -> Unit)? = null,
|
||||
val onCloseClick: (() -> Unit)? = null,
|
||||
) {
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,13 @@ fun combinedReference(refs: WrappedList<TextReference>): TextReference {
|
|||
@ReadOnlyComposable
|
||||
fun TextReference.resolveReference(): String {
|
||||
return when (this) {
|
||||
is TextReference.Res -> stringResource(id, *formatArgs.toTypedArray())
|
||||
is TextReference.Res -> {
|
||||
val args = formatArgs
|
||||
.map { if (it is TextReference) it.resolveReference() else it }
|
||||
.toTypedArray()
|
||||
|
||||
stringResource(id = id, *args)
|
||||
}
|
||||
is TextReference.PluralRes -> pluralStringResource(id, count, *formatArgs.toTypedArray())
|
||||
is TextReference.Str -> value
|
||||
is TextReference.Combined -> {
|
||||
|
|
@ -125,7 +131,13 @@ fun TextReference.resolveReference(): String {
|
|||
/** Resolve [TextReference] as [String] using [resources] (non-composable context) */
|
||||
fun TextReference.resolveReference(resources: Resources): String {
|
||||
return when (this) {
|
||||
is TextReference.Res -> resources.getString(id, *formatArgs.toTypedArray())
|
||||
is TextReference.Res -> {
|
||||
val args = formatArgs
|
||||
.map { if (it is TextReference) it.resolveReference(resources) else it }
|
||||
.toTypedArray()
|
||||
|
||||
resources.getString(id, *args)
|
||||
}
|
||||
is TextReference.PluralRes -> resources.getQuantityString(id, count, *formatArgs.toTypedArray())
|
||||
is TextReference.Str -> value
|
||||
is TextReference.Combined -> {
|
||||
|
|
|
|||
|
|
@ -381,10 +381,9 @@ internal object WalletPreviewData {
|
|||
onRefresh = {},
|
||||
),
|
||||
notifications = persistentListOf(
|
||||
WalletNotification.UnreachableNetworks,
|
||||
WalletNotification.LikeTangemApp(onClick = {}),
|
||||
WalletNotification.BackupCard(onClick = {}),
|
||||
WalletNotification.ScanCard(onClick = {}),
|
||||
WalletNotification.Critical.DevCard,
|
||||
WalletNotification.MissingAddresses(missingAddressesCount = 0, onGenerateClick = {}),
|
||||
WalletNotification.Warning.NetworksUnreachable,
|
||||
),
|
||||
bottomSheetConfig = bottomSheet,
|
||||
tokenActionsBottomSheet = actionsBottomSheet,
|
||||
|
|
@ -402,7 +401,7 @@ internal object WalletPreviewData {
|
|||
isRefreshing = false,
|
||||
onRefresh = {},
|
||||
),
|
||||
notifications = persistentListOf(WalletNotification.LikeTangemApp(onClick = {})),
|
||||
notifications = persistentListOf(WalletNotification.Warning.NetworksUnreachable),
|
||||
buttons = manageButtons,
|
||||
bottomSheetConfig = bottomSheet,
|
||||
marketPriceBlockState = MarketPriceBlockState.Content(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import androidx.annotation.DrawableRes
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.WrappedList
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
||||
|
|
@ -13,7 +14,6 @@ import com.tangem.feature.wallet.impl.R
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// TODO: Finalize notification strings [REDACTED_JIRA]
|
||||
sealed class WalletBottomSheetConfig(
|
||||
open val title: TextReference,
|
||||
open val subtitle: TextReference,
|
||||
|
|
@ -29,61 +29,24 @@ sealed class WalletBottomSheetConfig(
|
|||
@DrawableRes val iconResId: Int? = null,
|
||||
)
|
||||
|
||||
data class UnlockWallets(
|
||||
val onUnlockClick: () -> Unit,
|
||||
val onScanClick: () -> Unit,
|
||||
) : WalletBottomSheetConfig(
|
||||
title = TextReference.Str(value = "Unlock needed"),
|
||||
subtitle = TextReference.Str(
|
||||
value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " +
|
||||
"incididunt ut labore et dolore magna aliqua.",
|
||||
data class UnlockWallets(val onUnlockClick: () -> Unit, val onScanClick: () -> Unit) : WalletBottomSheetConfig(
|
||||
title = resourceReference(id = R.string.common_unlock_needed),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.unlock_wallet_description_full,
|
||||
formatArgs = wrappedList(
|
||||
resourceReference(R.string.common_biometrics),
|
||||
),
|
||||
),
|
||||
iconResId = R.drawable.ic_locked_24,
|
||||
tint = TangemColorPalette.Black,
|
||||
primaryButtonConfig = ButtonConfig(text = TextReference.Str(value = "Unlock"), onClick = onUnlockClick),
|
||||
primaryButtonConfig = ButtonConfig(
|
||||
text = resourceReference(id = R.string.user_wallet_list_unlock_all),
|
||||
onClick = onUnlockClick,
|
||||
),
|
||||
secondaryButtonConfig = ButtonConfig(
|
||||
text = TextReference.Str(value = "Scan card"),
|
||||
text = resourceReference(id = R.string.welcome_unlock_card),
|
||||
onClick = onScanClick,
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
),
|
||||
)
|
||||
|
||||
data class LikeTangemApp(
|
||||
val onRateTheAppClick: () -> Unit,
|
||||
val onShareClick: () -> Unit,
|
||||
) : WalletBottomSheetConfig(
|
||||
title = TextReference.Str(value = "Like Tangem App?"),
|
||||
subtitle = TextReference.Str(value = "How was your experience with our app? Let us know:"),
|
||||
iconResId = R.drawable.ic_star_24,
|
||||
tint = TangemColorPalette.Tangerine,
|
||||
primaryButtonConfig = ButtonConfig(
|
||||
text = TextReference.Str(value = "Rate the app"),
|
||||
onClick = onRateTheAppClick,
|
||||
),
|
||||
secondaryButtonConfig = ButtonConfig(
|
||||
text = TextReference.Str(value = "Share feedback"),
|
||||
onClick = onShareClick,
|
||||
),
|
||||
)
|
||||
|
||||
data class CriticalWarningAlreadySignedHashes(
|
||||
val onOkClick: () -> Unit,
|
||||
val onCancelClick: () -> Unit,
|
||||
) : WalletBottomSheetConfig(
|
||||
title = TextReference.Res(
|
||||
id = R.string.warning_important_security_info,
|
||||
formatArgs = WrappedList(listOf("\u26A0")),
|
||||
),
|
||||
subtitle = TextReference.Res(id = R.string.alert_signed_hashes_message),
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
tint = null,
|
||||
primaryButtonConfig = ButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_ok),
|
||||
onClick = onOkClick,
|
||||
),
|
||||
secondaryButtonConfig = ButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_cancel),
|
||||
onClick = onCancelClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -2,8 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.components
|
|||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.WrappedList
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
||||
/**
|
||||
|
|
@ -13,157 +12,144 @@ import com.tangem.feature.wallet.impl.R
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// TODO: Finalize notification strings [REDACTED_JIRA]
|
||||
@Immutable
|
||||
sealed class WalletNotification(open val config: NotificationConfig) {
|
||||
sealed class WalletNotification(val config: NotificationConfig) {
|
||||
|
||||
/** Clickable notification */
|
||||
sealed interface Clickable {
|
||||
sealed class Critical(title: TextReference, subtitle: TextReference) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
),
|
||||
) {
|
||||
|
||||
/** Lambda be invoked when notification is clicked */
|
||||
val onClick: () -> Unit
|
||||
object DevCard : Critical(
|
||||
title = resourceReference(id = R.string.common_warning),
|
||||
subtitle = resourceReference(id = R.string.alert_developer_card),
|
||||
)
|
||||
|
||||
object DemoCard : Critical(
|
||||
title = resourceReference(id = R.string.common_warning),
|
||||
subtitle = resourceReference(id = R.string.alert_demo_message),
|
||||
)
|
||||
|
||||
object TestNetCard : Critical(
|
||||
title = resourceReference(id = R.string.common_warning),
|
||||
subtitle = resourceReference(id = R.string.warning_testnet_card_message),
|
||||
)
|
||||
|
||||
object FailedCardValidation : Critical(
|
||||
title = resourceReference(id = R.string.warning_failed_to_verify_card_title),
|
||||
subtitle = resourceReference(id = R.string.warning_failed_to_verify_card_message),
|
||||
)
|
||||
|
||||
data class LowSignatures(val count: Int) : Critical(
|
||||
title = resourceReference(id = R.string.common_warning),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.warning_low_signatures_format,
|
||||
formatArgs = wrappedList(count),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/** "Development card" notification */
|
||||
object DevCard : WalletNotification(
|
||||
sealed class Warning(
|
||||
title: TextReference,
|
||||
subtitle: TextReference,
|
||||
buttonsState: NotificationConfig.ButtonsState? = null,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Res(id = R.string.common_warning),
|
||||
subtitle = TextReference.Res(id = R.string.alert_developer_card),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
buttonsState = buttonsState,
|
||||
onClick = onClick,
|
||||
),
|
||||
)
|
||||
) {
|
||||
|
||||
/** "Test card" notification */
|
||||
object TestCard : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Res(id = R.string.common_warning),
|
||||
subtitle = TextReference.Res(id = R.string.warning_testnet_card_message),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
),
|
||||
)
|
||||
data class MissingBackup(val onStartBackupClick: () -> Unit) : Warning(
|
||||
title = resourceReference(id = R.string.main_no_backup_warning_title),
|
||||
subtitle = resourceReference(id = R.string.main_no_backup_warning_subtitle),
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(id = R.string.button_start_backup_process),
|
||||
onClick = onStartBackupClick,
|
||||
),
|
||||
)
|
||||
|
||||
/** "Demo card" notification */
|
||||
object DemoCard : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Res(id = R.string.common_warning),
|
||||
subtitle = TextReference.Res(id = R.string.alert_demo_message),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
),
|
||||
)
|
||||
object NetworksUnreachable : Warning(
|
||||
title = resourceReference(id = R.string.wallet_balance_blockchain_unreachable),
|
||||
subtitle = resourceReference(id = R.string.warning_subtitle_network_unreachable),
|
||||
)
|
||||
|
||||
/** "Card verification failed" notification */
|
||||
object CardVerificationFailed : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Res(id = R.string.warning_failed_to_verify_card_title),
|
||||
subtitle = TextReference.Res(id = R.string.warning_failed_to_verify_card_message),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
),
|
||||
)
|
||||
object SomeNetworksUnreachable : Warning(
|
||||
title = resourceReference(id = R.string.warning_title_some_networks_unreachable),
|
||||
subtitle = resourceReference(id = R.string.warning_subtitle_some_networks_unreachable),
|
||||
)
|
||||
|
||||
/**
|
||||
* "Remaining signatures left" notification
|
||||
*
|
||||
* @param count number of remaining signatures
|
||||
*/
|
||||
class RemainingSignaturesLeft(count: Int) : WalletNotification(
|
||||
data class TopUpNote(val errorMessage: String) : Warning(
|
||||
title = resourceReference(id = R.string.warning_title_note_top_up),
|
||||
subtitle = stringReference(value = errorMessage),
|
||||
)
|
||||
|
||||
object NumberOfSignedHashesIncorrect : Warning(
|
||||
title = resourceReference(id = R.string.common_warning),
|
||||
subtitle = resourceReference(id = R.string.alert_card_signed_transactions),
|
||||
)
|
||||
|
||||
data class MultiWalletSignedHashesIncorrect(val onClick: () -> Unit) : Warning(
|
||||
title = resourceReference(id = R.string.common_warning),
|
||||
subtitle = resourceReference(id = R.string.warning_signed_tx_previously),
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
data class MissingAddresses(val missingAddressesCount: Int, val onGenerateClick: () -> Unit) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Res(id = R.string.common_warning),
|
||||
subtitle = TextReference.Res(
|
||||
id = R.string.warning_low_signatures_format,
|
||||
formatArgs = WrappedList(data = listOf(count)),
|
||||
title = resourceReference(id = R.string.main_warning_missing_derivation_title),
|
||||
subtitle = pluralReference(
|
||||
id = R.plurals.main_warning_missing_derivation_description,
|
||||
count = missingAddressesCount,
|
||||
formatArgs = wrappedList(missingAddressesCount),
|
||||
),
|
||||
iconResId = R.drawable.ic_alert_circle_24,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* "Already topped up and signed hashes" warning notification
|
||||
*
|
||||
* @property onClick lambda be invoked when notification's close button is clicked
|
||||
*/
|
||||
data class WarningAlreadySignedHashes(override val onClick: () -> Unit) : Clickable, WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Res(id = R.string.common_warning),
|
||||
subtitle = TextReference.Res(id = R.string.alert_card_signed_transactions),
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
onCloseClick = onClick,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* "Already signed hashes" critical warning notification
|
||||
*
|
||||
* @property onClick lambda be invoked when notification is clicked
|
||||
*/
|
||||
data class CriticalWarningAlreadySignedHashes(override val onClick: () -> Unit) : Clickable, WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Res(
|
||||
id = R.string.warning_important_security_info,
|
||||
formatArgs = WrappedList(listOf("\u26A0")),
|
||||
buttonsState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(id = R.string.common_generate_addresses),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
onClick = onGenerateClick,
|
||||
),
|
||||
subtitle = TextReference.Res(id = R.string.warning_signed_tx_previously),
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* "Backup the card" notification
|
||||
*
|
||||
* @property onClick lambda be invoked when notification is clicked
|
||||
*/
|
||||
data class BackupCard(override val onClick: () -> Unit) : Clickable, WalletNotification(
|
||||
data class UnlockWallets(val onClick: () -> Unit) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Str(value = "Backup your card"),
|
||||
subtitle = TextReference.Str(value = ""),
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
),
|
||||
)
|
||||
|
||||
/** "Unreachable networks" notification */
|
||||
object UnreachableNetworks : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Str(value = "Some networks are unreachable"),
|
||||
subtitle = TextReference.Str(value = ""),
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* "Like Tangem App" notification
|
||||
*
|
||||
* @property onClick lambda be invoked when notification is clicked
|
||||
*/
|
||||
data class LikeTangemApp(override val onClick: () -> Unit) : Clickable, WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Str(value = "Like Tangem App?"),
|
||||
subtitle = TextReference.Str(value = ""),
|
||||
iconResId = R.drawable.ic_star_24,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* "Scan the card" notification
|
||||
*
|
||||
* @property onClick lambda be invoked when notification is clicked
|
||||
*/
|
||||
data class ScanCard(override val onClick: () -> Unit) : Clickable, WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Str(value = "Scan your card to continue"),
|
||||
subtitle = TextReference.Str(value = ""),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* "Unlock wallets" notification
|
||||
*
|
||||
* @property onClick lambda be invoked when notification is clicked
|
||||
*/
|
||||
data class UnlockWallets(override val onClick: () -> Unit) : Clickable, WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = TextReference.Str(value = "Unlock needed"),
|
||||
subtitle = TextReference.Str(value = ""),
|
||||
title = resourceReference(id = R.string.common_unlock_needed),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.unlock_wallet_description_short,
|
||||
formatArgs = wrappedList(
|
||||
resourceReference(R.string.common_biometrics),
|
||||
),
|
||||
),
|
||||
iconResId = R.drawable.ic_locked_24,
|
||||
onClick = onClick,
|
||||
),
|
||||
)
|
||||
|
||||
data class RateApp(
|
||||
val onPositiveClick: () -> Unit,
|
||||
val onNegativeClick: () -> Unit,
|
||||
val onCloseClick: () -> Unit,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(id = R.string.warning_rate_app_title),
|
||||
subtitle = resourceReference(id = R.string.warning_rate_app_message),
|
||||
iconResId = R.drawable.ic_star_24,
|
||||
buttonsState = NotificationConfig.ButtonsState.PairButtonsConfig(
|
||||
primaryText = resourceReference(id = R.string.warning_button_love_it),
|
||||
onPrimaryClick = onPositiveClick,
|
||||
secondaryText = resourceReference(id = R.string.warning_button_can_be_better),
|
||||
onSecondaryClick = onNegativeClick,
|
||||
),
|
||||
onCloseClick = onCloseClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.lazy.LazyListScope
|
|||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
|
|
@ -20,8 +21,20 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
internal fun LazyListScope.notifications(configs: ImmutableList<WalletNotification>, modifier: Modifier = Modifier) {
|
||||
items(
|
||||
items = configs,
|
||||
key = { it.config.title.hashCode() },
|
||||
key = { it::class.java },
|
||||
contentType = { it.config::class.java },
|
||||
itemContent = { Notification(config = it.config, modifier = modifier.animateItemPlacement()) },
|
||||
itemContent = {
|
||||
Notification(
|
||||
config = it.config,
|
||||
modifier = modifier.animateItemPlacement(),
|
||||
iconTint = when (it) {
|
||||
is WalletNotification.Critical -> TangemTheme.colors.icon.warning
|
||||
is WalletNotification.MissingAddresses -> TangemTheme.colors.icon.accent
|
||||
is WalletNotification.RateApp -> TangemTheme.colors.icon.attention
|
||||
is WalletNotification.UnlockWallets -> TangemTheme.colors.icon.primary1
|
||||
is WalletNotification.Warning -> null
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -9,9 +9,7 @@ internal interface WalletClickIntents {
|
|||
|
||||
fun onBackClick()
|
||||
|
||||
fun onScanCardClick()
|
||||
|
||||
fun onScanCardNotificationClick()
|
||||
fun onGenerateMissedAddressesClick()
|
||||
|
||||
fun onScanToUnlockWalletClick()
|
||||
|
||||
|
|
@ -19,16 +17,12 @@ internal interface WalletClickIntents {
|
|||
|
||||
fun onBackupCardClick()
|
||||
|
||||
fun onCriticalWarningAlreadySignedHashesClick()
|
||||
|
||||
fun onCloseWarningAlreadySignedHashesClick()
|
||||
fun onMultiWalletSignedHashesNotificationClick()
|
||||
|
||||
fun onLikeTangemAppClick()
|
||||
|
||||
fun onRateTheAppClick()
|
||||
|
||||
fun onShareClick()
|
||||
|
||||
fun onWalletChange(index: Int)
|
||||
|
||||
fun onRefreshSwipe()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.viewmodels
|
||||
|
||||
import com.tangem.domain.card.GetCardWasScannedUseCase
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkGroup
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -13,126 +14,166 @@ import kotlinx.coroutines.flow.flow
|
|||
/**
|
||||
* Wallet notifications list factory
|
||||
*
|
||||
* @property wasCardScannedCallback callback that check if card was scanned
|
||||
* @property isUserAlreadyRateAppCallback callback that check if card is user already rate app
|
||||
* @property isDemoCardCallback callback that check if card is demo
|
||||
* @property clickIntents screen click intents
|
||||
* @property isDemoCardUseCase use case that check if card is demo
|
||||
* @property isUserAlreadyRateAppUseCase use case that check if card is user already rate app
|
||||
* @property getCardWasScannedUseCase use case that check if card was scanned
|
||||
* @property clickIntents screen click intents
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class WalletNotificationsListFactory(
|
||||
private val wasCardScannedCallback: suspend (String) -> Boolean,
|
||||
private val isUserAlreadyRateAppCallback: suspend () -> Boolean,
|
||||
private val isDemoCardCallback: (String) -> Boolean,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val isUserAlreadyRateAppUseCase: IsUserAlreadyRateAppUseCase,
|
||||
private val getCardWasScannedUseCase: GetCardWasScannedUseCase,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) {
|
||||
|
||||
fun create(cardTypesResolver: CardTypesResolver, tokenList: TokenList?): Flow<ImmutableList<WalletNotification>> {
|
||||
// TODO: [REDACTED_JIRA] order
|
||||
fun create(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
cryptoCurrencyList: List<CryptoCurrencyStatus.Status>,
|
||||
): Flow<ImmutableList<WalletNotification>> {
|
||||
return flow {
|
||||
emit(
|
||||
buildList {
|
||||
if (cardTypesResolver.isTestCard()) {
|
||||
add(element = WalletNotification.TestCard)
|
||||
return@buildList
|
||||
}
|
||||
addCriticalNotifications(cardTypesResolver)
|
||||
|
||||
addRemainingSignaturesLeftNotifications(cardTypesResolver)
|
||||
addMissingAddressesNotification(cryptoCurrencyList)
|
||||
|
||||
val isDemo = isDemoCardCallback(cardTypesResolver.getCardId())
|
||||
if (!cardTypesResolver.isReleaseFirmwareType()) {
|
||||
add(element = WalletNotification.DevCard)
|
||||
} else {
|
||||
addReleaseSpecialNotifications(cardTypesResolver = cardTypesResolver, isDemo = isDemo)
|
||||
}
|
||||
addRateTheAppNotification()
|
||||
|
||||
if (isDemo) {
|
||||
add(element = WalletNotification.DemoCard)
|
||||
}
|
||||
|
||||
if (hasUnreachableNetworks(tokenList)) {
|
||||
add(element = WalletNotification.UnreachableNetworks)
|
||||
}
|
||||
|
||||
if (!cardTypesResolver.isBackupForbidden() && !cardTypesResolver.hasBackup()) {
|
||||
add(element = WalletNotification.BackupCard(onClick = clickIntents::onBackupCardClick))
|
||||
}
|
||||
|
||||
if (tokenList != null && tokenList.hasMissedDerivations()) {
|
||||
add(element = WalletNotification.ScanCard(onClick = clickIntents::onScanCardNotificationClick))
|
||||
}
|
||||
|
||||
if (isUserAlreadyRateAppCallback()) {
|
||||
add(element = WalletNotification.LikeTangemApp(onClick = clickIntents::onLikeTangemAppClick))
|
||||
}
|
||||
}.toImmutableList(),
|
||||
addWarningNotifications(cardTypesResolver, cryptoCurrencyList)
|
||||
}
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addRemainingSignaturesLeftNotifications(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
) {
|
||||
val remainingSignatures = cardTypesResolver.getRemainingSignatures()
|
||||
if (remainingSignatures != null && remainingSignatures <= MAX_REMAINING_SIGNATURES_COUNT) {
|
||||
add(element = WalletNotification.RemainingSignaturesLeft(remainingSignatures))
|
||||
private fun MutableList<WalletNotification>.addCriticalNotifications(cardTypesResolver: CardTypesResolver) {
|
||||
addIf(
|
||||
element = WalletNotification.Critical.DevCard,
|
||||
condition = !cardTypesResolver.isReleaseFirmwareType(),
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Critical.DemoCard,
|
||||
condition = isDemoCardUseCase(cardId = cardTypesResolver.getCardId()),
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Critical.TestNetCard,
|
||||
condition = cardTypesResolver.isTestCard(),
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Critical.FailedCardValidation,
|
||||
condition = cardTypesResolver.isReleaseFirmwareType() && cardTypesResolver.isAttestationFailed(),
|
||||
)
|
||||
|
||||
cardTypesResolver.getRemainingSignatures()?.let { remainingSignatures ->
|
||||
addIf(
|
||||
element = WalletNotification.Critical.LowSignatures(count = remainingSignatures),
|
||||
condition = remainingSignatures <= MAX_REMAINING_SIGNATURES_COUNT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun MutableList<WalletNotification>.addReleaseSpecialNotifications(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
isDemo: Boolean,
|
||||
private fun MutableList<WalletNotification>.addMissingAddressesNotification(
|
||||
cryptoCurrencyList: List<CryptoCurrencyStatus.Status>,
|
||||
) {
|
||||
if (!wasCardScannedCallback(cardTypesResolver.getCardId()) && cardTypesResolver.isMultiwalletAllowed() &&
|
||||
!isDemo
|
||||
) {
|
||||
if (cardTypesResolver.isBackupForbidden() && cardTypesResolver.hasWalletSignedHashes()) {
|
||||
add(
|
||||
element = WalletNotification.CriticalWarningAlreadySignedHashes(
|
||||
onClick = clickIntents::onCriticalWarningAlreadySignedHashesClick,
|
||||
),
|
||||
val missedAddressesCount = cryptoCurrencyList.getMissedAddressesCount()
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.MissingAddresses(
|
||||
missingAddressesCount = missedAddressesCount,
|
||||
onGenerateClick = clickIntents::onGenerateMissedAddressesClick,
|
||||
),
|
||||
condition = missedAddressesCount > 0,
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus.Status>.getMissedAddressesCount(): Int {
|
||||
return filterIsInstance<CryptoCurrencyStatus.MissedDerivation>().count()
|
||||
}
|
||||
|
||||
// TODO: [REDACTED_JIRA]
|
||||
private suspend fun MutableList<WalletNotification>.addRateTheAppNotification() {
|
||||
addIf(
|
||||
element = WalletNotification.RateApp(
|
||||
onPositiveClick = {},
|
||||
onNegativeClick = {},
|
||||
onCloseClick = {},
|
||||
),
|
||||
condition = isUserAlreadyRateAppUseCase(),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun MutableList<WalletNotification>.addWarningNotifications(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
cryptoCurrencyList: List<CryptoCurrencyStatus.Status>,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.Warning.MissingBackup(
|
||||
onStartBackupClick = clickIntents::onBackupCardClick,
|
||||
),
|
||||
condition = !cardTypesResolver.isBackupForbidden() && !cardTypesResolver.hasBackup(),
|
||||
)
|
||||
|
||||
val isDemo = isDemoCardUseCase(cardId = cardTypesResolver.getCardId())
|
||||
if (cardTypesResolver.isMultiwalletAllowed()) {
|
||||
addIf(
|
||||
element = WalletNotification.Warning.SomeNetworksUnreachable,
|
||||
condition = cryptoCurrencyList.hasUnreachableNetworks(),
|
||||
)
|
||||
|
||||
if (cardTypesResolver.isBackupForbidden()) {
|
||||
addIf(
|
||||
element = WalletNotification.Warning.NumberOfSignedHashesIncorrect,
|
||||
condition = checkSignedHashes(cardTypesResolver = cardTypesResolver, isDemo = isDemo),
|
||||
)
|
||||
} else if (cardTypesResolver.hasWalletSignedHashes()) {
|
||||
add(
|
||||
element = WalletNotification.WarningAlreadySignedHashes(
|
||||
onClick = clickIntents::onCloseWarningAlreadySignedHashesClick,
|
||||
} else {
|
||||
addIf(
|
||||
element = WalletNotification.Warning.MultiWalletSignedHashesIncorrect(
|
||||
onClick = clickIntents::onMultiWalletSignedHashesNotificationClick,
|
||||
),
|
||||
condition = checkSignedHashes(cardTypesResolver = cardTypesResolver, isDemo = isDemo),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addIf(
|
||||
element = WalletNotification.Warning.NetworksUnreachable,
|
||||
condition = cryptoCurrencyList.hasUnreachableNetworks(),
|
||||
)
|
||||
|
||||
if (cardTypesResolver.isAttestationFailed()) {
|
||||
add(element = WalletNotification.CardVerificationFailed)
|
||||
// TODO: [REDACTED_JIRA]
|
||||
addIf(
|
||||
element = WalletNotification.Warning.TopUpNote(
|
||||
errorMessage = "To activate card top up it with at least 1 XLM",
|
||||
),
|
||||
condition = cryptoCurrencyList.hasNoAccountStatus(),
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Warning.NumberOfSignedHashesIncorrect,
|
||||
condition = checkSignedHashes(cardTypesResolver, isDemo),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasUnreachableNetworks(tokenList: TokenList?): Boolean {
|
||||
return when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> {
|
||||
tokenList.groups
|
||||
.flatMap(NetworkGroup::currencies)
|
||||
.map(CryptoCurrencyStatus::value)
|
||||
.any { it is CryptoCurrencyStatus.Unreachable }
|
||||
}
|
||||
is TokenList.Ungrouped -> {
|
||||
tokenList.currencies
|
||||
.map(CryptoCurrencyStatus::value)
|
||||
.any { it is CryptoCurrencyStatus.Unreachable }
|
||||
}
|
||||
is TokenList.NotInitialized,
|
||||
null,
|
||||
-> false
|
||||
}
|
||||
private fun MutableList<WalletNotification>.addIf(element: WalletNotification, condition: Boolean) {
|
||||
if (condition) add(element = element)
|
||||
}
|
||||
|
||||
private fun TokenList.hasMissedDerivations(): Boolean {
|
||||
val statuses = when (this) {
|
||||
is TokenList.GroupedByNetwork -> groups.flatMap(NetworkGroup::currencies).map(CryptoCurrencyStatus::value)
|
||||
is TokenList.Ungrouped -> currencies.map(CryptoCurrencyStatus::value)
|
||||
TokenList.NotInitialized -> emptyList()
|
||||
}
|
||||
private fun List<CryptoCurrencyStatus.Status>.hasUnreachableNetworks(): Boolean {
|
||||
return any { it is CryptoCurrencyStatus.Unreachable }
|
||||
}
|
||||
|
||||
return statuses.any { it is CryptoCurrencyStatus.MissedDerivation }
|
||||
private fun List<CryptoCurrencyStatus.Status>.hasNoAccountStatus(): Boolean {
|
||||
return any { it is CryptoCurrencyStatus.NoAccount }
|
||||
}
|
||||
|
||||
private suspend fun checkSignedHashes(cardTypesResolver: CardTypesResolver, isDemo: Boolean): Boolean {
|
||||
return cardTypesResolver.isReleaseFirmwareType() && cardTypesResolver.hasWalletSignedHashes() &&
|
||||
!isDemo && !getCardWasScannedUseCase(cardId = cardTypesResolver.getCardId())
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import com.tangem.core.navigation.AppScreen
|
|||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.balancehiding.IsBalanceHiddenUseCase
|
||||
|
|
@ -25,6 +24,7 @@ import com.tangem.domain.settings.*
|
|||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkGroup
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.domain.tokens.models.Network
|
||||
|
|
@ -32,7 +32,6 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
|||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.userwallets.UserWalletBuilder
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.SaveWalletError
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
|
|
@ -41,7 +40,6 @@ import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
|||
import com.tangem.feature.wallet.presentation.wallet.analytics.PortfolioEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory
|
||||
|
|
@ -80,9 +78,6 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
|
||||
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
|
||||
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
|
||||
private val getCardWasScannedUseCase: GetCardWasScannedUseCase,
|
||||
private val isUserAlreadyRateAppUseCase: IsUserAlreadyRateAppUseCase,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val scanCardProcessor: ScanCardProcessor,
|
||||
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
|
|
@ -99,6 +94,9 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
getCardWasScannedUseCase: GetCardWasScannedUseCase,
|
||||
isUserAlreadyRateAppUseCase: IsUserAlreadyRateAppUseCase,
|
||||
isDemoCardUseCase: IsDemoCardUseCase,
|
||||
// endregion Parameters
|
||||
) : ViewModel(), DefaultLifecycleObserver, WalletClickIntents {
|
||||
|
||||
|
|
@ -109,9 +107,9 @@ internal class WalletViewModel @Inject constructor(
|
|||
private var isBalanceHidden = true
|
||||
|
||||
private val notificationsListFactory = WalletNotificationsListFactory(
|
||||
wasCardScannedCallback = getCardWasScannedUseCase::invoke,
|
||||
isUserAlreadyRateAppCallback = isUserAlreadyRateAppUseCase::invoke,
|
||||
isDemoCardCallback = isDemoCardUseCase::invoke,
|
||||
getCardWasScannedUseCase = getCardWasScannedUseCase,
|
||||
isUserAlreadyRateAppUseCase = isUserAlreadyRateAppUseCase,
|
||||
isDemoCardUseCase = isDemoCardUseCase,
|
||||
clickIntents = this,
|
||||
)
|
||||
|
||||
|
|
@ -254,58 +252,9 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onScanCardClick() {
|
||||
override fun onGenerateMissedAddressesClick() {
|
||||
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.NoticeScanYourCardTapped)
|
||||
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
scanCardProcessor.scan()
|
||||
.doOnSuccess {
|
||||
// If card's public key is null then user wallet will be null
|
||||
val userWallet = UserWalletBuilder(scanResponse = it).build()
|
||||
|
||||
if (userWallet != null) {
|
||||
saveWalletUseCase(userWallet = userWallet, canOverride = false)
|
||||
.onLeft { saveWalletError ->
|
||||
showErrorIfWalletNotSaved(
|
||||
error = saveWalletError,
|
||||
scannedWalletId = userWallet.walletId,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showErrorIfWalletNotSaved(error: SaveWalletError, scannedWalletId: UserWalletId) {
|
||||
when (error) {
|
||||
is SaveWalletError.DataError -> Unit
|
||||
is SaveWalletError.WalletAlreadySaved -> {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
val alreadySavedWalletIndex = (uiState as? WalletState.ContentState)?.walletsListConfig
|
||||
?.wallets?.indexOfFirst { it.id == scannedWalletId }
|
||||
|
||||
if (alreadySavedWalletIndex != null && alreadySavedWalletIndex != -1) {
|
||||
uiState = stateFactory.getStateAndTriggerEvent(
|
||||
state = uiState,
|
||||
event = WalletEvent.ChangeWallet(index = alreadySavedWalletIndex),
|
||||
setUiState = { uiState = it },
|
||||
)
|
||||
|
||||
// Delay between events
|
||||
delay(timeMillis = 500L)
|
||||
}
|
||||
|
||||
uiState = stateFactory.getStateAndTriggerEvent(
|
||||
state = uiState,
|
||||
event = WalletEvent.ShowError(text = TextReference.Res(error.messageId)),
|
||||
setUiState = { uiState = it },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onScanCardNotificationClick() {
|
||||
scanToUpdateSelectedWallet(
|
||||
onSuccessSave = {
|
||||
// Reload currencies with missed derivation
|
||||
|
|
@ -361,33 +310,15 @@ internal class WalletViewModel @Inject constructor(
|
|||
router.openOnboardingScreen()
|
||||
}
|
||||
|
||||
override fun onCriticalWarningAlreadySignedHashesClick() {
|
||||
uiState = stateFactory.getStateWithOpenWalletBottomSheet(
|
||||
content = WalletBottomSheetConfig.CriticalWarningAlreadySignedHashes(
|
||||
onOkClick = {},
|
||||
onCancelClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCloseWarningAlreadySignedHashesClick() {
|
||||
override fun onMultiWalletSignedHashesNotificationClick() {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
}
|
||||
|
||||
override fun onLikeTangemAppClick() {
|
||||
uiState = stateFactory.getStateWithOpenWalletBottomSheet(
|
||||
content = WalletBottomSheetConfig.LikeTangemApp(
|
||||
onRateTheAppClick = ::onRateTheAppClick,
|
||||
onShareClick = ::onShareClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onRateTheAppClick() {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
}
|
||||
|
||||
override fun onShareClick() {
|
||||
override fun onRateTheAppClick() {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
}
|
||||
|
||||
|
|
@ -772,7 +703,19 @@ internal class WalletViewModel @Inject constructor(
|
|||
private fun updateNotifications(index: Int, tokenList: TokenList? = null) {
|
||||
notificationsListFactory.create(
|
||||
cardTypesResolver = getCardTypeResolver(index = index),
|
||||
tokenList = tokenList,
|
||||
cryptoCurrencyList = if (tokenList != null) {
|
||||
when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> {
|
||||
tokenList.groups
|
||||
.flatMap(NetworkGroup::currencies)
|
||||
.map(CryptoCurrencyStatus::value)
|
||||
}
|
||||
is TokenList.Ungrouped -> tokenList.currencies.map(CryptoCurrencyStatus::value)
|
||||
is TokenList.NotInitialized -> emptyList()
|
||||
}
|
||||
} else {
|
||||
listOfNotNull(singleWalletCryptoCurrencyStatus?.value)
|
||||
},
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.onEach { uiState = stateFactory.getStateByNotifications(notifications = it) }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue