diff --git a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt index c9d8acac7e..80eac8dc24 100644 --- a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt +++ b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt @@ -14,6 +14,7 @@ import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage import com.tangem.datasource.local.config.issuers.IssuersConfigStorage +import com.tangem.datasource.local.logs.AppLogsStore import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.apptheme.GetAppThemeModeUseCase @@ -124,5 +125,7 @@ interface ApplicationEntryPoint { fun getExcludedBlockchains(): ExcludedBlockchains + fun getAppLogsStore(): AppLogsStore + fun getClipboardManager(): ClipboardManager } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index f917e99f42..83bba376a4 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -27,6 +27,7 @@ import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.datasource.local.config.environment.EnvironmentConfig import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage import com.tangem.datasource.local.config.issuers.IssuersConfigStorage +import com.tangem.datasource.local.logs.AppLogsStore import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.apptheme.GetAppThemeModeUseCase @@ -67,6 +68,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking import org.rekotlin.Store +import kotlin.collections.set import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository lateinit var store: Store @@ -195,6 +197,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { private val excludedBlockchains: ExcludedBlockchains get() = entryPoint.getExcludedBlockchains() + private val appLogsStore: AppLogsStore + get() = entryPoint.getAppLogsStore() + private val clipboardManager: ClipboardManager get() = entryPoint.getClipboardManager() // endregion @@ -203,6 +208,21 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { super.onCreate() init() + + updateLogFiles() + } + + private fun updateLogFiles() { + appLogsStore.deleteOldLogsFile() + appLogsStore.deleteLastLogFile() + + // Temporally logs are not saved + // scope.launch { + // if (!appPreferencesStore.getSyncOrDefault(WAS_LOG_FILE_CLEARED, false)) { + // appLogsStore.deleteLastLogFile() + // appPreferencesStore.store(WAS_LOG_FILE_CLEARED, true) + // } + // } } fun init() { diff --git a/app/src/main/java/com/tangem/tap/common/log/TangemCardSDKLogger.kt b/app/src/main/java/com/tangem/tap/common/log/TangemCardSDKLogger.kt index ef740906db..e9c1efdd96 100644 --- a/app/src/main/java/com/tangem/tap/common/log/TangemCardSDKLogger.kt +++ b/app/src/main/java/com/tangem/tap/common/log/TangemCardSDKLogger.kt @@ -14,6 +14,7 @@ import com.tangem.datasource.local.logs.AppLogsStore * [REDACTED_AUTHOR] */ +@Suppress("UnusedPrivateMember") internal class TangemCardSDKLogger( private val levels: List, private val messageFormatter: LogFormat, @@ -21,8 +22,9 @@ internal class TangemCardSDKLogger( ) : TangemSdkLogger { override fun log(message: () -> String, level: Log.Level) { - if (!levels.contains(level)) return - - appLogsStore.saveLogMessage(message = messageFormatter.format(message, level)) + // Disabled for now in [REDACTED_JIRA] + // if (!levels.contains(level)) return + // + // appLogsStore.saveLogMessage(message = messageFormatter.format(message, level)) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt index cc9b89e775..49fdf2c1be 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt @@ -159,4 +159,10 @@ internal object WalletsDomainModule { dispatchers = dispatchers, ) } + + @Provides + @Singleton + fun providesSeedPhraseNotificationUseCase(walletsRepository: WalletsRepository): SeedPhraseNotificationUseCase { + return SeedPhraseNotificationUseCase(walletsRepository = walletsRepository) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 0cb19b30a1..d99a38a119 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -658,6 +658,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) var userWallet: UserWallet? = null if (scanResponse != null) { + scanResponse = updateScanResponseAfterBackup(scanResponse!!, backupState) userWallet = createUserWallet( scanResponse = requireNotNull(value = scanResponse, lazyMessage = { "ScanResponse is null" }), backupState = backupState, @@ -691,6 +692,14 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) ) } + scope.launch { + userWallet?.let { + if (it.scanResponse.cardTypesResolver.isWallet2() && it.isImported) { + store.inject(DaggerGraphState::walletsRepository).markWallet2WasCreated(it.walletId) + } + } + } + val notActivatedCardIds = gatherCardIds(backupState, card).mapNotNull { if (store.state.globalState.onboardingState.onboardingManager?.isActivationFinished(it) == true) { null diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 5fea431d0d..a1afc8ea37 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -10,6 +10,8 @@ android { dependencies { + implementation(projects.core.utils) + // region Firebase libraries implementation(platform(deps.firebase.bom)) implementation(deps.firebase.analytics) diff --git a/common/src/main/kotlin/com/tangem/common/TangemBlogUrlBuilder.kt b/common/src/main/kotlin/com/tangem/common/TangemBlogUrlBuilder.kt new file mode 100644 index 0000000000..27017a9505 --- /dev/null +++ b/common/src/main/kotlin/com/tangem/common/TangemBlogUrlBuilder.kt @@ -0,0 +1,28 @@ +package com.tangem.common + +import com.tangem.utils.SupportedLanguages + +/** +[REDACTED_AUTHOR] + */ +object TangemBlogUrlBuilder { + + fun build(post: Post): String { + val code = SupportedLanguages.getCurrentSupportedLanguageCode() + .takeIf { code -> + code == SupportedLanguages.RUSSIAN || code == SupportedLanguages.ENGLISH + } + ?: SupportedLanguages.ENGLISH + + return "https://tangem.com/$code/blog/post/${post.path}/" + } + + sealed interface Post { + + val path: String + + data object SeedNotify : Post { + override val path: String = "seed-notify" + } + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt index 99b5144126..660e465d75 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt @@ -49,6 +49,9 @@ interface TangemTechApi { @Body userTokens: UserTokensResponse, ): ApiResponse + @POST("user-tokens") + suspend fun markUserWallerWasCreated(@Body body: MarkUserWalletWasCreatedBody): ApiResponse + /** Returns referral status by [walletId] */ @GET("referral/{walletId}") suspend fun getReferralStatus(@Path("walletId") walletId: String): ReferralResponse @@ -108,6 +111,17 @@ interface TangemTechApi { @GET("networks/providers") suspend fun getBlockchainProviders(): Map> + @GET("seedphrase-notification/{wallet_id}") + suspend fun getSeedPhraseNotificationStatus( + @Path("wallet_id") walletId: String, + ): ApiResponse + + @PUT("seedphrase-notification/{wallet_id}") + suspend fun updateSeedPhraseNotificationStatus( + @Path("wallet_id") walletId: String, + @Body body: SeedPhraseNotificationDTO, + ): ApiResponse + companion object { val marketsQuoteFields = listOf( "price", diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/MarkUserWalletWasCreatedBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/MarkUserWalletWasCreatedBody.kt new file mode 100644 index 0000000000..0c4d2a5bd8 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/MarkUserWalletWasCreatedBody.kt @@ -0,0 +1,9 @@ +package com.tangem.datasource.api.tangemTech.models + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class MarkUserWalletWasCreatedBody( + @Json(name = "user_wallet_id") val userWalletId: String, +) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/SeedPhraseNotificationDTO.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/SeedPhraseNotificationDTO.kt new file mode 100644 index 0000000000..e54c1cab47 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/SeedPhraseNotificationDTO.kt @@ -0,0 +1,24 @@ +package com.tangem.datasource.api.tangemTech.models + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class SeedPhraseNotificationDTO(val status: Status) { + + enum class Status { + @Json(name = "notneeded") + NOT_NEEDED, + + @Json(name = "notified") + NOTIFIED, + + @Json(name = "declined") + DECLINED, + + @Json(name = "confirmed") + CONFIRMED, + + ; + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/logs/AppLogsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/logs/AppLogsStore.kt index 81ebc5f7ea..bb1112d268 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/logs/AppLogsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/logs/AppLogsStore.kt @@ -38,7 +38,7 @@ class AppLogsStore @Inject constructor( ) private val mutex = Mutex() - private val file = File(applicationContext.filesDir, LOG_FILE_NAME) + private val file = File(applicationContext.filesDir, NEW_LOG_FILE_NAME) private val formatter = DateTimeFormatterBuilder() .appendDayOfMonth(2) @@ -59,6 +59,9 @@ class AppLogsStore @Inject constructor( /** Save log [message] */ fun saveLogMessage(message: String) { + // Temporally logs are not saved + return + launchWithLock { createFileIfNotExist() @@ -68,6 +71,9 @@ class AppLogsStore @Inject constructor( /** Save log that consists from [messages] */ fun saveLogMessage(vararg messages: String) { + // Temporally logs are not saved + return + launchWithLock { createFileIfNotExist() @@ -84,6 +90,18 @@ class AppLogsStore @Inject constructor( } } + fun deleteOldLogsFile() { + val file = File(applicationContext.filesDir, LOG_FILE_NAME) + + if (file.exists()) file.delete() + } + + fun deleteLastLogFile() { + val file = File(applicationContext.filesDir, NEW_LOG_FILE_NAME) + + if (file.exists()) file.delete() + } + private fun writeMessage(vararg messages: String) { BufferedWriter(FileWriter(file, true)).use { writer -> writer.append(formatter.print(DateTime.now())) @@ -112,5 +130,6 @@ class AppLogsStore @Inject constructor( private companion object { const val LOG_FILE_NAME = "logs.txt" + const val NEW_LOG_FILE_NAME = "app_logs.txt" } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index 0e9a1885e9..a18f6044f7 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -111,6 +111,8 @@ object PreferencesKeys { val IS_GOOGLE_PAY_AVAILABLE_KEY by lazy { booleanPreferencesKey(name = "isGooglePayAvailable") } + val WAS_LOG_FILE_CLEARED by lazy { booleanPreferencesKey(name = "wasLogFileCleared") } + // region Permission fun getShouldShowPermission(permission: String) = booleanPreferencesKey("shouldShowPushPermission_$permission") diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index aa37b93bdc..6e3db49a06 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -51,6 +51,8 @@ Nicht mehr anzeigen Verstanden Guthaben sind ausgeblendet + Laut den Blockchain-Entwicklern befinden sich der Kaspa-Token derzeit in der Betaphase. Bleibe dran für Updates! + Beta-Phase Bitte Karte oder Ring scannen Bitte versuche es in 30 Sekunden erneut oder scanne die Karte oder Ring Zu viele Versuche @@ -1065,6 +1067,10 @@ Gefällt dir Tangem? Du musst deinen Token zuordnen, bevor du Token erhalten kannst Netzmietgebühr erforderlich + Vielen Dank! Alles erledigt! Keine weiteren Maßnahmen erforderlich. + Sie werden jetzt auf die offizielle Tangem-Website weitergeleitet. Bitte lesen und befolgen Sie dort die Anweisungen. + Haben Sie jemals das Support-Team von Tangem direkt über diese Anwendung kontaktiert? + Verpflichtendes Sicherheitsupdate %1$s ist ein Vermögenswert im %2$s Netzwerk. Um eine %3$s Transaktion durchzuführen, musst du etwas %4$s (%5$s) einzahlen, um die Netzwerkgebühr zu decken. Unzureichende %1$s zur Deckung der Netzgebühr Das Solana-Netz ist überlastet. Wenn deine Transaktion nicht innerhalb von 2 Minuten bearbeitet wird, wiederhole bitte die Transaktion. diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml index 1bd3de114c..ad2f784c78 100644 --- a/core/res/src/main/res/values-es/strings.xml +++ b/core/res/src/main/res/values-es/strings.xml @@ -1065,6 +1065,10 @@ ¿Disfrutando de Tangem? Deba asociar su token antes de recibir tokens Se requiere tarifa de alquiler de red + ¡Gracias! ¡Todo está listo! No se requieren más acciones. + Ahora será redirigido al sitio web oficial de Tangem. Por favor, lea y siga las instrucciones allí indicadas. + ¿Alguna vez contactó al equipo de soporte de Tangem directamente desde esta aplicación? + Actualización de seguridad obligatoria %1$s un activo en la red %2$s. Para realizar una transacción %3$s, deposite %4$s (%5$s) para cubrir la tarifa de red. %1$s insuficiente para cubrir la tarifa de red La red Solana está congestionada. Si su transacción no se procesa dentro de 2 minutos, repita la transacción. diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index e6647acb4d..2f0c658d65 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -51,6 +51,8 @@ Ne plus afficher Compris Les soldes sont masqués + Selon les développeurs de la blockchain, les jetons Kaspa sont actuellement en version bêta. Restez à l\'écoute des mises à jour ! + Mode bêta Veuillez scanner la carte/bague Veuillez réessayer dans 30 secondes ou scannez la carte/bague Trop de tentatives @@ -1065,6 +1067,10 @@ Vous appréciez Tangem ? Vous devez associer votre jeton avant de recevoir des jetons Frais de location de réseau requis + Merci ! Tout est en ordre ! Aucune autre action n\'est requise. + Vous allez être redirigé vers le site officiel de Tangem. Veuillez lire et suivre les instructions qui y sont indiquées. + Avez-vous déjà contacté l\'équipe de support Tangem directement depuis cette application ? + Mise à jour de sécurité obligatoire %1$s s\'agit d\'un actif du réseau %2$s. Pour effectuer une transaction %3$s, vous devez déposer une certaine quantité de %4$s (%5$s) pour couvrir les frais de réseau. %1$s insuffisant pour couvrir les frais de réseau Le réseau Solana est encombré. Si votre transaction n\'est pas traitée dans les 2 minutes, veuillez répéter la transaction. diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 66cd763ddb..7d9a780c4d 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -51,6 +51,8 @@ 今後表示しない わかりました 残高は非表示 + ブロックチェーン開発者によると、Kaspaトークンは現在ベータ版です。アップデートをお楽しみに! + ベータモード カードまたはリングをスキャンしてください 30秒後に再試行するか、カードまたはリングをスキャンしてください 試行回数が多すぎます diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index f63d0d3e28..191593c0bb 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -1086,6 +1086,10 @@ Нравится Tangem? Вам необходимо провести ассоциацию токена для того, чтобы иметь возможность принимать его Необходима плата за аренду сети + Спасибо! Всё в порядке! Дальнейшие действия не требуются. + Вы будете перенаправлены на официальный сайт Tangem. Пожалуйста, прочитайте и выполните указанные там инструкции. + Вы когда-либо обращались в службу поддержки Tangem напрямую из этого приложения? + Обязательное обновление безопасности %1$s - это монета в сети %2$s. Для совершения транзакции %3$s, вам необходимо внести немного %4$s (%5$s), чтобы покрыть комиссию сети. Недостаточно %1$s для оплаты комиссии сети Сеть Солана испытывает высокую нагрузку. Если Ваша транзакция не прошла в течение 2 минут, повторите её отправку. diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index 9b615a149f..5203d19d26 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -1087,6 +1087,10 @@ Подобається Tangem? Вам необхідно провести асоціацію токена, щоб мати можливість приймати його Необхідна плата за оренду мережі + Дякуємо! Все добре! Подальших дій не потрібно. + Ви будете перенаправлені на офіційний сайт Tangem. Будь ласка, прочитайте та виконайте вказані там інструкції. + Ви коли-небудь зверталися до служби підтримки Tangem безпосередньо з цього додатку? + Обов’язкове оновлення безпеки %1$s є активом у мережі %2$s. Щоб здійснити транзакцію %3$s, ви повинні внести певну суму %4$s (%5$s), щоб покрити комісію мережі. Недостатньо %1$s для покриття комісії мережі Мережа Солана зазнає високого навантаження. Якщо транзакція не пройшла протягом 2 хвилин, повторіть транзакцію. diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 99759792da..7f19088018 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1067,6 +1067,10 @@ Enjoying Tangem? You must associate your token before receiving tokens Network rent fee required + Thank you! All set! No further actions required. + You will now be redirected to the official Tangem website. Please read and follow instructions there. + Have you ever contacted the Tangem support team directly through this application? + Mandatory security update %1$s is an asset in the %2$s network. To make a %3$s transaction, you must deposit some %4$s (%5$s) to cover the network fee. Insufficient %1$s to cover network fee The Solana network is congested. If your transaction is not processed within 2 minutes, please repeat the transaction. diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt index c18e2c3e71..7bd5322685 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt @@ -216,6 +216,10 @@ private fun Buttons(state: NotificationButtonsState?, isEnabled: Boolean = true) ) is NotificationButtonsState.PrimaryButtonConfig -> SinglePrimaryButton(config = state, isEnabled = isEnabled) is NotificationButtonsState.PairButtonsConfig -> PairButtons(config = state, isEnabled = isEnabled) + is NotificationConfig.ButtonsState.SecondaryPairButtonsConfig -> SecondaryPairButtons( + config = state, + isEnabled = isEnabled, + ) null -> Unit } } @@ -284,6 +288,30 @@ private fun PairButtons(config: NotificationButtonsState.PairButtonsConfig, isEn } } +@Composable +private fun SecondaryPairButtons( + config: NotificationButtonsState.SecondaryPairButtonsConfig, + isEnabled: Boolean = true, +) { + Row(horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing8)) { + SecondaryButton( + text = config.leftText.resolveReference(), + onClick = config.onLeftClick, + modifier = Modifier.weight(weight = 1f), + size = TangemButtonSize.WideAction, + enabled = isEnabled, + ) + + SecondaryButton( + text = config.rightText.resolveReference(), + onClick = config.onRightClick, + modifier = Modifier.weight(weight = 1f), + size = TangemButtonSize.WideAction, + enabled = isEnabled, + ) + } +} + @Composable internal fun CloseableIconButton( onClick: (() -> Unit)?, @@ -385,6 +413,17 @@ private class NotificationConfigProvider : CollectionPreviewParameterProvider Unit, ) : ButtonsState() + + data class SecondaryPairButtonsConfig( + val leftText: TextReference, + val onLeftClick: () -> Unit, + val rightText: TextReference, + val onRightClick: () -> Unit, + ) : ButtonsState() } } \ No newline at end of file diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt index b7102a6b52..1b4083ad77 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt @@ -1,16 +1,33 @@ package com.tangem.data.wallets +import com.tangem.datasource.api.common.response.ApiResponseError.HttpException +import com.tangem.datasource.api.common.response.getOrThrow +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.api.tangemTech.models.MarkUserWalletWasCreatedBody +import com.tangem.datasource.api.tangemTech.models.SeedPhraseNotificationDTO +import com.tangem.datasource.api.tangemTech.models.SeedPhraseNotificationDTO.Status +import com.tangem.datasource.local.datastore.RuntimeStateStore import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.get import com.tangem.datasource.local.preferences.utils.getSyncOrDefault import com.tangem.datasource.local.preferences.utils.store +import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.repository.WalletsRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.coroutines.runCatching import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch internal class DefaultWalletsRepository( private val appPreferencesStore: AppPreferencesStore, + private val tangemTechApi: TangemTechApi, + private val userWalletsStore: UserWalletsStore, + private val seedPhraseNotificationVisibilityStore: RuntimeStateStore, + private val dispatchers: CoroutineDispatcherProvider, ) : WalletsRepository { override suspend fun shouldSaveUserWalletsSync(): Boolean { @@ -38,4 +55,71 @@ internal class DefaultWalletsRepository( it[PreferencesKeys.ADDED_WALLETS_WITH_RING_KEY] = added + userWalletId.stringValue } } + + override fun seedPhraseNotificationStatus(userWalletId: UserWalletId): Flow { + return channelFlow { + launch { + seedPhraseNotificationVisibilityStore.get().collectLatest(::send) + } + + fetchSeedPhraseNotificationStatus(userWalletId) + } + } + + private suspend fun fetchSeedPhraseNotificationStatus(userWalletId: UserWalletId) { + val userWallet = userWalletsStore.getSyncOrNull(key = userWalletId) + + val status = if (userWallet?.isImported == false) { + false + } else { + runCatching(dispatchers.io) { + tangemTechApi.getSeedPhraseNotificationStatus(walletId = userWalletId.stringValue).getOrThrow() + } + .fold( + onSuccess = { it.status == Status.NOTIFIED }, + onFailure = { it is HttpException && it.code == HttpException.Code.NOT_FOUND }, + ) + } + + seedPhraseNotificationVisibilityStore.store(value = status) + } + + override suspend fun notifiedSeedPhraseNotification(userWalletId: UserWalletId) { + runCatching(dispatchers.io) { + tangemTechApi.updateSeedPhraseNotificationStatus( + walletId = userWalletId.stringValue, + body = SeedPhraseNotificationDTO(status = Status.NOTIFIED), + ).getOrThrow() + } + } + + override suspend fun confirmSeedPhraseNotification(userWalletId: UserWalletId) { + runCatching(dispatchers.io) { + tangemTechApi.updateSeedPhraseNotificationStatus( + walletId = userWalletId.stringValue, + body = SeedPhraseNotificationDTO(status = Status.CONFIRMED), + ).getOrThrow() + } + + seedPhraseNotificationVisibilityStore.store(value = false) + } + + override suspend fun declineSeedPhraseNotification(userWalletId: UserWalletId) { + runCatching(dispatchers.io) { + tangemTechApi.updateSeedPhraseNotificationStatus( + walletId = userWalletId.stringValue, + body = SeedPhraseNotificationDTO(status = Status.DECLINED), + ).getOrThrow() + } + + seedPhraseNotificationVisibilityStore.store(value = false) + } + + override suspend fun markWallet2WasCreated(userWalletId: UserWalletId) { + runCatching(dispatchers.io) { + tangemTechApi.markUserWallerWasCreated( + body = MarkUserWalletWasCreatedBody(userWalletId = userWalletId.stringValue), + ) + } + } } \ No newline at end of file diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt index c41bb99652..20cf86eaa0 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt @@ -2,9 +2,13 @@ package com.tangem.data.wallets.di import com.tangem.data.wallets.DefaultWalletNamesMigrationRepository import com.tangem.data.wallets.DefaultWalletsRepository +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.datastore.RuntimeStateStore import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository import com.tangem.domain.wallets.repository.WalletsRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,8 +21,19 @@ internal object WalletsDataModule { @Provides @Singleton - fun providesWalletsRepository(appPreferencesStore: AppPreferencesStore): WalletsRepository { - return DefaultWalletsRepository(appPreferencesStore = appPreferencesStore) + fun providesWalletsRepository( + appPreferencesStore: AppPreferencesStore, + tangemTechApi: TangemTechApi, + userWalletsStore: UserWalletsStore, + dispatchers: CoroutineDispatcherProvider, + ): WalletsRepository { + return DefaultWalletsRepository( + appPreferencesStore = appPreferencesStore, + tangemTechApi = tangemTechApi, + userWalletsStore = userWalletsStore, + seedPhraseNotificationVisibilityStore = RuntimeStateStore(defaultValue = false), + dispatchers = dispatchers, + ) } @Provides diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt index ce7a49ad8e..904ee2d1d8 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt @@ -30,7 +30,8 @@ class SendFeedbackEmailUseCase( address = getAddress(type.cardInfo), subject = emailSubjectResolver.resolve(type), message = createMessage(type), - file = feedbackRepository.getLogFile(), + // Temporally user data is not sent + file = null, // feedbackRepository.getLogFile(), ) feedbackRepository.sendEmail(email) diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt index ff8f854078..18be29acdd 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt @@ -14,4 +14,14 @@ interface WalletsRepository { suspend fun isWalletWithRing(userWalletId: UserWalletId): Boolean suspend fun setHasWalletsWithRing(userWalletId: UserWalletId) + + fun seedPhraseNotificationStatus(userWalletId: UserWalletId): Flow + + suspend fun notifiedSeedPhraseNotification(userWalletId: UserWalletId) + + suspend fun confirmSeedPhraseNotification(userWalletId: UserWalletId) + + suspend fun declineSeedPhraseNotification(userWalletId: UserWalletId) + + suspend fun markWallet2WasCreated(userWalletId: UserWalletId) } \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SeedPhraseNotificationUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SeedPhraseNotificationUseCase.kt new file mode 100644 index 0000000000..e5e1217c7c --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SeedPhraseNotificationUseCase.kt @@ -0,0 +1,26 @@ +package com.tangem.domain.wallets.usecase + +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.wallets.repository.WalletsRepository +import kotlinx.coroutines.flow.Flow + +class SeedPhraseNotificationUseCase( + private val walletsRepository: WalletsRepository, +) { + + operator fun invoke(userWalletId: UserWalletId): Flow { + return walletsRepository.seedPhraseNotificationStatus(userWalletId) + } + + suspend fun notified(userWalletId: UserWalletId) { + walletsRepository.notifiedSeedPhraseNotification(userWalletId) + } + + suspend fun confirm(userWalletId: UserWalletId) { + walletsRepository.confirmSeedPhraseNotification(userWalletId) + } + + suspend fun decline(userWalletId: UserWalletId) { + walletsRepository.declineSeedPhraseNotification(userWalletId) + } +} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt index a48aa93e38..58f5125717 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt @@ -640,6 +640,7 @@ internal class StakingViewModel @Inject constructor( amount = amount.orZero(), fee = fee.orZero(), reduceAmountBy = confirmationState?.reduceAmountBy.orZero(), + actionType = value.actionType, ) val currencyStatus = getCurrencyCheckUseCase( userWalletId = userWalletId, @@ -699,14 +700,21 @@ internal class StakingViewModel @Inject constructor( amount: BigDecimal, fee: BigDecimal, reduceAmountBy: BigDecimal, + actionType: StakingActionCommonType, ): BigDecimal? { - val subtractedBalanceAmount = checkAndCalculateSubtractedAmount( - isAmountSubtractAvailable = isAmountSubtractAvailable, - cryptoCurrencyStatus = cryptoCurrencyStatus, - amountValue = amount.orZero(), - feeValue = fee.orZero(), - reduceAmountBy = reduceAmountBy, - ) + // TODO split for different networks + val subtractedBalanceAmount = when (actionType) { + StakingActionCommonType.Enter -> checkAndCalculateSubtractedAmount( + isAmountSubtractAvailable = isAmountSubtractAvailable, + cryptoCurrencyStatus = cryptoCurrencyStatus, + amountValue = amount.orZero(), + feeValue = fee.orZero(), + reduceAmountBy = reduceAmountBy, + ) + StakingActionCommonType.Exit, + is StakingActionCommonType.Pending, + -> BigDecimal.ZERO + } val statusValue = cryptoCurrencyStatus.value as? CryptoCurrencyStatus.Loaded return statusValue?.let { it.amount - subtractedBalanceAmount - fee.orZero() } } diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 4b4d928ae1..fcae2b21f7 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -103,8 +103,9 @@ dependencies { implementation(projects.features.onboardingV2.api) /** Common modules */ - implementation(projects.common.ui) + implementation(projects.common) implementation(projects.common.routing) + implementation(projects.common.ui) /** Test libraries */ implementation(deps.test.junit) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt index 0ab4cced59..aae1a9292b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt @@ -126,5 +126,11 @@ sealed class WalletScreenAnalyticsEvent { data object EditWalletTapped : MainScreen(event = "Button - Edit Wallet Tapped") data object DeleteWalletTapped : MainScreen(event = "Button - Delete Wallet Tapped") + + data object NoticeSeedPhraseSupport : MainScreen(event = "Notice - Seed Phrase Support") + + data object NoticeSeedPhraseSupportButtonNo : MainScreen(event = "Button - Support No") + + data object NoticeSeedPhraseSupportButtonYes : MainScreen(event = "Button - Support Yes") } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt index 89d0467012..cbe0c57c5f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt @@ -62,6 +62,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor( source = AnalyticsParam.ScreensSources.Main, programName = TokenSwapPromoAnalyticsEvent.ProgramName.Ring, ) + is WalletNotification.Critical.SeedPhraseNotification -> MainScreen.NoticeSeedPhraseSupport } } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsSingleEventSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsSingleEventSender.kt new file mode 100644 index 0000000000..e00a7a429b --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsSingleEventSender.kt @@ -0,0 +1,34 @@ +package com.tangem.feature.wallet.presentation.wallet.analytics.utils + +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider +import dagger.hilt.android.scopes.ViewModelScoped +import javax.inject.Inject + +@ViewModelScoped +internal class WalletWarningsSingleEventSender @Inject constructor( + private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase, + private val screenLifecycleProvider: ScreenLifecycleProvider, +) { + + suspend fun send( + userWalletId: UserWalletId, + displayedUiState: WalletState?, + newWarnings: List, + ) { + if (screenLifecycleProvider.isBackgroundState.value) return + if (newWarnings.isEmpty()) return + if (displayedUiState == null || displayedUiState.pullToRefreshConfig.isRefreshing) return + + val events = newWarnings.filter { it !in displayedUiState.warnings } + + events.forEach { event -> + if (event is WalletNotification.Critical.SeedPhraseNotification) { + seedPhraseNotificationUseCase.notified(userWalletId = userWalletId) + } + } + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt index 20c180fe36..b852180742 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt @@ -14,6 +14,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase +import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import dagger.hilt.android.scopes.ViewModelScoped @@ -35,23 +36,33 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( private val promoRepository: PromoRepository, private val isNeedToBackupUseCase: IsNeedToBackupUseCase, private val backupValidator: BackupValidator, + private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase, ) { + @Suppress("MagicNumber", "MaximumLineLength") fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow> { val cardTypesResolver = userWallet.scanResponse.cardTypesResolver val promoFlow = flow { emit(promoRepository.getRingPromoBanner()) } return combine( - flow = tokenListStore.getOrThrow(userWallet.walletId), - flow2 = isReadyToShowRateAppUseCase(), - flow3 = isNeedToBackupUseCase(userWallet.walletId), - flow4 = shouldShowRingPromoUseCase(userWalletId = userWallet.walletId), - flow5 = promoFlow, - ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, shouldShowPromo, promoBanner -> + tokenListStore.getOrThrow(userWallet.walletId), + isReadyToShowRateAppUseCase(), + isNeedToBackupUseCase(userWallet.walletId), + shouldShowRingPromoUseCase(userWalletId = userWallet.walletId), + promoFlow, + seedPhraseNotificationUseCase(userWalletId = userWallet.walletId), + ) { + val maybeTokenList = it[0] as Lce + val isReadyToShowRating = it[1] as Boolean + val isNeedToBackup = it[2] as Boolean + val shouldShowPromo = it[3] as Boolean + val promoBanner = it[4] as? PromoBanner + val seedPhraseIssueStatus = it[5] as Boolean + buildList { addRingPromoNotification(shouldShowPromo, promoBanner, clickIntents) - addCriticalNotifications(userWallet, clickIntents) + addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents) addInformationalNotifications(cardTypesResolver, maybeTokenList, clickIntents) @@ -83,8 +94,22 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( private fun MutableList.addCriticalNotifications( userWallet: UserWallet, + seedPhraseIssueStatus: Boolean, clickIntents: WalletClickIntents, ) { + addIf( + element = WalletNotification.Critical.SeedPhraseNotification( + onDeclineClick = clickIntents::onSeedPhraseNotificationDecline, + onConfirmClick = clickIntents::onSeedPhraseNotificationConfirm, + ), + condition = with(userWallet) { + val isDemo = isDemoCardUseCase(cardId = userWallet.cardId) + val isWalletWithSeedPhrase = scanResponse.cardTypesResolver.isWallet2() && userWallet.isImported + + !isDemo && isWalletWithSeedPhrase && seedPhraseIssueStatus + }, + ) + val cardTypesResolver = userWallet.scanResponse.cardTypesResolver addIf( element = WalletNotification.Critical.BackupError { clickIntents.onSupportClick() }, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt index 6b1652a33a..46ff5ba54e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt @@ -6,6 +6,7 @@ import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender +import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker @@ -22,6 +23,7 @@ internal class MultiWalletContentLoader( private val clickIntents: WalletClickIntents, private val tokenListAnalyticsSender: TokenListAnalyticsSender, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, + private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender, private val walletWithFundsChecker: WalletWithFundsChecker, private val tokenListStore: MultiWalletTokenListStore, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, @@ -49,6 +51,7 @@ internal class MultiWalletContentLoader( clickIntents = clickIntents, getMultiWalletWarningsFactory = getMultiWalletWarningsFactory, walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, + walletWarningsSingleEventSender = walletWarningsSingleEventSender, ), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt index 7a70b865b2..7dcd6859a7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt @@ -6,6 +6,7 @@ import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender +import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker @@ -25,6 +26,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor( private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, + private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender, private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, ) { @@ -39,6 +41,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor( getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase, getMultiWalletWarningsFactory = getMultiWalletWarningsFactory, walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, + walletWarningsSingleEventSender = walletWarningsSingleEventSender, applyTokenListSortingUseCase = applyTokenListSortingUseCase, runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt index 897b44ae92..956ded275b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt @@ -5,6 +5,7 @@ import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender +import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker @@ -21,6 +22,7 @@ internal class SingleWalletWithTokenContentLoader( private val stateHolder: WalletStateController, private val tokenListAnalyticsSender: TokenListAnalyticsSender, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, + private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender, private val walletWithFundsChecker: WalletWithFundsChecker, private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory, private val tokenListStore: MultiWalletTokenListStore, @@ -46,6 +48,7 @@ internal class SingleWalletWithTokenContentLoader( clickIntents = clickIntents, getMultiWalletWarningsFactory = getMultiWalletWarningsFactory, walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, + walletWarningsSingleEventSender = walletWarningsSingleEventSender, ), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt index 1c433dc756..3352d671cf 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt @@ -5,6 +5,7 @@ import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender +import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker @@ -22,6 +23,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor( private val tokenListStore: MultiWalletTokenListStore, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, + private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender, private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase, ) { @@ -36,6 +38,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor( tokenListStore = tokenListStore, getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase, walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, + walletWarningsSingleEventSender = walletWarningsSingleEventSender, runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertState.kt index 6103f851d0..63855cabc5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertState.kt @@ -28,6 +28,8 @@ internal sealed interface WalletAlertState { abstract val errorTextProvider: (String) -> TextReference? } + data class SimpleOkAlert(val message: TextReference, val onOkClick: () -> Unit) : WalletAlertState + data class DefaultAlert( override val title: TextReference?, override val message: TextReference, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt index feb796546d..2d80995272 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt @@ -2,7 +2,10 @@ package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.compose.runtime.Immutable import com.tangem.core.ui.components.notifications.NotificationConfig -import com.tangem.core.ui.extensions.* +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.pluralReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList import com.tangem.feature.wallet.impl.R import org.joda.time.DateTime @@ -47,6 +50,17 @@ sealed class WalletNotification(val config: NotificationConfig) { onClick = onSupportClick, ), ) + + data class SeedPhraseNotification(val onDeclineClick: () -> Unit, val onConfirmClick: () -> Unit) : Critical( + title = resourceReference(R.string.warning_seedphrase_issue_title), + subtitle = resourceReference(R.string.warning_seedphrase_issue_message), + buttonsState = NotificationConfig.ButtonsState.SecondaryPairButtonsConfig( + leftText = resourceReference(R.string.common_no), + onLeftClick = onDeclineClick, + rightText = resourceReference(R.string.common_yes), + onRightClick = onConfirmClick, + ), + ) } sealed class Warning( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/AddWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/AddWalletTransformer.kt index 86e534351d..5fb52aca73 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/AddWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/AddWalletTransformer.kt @@ -13,6 +13,7 @@ internal class AddWalletTransformer( private val clickIntents: WalletClickIntents, private val walletImageResolver: WalletImageResolver, private val walletFeatureToggles: WalletFeatureToggles, + private val selectedWalletIndex: Int, ) : WalletScreenStateTransformer { private val walletLoadingStateFactory by lazy { @@ -26,6 +27,7 @@ internal class AddWalletTransformer( override fun transform(prevState: WalletScreenState): WalletScreenState { return prevState.copy( wallets = (prevState.wallets + walletLoadingStateFactory.create(userWallet)).toImmutableList(), + selectedWalletIndex = selectedWalletIndex, ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt index cf18d05d6d..f4a628c17a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender +import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification @@ -20,6 +21,7 @@ internal class MultiWalletWarningsSubscriber( private val clickIntents: WalletClickIntents, private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, + private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender, ) : WalletSubscriber() { override fun create(coroutineScope: CoroutineScope): Flow> { @@ -31,6 +33,11 @@ internal class MultiWalletWarningsSubscriber( stateHolder.update(SetWarningsTransformer(userWallet.walletId, warnings)) walletWarningsAnalyticsSender.send(displayedState, warnings) + walletWarningsSingleEventSender.send( + userWalletId = userWallet.walletId, + displayedUiState = displayedState, + newWarnings = warnings, + ) } } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt index a3b1644d47..de71152155 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt @@ -16,6 +16,19 @@ internal fun WalletAlert(state: WalletAlertState, onDismiss: () -> Unit) { when (state) { is WalletAlertState.Basic -> BasicAlert(state, onDismiss) is WalletAlertState.TextInput -> TextInputAlert(state, onDismiss) + is WalletAlertState.SimpleOkAlert -> { + BasicDialog( + message = state.message.resolveReference(), + confirmButton = DialogButtonUM( + onClick = { + state.onOkClick() + onDismiss() + }, + ), + onDismissDialog = onDismiss, + isDismissable = false, + ) + } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index b1dcff9ddd..1d56ea3290 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -383,6 +383,7 @@ internal class WalletViewModel @Inject constructor( clickIntents = clickIntents, walletImageResolver = walletImageResolver, walletFeatureToggles = walletFeatureToggles, + selectedWalletIndex = action.selectedWalletIndex, ), ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt index 25aa286555..ee807b127a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt @@ -1,8 +1,10 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents import arrow.core.getOrElse +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.card.SetCardWasScannedUseCase @@ -23,6 +25,7 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockTy import com.tangem.domain.wallets.models.UnlockWalletsError import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.usecase.GetUserWalletUseCase +import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic @@ -68,6 +71,10 @@ internal interface WalletWarningsClickIntents { fun onSupportClick() fun onNoteMigrationButtonClick(url: String) + + fun onSeedPhraseNotificationConfirm() + + fun onSeedPhraseNotificationDecline() } @Suppress("LongParameterList") @@ -90,6 +97,8 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( private val shouldShowRingPromoUseCase: ShouldShowRingPromoUseCase, private val getCardInfoUseCase: GetCardInfoUseCase, private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, + private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase, + private val urlOpener: UrlOpener, ) : BaseWalletClickIntents(), WalletWarningsClickIntents { override fun onAddBackupCardClick() { @@ -279,6 +288,48 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( } } + override fun onSeedPhraseNotificationConfirm() { + val userWallet = getSelectedUserWallet() ?: return + + analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonYes) + + walletEventSender.send( + event = WalletEvent.ShowAlert( + state = WalletAlertState.SimpleOkAlert( + message = resourceReference(R.string.warning_seedphrase_issue_answer_yes), + onOkClick = { + viewModelScope.launch { + seedPhraseNotificationUseCase.confirm(userWalletId = userWallet.walletId) + + urlOpener.openUrl( + url = TangemBlogUrlBuilder.build(post = TangemBlogUrlBuilder.Post.SeedNotify), + ) + } + }, + ), + ), + ) + } + + override fun onSeedPhraseNotificationDecline() { + val userWallet = getSelectedUserWallet() ?: return + + analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonNo) + + walletEventSender.send( + event = WalletEvent.ShowAlert( + state = WalletAlertState.SimpleOkAlert( + message = resourceReference(R.string.warning_seedphrase_issue_answer_no), + onOkClick = { + viewModelScope.launch { + seedPhraseNotificationUseCase.decline(userWalletId = userWallet.walletId) + } + }, + ), + ), + ) + } + private fun getSelectedUserWallet(): UserWallet? { val userWalletId = stateHolder.getSelectedWalletId() return getUserWalletUseCase(userWalletId).getOrElse { diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 85e036d0a3..911961ff91 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -90,7 +90,7 @@ markdownComposeView = "0.5.4" # region Tangem tangemBlockchainSdk = "develop-896" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-416" +tangemCardSdk = "develop-420" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem17" #tangemVico = "0.0.1" # Keep it! - used for local builds ^