From ea2598e739698606bc24407807b365edc85ef8a2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 1 Jun 2023 16:43:45 +0300 Subject: [PATCH 01/44] Updated on 2026-08-14 --- core/res/src/main/res/values-de/strings.xml | 3 +- core/res/src/main/res/values-fr/strings.xml | 3 +- core/res/src/main/res/values-it/strings.xml | 3 +- core/res/src/main/res/values-ru/strings.xml | 27 ++++++++++++++-- .../src/main/res/values-zh-rTW/strings.xml | 22 +++++++++++-- core/res/src/main/res/values/strings.xml | 32 ++++++++++++++++++- 6 files changed, 79 insertions(+), 11 deletions(-) diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 3e71b3a98a..0c4d6e28e1 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -12,6 +12,7 @@ Erledigt OK Änderungen speichern + Absenden Erfolg Zugangscode Sie müssen den richtigen Zugangscode eingeben, bevor Sie die Karte scannen. @@ -48,7 +49,6 @@ Priorität Höchstbetrag Netzgebühr - Absenden Gesamt %1$s und %2$s werden gesendet ≈ %1$s (inkl. Gebühr: %2$s) @@ -61,7 +61,6 @@ Die Bilanz wird aufgeladen… Die Transaktion läuft… Verifizierte Bilanz - Absenden WalletConnect Das Konto ist nicht erstellt Diese Karte wird nicht unterstützt diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 01059b36ff..f809f8c50d 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -12,6 +12,7 @@ Exécuté OK Sauvegarder les modifications + Envoyer Avec succès Code d\'accès Vous devrez entrer le mot de passe correct avant de scanner la carte @@ -48,7 +49,6 @@ Priorité Somme maximale Commissions du réseau - Envoyer Total Sera envoyé %1$s et %2$s ≈ %1$s (incl. les commissions : %2$s) @@ -61,7 +61,6 @@ Solde est en cours de téléchargement… Transaction en cours… Solde confirmé - Envoyer WalletConnect Compte n\'est pas créé Cette carte n\'est pas prise en charge diff --git a/core/res/src/main/res/values-it/strings.xml b/core/res/src/main/res/values-it/strings.xml index bc24145f51..83b4019140 100644 --- a/core/res/src/main/res/values-it/strings.xml +++ b/core/res/src/main/res/values-it/strings.xml @@ -12,6 +12,7 @@ Fatto OK Mantieni le modifiche + Invia Con successo Codice di accesso Prima di scansionare la carta sarà necessario inserire il codice di accesso corretto @@ -48,7 +49,6 @@ Prioritario Importo totale Costi della rete - Invia Totale Sarà inviato %1$s e %2$s ≈ %1$s (inc. commissione: %2$s) @@ -61,7 +61,6 @@ Il saldo sta per essere caricato… Transazione in corso… Saldo verificato - Invia WalletConnect Conto non creato Questa carta non è supportata diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 57b1b1dcae..f2b6457174 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -15,7 +15,7 @@ Эта карта не является платежным средством. В настоящее время мы не можем сопоставить количество подписей на карте с информацией в блокчейне. Это нормально, но в редких случаях может означать, что предыдущий владелец удерживает подписанную транзакцию от публикации, что является поводом для беспокойства.\nНе принимайте эту карту в качестве физического платежа от кого-то, кому вы не доверяете.\nВо всех остальных отношениях эта карта совершенно безопасна.\nTangem — единственный аппаратный кошелек, предлагающий защиту методом подсчета подписей. У вас возникли трудности со сканированием карты? Эта карта не предназначена для работы с этим приложением - Перейдите в настройки, чтобы включить биометрическую аутентификацию в приложении Tangem App + Перейдите в настройки, чтобы включить биометрическую аутентификацию в приложении Tangem Включите биометрическую аутентификацию Все сохраненные коды доступа будут удалены. Вам потребуется вводить код доступа при работе с кошельком. При отключении функции сохранения кошелька все ранее сохраненные кошельки будут удалены из приложения. @@ -52,27 +52,37 @@ Баланс: %s биометрическую аутентификацию биометрией + Купить Вы не предоставили доступ к камере, пожалуйста, измените настройки конфиденциальности. Отмена Закрыть Копировать + Скопировать адрес Создать Удалить Отключено + Не нравится Готово Включить Включено - Обозреватель + Обменять + Нравится Нет + Нет данных Ок Основная карта + Получить Отклонить Сохранить изменения Искать + Секретная фраза + Продать + Отправить Сервер недоступен, повторите попытку позднее Поделиться Подписать Подписать и отправить + Стейкинг Начать Отправить Успешно @@ -81,6 +91,7 @@ Транзакции Перевод Я понял + Недоступно Да Адрес контракта скопирован! Доступные сети @@ -462,13 +473,21 @@ Dapp %1$s, запрос на\nподпись транзакции с BNB.\n\n%2$s Торговый ордер на %1$s\nЦена: %2$s\nСумма к получению: %3$s\nСумма к оплате: %4$s Детали транзакции:\nОт: %1$s\nК: %2$s\nСумма: %3$s + Транзакция с BNB успешно подписана и отправлена ​​в Dapp. Буфер обмена содержит код WalletConnect. Использовать скопированное значение или отсканировать QR-код Запрос на создание транзакции для %1$s\n%2$s\n\nСумма: %3$s\nКомиссия: %4$s\nВсего: %5$s\nБаланс: %6$s Невозможно отправить транзакцию. Недостаточно средств. Не удалось установить сессию WalletConnect. Пожалуйста, повторите попытку позже. + Не все токены добавлены в ваш список. Пожалуйста, добавьте их в начале, а потом попробуйте снова. Недостающие токены: \n + Не удалось подписать сообщение.\nПожалуйста, попробуйте еще раз Не удалось установить сессию WalletConnect за отведённое время. Пожалуйста, повторите попытку позже. Запрос на подключение через WalletConnect содержит неподдерживаемые блокчеины. Неподдерживаемые блокчеины:\n Cоединение с этим Dapp сервисом не может быть установлено из-за его технической реализации. + Произошла непредвиденная ошибка. Сообщение ошибки: %s Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки. + Неверная карта выбрана в приложении Tangem + Не удалось создать транзакцию из данных Dapp. Код: %s + Произошла непредвиденная ошибка. Код ошибки: %d Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки. + Сообщение было успешно подписано и отправлено в Dapp Сеть %s не найдена. Пожалуйста, добавьте её и попробуйте заново. Вставить из буфера обмена Запрос на открытие сессии для\n%1$s\n\nСЕТЬ: %2$s\n\nURL: %3$s @@ -477,8 +496,12 @@ Эту карту нельзя использовать с WalletConnect. Сеть не поддерживается. Пожалуйста, выберите другую сеть. Выберите сеть + Dapp не предоставил необходимые данные для открытия сессии WalletConnect Подключение к Dapps WalletConnect + Транзакция успешно подписана и отправлена ​​в Dapp + Транзакция успешно подписана и отправлена ​​в блокчейн + Не удалось найти хэш транзакции Сеть %s Аккаунт не создан Эта карта не поддерживается diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index 597be9911d..8b25c3a89d 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -46,23 +46,29 @@ 餘額: %s 生物識別 生物 + 購買 您尚未授予相機訪問權限,請更改您的隱私設置 刪除 關閉 複製 + 複製地址 創造 刪除 禁用 + 不喜歡 完成 允許 啟用 交易 + 喜歡 OK 主卡片 拒絕 保存設置 搜索 + 銷售 + 發送 伺服器不可用,請稍後在試 分享 簽署 @@ -74,6 +80,7 @@ 條款和條件 交易 我了解 + 無法觸達 已複製代幣地址 支持的網路 @@ -317,7 +324,6 @@ 優先 最大值 網路費 - 發送 發送 %s 總計 %1$s 和 %2$s 將被發送 @@ -432,13 +438,21 @@ Dapp %1$s,請求\n簽署 BNB 交易。\n%2$s %1$s 的交易訂單\n價格: %2$s\n接收金額:%3$s\n支付數量: %4$s 交易明細:\n從: %1$s\n到: %2$s\n數量: %3$s + BNB 交易已成功簽署並發送至 Dapp 剪貼板包含 WalletConnect 代碼。使用複制的值或掃描二維碼 請求為 %1$s 創建交易\n%2$s\n\n數量: %3$s\n費用: %4$s\n全部的: %5$s\n餘額: %6$s 無法交易,無足夠資金 未能建立 WalletConnect 連接。請稍後再試 + 並非所有代幣都已添加到您的列表中。請先添加它們,然後重試。缺少標記:\n + 無法簽署消息。請重試 無法建立 WalletConnect 連接:超時錯誤。請稍後再試 - 會話請求包含不支持 WalletConnect 連接的區塊鏈。不支持的區塊鏈: + 會話請求包含不支持 WalletConnect 連接的區塊鏈。不支持的區塊鏈:\n 由於技術問題,無法與此 Dapp 建立連接 + 我們遇到了未知錯誤。錯誤信息:%s。如果問題仍然存在-請隨時聯繫我們的客服人員 + 在 Tangem App 中選擇了錯誤的卡 + 無法從 Dapp 數據創建交易。代碼: %s + 我們遇到了未知錯誤。錯誤代碼:%d。如果問題仍然存在-請隨時聯繫我們的支持人員 + 消息已成功簽名並發送至Dapp 沒有 %s 網路,請先加入後再試一次 從剪貼板貼上 請求開始會話\n%1$s\n\n網絡: %2$s\n\n網址:%3$s @@ -447,8 +461,12 @@ 此卡不能用於建立 WalletConnect 連接 不支持此網絡。請選擇其他網絡 選擇網路 + Dapp 沒有提供必要的數據來建立 WalletConnect 連接 連結到Dapps WalletConnect + 交易已成功簽署並發送至 Dapp + 交易已成功簽署並發送至區塊鏈 + 未能找到交易哈希 %s 網路 帳號尚未被創造 不支持此卡 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index e68fd193bb..aa3dd033f5 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -50,27 +50,37 @@ Balance: %s biometric authentication biometrics + Buy You have not given access to your camera, please adjust your privacy settings Cancel Close Copy + Copy address Create Delete Disabled + Dislike Done Enable Enabled - Explorer + Exchange + Like No + No data OK Primary Card + Receive Reject Save changes Search + Seed phrase + Sell + Send The server is not available, please try again later Share Sign Sign and send + Stake Start Submit Success @@ -79,6 +89,7 @@ Transactions Transfer I understand + Unreachable Yes Contract address copied! Available networks @@ -143,6 +154,8 @@ Tap the card Internal error: wallet manager not found You have updated biometrics, scan your card to enter + You have completed the training and can get your 1inch tokens + Get a bonus Manage tokens To protect your assets, we advise you to carry out this procedure Your wallet has not been backed up @@ -341,6 +354,8 @@ I have a promo code… Tangem Wallet Other payment methods + Due to the high volume of orders we are receiving, Shipping and Local Delivery orders may be delayed. + Pre-order now Due to the high volume of orders we are receiving shipping may be delayed up to 5 weeks depending on your location Total Solana network charges a rent of %1$s every 2 days. Accounts that can\'t afford the rent are purged from the network. Deposit your account with more than %2$s to use it for free. @@ -354,6 +369,9 @@ Thousands of Currencies Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. The Wallet for Everyone + Complete the training, get the opportunity to buy Tangem wallet with a discount and receive 1inch tokens on your wallet + Learn + Learn and get a bonus Borrow Buy Exchange @@ -455,13 +473,21 @@ Dapp %1$s, requesting to\nsign BNB transaction.\n\n%2$s Trade order for %1$s\nPrice: %2$s\nAmount to receive: %3$s\nAmount to pay: %4$s Transaction details:\nFrom: %1$s\nTo: %2$s\nAmount: %3$s + The BNB transaction has been successfully signed and sent to the Dapp Clipboard contain WalletConnect code. Use copied value or scan QR-code Request to create transaction for %1$s\n%2$s\n\nAmount: %3$s\nFee: %4$s\nTotal: %5$s\nBalance: %6$s Can\'t send transaction. Not enough funds. Failed to establish WalletConnect session. Please, try again later. + Not all tokens were added to your list. Please add them first and try again. Missing tokens:\n + Failed to sign message.\nPlease, try again Failed to establish WalletConnect session: timeout error. Please, try again later. Session request contains unsupported blockchains for WalletConnect connection. Unsupported blockchains:\n Connection with this Dapp cannot be established due to its technical implementation. + We\'ve encountered unknown error. Error message: %s. If the problem persists — feel free to contact our support + Wrong card selected in Tangem App + Failed to create transaction from Dapp data. Code: %s + We\'ve encountered unknown error. Error code: %d. If the problem persists — feel free to contact our support + The message has been successfully signed and sent to the Dapp %s network not found. Please, add it first and try again. Paste from clipboard Request to start a session for\n%1$s\n\nNETWORK: %2$s\n\nURL: %3$s @@ -470,8 +496,12 @@ This card can\'t be used to establish WalletConnect session This network is not supported. Please select another network. Select network + Dapp didn\'t provide essential data to establish WalletConnect session Connect to Dapps WalletConnect + The transaction has been successfully signed and sent to the Dapp + The transaction has been succesfully signed and sent to the blockchain + Failed to find transaction hash %s network Account is not created This card is not supported From 201f1e514a0c8d7f43d7512800f9f8d6660aca54 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 1 Jun 2023 23:34:24 +0300 Subject: [PATCH 02/44] Updated on 2026-08-14 --- app/build.gradle.kts | 2 + features/learn2earn/api/.gitignore | 1 + features/learn2earn/api/build.gradle.kts | 5 ++ .../api/src/main/AndroidManifest.xml | 2 + features/learn2earn/impl/.gitignore | 1 + features/learn2earn/impl/build.gradle.kts | 50 +++++++++++++++++++ .../impl/src/main/AndroidManifest.xml | 3 ++ settings.gradle.kts | 3 ++ 8 files changed, 67 insertions(+) create mode 100644 features/learn2earn/api/.gitignore create mode 100644 features/learn2earn/api/build.gradle.kts create mode 100644 features/learn2earn/api/src/main/AndroidManifest.xml create mode 100644 features/learn2earn/impl/.gitignore create mode 100644 features/learn2earn/impl/build.gradle.kts create mode 100644 features/learn2earn/impl/src/main/AndroidManifest.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1634169316..efe2848d8c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -39,6 +39,8 @@ dependencies { /** Features */ implementation(project(":features:onboarding")) + implementation(project(":features:learn2earn:api")) + implementation(project(":features:learn2earn:impl")) implementation(project(":features:referral:presentation")) implementation(project(":features:referral:domain")) implementation(project(":features:referral:data")) diff --git a/features/learn2earn/api/.gitignore b/features/learn2earn/api/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/learn2earn/api/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/learn2earn/api/build.gradle.kts b/features/learn2earn/api/build.gradle.kts new file mode 100644 index 0000000000..1fbece85d5 --- /dev/null +++ b/features/learn2earn/api/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + id("configuration") +} \ No newline at end of file diff --git a/features/learn2earn/api/src/main/AndroidManifest.xml b/features/learn2earn/api/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..4d3df466b3 --- /dev/null +++ b/features/learn2earn/api/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/features/learn2earn/impl/.gitignore b/features/learn2earn/impl/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/learn2earn/impl/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/learn2earn/impl/build.gradle.kts b/features/learn2earn/impl/build.gradle.kts new file mode 100644 index 0000000000..f0071798aa --- /dev/null +++ b/features/learn2earn/impl/build.gradle.kts @@ -0,0 +1,50 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + alias(deps.plugins.kotlin.serialization) + alias(deps.plugins.hilt.android) + id("configuration") +} + +dependencies { + /** AndroidX */ + implementation(deps.androidx.activity.compose) + implementation(deps.androidx.core.ktx) + implementation(deps.androidx.appCompat) + implementation(deps.androidx.constraintLayout) + + /** Compose */ + implementation(deps.compose.material) + implementation(deps.compose.foundation) + implementation(deps.compose.constraintLayout) + implementation(deps.compose.ui) + implementation(deps.compose.ui.tooling) + implementation(deps.compose.animation) + implementation(deps.compose.navigation) + implementation(deps.compose.navigation.hilt) + + /** Core modules */ + implementation(project(":common")) + implementation(project(":core:featuretoggles")) + implementation(project(":core:datasource")) + implementation(project(":core:analytics")) + implementation(project(":core:utils")) + implementation(project(":core:ui")) + implementation(project(":core:res")) + + /** Tangem libraries */ + // implementation(deps.tangem.card.core) + // implementation(deps.tangem.card.android) { + // exclude(module = "joda-time") + // } + + /** Other libraries */ + implementation(deps.kotlin.immutable.collections) + implementation(deps.kotlin.serialization) + implementation(deps.timber) + + /** DI */ + implementation(deps.hilt.android) + kapt(deps.hilt.kapt) +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/AndroidManifest.xml b/features/learn2earn/impl/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..472f44a1a5 --- /dev/null +++ b/features/learn2earn/impl/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 3928077a46..15b3f6a078 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -60,6 +60,9 @@ include(":features:tester:impl") include(":features:wallet:api") include(":features:wallet:impl") + +include(":features:learn2earn:api") +include(":features:learn2earn:impl") // endregion Feature modules // region Domain modules From 0196e566196c10528af11a53ce5761a8227086c8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 1 Jun 2023 23:34:55 +0300 Subject: [PATCH 03/44] Updated on 2026-08-14 --- .../res/drawable/img_1inch_logo_401_378.xml | 76 +++++++++++++++++ .../res/drawable/img_1inch_logo_42_40.xml | 83 +++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 core/ui/src/main/res/drawable/img_1inch_logo_401_378.xml create mode 100644 core/ui/src/main/res/drawable/img_1inch_logo_42_40.xml diff --git a/core/ui/src/main/res/drawable/img_1inch_logo_401_378.xml b/core/ui/src/main/res/drawable/img_1inch_logo_401_378.xml new file mode 100644 index 0000000000..5c2c3c1156 --- /dev/null +++ b/core/ui/src/main/res/drawable/img_1inch_logo_401_378.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/ui/src/main/res/drawable/img_1inch_logo_42_40.xml b/core/ui/src/main/res/drawable/img_1inch_logo_42_40.xml new file mode 100644 index 0000000000..c704358828 --- /dev/null +++ b/core/ui/src/main/res/drawable/img_1inch_logo_42_40.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 09907d00f67fe7fea5f63799d0d081c97959b029 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 1 Jun 2023 23:35:17 +0300 Subject: [PATCH 04/44] Updated on 2026-08-14 --- .../features/home/compose/StoriesScreen.kt | 107 ++++++++---------- .../home/compose/views/StoriesProgressBar.kt | 9 +- .../tap/features/home/redux/HomeState.kt | 30 +++++ .../presentation/ui/OneInchStoriesScreen.kt | 105 +++++++++++++++++ .../presentation/ui/component/Gradient.kt | 38 +++++++ 5 files changed, 220 insertions(+), 69 deletions(-) create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/OneInchStoriesScreen.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/component/Gradient.kt diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt index ef40d850f6..2d453259c4 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt @@ -5,29 +5,11 @@ package com.tangem.tap.features.home.compose import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBars -import androidx.compose.foundation.layout.union -import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.layout.* import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha @@ -43,20 +25,16 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.google.accompanist.systemuicontroller.rememberSystemUiController -import com.tangem.tap.features.home.compose.content.FirstStoriesContent -import com.tangem.tap.features.home.compose.content.StoriesCurrencies -import com.tangem.tap.features.home.compose.content.StoriesRevolutionaryWallet -import com.tangem.tap.features.home.compose.content.StoriesUltraSecureBackup -import com.tangem.tap.features.home.compose.content.StoriesWalletForEveryone -import com.tangem.tap.features.home.compose.content.StoriesWeb3 +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.learn2earn.presentation.ui.OneInchStoriesContent +import com.tangem.tap.features.home.compose.content.* import com.tangem.tap.features.home.compose.views.HomeButtons import com.tangem.tap.features.home.compose.views.StoriesProgressBar import com.tangem.tap.features.home.redux.HomeState +import com.tangem.tap.features.home.redux.Stories import com.tangem.wallet.R import kotlin.math.max -private const val STEPS = 6 - @Suppress("LongMethod", "ComplexMethod") @Composable fun StoriesScreen( @@ -65,16 +43,18 @@ fun StoriesScreen( onShopButtonClick: () -> Unit, onSearchTokensClick: () -> Unit, ) { - val currentStep = remember { mutableStateOf(1) } val systemUiController = rememberSystemUiController() + val state = homeState.value - val isDarkBackground = currentStep.value !in 3..5 + var currentStory by remember { mutableStateOf(state.firstStory) } + val currentStep = { state.stepOf(currentStory) } val goToPreviousScreen = { - currentStep.value = max(1, currentStep.value - 1) + currentStory = state.stories[max(0, currentStep() - 1)] } val goToNextScreen = { - currentStep.value = if (currentStep.value < STEPS) currentStep.value + 1 else 1 + currentStory = + if (currentStep() < state.stories.lastIndex) state.stories[currentStep() + 1] else state.firstStory } val isPressed = remember { mutableStateOf(false) } @@ -82,10 +62,10 @@ fun StoriesScreen( val hideContent = remember { mutableStateOf(true) } - LaunchedEffect(key1 = isDarkBackground) { + LaunchedEffect(key1 = currentStory.isDarkBackground) { systemUiController.setSystemBarsColor( color = Color.Transparent, - darkIcons = !isDarkBackground, + darkIcons = !currentStory.isDarkBackground, ) } @@ -134,7 +114,7 @@ fun StoriesScreen( }, ) } - if (!isDarkBackground) { + if (!currentStory.isDarkBackground) { Image( modifier = Modifier.fillMaxSize(), painter = painterResource(id = R.drawable.ic_overlay), @@ -154,9 +134,9 @@ fun StoriesScreen( verticalArrangement = Arrangement.Center, ) { StoriesProgressBar( - steps = STEPS, - currentStep = currentStep.value, - stepDuration = currentStep.duration(), + steps = state.stories.lastIndex, + currentStep = currentStep(), + stepDuration = currentStory.duration, paused = isPaused, onStepFinish = goToNextScreen, ) @@ -169,15 +149,21 @@ fun StoriesScreen( .height(17.dp) .alpha(if (hideContent.value) 0f else 1f) .align(Alignment.Start), - colorFilter = if (isDarkBackground) null else ColorFilter.tint(Color.Black), + colorFilter = if (currentStory.isDarkBackground) null else ColorFilter.tint(Color.Black), ) - when (currentStep.value) { - 1 -> FirstStoriesContent(isPaused, currentStep.duration()) { hideContent.value = it } - 2 -> StoriesRevolutionaryWallet(currentStep.duration()) - 3 -> StoriesUltraSecureBackup(isPaused, currentStep.duration()) - 4 -> StoriesCurrencies(isPaused, currentStep.duration()) - 5 -> StoriesWeb3(isPaused, currentStep.duration()) - 6 -> StoriesWalletForEveryone(currentStep.duration()) + when (currentStory) { + Stories.OneInchPromo -> { + // TODO: fixme + TangemTheme { + OneInchStoriesContent({}) + } + } + Stories.TangemIntro -> FirstStoriesContent(isPaused, currentStory.duration) { hideContent.value = it } + Stories.RevolutionaryWallet -> StoriesRevolutionaryWallet(currentStory.duration) + Stories.UltraSecureBackup -> StoriesUltraSecureBackup(isPaused, currentStory.duration) + Stories.Currencies -> StoriesCurrencies(isPaused, currentStory.duration) + Stories.Web3 -> StoriesWeb3(isPaused, currentStory.duration) + Stories.WalletForEveryone -> StoriesWalletForEveryone(currentStory.duration) } } Column( @@ -186,7 +172,7 @@ fun StoriesScreen( .align(Alignment.BottomCenter) .fillMaxWidth(), ) { - if (currentStep.value == 4) { + if (currentStory == Stories.Currencies) { Button( onClick = onSearchTokensClick, modifier = Modifier @@ -210,25 +196,22 @@ fun StoriesScreen( ) } } - HomeButtons( - modifier = Modifier - .padding(start = 16.dp, top = 0.dp, end = 16.dp, bottom = 37.dp) - .fillMaxWidth(), - isDarkBackground = isDarkBackground, - btnScanStateInProgress = homeState.value.btnScanStateInProgress, - onScanButtonClick = onScanButtonClick, - onShopButtonClick = onShopButtonClick, - ) + + if (currentStory != Stories.OneInchPromo) { + HomeButtons( + modifier = Modifier + .padding(start = 16.dp, top = 0.dp, end = 16.dp, bottom = 37.dp) + .fillMaxWidth(), + isDarkBackground = currentStory.isDarkBackground, + btnScanStateInProgress = homeState.value.btnScanStateInProgress, + onScanButtonClick = onScanButtonClick, + onShopButtonClick = onShopButtonClick, + ) + } } } } -@Suppress("MagicNumber") -private fun MutableState.duration(): Int = when (this.value) { - 1 -> 8000 - else -> 6000 -} - @Preview @Composable private fun StoriesScreenPreview() { diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesProgressBar.kt b/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesProgressBar.kt index f92adcf9d1..2774f0f683 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesProgressBar.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesProgressBar.kt @@ -4,12 +4,7 @@ import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember @@ -52,7 +47,7 @@ fun StoriesProgressBar( // .height() .padding(start = 9.dp, end = 9.dp, top = 16.dp), ) { - for (index in 1..steps) { + for (index in 0..steps) { Row( modifier = Modifier .height(2.dp) diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt index 618e432dec..9342420e6b 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt @@ -8,8 +8,38 @@ import org.rekotlin.StateType data class HomeState( val scanInProgress: Boolean = false, val btnScanState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.ENABLED), + val stories: List = initDefaultStories(), ) : StateType { + val firstStory: Stories = stories[0] + val btnScanStateInProgress: Boolean get() = btnScanState.progressState == ProgressState.Loading + + fun stepOf(story: Stories): Int = stories.indexOf(story) + + companion object { + fun initDefaultStories(): List = listOf( + Stories.OneInchPromo, + Stories.TangemIntro, + Stories.RevolutionaryWallet, + Stories.UltraSecureBackup, + Stories.Currencies, + Stories.Web3, + Stories.WalletForEveryone, + ) + } +} + +sealed class Stories( + val isDarkBackground: Boolean, + val duration: Int, +) { + object OneInchPromo : Stories(true, duration = 8000) + object TangemIntro : Stories(true, duration = 8000) + object RevolutionaryWallet : Stories(true, duration = 6000) + object UltraSecureBackup : Stories(false, duration = 6000) + object Currencies : Stories(false, duration = 6000) + object Web3 : Stories(false, duration = 6000) + object WalletForEveryone : Stories(true, duration = 6000) } \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/OneInchStoriesScreen.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/OneInchStoriesScreen.kt new file mode 100644 index 0000000000..4d7c7ee283 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/OneInchStoriesScreen.kt @@ -0,0 +1,105 @@ +package com.tangem.feature.learn2earn.presentation.ui + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.SecondaryButton +import com.tangem.core.ui.components.SpacerH16 +import com.tangem.core.ui.components.SpacerH32 +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemTypography +import com.tangem.feature.learn2earn.impl.R +import com.tangem.feature.learn2earn.presentation.ui.component.GradientCircle + +/** +[REDACTED_AUTHOR] + */ +@Suppress("ReusedModifierInstance") +@Composable +fun OneInchStoriesContent(onLearnClick: () -> Unit, modifier: Modifier = Modifier) { + Box(modifier = modifier.fillMaxSize()) { + ContentBackground( + modifier = modifier + .fillMaxSize() + .align(Alignment.Center), + ) + StoryDescription( + headerText = stringResource(id = R.string.story_learn_title), + bodyText = stringResource(id = R.string.story_learn_description), + ) + Image( + modifier = Modifier + .padding(bottom = TangemTheme.dimens.size68) + .align(Alignment.BottomCenter), + painter = painterResource(id = R.drawable.img_1inch_logo_401_378), + contentDescription = null, + ) + SecondaryButton( + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.size16) + .padding(bottom = TangemTheme.dimens.size84) + .fillMaxWidth() + .align(Alignment.BottomCenter), + text = stringResource(id = R.string.story_learn_learn), + onClick = onLearnClick, + ) + } +} + +@Suppress("MagicNumber") +@Composable +private fun ContentBackground(modifier: Modifier = Modifier) { + Box(modifier = modifier) { + GradientCircle( + size = 800.dp, + offsetX = (-220).dp, + offsetY = (-200).dp, + startColor = Color(0xCE164594), + endColor = Color(0x000B173D), + ) + GradientCircle( + size = 800.dp, + offsetX = 200.dp, + offsetY = 290.dp, + startColor = Color(0xFF3D0230), + endColor = Color(0x000B0E17), + ) + } +} + +@Composable +private fun StoryDescription(headerText: String, bodyText: String) { + Column( + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.size40) + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Top, + ) { + SpacerH32() + Text( + text = headerText, + textAlign = TextAlign.Center, + style = TangemTheme.typography.h1.copy( + fontWeight = FontWeight.Bold, + ), + color = TangemTheme.colors.text.primary2, + ) + SpacerH16() + Text( + text = bodyText, + textAlign = TextAlign.Center, + style = TangemTypography.subtitle1, + color = TangemTheme.colors.text.tertiary, + ) + } +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/component/Gradient.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/component/Gradient.kt new file mode 100644 index 0000000000..e54a15c863 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/component/Gradient.kt @@ -0,0 +1,38 @@ +package com.tangem.feature.learn2earn.presentation.ui.component + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp + +/** +[REDACTED_AUTHOR] + */ +@Composable +internal fun GradientCircle( + size: Dp, + offsetX: Dp, + offsetY: Dp, + startColor: Color, + endColor: Color, + radius: Dp = size / 2, +) { + Canvas( + modifier = Modifier + .size(size) + .offset(x = offsetX, y = offsetY), + onDraw = { + drawCircle( + radius = radius.toPx(), + brush = Brush.radialGradient( + radius = radius.toPx(), + colors = listOf(startColor, endColor), + ), + ) + }, + ) +} \ No newline at end of file From cd3d290eb1414798ef9b0845e9df7a02371fa75f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 1 Jun 2023 23:35:59 +0300 Subject: [PATCH 05/44] Updated on 2026-08-14 --- .../presentation/ui/GetBonusAlert.kt | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusAlert.kt diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusAlert.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusAlert.kt new file mode 100644 index 0000000000..bbff8f8fa8 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusAlert.kt @@ -0,0 +1,106 @@ +package com.tangem.feature.learn2earn.presentation.ui + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemTypography +import com.tangem.feature.learn2earn.impl.R +import com.tangem.feature.learn2earn.presentation.ui.component.GradientCircle + +/** +[REDACTED_AUTHOR] + */ +@Composable +fun GetBonusAlert(onClick: () -> Unit, modifier: Modifier = Modifier) { + val shape = TangemTheme.shapes.roundedCornersMedium + Box( + modifier = modifier + .height(TangemTheme.dimens.size96) + .background( + color = TangemTheme.colors.background.action, + shape = shape, + ), + ) { + ContentBackground(shape) + Row( + modifier = Modifier.fillMaxHeight(), + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + modifier = Modifier.padding(horizontal = TangemTheme.dimens.size16), + painter = painterResource(id = R.drawable.img_1inch_logo_42_40), + contentDescription = null, + ) + Column( + modifier = Modifier.padding(end = TangemTheme.dimens.size40), + ) { + Text( + text = stringResource(id = R.string.main_learn_title), + style = TangemTypography.body1, + color = TangemTheme.colors.text.primary2, + ) + Text( + text = stringResource(id = R.string.main_learn_subtitle), + style = TangemTypography.caption, + color = TangemTheme.colors.text.secondary, + ) + } + } + Image( + modifier = Modifier + .size(TangemTheme.dimens.size40) + .padding(end = TangemTheme.dimens.size16) + .align(Alignment.CenterEnd) + .clickable(onClick = onClick), + painter = painterResource(id = R.drawable.ic_chevron_right_24), + contentDescription = null, + colorFilter = ColorFilter.tint(Color.Gray), + ) + } +} + +@Suppress("MagicNumber") +@Composable +private fun ContentBackground(shape: Shape) { + Box( + modifier = Modifier + .clip(shape) + .fillMaxSize(), + ) { + GradientCircle( + size = 340.dp, + offsetX = (-103).dp, + offsetY = 16.dp, + startColor = Color(0xFF1D5DC7), + endColor = Color(0x000B173D), + ) + GradientCircle( + size = 268.dp, + offsetX = 90.dp, + offsetY = 0.dp, + startColor = Color(0xD8FF0CCA), + endColor = Color(0x000B0E17), + ) + } + Box( + modifier = Modifier + .background( + color = TangemTheme.colors.background.action.copy(alpha = 0.7f), + shape = shape, + ) + .fillMaxSize(), + ) +} \ No newline at end of file From f3afa5546910bcbad38910312b0b7aae73a986da Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Jun 2023 17:11:37 +0300 Subject: [PATCH 06/44] Updated on 2026-08-14 --- .../tangem/tap/features/home/HomeFragment.kt | 11 +++-- .../features/home/compose/StoriesScreen.kt | 15 ++++--- features/learn2earn/api/build.gradle.kts | 3 +- .../api/src/main/AndroidManifest.xml | 2 - features/learn2earn/impl/build.gradle.kts | 1 + .../presentation/ui/GetBonusAlert.kt | 25 +++++++++++ .../presentation/ui/OneInchStoriesScreen.kt | 41 +++++++++++++++---- .../res/drawable/img_1inch_logo_401_378.xml | 0 .../res/drawable/img_1inch_logo_42_40.xml | 0 9 files changed, 72 insertions(+), 26 deletions(-) delete mode 100644 features/learn2earn/api/src/main/AndroidManifest.xml rename {core/ui => features/learn2earn/impl}/src/main/res/drawable/img_1inch_logo_401_378.xml (100%) rename {core/ui => features/learn2earn/impl}/src/main/res/drawable/img_1inch_logo_42_40.xml (100%) diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index acf3408acf..bdb1d84900 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -12,8 +12,8 @@ import androidx.compose.ui.platform.ComposeView import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.fragment.app.Fragment -import com.google.accompanist.appcompattheme.AppCompatTheme import com.tangem.core.analytics.Analytics +import com.tangem.core.ui.res.TangemTheme import com.tangem.tap.common.analytics.events.IntroductionProcess import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction @@ -37,11 +37,10 @@ class HomeFragment : Fragment(), StoreSubscriber { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return ComposeView(inflater.context).apply { setContent { - BackHandler { - requireActivity().finish() - } - - AppCompatTheme { + TangemTheme { + BackHandler { + requireActivity().finish() + } ScreenContent() } } diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt index 2d453259c4..af5df09c59 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt @@ -26,7 +26,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.learn2earn.presentation.ui.OneInchStoriesContent +import com.tangem.feature.learn2earn.presentation.ui.OneInchStoriesScreen import com.tangem.tap.features.home.compose.content.* import com.tangem.tap.features.home.compose.views.HomeButtons import com.tangem.tap.features.home.compose.views.StoriesProgressBar @@ -152,12 +152,7 @@ fun StoriesScreen( colorFilter = if (currentStory.isDarkBackground) null else ColorFilter.tint(Color.Black), ) when (currentStory) { - Stories.OneInchPromo -> { - // TODO: fixme - TangemTheme { - OneInchStoriesContent({}) - } - } + Stories.OneInchPromo -> OneInchStoriesScreen({}) Stories.TangemIntro -> FirstStoriesContent(isPaused, currentStory.duration) { hideContent.value = it } Stories.RevolutionaryWallet -> StoriesRevolutionaryWallet(currentStory.duration) Stories.UltraSecureBackup -> StoriesUltraSecureBackup(isPaused, currentStory.duration) @@ -200,7 +195,11 @@ fun StoriesScreen( if (currentStory != Stories.OneInchPromo) { HomeButtons( modifier = Modifier - .padding(start = 16.dp, top = 0.dp, end = 16.dp, bottom = 37.dp) + .padding( + start = TangemTheme.dimens.size16, + end = TangemTheme.dimens.size16, + bottom = TangemTheme.dimens.size36, + ) .fillMaxWidth(), isDarkBackground = currentStory.isDarkBackground, btnScanStateInProgress = homeState.value.btnScanStateInProgress, diff --git a/features/learn2earn/api/build.gradle.kts b/features/learn2earn/api/build.gradle.kts index 1fbece85d5..7ff7fb7522 100644 --- a/features/learn2earn/api/build.gradle.kts +++ b/features/learn2earn/api/build.gradle.kts @@ -1,5 +1,4 @@ plugins { - alias(deps.plugins.android.library) - alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.jvm) id("configuration") } \ No newline at end of file diff --git a/features/learn2earn/api/src/main/AndroidManifest.xml b/features/learn2earn/api/src/main/AndroidManifest.xml deleted file mode 100644 index 4d3df466b3..0000000000 --- a/features/learn2earn/api/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/features/learn2earn/impl/build.gradle.kts b/features/learn2earn/impl/build.gradle.kts index f0071798aa..9e390b1f67 100644 --- a/features/learn2earn/impl/build.gradle.kts +++ b/features/learn2earn/impl/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { implementation(project(":core:res")) /** Tangem libraries */ + // TODO: fixme: delete if later it doesn't needed // implementation(deps.tangem.card.core) // implementation(deps.tangem.card.android) { // exclude(module = "joda-time") diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusAlert.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusAlert.kt index bbff8f8fa8..0079c24f11 100644 --- a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusAlert.kt +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusAlert.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTypography @@ -103,4 +104,28 @@ private fun ContentBackground(shape: Shape) { ) .fillMaxSize(), ) +} + +@Preview +@Composable +private fun OneInchStoriesContentPreview_Light() { + TangemTheme( + isDark = false, + ) { + GetBonusAlert( + onClick = {}, + ) + } +} + +@Preview +@Composable +private fun OneInchStoriesContentPreview_Dark() { + TangemTheme( + isDark = true, + ) { + GetBonusAlert( + onClick = {}, + ) + } } \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/OneInchStoriesScreen.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/OneInchStoriesScreen.kt index 4d7c7ee283..ff3c3218b7 100644 --- a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/OneInchStoriesScreen.kt +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/OneInchStoriesScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.SecondaryButton import com.tangem.core.ui.components.SpacerH16 @@ -23,16 +24,19 @@ import com.tangem.feature.learn2earn.presentation.ui.component.GradientCircle /** [REDACTED_AUTHOR] */ -@Suppress("ReusedModifierInstance") +// TODO: fixme: make function as internal after adding feature interface @Composable -fun OneInchStoriesContent(onLearnClick: () -> Unit, modifier: Modifier = Modifier) { +fun OneInchStoriesScreen(onLearnClick: () -> Unit, modifier: Modifier = Modifier) { Box(modifier = modifier.fillMaxSize()) { ContentBackground( - modifier = modifier + modifier = Modifier .fillMaxSize() .align(Alignment.Center), ) StoryDescription( + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.size40) + .fillMaxSize(), headerText = stringResource(id = R.string.story_learn_title), bodyText = stringResource(id = R.string.story_learn_description), ) @@ -77,13 +81,10 @@ private fun ContentBackground(modifier: Modifier = Modifier) { } @Composable -private fun StoryDescription(headerText: String, bodyText: String) { +private fun StoryDescription(headerText: String, bodyText: String, modifier: Modifier = Modifier) { Column( - modifier = Modifier - .padding(horizontal = TangemTheme.dimens.size40) - .fillMaxSize(), + modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Top, ) { SpacerH32() Text( @@ -102,4 +103,28 @@ private fun StoryDescription(headerText: String, bodyText: String) { color = TangemTheme.colors.text.tertiary, ) } +} + +@Preview +@Composable +private fun OneInchStoriesContentPreview_Light() { + TangemTheme( + isDark = false, + ) { + OneInchStoriesScreen( + onLearnClick = {}, + ) + } +} + +@Preview +@Composable +private fun OneInchStoriesContentPreview_Dark() { + TangemTheme( + isDark = true, + ) { + OneInchStoriesScreen( + onLearnClick = {}, + ) + } } \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/img_1inch_logo_401_378.xml b/features/learn2earn/impl/src/main/res/drawable/img_1inch_logo_401_378.xml similarity index 100% rename from core/ui/src/main/res/drawable/img_1inch_logo_401_378.xml rename to features/learn2earn/impl/src/main/res/drawable/img_1inch_logo_401_378.xml diff --git a/core/ui/src/main/res/drawable/img_1inch_logo_42_40.xml b/features/learn2earn/impl/src/main/res/drawable/img_1inch_logo_42_40.xml similarity index 100% rename from core/ui/src/main/res/drawable/img_1inch_logo_42_40.xml rename to features/learn2earn/impl/src/main/res/drawable/img_1inch_logo_42_40.xml From 4affe1293d1b05631e94ade9ce8d0a8c707cd1a3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Jun 2023 16:07:14 +0300 Subject: [PATCH 07/44] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 46 +++++-- .../java/com/tangem/tap/TapApplication.kt | 1 - .../com/tangem/tap/common/IntentHandler.kt | 127 ------------------ .../features/intentHandler/IntentHandler.kt | 10 ++ .../intentHandler/MainIntentHandler.kt | 29 ++++ .../handlers/BackgroundScanIntentHandler.kt | 52 +++++++ .../handlers/BuyCurrencyIntentHandler.kt | 30 +++++ .../handlers/SellCurrencyIntentHandler.kt | 43 ++++++ .../WalletConnectLinkIntentHandler.kt | 37 +++++ .../features/welcome/redux/WelcomeAction.kt | 2 +- .../welcome/redux/WelcomeMiddleware.kt | 37 ++--- .../features/welcome/redux/WelcomeReducer.kt | 2 +- 12 files changed, 254 insertions(+), 162 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/common/IntentHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/features/intentHandler/IntentHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BackgroundScanIntentHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BuyCurrencyIntentHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index e108d35af3..18dd61e8ea 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -32,6 +32,11 @@ import com.tangem.tap.domain.TangemSdkManager import com.tangem.tap.domain.userWalletList.UserWalletsListManager import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation +import com.tangem.tap.features.intentHandler.MainIntentHandler +import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler +import com.tangem.tap.features.intentHandler.handlers.BuyCurrencyIntentHandler +import com.tangem.tap.features.intentHandler.handlers.SellCurrencyIntentHandler +import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction import com.tangem.tap.features.shop.redux.ShopAction @@ -45,6 +50,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.launch import java.lang.ref.WeakReference import javax.inject.Inject import kotlin.coroutines.CoroutineContext @@ -95,6 +101,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac @Inject lateinit var walletConnectInteractor: WalletConnectInteractor + // TODO: fixme: inject through DI + private val intentHandler: MainIntentHandler = MainIntentHandler() + private var snackbar: Snackbar? = null private val dialogManager = DialogManager() private val binding: ActivityMainBinding by viewBinding(ActivityMainBinding::bind) @@ -117,6 +126,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac lockUserWalletsTimer = LockUserWalletsTimer(owner = this) initUserWalletsListManager() + initIntentHandlers() store.dispatch( ShopAction.CheckIfGooglePayAvailable( @@ -133,6 +143,14 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac ) } + private fun initIntentHandlers() { + val hasSavedWalletsProvider = { store.state.globalState.userWalletsListManager?.hasUserWallets == true } + intentHandler.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider)) + intentHandler.addHandler(WalletConnectLinkIntentHandler()) + intentHandler.addHandler(BuyCurrencyIntentHandler()) + intentHandler.addHandler(SellCurrencyIntentHandler()) + } + private fun initUserWalletsListManager() { val manager = if (preferencesStorage.shouldSaveUserWallets) { UserWalletsListManager.provideBiometricImplementation( @@ -164,12 +182,14 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac super.onResume() notificationsHandler = NotificationsHandler(binding.fragmentContainer) - navigateToInitialScreenIfNeeded(intent) + navigateToInitialScreenIfNeededOnResume(intent) } override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - intentHandler.handleIntent(intent, userWalletsListManager.hasUserWallets) + scope.launch { + intentHandler.handleIntent(intent) + } } override fun onStart() { @@ -185,6 +205,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac override fun onDestroy() { store.dispatch(NavigationAction.ActivityDestroyed(WeakReference(this))) + intentHandler.removeAll() super.onDestroy() } @@ -235,29 +256,38 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac lockUserWalletsTimer?.restart() } - private fun navigateToInitialScreenIfNeeded(intent: Intent?) { + private fun navigateToInitialScreenIfNeededOnResume(intentWhichStartedActivity: Intent?) { val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0 val isNotScannedBefore = store.state.globalState.scanResponse == null val isOnboardingServiceNotActive = store.state.globalState.onboardingState.onboardingStarted val isShopNotOpened = store.state.shopState.total != null when { !backStackIsEmpty && isNotScannedBefore && isOnboardingServiceNotActive && isShopNotOpened -> { - navigateToInitialScreen(intent) + navigateToInitialScreenOnResume(intentWhichStartedActivity) } backStackIsEmpty -> { - navigateToInitialScreen(intent) + navigateToInitialScreenOnResume(intentWhichStartedActivity) } else -> Unit } } - private fun navigateToInitialScreen(intent: Intent?) { + private fun navigateToInitialScreenOnResume(intentWhichStartedActivity: Intent?) { if (store.state.globalState.userWalletsListManager?.hasUserWallets == true) { store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Welcome)) - store.dispatchOnMain(WelcomeAction.HandleIntentIfNeeded(intent)) + store.dispatchOnMain(WelcomeAction.SetInitialIntent(intentWhichStartedActivity)) + scope.launch { + val handler = BackgroundScanIntentHandler(hasSavedUserWalletsProvider = { true }) + val intentWasHandled = handler.handleIntent(intentWhichStartedActivity) + if (!intentWasHandled) { + store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics) + } + } } else { store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Home)) - intentHandler.handleIntent(intent, hasSavedUserWallets = false) + scope.launch { + intentHandler.handleIntent(intentWhichStartedActivity) + } } store.dispatch(BackupAction.CheckForUnfinishedBackup) } diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 5de365ee79..496b4e742b 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -102,7 +102,6 @@ val walletCurrenciesManager by lazy { val totalFiatBalanceCalculator by lazy { TotalFiatBalanceCalculator.provideDefaultImplementation() } -val intentHandler by lazy { IntentHandler() } @HiltAndroidApp class TapApplication : Application(), ImageLoaderFactory { diff --git a/app/src/main/java/com/tangem/tap/common/IntentHandler.kt b/app/src/main/java/com/tangem/tap/common/IntentHandler.kt deleted file mode 100644 index 4d50c59679..0000000000 --- a/app/src/main/java/com/tangem/tap/common/IntentHandler.kt +++ /dev/null @@ -1,127 +0,0 @@ -package com.tangem.tap.common - -import android.content.Intent -import android.net.Uri -import android.nfc.NfcAdapter -import android.nfc.Tag -import android.os.Build -import com.tangem.core.analytics.Analytics -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.extensions.removePrefixOrNull -import com.tangem.tap.domain.walletconnect.WalletConnectManager -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.features.home.redux.HomeAction -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.welcome.redux.WelcomeAction -import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder -import com.tangem.tap.scope -import com.tangem.tap.store -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import timber.log.Timber - -class IntentHandler { - - private val nfcActions = arrayOf( - NfcAdapter.ACTION_NDEF_DISCOVERED, - NfcAdapter.ACTION_TECH_DISCOVERED, - NfcAdapter.ACTION_TAG_DISCOVERED, - ) - - fun handleIntent(intent: Intent?, hasSavedUserWallets: Boolean) { - handleBackgroundScan(intent, hasSavedUserWallets) - handleWalletConnectLink(intent) - handleBuyCurrencyCallback(intent) - handleSellCurrencyCallback(intent) - } - - fun handleWalletConnectLink(intent: Intent?) { - val wcUri = when (intent?.scheme) { - WalletConnectManager.WC_SCHEME -> { - intent.data?.toString() - } - TANGEM_SCHEME -> { - intent.data?.toString()?.removePrefixOrNull(TANGEM_WC_PREFIX) - } - else -> { - null - } - } - if (wcUri != null) { - store.dispatch(WalletConnectAction.HandleDeepLink(wcUri)) - } - } - - fun handleBackgroundScan(intent: Intent?, hasSavedUserWallets: Boolean): Boolean { - if (intent == null || intent.action !in nfcActions) return false - - val tag: Tag? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra(NfcAdapter.EXTRA_TAG, Tag::class.java) - } else { - @Suppress("DEPRECATION") - intent.getParcelableExtra(NfcAdapter.EXTRA_TAG) - } - if (tag == null) return false - - intent.action = null - if (hasSavedUserWallets) { - // TODO: Remove delay after [REDACTED_JIRA] - scope.launch { - delay(timeMillis = 200) - store.dispatch(WelcomeAction.ProceedWithCard) - } - } else { - store.dispatch(HomeAction.ReadCard()) - } - - return true - } - - private fun handleBuyCurrencyCallback(intent: Intent?) { - val data = intent?.data ?: return - - val successUri = Uri.parse(ExchangeUrlBuilder.SUCCESS_URL) - if (data.host == successUri.host && data.authority == successUri.authority) { - val currency = store.state.walletState.selectedCurrency ?: return - val currencyType = AnalyticsParam.CurrencyType.Currency(currency) - Analytics.send(Token.Bought(currencyType)) - } - } - - fun handleSellCurrencyCallback(intent: Intent?) { - try { - val transactionID = - intent?.data?.getQueryParameter(TRANSACTION_ID_PARAM) ?: return - val currency = - intent.data?.getQueryParameter(CURRENCY_CODE_PARAM) ?: return - val amount = - intent.data?.getQueryParameter(CURRENCY_AMOUNT_PARAM) ?: return - val destinationAddress = - intent.data?.getQueryParameter(DEPOSIT_WALLET_ADDRESS_PARAM) - ?: return - - Timber.d("MoonPay Sell: $amount $currency to $destinationAddress") - - store.dispatch( - WalletAction.TradeCryptoAction.SendCrypto( - currencyId = currency, - amount = amount, - destinationAddress = destinationAddress, - transactionId = transactionID, - ), - ) - } catch (exception: Exception) { - Timber.d("Not MoonPay URL") - } - } - - companion object { - private const val TRANSACTION_ID_PARAM = "transactionId" - private const val CURRENCY_CODE_PARAM = "baseCurrencyCode" - private const val CURRENCY_AMOUNT_PARAM = "baseCurrencyAmount" - private const val DEPOSIT_WALLET_ADDRESS_PARAM = "depositWalletAddress" - private const val TANGEM_SCHEME = "tangem" - private const val TANGEM_WC_PREFIX = "tangem://wc?uri=" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/IntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/IntentHandler.kt new file mode 100644 index 0000000000..ba008d4e5a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/IntentHandler.kt @@ -0,0 +1,10 @@ +package com.tangem.tap.features.intentHandler + +import android.content.Intent + +/** +[REDACTED_AUTHOR] + */ +interface IntentHandler { + suspend fun handleIntent(intent: Intent?): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt new file mode 100644 index 0000000000..7a952f7e63 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt @@ -0,0 +1,29 @@ +package com.tangem.tap.features.intentHandler + +import android.content.Intent + +/** +[REDACTED_AUTHOR] + */ +class MainIntentHandler { + + private val intentHandlers = mutableListOf() + + fun addHandler(handler: IntentHandler) { + intentHandlers.add(handler) + } + + fun removeIntentHandler(handler: IntentHandler) { + intentHandlers.remove(handler) + } + + fun removeAll() { + intentHandlers.clear() + } + + suspend fun handleIntent(intent: Intent?) { + intentHandlers.toList().forEach { + it.handleIntent(intent) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BackgroundScanIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BackgroundScanIntentHandler.kt new file mode 100644 index 0000000000..18a51c222f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BackgroundScanIntentHandler.kt @@ -0,0 +1,52 @@ +package com.tangem.tap.features.intentHandler.handlers + +import android.content.Intent +import android.nfc.NfcAdapter +import android.nfc.Tag +import android.os.Build +import com.tangem.tap.features.home.redux.HomeAction +import com.tangem.tap.features.intentHandler.IntentHandler +import com.tangem.tap.features.welcome.redux.WelcomeAction +import com.tangem.tap.scope +import com.tangem.tap.store +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +/** +[REDACTED_AUTHOR] + */ +class BackgroundScanIntentHandler( + private val hasSavedUserWalletsProvider: () -> Boolean, +) : IntentHandler { + + private val nfcActions = arrayOf( + NfcAdapter.ACTION_NDEF_DISCOVERED, + NfcAdapter.ACTION_TECH_DISCOVERED, + NfcAdapter.ACTION_TAG_DISCOVERED, + ) + + override suspend fun handleIntent(intent: Intent?): Boolean { + if (intent == null || intent.action !in nfcActions) return false + + val tag: Tag? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getParcelableExtra(NfcAdapter.EXTRA_TAG, Tag::class.java) + } else { + @Suppress("DEPRECATION") + intent.getParcelableExtra(NfcAdapter.EXTRA_TAG) + } + if (tag == null) return false + + intent.action = null + if (hasSavedUserWalletsProvider.invoke()) { + // TODO: Remove delay after [REDACTED_JIRA] + scope.launch { + delay(timeMillis = 200) + store.dispatch(WelcomeAction.ProceedWithCard) + } + } else { + store.dispatch(HomeAction.ReadCard()) + } + + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BuyCurrencyIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BuyCurrencyIntentHandler.kt new file mode 100644 index 0000000000..39dda7cd24 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BuyCurrencyIntentHandler.kt @@ -0,0 +1,30 @@ +package com.tangem.tap.features.intentHandler.handlers + +import android.content.Intent +import android.net.Uri +import com.tangem.core.analytics.Analytics +import com.tangem.tap.common.analytics.events.AnalyticsParam +import com.tangem.tap.common.analytics.events.Token +import com.tangem.tap.features.intentHandler.IntentHandler +import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder +import com.tangem.tap.store + +/** +[REDACTED_AUTHOR] + */ +class BuyCurrencyIntentHandler : IntentHandler { + + override suspend fun handleIntent(intent: Intent?): Boolean { + val data = intent?.data ?: return false + val currency = store.state.walletState.selectedCurrency ?: return false + + val successUri = Uri.parse(ExchangeUrlBuilder.SUCCESS_URL) + return if (data.host == successUri.host && data.authority == successUri.authority) { + val currencyType = AnalyticsParam.CurrencyType.Currency(currency) + Analytics.send(Token.Bought(currencyType)) + true + } else { + false + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt new file mode 100644 index 0000000000..35d0b31763 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt @@ -0,0 +1,43 @@ +package com.tangem.tap.features.intentHandler.handlers + +import android.content.Intent +import com.tangem.tap.features.intentHandler.IntentHandler +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.store +import timber.log.Timber + +/** +[REDACTED_AUTHOR] + */ +class SellCurrencyIntentHandler : IntentHandler { + override suspend fun handleIntent(intent: Intent?): Boolean { + return try { + val intentData = intent?.data ?: return false + val transactionID = intentData.getQueryParameter(TRANSACTION_ID_PARAM) ?: return false + val currency = intentData.getQueryParameter(CURRENCY_CODE_PARAM) ?: return false + val amount = intentData.getQueryParameter(CURRENCY_AMOUNT_PARAM) ?: return false + val destinationAddress = intentData.getQueryParameter(DEPOSIT_WALLET_ADDRESS_PARAM) ?: return false + + Timber.d("MoonPay Sell: $amount $currency to $destinationAddress") + store.dispatch( + WalletAction.TradeCryptoAction.SendCrypto( + currencyId = currency, + amount = amount, + destinationAddress = destinationAddress, + transactionId = transactionID, + ), + ) + true + } catch (exception: Exception) { + Timber.d("Not MoonPay URL") + false + } + } + + companion object { + private const val TRANSACTION_ID_PARAM = "transactionId" + private const val CURRENCY_CODE_PARAM = "baseCurrencyCode" + private const val CURRENCY_AMOUNT_PARAM = "baseCurrencyAmount" + private const val DEPOSIT_WALLET_ADDRESS_PARAM = "depositWalletAddress" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt new file mode 100644 index 0000000000..1154f5ca14 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt @@ -0,0 +1,37 @@ +package com.tangem.tap.features.intentHandler.handlers + +import android.content.Intent +import com.tangem.tap.common.extensions.removePrefixOrNull +import com.tangem.tap.domain.walletconnect.WalletConnectManager +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.features.intentHandler.IntentHandler +import com.tangem.tap.store + +/** +[REDACTED_AUTHOR] + */ +class WalletConnectLinkIntentHandler : IntentHandler { + + override suspend fun handleIntent(intent: Intent?): Boolean { + val intentData = intent?.data ?: return false + val scheme = intent.scheme ?: return false + + val wcUri = when (scheme) { + WalletConnectManager.WC_SCHEME -> intentData.toString() + TANGEM_SCHEME -> intentData.toString().removePrefixOrNull(TANGEM_WC_PREFIX) + else -> null + } + + return if (wcUri == null) { + false + } else { + store.dispatch(WalletConnectAction.HandleDeepLink(wcUri)) + true + } + } + + companion object { + private const val TANGEM_SCHEME = "tangem" + private const val TANGEM_WC_PREFIX = "tangem://wc?uri=" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt index a0484fc798..e7ce3e5957 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt @@ -15,7 +15,7 @@ internal sealed interface WelcomeAction : Action { data class Error(val error: TangemError) : WelcomeAction } - data class HandleIntentIfNeeded(val intent: Intent?) : WelcomeAction + data class SetInitialIntent(val intent: Intent?) : WelcomeAction object CloseError : WelcomeAction diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index 9955bf6d6d..7f22650f5e 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -1,11 +1,8 @@ package com.tangem.tap.features.welcome.redux import android.content.Intent +import com.tangem.common.* import com.tangem.common.core.TangemSdkError -import com.tangem.common.doOnFailure -import com.tangem.common.doOnResult -import com.tangem.common.doOnSuccess -import com.tangem.common.flatMap import com.tangem.domain.models.scan.ScanResponse import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam @@ -19,6 +16,8 @@ import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.model.builders.UserWalletBuilder import com.tangem.tap.domain.scanCard.ScanCardProcessor import com.tangem.tap.domain.userWalletList.unlockIfLockable +import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler +import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayActivationError import com.tangem.tap.features.signin.redux.SignInAction import kotlinx.coroutines.launch import org.rekotlin.Middleware @@ -39,24 +38,10 @@ internal class WelcomeMiddleware { private fun handleAction(action: WelcomeAction, state: WelcomeState) { when (action) { - is WelcomeAction.ProceedWithBiometrics -> { - proceedWithBiometrics(state) - } - is WelcomeAction.ProceedWithCard -> { - proceedWithCard(state) - } - is WelcomeAction.HandleIntentIfNeeded -> { - handleInitialIntent(action.intent) - } - is WelcomeAction.ClearUserWallets -> { - disableUserWalletsSaving() - } - is WelcomeAction.ProceedWithBiometrics.Error, - is WelcomeAction.ProceedWithCard.Error, - is WelcomeAction.ProceedWithBiometrics.Success, - is WelcomeAction.ProceedWithCard.Success, - is WelcomeAction.CloseError, - -> Unit + is WelcomeAction.ProceedWithBiometrics -> proceedWithBiometrics(state) + is WelcomeAction.ProceedWithCard -> proceedWithCard(state) + is WelcomeAction.ClearUserWallets -> disableUserWalletsSaving() + else -> Unit } } @@ -85,7 +70,9 @@ internal class WelcomeMiddleware { store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics.Success) store.onUserWalletSelected(userWallet = selectedUserWallet) - intentHandler.handleWalletConnectLink(state.intent) + state.intent?.let { + WalletConnectLinkIntentHandler().handleIntent(it) + } } } @@ -104,7 +91,9 @@ internal class WelcomeMiddleware { store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success) store.onUserWalletSelected(userWallet = userWallet) - intentHandler.handleWalletConnectLink(state.intent) + state.intent?.let { + WalletConnectLinkIntentHandler().handleIntent(it) + } } } } diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt index 5f14d1744d..11bfb0ea4a 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt @@ -14,7 +14,7 @@ internal object WelcomeReducer { private fun internalReduce(action: WelcomeAction, state: WelcomeState): WelcomeState { return when (action) { - is WelcomeAction.HandleIntentIfNeeded -> state.copy(intent = action.intent) + is WelcomeAction.SetInitialIntent -> state.copy(intent = action.intent) is WelcomeAction.ProceedWithBiometrics -> state.copy(isUnlockWithBiometricsInProgress = true) is WelcomeAction.ProceedWithCard -> state.copy(isUnlockWithCardInProgress = true) is WelcomeAction.ProceedWithBiometrics.Error -> state.copy( From e73e91e07a764e0a2967e1e31339588e30510eac Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Jun 2023 07:02:28 +0300 Subject: [PATCH 08/44] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 18 +++++++++--------- ...MainIntentHandler.kt => IntentProcessor.kt} | 8 +++++--- .../handlers/SellCurrencyIntentHandler.kt | 3 ++- .../handlers/WalletConnectLinkIntentHandler.kt | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) rename app/src/main/java/com/tangem/tap/features/intentHandler/{MainIntentHandler.kt => IntentProcessor.kt} (62%) diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 18dd61e8ea..61a591e5c7 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -32,7 +32,7 @@ import com.tangem.tap.domain.TangemSdkManager import com.tangem.tap.domain.userWalletList.UserWalletsListManager import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation -import com.tangem.tap.features.intentHandler.MainIntentHandler +import com.tangem.tap.features.intentHandler.IntentProcessor import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler import com.tangem.tap.features.intentHandler.handlers.BuyCurrencyIntentHandler import com.tangem.tap.features.intentHandler.handlers.SellCurrencyIntentHandler @@ -102,7 +102,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac lateinit var walletConnectInteractor: WalletConnectInteractor // TODO: fixme: inject through DI - private val intentHandler: MainIntentHandler = MainIntentHandler() + private val intentProcessor: IntentProcessor = IntentProcessor() private var snackbar: Snackbar? = null private val dialogManager = DialogManager() @@ -145,10 +145,10 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac private fun initIntentHandlers() { val hasSavedWalletsProvider = { store.state.globalState.userWalletsListManager?.hasUserWallets == true } - intentHandler.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider)) - intentHandler.addHandler(WalletConnectLinkIntentHandler()) - intentHandler.addHandler(BuyCurrencyIntentHandler()) - intentHandler.addHandler(SellCurrencyIntentHandler()) + intentProcessor.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider)) + intentProcessor.addHandler(WalletConnectLinkIntentHandler()) + intentProcessor.addHandler(BuyCurrencyIntentHandler()) + intentProcessor.addHandler(SellCurrencyIntentHandler()) } private fun initUserWalletsListManager() { @@ -188,7 +188,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) scope.launch { - intentHandler.handleIntent(intent) + intentProcessor.handleIntent(intent) } } @@ -205,7 +205,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac override fun onDestroy() { store.dispatch(NavigationAction.ActivityDestroyed(WeakReference(this))) - intentHandler.removeAll() + intentProcessor.removeAll() super.onDestroy() } @@ -286,7 +286,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac } else { store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Home)) scope.launch { - intentHandler.handleIntent(intentWhichStartedActivity) + intentProcessor.handleIntent(intentWhichStartedActivity) } } store.dispatch(BackupAction.CheckForUnfinishedBackup) diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/IntentProcessor.kt similarity index 62% rename from app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt rename to app/src/main/java/com/tangem/tap/features/intentHandler/IntentProcessor.kt index 7a952f7e63..47cf6a31fd 100644 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/IntentProcessor.kt @@ -1,13 +1,15 @@ package com.tangem.tap.features.intentHandler import android.content.Intent +import java.util.concurrent.CopyOnWriteArrayList /** [REDACTED_AUTHOR] */ -class MainIntentHandler { +// TODO: fixme: close it with the combined interfaces IntentHandler and IntentHandlerHolder +class IntentProcessor { - private val intentHandlers = mutableListOf() + private val intentHandlers = CopyOnWriteArrayList() fun addHandler(handler: IntentHandler) { intentHandlers.add(handler) @@ -22,7 +24,7 @@ class MainIntentHandler { } suspend fun handleIntent(intent: Intent?) { - intentHandlers.toList().forEach { + intentHandlers.forEach { it.handleIntent(intent) } } diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt index 35d0b31763..3fc6c318d6 100644 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt @@ -10,6 +10,7 @@ import timber.log.Timber [REDACTED_AUTHOR] */ class SellCurrencyIntentHandler : IntentHandler { + override suspend fun handleIntent(intent: Intent?): Boolean { return try { val intentData = intent?.data ?: return false @@ -34,7 +35,7 @@ class SellCurrencyIntentHandler : IntentHandler { } } - companion object { + private companion object { private const val TRANSACTION_ID_PARAM = "transactionId" private const val CURRENCY_CODE_PARAM = "baseCurrencyCode" private const val CURRENCY_AMOUNT_PARAM = "baseCurrencyAmount" diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt index 1154f5ca14..e61d0b74d7 100644 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/WalletConnectLinkIntentHandler.kt @@ -30,7 +30,7 @@ class WalletConnectLinkIntentHandler : IntentHandler { } } - companion object { + private companion object { private const val TANGEM_SCHEME = "tangem" private const val TANGEM_WC_PREFIX = "tangem://wc?uri=" } From c0bc4ddf523d5080d85c794992a13da889c54186 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 16 Jun 2023 09:29:22 +0300 Subject: [PATCH 09/44] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/MainActivity.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 61a591e5c7..4b406eaccd 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -32,12 +32,12 @@ import com.tangem.tap.domain.TangemSdkManager import com.tangem.tap.domain.userWalletList.UserWalletsListManager import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation +import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor import com.tangem.tap.features.intentHandler.IntentProcessor import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler import com.tangem.tap.features.intentHandler.handlers.BuyCurrencyIntentHandler import com.tangem.tap.features.intentHandler.handlers.SellCurrencyIntentHandler import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler -import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction import com.tangem.tap.features.shop.redux.ShopAction import com.tangem.tap.features.welcome.redux.WelcomeAction @@ -278,8 +278,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac store.dispatchOnMain(WelcomeAction.SetInitialIntent(intentWhichStartedActivity)) scope.launch { val handler = BackgroundScanIntentHandler(hasSavedUserWalletsProvider = { true }) - val intentWasHandled = handler.handleIntent(intentWhichStartedActivity) - if (!intentWasHandled) { + val isBackgroundScanNotHandled = handler.handleIntent(intentWhichStartedActivity) + val hasNotIncompletedBackup = !backupService.hasIncompletedBackup + if (isBackgroundScanNotHandled && hasNotIncompletedBackup) { store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics) } } From 35ea0954826a2b8dc7cf0e2082e32cb9e508a53e Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Jun 2023 16:07:14 +0300 Subject: [PATCH 10/44] Updated on 2026-08-14 --- .../intentHandler/MainIntentHandler.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt new file mode 100644 index 0000000000..7a952f7e63 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt @@ -0,0 +1,29 @@ +package com.tangem.tap.features.intentHandler + +import android.content.Intent + +/** +[REDACTED_AUTHOR] + */ +class MainIntentHandler { + + private val intentHandlers = mutableListOf() + + fun addHandler(handler: IntentHandler) { + intentHandlers.add(handler) + } + + fun removeIntentHandler(handler: IntentHandler) { + intentHandlers.remove(handler) + } + + fun removeAll() { + intentHandlers.clear() + } + + suspend fun handleIntent(intent: Intent?) { + intentHandlers.toList().forEach { + it.handleIntent(intent) + } + } +} \ No newline at end of file From e16fe695751b883621a607b8ff5c5b2cf079447b Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Jun 2023 07:02:28 +0300 Subject: [PATCH 11/44] Updated on 2026-08-14 --- .../intentHandler/MainIntentHandler.kt | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt deleted file mode 100644 index 7a952f7e63..0000000000 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/MainIntentHandler.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.tap.features.intentHandler - -import android.content.Intent - -/** -[REDACTED_AUTHOR] - */ -class MainIntentHandler { - - private val intentHandlers = mutableListOf() - - fun addHandler(handler: IntentHandler) { - intentHandlers.add(handler) - } - - fun removeIntentHandler(handler: IntentHandler) { - intentHandlers.remove(handler) - } - - fun removeAll() { - intentHandlers.clear() - } - - suspend fun handleIntent(intent: Intent?) { - intentHandlers.toList().forEach { - it.handleIntent(intent) - } - } -} \ No newline at end of file From 353c7a2397fdb4dd8a06e4033563078fe349bb16 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 19 Jun 2023 08:47:01 +0300 Subject: [PATCH 12/44] Updated on 2026-08-14 --- .../network/auth/TangemBasicAuthProvider.kt | 7 ++++ .../tangem/tap/network/auth/di/AuthModule.kt | 8 ++++ .../datasource/api/promotion/PromotionApi.kt | 38 +++++++++++++++++ .../models/AbstractPromotionResponse.kt | 20 +++++++++ .../api/promotion/models/AwardRequestBody.kt | 11 +++++ .../api/promotion/models/AwardResponse.kt | 11 +++++ .../promotion/models/CodeAwardRequestBody.kt | 12 ++++++ .../api/promotion/models/CodeAwardResponse.kt | 11 +++++ .../models/CodeValidateRequestBody.kt | 11 +++++ .../promotion/models/CodeValidateResponse.kt | 11 +++++ .../promotion/models/PromotionInfoResponse.kt | 41 +++++++++++++++++++ .../promotion/models/ValidateRequestBody.kt | 11 +++++ .../api/promotion/models/ValidateResponse.kt | 11 +++++ .../datasource/config/ConfigManagerImpl.kt | 8 +--- .../tangem/datasource/config/models/Config.kt | 1 + .../datasource/config/models/JsonModels.kt | 1 + .../com/tangem/datasource/di/NetworkModule.kt | 33 ++++++++++++++- .../tangem/datasource/di/PromotionOneInch.kt | 8 ++++ .../com/tangem/lib/auth/BasicAuthProvider.kt | 9 ++++ 19 files changed, 256 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/network/auth/TangemBasicAuthProvider.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/PromotionApi.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AbstractPromotionResponse.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardRequestBody.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardResponse.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardRequestBody.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardResponse.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateRequestBody.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateResponse.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateRequestBody.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateResponse.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/PromotionOneInch.kt create mode 100644 libs/auth/src/main/java/com/tangem/lib/auth/BasicAuthProvider.kt diff --git a/app/src/main/java/com/tangem/tap/network/auth/TangemBasicAuthProvider.kt b/app/src/main/java/com/tangem/tap/network/auth/TangemBasicAuthProvider.kt new file mode 100644 index 0000000000..0db94f45ab --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/auth/TangemBasicAuthProvider.kt @@ -0,0 +1,7 @@ +package com.tangem.tap.network.auth + +import com.tangem.lib.auth.BasicAuthProvider + +class TangemBasicAuthProvider(private val credentials: String?) : BasicAuthProvider { + override fun getCredentials(): String? = credentials +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt index 6e9eabd1f1..ebd04d5b97 100644 --- a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt +++ b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt @@ -1,7 +1,9 @@ package com.tangem.tap.network.auth.di +import com.tangem.datasource.config.ConfigManager import com.tangem.lib.auth.AuthProvider import com.tangem.tap.network.auth.AuthProviderImpl +import com.tangem.tap.network.auth.TangemBasicAuthProvider import com.tangem.tap.proxy.AppStateHolder import dagger.Module import dagger.Provides @@ -18,4 +20,10 @@ class AuthModule { fun provideAuthProvider(appStateHolder: AppStateHolder): AuthProvider { return AuthProviderImpl(appStateHolder) } + + @Provides + @Singleton + fun provideTangemBasicAuthProvider(configManager: ConfigManager): com.tangem.lib.auth.BasicAuthProvider { + return TangemBasicAuthProvider(configManager.config.tangemComAuthorization) + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/PromotionApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/PromotionApi.kt new file mode 100644 index 0000000000..4c6dab75bc --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/PromotionApi.kt @@ -0,0 +1,38 @@ +package com.tangem.datasource.api.promotion + +import com.tangem.datasource.api.promotion.models.* +import retrofit2.http.* + +/** +[REDACTED_AUTHOR] + * Api details + * https://www.notion.so/tangem/Promotion-Program-API-0907159c3fdb4975aac761be632f44da + */ +interface PromotionApi { + + + @Headers("Cache-Control: max-age=$CACHE_CONTROL_SECONDS") + @GET("promotion") + suspend fun getPromotionInfo(@Query("programName") name: String): PromotionInfoResponse + + + @POST("promotion/code/validate") + suspend fun codeValidate(@Body request: CodeValidateRequestBody): CodeValidateResponse + + + @POST("promotion/code/award") + suspend fun codeAward(@Body request: CodeAwardRequestBody): CodeAwardResponse + + + @POST("promotion/validate") + suspend fun validate(@Body request: ValidateRequestBody): ValidateResponse + + + @GET("promotion/award") + suspend fun award(@Body request: AwardRequestBody): AwardResponse + + companion object { + const val ONE_INCH_TIMEOUT_MS = 5000L + const val CACHE_CONTROL_SECONDS = 3600 + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AbstractPromotionResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AbstractPromotionResponse.kt new file mode 100644 index 0000000000..7cf6126c2c --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AbstractPromotionResponse.kt @@ -0,0 +1,20 @@ +package com.tangem.datasource.api.promotion.models + +import com.squareup.moshi.Json + +/** +[REDACTED_AUTHOR] + */ +abstract class AbstractPromotionResponse { + + abstract val error: Error? + + fun isError(): Boolean = error != null + + data class Error( + @Json(name = "code") val code: Int, + @Json(name = "description") val description: String, + ) { + companion object + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardRequestBody.kt new file mode 100644 index 0000000000..4ce66c7d76 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardRequestBody.kt @@ -0,0 +1,11 @@ +package com.tangem.datasource.api.promotion.models + +import com.squareup.moshi.Json + +/** +[REDACTED_AUTHOR] + */ +data class AwardRequestBody( + @Json(name = "walletId") val walletId: String, + @Json(name = "programName") val programName: String, +) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardResponse.kt new file mode 100644 index 0000000000..c43c59fa45 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardResponse.kt @@ -0,0 +1,11 @@ +package com.tangem.datasource.api.promotion.models + +import com.squareup.moshi.Json + +/** +[REDACTED_AUTHOR] + */ +data class AwardResponse( + @Json(name = "status") val status: Boolean?, + @Json(name = "error") override val error: Error? = null, +) : AbstractPromotionResponse() \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardRequestBody.kt new file mode 100644 index 0000000000..51e8626d4b --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardRequestBody.kt @@ -0,0 +1,12 @@ +package com.tangem.datasource.api.promotion.models + +import com.squareup.moshi.Json + +/** +[REDACTED_AUTHOR] + */ +data class CodeAwardRequestBody( + @Json(name = "walletId") val walletId: String, + @Json(name = "address") val address: String, + @Json(name = "code") val code: String?, +) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardResponse.kt new file mode 100644 index 0000000000..33d9313c42 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardResponse.kt @@ -0,0 +1,11 @@ +package com.tangem.datasource.api.promotion.models + +import com.squareup.moshi.Json + +/** +[REDACTED_AUTHOR] + */ +data class CodeAwardResponse( + @Json(name = "status") val status: Boolean?, + @Json(name = "error") override val error: Error? = null, +) : AbstractPromotionResponse() \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateRequestBody.kt new file mode 100644 index 0000000000..769ecdad26 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateRequestBody.kt @@ -0,0 +1,11 @@ +package com.tangem.datasource.api.promotion.models + +import com.squareup.moshi.Json + +/** +[REDACTED_AUTHOR] + */ +data class CodeValidateRequestBody( + @Json(name = "walletId") val walletId: String, + @Json(name = "code") val code: String?, +) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateResponse.kt new file mode 100644 index 0000000000..e6e87e0ff3 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateResponse.kt @@ -0,0 +1,11 @@ +package com.tangem.datasource.api.promotion.models + +import com.squareup.moshi.Json + +/** +[REDACTED_AUTHOR] + */ +data class CodeValidateResponse( + @Json(name = "valid") val valid: Boolean?, + @Json(name = "error") override val error: Error? = null, +) : AbstractPromotionResponse() \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt new file mode 100644 index 0000000000..2caaaacf14 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt @@ -0,0 +1,41 @@ +package com.tangem.datasource.api.promotion.models + +import com.squareup.moshi.Json + +/** +[REDACTED_AUTHOR] + */ +data class PromotionInfoResponse( + @Json(name = "status") val status: Status?, + @Json(name = "awardForNewCard") val awardForNewCard: Float?, + @Json(name = "awardForOldCard") val awardForOldCard: Float?, + @Json(name = "awardPaymentToken") val awardPaymentToken: TokenInfo?, + @Json(name = "error") override val error: Error? = null, +) : AbstractPromotionResponse() { + + enum class Status(val value: String) { + @Json(name = "pending") + PENDING("pending"), + + @Json(name = "active") + ACTIVE("active"), + + @Json(name = "finished") + FINISHED("finished"), + } + + data class TokenInfo( + @Json(name = "id") val id: String, + @Json(name = "name") val name: String, + @Json(name = "symbol") val symbol: String, + @Json(name = "active") val active: Boolean, + @Json(name = "networks") val networks: List, + ) { + data class Network( + @Json(name = "networkId") val networkId: String, + @Json(name = "exchangeable") val exchangeable: Boolean, + @Json(name = "contractAddress") val contractAddress: String, + @Json(name = "decimalCount") val decimalCount: Int, + ) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateRequestBody.kt new file mode 100644 index 0000000000..090f0e0d29 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateRequestBody.kt @@ -0,0 +1,11 @@ +package com.tangem.datasource.api.promotion.models + +import com.squareup.moshi.Json + +/** +[REDACTED_AUTHOR] + */ +data class ValidateRequestBody( + @Json(name = "walletId") val walletId: String, + @Json(name = "programName") val programName: String, +) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateResponse.kt new file mode 100644 index 0000000000..97cd74645d --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateResponse.kt @@ -0,0 +1,11 @@ +package com.tangem.datasource.api.promotion.models + +import com.squareup.moshi.Json + +/** +[REDACTED_AUTHOR] + */ +data class ValidateResponse( + @Json(name = "valid") val valid: Boolean?, + @Json(name = "error") override val error: Error? = null, +) : AbstractPromotionResponse() \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt index 4f95438c33..dfbc328755 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt @@ -1,11 +1,6 @@ package com.tangem.datasource.config -import com.tangem.blockchain.common.BlockchainSdkConfig -import com.tangem.blockchain.common.BlockchairCredentials -import com.tangem.blockchain.common.GetBlockCredentials -import com.tangem.blockchain.common.NowNodeCredentials -import com.tangem.blockchain.common.QuickNodeCredentials -import com.tangem.blockchain.common.TonCenterCredentials +import com.tangem.blockchain.common.* import com.tangem.datasource.config.ConfigManager.Companion.IS_CREATING_TWIN_CARDS_ALLOWED import com.tangem.datasource.config.ConfigManager.Companion.IS_SENDING_TO_PAY_ID_ENABLED import com.tangem.datasource.config.ConfigManager.Companion.IS_TOP_UP_ENABLED @@ -111,6 +106,7 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager { zendesk = configValues.zendesk, swapReferrerAccount = configValues.swapReferrerAccount, walletConnectProjectId = configValues.walletConnectProjectId, + tangemComAuthorization = configValues.tangemComAuthorization, ) } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/models/Config.kt b/core/datasource/src/main/java/com/tangem/datasource/config/models/Config.kt index 24d8735376..0a49988295 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/models/Config.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/models/Config.kt @@ -19,4 +19,5 @@ data class Config( val zendesk: ZendeskConfig? = null, val swapReferrerAccount: SwapReferrerAccount? = null, val walletConnectProjectId: String = "", + val tangemComAuthorization: String? = null, ) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt index cd518d4c31..6bcb6f9bc9 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt @@ -38,6 +38,7 @@ class ConfigValueModel( val swapReferrerAccount: SwapReferrerAccount?, val kaspaSecondaryApiUrl: String, val walletConnectProjectId: String, + val tangemComAuthorization: String?, ) data class AppsFlyer( diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt index 4ecc2b2529..f0dbc8d7db 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt @@ -2,10 +2,12 @@ package com.tangem.datasource.di import com.squareup.moshi.Moshi import com.tangem.datasource.api.paymentology.PaymentologyApi +import com.tangem.datasource.api.promotion.PromotionApi import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.utils.RequestHeader.* import com.tangem.datasource.utils.addHeaders import com.tangem.datasource.utils.allowLogging +import com.tangem.lib.auth.AuthProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -13,6 +15,7 @@ import dagger.hilt.components.SingletonComponent import okhttp3.OkHttpClient import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory +import java.util.concurrent.TimeUnit import javax.inject.Singleton @Module @@ -54,10 +57,38 @@ class NetworkModule { .create(PaymentologyApi::class.java) } + @Provides + @Singleton + @PromotionOneInch + fun providePromotionOneInchApi(authProvider: AuthProvider, @NetworkMoshi moshi: Moshi): PromotionApi { + val okClient = OkHttpClient.Builder() + .addHeaders( + AuthenticationHeader(authProvider), + ) + .allowLogging() + .callTimeout(PromotionApi.ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS) + .connectTimeout(PromotionApi.ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS) + .readTimeout(PromotionApi.ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS) + .writeTimeout(PromotionApi.ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS) + .build() + return createBasePromotionRetrofit(okClient, moshi) + } + + private fun createBasePromotionRetrofit(okHttpClient: OkHttpClient, moshi: Moshi): PromotionApi { + return Retrofit.Builder() + .addConverterFactory(MoshiConverterFactory.create(moshi)) + .baseUrl(PROMOTION_BASE_URL) + .client(okHttpClient) + .build() + .create(PromotionApi::class.java) + } + private companion object { const val PROD_TANGEM_TECH_BASE_URL = "https://api.tangem-tech.com/v1/" const val DEV_TANGEM_TECH_BASE_URL = "https://devapi.tangem-tech.com/v1/" - private const val PAYMENTOLOGY_BASE_URL: String = "https://paymentologygate.oa.r.appspot.com/" + const val PROMOTION_BASE_URL = "https://tangem.com/v1/" + + const val PAYMENTOLOGY_BASE_URL: String = "https://paymentologygate.oa.r.appspot.com/" } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/PromotionOneInch.kt b/core/datasource/src/main/java/com/tangem/datasource/di/PromotionOneInch.kt new file mode 100644 index 0000000000..9e2d6b3981 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/PromotionOneInch.kt @@ -0,0 +1,8 @@ +package com.tangem.datasource.di + +import javax.inject.Qualifier + +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class PromotionOneInch \ No newline at end of file diff --git a/libs/auth/src/main/java/com/tangem/lib/auth/BasicAuthProvider.kt b/libs/auth/src/main/java/com/tangem/lib/auth/BasicAuthProvider.kt new file mode 100644 index 0000000000..09dcf8f302 --- /dev/null +++ b/libs/auth/src/main/java/com/tangem/lib/auth/BasicAuthProvider.kt @@ -0,0 +1,9 @@ +package com.tangem.lib.auth + +/** + * Provides auth for Tangem WebView + */ +interface BasicAuthProvider { + + fun getCredentials(): String? +} \ No newline at end of file From 7e765758b4332083ec5c16beaf58ef54d66286ee Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 19 Jun 2023 08:50:47 +0300 Subject: [PATCH 13/44] Updated on 2026-08-14 --- .../storage/UsedCardsPrefStorage.kt | 4 + features/learn2earn/impl/build.gradle.kts | 43 ++++--- .../data/DefaultLearn2earnRepository.kt | 111 ++++++++++++++++++ .../data/DefaultPreferenceStorage.kt | 20 ++++ .../data/DefaultUsedCardsPreferenceStorage.kt | 14 +++ .../data/api/Learn2earnPreferenceStorage.kt | 10 ++ .../data/api/Learn2earnRepository.kt | 20 ++++ .../data/api/UsedCardsPreferenceStorage.kt | 9 ++ .../data/di/Learn2earnDataModule.kt | 55 +++++++++ 9 files changed, 268 insertions(+), 18 deletions(-) create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultLearn2earnRepository.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultPreferenceStorage.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultUsedCardsPreferenceStorage.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/Learn2earnPreferenceStorage.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/Learn2earnRepository.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/UsedCardsPreferenceStorage.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/di/Learn2earnDataModule.kt diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/UsedCardsPrefStorage.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/UsedCardsPrefStorage.kt index b1fbf61361..de987e84fc 100644 --- a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/UsedCardsPrefStorage.kt +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/UsedCardsPrefStorage.kt @@ -68,6 +68,10 @@ class UsedCardsPrefStorage internal constructor( return cardInfo.isActivationStarted && !cardInfo.isActivationFinished } + fun hadFinishedActivation(): Boolean { + return restore().any { it.isActivationFinished } + } + private fun findCardInfo( cardId: String, list: MutableList? = null, diff --git a/features/learn2earn/impl/build.gradle.kts b/features/learn2earn/impl/build.gradle.kts index 9e390b1f67..bd20fe9bf6 100644 --- a/features/learn2earn/impl/build.gradle.kts +++ b/features/learn2earn/impl/build.gradle.kts @@ -8,38 +8,45 @@ plugins { } dependencies { - /** AndroidX */ - implementation(deps.androidx.activity.compose) - implementation(deps.androidx.core.ktx) - implementation(deps.androidx.appCompat) - implementation(deps.androidx.constraintLayout) - - /** Compose */ - implementation(deps.compose.material) - implementation(deps.compose.foundation) - implementation(deps.compose.constraintLayout) - implementation(deps.compose.ui) - implementation(deps.compose.ui.tooling) - implementation(deps.compose.animation) - implementation(deps.compose.navigation) - implementation(deps.compose.navigation.hilt) - /** Core modules */ implementation(project(":common")) implementation(project(":core:featuretoggles")) implementation(project(":core:datasource")) - implementation(project(":core:analytics")) implementation(project(":core:utils")) implementation(project(":core:ui")) implementation(project(":core:res")) + implementation(project(":data:source:preferences")) + implementation(project(":libs:auth")) + + implementation(deps.material) + + /** AndroidX */ + implementation(deps.androidx.core.ktx) + implementation(deps.androidx.appCompat) + implementation(deps.androidx.activity.compose) + implementation(deps.androidx.constraintLayout) + implementation(deps.androidx.browser) + + /** Compose */ + implementation(deps.compose.material) + implementation(deps.compose.foundation) + implementation(deps.compose.ui) + implementation(deps.compose.ui.tooling) /** Tangem libraries */ - // TODO: fixme: delete if later it doesn't needed + // TODO: 1inch: delete if later it doesn't needed // implementation(deps.tangem.card.core) // implementation(deps.tangem.card.android) { // exclude(module = "joda-time") // } + /** Network */ + implementation(deps.krateSharedPref) + implementation(deps.moshi) + implementation(deps.moshi.kotlin) + implementation(deps.retrofit) + implementation(deps.retrofit.moshi) + /** Other libraries */ implementation(deps.kotlin.immutable.collections) implementation(deps.kotlin.serialization) diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultLearn2earnRepository.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultLearn2earnRepository.kt new file mode 100644 index 0000000000..8443d28b82 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultLearn2earnRepository.kt @@ -0,0 +1,111 @@ +package com.tangem.feature.learn2earn.data + +import com.squareup.moshi.Moshi +import com.tangem.datasource.api.promotion.PromotionApi +import com.tangem.datasource.api.promotion.models.* +import com.tangem.feature.learn2earn.data.api.Learn2earnPreferenceStorage +import com.tangem.feature.learn2earn.data.api.Learn2earnRepository +import com.tangem.feature.learn2earn.data.api.UsedCardsPreferenceStorage +import timber.log.Timber + +/** +[REDACTED_AUTHOR] + */ +internal class DefaultLearn2earnRepository( + private val preferencesStorage: Learn2earnPreferenceStorage, + private val usedCardsPreferenceStorage: UsedCardsPreferenceStorage, + private val moshi: Moshi, + private val api: PromotionApi, +) : Learn2earnRepository { + + private val promotionInfoAdapter = moshi.adapter(PromotionInfoResponse::class.java) + + override fun isHadActivatedCards(): Boolean { + return usedCardsPreferenceStorage.isHadActivatedCards() + } + + override fun getPromoCode(): String? { + return preferencesStorage.promoCode + } + + override fun savePromoCode(code: String) { + preferencesStorage.promoCode = code + } + + override fun getProgramName(): String { + return PROGRAM_NAME + } + + override fun isAlreadyReceivedAward(): Boolean { + return preferencesStorage.alreadyReceivedAward + } + + override suspend fun requestAwardByCode(walletId: String, address: String): Result { + return runCatching { + api.codeAward(CodeAwardRequestBody(walletId, address, getPromoCode())) + }.fold( + onSuccess = { response -> + val alreadyReceived = response.status ?: false + preferencesStorage.alreadyReceivedAward = alreadyReceived + Result.success(alreadyReceived) + }, + onFailure = { exception -> Result.failure(exception) }, + ) + } + + override suspend fun getPromotionInfo(): Result { + val info = restorePromotionInfo() + + return if (info?.status == PromotionInfoResponse.Status.FINISHED) { + Result.success(info) + } else { + runCatching { api.getPromotionInfo(getProgramName()) } + .fold( + onSuccess = { infoResponse -> + savePromotionInfo(infoResponse) + Result.success(infoResponse) + }, + onFailure = { exception -> Result.failure(exception) }, + ) + } + } + + override suspend fun validate(walletId: String): ValidateResponse { + return api.validate(ValidateRequestBody(walletId, getProgramName())) + } + + override suspend fun award(walletId: String): AwardResponse { + return api.award(AwardRequestBody(walletId, getProgramName())) + } + + override suspend fun codeValidate(walletId: String, code: String?): CodeValidateResponse { + return api.codeValidate(CodeValidateRequestBody(walletId, code)) + } + + override suspend fun codeAward(walletId: String, address: String, code: String?): CodeAwardResponse { + return api.codeAward(CodeAwardRequestBody(walletId, address, code)) + } + + private fun restorePromotionInfo(): PromotionInfoResponse? { + return try { + preferencesStorage.promotionInfo?.let { promotionInfoAdapter.fromJson(it) } + } catch (ex: Exception) { + Timber.e(ex) + preferencesStorage.promotionInfo = null + null + } + } + + private fun savePromotionInfo(infoResponse: PromotionInfoResponse) { + try { + preferencesStorage.promotionInfo = promotionInfoAdapter.toJson(infoResponse) + } catch (ex: Exception) { + Timber.e(ex) + preferencesStorage.promotionInfo = null + } + } + + private companion object { + const val PROGRAM_NAME: String = "1inch" + } +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultPreferenceStorage.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultPreferenceStorage.kt new file mode 100644 index 0000000000..92f5d7da83 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultPreferenceStorage.kt @@ -0,0 +1,20 @@ +package com.tangem.feature.learn2earn.data + +import android.content.Context +import com.tangem.feature.learn2earn.data.api.Learn2earnPreferenceStorage +import hu.autsoft.krate.SimpleKrate +import hu.autsoft.krate.booleanPref +import hu.autsoft.krate.default.withDefault +import hu.autsoft.krate.stringPref + +/** +[REDACTED_AUTHOR] + */ +internal class DefaultPreferenceStorage( + context: Context, +) : SimpleKrate(context = context, name = "Lear2earnPromotion"), Learn2earnPreferenceStorage { + + override var promoCode: String? by stringPref().withDefault(null) + override var promotionInfo: String? by stringPref().withDefault(null) + override var alreadyReceivedAward: Boolean by booleanPref().withDefault(false) +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultUsedCardsPreferenceStorage.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultUsedCardsPreferenceStorage.kt new file mode 100644 index 0000000000..a2f3a8cab4 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/DefaultUsedCardsPreferenceStorage.kt @@ -0,0 +1,14 @@ +package com.tangem.feature.learn2earn.data + +import com.tangem.data.source.preferences.storage.UsedCardsPrefStorage +import com.tangem.feature.learn2earn.data.api.UsedCardsPreferenceStorage + +/** +[REDACTED_AUTHOR] + */ +class DefaultUsedCardsPreferenceStorage( + private val usedCardsPrefStorage: UsedCardsPrefStorage, +) : UsedCardsPreferenceStorage { + + override fun isHadActivatedCards(): Boolean = usedCardsPrefStorage.hadFinishedActivation() +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/Learn2earnPreferenceStorage.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/Learn2earnPreferenceStorage.kt new file mode 100644 index 0000000000..78f548febc --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/Learn2earnPreferenceStorage.kt @@ -0,0 +1,10 @@ +package com.tangem.feature.learn2earn.data.api + +/** +[REDACTED_AUTHOR] + */ +interface Learn2earnPreferenceStorage { + var promoCode: String? + var promotionInfo: String? + var alreadyReceivedAward: Boolean +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/Learn2earnRepository.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/Learn2earnRepository.kt new file mode 100644 index 0000000000..6641bc36c8 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/Learn2earnRepository.kt @@ -0,0 +1,20 @@ +package com.tangem.feature.learn2earn.data.api + +import com.tangem.datasource.api.promotion.models.* + +/** +[REDACTED_AUTHOR] + */ +interface Learn2earnRepository { + fun isHadActivatedCards(): Boolean + fun getPromoCode(): String? + fun savePromoCode(code: String) + fun getProgramName(): String + fun isAlreadyReceivedAward(): Boolean + suspend fun requestAwardByCode(walletId: String, address: String): Result + suspend fun getPromotionInfo(): Result + suspend fun validate(walletId: String): ValidateResponse + suspend fun award(walletId: String): AwardResponse + suspend fun codeValidate(walletId: String, code: String?): CodeValidateResponse + suspend fun codeAward(walletId: String, address: String, code: String?): CodeAwardResponse +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/UsedCardsPreferenceStorage.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/UsedCardsPreferenceStorage.kt new file mode 100644 index 0000000000..c7d1174496 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/api/UsedCardsPreferenceStorage.kt @@ -0,0 +1,9 @@ +package com.tangem.feature.learn2earn.data.api + +/** +[REDACTED_AUTHOR] + * An interface that encapsulates access to deprecated PreferencesDataSource.usedCardsPrefStorage. + */ +interface UsedCardsPreferenceStorage { + fun isHadActivatedCards(): Boolean +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/di/Learn2earnDataModule.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/di/Learn2earnDataModule.kt new file mode 100644 index 0000000000..9d6e5a0507 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/data/di/Learn2earnDataModule.kt @@ -0,0 +1,55 @@ +package com.tangem.feature.learn2earn.data.di + +import android.content.Context +import com.squareup.moshi.Moshi +import com.tangem.data.source.preferences.PreferencesDataSource +import com.tangem.datasource.api.promotion.PromotionApi +import com.tangem.datasource.di.NetworkMoshi +import com.tangem.datasource.di.PromotionOneInch +import com.tangem.feature.learn2earn.data.DefaultLearn2earnRepository +import com.tangem.feature.learn2earn.data.DefaultPreferenceStorage +import com.tangem.feature.learn2earn.data.DefaultUsedCardsPreferenceStorage +import com.tangem.feature.learn2earn.data.api.Learn2earnPreferenceStorage +import com.tangem.feature.learn2earn.data.api.Learn2earnRepository +import com.tangem.feature.learn2earn.data.api.UsedCardsPreferenceStorage +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +class Learn2earnDataModule { + + @Provides + @Singleton + fun providePreferenceStorage(@ApplicationContext context: Context): Learn2earnPreferenceStorage { + return DefaultPreferenceStorage(context) + } + + @Provides + @Singleton + fun provideDeprecatedPreferenceDataSource( + preferenceDataSource: PreferencesDataSource, + ): UsedCardsPreferenceStorage { + return DefaultUsedCardsPreferenceStorage(preferenceDataSource.usedCardsPrefStorage) + } + + @Provides + @Singleton + fun provideRepository( + preferenceStorage: Learn2earnPreferenceStorage, + usedCardsPreferenceStorage: UsedCardsPreferenceStorage, + @NetworkMoshi moshi: Moshi, + @PromotionOneInch promotionApi: PromotionApi, + ): Learn2earnRepository { + return DefaultLearn2earnRepository( + preferencesStorage = preferenceStorage, + usedCardsPreferenceStorage = usedCardsPreferenceStorage, + moshi = moshi, + api = promotionApi, + ) + } +} \ No newline at end of file From efe271e1a072b3ab3cd7b0db0fe4803d7e366c21 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 19 Jun 2023 08:51:46 +0300 Subject: [PATCH 14/44] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 2 + .../java/com/tangem/tap/TapApplication.kt | 118 +++++++++-------- .../tangem/tap/features/home/HomeFragment.kt | 15 +++ .../features/home/compose/StoriesScreen.kt | 6 +- .../tap/features/home/redux/HomeAction.kt | 2 + .../tap/features/home/redux/HomeReducer.kt | 7 + .../tap/features/home/redux/HomeState.kt | 4 +- .../welcome/redux/WelcomeMiddleware.kt | 16 +-- app/src/main/res/layout/fragment_wallet.xml | 13 +- .../java/com/tangem/utils/extensions/Any.kt | 10 ++ .../domain/DefaultLearn2earnInteractor.kt | 123 ++++++++++++++++++ .../learn2earn/domain/WebViewUriBuilder.kt | 98 ++++++++++++++ .../domain/api/Learn2earnInteractor.kt | 18 +++ .../domain/di/Learn2earnDomainModule.kt | 39 ++++++ .../learn2earn/domain/models/CardType.kt | 9 ++ .../learn2earn/domain/models/MockModels.kt | 76 +++++++++++ .../learn2earn/domain/models/Promotion.kt | 30 +++++ .../domain/models/PromotionError.kt | 15 +++ .../learn2earn/domain/models/WebViewHelper.kt | 17 +++ .../presentation/Learn2earnRouter.kt | 23 ++++ .../presentation/Learn2earnViewModel.kt | 58 +++++++++ .../presentation/Learn2earnWebViewActivity.kt | 90 +++++++++++++ ...eInchStoriesScreen.kt => StoriesScreen.kt} | 8 +- .../src/main/res/layout/activity_web_view.xml | 29 +++++ 24 files changed, 751 insertions(+), 75 deletions(-) create mode 100644 core/utils/src/main/java/com/tangem/utils/extensions/Any.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/DefaultLearn2earnInteractor.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/WebViewUriBuilder.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/api/Learn2earnInteractor.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/di/Learn2earnDomainModule.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/CardType.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/MockModels.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/Promotion.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/PromotionError.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/WebViewHelper.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnRouter.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnViewModel.kt create mode 100644 features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnWebViewActivity.kt rename features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/{OneInchStoriesScreen.kt => StoriesScreen.kt} (94%) create mode 100644 features/learn2earn/impl/src/main/res/layout/activity_web_view.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ef93ea2179..13d09bd76f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -138,6 +138,8 @@ android:name="com.tangem.tap.features.sprinklr.ui.SprinklrActivity" android:theme="@style/AppTheme" /> + + @@ -136,6 +144,9 @@ class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var walletConnectSessionsRepository: WalletConnectSessionsRepository + @Inject + lateinit var learn2ernInteractor: Learn2earnInteractor + override fun onCreate() { super.onCreate() @@ -189,8 +200,9 @@ class TapApplication : Application(), ImageLoaderFactory { appStateHolder.userTokensRepository = userTokensRepository appStateHolder.walletStoresManager = walletStoresManager - scope.launch { + runBlocking { featureTogglesManager.init() + learn2ernInteractor.init() } initTopUpController() diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index bdb1d84900..591f7b6369 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -12,26 +12,38 @@ import androidx.compose.ui.platform.ComposeView import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import com.tangem.core.analytics.Analytics import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.learn2earn.presentation.Learn2earnViewModel import com.tangem.tap.common.analytics.events.IntroductionProcess import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.features.home.compose.StoriesScreen import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.home.redux.HomeState +import com.tangem.tap.features.home.redux.Stories import com.tangem.tap.features.tokens.legacy.redux.TokensAction import com.tangem.tap.store +import dagger.hilt.android.AndroidEntryPoint import org.rekotlin.StoreSubscriber +@AndroidEntryPoint class HomeFragment : Fragment(), StoreSubscriber { private var homeState: MutableState = mutableStateOf(store.state.homeState) + private val learn2earnViewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) store.dispatch(HomeAction.OnCreate) store.dispatch(HomeAction.Init) + if (learn2earnViewModel.isNeedToShowViewOnStoriesScreen()) { + store.dispatch(HomeAction.InsertStory(position = 0, Stories.OneInchPromo)) + // re init homeState after inserting learn2earn story + homeState = mutableStateOf(store.state.homeState) + } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -79,6 +91,9 @@ class HomeFragment : Fragment(), StoreSubscriber { private fun ScreenContent() { StoriesScreen( homeState, + onLearn2earnClick = { + learn2earnViewModel.onStoriesClick() + }, onScanButtonClick = { Analytics.send(IntroductionProcess.ButtonScanCard()) store.dispatch(HomeAction.ReadCard()) diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt index af5df09c59..46ef676001 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt @@ -26,7 +26,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.learn2earn.presentation.ui.OneInchStoriesScreen +import com.tangem.feature.learn2earn.presentation.ui.StoriesScreen import com.tangem.tap.features.home.compose.content.* import com.tangem.tap.features.home.compose.views.HomeButtons import com.tangem.tap.features.home.compose.views.StoriesProgressBar @@ -39,6 +39,7 @@ import kotlin.math.max @Composable fun StoriesScreen( homeState: MutableState, + onLearn2earnClick: () -> Unit, onScanButtonClick: () -> Unit, onShopButtonClick: () -> Unit, onSearchTokensClick: () -> Unit, @@ -152,7 +153,7 @@ fun StoriesScreen( colorFilter = if (currentStory.isDarkBackground) null else ColorFilter.tint(Color.Black), ) when (currentStory) { - Stories.OneInchPromo -> OneInchStoriesScreen({}) + Stories.OneInchPromo -> StoriesScreen(onLearn2earnClick) Stories.TangemIntro -> FirstStoriesContent(isPaused, currentStory.duration) { hideContent.value = it } Stories.RevolutionaryWallet -> StoriesRevolutionaryWallet(currentStory.duration) Stories.UltraSecureBackup -> StoriesUltraSecureBackup(isPaused, currentStory.duration) @@ -215,6 +216,7 @@ fun StoriesScreen( @Composable private fun StoriesScreenPreview() { StoriesScreen( + onLearn2earnClick = {}, onScanButtonClick = {}, onShopButtonClick = {}, onSearchTokensClick = {}, diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt index bb9be2edc3..86b139defb 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt @@ -11,6 +11,8 @@ sealed class HomeAction : Action { object OnCreate : HomeAction() object Init : HomeAction() + data class InsertStory(val position: Int, val story: Stories) : HomeAction() + data class ReadCard( val analyticsEvent: AnalyticsEvent? = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Introduction), ) : HomeAction() diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt index f9f2911c32..6843253f5a 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt @@ -12,6 +12,13 @@ private fun internalReduce(action: Action, state: AppState): HomeState { var state = state.homeState when (action) { + is HomeAction.InsertStory -> { + state = state.copy( + stories = state.stories.toMutableList().apply { + add(action.position, action.story) + }, + ) + } is HomeAction.ScanInProgress -> { state = state.copy(scanInProgress = action.scanInProgress) } diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt index 9342420e6b..415deb938d 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt @@ -11,7 +11,8 @@ data class HomeState( val stories: List = initDefaultStories(), ) : StateType { - val firstStory: Stories = stories[0] + val firstStory: Stories + get() = stories[0] val btnScanStateInProgress: Boolean get() = btnScanState.progressState == ProgressState.Loading @@ -20,7 +21,6 @@ data class HomeState( companion object { fun initDefaultStories(): List = listOf( - Stories.OneInchPromo, Stories.TangemIntro, Stories.RevolutionaryWallet, Stories.UltraSecureBackup, diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index 7f22650f5e..005bcca0a4 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -1,8 +1,10 @@ package com.tangem.tap.features.welcome.redux -import android.content.Intent -import com.tangem.common.* import com.tangem.common.core.TangemSdkError +import com.tangem.common.doOnFailure +import com.tangem.common.doOnResult +import com.tangem.common.doOnSuccess +import com.tangem.common.flatMap import com.tangem.domain.models.scan.ScanResponse import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam @@ -17,7 +19,6 @@ import com.tangem.tap.domain.model.builders.UserWalletBuilder import com.tangem.tap.domain.scanCard.ScanCardProcessor import com.tangem.tap.domain.userWalletList.unlockIfLockable import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler -import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayActivationError import com.tangem.tap.features.signin.redux.SignInAction import kotlinx.coroutines.launch import org.rekotlin.Middleware @@ -98,15 +99,6 @@ internal class WelcomeMiddleware { } } - private fun handleInitialIntent(intent: Intent?) { - val isBackgroundScanNotHandled = !intentHandler.handleBackgroundScan(intent, hasSavedUserWallets = true) - val hasNotIncompletedBackup = !backupService.hasIncompletedBackup - - if (isBackgroundScanNotHandled && hasNotIncompletedBackup) { - store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics) - } - } - private suspend inline fun scanCardInternal(crossinline onCardScanned: suspend (ScanResponse) -> Unit) { tangemSdkManager.setAccessCodeRequestPolicy( useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes, diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml index b4287625d4..ac04bb12dd 100644 --- a/app/src/main/res/layout/fragment_wallet.xml +++ b/app/src/main/res/layout/fragment_wallet.xml @@ -46,8 +46,8 @@ android:id="@+id/cl_wallet" android:layout_width="match_parent" android:layout_height="wrap_content" - android:clipToPadding="false" android:clipChildren="false" + android:clipToPadding="false" android:paddingBottom="92dp"> + + T.toWeakReference(): WeakReference { + return WeakReference(this) +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/DefaultLearn2earnInteractor.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/DefaultLearn2earnInteractor.kt new file mode 100644 index 0000000000..fcb7c48add --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/DefaultLearn2earnInteractor.kt @@ -0,0 +1,123 @@ +package com.tangem.feature.learn2earn.domain + +import android.net.Uri +import com.tangem.datasource.api.promotion.models.PromotionInfoResponse +import com.tangem.feature.learn2earn.data.api.Learn2earnRepository +import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor +import com.tangem.feature.learn2earn.domain.models.CardType +import com.tangem.feature.learn2earn.domain.models.Promotion +import com.tangem.feature.learn2earn.domain.models.PromotionError +import com.tangem.feature.learn2earn.domain.models.PromotionError.Companion.toDomainError +import com.tangem.feature.learn2earn.domain.models.RedirectConsequences +import com.tangem.lib.auth.BasicAuthProvider + +/** +[REDACTED_AUTHOR] + */ +class DefaultLearn2earnInteractor( + private val repository: Learn2earnRepository, + basicAuthProvider: BasicAuthProvider, + userCountryCodeProvider: () -> String, +) : Learn2earnInteractor { + + private val webViewUriBuilder = WebViewUriBuilder( + basicAuthProvider = basicAuthProvider, + userCountryCodeProvider = userCountryCodeProvider, + promoCodeProvider = { repository.getPromoCode() }, + promoNameProvider = { repository.getProgramName() }, + ) + + private lateinit var promotion: Promotion + + override suspend fun init() { + initPromotionInfo() + } + + override fun isNeedToShowViewOnStoriesScreen(): Boolean { + return promotionIsActive() + } + + override fun getBasicAuthHeaders(): Map { + return webViewUriBuilder.getBasicAuthHeaders() + } + + override suspend fun isNeedToShowViewOnWalletScreen(walletId: String): Boolean { + val response = repository.validate(walletId) + + return if (response.valid == false && response.isError()) { + false + } else { + promotionIsActive() + } + } + + override fun buildUriForStories(): Uri { + val type = if (repository.isHadActivatedCards()) { + CardType.NEW + } else { + CardType.EXISTED + } + + return webViewUriBuilder.buildUriForStories(type) + } + + override fun buildUriForMainPage(walletId: String, cardId: String, cardPubKey: String): Uri { + return webViewUriBuilder.buildUriForMainPage(walletId, cardId, cardPubKey) + } + + override fun handleWebViewRedirect(uri: Uri): RedirectConsequences { + if (webViewUriBuilder.isReadyForExistedCardAwardRedirect(uri)) { + return RedirectConsequences.FINISH_SESSION + } + + return if (webViewUriBuilder.isPromoCodeRedirect(uri)) { + webViewUriBuilder.extractPromoCode(uri)?.let { repository.savePromoCode(it) } + RedirectConsequences.NOTHING + } else { + RedirectConsequences.PROCEED + } + } + + private fun promotionIsActive(): Boolean { + val isActive = when { + repository.isAlreadyReceivedAward() -> false + promotion.isError() -> false + else -> promotion.getInfo().status == PromotionInfoResponse.Status.ACTIVE + } + + return isActive + } + + private suspend fun initPromotionInfo() { + // return Promotion.dummyActive() + + promotion = repository.getPromotionInfo() + .fold( + onSuccess = { response -> + val responseError = response.error + if (responseError == null) { + Promotion( + info = Promotion.PromotionInfo( + status = response.status!!, + awardForNewCard = response.awardForNewCard!!, + awardForOldCard = response.awardForOldCard!!, + awardPaymentToken = response.awardPaymentToken!!, + ), + error = null, + ) + } else { + Promotion( + info = null, + error = responseError.toDomainError(), + ) + } + }, + onFailure = { + Promotion( + info = null, + error = PromotionError.NetworkUnreachable, + ) + }, + ) + } +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/WebViewUriBuilder.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/WebViewUriBuilder.kt new file mode 100644 index 0000000000..c70e983a15 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/WebViewUriBuilder.kt @@ -0,0 +1,98 @@ +package com.tangem.feature.learn2earn.domain + +import android.net.Uri +import com.tangem.feature.learn2earn.domain.models.CardType +import com.tangem.feature.learn2earn.impl.BuildConfig +import com.tangem.lib.auth.BasicAuthProvider + +/** +[REDACTED_AUTHOR] + */ +class WebViewUriBuilder( + private val basicAuthProvider: BasicAuthProvider, + private val userCountryCodeProvider: () -> String, + private val promoCodeProvider: () -> String?, + private val promoNameProvider: () -> String, +) { + + fun buildUriForStories(type: CardType): Uri { + val builder = makeWebViewUriBuilder() + .appendQueryParameter("type", type.typeName) + + promoCodeProvider.invoke()?.let { + builder.appendQueryParameter("code", it) + } + + return builder.build() + } + + fun buildUriForMainPage(walletId: String, cardId: String, cardPubKey: String): Uri { + val builder = makeWebViewUriBuilder() + .appendQueryParameter("type", CardType.EXISTED.typeName) + .appendQueryParameter("cardPublicKey", cardPubKey) + .appendQueryParameter("cardId", cardId) + .appendQueryParameter("walletId", walletId) + .appendQueryParameter("programName", promoNameProvider.invoke()) + + promoCodeProvider.invoke()?.let { + builder.appendQueryParameter("code", it) + } + + return builder.build() + } + + fun getBasicAuthHeaders(): Map { + return basicAuthProvider.getCredentials()?.let { + mapOf("Authorization" to "Basic $it") + } ?: mapOf() + } + + fun isPromoCodeRedirect(uri: Uri): Boolean { + return uri.toString().contains(SUFFIX_CODE_CREATED) + } + + fun isReadyForExistedCardAwardRedirect(uri: Uri): Boolean { + return uri.toString().endsWith(SUFFIX_READY_FOR_AWARD) + } + + fun extractPromoCode(uri: Uri): String? { + val url = uri.toString() + + return if (isPromoCodeRedirect(uri)) { + val replaceString = makeWebViewUriBuilder().build().toString() + SUFFIX_CODE_CREATED + val code = url.replace(replaceString, "") + code + } else { + null + } + } + + private fun makeWebViewUriBuilder(): Uri.Builder { + val builder = Uri.Builder() + builder.scheme("https") + + if (BuildConfig.DEBUG) { + builder.authority(DEV_WEB_VIEW_BASE_URL) + builder.appendPath(userCountryCodeProvider.invoke()) + builder.appendPath("promotion-test") + } else { + builder.authority(WEB_VIEW_BASE_URL) + builder.appendPath(userCountryCodeProvider.invoke()) + builder.appendPath("promotion") + } + + return builder + } + + private companion object { + const val WEB_VIEW_BASE_URL = "tangem.com" + + const val DEV_WEB_VIEW_BASE_URL = "devweb.tangem.com" + const val DEV_WEB_VIEW_BASIC_AUTH = "Basic dGFuZ2VtOnRhbmdlbWRldjc=" + // TODO: 1inch: replace by invalid auth + // const val DEV_WEB_VIEW_BASIC_AUTH = "Basic paste valid value" + + const val SUFFIX_CODE_CREATED = "/code-created?code=" + const val SUFFIX_READY_FOR_AWARD = "/ready-for-exsited-card-award" + } +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/api/Learn2earnInteractor.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/api/Learn2earnInteractor.kt new file mode 100644 index 0000000000..4aa046c5e3 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/api/Learn2earnInteractor.kt @@ -0,0 +1,18 @@ +package com.tangem.feature.learn2earn.domain.api + +import android.net.Uri +import com.tangem.feature.learn2earn.domain.models.RedirectConsequences + +/** +[REDACTED_AUTHOR] + */ +interface Learn2earnInteractor { + + suspend fun init() + fun isNeedToShowViewOnStoriesScreen(): Boolean + suspend fun isNeedToShowViewOnWalletScreen(walletId: String): Boolean + fun getBasicAuthHeaders(): Map + fun buildUriForStories(): Uri + fun buildUriForMainPage(walletId: String, cardId: String, cardPubKey: String): Uri + fun handleWebViewRedirect(uri: Uri): RedirectConsequences +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/di/Learn2earnDomainModule.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/di/Learn2earnDomainModule.kt new file mode 100644 index 0000000000..d33f81422f --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/di/Learn2earnDomainModule.kt @@ -0,0 +1,39 @@ +package com.tangem.feature.learn2earn.domain.di + +import android.content.Context +import com.tangem.feature.learn2earn.data.api.Learn2earnRepository +import com.tangem.feature.learn2earn.domain.DefaultLearn2earnInteractor +import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor +import com.tangem.feature.learn2earn.presentation.Learn2earnRouter +import com.tangem.lib.auth.BasicAuthProvider +import com.tangem.utils.extensions.toWeakReference +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +class Learn2earnDomainModule { + + @Provides + @Singleton + fun provideRouter(@ApplicationContext context: Context): Learn2earnRouter { + return Learn2earnRouter(context.toWeakReference()) + } + + @Provides + @Singleton + fun provideInteractor( + repository: Learn2earnRepository, + basicAuthProvider: BasicAuthProvider, + ): Learn2earnInteractor { + return DefaultLearn2earnInteractor( + repository = repository, + basicAuthProvider = basicAuthProvider, + userCountryCodeProvider = { "ru" }, + ) + } +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/CardType.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/CardType.kt new file mode 100644 index 0000000000..1e05115373 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/CardType.kt @@ -0,0 +1,9 @@ +package com.tangem.feature.learn2earn.domain.models + +/** +[REDACTED_AUTHOR] + */ +enum class CardType(val typeName: String) { + NEW("new-card"), + EXISTED("existed-card"), +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/MockModels.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/MockModels.kt new file mode 100644 index 0000000000..340de5e151 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/MockModels.kt @@ -0,0 +1,76 @@ +package com.tangem.feature.learn2earn.domain.models + +import com.tangem.datasource.api.promotion.models.PromotionInfoResponse + +/** +[REDACTED_AUTHOR] + */ +internal fun Promotion.Companion.dummyPending(): Promotion { + return Promotion( + info = Promotion.PromotionInfo.dummy().copy( + status = PromotionInfoResponse.Status.PENDING, + ), + error = null, + ) +} + +internal fun Promotion.Companion.dummyActive(): Promotion { + return Promotion( + info = Promotion.PromotionInfo.dummy().copy( + status = PromotionInfoResponse.Status.ACTIVE, + ), + error = null, + ) +} + +internal fun Promotion.Companion.dummyFinished(): Promotion { + return Promotion( + info = Promotion.PromotionInfo.dummy().copy( + status = PromotionInfoResponse.Status.FINISHED, + ), + error = null, + ) +} + +internal fun Promotion.Companion.dummyError(): Promotion { + return Promotion( + info = null, + error = PromotionError.Error( + code = 105, + description = "any", + ), + ) +} + +internal fun Promotion.Companion.dummyErrorUnreachable(): Promotion { + return Promotion( + info = null, + error = PromotionError.NetworkUnreachable, + ) +} + +private fun Promotion.PromotionInfo.Companion.dummy(): Promotion.PromotionInfo { + return Promotion.PromotionInfo( + status = PromotionInfoResponse.Status.ACTIVE, + awardForNewCard = 10f, + awardForOldCard = 5.5f, + awardPaymentToken = Promotion.tokenInfo(), + ) +} + +private fun Promotion.Companion.tokenInfo(): PromotionInfoResponse.TokenInfo { + return PromotionInfoResponse.TokenInfo( + id = "1inch", + name = "1inch", + symbol = "1INCH", + active = true, + networks = listOf( + PromotionInfoResponse.TokenInfo.Network( + networkId = "polygon-pos", + exchangeable = false, + contractAddress = "0x9c2c5fd7b07e95ee044ddeba0e97a665f142394f", + decimalCount = 1, + ), + ), + ) +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/Promotion.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/Promotion.kt new file mode 100644 index 0000000000..e5b071a15d --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/Promotion.kt @@ -0,0 +1,30 @@ +package com.tangem.feature.learn2earn.domain.models + +import com.tangem.datasource.api.promotion.models.PromotionInfoResponse + +/** +[REDACTED_AUTHOR] + */ +data class Promotion( + val info: PromotionInfo?, + val error: PromotionError?, +) { + + @Throws(NullPointerException::class) + fun getInfo(): PromotionInfo = info!! + + fun isError(): Boolean = error != null + + fun isUnreachable(): Boolean = error == PromotionError.NetworkUnreachable + + data class PromotionInfo( + val status: PromotionInfoResponse.Status, + val awardForNewCard: Float, + val awardForOldCard: Float, + val awardPaymentToken: PromotionInfoResponse.TokenInfo, + ) { + companion object + } + + companion object +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/PromotionError.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/PromotionError.kt new file mode 100644 index 0000000000..8cd24cfa2c --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/PromotionError.kt @@ -0,0 +1,15 @@ +package com.tangem.feature.learn2earn.domain.models + +import com.tangem.datasource.api.promotion.models.AbstractPromotionResponse + +/** +[REDACTED_AUTHOR] + */ +sealed class PromotionError(val code: Int, val description: String) { + object NetworkUnreachable : PromotionError(-1, "Network or service is unreachable") + class Error(code: Int, description: String) : PromotionError(code, description) + + companion object { + fun AbstractPromotionResponse.Error.toDomainError(): PromotionError = Error(code, description) + } +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/WebViewHelper.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/WebViewHelper.kt new file mode 100644 index 0000000000..0075b17435 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/models/WebViewHelper.kt @@ -0,0 +1,17 @@ +package com.tangem.feature.learn2earn.domain.models + +import android.net.Uri + +/** +[REDACTED_AUTHOR] + */ +interface WebViewHelper { + fun handleWebViewRedirect(uri: Uri): RedirectConsequences + fun getWebViewHeaders(): Map +} + +enum class RedirectConsequences { + NOTHING, + PROCEED, + FINISH_SESSION, +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnRouter.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnRouter.kt new file mode 100644 index 0000000000..0c6c1a7e34 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnRouter.kt @@ -0,0 +1,23 @@ +package com.tangem.feature.learn2earn.presentation + +import android.content.Context +import android.content.Intent +import timber.log.Timber +import java.lang.ref.WeakReference + +/** +[REDACTED_AUTHOR] + */ +class Learn2earnRouter( + private val wContext: WeakReference, +) { + + fun openWebView() { + val context = wContext.get() + if (context == null) { + Timber.e("Can't open the Learn2earnWebViewActivity") + } else { + context.startActivity(Intent(context, Learn2earnWebViewActivity::class.java)) + } + } +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnViewModel.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnViewModel.kt new file mode 100644 index 0000000000..f39aee6032 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnViewModel.kt @@ -0,0 +1,58 @@ +package com.tangem.feature.learn2earn.presentation + +import android.net.Uri +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor +import com.tangem.feature.learn2earn.domain.models.RedirectConsequences +import com.tangem.feature.learn2earn.domain.models.WebViewHelper +import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import javax.inject.Inject + +/** +[REDACTED_AUTHOR] + */ +@HiltViewModel +class Learn2earnViewModel @Inject constructor( + private val interactor: Learn2earnInteractor, + private val router: Learn2earnRouter, + private val dispatchers: AppCoroutineDispatcherProvider, +) : ViewModel(), WebViewHelper { + + var webViewUri: Uri = Uri.parse("https://localhost") + private set + + fun isNeedToShowViewOnStoriesScreen(): Boolean = interactor.isNeedToShowViewOnStoriesScreen() + + fun onStoriesClick() { + webViewUri = interactor.buildUriForStories() + router.openWebView() + } + + fun isNeedToShowViewOnWalletScreen(walletId: String, callback: (Boolean) -> Unit) { + viewModelScope.launch(dispatchers.io) { + val isNeedToShow = interactor.isNeedToShowViewOnWalletScreen(walletId) + withContext(dispatchers.main) { + callback(isNeedToShow) + } + } + } + + fun onMainPageClick(walletId: String, cardId: String, cardPubKey: String) { + webViewUri = interactor.buildUriForMainPage(walletId, cardId, cardPubKey) + router.openWebView() + } + + // region WebViewHelper + override fun handleWebViewRedirect(uri: Uri): RedirectConsequences { + return interactor.handleWebViewRedirect(uri) + } + + override fun getWebViewHeaders(): Map { + return interactor.getBasicAuthHeaders() + } + // endregion WebViewHelper +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnWebViewActivity.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnWebViewActivity.kt new file mode 100644 index 0000000000..c054b21ef0 --- /dev/null +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/Learn2earnWebViewActivity.kt @@ -0,0 +1,90 @@ +package com.tangem.feature.learn2earn.presentation + +import android.os.Bundle +import android.view.MenuItem +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.activity.viewModels +import androidx.appcompat.app.ActionBar +import androidx.appcompat.app.AppCompatActivity +import com.tangem.feature.learn2earn.domain.models.RedirectConsequences +import com.tangem.feature.learn2earn.domain.models.WebViewHelper +import com.tangem.feature.learn2earn.impl.R +import dagger.hilt.android.AndroidEntryPoint + +/** +[REDACTED_AUTHOR] + */ +@AndroidEntryPoint +class Learn2earnWebViewActivity : AppCompatActivity() { + + private val learn2earnViewModel by viewModels() + + private lateinit var actionBar: ActionBar + private lateinit var webView: WebView + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_web_view) + + setSupportActionBar(findViewById(R.id.toolbar)) + actionBar = supportActionBar!! + actionBar.setDisplayHomeAsUpEnabled(true) + actionBar.setDisplayShowHomeEnabled(true) + actionBar.title = learn2earnViewModel.webViewUri.authority + + webView = findViewById(R.id.web_view) + webView.settings.javaScriptEnabled = true + webView.settings.domStorageEnabled = true + webView.settings.loadsImagesAutomatically = true + + webView.webViewClient = Learn2earnWebViewClient( + helper = learn2earnViewModel, + finishSessionHandler = { finish() }, + ) + webView.loadUrl( + learn2earnViewModel.webViewUri.toString(), + learn2earnViewModel.getWebViewHeaders(), + ) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + android.R.id.home -> { + finish() + true + } + else -> super.onOptionsItemSelected(item) + } + } + + override fun onDestroy() { + webView.clearHistory() + webView.clearCache(true) + webView.destroy() + + super.onDestroy() + } +} + +private class Learn2earnWebViewClient( + private val helper: WebViewHelper, + private val finishSessionHandler: () -> Unit, +) : WebViewClient() { + + override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { + when (helper.handleWebViewRedirect(request.url)) { + RedirectConsequences.NOTHING -> Unit + RedirectConsequences.PROCEED -> { + val headers = helper.getWebViewHeaders().toMutableMap() + request.requestHeaders?.let { headers.putAll(it) } + view.loadUrl(request.url.toString(), headers) + } + RedirectConsequences.FINISH_SESSION -> { + finishSessionHandler.invoke() + } + } + return true + } +} \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/OneInchStoriesScreen.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/StoriesScreen.kt similarity index 94% rename from features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/OneInchStoriesScreen.kt rename to features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/StoriesScreen.kt index ff3c3218b7..74f3584e6b 100644 --- a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/OneInchStoriesScreen.kt +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/StoriesScreen.kt @@ -24,9 +24,9 @@ import com.tangem.feature.learn2earn.presentation.ui.component.GradientCircle /** [REDACTED_AUTHOR] */ -// TODO: fixme: make function as internal after adding feature interface +// TODO: 1inch: make function as internal after adding feature interface @Composable -fun OneInchStoriesScreen(onLearnClick: () -> Unit, modifier: Modifier = Modifier) { +fun StoriesScreen(onLearnClick: () -> Unit, modifier: Modifier = Modifier) { Box(modifier = modifier.fillMaxSize()) { ContentBackground( modifier = Modifier @@ -111,7 +111,7 @@ private fun OneInchStoriesContentPreview_Light() { TangemTheme( isDark = false, ) { - OneInchStoriesScreen( + StoriesScreen( onLearnClick = {}, ) } @@ -123,7 +123,7 @@ private fun OneInchStoriesContentPreview_Dark() { TangemTheme( isDark = true, ) { - OneInchStoriesScreen( + StoriesScreen( onLearnClick = {}, ) } diff --git a/features/learn2earn/impl/src/main/res/layout/activity_web_view.xml b/features/learn2earn/impl/src/main/res/layout/activity_web_view.xml new file mode 100644 index 0000000000..46968b0a14 --- /dev/null +++ b/features/learn2earn/impl/src/main/res/layout/activity_web_view.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file From b87a0ae96b1ac0ad6820520a758caac383b372aa Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 21 Jun 2023 13:25:40 +0300 Subject: [PATCH 15/44] Updated on 2026-08-14 --- .idea/codeStyles/Project.xml | 3 + .../java/com/tangem/tap/TapApplication.kt | 2 + .../network/auth/TangemBasicAuthProvider.kt | 7 +- .../tangem/tap/network/auth/di/AuthModule.kt | 3 +- .../datasource/api/promotion/PromotionApi.kt | 23 ++----- .../models/AbstractPromotionResponse.kt | 4 +- .../com/tangem/datasource/di/NetworkModule.kt | 9 +-- .../data/DefaultLearn2earnRepository.kt | 65 +++++++++---------- .../data/DefaultPreferenceStorage.kt | 2 + .../data/api/Learn2earnPreferenceStorage.kt | 3 + .../data/api/Learn2earnRepository.kt | 17 +++-- .../data/di/Learn2earnDataModule.kt | 3 + .../domain/DefaultLearn2earnInteractor.kt | 2 - .../learn2earn/domain/WebViewUriBuilder.kt | 4 +- .../domain/api/Learn2earnInteractor.kt | 6 ++ .../domain/di/Learn2earnDomainModule.kt | 4 +- .../presentation/Learn2earnRouter.kt | 11 +--- .../com/tangem/lib/auth/BasicAuthProvider.kt | 2 +- 18 files changed, 91 insertions(+), 79 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 0db8c6d7c9..ac3eeb6f5d 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -13,6 +13,9 @@ + - +