From 0570617415761e3f7f7a108c6f4f3617291b9e27 Mon Sep 17 00:00:00 2001 From: Andrey Sorokin <sorokin0andrey@gmail.com> Date: Wed, 26 Apr 2023 04:51:23 +0600 Subject: [PATCH 01/45] add tonApiV2Key (#340) --- packages/mobile/src/shared/constants/config.ts | 2 +- packages/mobile/src/shared/constants/serverConfig.ts | 2 ++ packages/mobile/src/store/zustand/staking/useStakingStore.ts | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/mobile/src/shared/constants/config.ts b/packages/mobile/src/shared/constants/config.ts index 0ca7f8305..da977517e 100644 --- a/packages/mobile/src/shared/constants/config.ts +++ b/packages/mobile/src/shared/constants/config.ts @@ -1,6 +1,6 @@ export const TEST = 1; -export const ServerConfigVersion = 2; +export const ServerConfigVersion = 3; export const GOOGLE_PACKAGE_NAME = 'com.ton_keeper'; export const APPLE_STORE_ID = '1587742107'; diff --git a/packages/mobile/src/shared/constants/serverConfig.ts b/packages/mobile/src/shared/constants/serverConfig.ts index 57f9c68e0..420bbdfc4 100644 --- a/packages/mobile/src/shared/constants/serverConfig.ts +++ b/packages/mobile/src/shared/constants/serverConfig.ts @@ -18,6 +18,7 @@ export interface ServerConfig { subscriptionsHost: string; tonapiTestnetHost: string; tonApiKey: string; + tonApiV2Key: string; cachedMediaEndpoint: string; cachedMediaKey: string; cachedMediaSalt: string; @@ -48,6 +49,7 @@ export function setServerConfig(data: any, isTestnet: boolean) { tonNFTsMarketplaceEndpoint: data.tonNFTsMarketplaceEndpoint, tonapiIOEndpoint: data.tonapiIOEndpoint || 'https://keeper.tonapi.io', tonApiKey: data.tonApiKey, + tonApiV2Key: data.tonApiV2Key, subscriptionsHost: data.subscriptionsHost || 'https://api.tonkeeper.com', tonapiMainnetHost: data.tonapiMainnetHost || 'https://tonapi.io', tonapiTestnetHost: data.tonapiTestnetHost || 'https://testnet.tonapi.io', diff --git a/packages/mobile/src/store/zustand/staking/useStakingStore.ts b/packages/mobile/src/store/zustand/staking/useStakingStore.ts index 1262ab62b..f23c769b3 100644 --- a/packages/mobile/src/store/zustand/staking/useStakingStore.ts +++ b/packages/mobile/src/store/zustand/staking/useStakingStore.ts @@ -1,4 +1,4 @@ -import { KNOWN_STAKING_IMPLEMENTATIONS } from '$shared/constants'; +import { KNOWN_STAKING_IMPLEMENTATIONS, getServerConfig } from '$shared/constants'; import { store } from '$store'; import { i18n } from '$translation'; import { calculatePoolBalance } from '$utils'; @@ -13,8 +13,9 @@ import { IStakingStore, StakingApiStatus, StakingInfo, StakingProvider } from '. const getStakingApi = () => { return new StakingApi( new Configuration({ - basePath: 'https://tonapi.io', // TODO: remove that hardcode when staking API will be available on tonkeeper endpoint + basePath: getServerConfig('tonapiIOEndpoint'), headers: { + Authorization: `Bearer ${getServerConfig('tonApiV2Key')}`, 'Accept-Language': i18n.locale, }, }), From df6f87b8c89d61f7d968e542dd1799694df10dab Mon Sep 17 00:00:00 2001 From: Andrey Sorokin <sorokin0andrey@gmail.com> Date: Wed, 26 Apr 2023 05:31:39 +0600 Subject: [PATCH 02/45] Bump(mobile): 3.1 (313) --- packages/mobile/android/app/build.gradle | 2 +- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index d689b9293..f10def676 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -123,7 +123,7 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 312 + versionCode 313 versionName "3.1" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 678f2c850..ca34ff927 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 312; + CURRENT_PROJECT_VERSION = 313; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1253,7 +1253,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 312; + CURRENT_PROJECT_VERSION = 313; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; From 6bc180241285992c17da8c010feef6487b940854 Mon Sep 17 00:00:00 2001 From: Andrey Sorokin <sorokin0andrey@gmail.com> Date: Wed, 17 May 2023 18:32:29 +0600 Subject: [PATCH 03/45] hotfix: bounce pool address (#361) --- packages/mobile/android/app/build.gradle | 4 ++-- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 8 ++++---- packages/mobile/src/core/StakingSend/utils.ts | 6 ++++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index f10def676..8809ecd4a 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -123,8 +123,8 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 313 - versionName "3.1" + versionCode 318 + versionName "3.1.2" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index ca34ff927..bb1699259 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 313; + CURRENT_PROJECT_VERSION = 318; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1228,7 +1228,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.1; + MARKETING_VERSION = 3.1.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1253,7 +1253,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 313; + CURRENT_PROJECT_VERSION = 318; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; @@ -1262,7 +1262,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.1; + MARKETING_VERSION = 3.1.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/packages/mobile/src/core/StakingSend/utils.ts b/packages/mobile/src/core/StakingSend/utils.ts index b4b9c1cee..a7aca0c92 100644 --- a/packages/mobile/src/core/StakingSend/utils.ts +++ b/packages/mobile/src/core/StakingSend/utils.ts @@ -1,5 +1,5 @@ import { SignRawMessage } from '$core/ModalContainer/NFTOperations/TXRequest.types'; -import { Ton } from '$libs/Ton'; +import { Address, Ton } from '$libs/Ton'; import { encodeBytes } from '$utils/base64'; import { PoolInfo } from '@tonkeeper/core'; import BN from 'bn.js'; @@ -39,6 +39,8 @@ export const getStakeSignRawMessage = async ( ): Promise<SignRawMessage> => { const withdrawalFee = getWithdrawalFee(pool); + const address = new Address(pool.address).format({ bounce: true }); + if (pool.implementation === 'whales') { const payload = transactionType === StakingTransactionType.DEPOSIT @@ -46,7 +48,7 @@ export const getStakeSignRawMessage = async ( : await createWhalesWithdrawStakeCell(isWithdrawAll ? Ton.toNano(0) : amount); return { - address: pool.address, + address, amount: transactionType === StakingTransactionType.DEPOSIT ? amount : withdrawalFee, payload, }; From 93a92b6de148c9f6466b72d24ccbf4999ddc9367 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Wed, 17 May 2023 20:01:34 +0400 Subject: [PATCH 04/45] fix(android): crash --- .../app/src/main/java/com/ton_keeper/TonkeeperActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile/android/app/src/main/java/com/ton_keeper/TonkeeperActivity.kt b/packages/mobile/android/app/src/main/java/com/ton_keeper/TonkeeperActivity.kt index 545ec11f5..710ca5010 100644 --- a/packages/mobile/android/app/src/main/java/com/ton_keeper/TonkeeperActivity.kt +++ b/packages/mobile/android/app/src/main/java/com/ton_keeper/TonkeeperActivity.kt @@ -28,7 +28,7 @@ class TonkeeperActivity : AppCompatActivity(), override fun onCreate(savedInstanceState: Bundle?) { setTheme(R.style.AppTheme) - super.onCreate(savedInstanceState) + super.onCreate(null) setContentView(R.layout.activity_tonkeeper) setRootFragment() From 82acbd83d58a2bb6181e7f5c39b9cd1ea311c76d Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Sun, 13 Aug 2023 00:07:34 +0300 Subject: [PATCH 05/45] fix: build google play release without sensitive module --- packages/mobile/package.json | 1 + patches/google-play-release.patch | 44 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 patches/google-play-release.patch diff --git a/packages/mobile/package.json b/packages/mobile/package.json index a4771c905..7e4f6ede1 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -13,6 +13,7 @@ "build:android": "cd android && ./gradlew assembleRelease && ./gradlew bundleRelease && cd .. && node scripts/prepare_builds.js", "build:android:apk": "cd android && ./gradlew assembleRelease && cd ..", "build:android:apk-site": "ENVFILE=.env.site yarn build:android:apk", + "build:android:google-play": "git apply ../../patches/google-play-release.patch && yarn build:android:apk && git apply -R ../../patches/google-play-release.patch", "clean:android": "cd android && ./gradlew clean && cd ..", "open_build_folder": "open ./android/app/build/outputs/bundle", "postinstall": "expo-yarn-workspaces postinstall && patch-package", diff --git a/patches/google-play-release.patch b/patches/google-play-release.patch new file mode 100644 index 000000000..7b1f863dd --- /dev/null +++ b/patches/google-play-release.patch @@ -0,0 +1,44 @@ +From a28b5714aabe8261b2f605caa55de30a6836656f Mon Sep 17 00:00:00 2001 +From: Max Voloshinskii <me@voloshinskii.ru> +Date: Thu, 10 Aug 2023 05:02:20 +0400 +Subject: [PATCH] patch + +--- + packages/mobile/android/app/src/main/AndroidManifest.xml | 4 +++- + packages/mobile/react-native.config.js | 8 ++++++++ + 2 files changed, 11 insertions(+), 1 deletion(-) + +diff --git a/packages/mobile/android/app/src/main/AndroidManifest.xml b/packages/mobile/android/app/src/main/AndroidManifest.xml +index 24d427b9..995a16d0 100644 +--- a/packages/mobile/android/app/src/main/AndroidManifest.xml ++++ b/packages/mobile/android/app/src/main/AndroidManifest.xml +@@ -8,7 +8,9 @@ + <package android:name="com.github.android" /> + </queries> + +- <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> ++ <!-- ++ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> ++ --> + <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> +diff --git a/packages/mobile/react-native.config.js b/packages/mobile/react-native.config.js +index d493c3ec..f45c7606 100644 +--- a/packages/mobile/react-native.config.js ++++ b/packages/mobile/react-native.config.js +@@ -1,4 +1,12 @@ + module.exports = { ++ dependencies: { ++ 'react-native-apk-install': { ++ platforms: { ++ android: null, ++ ios: null, ++ }, ++ }, ++ }, + project: { + ios: {}, + android: {}, +-- +2.39.2 (Apple Git-143) \ No newline at end of file From 33bf9275f4c27ac45d25b145f2532537c88b79c7 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Sun, 13 Aug 2023 00:08:18 +0300 Subject: [PATCH 06/45] bump(mobile): 3.3.1 351 --- packages/mobile/android/app/build.gradle | 4 ++-- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 5c285e437..397d3064e 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -123,8 +123,8 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 341 - versionName "3.3" + versionCode 351 + versionName "3.3.1" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 01fddd0c7..32911f70f 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 341; + CURRENT_PROJECT_VERSION = 351; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1228,7 +1228,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.3; + MARKETING_VERSION = 3.3.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1253,7 +1253,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 341; + CURRENT_PROJECT_VERSION = 351; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; @@ -1262,7 +1262,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.3; + MARKETING_VERSION = 3.3.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", From f2a18c615c329e23b309af910007760e1d1b7747 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Sun, 13 Aug 2023 00:11:36 +0300 Subject: [PATCH 07/45] fix: script --- packages/mobile/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 7e4f6ede1..fbfac18e4 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -13,7 +13,7 @@ "build:android": "cd android && ./gradlew assembleRelease && ./gradlew bundleRelease && cd .. && node scripts/prepare_builds.js", "build:android:apk": "cd android && ./gradlew assembleRelease && cd ..", "build:android:apk-site": "ENVFILE=.env.site yarn build:android:apk", - "build:android:google-play": "git apply ../../patches/google-play-release.patch && yarn build:android:apk && git apply -R ../../patches/google-play-release.patch", + "build:android:google-play": "git apply ../../patches/google-play-release.patch && yarn build:android && git apply -R ../../patches/google-play-release.patch", "clean:android": "cd android && ./gradlew clean && cd ..", "open_build_folder": "open ./android/app/build/outputs/bundle", "postinstall": "expo-yarn-workspaces postinstall && patch-package", From c516ecd710c5fc0e5573f234f5ba24ba279af100 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Mon, 28 Aug 2023 23:27:26 +0300 Subject: [PATCH 08/45] feat(mobile): renew all domains --- .../src/assets/icons/png/ic-globe-28@4x.png | Bin 0 -> 2987 bytes .../src/assets/icons/svg/28/ic-globe-28.svg | 5 + .../ApprovalCell/components/ApprovalCell.tsx | 4 +- packages/mobile/src/navigation/ModalStack.tsx | 2 + .../mobile/src/store/zustand/domains/types.ts | 39 ++++ .../zustand/domains/useExpiringDomains.ts | 20 +- .../src/tabs/Wallet/RenewAllDomainModal.tsx | 174 ++++++++++++++++++ .../mobile/src/tabs/Wallet/WalletScreen.tsx | 37 ++-- .../Wallet/components/ExpiringDomainCell.tsx | 61 ++++++ .../mobile/src/translation/locales/en.json | 9 + .../mobile/src/translation/locales/ru.json | 9 + .../mobile/src/uikit/Icon/IconsMobileList.ts | 1 + .../mobile/src/uikit/Icon/generated.types.ts | 3 + packages/mobile/src/uikit/List/ListItem.tsx | 2 +- 14 files changed, 343 insertions(+), 23 deletions(-) create mode 100644 packages/mobile/src/assets/icons/png/ic-globe-28@4x.png create mode 100644 packages/mobile/src/assets/icons/svg/28/ic-globe-28.svg create mode 100644 packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx create mode 100644 packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx diff --git a/packages/mobile/src/assets/icons/png/ic-globe-28@4x.png b/packages/mobile/src/assets/icons/png/ic-globe-28@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..b96ef40a450df70153fa5941383587dfe27e9fcb GIT binary patch literal 2987 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 literal 0 HcmV?d00001 diff --git a/packages/mobile/src/assets/icons/svg/28/ic-globe-28.svg b/packages/mobile/src/assets/icons/svg/28/ic-globe-28.svg new file mode 100644 index 000000000..49eba3b39 --- /dev/null +++ b/packages/mobile/src/assets/icons/svg/28/ic-globe-28.svg @@ -0,0 +1,5 @@ +<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M16.9429 20.9214C16.4787 21.2508 15.9883 21.4155 15.4717 21.4155C14.8802 21.4155 14.431 21.262 14.124 20.9551C13.8245 20.6481 13.6149 20.2588 13.4951 19.7871C13.3828 19.3079 13.3192 18.81 13.3042 18.2934C13.2967 17.7768 13.293 17.3089 13.293 16.8896C13.293 16.3506 13.1021 15.9088 12.7202 15.5644C12.3459 15.2125 11.8667 15.0366 11.2827 15.0366C10.3319 15.0366 9.5944 14.7783 9.07031 14.2617C8.54622 13.7451 8.28418 13.0264 8.28418 12.1055C8.28418 11.3942 8.49381 10.7428 8.91309 10.1514C9.33236 9.55988 9.89014 9.08446 10.5864 8.72508C11.2902 8.36571 12.0614 8.18602 12.8999 8.18602C13.2743 8.18602 13.6486 8.23843 14.0229 8.34325C14.4048 8.44058 14.7492 8.53791 15.0562 8.63524C15.3706 8.73257 15.6139 8.78123 15.7861 8.78123C15.9359 8.78123 16.0519 8.70262 16.1343 8.54539C16.2166 8.38068 16.2578 8.18228 16.2578 7.95018C16.2578 7.78547 16.1792 7.60204 16.022 7.39989C15.8722 7.19025 15.6813 7.01431 15.4492 6.87206C15.2171 6.72232 14.9813 6.64745 14.7417 6.64745C14.6743 6.64745 14.6219 6.66616 14.5845 6.7036C14.5545 6.73355 14.5321 6.78595 14.5171 6.86082C14.5096 7.02554 14.4572 7.16405 14.3599 7.27635C14.27 7.38117 14.1315 7.43358 13.9443 7.43358C13.6673 7.43358 13.4277 7.37368 13.2256 7.25389C13.0234 7.12661 12.8363 6.99933 12.6641 6.87206C12.4993 6.74478 12.3346 6.68114 12.1699 6.68114C12.0052 6.68114 11.8704 6.75226 11.7656 6.89452C11.6608 7.03677 11.5485 7.20148 11.4287 7.38866C11.3164 7.57583 11.1667 7.74054 10.9795 7.8828C10.7998 8.02505 10.5452 8.09618 10.2158 8.09618C9.90885 8.09618 9.66553 8.01008 9.48584 7.83788C9.30615 7.66567 9.21631 7.44107 9.21631 7.16405C9.21631 6.93944 9.2762 6.74103 9.396 6.56883C9.51579 6.39663 9.6543 6.22069 9.81152 6.041C9.97624 5.85383 10.1185 5.6367 10.2383 5.38963C10.3581 5.14256 10.418 4.8356 10.418 4.46873C10.418 4.09439 10.5677 3.72378 10.8672 3.35692C10.914 3.29845 10.9738 3.22515 11.0414 3.14307C6.26348 4.44202 2.75 8.81069 2.75 14C2.75 20.2132 7.7868 25.25 14 25.25C19.8771 25.25 24.7016 20.7434 25.2064 14.9973C24.8498 14.9552 24.2583 14.4242 24.0068 14.0146C23.7523 13.5879 23.5501 13.1387 23.4004 12.667C23.2507 12.1953 23.1196 11.7947 23.0073 11.4653C22.9025 11.1284 22.779 10.9599 22.6367 10.9599C22.5544 10.9599 22.487 10.9936 22.4346 11.061C22.3822 11.1284 22.3185 11.1958 22.2437 11.2632C22.1688 11.3231 22.0565 11.353 21.9067 11.353C21.7345 11.353 21.5361 11.2744 21.3115 11.1172C21.0944 10.9599 20.8698 10.7803 20.6377 10.5781C20.4131 10.3685 20.1997 10.185 19.9976 10.0278C19.8029 9.87059 19.6419 9.79198 19.5146 9.79198C19.3724 9.79198 19.2414 9.85936 19.1216 9.99413C19.0093 10.1289 18.9531 10.2861 18.9531 10.4658C18.9531 10.6081 19.0692 10.7615 19.3013 10.9263C19.5334 11.091 19.7992 11.2744 20.0986 11.4765C20.4056 11.6712 20.6751 11.8808 20.9072 12.1055C21.1393 12.3226 21.2554 12.5547 21.2554 12.8017C21.2554 13.1237 21.1543 13.4306 20.9521 13.7226C20.7575 14.0146 20.5591 14.2729 20.3569 14.4975C20.1623 14.7222 20.0649 14.9018 20.0649 15.0366C20.0649 15.3211 20.0837 15.6805 20.1211 16.1147C20.166 16.5415 20.0462 16.987 19.7617 17.4512C19.6494 17.6383 19.4585 17.9453 19.189 18.3721C18.9269 18.7988 18.605 19.248 18.2231 19.7197C17.8413 20.1839 17.4146 20.5845 16.9429 20.9214Z" fill="white"/> +<path d="M17.8076 6.23192C17.9349 6.15705 18.0996 6.11961 18.3018 6.11961C18.4215 6.11961 18.5413 6.16079 18.6611 6.24315C18.7884 6.31802 18.9531 6.35545 19.1553 6.35545C19.2826 6.35545 19.3874 6.30679 19.4697 6.20946C19.5521 6.10464 19.5933 5.97736 19.5933 5.82762C19.5933 5.56558 19.4622 5.34471 19.2002 5.16502C18.9382 4.98534 18.6087 4.89549 18.2119 4.89549C17.7477 4.89549 17.3883 4.97036 17.1338 5.1201C16.8792 5.26235 16.752 5.46825 16.752 5.73778C16.752 5.91747 16.8081 6.06346 16.9204 6.17577C17.0402 6.28058 17.1862 6.33299 17.3584 6.33299C17.5381 6.33299 17.6878 6.2993 17.8076 6.23192Z" fill="white"/> +<path opacity="0.32" d="M17.2332 21.3292L17.2345 21.3283C17.7431 20.965 18.2017 20.5341 18.6103 20.0374L18.6127 20.0343C19.0047 19.5502 19.3392 19.0842 19.6144 18.6364C19.8822 18.2123 20.0749 17.9026 20.1903 17.7103C20.5148 17.1801 20.6775 16.6277 20.6199 16.0675C20.5846 15.6576 20.5671 15.3243 20.566 15.0643C20.5686 15.0584 20.5726 15.05 20.5786 15.0389C20.6038 14.9924 20.6514 14.9227 20.7329 14.8283C20.9515 14.585 21.1626 14.3095 21.3668 14.0035C21.619 13.6379 21.7563 13.2344 21.7563 12.8018C21.7563 12.3809 21.5538 12.0259 21.253 11.7434C21.1471 11.6411 21.035 11.5422 20.9168 11.4468C20.951 11.4724 20.9852 11.4975 21.0192 11.5222L21.0192 11.5222L21.0258 11.5268C21.2886 11.7108 21.5884 11.853 21.9077 11.853C22.1075 11.853 22.3476 11.8159 22.5469 11.6615C22.6551 11.98 22.781 12.3654 22.9248 12.8183C23.0862 13.3268 23.3042 13.8112 23.5784 14.2708L23.5784 14.2708L23.5817 14.2763C23.7415 14.5365 23.9911 14.8107 24.2454 15.0257C24.3747 15.1349 24.5153 15.2376 24.6582 15.3186C24.7945 15.3958 24.9653 15.4722 25.1488 15.4939L25.6604 15.5543L25.7055 15.0411C25.7356 14.698 25.751 14.3507 25.751 14C25.751 7.51065 20.4903 2.25 14.001 2.25C12.9323 2.25 11.8963 2.39279 10.9112 2.66061L10.7576 2.70236L10.6564 2.8252C10.5889 2.90722 10.5279 2.98191 10.4792 3.04273C10.1276 3.47416 9.91895 3.95221 9.91895 4.46876C9.91895 4.78427 9.86703 5.01131 9.78935 5.17152C9.68727 5.38207 9.56922 5.56063 9.43714 5.71071L9.43621 5.71178C9.26843 5.90353 9.11821 6.09403 8.98652 6.28333C8.80423 6.54537 8.71729 6.84499 8.71729 7.16408C8.71729 7.5604 8.85114 7.92125 9.14086 8.1989C9.34788 8.39729 9.59719 8.51351 9.86802 8.56475C9.32614 8.91627 8.87035 9.34844 8.50615 9.86223C8.02821 10.5365 7.78516 11.2885 7.78516 12.1055C7.78516 13.1246 8.07819 13.9849 8.72029 14.6178C9.363 15.2513 10.2415 15.5366 11.2837 15.5366C11.7592 15.5366 12.1099 15.6761 12.3787 15.9288L12.3787 15.9289L12.3863 15.9357C12.6574 16.1803 12.7939 16.4844 12.7939 16.8897C12.7939 17.3118 12.7977 17.7822 12.8052 18.3007L12.8052 18.3007L12.8054 18.308C12.8213 18.8564 12.8889 19.3878 13.0093 19.9012L13.0092 19.9012L13.0115 19.9102C13.1499 20.4554 13.3983 20.9263 13.7671 21.3043L13.7671 21.3043L13.7714 21.3086C14.2021 21.7393 14.7979 21.9155 15.4727 21.9155C16.1035 21.9155 16.6935 21.7122 17.2332 21.3292ZM20.3103 10.9552C20.4457 11.0731 20.5794 11.1844 20.7114 11.2888C20.6032 11.2095 20.4906 11.1326 20.3735 11.0582C20.0789 10.8593 19.8183 10.6794 19.5916 10.5185C19.5242 10.4707 19.4831 10.4336 19.4594 10.4087C19.4665 10.3748 19.4805 10.3472 19.5018 10.3202C19.5082 10.3134 19.5138 10.3078 19.5186 10.3034C19.5552 10.322 19.61 10.3568 19.6844 10.4168L19.6843 10.4169L19.6916 10.4225C19.8798 10.569 20.0818 10.7423 20.2975 10.9437L20.3038 10.9495L20.3103 10.9552ZM12.9009 7.68605C12.5088 7.68605 12.1284 7.72226 11.7603 7.79508C11.7935 7.74894 11.8249 7.70145 11.8544 7.65259C11.9683 7.4748 12.0732 7.32131 12.1691 7.19114C12.1722 7.18703 12.1748 7.1838 12.1769 7.1813C12.2069 7.18266 12.2655 7.19522 12.3593 7.26773L12.3592 7.26782L12.3678 7.27417C12.5511 7.40963 12.7487 7.54388 12.9602 7.67704L12.9601 7.67719L12.9717 7.68407C12.9731 7.68494 12.9746 7.68581 12.9761 7.68668C12.951 7.68626 12.926 7.68605 12.9009 7.68605ZM15.2066 8.15812C14.9242 8.06863 14.6135 7.97982 14.2748 7.89159C14.4451 7.84503 14.6082 7.75558 14.7396 7.60281C14.8478 7.47766 14.9225 7.33683 14.9673 7.18661C15.034 7.2097 15.1044 7.24403 15.1791 7.29223L15.1791 7.29234L15.1889 7.29838C15.3672 7.40764 15.5075 7.53848 15.6161 7.69053L15.622 7.69883L15.6283 7.70689C15.7529 7.86709 15.7588 7.94337 15.7588 7.95021C15.7588 8.09376 15.7378 8.19906 15.7081 8.27621C15.6021 8.26432 15.439 8.22999 15.2066 8.15812ZM17.3787 5.5566L17.3787 5.55671L17.3883 5.5511C17.534 5.46537 17.7915 5.39552 18.2129 5.39552C18.534 5.39552 18.7583 5.46759 18.9184 5.57742C19.0774 5.68644 19.0942 5.76672 19.0942 5.82765C19.0942 5.83758 19.0938 5.84616 19.0931 5.85358C19.002 5.84782 18.9505 5.83016 18.9242 5.81686C18.7438 5.69764 18.5344 5.61964 18.3027 5.61964C18.0453 5.61964 17.7872 5.66637 17.5613 5.79737C17.5347 5.81168 17.4755 5.83302 17.3594 5.83302C17.3115 5.83302 17.2851 5.82388 17.2659 5.81127C17.2596 5.80061 17.2529 5.78029 17.2529 5.7378C17.2529 5.6669 17.2664 5.61936 17.3787 5.5566Z" fill="white" stroke="white"/> +</svg> diff --git a/packages/mobile/src/core/ApprovalCell/components/ApprovalCell.tsx b/packages/mobile/src/core/ApprovalCell/components/ApprovalCell.tsx index 0a798ec19..1911cf67b 100644 --- a/packages/mobile/src/core/ApprovalCell/components/ApprovalCell.tsx +++ b/packages/mobile/src/core/ApprovalCell/components/ApprovalCell.tsx @@ -103,7 +103,7 @@ export const ApprovalCell = React.memo(ApprovalCellComponent); const styles = Steezy.create(({ colors }) => ({ container: { - backgroundColor: colors.backgroundContentAttention, + backgroundColor: colors.backgroundContentTint, marginBottom: 4, }, title: { @@ -111,7 +111,7 @@ const styles = Steezy.create(({ colors }) => ({ }, iconContainer: { padding: 8, - backgroundColor: colors.iconTertiary, + backgroundColor: colors.backgroundContentAttention, borderRadius: 32, }, })); diff --git a/packages/mobile/src/navigation/ModalStack.tsx b/packages/mobile/src/navigation/ModalStack.tsx index a001d94a8..f05c3b157 100644 --- a/packages/mobile/src/navigation/ModalStack.tsx +++ b/packages/mobile/src/navigation/ModalStack.tsx @@ -32,6 +32,7 @@ import { NewConfirmSending } from '$core/ModalContainer/NewConfirmSending/NewCon import { ActionModal } from '$core/ModalContainer/Action/Action'; import { ExchangeModal } from '$modals/ExchangeModal'; import { Swap } from '$core/Swap/Swap'; +import { RenewAllDomainModal } from '../tabs/Wallet/RenewAllDomainModal'; const Stack = createModalStackNavigator(); @@ -57,6 +58,7 @@ export const ModalStack = React.memo(() => ( </Stack.Group> <Stack.Group behavior="modal"> <Stack.Modal component={NFT} path="NFTItemDetails" /> + <Stack.Modal component={RenewAllDomainModal} path="RenewAllDomains" /> <Stack.Modal component={Receive} path={AppStackRouteNames.Receive} /> <Stack.Modal component={Send} path={AppStackRouteNames.Send} /> <Stack.Modal component={StakingSend} path={AppStackRouteNames.StakingSend} /> diff --git a/packages/mobile/src/store/zustand/domains/types.ts b/packages/mobile/src/store/zustand/domains/types.ts index 24184db9c..8eff163b8 100644 --- a/packages/mobile/src/store/zustand/domains/types.ts +++ b/packages/mobile/src/store/zustand/domains/types.ts @@ -1,8 +1,47 @@ export type RawAddress = string; export type ExpiringAt = number; +export interface ExpiringDomainItem { + expiring_at: number + name: string + dns_item: DnsItem +} + +export interface DnsItem { + address: string + index: number + owner: Owner + collection: Collection + verified: boolean + metadata: Metadata + previews: Preview[] + dns: string + approved_by: string[] +} + +export interface Owner { + address: string + is_scam: boolean +} + +export interface Collection { + address: string + name: string + description: string +} + +export interface Metadata { + name: string +} + +export interface Preview { + resolution: string + url: string +} + export type ExpiringDomains = { domains: { [key in RawAddress]: ExpiringAt }; + items: ExpiringDomainItem[]; actions: { load: (account_id: string) => void; remove: (address: string) => void; diff --git a/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts b/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts index bba92b08b..172ac0b18 100644 --- a/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts +++ b/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts @@ -8,6 +8,7 @@ import { Address } from '$libs/Ton'; const initialState: Omit<ExpiringDomains, 'actions'> = { domains: {}, + items: [], }; export const useExpiringDomains = create( @@ -17,27 +18,30 @@ export const useExpiringDomains = create( load: async (account_id) => { try { const { data } = await Tonapi.getExpiringDNS({ - account_id, - period: 30, + account_id, + period: 365, }); - + const domains = {}; for (let item of data.items) { domains[item.dns_item.address] = item.expiring_at; } - set({ domains }); + set({ domains, items: data.items }); } catch (err) { console.log('err[getExpiringDNS]', err); } }, remove: (address) => { - set(({ domains }) => { + set(({ domains, items }) => { const rawAddress = new Address(address).format({ raw: true }); const { [rawAddress]: remove, ...rest } = domains; - return { domains: rest }; + + const newItems = items.filter((item) => item.dns_item.address !== address); + + return { domains: rest, items: newItems }; }); - } + }, }, })), ); @@ -56,4 +60,4 @@ export function useLoadExpiringDomains() { }, []); return null; -} \ No newline at end of file +} diff --git a/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx b/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx new file mode 100644 index 000000000..b1dc4a2b4 --- /dev/null +++ b/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx @@ -0,0 +1,174 @@ +import { useNavigation } from '$libs/navigation'; +import { navigate } from '$navigation/helper'; +import { DnsItem, ExpiringDomainItem } from '$store/zustand/domains/types'; +import { useExpiringDomains } from '$store/zustand/domains/useExpiringDomains'; +import { t } from '$translation'; +import { Button, List, NavBar, SText } from '$uikit'; +import { Base64, ONE_YEAR_MILISEC, compareAddresses, delay, format, getCountOfDays, getLocale, ns } from '$utils'; +import { memo, useCallback, useEffect, useRef } from 'react'; +import { View, StyleSheet } from 'react-native'; +import Animated, { + useAnimatedScrollHandler, + useSharedValue, +} from 'react-native-reanimated'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { useWallet } from './hooks/useWallet'; +import { getTimeSec } from '$utils/getTimeSec'; +import TonWeb from 'tonweb'; +import { openSignRawModal } from '$core/ModalContainer/NFTOperations/Modals/SignRawModal'; +import { Ton } from '$libs/Ton'; +import { openAddressMismatchModal } from '$core/ModalContainer/AddressMismatch/AddressMismatch'; +import { BottomButtonWrap } from '$shared/components'; +import { Toast } from '$store/zustand/toast'; + +export const RenewAllDomainModal = memo((props) => { + const safeArea = useSafeAreaInsets(); + const scrollTop = useSharedValue(0); + const scrollRef = useRef<Animated.ScrollView>(null); + const nav = useNavigation(); + const domains = useExpiringDomains((state) => state.items); + + const handleRenew = useCallback(async () => { + await delay(1000); + if (domains.length < 1) { + nav.goBack(); + } + }, [domains]); + + const scrollHandler = useAnimatedScrollHandler({ + onScroll: (event) => { + scrollTop.value = event.contentOffset.y; + }, + }); + + return ( + <View style={styles.container}> + <NavBar + hideBackButton + isClosedButton + isModal + scrollTop={scrollTop} + titleProps={{ numberOfLines: 1 }} + > + {t('expiring_domains')} + </NavBar> + <Animated.ScrollView + alwaysBounceVertical={false} + showsVerticalScrollIndicator={false} + keyboardShouldPersistTaps="handled" + keyboardDismissMode="none" + ref={scrollRef} + contentContainerStyle={{ + paddingHorizontal: ns(16), + paddingBottom: safeArea.bottom, + }} + onScroll={scrollHandler} + scrollEventThrottle={16} + > + <List indent={false}> + {domains.map((domain) => ( + <ExpiringListItem key={domain.name} domain={domain} onRenew={handleRenew} /> + ))} + </List> + </Animated.ScrollView> + <BottomButtonWrap> + <Button onPress={() => {}}> + {t('dns_renew_all_until_btn', { + untilDate: format(+new Date() + ONE_YEAR_MILISEC, 'dd MMM yyyy', { + locale: getLocale(), + }), + })} + </Button> + </BottomButtonWrap> + </View> + ); +}); + +const ExpiringListItem = memo((props: { domain: ExpiringDomainItem, onRenew: () => void; }) => { + const { domain } = props; + const countOfDays = getCountOfDays(+new Date(), domain.expiring_at * 1000); + const days = countOfDays === 366 ? countOfDays - 1 : countOfDays; + const wallet = useWallet(); + const ownerAddress = domain.dns_item.owner.address; + const domainAddress = domain.dns_item.address; + const remove = useExpiringDomains((state) => state.actions.remove); + + const openRenew = useCallback(async () => { + if (!wallet || !wallet.address?.rawAddress) { + return; + } + + const valid_until = getTimeSec() + 10 * 60; + + const payload = new TonWeb.boc.Cell(); + + payload.bits.writeUint(0x4eb1f0f9, 32); + payload.bits.writeUint(0, 64); + payload.bits.writeUint(0, 256); + + openSignRawModal( + { + source: wallet.address.rawAddress, + valid_until, + messages: [ + { + address: domainAddress, + amount: Ton.toNano('0.02'), + payload: Base64.encodeBytes(await payload.toBoc()), + }, + ], + }, + { + expires_sec: valid_until, + response_options: { + broadcast: false, + }, + }, + () => { + Toast.show(t('dns_renew_toast_success')); + remove(domainAddress); + props.onRenew(); + }, + ); + }, [wallet, ownerAddress]); + + const handlePress = useCallback(() => { + if (!wallet || !wallet.address?.rawAddress) { + return; + } + + if (!compareAddresses(wallet.address.rawAddress, ownerAddress)) { + return openAddressMismatchModal(openRenew, ownerAddress); + } else { + openRenew(); + } + }, [wallet, ownerAddress, openRenew]); + + return ( + <List.Item + onPress={handlePress} + key={domain.name} + title={domain.name} + chevron + subtitle={ + <SText + color={days <= 30 ? 'accentNegative' : 'textSecondary'} + variant="body2" + numberOfLines={1} + > + {t('dns_renew_valid_caption', { count: days })} + </SText> + } + /> + ); +}); + +export function openRenewAllDomainModal() { + navigate('RenewAllDomains'); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, +}); diff --git a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx index a5840e897..22d2f0698 100644 --- a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx +++ b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx @@ -44,6 +44,8 @@ import { UpdateState } from '$store/zustand/updates/types'; import { HideableAmount } from '$core/HideableAmount/HideableAmount'; import { usePrivacyStore } from '$store/zustand/privacy/usePrivacyStore'; import { ShowBalance } from '$core/HideableAmount/ShowBalance'; +import { useExpiringDomains } from '$store/zustand/domains/useExpiringDomains'; +import { ExpiringDomainCell } from './components/ExpiringDomainCell'; export const WalletScreen = memo(() => { const flags = useFlags(['disable_swap']); @@ -64,6 +66,7 @@ export const WalletScreen = memo(() => { const isFocused = useIsFocused(); const notifications = useInternalNotifications(); + const expiringDomains = useExpiringDomains((state) => Object.keys(state.domains).length); // TODO: rewrite useEffect(() => { @@ -245,12 +248,20 @@ export const WalletScreen = memo(() => { <Tabs.Section index={0}> <BalancesList ListHeaderComponent={ - wallet ? ( - <ApprovalCell - withoutSpacer - style={{ paddingHorizontal: ns(16), paddingBottom: ns(16) }} - /> - ) : undefined + <> + {wallet && expiringDomains > 0 && ( + <ExpiringDomainCell + withoutSpacer + style={{ paddingHorizontal: ns(16), paddingBottom: ns(8) }} + /> + )} + {wallet ? ( + <ApprovalCell + withoutSpacer + style={{ paddingHorizontal: ns(16), paddingBottom: ns(16) }} + /> + ) : undefined} + </> } balance={balance} tokens={tokens} @@ -263,12 +274,14 @@ export const WalletScreen = memo(() => { <Tabs.Section index={1}> <Tabs.FlashList ListHeaderComponent={ - wallet ? ( - <ApprovalCell - withoutSpacer - style={{ paddingHorizontal: ns(6), paddingBottom: ns(16) }} - /> - ) : undefined + <> + {wallet ? ( + <ApprovalCell + withoutSpacer + style={{ paddingHorizontal: ns(6), paddingBottom: ns(16) }} + /> + ) : undefined} + </> } contentContainerStyle={styles.scrollContainer.static} estimatedItemSize={1000} diff --git a/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx new file mode 100644 index 000000000..d5a059ba6 --- /dev/null +++ b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx @@ -0,0 +1,61 @@ +import React, { memo } from 'react'; +import { Steezy } from '$styles'; +import { Icon, Spacer, SText, View, List } from '$uikit'; +import { ViewStyle } from 'react-native'; +import { useExpiringDomains } from '$store/zustand/domains/useExpiringDomains'; +import { ONE_YEAR_MILISEC, format, getLocale } from '$utils/date'; +import { t } from '$translation'; +import { openRenewAllDomainModal } from '../RenewAllDomainModal'; + +interface ApprovalCellProps { + withoutSpacer?: boolean; + style?: ViewStyle; +} + +export const ExpiringDomainCell = memo<ApprovalCellProps>(({ withoutSpacer, style }) => { + const expiringDomains = useExpiringDomains((state) => state.domains); + + return ( + <View style={style}> + {!withoutSpacer && <Spacer y={16} />} + <List indent={false} style={styles.container}> + <List.Item + chevronColor="iconSecondary" + onPress={() => openRenewAllDomainModal()} + leftContent={ + <View style={styles.iconContainer}> + <Icon color="foregroundPrimary" name="ic-globe-28" /> + </View> + } + title={ + <SText numberOfLines={2} style={styles.title} variant="body2"> + {t('dns_alert_expiring', { count: Object.keys(expiringDomains).length })} + {'\n'} + {t('dns_renew_all_until_btn', { + untilDate: format(+new Date() + ONE_YEAR_MILISEC, 'dd MMM yyyy', { + locale: getLocale(), + }), + })} + </SText> + } + chevron + /> + </List> + </View> + ); +}); + +const styles = Steezy.create(({ colors }) => ({ + container: { + backgroundColor: colors.backgroundContentTint, + marginBottom: 4, + }, + title: { + marginRight: 40, + }, + iconContainer: { + padding: 8, + backgroundColor: colors.backgroundContentAttention, + borderRadius: 32, + }, +})); diff --git a/packages/mobile/src/translation/locales/en.json b/packages/mobile/src/translation/locales/en.json index 0ce5745b6..6a3450d9c 100644 --- a/packages/mobile/src/translation/locales/en.json +++ b/packages/mobile/src/translation/locales/en.json @@ -121,6 +121,15 @@ "one": "Expires in %{count} day", "other": "Expires in %{count} days" }, + "dns_alert_expiring": { + "one": "You have %{count} expiring domain.", + "few": "You have %{count} expiring domains.", + "other": "You have %{count} expiring domains.", + "many": "You have %{count} expiring domains." + }, + "dns_renew_all_until_btn": "Renew all until %{untilDate}", + "expiring_domains": "Expiring domains", + "domains_renewed": "Domains renewed", "nft_title": "NFTs", "nft_about_dns": "TON DNS is a service that allows users to assign a human-readable name to crypto wallets, smart contracts, and websites. \n\nWith TON DNS, access to decentralized services is analogous to access to websites on the internet.", diff --git a/packages/mobile/src/translation/locales/ru.json b/packages/mobile/src/translation/locales/ru.json index cf97f6e75..7b2fba886 100644 --- a/packages/mobile/src/translation/locales/ru.json +++ b/packages/mobile/src/translation/locales/ru.json @@ -123,6 +123,15 @@ "other": "Истекает через %{count} дней", "many": "Истекает через %{count} дней" }, + "dns_alert_expiring": { + "one": "У вас есть %{count} истекающий домен.", + "few": "У вас есть %{count} истекающих домена.", + "other": "У вас есть %{count} истекающих доменов.", + "many": "У вас есть %{count} истекающих доменов." + }, + "dns_renew_all_until_btn": "Продлить все до %{untilDate}", + "expiring_domains": "Истекающие домены", + "domains_renewed": "Домены продлены", "nft_title": "NFT", "nft_about_dns": "TON DNS — сервис, который позволяет задать криптокошелькам, смарт-контрактам или сайтам короткие читаемые имена. \n\nС TON DNS доступ к децентрализованным сервисам аналогичен доступу к веб-сайтам в интернете.", diff --git a/packages/mobile/src/uikit/Icon/IconsMobileList.ts b/packages/mobile/src/uikit/Icon/IconsMobileList.ts index 73f9c2673..f39d1b19e 100644 --- a/packages/mobile/src/uikit/Icon/IconsMobileList.ts +++ b/packages/mobile/src/uikit/Icon/IconsMobileList.ts @@ -66,6 +66,7 @@ export const MobileIconsList = { 'ic-explore-28': require('$assets/icons/png/ic-explore-28.png'), 'ic-flash-28': require('$assets/icons/png/ic-flash-28.png'), 'ic-gear-28': require('$assets/icons/png/ic-gear-28.png'), + 'ic-globe-28': require('$assets/icons/png/ic-globe-28.png'), 'ic-home-28': require('$assets/icons/png/ic-home-28.png'), 'ic-jetton-28': require('$assets/icons/png/ic-jetton-28.png'), 'ic-key-28': require('$assets/icons/png/ic-key-28.png'), diff --git a/packages/mobile/src/uikit/Icon/generated.types.ts b/packages/mobile/src/uikit/Icon/generated.types.ts index 7ce7342db..a9a2fb187 100644 --- a/packages/mobile/src/uikit/Icon/generated.types.ts +++ b/packages/mobile/src/uikit/Icon/generated.types.ts @@ -66,6 +66,7 @@ export type IconNames = | 'ic-explore-28' | 'ic-flash-28' | 'ic-gear-28' + | 'ic-globe-28' | 'ic-home-28' | 'ic-jetton-28' | 'ic-key-28' @@ -188,6 +189,7 @@ export const AllIcons = [ 'ic-explore-28', 'ic-flash-28', 'ic-gear-28', + 'ic-globe-28', 'ic-home-28', 'ic-jetton-28', 'ic-key-28', @@ -311,6 +313,7 @@ export const IconSizes = { 'ic-explore-28': 28, 'ic-flash-28': 28, 'ic-gear-28': 28, + 'ic-globe-28': 28, 'ic-home-28': 28, 'ic-jetton-28': 28, 'ic-key-28': 28, diff --git a/packages/mobile/src/uikit/List/ListItem.tsx b/packages/mobile/src/uikit/List/ListItem.tsx index df4f2ace6..1287c2f16 100644 --- a/packages/mobile/src/uikit/List/ListItem.tsx +++ b/packages/mobile/src/uikit/List/ListItem.tsx @@ -199,7 +199,7 @@ const styles = Steezy.create(({ colors }) => ({ height: 44, borderRadius: 44 / 2, overflow: 'hidden', - backgroundColor: colors.backgroundContentTint, + backgroundColor: colors.backgroundContentAttention, }, picture: { width: 44, From eb9d9911b62a68f777092f9ddc9b1f619594a9d8 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Mon, 28 Aug 2023 23:27:42 +0300 Subject: [PATCH 09/45] bump(mobile): 3.3.2 361 --- packages/mobile/android/app/build.gradle | 4 ++-- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 397d3064e..15c38bf14 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -123,8 +123,8 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 351 - versionName "3.3.1" + versionCode 361 + versionName "3.3.2" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 32911f70f..60d1083fb 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 351; + CURRENT_PROJECT_VERSION = 361; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1228,7 +1228,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.3.1; + MARKETING_VERSION = 3.3.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1253,7 +1253,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 351; + CURRENT_PROJECT_VERSION = 361; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; @@ -1262,7 +1262,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.3.1; + MARKETING_VERSION = 3.3.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", From 4e8e09fb2e94b0616f0f4b947232b1086456c187 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Tue, 29 Aug 2023 01:37:26 +0300 Subject: [PATCH 10/45] fix(mobile): renew all domains --- packages/mobile/src/navigation/ModalStack.tsx | 2 + .../zustand/domains/useExpiringDomains.ts | 2 +- .../src/tabs/Wallet/RenewAllDomainModal.tsx | 130 ++++------- .../components/RenewAllProgressButton.tsx | 73 ++++++ .../\320\241onfirmRenewAllDomains.tsx" | 210 ++++++++++++++++++ .../mobile/src/translation/locales/en.json | 9 + .../mobile/src/translation/locales/ru.json | 9 + packages/mobile/src/uikit/List/ListItem.tsx | 4 +- .../mobile/src/uikit/TransitionOpacity.tsx | 62 +++--- 9 files changed, 386 insertions(+), 115 deletions(-) create mode 100644 packages/mobile/src/tabs/Wallet/components/RenewAllProgressButton.tsx create mode 100644 "packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" diff --git a/packages/mobile/src/navigation/ModalStack.tsx b/packages/mobile/src/navigation/ModalStack.tsx index f05c3b157..7fb1d5d79 100644 --- a/packages/mobile/src/navigation/ModalStack.tsx +++ b/packages/mobile/src/navigation/ModalStack.tsx @@ -33,6 +33,7 @@ import { ActionModal } from '$core/ModalContainer/Action/Action'; import { ExchangeModal } from '$modals/ExchangeModal'; import { Swap } from '$core/Swap/Swap'; import { RenewAllDomainModal } from '../tabs/Wallet/RenewAllDomainModal'; +import { СonfirmRenewAllDomains } from '../tabs/Wallet/components/СonfirmRenewAllDomains'; const Stack = createModalStackNavigator(); @@ -49,6 +50,7 @@ export const ModalStack = React.memo(() => ( <Stack.Modal component={NFTSaleCancelModal} path="NFTSaleCancel" /> <Stack.Modal component={ExchangeModal} path="Exchange" /> <Stack.Modal component={OldExchange} path="OldExchange" /> + <Stack.Modal component={СonfirmRenewAllDomains} path="СonfirmRenewAllDomains"/> <Stack.Modal component={NFTTransferInputAddressModal} path="NFTTransferInputAddress" diff --git a/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts b/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts index 172ac0b18..d1c27314e 100644 --- a/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts +++ b/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts @@ -19,7 +19,7 @@ export const useExpiringDomains = create( try { const { data } = await Tonapi.getExpiringDNS({ account_id, - period: 365, + period: 30, }); const domains = {}; diff --git a/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx b/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx index b1dc4a2b4..b8130f5ac 100644 --- a/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx +++ b/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx @@ -1,27 +1,23 @@ import { useNavigation } from '$libs/navigation'; -import { navigate } from '$navigation/helper'; -import { DnsItem, ExpiringDomainItem } from '$store/zustand/domains/types'; +import { navigate, openNFT } from '$navigation/helper'; +import { ExpiringDomainItem } from '$store/zustand/domains/types'; import { useExpiringDomains } from '$store/zustand/domains/useExpiringDomains'; import { t } from '$translation'; import { Button, List, NavBar, SText } from '$uikit'; -import { Base64, ONE_YEAR_MILISEC, compareAddresses, delay, format, getCountOfDays, getLocale, ns } from '$utils'; -import { memo, useCallback, useEffect, useRef } from 'react'; +import { ONE_YEAR_MILISEC, delay, format, getCountOfDays, getLocale, ns } from '$utils'; +import { memo, useCallback, useRef } from 'react'; import { View, StyleSheet } from 'react-native'; import Animated, { useAnimatedScrollHandler, useSharedValue, } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useWallet } from './hooks/useWallet'; -import { getTimeSec } from '$utils/getTimeSec'; -import TonWeb from 'tonweb'; -import { openSignRawModal } from '$core/ModalContainer/NFTOperations/Modals/SignRawModal'; import { Ton } from '$libs/Ton'; -import { openAddressMismatchModal } from '$core/ModalContainer/AddressMismatch/AddressMismatch'; import { BottomButtonWrap } from '$shared/components'; -import { Toast } from '$store/zustand/toast'; +import { openСonfirmRenewAllDomains } from './components/СonfirmRenewAllDomains'; +import { CryptoCurrencies } from '$shared/constants'; -export const RenewAllDomainModal = memo((props) => { +export const RenewAllDomainModal = memo(() => { const safeArea = useSafeAreaInsets(); const scrollTop = useSharedValue(0); const scrollRef = useRef<Animated.ScrollView>(null); @@ -35,6 +31,12 @@ export const RenewAllDomainModal = memo((props) => { } }, [domains]); + const handleRenewAll = useCallback(async () => { + nav.goBack(); + await delay(350); + openСonfirmRenewAllDomains(); + }, [domains]); + const scrollHandler = useAnimatedScrollHandler({ onScroll: (event) => { scrollTop.value = event.contentOffset.y; @@ -72,7 +74,7 @@ export const RenewAllDomainModal = memo((props) => { </List> </Animated.ScrollView> <BottomButtonWrap> - <Button onPress={() => {}}> + <Button onPress={handleRenewAll}> {t('dns_renew_all_until_btn', { untilDate: format(+new Date() + ONE_YEAR_MILISEC, 'dd MMM yyyy', { locale: getLocale(), @@ -84,84 +86,38 @@ export const RenewAllDomainModal = memo((props) => { ); }); -const ExpiringListItem = memo((props: { domain: ExpiringDomainItem, onRenew: () => void; }) => { - const { domain } = props; - const countOfDays = getCountOfDays(+new Date(), domain.expiring_at * 1000); - const days = countOfDays === 366 ? countOfDays - 1 : countOfDays; - const wallet = useWallet(); - const ownerAddress = domain.dns_item.owner.address; - const domainAddress = domain.dns_item.address; - const remove = useExpiringDomains((state) => state.actions.remove); - - const openRenew = useCallback(async () => { - if (!wallet || !wallet.address?.rawAddress) { - return; - } - - const valid_until = getTimeSec() + 10 * 60; - - const payload = new TonWeb.boc.Cell(); +const ExpiringListItem = memo( + (props: { domain: ExpiringDomainItem; onRenew: () => void }) => { + const { domain } = props; + const countOfDays = getCountOfDays(+new Date(), domain.expiring_at * 1000); + const days = countOfDays === 366 ? countOfDays - 1 : countOfDays; - payload.bits.writeUint(0x4eb1f0f9, 32); - payload.bits.writeUint(0, 64); - payload.bits.writeUint(0, 256); + const handlePress = useCallback(() => { + openNFT({ + address: Ton.formatAddress(domain.dns_item.address), + currency: CryptoCurrencies.Ton, + }); + }, [domain.dns_item.address]); - openSignRawModal( - { - source: wallet.address.rawAddress, - valid_until, - messages: [ - { - address: domainAddress, - amount: Ton.toNano('0.02'), - payload: Base64.encodeBytes(await payload.toBoc()), - }, - ], - }, - { - expires_sec: valid_until, - response_options: { - broadcast: false, - }, - }, - () => { - Toast.show(t('dns_renew_toast_success')); - remove(domainAddress); - props.onRenew(); - }, + return ( + <List.Item + onPress={handlePress} + key={domain.name} + title={domain.name} + chevron + subtitle={ + <SText + color={days <= 30 ? 'accentNegative' : 'textSecondary'} + variant="body2" + numberOfLines={1} + > + {t('dns_renew_valid_caption', { count: days })} + </SText> + } + /> ); - }, [wallet, ownerAddress]); - - const handlePress = useCallback(() => { - if (!wallet || !wallet.address?.rawAddress) { - return; - } - - if (!compareAddresses(wallet.address.rawAddress, ownerAddress)) { - return openAddressMismatchModal(openRenew, ownerAddress); - } else { - openRenew(); - } - }, [wallet, ownerAddress, openRenew]); - - return ( - <List.Item - onPress={handlePress} - key={domain.name} - title={domain.name} - chevron - subtitle={ - <SText - color={days <= 30 ? 'accentNegative' : 'textSecondary'} - variant="body2" - numberOfLines={1} - > - {t('dns_renew_valid_caption', { count: days })} - </SText> - } - /> - ); -}); + }, +); export function openRenewAllDomainModal() { navigate('RenewAllDomains'); diff --git a/packages/mobile/src/tabs/Wallet/components/RenewAllProgressButton.tsx b/packages/mobile/src/tabs/Wallet/components/RenewAllProgressButton.tsx new file mode 100644 index 000000000..e1af897d1 --- /dev/null +++ b/packages/mobile/src/tabs/Wallet/components/RenewAllProgressButton.tsx @@ -0,0 +1,73 @@ +import { Steezy } from '$styles'; +import { t } from '$translation'; +import { Spacer, Text, View } from '$uikit'; +import { memo, useCallback, useEffect, useState } from 'react'; +import { LayoutChangeEvent } from 'react-native'; + +import Animated, { + useAnimatedStyle, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; + +interface RenewAllProgressButtonProps { + current: number; + total: number; +} + +export const RenewAllProgressButton = memo<RenewAllProgressButtonProps>((props) => { + const { current, total } = props; + const [width, setWidth] = useState(0); + const progress = useSharedValue(0); + + useEffect(() => { + progress.value = withTiming((current * width) / total, { duration: 100 }); + }, [current, total]); + + const handleLayout = useCallback((ev: LayoutChangeEvent) => { + setWidth(ev.nativeEvent.layout.width); + }, []); + + const progressStyle = useAnimatedStyle(() => ({ + width: progress.value, + })); + + return ( + <View style={styles.container} onLayout={handleLayout}> + <View style={styles.content}> + <Text variant="label1">{t('renew_in_progress')}</Text> + <Spacer x={8} /> + <Text variant="label1" color="textSecondary"> + {t('renew_progress_of', { current, count: total })} + </Text> + </View> + <Animated.View style={[styles.progress.static, progressStyle]} /> + </View> + ); +}); + +const styles = Steezy.create(({ corners, colors }) => ({ + container: { + height: 56, + borderRadius: corners.medium, + backgroundColor: colors.buttonSecondaryBackground, + justifyContent: 'center', + alignItems: 'center', + overflow: 'hidden', + }, + content: { + position: 'absolute', + top: 0, + height: 56, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + }, + progress: { + position: 'absolute', + height: 56, + top: 0, + left: 0, + backgroundColor: 'rgba(194, 218, 255, 0.08)', + }, +})); diff --git "a/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" new file mode 100644 index 000000000..7dd31f292 --- /dev/null +++ "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" @@ -0,0 +1,210 @@ +import { Modal, useNavigation } from '$libs/navigation'; +import { SheetActions } from '$libs/navigation/components/Modal/Sheet/SheetsProvider'; +import { push } from '$navigation/helper'; +import { t } from '$translation'; +import { Icon, List, Spacer, Text, TransitionOpacity } from '$uikit'; +import { memo, useCallback, useState } from 'react'; +import { View, StyleSheet } from 'react-native'; +import * as S from '../../../core/ModalContainer/NFTOperations/NFTOperations.styles'; +import { useExpiringDomains } from '$store/zustand/domains/useExpiringDomains'; +import { formatter } from '$utils/formatter'; +import { useFiatValue, useWallet } from '$hooks'; +import { CryptoCurrencies, Decimals } from '$shared/constants'; +import { copyText } from '$hooks/useCopyText'; +import { useUnlockVault } from '$core/ModalContainer/NFTOperations/useUnlockVault'; +import { RenewAllProgressButton } from './RenewAllProgressButton'; +import { Base64, debugLog, delay, triggerNotificationSuccess } from '$utils'; +import { Toast } from '$store/zustand/toast'; +import { getTimeSec } from '$utils/getTimeSec'; +import { Ton } from '$libs/Ton'; +import TonWeb from 'tonweb'; +import { Tonapi } from '$libs/Tonapi'; + +enum States { + INITIAL, + PROGRESS, + SUCCESS, + ERROR, +} + +export const СonfirmRenewAllDomains = memo((props) => { + const [state, setState] = useState(States.INITIAL); + const nav = useNavigation(); + const domains = useExpiringDomains((state) => state.items); + const remove = useExpiringDomains((state) => state.actions.remove); + const unlock = useUnlockVault(); + const [current, setCurrent] = useState(0); + const wallet = useWallet(); + + const [count] = useState(domains.length); + const [amount] = useState(0.02 * count); + + const fiatValue = useFiatValue( + CryptoCurrencies.Ton, + String(amount), + Decimals[CryptoCurrencies.Ton], + ); + + const handleConfirm = useCallback(async () => { + const unlocked = await unlock(); + const secretKey = await unlocked.getTonPrivateKey(); + + try { + setState(States.PROGRESS); + + for (let i = 0; i < domains.length; i++) { + const domain = domains[i]; + + setCurrent(i + 1); + + const payload = new TonWeb.boc.Cell(); + payload.bits.writeUint(0x4eb1f0f9, 32); + payload.bits.writeUint(0, 64); + payload.bits.writeUint(0, 256); + + const seqno = await wallet.ton.getSeqno(await wallet.ton.getAddress()); + const tx = wallet.vault.tonWallet.methods.transfer({ + toAddress: domain.dns_item.address, + amount: Ton.toNano('0.02'), + seqno: seqno, + payload, + sendMode: 3, + secretKey, + }); + + const queryMsg = await tx.getQuery(); + const boc = Base64.encodeBytes(await queryMsg.toBoc(false)); + const t = await Tonapi.sendBoc(boc); + console.log(t); + + remove(domain.dns_item.address); + } + + setState(States.SUCCESS); + triggerNotificationSuccess(); + await delay(1750); + + nav.goBack(); + Toast.show(t('domains_renewed')); + } catch (err) { + debugLog(err); + setState(States.ERROR); + } + }, [setCurrent]); + + const fiatAmount = `≈ ${fiatValue.fiatInfo.amount}`; + const formattedAmount = formatter.format(amount, { + currency: 'TON', + currencySeparator: 'wide', + }); + + return ( + <Modal> + <Modal.Header title={t('confirm_renew_all_domains_title')} /> + <Modal.Content> + <List> + <List.Item + onPress={() => copyText(formattedAmount)} + titleStyle={styles.listItemTitle} + title={ + <Text variant="body1" color="textSecondary"> + {t('txActions.amount')} + </Text> + } + value={formattedAmount} + subvalue={fiatAmount} + /> + <List.Item + onPress={() => copyText(t('dns_addresses', { count: count }))} + titleStyle={styles.listItemTitle} + title={ + <Text variant="body1" color="textSecondary"> + {t('txActions.signRaw.recipient')} + </Text> + } + value={t('dns_addresses', { count: count })} + /> + </List> + </Modal.Content> + <Modal.Footer> + <View style={S.styles.footer}> + <TransitionOpacity + style={S.styles.transitionContainer} + isVisible={state === States.INITIAL} + entranceAnimation={false} + > + <View style={S.styles.footerButtons}> + <S.ActionButton mode="secondary" onPress={() => nav.goBack()}> + {t('cancel')} + </S.ActionButton> + <Spacer x={16} /> + <S.ActionButton onPress={() => handleConfirm()}> + {t('nft_confirm_operation')} + </S.ActionButton> + </View> + </TransitionOpacity> + <TransitionOpacity + style={S.styles.transitionContainer} + isVisible={state === States.PROGRESS} + entranceAnimation={false} + > + <View style={styles.footer}> + <RenewAllProgressButton total={count} current={current} /> + </View> + </TransitionOpacity> + <TransitionOpacity + style={S.styles.transitionContainer} + isVisible={state === States.SUCCESS} + > + <View style={S.styles.center}> + <View style={S.styles.iconContainer}> + <Icon name="ic-checkmark-circle-32" color="accentPositive" /> + </View> + <Text variant="label2" color="accentPositive"> + {t('nft_operation_success')} + </Text> + </View> + </TransitionOpacity> + <TransitionOpacity + style={S.styles.transitionContainer} + isVisible={state === States.ERROR} + > + <View style={S.styles.center}> + <View style={S.styles.iconContainer}> + <Icon color="accentNegative" name="ic-exclamationmark-circle-32" /> + </View> + <Text + color="accentNegative" + textAlign="center" + variant="label2" + numberOfLines={2} + > + {t('error_occurred')} + </Text> + </View> + </TransitionOpacity> + </View> + </Modal.Footer> + </Modal> + ); +}); + +export function openСonfirmRenewAllDomains() { + push('SheetsProvider', { + $$action: SheetActions.ADD, + component: СonfirmRenewAllDomains, + params: {}, + path: 'ConfirmRenewAll', + }); +} + +const styles = StyleSheet.create({ + container: {}, + listItemTitle: { + alignSelf: 'flex-start', + }, + footer: { + paddingHorizontal: 16, + paddingTop: 16, + }, +}); diff --git a/packages/mobile/src/translation/locales/en.json b/packages/mobile/src/translation/locales/en.json index 6a3450d9c..fbba3179c 100644 --- a/packages/mobile/src/translation/locales/en.json +++ b/packages/mobile/src/translation/locales/en.json @@ -130,6 +130,15 @@ "dns_renew_all_until_btn": "Renew all until %{untilDate}", "expiring_domains": "Expiring domains", "domains_renewed": "Domains renewed", + "confirm_renew_all_domains_title": "Confirm action", + "dns_addresses": { + "one": "%{count} address", + "few": "%{count} addresses", + "other": "%{count} addresses", + "many": "%{count} addresses" + }, + "renew_in_progress": "Renew in progress…", + "renew_progress_of": "%{current} of %{count}", "nft_title": "NFTs", "nft_about_dns": "TON DNS is a service that allows users to assign a human-readable name to crypto wallets, smart contracts, and websites. \n\nWith TON DNS, access to decentralized services is analogous to access to websites on the internet.", diff --git a/packages/mobile/src/translation/locales/ru.json b/packages/mobile/src/translation/locales/ru.json index 7b2fba886..f16fc3c76 100644 --- a/packages/mobile/src/translation/locales/ru.json +++ b/packages/mobile/src/translation/locales/ru.json @@ -132,6 +132,15 @@ "dns_renew_all_until_btn": "Продлить все до %{untilDate}", "expiring_domains": "Истекающие домены", "domains_renewed": "Домены продлены", + "confirm_renew_all_domains_title": "Confirm action", + "dns_addresses": { + "one": "%{count} адрес", + "few": "%{count} адреса", + "other": "%{count} адресов", + "many": "%{count} адресов" + }, + "renew_in_progress": "Выполняется продление…", + "renew_progress_of": "%{current} из %{count}", "nft_title": "NFT", "nft_about_dns": "TON DNS — сервис, который позволяет задать криптокошелькам, смарт-контрактам или сайтам короткие читаемые имена. \n\nС TON DNS доступ к децентрализованным сервисам аналогичен доступу к веб-сайтам в интернете.", diff --git a/packages/mobile/src/uikit/List/ListItem.tsx b/packages/mobile/src/uikit/List/ListItem.tsx index 1287c2f16..d2d9691cd 100644 --- a/packages/mobile/src/uikit/List/ListItem.tsx +++ b/packages/mobile/src/uikit/List/ListItem.tsx @@ -31,6 +31,8 @@ export interface ListItemProps { disabled?: boolean; titleProps?: TextProps; leftContentStyle?: StyleProp<ViewStyle>; + + titleStyle?: ViewStyle; /* Shared value that will be updated when user press on ListItem */ @@ -78,7 +80,7 @@ export const ListItem = memo<ListItemProps>((props) => { const TouchableComponent = isAndroid ? Pressable : TouchableHighlight; const renderTitle = useCallback(() => { return ( - <View style={styles.title}> + <View style={[styles.title, props.titleStyle]}> <View style={styles.titleTextContainer}> {typeof props.title === 'string' ? ( <SText diff --git a/packages/mobile/src/uikit/TransitionOpacity.tsx b/packages/mobile/src/uikit/TransitionOpacity.tsx index 1c59864e7..4908323fc 100644 --- a/packages/mobile/src/uikit/TransitionOpacity.tsx +++ b/packages/mobile/src/uikit/TransitionOpacity.tsx @@ -1,6 +1,12 @@ import React from 'react'; import { StyleProp, ViewStyle } from 'react-native'; -import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withDelay, withTiming } from 'react-native-reanimated'; +import Animated, { + runOnJS, + useAnimatedStyle, + useSharedValue, + withDelay, + withTiming, +} from 'react-native-reanimated'; interface TransitionOpacity { isVisible: boolean; @@ -8,49 +14,53 @@ interface TransitionOpacity { entranceAnimation?: boolean; style?: StyleProp<ViewStyle>; duration?: number; -} + children?: React.ReactNode; +} export const TransitionOpacity: React.FC<TransitionOpacity> = (props) => { - const { - children, - isVisible, - alwaysShown, - style, - entranceAnimation = true, - duration = 150 + const { + children, + isVisible, + alwaysShown, + style, + entranceAnimation = true, + duration = 150, } = props; const [shown, setIsShown] = React.useState(false); - const opacity = useSharedValue(entranceAnimation ? 0 : 1); + const opacity = useSharedValue(entranceAnimation ? 0 : 1); React.useEffect(() => { if (isVisible) { setIsShown(true); - opacity.value = withDelay(250, withTiming(1, { - duration, - })); + opacity.value = withDelay( + 250, + withTiming(1, { + duration, + }), + ); } else { - opacity.value = withTiming(0, { - duration, - }, (isComplete) => { - if (isComplete) { - runOnJS(setIsShown)(false); - } - }); + opacity.value = withTiming( + 0, + { + duration, + }, + (isComplete) => { + if (isComplete) { + runOnJS(setIsShown)(false); + } + }, + ); } }, [isVisible]); const opacityStyle = useAnimatedStyle(() => ({ - opacity: opacity.value + opacity: opacity.value, })); if (!shown && !alwaysShown) { return null; } - return ( - <Animated.View style={[style, opacityStyle]}> - {children} - </Animated.View> - ); + return <Animated.View style={[style, opacityStyle]}>{children}</Animated.View>; }; From e6b29a57fc9299e68a4fe65ca1ed11d3257ccf2e Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Tue, 29 Aug 2023 01:37:51 +0300 Subject: [PATCH 11/45] bump(mobile): 3.3.2 362 --- packages/mobile/android/app/build.gradle | 2 +- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 15c38bf14..db24eee70 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -123,7 +123,7 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 361 + versionCode 362 versionName "3.3.2" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 60d1083fb..17a73b57f 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 361; + CURRENT_PROJECT_VERSION = 362; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1253,7 +1253,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 361; + CURRENT_PROJECT_VERSION = 362; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; From 70ee20e8b754e6b99232dba0c24475a7d5040e47 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Tue, 29 Aug 2023 01:40:09 +0300 Subject: [PATCH 12/45] chore(mobile): remove console.log --- .../tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git "a/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" index 7dd31f292..b714f46a6 100644 --- "a/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" +++ "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" @@ -74,8 +74,7 @@ export const СonfirmRenewAllDomains = memo((props) => { const queryMsg = await tx.getQuery(); const boc = Base64.encodeBytes(await queryMsg.toBoc(false)); - const t = await Tonapi.sendBoc(boc); - console.log(t); + await Tonapi.sendBoc(boc); remove(domain.dns_item.address); } From a863f81328ec3139a0dbcf9637e80c2ff02f3f03 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Tue, 29 Aug 2023 01:55:33 +0300 Subject: [PATCH 13/45] fix(mobile): add delay --- .../tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" | 1 + 1 file changed, 1 insertion(+) diff --git "a/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" index b714f46a6..cf5018b70 100644 --- "a/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" +++ "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" @@ -76,6 +76,7 @@ export const СonfirmRenewAllDomains = memo((props) => { const boc = Base64.encodeBytes(await queryMsg.toBoc(false)); await Tonapi.sendBoc(boc); + await delay(15000); remove(domain.dns_item.address); } From 4bcfd67252690533119e0fd2ad2c157651336b8b Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Tue, 29 Aug 2023 01:56:00 +0300 Subject: [PATCH 14/45] bump(mobile): 3.3.2 363 --- packages/mobile/android/app/build.gradle | 2 +- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index db24eee70..a191c7267 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -123,7 +123,7 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 362 + versionCode 363 versionName "3.3.2" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 17a73b57f..e0cc3357b 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 362; + CURRENT_PROJECT_VERSION = 363; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1253,7 +1253,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 362; + CURRENT_PROJECT_VERSION = 363; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; From 8a070ab87ca664b499b2ea981d81eefd3f2119de Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Tue, 29 Aug 2023 03:37:26 +0300 Subject: [PATCH 15/45] fix(mobile): send pack messages --- .../components/RenewAllProgressButton.tsx | 24 +++++-- .../\320\241onfirmRenewAllDomains.tsx" | 68 +++++++++++++------ 2 files changed, 66 insertions(+), 26 deletions(-) diff --git a/packages/mobile/src/tabs/Wallet/components/RenewAllProgressButton.tsx b/packages/mobile/src/tabs/Wallet/components/RenewAllProgressButton.tsx index e1af897d1..6ede85bff 100644 --- a/packages/mobile/src/tabs/Wallet/components/RenewAllProgressButton.tsx +++ b/packages/mobile/src/tabs/Wallet/components/RenewAllProgressButton.tsx @@ -1,12 +1,16 @@ import { Steezy } from '$styles'; import { t } from '$translation'; import { Spacer, Text, View } from '$uikit'; -import { memo, useCallback, useEffect, useState } from 'react'; +import { delay } from '$utils'; +import { memo, useCallback, useEffect, useRef, useState } from 'react'; import { LayoutChangeEvent } from 'react-native'; import Animated, { + interpolate, + runOnJS, useAnimatedStyle, useSharedValue, + withDelay, withTiming, } from 'react-native-reanimated'; @@ -16,20 +20,28 @@ interface RenewAllProgressButtonProps { } export const RenewAllProgressButton = memo<RenewAllProgressButtonProps>((props) => { - const { current, total } = props; + const { total } = props; + const [count, setCount] = useState(1); const [width, setWidth] = useState(0); const progress = useSharedValue(0); useEffect(() => { - progress.value = withTiming((current * width) / total, { duration: 100 }); - }, [current, total]); + (async () => { + for (let i = 0; i < total; i++) { + progress.value = withTiming(i + 1, { duration: 3750 }, () => + runOnJS(setCount)(i + 2), + ); + await delay(3750); + } + })(); + }, []); const handleLayout = useCallback((ev: LayoutChangeEvent) => { setWidth(ev.nativeEvent.layout.width); }, []); const progressStyle = useAnimatedStyle(() => ({ - width: progress.value, + width: interpolate(progress.value, [0, total], [0, width]), })); return ( @@ -38,7 +50,7 @@ export const RenewAllProgressButton = memo<RenewAllProgressButtonProps>((props) <Text variant="label1">{t('renew_in_progress')}</Text> <Spacer x={8} /> <Text variant="label1" color="textSecondary"> - {t('renew_progress_of', { current, count: total })} + {t('renew_progress_of', { current: Math.min(count, total), count: total })} </Text> </View> <Animated.View style={[styles.progress.static, progressStyle]} /> diff --git "a/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" index cf5018b70..cf68ae059 100644 --- "a/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" +++ "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" @@ -19,6 +19,8 @@ import { getTimeSec } from '$utils/getTimeSec'; import { Ton } from '$libs/Ton'; import TonWeb from 'tonweb'; import { Tonapi } from '$libs/Tonapi'; +import { eventsActions } from '$store/events'; +import { useDispatch } from 'react-redux'; enum States { INITIAL, @@ -35,6 +37,7 @@ export const СonfirmRenewAllDomains = memo((props) => { const unlock = useUnlockVault(); const [current, setCurrent] = useState(0); const wallet = useWallet(); + const dispatch = useDispatch(); const [count] = useState(domains.length); const [amount] = useState(0.02 * count); @@ -51,33 +54,58 @@ export const СonfirmRenewAllDomains = memo((props) => { try { setState(States.PROGRESS); - - for (let i = 0; i < domains.length; i++) { - const domain = domains[i]; - - setCurrent(i + 1); - - const payload = new TonWeb.boc.Cell(); - payload.bits.writeUint(0x4eb1f0f9, 32); - payload.bits.writeUint(0, 64); - payload.bits.writeUint(0, 256); - + const divider = 4; + for (let i = 0; i < Math.ceil(domains.length / divider); i++) { + const tonWallet = wallet.vault.tonWallet; const seqno = await wallet.ton.getSeqno(await wallet.ton.getAddress()); - const tx = wallet.vault.tonWallet.methods.transfer({ - toAddress: domain.dns_item.address, - amount: Ton.toNano('0.02'), - seqno: seqno, - payload, - sendMode: 3, - secretKey, - }); + const signingMessage = (tonWallet as any).createSigningMessage(seqno); + + const part = domains.slice(i * divider, i * divider + divider); + for (let iPart = 0; iPart < part.length; iPart++) { + const index = i * divider + iPart; + const domain = domains[index]; + setCurrent(index + 1); + + const payload = new TonWeb.boc.Cell(); + payload.bits.writeUint(0x4eb1f0f9, 32); + payload.bits.writeUint(0, 64); + payload.bits.writeUint(0, 256); + + const order = TonWeb.Contract.createCommonMsgInfo( + TonWeb.Contract.createInternalMessageHeader( + new TonWeb.Address(domain.dns_item.address), + Ton.toNano('0.02'), + ), + undefined, + payload, + ); + + signingMessage.bits.writeUint8(3); + signingMessage.refs.push(order); + } + + const tx = TonWeb.Contract.createMethod( + tonWallet.provider, + (tonWallet as any).createExternalMessage( + signingMessage, + secretKey, + seqno, + !secretKey, + ), + ); const queryMsg = await tx.getQuery(); const boc = Base64.encodeBytes(await queryMsg.toBoc(false)); await Tonapi.sendBoc(boc); + dispatch(eventsActions.pollEvents()); await delay(15000); - remove(domain.dns_item.address); + + for (let iPart = 0; iPart < part.length; iPart++) { + const index = i * divider + iPart; + const domain = domains[index]; + remove(domain.dns_item.address); + } } setState(States.SUCCESS); From cc14bbd49d3f684fdf923f51a41025ff39f26ed7 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Wed, 30 Aug 2023 14:59:57 +0300 Subject: [PATCH 16/45] fix(mobile): some fixes for renew all domains --- .../zustand/domains/useExpiringDomains.ts | 3 +- .../mobile/src/tabs/Wallet/WalletScreen.tsx | 9 ++++- .../Wallet/components/ExpiringDomainCell.tsx | 40 ++++++++++++++----- .../\320\241onfirmRenewAllDomains.tsx" | 23 ++++++++--- .../mobile/src/translation/locales/en.json | 12 +++--- .../mobile/src/translation/locales/ru.json | 14 +++---- packages/mobile/src/utils/address.ts | 19 +++++++-- 7 files changed, 84 insertions(+), 36 deletions(-) diff --git a/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts b/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts index d1c27314e..306d0981a 100644 --- a/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts +++ b/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts @@ -36,8 +36,7 @@ export const useExpiringDomains = create( set(({ domains, items }) => { const rawAddress = new Address(address).format({ raw: true }); const { [rawAddress]: remove, ...rest } = domains; - - const newItems = items.filter((item) => item.dns_item.address !== address); + const newItems = items.filter((item) => item.dns_item.address !== rawAddress); return { domains: rest, items: newItems }; }); diff --git a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx index 22d2f0698..b7692ac12 100644 --- a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx +++ b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx @@ -66,7 +66,9 @@ export const WalletScreen = memo(() => { const isFocused = useIsFocused(); const notifications = useInternalNotifications(); - const expiringDomains = useExpiringDomains((state) => Object.keys(state.domains).length); + const expiringDomains = useExpiringDomains( + (state) => Object.keys(state.domains).length, + ); // TODO: rewrite useEffect(() => { @@ -171,7 +173,10 @@ export const WalletScreen = memo(() => { /> )} </IconButtonList> - {wallet && visibleApproval && <ApprovalCell />} + {wallet && visibleApproval && expiringDomains > 0 && ( + <ExpiringDomainCell style={{ paddingBottom: 8 }} /> + )} + {wallet && visibleApproval && <ApprovalCell withoutSpacer={expiringDomains > 0} />} </View> ); diff --git a/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx index d5a059ba6..f996bfa7e 100644 --- a/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx +++ b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx @@ -1,11 +1,12 @@ -import React, { memo } from 'react'; +import React, { memo, useMemo } from 'react'; import { Steezy } from '$styles'; import { Icon, Spacer, SText, View, List } from '$uikit'; import { ViewStyle } from 'react-native'; import { useExpiringDomains } from '$store/zustand/domains/useExpiringDomains'; -import { ONE_YEAR_MILISEC, format, getLocale } from '$utils/date'; +import { ONE_YEAR_MILISEC, format, getCountOfDays, getLocale } from '$utils/date'; import { t } from '$translation'; import { openRenewAllDomainModal } from '../RenewAllDomainModal'; +import { maskifyAddress, maskifyDomain } from '$utils'; interface ApprovalCellProps { withoutSpacer?: boolean; @@ -13,7 +14,30 @@ interface ApprovalCellProps { } export const ExpiringDomainCell = memo<ApprovalCellProps>(({ withoutSpacer, style }) => { - const expiringDomains = useExpiringDomains((state) => state.domains); + const expiringDomains = useExpiringDomains((state) => state.items); + + const title = useMemo(() => { + const untilDate = format(+new Date() + ONE_YEAR_MILISEC, 'dd MMM yyyy', { + locale: getLocale(), + }); + + if (expiringDomains.length === 1) { + const domain = expiringDomains[0]; + const countOfDays = getCountOfDays(+new Date(), domain.expiring_at * 1000); + const expire = countOfDays === 366 ? countOfDays - 1 : countOfDays; + + return t('dns_alert_expiring_one', { + domain: maskifyDomain(domain.name), + untilDate, + expire, + }); + } + + return t('dns_alert_expiring_many', { + count: expiringDomains.length, + untilDate, + }); + }, [expiringDomains]); return ( <View style={style}> @@ -28,14 +52,8 @@ export const ExpiringDomainCell = memo<ApprovalCellProps>(({ withoutSpacer, styl </View> } title={ - <SText numberOfLines={2} style={styles.title} variant="body2"> - {t('dns_alert_expiring', { count: Object.keys(expiringDomains).length })} - {'\n'} - {t('dns_renew_all_until_btn', { - untilDate: format(+new Date() + ONE_YEAR_MILISEC, 'dd MMM yyyy', { - locale: getLocale(), - }), - })} + <SText numberOfLines={3} style={styles.title} variant="body2"> + {title} </SText> } chevron diff --git "a/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" index cf68ae059..8b920f05c 100644 --- "a/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" +++ "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" @@ -15,12 +15,16 @@ import { useUnlockVault } from '$core/ModalContainer/NFTOperations/useUnlockVaul import { RenewAllProgressButton } from './RenewAllProgressButton'; import { Base64, debugLog, delay, triggerNotificationSuccess } from '$utils'; import { Toast } from '$store/zustand/toast'; -import { getTimeSec } from '$utils/getTimeSec'; import { Ton } from '$libs/Ton'; import TonWeb from 'tonweb'; import { Tonapi } from '$libs/Tonapi'; import { eventsActions } from '$store/events'; import { useDispatch } from 'react-redux'; +import { + checkIsInsufficient, + openInsufficientFundsModal, +} from '$core/ModalContainer/InsufficientFunds/InsufficientFunds'; +import BigNumber from 'bignumber.js'; enum States { INITIAL, @@ -49,9 +53,17 @@ export const СonfirmRenewAllDomains = memo((props) => { ); const handleConfirm = useCallback(async () => { + const amount = Ton.toNano('0.02'); + const totalAmount = new BigNumber(amount) + .multipliedBy(new BigNumber(domains.length)) + .toString(); + const checkResult = await checkIsInsufficient(totalAmount); + if (checkResult.insufficient) { + return openInsufficientFundsModal({ totalAmount, balance: checkResult.balance }); + } + const unlocked = await unlock(); const secretKey = await unlocked.getTonPrivateKey(); - try { setState(States.PROGRESS); const divider = 4; @@ -74,7 +86,7 @@ export const СonfirmRenewAllDomains = memo((props) => { const order = TonWeb.Contract.createCommonMsgInfo( TonWeb.Contract.createInternalMessageHeader( new TonWeb.Address(domain.dns_item.address), - Ton.toNano('0.02'), + amount, ), undefined, payload, @@ -143,14 +155,14 @@ export const СonfirmRenewAllDomains = memo((props) => { subvalue={fiatAmount} /> <List.Item - onPress={() => copyText(t('dns_addresses', { count: count }))} + onPress={() => copyText(t('dns_addresses', { count }))} titleStyle={styles.listItemTitle} title={ <Text variant="body1" color="textSecondary"> {t('txActions.signRaw.recipient')} </Text> } - value={t('dns_addresses', { count: count })} + value={t('dns_addresses', { count })} /> </List> </Modal.Content> @@ -227,7 +239,6 @@ export function openСonfirmRenewAllDomains() { } const styles = StyleSheet.create({ - container: {}, listItemTitle: { alignSelf: 'flex-start', }, diff --git a/packages/mobile/src/translation/locales/en.json b/packages/mobile/src/translation/locales/en.json index fbba3179c..8bbdef879 100644 --- a/packages/mobile/src/translation/locales/en.json +++ b/packages/mobile/src/translation/locales/en.json @@ -121,13 +121,15 @@ "one": "Expires in %{count} day", "other": "Expires in %{count} days" }, - "dns_alert_expiring": { - "one": "You have %{count} expiring domain.", - "few": "You have %{count} expiring domains.", - "other": "You have %{count} expiring domains.", - "many": "You have %{count} expiring domains." + "dns_alert_expiring_many": { + "few": "You have %{count} expiring domains. Renew all until %{untilDate}.", + "other": "You have %{count} expiring domains. Renew all until %{untilDate}.", + "many": "You have %{count} expiring domains. Renew all until %{untilDate}." }, + "dns_alert_expiring_one": "%{domain} expires in %{expire} days. Renew it until %{untilDate}.", "dns_renew_all_until_btn": "Renew all until %{untilDate}", + + "expiring_domains": "Expiring domains", "domains_renewed": "Domains renewed", "confirm_renew_all_domains_title": "Confirm action", diff --git a/packages/mobile/src/translation/locales/ru.json b/packages/mobile/src/translation/locales/ru.json index f16fc3c76..4d3dd8d17 100644 --- a/packages/mobile/src/translation/locales/ru.json +++ b/packages/mobile/src/translation/locales/ru.json @@ -123,16 +123,17 @@ "other": "Истекает через %{count} дней", "many": "Истекает через %{count} дней" }, - "dns_alert_expiring": { - "one": "У вас есть %{count} истекающий домен.", - "few": "У вас есть %{count} истекающих домена.", - "other": "У вас есть %{count} истекающих доменов.", - "many": "У вас есть %{count} истекающих доменов." + "dns_alert_expiring_many": { + "few": "У вас есть %{count} истекающих домена. Продлить все до %{untilDate}.", + "other": "У вас есть %{count} истекающих доменов. Продлить все до %{untilDate}.", + "many": "У вас есть %{count} истекающих доменов. Продлить все до %{untilDate}." }, + "dns_alert_expiring_one": "%{domain} истекает через %{expire} дней. Продлить до %{untilDate}.", "dns_renew_all_until_btn": "Продлить все до %{untilDate}", + "expiring_domains": "Истекающие домены", "domains_renewed": "Домены продлены", - "confirm_renew_all_domains_title": "Confirm action", + "confirm_renew_all_domains_title": "Подтвердить действие", "dns_addresses": { "one": "%{count} адрес", "few": "%{count} адреса", @@ -891,4 +892,3 @@ } } } - diff --git a/packages/mobile/src/utils/address.ts b/packages/mobile/src/utils/address.ts index 99e874e9b..9a70bdf42 100644 --- a/packages/mobile/src/utils/address.ts +++ b/packages/mobile/src/utils/address.ts @@ -1,5 +1,17 @@ import TonWeb from 'tonweb'; +export function maskifyDomain(domain: string) { + if (domain.length > 24) { + const initialPart = domain.substring(0, 8); + const finalPart = domain.substring(domain.length - (8 + 4)); + const ellipsis = '...'; + + return initialPart + ellipsis + finalPart; + } + + return domain; +} + export function maskifyAddress(address: string, symbolsInPart: number = 4) { if (!address) { address = ''; @@ -21,7 +33,7 @@ export const isValidAddress = (str: string) => TonWeb.Address.isValid(str); export const compareAddresses = (adr1?: string, adr2?: string) => { if (adr1 === undefined || adr2 === undefined) { return false; - } + } if (!TonWeb.Address.isValid(adr1) || !TonWeb.Address.isValid(adr2)) { return false; @@ -29,9 +41,10 @@ export const compareAddresses = (adr1?: string, adr2?: string) => { try { return ( - new TonWeb.Address(adr1).toString(false) === new TonWeb.Address(adr2).toString(false) + new TonWeb.Address(adr1).toString(false) === + new TonWeb.Address(adr2).toString(false) ); } catch { return false; } -}; \ No newline at end of file +}; From df196df1390be8e1595d8f5a91c3f01349e2bbb8 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Wed, 30 Aug 2023 15:00:33 +0300 Subject: [PATCH 17/45] bump(mobile): 3.3.2 364 --- packages/mobile/android/app/build.gradle | 2 +- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index a191c7267..62c23a95c 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -123,7 +123,7 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 363 + versionCode 364 versionName "3.3.2" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index e0cc3357b..fe88debc5 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 363; + CURRENT_PROJECT_VERSION = 364; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1253,7 +1253,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 363; + CURRENT_PROJECT_VERSION = 364; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; From fe98ec9fab34931b2edad22ce775a6ef73fea288 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Wed, 30 Aug 2023 16:22:46 +0300 Subject: [PATCH 18/45] fix(mobile): translactions --- packages/mobile/src/translation/locales/ru.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/src/translation/locales/ru.json b/packages/mobile/src/translation/locales/ru.json index 4d3dd8d17..113de51e8 100644 --- a/packages/mobile/src/translation/locales/ru.json +++ b/packages/mobile/src/translation/locales/ru.json @@ -124,9 +124,9 @@ "many": "Истекает через %{count} дней" }, "dns_alert_expiring_many": { - "few": "У вас есть %{count} истекающих домена. Продлить все до %{untilDate}.", - "other": "У вас есть %{count} истекающих доменов. Продлить все до %{untilDate}.", - "many": "У вас есть %{count} истекающих доменов. Продлить все до %{untilDate}." + "few": "У вас есть %{count} домена с истекающим сроком аренды. Продлить все до %{untilDate}.", + "other": "У вас есть %{count} доменов с истекающим сроком аренды. Продлить все до %{untilDate}.", + "many": "У вас есть %{count} доменов с истекающим сроком аренды. Продлить все до %{untilDate}." }, "dns_alert_expiring_one": "%{domain} истекает через %{expire} дней. Продлить до %{untilDate}.", "dns_renew_all_until_btn": "Продлить все до %{untilDate}", From dec55122c03ed41215d920faa6959af13a4fc163 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Wed, 30 Aug 2023 16:27:18 +0300 Subject: [PATCH 19/45] fix(mobile): add renew alert for collectibles tab --- packages/mobile/src/tabs/Wallet/WalletScreen.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx index b7692ac12..c0e73a19a 100644 --- a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx +++ b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx @@ -280,6 +280,12 @@ export const WalletScreen = memo(() => { <Tabs.FlashList ListHeaderComponent={ <> + {wallet && expiringDomains > 0 && ( + <ExpiringDomainCell + withoutSpacer + style={{ paddingHorizontal: ns(16), paddingBottom: ns(8) }} + /> + )} {wallet ? ( <ApprovalCell withoutSpacer From 202f660b12ae7a6f931fc87708a05c31c6d6720d Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Wed, 30 Aug 2023 20:37:32 +0300 Subject: [PATCH 20/45] fix(mobile): some fixes --- packages/mobile/src/store/main/sagas.ts | 4 +++- .../mobile/src/store/zustand/domains/useExpiringDomains.ts | 4 ++-- packages/mobile/src/tabs/Wallet/WalletScreen.tsx | 2 +- .../mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx | 5 +---- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/mobile/src/store/main/sagas.ts b/packages/mobile/src/store/main/sagas.ts index 4868c6ab5..c9807c687 100644 --- a/packages/mobile/src/store/main/sagas.ts +++ b/packages/mobile/src/store/main/sagas.ts @@ -66,6 +66,7 @@ import { clearSubscribeStatus } from '$utils/messaging'; import { useJettonEventsStore } from '$store/zustand/jettonEvents'; import { useSwapStore } from '$store/zustand/swap'; import * as SecureStore from 'expo-secure-store'; +import { useExpiringDomains } from '$store/zustand/domains/useExpiringDomains'; SplashScreen.preventAutoHideAsync() .then((result) => @@ -210,8 +211,9 @@ export function* initHandler(isTestnet: boolean, canRetry = false) { yield put(jettonsActions.loadJettons()); yield put(subscriptionsActions.loadSubscriptions()); const { wallet: walletNew } = yield select(walletSelector); - yield call([walletNew.ton, 'getAddress']); + const address = yield call([walletNew.ton, 'getAddress']); useSwapStore.getState().actions.fetchAssets(); + useExpiringDomains.getState().actions.load(address); } else { yield put(walletActions.endLoading()); } diff --git a/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts b/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts index 306d0981a..63f27eb1d 100644 --- a/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts +++ b/packages/mobile/src/store/zustand/domains/useExpiringDomains.ts @@ -29,7 +29,7 @@ export const useExpiringDomains = create( set({ domains, items: data.items }); } catch (err) { - console.log('err[getExpiringDNS]', err); + console.log('[getExpiringDNS]', err); } }, remove: (address) => { @@ -56,7 +56,7 @@ export function useLoadExpiringDomains() { if (wallet) { load(wallet.address.rawAddress); } - }, []); + }, [wallet?.address.rawAddress]); return null; } diff --git a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx index c0e73a19a..9a8d4a9d3 100644 --- a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx +++ b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx @@ -283,7 +283,7 @@ export const WalletScreen = memo(() => { {wallet && expiringDomains > 0 && ( <ExpiringDomainCell withoutSpacer - style={{ paddingHorizontal: ns(16), paddingBottom: ns(8) }} + style={{ paddingHorizontal: ns(6), paddingBottom: ns(8) }} /> )} {wallet ? ( diff --git a/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx index f996bfa7e..12ad5b95b 100644 --- a/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx +++ b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx @@ -52,7 +52,7 @@ export const ExpiringDomainCell = memo<ApprovalCellProps>(({ withoutSpacer, styl </View> } title={ - <SText numberOfLines={3} style={styles.title} variant="body2"> + <SText numberOfLines={3} variant="body2"> {title} </SText> } @@ -68,9 +68,6 @@ const styles = Steezy.create(({ colors }) => ({ backgroundColor: colors.backgroundContentTint, marginBottom: 4, }, - title: { - marginRight: 40, - }, iconContainer: { padding: 8, backgroundColor: colors.backgroundContentAttention, From 7cf38f139d5fe43db22eb0dcc092dafa9c5c7b46 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Wed, 30 Aug 2023 20:38:09 +0300 Subject: [PATCH 21/45] bump(mobile): 3.3.2 365 --- packages/mobile/android/app/build.gradle | 2 +- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 62c23a95c..fc15ce4c8 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -123,7 +123,7 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 364 + versionCode 365 versionName "3.3.2" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index fe88debc5..eadb5b885 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 364; + CURRENT_PROJECT_VERSION = 365; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1253,7 +1253,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 364; + CURRENT_PROJECT_VERSION = 365; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; From 29b2d08f4e686f4387f5bf4777147fd96dd9b295 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Thu, 31 Aug 2023 11:56:14 +0300 Subject: [PATCH 22/45] fix(mobile): some fixes --- .../components/BottomButtonWrap/BottomButtonWrap.tsx | 5 +++-- packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx | 3 ++- packages/mobile/src/translation/locales/en.json | 1 + packages/mobile/src/translation/locales/ru.json | 8 +++++++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/mobile/src/shared/components/BottomButtonWrap/BottomButtonWrap.tsx b/packages/mobile/src/shared/components/BottomButtonWrap/BottomButtonWrap.tsx index 17a84e398..b24c2b718 100644 --- a/packages/mobile/src/shared/components/BottomButtonWrap/BottomButtonWrap.tsx +++ b/packages/mobile/src/shared/components/BottomButtonWrap/BottomButtonWrap.tsx @@ -5,12 +5,13 @@ import { ns } from '$utils'; import * as S from './BottomButtonWrap.style'; import { useTheme } from '$hooks'; -export const BottomButtonWrapHelper: FC = () => { +export const BottomButtonWrapHelper: FC<{ safeArea?: boolean }> = (props) => { + const { safeArea = true } = props; const { bottom } = useSafeAreaInsets(); return ( <S.Helper style={{ - height: ns(56 + 16) + bottom + ns(16), + height: ns(56 + 16) + (safeArea ? bottom : 0) + ns(16), }} /> ); diff --git a/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx b/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx index b8130f5ac..f5b063159 100644 --- a/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx +++ b/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx @@ -13,7 +13,7 @@ import Animated, { } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Ton } from '$libs/Ton'; -import { BottomButtonWrap } from '$shared/components'; +import { BottomButtonWrap, BottomButtonWrapHelper } from '$shared/components'; import { openСonfirmRenewAllDomains } from './components/СonfirmRenewAllDomains'; import { CryptoCurrencies } from '$shared/constants'; @@ -72,6 +72,7 @@ export const RenewAllDomainModal = memo(() => { <ExpiringListItem key={domain.name} domain={domain} onRenew={handleRenew} /> ))} </List> + <BottomButtonWrapHelper safeArea={false} /> </Animated.ScrollView> <BottomButtonWrap> <Button onPress={handleRenewAll}> diff --git a/packages/mobile/src/translation/locales/en.json b/packages/mobile/src/translation/locales/en.json index 8bbdef879..54763c7d9 100644 --- a/packages/mobile/src/translation/locales/en.json +++ b/packages/mobile/src/translation/locales/en.json @@ -122,6 +122,7 @@ "other": "Expires in %{count} days" }, "dns_alert_expiring_many": { + "one": "You have %{count} expiring domain. Renew all until %{untilDate}.", "few": "You have %{count} expiring domains. Renew all until %{untilDate}.", "other": "You have %{count} expiring domains. Renew all until %{untilDate}.", "many": "You have %{count} expiring domains. Renew all until %{untilDate}." diff --git a/packages/mobile/src/translation/locales/ru.json b/packages/mobile/src/translation/locales/ru.json index 113de51e8..3ce628a3c 100644 --- a/packages/mobile/src/translation/locales/ru.json +++ b/packages/mobile/src/translation/locales/ru.json @@ -124,11 +124,17 @@ "many": "Истекает через %{count} дней" }, "dns_alert_expiring_many": { + "one": "У вас есть %{count} домен с истекающим сроком аренды. Продлить все до %{untilDate}.", "few": "У вас есть %{count} домена с истекающим сроком аренды. Продлить все до %{untilDate}.", "other": "У вас есть %{count} доменов с истекающим сроком аренды. Продлить все до %{untilDate}.", "many": "У вас есть %{count} доменов с истекающим сроком аренды. Продлить все до %{untilDate}." }, - "dns_alert_expiring_one": "%{domain} истекает через %{expire} дней. Продлить до %{untilDate}.", + "dns_alert_expiring_one": { + "one": "%{domain} истекает через %{count} день. Продлить до %{untilDate}.", + "few": "%{domain} истекает через %{count} дня. Продлить до %{untilDate}.", + "other": "%{domain} истекает через %{count} дней. Продлить до %{untilDate}.", + "many": "%{domain} истекает через %{count} дней. Продлить до %{untilDate}." + }, "dns_renew_all_until_btn": "Продлить все до %{untilDate}", "expiring_domains": "Истекающие домены", From dd53a8ffecfc7d0243dee5a790d12af8bad72d8a Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Thu, 31 Aug 2023 11:56:35 +0300 Subject: [PATCH 23/45] bump(mobile): 3.3.2 366 --- packages/mobile/android/app/build.gradle | 2 +- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index fc15ce4c8..f519bc703 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -123,7 +123,7 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 365 + versionCode 366 versionName "3.3.2" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index eadb5b885..05a1f08f1 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 365; + CURRENT_PROJECT_VERSION = 366; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1253,7 +1253,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 365; + CURRENT_PROJECT_VERSION = 366; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; From 868a24f65a9b8ffb71c64ed3df41ccc56bae1dec Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Thu, 31 Aug 2023 12:46:03 +0300 Subject: [PATCH 24/45] fix(mobile): translations --- packages/mobile/src/translation/locales/ru.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/mobile/src/translation/locales/ru.json b/packages/mobile/src/translation/locales/ru.json index 3ce628a3c..905b5a06d 100644 --- a/packages/mobile/src/translation/locales/ru.json +++ b/packages/mobile/src/translation/locales/ru.json @@ -124,16 +124,16 @@ "many": "Истекает через %{count} дней" }, "dns_alert_expiring_many": { - "one": "У вас есть %{count} домен с истекающим сроком аренды. Продлить все до %{untilDate}.", - "few": "У вас есть %{count} домена с истекающим сроком аренды. Продлить все до %{untilDate}.", - "other": "У вас есть %{count} доменов с истекающим сроком аренды. Продлить все до %{untilDate}.", - "many": "У вас есть %{count} доменов с истекающим сроком аренды. Продлить все до %{untilDate}." + "one": "У вас есть %{count} домен с истекающим сроком аренды. Продлите все до %{untilDate}.", + "few": "У вас есть %{count} домена с истекающим сроком аренды. Продлите все до %{untilDate}.", + "other": "У вас есть %{count} доменов с истекающим сроком аренды. Продлите все до %{untilDate}.", + "many": "У вас есть %{count} доменов с истекающим сроком аренды. Продлите все до %{untilDate}." }, "dns_alert_expiring_one": { - "one": "%{domain} истекает через %{count} день. Продлить до %{untilDate}.", - "few": "%{domain} истекает через %{count} дня. Продлить до %{untilDate}.", - "other": "%{domain} истекает через %{count} дней. Продлить до %{untilDate}.", - "many": "%{domain} истекает через %{count} дней. Продлить до %{untilDate}." + "one": "%{domain} истекает через %{count} день. Продлите до %{untilDate}.", + "few": "%{domain} истекает через %{count} дня. Продлите до %{untilDate}.", + "other": "%{domain} истекает через %{count} дней. Продлите до %{untilDate}.", + "many": "%{domain} истекает через %{count} дней. Продлите до %{untilDate}." }, "dns_renew_all_until_btn": "Продлить все до %{untilDate}", From b15e38464ff57839ecdcf7727ad4e8ea7c1d4ef2 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Fri, 1 Sep 2023 16:20:22 +0400 Subject: [PATCH 25/45] fix(mobile): translations --- .../mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx | 4 ++-- packages/mobile/src/translation/locales/en.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx index 12ad5b95b..e23935f1d 100644 --- a/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx +++ b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx @@ -24,12 +24,12 @@ export const ExpiringDomainCell = memo<ApprovalCellProps>(({ withoutSpacer, styl if (expiringDomains.length === 1) { const domain = expiringDomains[0]; const countOfDays = getCountOfDays(+new Date(), domain.expiring_at * 1000); - const expire = countOfDays === 366 ? countOfDays - 1 : countOfDays; + const count = countOfDays === 366 ? countOfDays - 1 : countOfDays; return t('dns_alert_expiring_one', { domain: maskifyDomain(domain.name), untilDate, - expire, + count, }); } diff --git a/packages/mobile/src/translation/locales/en.json b/packages/mobile/src/translation/locales/en.json index 54763c7d9..c8215e19f 100644 --- a/packages/mobile/src/translation/locales/en.json +++ b/packages/mobile/src/translation/locales/en.json @@ -127,7 +127,7 @@ "other": "You have %{count} expiring domains. Renew all until %{untilDate}.", "many": "You have %{count} expiring domains. Renew all until %{untilDate}." }, - "dns_alert_expiring_one": "%{domain} expires in %{expire} days. Renew it until %{untilDate}.", + "dns_alert_expiring_one": "%{domain} expires in %{count} days. Renew it until %{untilDate}.", "dns_renew_all_until_btn": "Renew all until %{untilDate}", From e50ae7fd240cf977e00ec488a99da6602114f906 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Fri, 1 Sep 2023 16:20:49 +0400 Subject: [PATCH 26/45] bump(mobile): 3.3.2 367 --- packages/mobile/android/app/build.gradle | 2 +- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index f519bc703..ab23d7d24 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -123,7 +123,7 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 366 + versionCode 367 versionName "3.3.2" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 05a1f08f1..7663f7b6c 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 366; + CURRENT_PROJECT_VERSION = 367; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1253,7 +1253,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 366; + CURRENT_PROJECT_VERSION = 367; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; From 71387de5b36571e4db5702ef5165525f4a5723db Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Wed, 13 Sep 2023 20:15:57 +0400 Subject: [PATCH 27/45] fix(mobile): disable refresh browser when catch error --- packages/mobile/src/core/BuyFiat/BuyFiat.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/mobile/src/core/BuyFiat/BuyFiat.tsx b/packages/mobile/src/core/BuyFiat/BuyFiat.tsx index 739881f70..1cf66a096 100644 --- a/packages/mobile/src/core/BuyFiat/BuyFiat.tsx +++ b/packages/mobile/src/core/BuyFiat/BuyFiat.tsx @@ -13,7 +13,7 @@ import { useExchangeMethodInfo, useTheme } from '$hooks'; import { goBack } from '$navigation'; import { getServerConfig } from '$shared/constants'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { deviceWidth, isAndroid, trackEvent } from '$utils'; +import { debugLog, deviceWidth, isAndroid, trackEvent } from '$utils'; import { useDeeplinking } from '$libs/deeplinking'; export const BuyFiat: FC<BuyFiatProps> = ({ route }) => { @@ -34,14 +34,14 @@ export const BuyFiat: FC<BuyFiatProps> = ({ route }) => { const deeplinking = useDeeplinking(); - const handleHttpError = useCallback(() => { - setWebViewKey(webViewKey + 1); + const handleHttpError = useCallback((event) => { + debugLog('[BuyFiat:handleHttpError]', event.nativeEvent.url); }, [setWebViewKey, webViewKey]); - const handleError = useCallback((e) => { - console.log(e.nativeEvent); + const handleError = useCallback((event) => { + debugLog('[BuyFiat:handleError]', event.nativeEvent.url); }, []); - + const webviewUrl = useMemo(() => { const addr = address[currency]; From 0e04193b2428d001232cf426129b118d8ef6bef9 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Wed, 13 Sep 2023 20:21:36 +0400 Subject: [PATCH 28/45] bump(mobile): 3.3.3 369 --- packages/mobile/android/app/build.gradle | 4 ++-- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index ab23d7d24..e257e1efd 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -123,8 +123,8 @@ android { applicationId "com.ton_keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 367 - versionName "3.3.2" + versionCode 369 + versionName "3.3.3" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 7663f7b6c..274c652f5 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 367; + CURRENT_PROJECT_VERSION = 369; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; ENABLE_BITCODE = NO; @@ -1228,7 +1228,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.3.2; + MARKETING_VERSION = 3.3.3; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1253,7 +1253,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = ton_keeper/ton_keeper.entitlements; - CURRENT_PROJECT_VERSION = 367; + CURRENT_PROJECT_VERSION = 369; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = CT523DK2KC; INFOPLIST_FILE = ton_keeper/SupportingFiles/Info.plist; @@ -1262,7 +1262,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.3.2; + MARKETING_VERSION = 3.3.3; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", From 11b2dbc0b2c3ddf226381ce8102b2ce134dcd896 Mon Sep 17 00:00:00 2001 From: bogoslavskiy <luice96@yahoo.com> Date: Thu, 28 Sep 2023 15:46:22 +0400 Subject: [PATCH 29/45] fix(mobile): fixes after merge --- packages/mobile/package.json | 9 ++++----- .../src/tabs/Wallet/RenewAllDomainModal.tsx | 9 +++++---- .../Wallet/components/ExpiringDomainCell.tsx | 2 +- .../\320\241onfirmRenewAllDomains.tsx" | 18 ++++++++++-------- yarn.lock | 8 ++++---- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 4fc209573..12ba14cb7 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -12,7 +12,6 @@ "clear-cache": "rm -rf tmp/haste-map-react-native-packager && rm -rf node_modules && yarn && yarn start --reset-cache", "build:android": "cd android && ./gradlew assembleRelease && ./gradlew bundleRelease && cd .. && node scripts/prepare_builds.js", "build:android:apk": "cd android && ./gradlew assembleRelease && cd ..", - "build:android:google-play": "git apply ../../patches/google-play-release.patch && yarn build:android:apk && git apply -R ../../patches/google-play-release.patch", "build:android:apk-site": "ENVFILE=.env.site yarn build:android:apk", "build:android:google-play": "git apply ../../patches/google-play-release.patch && yarn build:android && git apply -R ../../patches/google-play-release.patch", "clean:android": "cd android && ./gradlew clean && cd ..", @@ -36,10 +35,10 @@ "@react-native-async-storage/async-storage": "^1.15.5", "@react-native-community/clipboard": "^1.5.1", "@react-native-community/netinfo": "^9.3.2", - "@react-native-firebase/analytics": "^18.5.0", - "@react-native-firebase/app": "^18.5.0", - "@react-native-firebase/crashlytics": "^18.5.0", - "@react-native-firebase/messaging": "^18.5.0", + "@react-native-firebase/analytics": "18.5.0", + "@react-native-firebase/app": "18.5.0", + "@react-native-firebase/crashlytics": "18.5.0", + "@react-native-firebase/messaging": "18.5.0", "@reduxjs/toolkit": "^1.6.1", "@shopify/flash-list": "^1.5.0", "@tonapps/tonlogin-client": "0.2.5", diff --git a/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx b/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx index f5b063159..b49a50059 100644 --- a/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx +++ b/packages/mobile/src/tabs/Wallet/RenewAllDomainModal.tsx @@ -1,5 +1,6 @@ -import { useNavigation } from '$libs/navigation'; -import { navigate, openNFT } from '$navigation/helper'; +import { useNavigation } from '@tonkeeper/router'; +import { openNFT } from '$navigation/helper'; +import { navigate } from '$navigation/imperative'; import { ExpiringDomainItem } from '$store/zustand/domains/types'; import { useExpiringDomains } from '$store/zustand/domains/useExpiringDomains'; import { t } from '$translation'; @@ -12,10 +13,10 @@ import Animated, { useSharedValue, } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { Ton } from '$libs/Ton'; import { BottomButtonWrap, BottomButtonWrapHelper } from '$shared/components'; import { openСonfirmRenewAllDomains } from './components/СonfirmRenewAllDomains'; import { CryptoCurrencies } from '$shared/constants'; +import { Address } from '@tonkeeper/shared/Address'; export const RenewAllDomainModal = memo(() => { const safeArea = useSafeAreaInsets(); @@ -95,7 +96,7 @@ const ExpiringListItem = memo( const handlePress = useCallback(() => { openNFT({ - address: Ton.formatAddress(domain.dns_item.address), + address: Address.parse(domain.dns_item.address).toFriendly(), currency: CryptoCurrencies.Ton, }); }, [domain.dns_item.address]); diff --git a/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx index e23935f1d..ff7b20f10 100644 --- a/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx +++ b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx @@ -6,7 +6,7 @@ import { useExpiringDomains } from '$store/zustand/domains/useExpiringDomains'; import { ONE_YEAR_MILISEC, format, getCountOfDays, getLocale } from '$utils/date'; import { t } from '$translation'; import { openRenewAllDomainModal } from '../RenewAllDomainModal'; -import { maskifyAddress, maskifyDomain } from '$utils'; +import { maskifyAddress, maskifyDomain } from '$utils/address'; interface ApprovalCellProps { withoutSpacer?: boolean; diff --git "a/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" index 8b920f05c..265cce98b 100644 --- "a/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" +++ "b/packages/mobile/src/tabs/Wallet/components/\320\241onfirmRenewAllDomains.tsx" @@ -1,6 +1,6 @@ -import { Modal, useNavigation } from '$libs/navigation'; -import { SheetActions } from '$libs/navigation/components/Modal/Sheet/SheetsProvider'; -import { push } from '$navigation/helper'; +import { useNavigation, SheetActions } from '@tonkeeper/router'; +import { Modal } from '@tonkeeper/uikit'; +import { push } from '$navigation/imperative'; import { t } from '$translation'; import { Icon, List, Spacer, Text, TransitionOpacity } from '$uikit'; import { memo, useCallback, useState } from 'react'; @@ -8,23 +8,25 @@ import { View, StyleSheet } from 'react-native'; import * as S from '../../../core/ModalContainer/NFTOperations/NFTOperations.styles'; import { useExpiringDomains } from '$store/zustand/domains/useExpiringDomains'; import { formatter } from '$utils/formatter'; -import { useFiatValue, useWallet } from '$hooks'; +import { useFiatValue } from '$hooks/useFiatValue'; +import { useWallet } from '$hooks/useWallet'; import { CryptoCurrencies, Decimals } from '$shared/constants'; import { copyText } from '$hooks/useCopyText'; import { useUnlockVault } from '$core/ModalContainer/NFTOperations/useUnlockVault'; import { RenewAllProgressButton } from './RenewAllProgressButton'; -import { Base64, debugLog, delay, triggerNotificationSuccess } from '$utils'; +import { Base64, delay, triggerNotificationSuccess } from '$utils'; +import { debugLog } from '$utils/debugLog'; import { Toast } from '$store/zustand/toast'; import { Ton } from '$libs/Ton'; import TonWeb from 'tonweb'; import { Tonapi } from '$libs/Tonapi'; -import { eventsActions } from '$store/events'; import { useDispatch } from 'react-redux'; import { checkIsInsufficient, openInsufficientFundsModal, } from '$core/ModalContainer/InsufficientFunds/InsufficientFunds'; import BigNumber from 'bignumber.js'; +import { tk } from '@tonkeeper/shared/tonkeeper'; enum States { INITIAL, @@ -109,7 +111,7 @@ export const СonfirmRenewAllDomains = memo((props) => { const queryMsg = await tx.getQuery(); const boc = Base64.encodeBytes(await queryMsg.toBoc(false)); await Tonapi.sendBoc(boc); - dispatch(eventsActions.pollEvents()); + tk.wallet.activityList.reload(); await delay(15000); @@ -132,7 +134,7 @@ export const СonfirmRenewAllDomains = memo((props) => { } }, [setCurrent]); - const fiatAmount = `≈ ${fiatValue.fiatInfo.amount}`; + const fiatAmount = `≈ ${fiatValue.fiat}`; const formattedAmount = formatter.format(amount, { currency: 'TON', currencySeparator: 'wide', diff --git a/yarn.lock b/yarn.lock index b9f0053dd..5265ac03a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2671,12 +2671,12 @@ resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-9.4.1.tgz#7b880758adca65fe47ee866cf7b00416b9dcc192" integrity sha512-dAbY5mfw+6Kas/GJ6QX9AZyY+K+eq9ad4Su6utoph/nxyH3whp5cMSgRNgE2VhGQVRZ/OG0qq3IaD3+wzoqJXw== -"@react-native-firebase/analytics@^18.5.0": +"@react-native-firebase/analytics@18.5.0": version "18.5.0" resolved "https://registry.yarnpkg.com/@react-native-firebase/analytics/-/analytics-18.5.0.tgz#cca5858e44f3f8cbf387fa7cd6debb95fbcf8023" integrity sha512-+8fkDedcftieOBEkNdqEW0lS/FB9SKzfUc3yYXnJcLTy3Ceg9JZga/tWz4BGbbxPddUy1Sm2UuWDuOxsdkFUyA== -"@react-native-firebase/app@^18.5.0": +"@react-native-firebase/app@18.5.0": version "18.5.0" resolved "https://registry.yarnpkg.com/@react-native-firebase/app/-/app-18.5.0.tgz#198a133229d1b8710f257e76d9703dcb7e4cd6e0" integrity sha512-AhHQi5KFDlKZn/lH7rEYtLfpsGamEq+P/cXZWcNPcP0WGlmi++abk7Pxnn4MjnG7TNhEyG/C9uq//qb6VhXaZg== @@ -2684,14 +2684,14 @@ opencollective-postinstall "^2.0.1" superstruct "^0.6.2" -"@react-native-firebase/crashlytics@^18.5.0": +"@react-native-firebase/crashlytics@18.5.0": version "18.5.0" resolved "https://registry.yarnpkg.com/@react-native-firebase/crashlytics/-/crashlytics-18.5.0.tgz#b5c1e15d260d5243c5ffb2963114bb7e42fe2b7f" integrity sha512-i/jt/kNyhLws10/DeYgSQbGIekNPCAdtdg36NmFSw7URZf+1JUczKePbHqq3TvMFZJi5OjnDj06Fj9oenQKjWA== dependencies: stacktrace-js "^2.0.0" -"@react-native-firebase/messaging@^18.5.0": +"@react-native-firebase/messaging@18.5.0": version "18.5.0" resolved "https://registry.yarnpkg.com/@react-native-firebase/messaging/-/messaging-18.5.0.tgz#2a80b25816470e9843682e031a3a113566067ce6" integrity sha512-y1FApYxBMcygmbWBqUPFC+fCfvx6Yf6TdZewun7kPwx+S+tkYzoKx1IsXtxOXtqyJjCNEYirjFgNrs5SSd02zA== From d634064bafdd89733542e749ddfe31788fddab5c Mon Sep 17 00:00:00 2001 From: Andrey Sorokin <sorokin0andrey@gmail.com> Date: Fri, 6 Oct 2023 04:04:37 +0600 Subject: [PATCH 30/45] Merge branch 'main' into release/3.4.1 --- packages/mobile/package.json | 10 +-- .../src/assets/icons/png/ic-globe-28@4x.png | Bin 0 -> 2987 bytes .../src/assets/icons/svg/28/ic-globe-28.svg | 5 ++ packages/mobile/src/core/BuyFiat/BuyFiat.tsx | 10 +-- packages/mobile/src/navigation/ModalStack.tsx | 3 +- .../mobile/src/store/zustand/domains/types.ts | 41 ++++++------ .../mobile/src/uikit/Icon/IconsMobileList.ts | 1 + .../mobile/src/uikit/Icon/generated.types.ts | 3 + packages/mobile/src/uikit/List/ListItem.tsx | 6 +- .../mobile/src/uikit/TransitionOpacity.tsx | 59 ++++++++++-------- packages/shared/i18n/locales/en.json | 3 +- yarn.lock | 8 +-- 12 files changed, 84 insertions(+), 65 deletions(-) create mode 100644 packages/mobile/src/assets/icons/png/ic-globe-28@4x.png create mode 100644 packages/mobile/src/assets/icons/svg/28/ic-globe-28.svg diff --git a/packages/mobile/package.json b/packages/mobile/package.json index cf7dec821..12ba14cb7 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -12,8 +12,8 @@ "clear-cache": "rm -rf tmp/haste-map-react-native-packager && rm -rf node_modules && yarn && yarn start --reset-cache", "build:android": "cd android && ./gradlew assembleRelease && ./gradlew bundleRelease && cd .. && node scripts/prepare_builds.js", "build:android:apk": "cd android && ./gradlew assembleRelease && cd ..", - "build:android:google-play": "git apply ../../patches/google-play-release.patch && yarn build:android:apk && git apply -R ../../patches/google-play-release.patch", "build:android:apk-site": "ENVFILE=.env.site yarn build:android:apk", + "build:android:google-play": "git apply ../../patches/google-play-release.patch && yarn build:android && git apply -R ../../patches/google-play-release.patch", "clean:android": "cd android && ./gradlew clean && cd ..", "open_build_folder": "open ./android/app/build/outputs/bundle", "postinstall": "expo-yarn-workspaces postinstall && patch-package", @@ -35,10 +35,10 @@ "@react-native-async-storage/async-storage": "^1.15.5", "@react-native-community/clipboard": "^1.5.1", "@react-native-community/netinfo": "^9.3.2", - "@react-native-firebase/analytics": "^18.5.0", - "@react-native-firebase/app": "^18.5.0", - "@react-native-firebase/crashlytics": "^18.5.0", - "@react-native-firebase/messaging": "^18.5.0", + "@react-native-firebase/analytics": "18.5.0", + "@react-native-firebase/app": "18.5.0", + "@react-native-firebase/crashlytics": "18.5.0", + "@react-native-firebase/messaging": "18.5.0", "@reduxjs/toolkit": "^1.6.1", "@shopify/flash-list": "^1.5.0", "@tonapps/tonlogin-client": "0.2.5", diff --git a/packages/mobile/src/assets/icons/png/ic-globe-28@4x.png b/packages/mobile/src/assets/icons/png/ic-globe-28@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..b96ef40a450df70153fa5941383587dfe27e9fcb GIT binary patch literal 2987 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 literal 0 HcmV?d00001 diff --git a/packages/mobile/src/assets/icons/svg/28/ic-globe-28.svg b/packages/mobile/src/assets/icons/svg/28/ic-globe-28.svg new file mode 100644 index 000000000..49eba3b39 --- /dev/null +++ b/packages/mobile/src/assets/icons/svg/28/ic-globe-28.svg @@ -0,0 +1,5 @@ +<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M16.9429 20.9214C16.4787 21.2508 15.9883 21.4155 15.4717 21.4155C14.8802 21.4155 14.431 21.262 14.124 20.9551C13.8245 20.6481 13.6149 20.2588 13.4951 19.7871C13.3828 19.3079 13.3192 18.81 13.3042 18.2934C13.2967 17.7768 13.293 17.3089 13.293 16.8896C13.293 16.3506 13.1021 15.9088 12.7202 15.5644C12.3459 15.2125 11.8667 15.0366 11.2827 15.0366C10.3319 15.0366 9.5944 14.7783 9.07031 14.2617C8.54622 13.7451 8.28418 13.0264 8.28418 12.1055C8.28418 11.3942 8.49381 10.7428 8.91309 10.1514C9.33236 9.55988 9.89014 9.08446 10.5864 8.72508C11.2902 8.36571 12.0614 8.18602 12.8999 8.18602C13.2743 8.18602 13.6486 8.23843 14.0229 8.34325C14.4048 8.44058 14.7492 8.53791 15.0562 8.63524C15.3706 8.73257 15.6139 8.78123 15.7861 8.78123C15.9359 8.78123 16.0519 8.70262 16.1343 8.54539C16.2166 8.38068 16.2578 8.18228 16.2578 7.95018C16.2578 7.78547 16.1792 7.60204 16.022 7.39989C15.8722 7.19025 15.6813 7.01431 15.4492 6.87206C15.2171 6.72232 14.9813 6.64745 14.7417 6.64745C14.6743 6.64745 14.6219 6.66616 14.5845 6.7036C14.5545 6.73355 14.5321 6.78595 14.5171 6.86082C14.5096 7.02554 14.4572 7.16405 14.3599 7.27635C14.27 7.38117 14.1315 7.43358 13.9443 7.43358C13.6673 7.43358 13.4277 7.37368 13.2256 7.25389C13.0234 7.12661 12.8363 6.99933 12.6641 6.87206C12.4993 6.74478 12.3346 6.68114 12.1699 6.68114C12.0052 6.68114 11.8704 6.75226 11.7656 6.89452C11.6608 7.03677 11.5485 7.20148 11.4287 7.38866C11.3164 7.57583 11.1667 7.74054 10.9795 7.8828C10.7998 8.02505 10.5452 8.09618 10.2158 8.09618C9.90885 8.09618 9.66553 8.01008 9.48584 7.83788C9.30615 7.66567 9.21631 7.44107 9.21631 7.16405C9.21631 6.93944 9.2762 6.74103 9.396 6.56883C9.51579 6.39663 9.6543 6.22069 9.81152 6.041C9.97624 5.85383 10.1185 5.6367 10.2383 5.38963C10.3581 5.14256 10.418 4.8356 10.418 4.46873C10.418 4.09439 10.5677 3.72378 10.8672 3.35692C10.914 3.29845 10.9738 3.22515 11.0414 3.14307C6.26348 4.44202 2.75 8.81069 2.75 14C2.75 20.2132 7.7868 25.25 14 25.25C19.8771 25.25 24.7016 20.7434 25.2064 14.9973C24.8498 14.9552 24.2583 14.4242 24.0068 14.0146C23.7523 13.5879 23.5501 13.1387 23.4004 12.667C23.2507 12.1953 23.1196 11.7947 23.0073 11.4653C22.9025 11.1284 22.779 10.9599 22.6367 10.9599C22.5544 10.9599 22.487 10.9936 22.4346 11.061C22.3822 11.1284 22.3185 11.1958 22.2437 11.2632C22.1688 11.3231 22.0565 11.353 21.9067 11.353C21.7345 11.353 21.5361 11.2744 21.3115 11.1172C21.0944 10.9599 20.8698 10.7803 20.6377 10.5781C20.4131 10.3685 20.1997 10.185 19.9976 10.0278C19.8029 9.87059 19.6419 9.79198 19.5146 9.79198C19.3724 9.79198 19.2414 9.85936 19.1216 9.99413C19.0093 10.1289 18.9531 10.2861 18.9531 10.4658C18.9531 10.6081 19.0692 10.7615 19.3013 10.9263C19.5334 11.091 19.7992 11.2744 20.0986 11.4765C20.4056 11.6712 20.6751 11.8808 20.9072 12.1055C21.1393 12.3226 21.2554 12.5547 21.2554 12.8017C21.2554 13.1237 21.1543 13.4306 20.9521 13.7226C20.7575 14.0146 20.5591 14.2729 20.3569 14.4975C20.1623 14.7222 20.0649 14.9018 20.0649 15.0366C20.0649 15.3211 20.0837 15.6805 20.1211 16.1147C20.166 16.5415 20.0462 16.987 19.7617 17.4512C19.6494 17.6383 19.4585 17.9453 19.189 18.3721C18.9269 18.7988 18.605 19.248 18.2231 19.7197C17.8413 20.1839 17.4146 20.5845 16.9429 20.9214Z" fill="white"/> +<path d="M17.8076 6.23192C17.9349 6.15705 18.0996 6.11961 18.3018 6.11961C18.4215 6.11961 18.5413 6.16079 18.6611 6.24315C18.7884 6.31802 18.9531 6.35545 19.1553 6.35545C19.2826 6.35545 19.3874 6.30679 19.4697 6.20946C19.5521 6.10464 19.5933 5.97736 19.5933 5.82762C19.5933 5.56558 19.4622 5.34471 19.2002 5.16502C18.9382 4.98534 18.6087 4.89549 18.2119 4.89549C17.7477 4.89549 17.3883 4.97036 17.1338 5.1201C16.8792 5.26235 16.752 5.46825 16.752 5.73778C16.752 5.91747 16.8081 6.06346 16.9204 6.17577C17.0402 6.28058 17.1862 6.33299 17.3584 6.33299C17.5381 6.33299 17.6878 6.2993 17.8076 6.23192Z" fill="white"/> +<path opacity="0.32" d="M17.2332 21.3292L17.2345 21.3283C17.7431 20.965 18.2017 20.5341 18.6103 20.0374L18.6127 20.0343C19.0047 19.5502 19.3392 19.0842 19.6144 18.6364C19.8822 18.2123 20.0749 17.9026 20.1903 17.7103C20.5148 17.1801 20.6775 16.6277 20.6199 16.0675C20.5846 15.6576 20.5671 15.3243 20.566 15.0643C20.5686 15.0584 20.5726 15.05 20.5786 15.0389C20.6038 14.9924 20.6514 14.9227 20.7329 14.8283C20.9515 14.585 21.1626 14.3095 21.3668 14.0035C21.619 13.6379 21.7563 13.2344 21.7563 12.8018C21.7563 12.3809 21.5538 12.0259 21.253 11.7434C21.1471 11.6411 21.035 11.5422 20.9168 11.4468C20.951 11.4724 20.9852 11.4975 21.0192 11.5222L21.0192 11.5222L21.0258 11.5268C21.2886 11.7108 21.5884 11.853 21.9077 11.853C22.1075 11.853 22.3476 11.8159 22.5469 11.6615C22.6551 11.98 22.781 12.3654 22.9248 12.8183C23.0862 13.3268 23.3042 13.8112 23.5784 14.2708L23.5784 14.2708L23.5817 14.2763C23.7415 14.5365 23.9911 14.8107 24.2454 15.0257C24.3747 15.1349 24.5153 15.2376 24.6582 15.3186C24.7945 15.3958 24.9653 15.4722 25.1488 15.4939L25.6604 15.5543L25.7055 15.0411C25.7356 14.698 25.751 14.3507 25.751 14C25.751 7.51065 20.4903 2.25 14.001 2.25C12.9323 2.25 11.8963 2.39279 10.9112 2.66061L10.7576 2.70236L10.6564 2.8252C10.5889 2.90722 10.5279 2.98191 10.4792 3.04273C10.1276 3.47416 9.91895 3.95221 9.91895 4.46876C9.91895 4.78427 9.86703 5.01131 9.78935 5.17152C9.68727 5.38207 9.56922 5.56063 9.43714 5.71071L9.43621 5.71178C9.26843 5.90353 9.11821 6.09403 8.98652 6.28333C8.80423 6.54537 8.71729 6.84499 8.71729 7.16408C8.71729 7.5604 8.85114 7.92125 9.14086 8.1989C9.34788 8.39729 9.59719 8.51351 9.86802 8.56475C9.32614 8.91627 8.87035 9.34844 8.50615 9.86223C8.02821 10.5365 7.78516 11.2885 7.78516 12.1055C7.78516 13.1246 8.07819 13.9849 8.72029 14.6178C9.363 15.2513 10.2415 15.5366 11.2837 15.5366C11.7592 15.5366 12.1099 15.6761 12.3787 15.9288L12.3787 15.9289L12.3863 15.9357C12.6574 16.1803 12.7939 16.4844 12.7939 16.8897C12.7939 17.3118 12.7977 17.7822 12.8052 18.3007L12.8052 18.3007L12.8054 18.308C12.8213 18.8564 12.8889 19.3878 13.0093 19.9012L13.0092 19.9012L13.0115 19.9102C13.1499 20.4554 13.3983 20.9263 13.7671 21.3043L13.7671 21.3043L13.7714 21.3086C14.2021 21.7393 14.7979 21.9155 15.4727 21.9155C16.1035 21.9155 16.6935 21.7122 17.2332 21.3292ZM20.3103 10.9552C20.4457 11.0731 20.5794 11.1844 20.7114 11.2888C20.6032 11.2095 20.4906 11.1326 20.3735 11.0582C20.0789 10.8593 19.8183 10.6794 19.5916 10.5185C19.5242 10.4707 19.4831 10.4336 19.4594 10.4087C19.4665 10.3748 19.4805 10.3472 19.5018 10.3202C19.5082 10.3134 19.5138 10.3078 19.5186 10.3034C19.5552 10.322 19.61 10.3568 19.6844 10.4168L19.6843 10.4169L19.6916 10.4225C19.8798 10.569 20.0818 10.7423 20.2975 10.9437L20.3038 10.9495L20.3103 10.9552ZM12.9009 7.68605C12.5088 7.68605 12.1284 7.72226 11.7603 7.79508C11.7935 7.74894 11.8249 7.70145 11.8544 7.65259C11.9683 7.4748 12.0732 7.32131 12.1691 7.19114C12.1722 7.18703 12.1748 7.1838 12.1769 7.1813C12.2069 7.18266 12.2655 7.19522 12.3593 7.26773L12.3592 7.26782L12.3678 7.27417C12.5511 7.40963 12.7487 7.54388 12.9602 7.67704L12.9601 7.67719L12.9717 7.68407C12.9731 7.68494 12.9746 7.68581 12.9761 7.68668C12.951 7.68626 12.926 7.68605 12.9009 7.68605ZM15.2066 8.15812C14.9242 8.06863 14.6135 7.97982 14.2748 7.89159C14.4451 7.84503 14.6082 7.75558 14.7396 7.60281C14.8478 7.47766 14.9225 7.33683 14.9673 7.18661C15.034 7.2097 15.1044 7.24403 15.1791 7.29223L15.1791 7.29234L15.1889 7.29838C15.3672 7.40764 15.5075 7.53848 15.6161 7.69053L15.622 7.69883L15.6283 7.70689C15.7529 7.86709 15.7588 7.94337 15.7588 7.95021C15.7588 8.09376 15.7378 8.19906 15.7081 8.27621C15.6021 8.26432 15.439 8.22999 15.2066 8.15812ZM17.3787 5.5566L17.3787 5.55671L17.3883 5.5511C17.534 5.46537 17.7915 5.39552 18.2129 5.39552C18.534 5.39552 18.7583 5.46759 18.9184 5.57742C19.0774 5.68644 19.0942 5.76672 19.0942 5.82765C19.0942 5.83758 19.0938 5.84616 19.0931 5.85358C19.002 5.84782 18.9505 5.83016 18.9242 5.81686C18.7438 5.69764 18.5344 5.61964 18.3027 5.61964C18.0453 5.61964 17.7872 5.66637 17.5613 5.79737C17.5347 5.81168 17.4755 5.83302 17.3594 5.83302C17.3115 5.83302 17.2851 5.82388 17.2659 5.81127C17.2596 5.80061 17.2529 5.78029 17.2529 5.7378C17.2529 5.6669 17.2664 5.61936 17.3787 5.5566Z" fill="white" stroke="white"/> +</svg> diff --git a/packages/mobile/src/core/BuyFiat/BuyFiat.tsx b/packages/mobile/src/core/BuyFiat/BuyFiat.tsx index 77f181846..dff75a995 100644 --- a/packages/mobile/src/core/BuyFiat/BuyFiat.tsx +++ b/packages/mobile/src/core/BuyFiat/BuyFiat.tsx @@ -37,14 +37,14 @@ export const BuyFiat: FC<BuyFiatProps> = ({ route }) => { const deeplinking = useDeeplinking(); - const handleHttpError = useCallback(() => { - setWebViewKey(webViewKey + 1); + const handleHttpError = useCallback((event) => { + debugLog('[BuyFiat:handleHttpError]', event.nativeEvent.url); }, [setWebViewKey, webViewKey]); - const handleError = useCallback((e) => { - console.log(e.nativeEvent); + const handleError = useCallback((event) => { + debugLog('[BuyFiat:handleError]', event.nativeEvent.url); }, []); - + const webviewUrl = useMemo(() => { const addr = address[currency]; diff --git a/packages/mobile/src/navigation/ModalStack.tsx b/packages/mobile/src/navigation/ModalStack.tsx index 6226f8e04..c09a00ae9 100644 --- a/packages/mobile/src/navigation/ModalStack.tsx +++ b/packages/mobile/src/navigation/ModalStack.tsx @@ -56,7 +56,7 @@ export const ModalStack = React.memo(() => ( <Stack.Modal component={NFTSaleCancelModal} path="NFTSaleCancel" /> <Stack.Modal component={ExchangeModal} path="Exchange" /> <Stack.Modal component={OldExchange} path="OldExchange" /> - <Stack.Modal component={СonfirmRenewAllDomains} path="СonfirmRenewAllDomains"/> + <Stack.Modal component={СonfirmRenewAllDomains} path="СonfirmRenewAllDomains" /> <Stack.Modal component={NFTTransferInputAddressModal} path="NFTTransferInputAddress" @@ -71,6 +71,7 @@ export const ModalStack = React.memo(() => ( <Stack.Modal component={ReceiveModal} path="ReceiveModal" /> <Stack.Modal component={ReceiveJettonModal} path="/receive/jetton/" /> <Stack.Modal component={NFT} path="NFTItemDetails" /> + {/* <Stack.Modal component={Receive} path={AppStackRouteNames.Receive} /> */} <Stack.Modal component={Send} path={AppStackRouteNames.Send} /> <Stack.Modal component={RenewAllDomainModal} path="RenewAllDomains" /> <Stack.Modal component={ChooseCountry} path={AppStackRouteNames.ChooseCountry} /> diff --git a/packages/mobile/src/store/zustand/domains/types.ts b/packages/mobile/src/store/zustand/domains/types.ts index b83d03908..8d51a7317 100644 --- a/packages/mobile/src/store/zustand/domains/types.ts +++ b/packages/mobile/src/store/zustand/domains/types.ts @@ -1,43 +1,42 @@ export type RawAddress = string; export type ExpiringAt = number; - export interface ExpiringDomainItem { - expiring_at: number - name: string - dns_item: DnsItem + expiring_at: number; + name: string; + dns_item: DnsItem; } export interface DnsItem { - address: string - index: number - owner: Owner - collection: Collection - verified: boolean - metadata: Metadata - previews: Preview[] - dns: string - approved_by: string[] + address: string; + index: number; + owner: Owner; + collection: Collection; + verified: boolean; + metadata: Metadata; + previews: Preview[]; + dns: string; + approved_by: string[]; } export interface Owner { - address: string - is_scam: boolean + address: string; + is_scam: boolean; } export interface Collection { - address: string - name: string - description: string + address: string; + name: string; + description: string; } export interface Metadata { - name: string + name: string; } export interface Preview { - resolution: string - url: string + resolution: string; + url: string; } export type ExpiringDomains = { diff --git a/packages/mobile/src/uikit/Icon/IconsMobileList.ts b/packages/mobile/src/uikit/Icon/IconsMobileList.ts index 0f92433f9..8368c40f4 100644 --- a/packages/mobile/src/uikit/Icon/IconsMobileList.ts +++ b/packages/mobile/src/uikit/Icon/IconsMobileList.ts @@ -67,6 +67,7 @@ export const MobileIconsList = { 'ic-explore-28': require('$assets/icons/png/ic-explore-28.png'), 'ic-flash-28': require('$assets/icons/png/ic-flash-28.png'), 'ic-gear-28': require('$assets/icons/png/ic-gear-28.png'), + 'ic-globe-28': require('$assets/icons/png/ic-globe-28.png'), 'ic-home-28': require('$assets/icons/png/ic-home-28.png'), 'ic-jetton-28': require('$assets/icons/png/ic-jetton-28.png'), 'ic-key-28': require('$assets/icons/png/ic-key-28.png'), diff --git a/packages/mobile/src/uikit/Icon/generated.types.ts b/packages/mobile/src/uikit/Icon/generated.types.ts index ad8cd3a39..7991f0183 100644 --- a/packages/mobile/src/uikit/Icon/generated.types.ts +++ b/packages/mobile/src/uikit/Icon/generated.types.ts @@ -67,6 +67,7 @@ export type IconNames = | 'ic-explore-28' | 'ic-flash-28' | 'ic-gear-28' + | 'ic-globe-28' | 'ic-home-28' | 'ic-jetton-28' | 'ic-key-28' @@ -191,6 +192,7 @@ export const AllIcons = [ 'ic-explore-28', 'ic-flash-28', 'ic-gear-28', + 'ic-globe-28', 'ic-home-28', 'ic-jetton-28', 'ic-key-28', @@ -316,6 +318,7 @@ export const IconSizes = { 'ic-explore-28': 28, 'ic-flash-28': 28, 'ic-gear-28': 28, + 'ic-globe-28': 28, 'ic-home-28': 28, 'ic-jetton-28': 28, 'ic-key-28': 28, diff --git a/packages/mobile/src/uikit/List/ListItem.tsx b/packages/mobile/src/uikit/List/ListItem.tsx index 9ac47dcdc..aefaba6ca 100644 --- a/packages/mobile/src/uikit/List/ListItem.tsx +++ b/packages/mobile/src/uikit/List/ListItem.tsx @@ -33,6 +33,8 @@ export interface ListItemProps { disabled?: boolean; titleProps?: TextProps; leftContentStyle?: StyleProp<ViewStyle>; + + titleStyle?: ViewStyle; /* Shared value that will be updated when user press on ListItem */ @@ -81,7 +83,7 @@ export const ListItem = memo<ListItemProps>((props) => { const TouchableComponent = isAndroid ? Pressable : TouchableHighlight; const renderTitle = useCallback(() => { return ( - <View style={styles.title}> + <View style={[styles.title, props.titleStyle]}> <View style={styles.titleTextContainer}> {typeof props.title === 'string' ? ( <SText @@ -205,7 +207,7 @@ const styles = Steezy.create(({ colors }) => ({ height: 44, borderRadius: 44 / 2, overflow: 'hidden', - backgroundColor: colors.backgroundContentTint, + backgroundColor: colors.backgroundContentAttention, }, picture: { width: 44, diff --git a/packages/mobile/src/uikit/TransitionOpacity.tsx b/packages/mobile/src/uikit/TransitionOpacity.tsx index 9576a80fb..d5638fd2c 100644 --- a/packages/mobile/src/uikit/TransitionOpacity.tsx +++ b/packages/mobile/src/uikit/TransitionOpacity.tsx @@ -1,6 +1,12 @@ import React from 'react'; import { StyleProp, ViewStyle } from 'react-native'; -import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withDelay, withTiming } from 'react-native-reanimated'; +import Animated, { + runOnJS, + useAnimatedStyle, + useSharedValue, + withDelay, + withTiming, +} from 'react-native-reanimated'; interface TransitionOpacity { isVisible: boolean; @@ -12,46 +18,49 @@ interface TransitionOpacity { } export const TransitionOpacity: React.FC<TransitionOpacity> = (props) => { - const { - children, - isVisible, - alwaysShown, - style, - entranceAnimation = true, - duration = 150 + const { + children, + isVisible, + alwaysShown, + style, + entranceAnimation = true, + duration = 150, } = props; const [shown, setIsShown] = React.useState(false); - const opacity = useSharedValue(entranceAnimation ? 0 : 1); + const opacity = useSharedValue(entranceAnimation ? 0 : 1); React.useEffect(() => { if (isVisible) { setIsShown(true); - opacity.value = withDelay(250, withTiming(1, { - duration, - })); + opacity.value = withDelay( + 250, + withTiming(1, { + duration, + }), + ); } else { - opacity.value = withTiming(0, { - duration, - }, (isComplete) => { - if (isComplete) { - runOnJS(setIsShown)(false); - } - }); + opacity.value = withTiming( + 0, + { + duration, + }, + (isComplete) => { + if (isComplete) { + runOnJS(setIsShown)(false); + } + }, + ); } }, [isVisible]); const opacityStyle = useAnimatedStyle(() => ({ - opacity: opacity.value + opacity: opacity.value, })); if (!shown && !alwaysShown) { return null; } - return ( - <Animated.View style={[style, opacityStyle]}> - {children} - </Animated.View> - ); + return <Animated.View style={[style, opacityStyle]}>{children}</Animated.View>; }; diff --git a/packages/shared/i18n/locales/en.json b/packages/shared/i18n/locales/en.json index a7f05c129..ea98ec013 100644 --- a/packages/shared/i18n/locales/en.json +++ b/packages/shared/i18n/locales/en.json @@ -110,7 +110,7 @@ "transaction_copy_caution": "Be careful with external links. Never give your secret phrase to third-party resources — you can lose all your funds.\n\n- - -\n\n", "spam_action": "Spam", - + "dns_renew_toast_success": "Domain renewed for 1 year", "dns_expiration_date": "Expiration date", "dns_renew_until_btn": "Renew until %{untilDate}", @@ -128,7 +128,6 @@ "dns_alert_expiring_one": "%{domain} expires in %{count} days. Renew it until %{untilDate}.", "dns_renew_all_until_btn": "Renew all until %{untilDate}", - "expiring_domains": "Expiring domains", "domains_renewed": "Domains renewed", "confirm_renew_all_domains_title": "Confirm action", diff --git a/yarn.lock b/yarn.lock index b9f0053dd..5265ac03a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2671,12 +2671,12 @@ resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-9.4.1.tgz#7b880758adca65fe47ee866cf7b00416b9dcc192" integrity sha512-dAbY5mfw+6Kas/GJ6QX9AZyY+K+eq9ad4Su6utoph/nxyH3whp5cMSgRNgE2VhGQVRZ/OG0qq3IaD3+wzoqJXw== -"@react-native-firebase/analytics@^18.5.0": +"@react-native-firebase/analytics@18.5.0": version "18.5.0" resolved "https://registry.yarnpkg.com/@react-native-firebase/analytics/-/analytics-18.5.0.tgz#cca5858e44f3f8cbf387fa7cd6debb95fbcf8023" integrity sha512-+8fkDedcftieOBEkNdqEW0lS/FB9SKzfUc3yYXnJcLTy3Ceg9JZga/tWz4BGbbxPddUy1Sm2UuWDuOxsdkFUyA== -"@react-native-firebase/app@^18.5.0": +"@react-native-firebase/app@18.5.0": version "18.5.0" resolved "https://registry.yarnpkg.com/@react-native-firebase/app/-/app-18.5.0.tgz#198a133229d1b8710f257e76d9703dcb7e4cd6e0" integrity sha512-AhHQi5KFDlKZn/lH7rEYtLfpsGamEq+P/cXZWcNPcP0WGlmi++abk7Pxnn4MjnG7TNhEyG/C9uq//qb6VhXaZg== @@ -2684,14 +2684,14 @@ opencollective-postinstall "^2.0.1" superstruct "^0.6.2" -"@react-native-firebase/crashlytics@^18.5.0": +"@react-native-firebase/crashlytics@18.5.0": version "18.5.0" resolved "https://registry.yarnpkg.com/@react-native-firebase/crashlytics/-/crashlytics-18.5.0.tgz#b5c1e15d260d5243c5ffb2963114bb7e42fe2b7f" integrity sha512-i/jt/kNyhLws10/DeYgSQbGIekNPCAdtdg36NmFSw7URZf+1JUczKePbHqq3TvMFZJi5OjnDj06Fj9oenQKjWA== dependencies: stacktrace-js "^2.0.0" -"@react-native-firebase/messaging@^18.5.0": +"@react-native-firebase/messaging@18.5.0": version "18.5.0" resolved "https://registry.yarnpkg.com/@react-native-firebase/messaging/-/messaging-18.5.0.tgz#2a80b25816470e9843682e031a3a113566067ce6" integrity sha512-y1FApYxBMcygmbWBqUPFC+fCfvx6Yf6TdZewun7kPwx+S+tkYzoKx1IsXtxOXtqyJjCNEYirjFgNrs5SSd02zA== From b9b189982376ef09afc0222011c93eb04f80eeb4 Mon Sep 17 00:00:00 2001 From: Andrey Sorokin <sorokin0andrey@gmail.com> Date: Fri, 6 Oct 2023 04:10:53 +0600 Subject: [PATCH 31/45] merge fixes --- packages/mobile/src/store/main/sagas.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/mobile/src/store/main/sagas.ts b/packages/mobile/src/store/main/sagas.ts index 1186661ab..d8ddd8058 100644 --- a/packages/mobile/src/store/main/sagas.ts +++ b/packages/mobile/src/store/main/sagas.ts @@ -223,7 +223,6 @@ export function* initHandler(isTestnet: boolean, canRetry = false) { !getFlag('address_style_nobounce'), ); useSwapStore.getState().actions.fetchAssets(); - useExpiringDomains.getState().actions.load(addr); } else { yield put(walletActions.endLoading()); } From e5a889c08fa7d6dac763855aadeef14d327a3d46 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Tue, 12 Mar 2024 22:53:34 +0300 Subject: [PATCH 32/45] fix(mobile): fix migration using biometry (#762) * fix(mobile): fix migration using biometry * bump(mobile): 4.0.1 --- packages/mobile/android/app/build.gradle | 2 +- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 4 ++-- packages/mobile/src/hooks/useMigration.ts | 6 +----- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index ef0441fb9..f6584c79e 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -92,7 +92,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 433 - versionName "4.0.0" + versionName "4.0.1" missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'store', 'play' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 3742e1b05..05e38f21d 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1294,7 +1294,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.0.0; + MARKETING_VERSION = 4.0.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1328,7 +1328,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.0.0; + MARKETING_VERSION = 4.0.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/packages/mobile/src/hooks/useMigration.ts b/packages/mobile/src/hooks/useMigration.ts index 855721763..e91e2f4a8 100644 --- a/packages/mobile/src/hooks/useMigration.ts +++ b/packages/mobile/src/hooks/useMigration.ts @@ -22,12 +22,8 @@ export const useMigration = () => { if (isNewSecurityFlow) { const keychainService = await AsyncStorage.getItem('keychainService'); - if (!keychainService) { - throw new Error(); - } - jsonstr = await SecureStore.getItemAsync('biometry_' + keychainItemName, { - keychainService, + keychainService: keychainService || 'TKProtected', }); } else { jsonstr = await EncryptedStorage.getItem(keychainItemName); From f18d810da0a218daa7c66d1651ff227e0729b0f3 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Mon, 18 Mar 2024 13:48:30 +0300 Subject: [PATCH 33/45] hotfix(mobile): race condition fix (#764) * fix(mobile): fix migration using biometry * bump(mobile): 4.0.1 * fix(mobile): Move isMigrated flag to separated storage to avoid race condition * fix(mobile): add setAsync method * add persist options for migrationStore * fix(mobile): fix seed phrase on small displays --------- Co-authored-by: Andrey Sorokin <sorokin0andrey@gmail.com> --- packages/@core-js/src/utils/State.ts | 9 +++ .../src/navigation/MainStack/MainStack.tsx | 2 +- .../BackupPhraseScreen/BackupPhraseScreen.tsx | 77 +++++++++---------- packages/mobile/src/wallet/Tonkeeper.ts | 45 ++++++++--- 4 files changed, 80 insertions(+), 53 deletions(-) diff --git a/packages/@core-js/src/utils/State.ts b/packages/@core-js/src/utils/State.ts index b0c43ef23..a1a40c589 100644 --- a/packages/@core-js/src/utils/State.ts +++ b/packages/@core-js/src/utils/State.ts @@ -98,6 +98,15 @@ export class State<TData extends DefaultStateData> { this.storeIfNeeded(); }; + public setAsync = async ( + updater: Partial<TData> | ((data: TData) => Partial<TData>), + ) => { + const newData = typeof updater === 'function' ? updater(this.data) : updater; + this.data = { ...this.data, ...newData }; + this.emit(); + await this.storeIfNeeded(); + }; + private emit() { this.subscribers.forEach((subscriber) => subscriber(this.data)); } diff --git a/packages/mobile/src/navigation/MainStack/MainStack.tsx b/packages/mobile/src/navigation/MainStack/MainStack.tsx index 1f8300cbe..c2a9f5106 100644 --- a/packages/mobile/src/navigation/MainStack/MainStack.tsx +++ b/packages/mobile/src/navigation/MainStack/MainStack.tsx @@ -63,7 +63,7 @@ export const MainStack: FC = () => { const showLockScreen = tk.lockEnabled && !isUnlocked && hasWallet && !attachedScreen.pathname; - const isMigrated = useExternalState(tk.walletsStore, (state) => state.isMigrated); + const isMigrated = useExternalState(tk.migrationStore, (state) => state.isMigrated); const root = useMemo(() => { if (hasWallet) { diff --git a/packages/mobile/src/screens/BackupPhraseScreen/BackupPhraseScreen.tsx b/packages/mobile/src/screens/BackupPhraseScreen/BackupPhraseScreen.tsx index 0b03553ec..9b56fb282 100644 --- a/packages/mobile/src/screens/BackupPhraseScreen/BackupPhraseScreen.tsx +++ b/packages/mobile/src/screens/BackupPhraseScreen/BackupPhraseScreen.tsx @@ -41,43 +41,45 @@ export const BackupPhraseScreen = memo(() => { return ( <Screen> <Screen.Header /> - <View style={styles.container}> - <Text type="h2" textAlign="center"> - {t('recovery_phrase.title')} - </Text> - <Spacer y={4} /> - <Text type="body1" color="textSecondary" textAlign="center"> - {t('recovery_phrase.caption')} - </Text> - <Spacer y={16} /> + <Screen.ScrollView> + <View style={styles.container}> + <Text type="h2" textAlign="center"> + {t('recovery_phrase.title')} + </Text> + <Spacer y={4} /> + <Text type="body1" color="textSecondary" textAlign="center"> + {t('recovery_phrase.caption')} + </Text> + <Spacer y={16} /> - <View style={styles.centered}> - <View style={styles.columns}> - <View style={styles.leftColumn}> - {leftColumn.map((word, index) => ( - <View style={styles.line} key={`${word}-${index}`}> - <Text type="body2" color="textSecondary" style={styles.num}> - {index + 1}. - </Text> - <Text type="body1">{word}</Text> - </View> - ))} - </View> - <View> - {rightColumn.map((word, index) => ( - <View style={styles.line} key={`${word}-${index + 1 + 12}`}> - <Text type="body2" color="textSecondary" style={styles.num}> - {index + 1 + 12}. - </Text> - <Text type="body1">{word}</Text> - </View> - ))} + <View style={styles.centered}> + <View style={styles.columns}> + <View style={styles.leftColumn}> + {leftColumn.map((word, index) => ( + <View style={styles.line} key={`${word}-${index}`}> + <Text type="body2" color="textSecondary" style={styles.num}> + {index + 1}. + </Text> + <Text type="body1">{word}</Text> + </View> + ))} + </View> + <View> + {rightColumn.map((word, index) => ( + <View style={styles.line} key={`${word}-${index + 1 + 12}`}> + <Text type="body2" color="textSecondary" style={styles.num}> + {index + 1 + 12}. + </Text> + <Text type="body1">{word}</Text> + </View> + ))} + </View> </View> </View> </View> - </View> - - <View style={[styles.buttonContainer, { paddingBottom: safeArea.bottom + 32 }]}> + <Screen.ButtonSpacer /> + </Screen.ScrollView> + <Screen.ButtonContainer> {lastBackupAt !== null && !params.isBackupAgain ? ( <Button title={t('recovery_phrase.copy_button')} @@ -94,7 +96,7 @@ export const BackupPhraseScreen = memo(() => { } /> )} - </View> + </Screen.ButtonContainer> </Screen> ); }); @@ -102,7 +104,6 @@ export const BackupPhraseScreen = memo(() => { const styles = StyleSheet.create({ container: { paddingHorizontal: 16, - flex: 1, }, centered: { justifyContent: 'center', @@ -111,7 +112,7 @@ const styles = StyleSheet.create({ columns: { flexDirection: 'row', maxWidth: 310, - paddingTop: 16, + paddingVertical: 16, }, line: { width: 151, @@ -129,8 +130,4 @@ const styles = StyleSheet.create({ position: 'relative', top: 3, }, - buttonContainer: { - marginTop: 16, - marginHorizontal: 32, - }, }); diff --git a/packages/mobile/src/wallet/Tonkeeper.ts b/packages/mobile/src/wallet/Tonkeeper.ts index 1fe5a8834..65c7e28a4 100644 --- a/packages/mobile/src/wallet/Tonkeeper.ts +++ b/packages/mobile/src/wallet/Tonkeeper.ts @@ -45,6 +45,9 @@ export interface WalletsStoreState { selectedIdentifier: string; biometryEnabled: boolean; lockEnabled: boolean; +} + +export interface MigrationState { isMigrated: boolean; } @@ -77,6 +80,9 @@ export class Tonkeeper { selectedIdentifier: '', biometryEnabled: false, lockEnabled: true, + }); + + public migrationStore = new State<MigrationState>({ isMigrated: false, }); @@ -99,6 +105,11 @@ export class Tonkeeper { storage: this.storage, key: 'walletsStore', }); + + this.migrationStore.persist({ + storage: this.storage, + key: 'migrationStore', + }); } public get wallet() { @@ -122,12 +133,18 @@ export class Tonkeeper { await Promise.all([ this.walletsStore.rehydrate(), this.tonPrice.rehydrate(), + this.migrationStore.rehydrate(), this.biometry.detectTypes(), ]); + // @ts-ignore moved to migrationStore, may be set on some clients. So we need to migrate it + if (this.walletsStore.data.isMigrated) { + this.setMigrated(); + } + this.tonPrice.load(); - if (!this.walletsStore.data.isMigrated) { + if (!this.migrationStore.data.isMigrated) { this.migrationData = await this.getMigrationData(); } @@ -237,7 +254,9 @@ export class Tonkeeper { return indexA - indexB; }); - this.walletsStore.set(({ wallets }) => ({ wallets: [...wallets, ...sortedWallets] })); + await this.walletsStore.setAsync(({ wallets }) => ({ + wallets: [...wallets, ...sortedWallets], + })); const walletsInstances = await Promise.all( sortedWallets.map((wallet) => this.createWalletInstance(wallet)), ); @@ -334,7 +353,9 @@ export class Tonkeeper { version, }; - this.walletsStore.set(({ wallets }) => ({ wallets: [...wallets, config] })); + await this.walletsStore.setAsync(({ wallets }) => ({ + wallets: [...wallets, config], + })); const wallet = await this.createWalletInstance(config); this.setWallet(wallet); @@ -348,7 +369,7 @@ export class Tonkeeper { Array.from(this.wallets.keys()).find((item) => item !== identifier) ?? '', ) ?? null; - this.walletsStore.set(({ wallets }) => ({ + await this.walletsStore.setAsync(({ wallets }) => ({ wallets: wallets.filter((w) => w.identifier !== identifier), selectedIdentifier: nextWallet?.identifier ?? '', })); @@ -358,7 +379,7 @@ export class Tonkeeper { this.wallets.delete(identifier); if (this.wallets.size === 0) { - this.walletsStore.set({ biometryEnabled: false }); + await this.walletsStore.setAsync({ biometryEnabled: false }); this.vault.destroy(); } @@ -369,7 +390,7 @@ export class Tonkeeper { } public async removeAllWallets() { - this.walletsStore.set({ + await this.walletsStore.setAsync({ wallets: [], selectedIdentifier: '', biometryEnabled: false, @@ -441,7 +462,7 @@ export class Tonkeeper { }, ); - this.walletsStore.set({ wallets: updatedWallets }); + await this.walletsStore.setAsync({ wallets: updatedWallets }); identifiers.forEach((identifier) => { const currentConfig = updatedWallets.find( @@ -496,7 +517,7 @@ export class Tonkeeper { public setMigrated() { console.log('migrated'); - this.walletsStore.set({ isMigrated: true }); + this.migrationStore.set({ isMigrated: true }); } public saveLastBackupTimestampAll(identifiers: string[], dismissSetup = false) { @@ -512,7 +533,7 @@ export class Tonkeeper { public async enableBiometry(passcode: string) { await this.vault.setupBiometry(passcode); - this.walletsStore.set({ biometryEnabled: true }); + await this.walletsStore.setAsync({ biometryEnabled: true }); } public async disableBiometry() { @@ -520,14 +541,14 @@ export class Tonkeeper { await this.vault.removeBiometry(); } catch {} - this.walletsStore.set({ biometryEnabled: false }); + await this.walletsStore.setAsync({ biometryEnabled: false }); } public async enableLock() { - this.walletsStore.set({ lockEnabled: true }); + await this.walletsStore.setAsync({ lockEnabled: true }); } public async disableLock() { - this.walletsStore.set({ lockEnabled: false }); + await this.walletsStore.setAsync({ lockEnabled: false }); } } From 354f5814675f2758f7dd3b5e0f785788d4cb9dd4 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Tue, 26 Mar 2024 16:48:54 +0300 Subject: [PATCH 34/45] fix(mobile): Restake banner hotfix for non-whitelisted pools --- packages/mobile/index.js | 15 ++++---- .../RestakeBanner/RestakeBanner.tsx | 23 ++++++++----- .../src/core/Notifications/Notification.tsx | 6 +++- packages/mobile/src/core/Staking/Staking.tsx | 32 ++++++++++------- .../src/core/StakingSend/StakingSend.tsx | 32 ++++++++++++----- .../src/store/zustand/notifications/types.ts | 9 +++++ .../notifications/useNotificationsStore.ts | 34 +++++++++++++++++++ .../src/wallet/managers/StakingManager.ts | 23 ------------- 8 files changed, 114 insertions(+), 60 deletions(-) diff --git a/packages/mobile/index.js b/packages/mobile/index.js index 6b6d6dad9..d5b3354c7 100644 --- a/packages/mobile/index.js +++ b/packages/mobile/index.js @@ -22,7 +22,6 @@ import crashlytics from '@react-native-firebase/crashlytics'; import messaging from '@react-native-firebase/messaging'; import { withIAPContext } from 'react-native-iap'; import { startApp } from './src/index'; -import { tk } from './src/wallet'; LogBox.ignoreLogs([ 'Non-serializable values were found in the navigation state', @@ -47,15 +46,17 @@ async function handleDappMessage(remoteMessage) { ) { return null; } - + await useNotificationsStore.persist.rehydrate(); if (remoteMessage.data?.type === 'better_stake_option_found') { - tk.wallet.staking.toggleRestakeBanner( - true, - remoteMessage.data.stakingAddressToMigrateFrom, - ); + useNotificationsStore + .getState() + .actions.toggleRestakeBanner( + remoteMessage.data.account, + true, + remoteMessage.data.stakingAddressToMigrateFrom, + ); } - await useNotificationsStore.persist.rehydrate(); useNotificationsStore.getState().actions.addNotification( { ...remoteMessage.data, diff --git a/packages/mobile/src/components/RestakeBanner/RestakeBanner.tsx b/packages/mobile/src/components/RestakeBanner/RestakeBanner.tsx index 3ab76b3d2..29f74e041 100644 --- a/packages/mobile/src/components/RestakeBanner/RestakeBanner.tsx +++ b/packages/mobile/src/components/RestakeBanner/RestakeBanner.tsx @@ -26,6 +26,7 @@ import { IconsComposition } from './IconsComposition'; import { useFiatValue } from '$hooks/useFiatValue'; import { CryptoCurrencies } from '$shared/constants'; import { stakingFormatter } from '$utils/formatter'; +import { useNotificationsStore } from '$store'; export interface ExtendedPoolInfo extends PoolInfo { isWithdrawal: boolean; @@ -35,6 +36,7 @@ export interface ExtendedPoolInfo extends PoolInfo { export interface RestakeBannerProps { poolsList: ExtendedPoolInfo[]; migrateFrom: string; + bypassUnstakeStep?: boolean; } export enum RestakeSteps { @@ -52,11 +54,14 @@ export const RestakeBanner = memo<RestakeBannerProps>((props) => { ) as ExtendedPoolInfo; }, [props.poolsList]); const { handleTopUpPress } = usePoolInfo(tonstakersPool); + const toggleRestakeBanner = useNotificationsStore( + (state) => state.actions.toggleRestakeBanner, + ); const handleCloseRestakeBanner = useCallback(() => { LayoutAnimation.easeInEaseOut(); - tk.wallet.staking.toggleRestakeBanner(false); - }, []); + toggleRestakeBanner(tk.wallet.address.ton.raw, false); + }, [toggleRestakeBanner]); const poolToWithdrawal = useMemo( () => @@ -68,8 +73,6 @@ export const RestakeBanner = memo<RestakeBannerProps>((props) => { [poolToWithdrawal], ); - const bypassStakeStep = useStakingState((s) => s.bypassStakeStep, []); - const readyWithdraw = useFiatValue( CryptoCurrencies.Ton, stakingFormatter.fromNano(toWithdrawalStakingInfo?.ready_withdraw ?? '0'), @@ -107,7 +110,11 @@ export const RestakeBanner = memo<RestakeBannerProps>((props) => { return RestakeSteps.DONE; } // Go to last step if pool to withdrawal is empty now (or if balance so small, or step is bypassed) - if (bypassStakeStep || Number(poolToWithdrawal?.balance) < 0.1) { + if ( + props.bypassUnstakeStep || + !poolToWithdrawal?.balance || + Number(poolToWithdrawal?.balance) < 0.1 + ) { return RestakeSteps.STAKE_INTO_TONSTAKERS; } // If user has pending withdrawal, render step with waiting @@ -116,7 +123,7 @@ export const RestakeBanner = memo<RestakeBannerProps>((props) => { } return RestakeSteps.UNSTAKE; }, [ - bypassStakeStep, + props.bypassUnstakeStep, isWaitingForWithdrawal, poolToWithdrawal?.balance, readyWithdraw.amount, @@ -130,8 +137,8 @@ export const RestakeBanner = memo<RestakeBannerProps>((props) => { }, [currentStepId, handleCloseRestakeBanner]); const { formattedDuration, isCooldown } = useStakingCycle( - poolToWithdrawal?.cycle_start, - poolToWithdrawal?.cycle_end, + poolToWithdrawal?.cycle_start ?? Date.now(), + poolToWithdrawal?.cycle_end ?? Date.now(), isWaitingForWithdrawal, ); diff --git a/packages/mobile/src/core/Notifications/Notification.tsx b/packages/mobile/src/core/Notifications/Notification.tsx index 64d33236b..15afa856b 100644 --- a/packages/mobile/src/core/Notifications/Notification.tsx +++ b/packages/mobile/src/core/Notifications/Notification.tsx @@ -6,6 +6,7 @@ import { disableNotifications, useConnectedAppsList, useDAppsNotifications, + useNotificationsStore, } from '$store'; import { format, getDomainFromURL } from '$utils'; import { Swipeable, TouchableOpacity } from 'react-native-gesture-handler'; @@ -54,6 +55,9 @@ export const Notification: React.FC<NotificationProps> = (props) => { const { showActionSheetWithOptions } = useActionSheet(); const { deleteNotificationByReceivedAt } = useDAppsNotifications(); const listItemRef = useRef(null); + const toggleRestakeBanner = useNotificationsStore( + (state) => state.actions.toggleRestakeBanner, + ); const handleDelete = useCallback(() => { deleteNotificationByReceivedAt(props.notification.received_at); @@ -141,7 +145,7 @@ export const Notification: React.FC<NotificationProps> = (props) => { const handleOpenInWebView = useCallback(() => { if (props.notification.type === NotificationType.BETTER_STAKE_OPTION_FOUND) { - tk.wallet.staking.toggleRestakeBanner(true); + toggleRestakeBanner(tk.wallet.address.ton.raw, true); } if (!props.notification.link && !props.notification.deeplink) { diff --git a/packages/mobile/src/core/Staking/Staking.tsx b/packages/mobile/src/core/Staking/Staking.tsx index 47dbd7dff..dec0220fa 100644 --- a/packages/mobile/src/core/Staking/Staking.tsx +++ b/packages/mobile/src/core/Staking/Staking.tsx @@ -2,7 +2,7 @@ import { useStakingRefreshControl } from '$hooks/useStakingRefreshControl'; import { useNavigation } from '@tonkeeper/router'; import { MainStackRouteNames, openDAppBrowser } from '$navigation'; import { StakingListCell } from '$shared/components'; -import { FlashCountKeys, useFlashCount } from '$store'; +import { FlashCountKeys, useFlashCount, useNotificationsStore } from '$store'; import { Button, Icon, ScrollHandler, Spacer, Text } from '$uikit'; import { List } from '$uikit/List/old/List'; import { getImplementationIcon, getPoolIcon } from '$utils/staking'; @@ -24,6 +24,8 @@ import { useBalancesState, useJettons, useStakingState } from '@tonkeeper/shared import { StakingManager, StakingProvider } from '$wallet/managers/StakingManager'; import { config } from '$config'; import { RestakeBanner } from '../../components/RestakeBanner/RestakeBanner'; +import { shallow } from 'zustand/shallow'; +import { tk } from '$wallet'; interface Props {} @@ -36,9 +38,11 @@ export const Staking: FC<Props> = () => { const pools = useStakingState((s) => s.pools); const stakingInfo = useStakingState((s) => s.stakingInfo); const highestApyPool = useStakingState((s) => s.highestApyPool); - const showRestakeBanner = useStakingState((s) => s.showRestakeBanner); - const stakingAddressToMigrateFrom = useStakingState( - (s) => s.stakingAddressToMigrateFrom, + + const rawAddress = tk.wallet.address.ton.raw ?? ''; + const notificationsStore = useNotificationsStore( + (state) => state.wallets[rawAddress], + shallow, ); const [flashShownCount] = useFlashCount(FlashCountKeys.Staking); @@ -221,15 +225,17 @@ export const Staking: FC<Props> = () => { showsVerticalScrollIndicator={false} > <S.Content bottomInset={bottomInset}> - {showRestakeBanner && stakingAddressToMigrateFrom && ( - <> - <RestakeBanner - migrateFrom={stakingAddressToMigrateFrom} - poolsList={poolsList} - /> - <Spacer y={16} /> - </> - )} + {notificationsStore?.showRestakeBanner && + notificationsStore?.stakingAddressToMigrateFrom && ( + <> + <RestakeBanner + bypassUnstakeStep={notificationsStore?.bypassUnstakeStep} + migrateFrom={notificationsStore?.stakingAddressToMigrateFrom} + poolsList={poolsList} + /> + <Spacer y={16} /> + </> + )} {!hasActivePools ? ( <S.LargeTitleContainer> <Text variant="h2">{t('staking.title_large')}</Text> diff --git a/packages/mobile/src/core/StakingSend/StakingSend.tsx b/packages/mobile/src/core/StakingSend/StakingSend.tsx index 826b9f396..c478febcb 100644 --- a/packages/mobile/src/core/StakingSend/StakingSend.tsx +++ b/packages/mobile/src/core/StakingSend/StakingSend.tsx @@ -9,7 +9,7 @@ import { AppStackRouteNames } from '$navigation'; import { AppStackParamList } from '$navigation/AppStack'; import { StepView, StepViewItem, StepViewRef } from '$shared/components'; import { CryptoCurrencies, Decimals } from '$shared/constants'; -import { Toast } from '$store'; +import { Toast, useNotificationsStore } from '$store'; import { getStakingPoolByAddress } from '@tonkeeper/shared/utils/staking'; import { walletWalletSelector } from '$store/wallet'; import { NavBar } from '$uikit'; @@ -38,6 +38,7 @@ import { SignRawMessage } from '$core/ModalContainer/NFTOperations/TXRequest.typ import { useStakingState, useWallet } from '@tonkeeper/shared/hooks'; import { tk } from '$wallet'; import { Address } from '@tonkeeper/shared/Address'; +import { shallow } from 'zustand/shallow'; interface Props { route: RouteProp<AppStackParamList, AppStackRouteNames.StakingSend>; @@ -176,6 +177,15 @@ export const StakingSend: FC<Props> = (props) => { const messages = useRef<SignRawMessage[]>([]); + const rawAddress = wallet.address.ton.raw ?? ''; + const stakingAddressToMigrateFrom = useNotificationsStore( + (state) => state.wallets[rawAddress]?.stakingAddressToMigrateFrom, + shallow, + ); + const bypassUnstakeStep = useNotificationsStore( + (state) => state.actions.bypassUnstakeStep, + ); + const { isLiquidJetton, price } = useCurrencyToSend(currency, tokenType); const parsedAmount = useMemo(() => { @@ -336,20 +346,26 @@ export const StakingSend: FC<Props> = (props) => { await actionRef.current.send(privateKey); if ( isWithdrawalConfrim && - tk.wallet.staking.state.data.stakingAddressToMigrateFrom && - Address.compare( - tk.wallet.staking.state.data.stakingAddressToMigrateFrom, - pool.address, - ) + stakingAddressToMigrateFrom && + Address.compare(stakingAddressToMigrateFrom, pool.address) ) { - tk.wallet.staking.setBypassStakeStep(); + bypassUnstakeStep(rawAddress); } } catch (e) { throw e; } finally { setSending(false); } - }, [isDeposit, isWithdrawalConfrim, pool, totalFee, unlockVault]); + }, [ + bypassUnstakeStep, + isDeposit, + isWithdrawalConfrim, + pool, + rawAddress, + stakingAddressToMigrateFrom, + totalFee, + unlockVault, + ]); useEffect(() => { if (isWithdrawalConfrim || initialAmount) { diff --git a/packages/mobile/src/store/zustand/notifications/types.ts b/packages/mobile/src/store/zustand/notifications/types.ts index 33119a0c3..97a8536d4 100644 --- a/packages/mobile/src/store/zustand/notifications/types.ts +++ b/packages/mobile/src/store/zustand/notifications/types.ts @@ -20,6 +20,9 @@ export interface INotificationsState { last_seen: number; last_seen_activity_screen: number; should_show_red_dot: boolean; + showRestakeBanner?: boolean; + stakingAddressToMigrateFrom?: string; + bypassUnstakeStep?: boolean; } export interface INotificationsStore { @@ -35,5 +38,11 @@ export interface INotificationsStore { deleteNotificationByReceivedAt: (receivedAt: number, rawAddress: string) => void; removeNotificationsByDappUrl: (dapp_url: string, rawAddress: string) => void; removeRedDot: (rawAddress: string) => void; + toggleRestakeBanner: ( + rawAddress: string, + showRestakeBanner: boolean, + stakingAddressToMigrateFrom?: string, + ) => void; + bypassUnstakeStep: (rawAddress: string) => void; }; } diff --git a/packages/mobile/src/store/zustand/notifications/useNotificationsStore.ts b/packages/mobile/src/store/zustand/notifications/useNotificationsStore.ts index e59ef6da8..8b4769b2d 100644 --- a/packages/mobile/src/store/zustand/notifications/useNotificationsStore.ts +++ b/packages/mobile/src/store/zustand/notifications/useNotificationsStore.ts @@ -110,6 +110,40 @@ export const useNotificationsStore = create( }; }); }, + toggleRestakeBanner: ( + rawAddress: string, + showRestakeBanner: boolean, + stakingAddressToMigrateFrom?: string, + ) => { + set((state) => { + const wallet = state.wallets[rawAddress]; + return { + wallets: { + ...state.wallets, + [rawAddress]: { + ...wallet, + showRestakeBanner, + bypassUnstakeStep: false, + ...(stakingAddressToMigrateFrom ? { stakingAddressToMigrateFrom } : {}), + }, + }, + }; + }); + }, + bypassUnstakeStep: (rawAddress) => { + set((state) => { + const wallet = state.wallets[rawAddress]; + return { + wallets: { + ...state.wallets, + [rawAddress]: { + ...wallet, + bypassUnstakeStep: true, + }, + }, + }; + }); + }, reset: () => set(initialState), }, }), diff --git a/packages/mobile/src/wallet/managers/StakingManager.ts b/packages/mobile/src/wallet/managers/StakingManager.ts index ccc7bf097..72af2fef4 100644 --- a/packages/mobile/src/wallet/managers/StakingManager.ts +++ b/packages/mobile/src/wallet/managers/StakingManager.ts @@ -40,9 +40,6 @@ export type StakingState = { stakingJettons: Record<string, JettonMetadata>; stakingJettonsUpdatedAt: number; stakingBalance: string; - showRestakeBanner: boolean; - stakingAddressToMigrateFrom?: string; - bypassStakeStep?: boolean; }; export class StakingManager { @@ -61,8 +58,6 @@ export class StakingManager { providers: [], highestApyPool: null, stakingBalance: '0', - showRestakeBanner: false, - bypassStakeStep: false, }; static calculatePoolBalance(pool: PoolInfo, stakingInfo: StakingInfo) { @@ -281,24 +276,6 @@ export class StakingManager { } } - public toggleRestakeBanner( - showRestakeBanner: boolean, - stakingAddressToMigrateFrom?: string, - ) { - if (stakingAddressToMigrateFrom) { - return this.state.set({ - stakingAddressToMigrateFrom, - showRestakeBanner, - bypassStakeStep: false, - }); - } - this.state.set({ showRestakeBanner, bypassStakeStep: false }); - } - - public setBypassStakeStep() { - this.state.set({ bypassStakeStep: true }); - } - public async reload() { await this.load(); } From 26603d1aeb3fddc42d34d4868724c97de9fff9dc Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Tue, 26 Mar 2024 16:49:41 +0300 Subject: [PATCH 35/45] fix(mobile): account delete feature resets only current wallet --- .../mobile/src/core/DeleteAccountDone/DeleteAccountDone.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile/src/core/DeleteAccountDone/DeleteAccountDone.tsx b/packages/mobile/src/core/DeleteAccountDone/DeleteAccountDone.tsx index 98f0d95bb..3b6abc06d 100644 --- a/packages/mobile/src/core/DeleteAccountDone/DeleteAccountDone.tsx +++ b/packages/mobile/src/core/DeleteAccountDone/DeleteAccountDone.tsx @@ -22,7 +22,7 @@ export const DeleteAccountDone: React.FC = () => { }, []); const handleAnimationEnd = useCallback(() => { - dispatch(walletActions.cleanWallet({ cleanAll: true })); + dispatch(walletActions.cleanWallet({ cleanAll: false })); }, [dispatch]); return ( From 172d1e260ecec58d3fbac22e374b6c8d4d582799 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Tue, 26 Mar 2024 16:50:30 +0300 Subject: [PATCH 36/45] bump(mobile): 4.1.1 --- packages/mobile/android/app/build.gradle | 2 +- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 8af57145e..b36ee490c 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -92,7 +92,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 433 - versionName "4.1.0" + versionName "4.1.1" missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'store', 'play' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 5bffe413f..a5a93362c 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1294,7 +1294,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.1.0; + MARKETING_VERSION = 4.1.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1328,7 +1328,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.1.0; + MARKETING_VERSION = 4.1.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", From 19e93fce6b1a1b8cf02ecbae4647a560f793d6ab Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Tue, 26 Mar 2024 18:14:49 +0300 Subject: [PATCH 37/45] fix(mobile): bypass unstake step if it's cooldown now too --- packages/mobile/src/core/StakingSend/StakingSend.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/mobile/src/core/StakingSend/StakingSend.tsx b/packages/mobile/src/core/StakingSend/StakingSend.tsx index c478febcb..46d565839 100644 --- a/packages/mobile/src/core/StakingSend/StakingSend.tsx +++ b/packages/mobile/src/core/StakingSend/StakingSend.tsx @@ -344,8 +344,12 @@ export const StakingSend: FC<Props> = (props) => { const privateKey = await vault.getTonPrivateKey(); await actionRef.current.send(privateKey); + + const endTimestamp = pool.cycle_end * 1000; + const isCooldown = Date.now() > endTimestamp; + if ( - isWithdrawalConfrim && + (isWithdrawalConfrim || isCooldown) && stakingAddressToMigrateFrom && Address.compare(stakingAddressToMigrateFrom, pool.address) ) { From f76e1753a5f178d9134bc9963843887f17a91de5 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Tue, 26 Mar 2024 18:21:53 +0300 Subject: [PATCH 38/45] fix(mobile): Untake all for TF pool --- .../RestakeBanner/RestakeBanner.tsx | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/mobile/src/components/RestakeBanner/RestakeBanner.tsx b/packages/mobile/src/components/RestakeBanner/RestakeBanner.tsx index 29f74e041..302f802d7 100644 --- a/packages/mobile/src/components/RestakeBanner/RestakeBanner.tsx +++ b/packages/mobile/src/components/RestakeBanner/RestakeBanner.tsx @@ -82,13 +82,17 @@ export const RestakeBanner = memo<RestakeBannerProps>((props) => { const handleWithdrawal = useCallback( (pool: ExtendedPoolInfo, withdrawAll?: boolean) => () => { + if (pool.implementation === PoolImplementationType.Tf) { + nav.push(AppStackRouteNames.StakingSend, { + poolAddress: pool.address, + transactionType: StakingTransactionType.WITHDRAWAL_CONFIRM, + }); + return; + } nav.push(AppStackRouteNames.StakingSend, { amount: withdrawAll && pool.balance, poolAddress: pool.address, - transactionType: - pool.implementation === PoolImplementationType.Tf - ? StakingTransactionType.WITHDRAWAL_CONFIRM - : StakingTransactionType.WITHDRAWAL, + transactionType: StakingTransactionType.WITHDRAWAL, }); }, [nav], @@ -202,12 +206,14 @@ export const RestakeBanner = memo<RestakeBannerProps>((props) => { }), })} />, - <Button - color="tertiary" - size="small" - onPress={handleWithdrawal(poolToWithdrawal)} - title={t('restake_banner.unstake_action_manual')} - />, + poolToWithdrawal.implementation !== PoolImplementationType.Tf && ( + <Button + color="tertiary" + size="small" + onPress={handleWithdrawal(poolToWithdrawal)} + title={t('restake_banner.unstake_action_manual')} + /> + ), ] } stepId={RestakeSteps.UNSTAKE} From 0dcc6f7e93c430c5cfd0cbce36627fdc86c2c3ff Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Tue, 26 Mar 2024 18:42:03 +0300 Subject: [PATCH 39/45] =?UTF-8?q?fix(mobile):=20=E2=9E=95=20add=20?= =?UTF-8?q?=F0=9F=98=A7=20scary=20=F0=9F=A5=B0=F0=9F=A5=B0=F0=9F=A5=B0=20e?= =?UTF-8?q?mojis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/shared/i18n/locales/tonkeeper/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/shared/i18n/locales/tonkeeper/en.json b/packages/shared/i18n/locales/tonkeeper/en.json index 6021cb5ff..b6e7d39a6 100644 --- a/packages/shared/i18n/locales/tonkeeper/en.json +++ b/packages/shared/i18n/locales/tonkeeper/en.json @@ -620,7 +620,7 @@ "settings_delete_account": "Delete account", "settings_delete_alert_button": "Delete account and data", "settings_delete_alert_caption": "This action will delete your account and all data from this application.", - "settings_delete_alert_title": "Are you sure you want to delete your account?", + "settings_delete_alert_title": "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\u2028Are you sure you want to delete your account?", "settings_jettons_list": "Tokens", "settings_legal_documents": "Legal", "language": { @@ -648,9 +648,9 @@ "settings_reset": "Sign Out", "settings_reset_alert_button": "Sign Out", "settings_reset_alert_caption": "This will erase key to your wallet. Make sure you have backed up your recovery phrase.", - "settings_reset_alert_title": "Sign Out?", + "settings_reset_alert_title": "\uD83D\uDEA7 \uD83D\uDEA8 Sign Out? \uD83D\uDEA8 \uD83D\uDEA7", "settings_reset_alert_caption_all": "This will erase keys to all wallets. Make sure you have backed up your recovery phrases.", - "settings_reset_alert_title_all": "Sign Out of All Wallets?", + "settings_reset_alert_title_all": "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\u2028Sign Out of All Wallets?", "settings_delete_watch_account": "Delete Watch Account?", "settings_delete_watch_account_button": "Delete", "settings_search_engine": "Search", From f36105622cf16c052e16277756d444ddcbddd1f8 Mon Sep 17 00:00:00 2001 From: Andrey Sorokin <sorokin0andrey@gmail.com> Date: Tue, 26 Mar 2024 19:04:44 +0400 Subject: [PATCH 40/45] fix(mobile): fix interactions while closing modal (#778) --- packages/shared/modals/AddWalletModal.tsx | 17 ++++++++++++----- .../containers/Modal/SheetModal/SheetModal.tsx | 12 +++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/shared/modals/AddWalletModal.tsx b/packages/shared/modals/AddWalletModal.tsx index e1702b490..1ad6ee80f 100644 --- a/packages/shared/modals/AddWalletModal.tsx +++ b/packages/shared/modals/AddWalletModal.tsx @@ -16,6 +16,7 @@ import { tk } from '@tonkeeper/mobile/src/wallet'; import { useUnlockVault } from '@tonkeeper/mobile/src/core/ModalContainer/NFTOperations/useUnlockVault'; import { getLastEnteredPasscode } from '@tonkeeper/mobile/src/store/wallet/sagas'; import { config } from '@tonkeeper/mobile/src/config'; +import { InteractionManager } from 'react-native'; interface AddWalletModalProps { isTonConnect?: boolean; @@ -89,7 +90,9 @@ export const AddWalletModal = memo<AddWalletModalProps>(({ isTonConnect }) => { <List.Item onPress={() => { nav.goBack(); - nav.navigate('ImportWalletStack'); + InteractionManager.runAfterInteractions(() => { + nav.navigate('ImportWalletStack'); + }); }} leftContentStyle={styles.iconContainer} leftContent={<Icon name="ic-key-28" color="accentBlue" />} @@ -104,7 +107,9 @@ export const AddWalletModal = memo<AddWalletModalProps>(({ isTonConnect }) => { <List.Item onPress={() => { nav.goBack(); - nav.navigate('AddWatchOnlyStack'); + InteractionManager.runAfterInteractions(() => { + nav.navigate('AddWatchOnlyStack'); + }); }} leftContentStyle={styles.iconContainer} leftContent={<Icon name="ic-magnifying-glass-28" color="accentBlue" />} @@ -120,9 +125,11 @@ export const AddWalletModal = memo<AddWalletModalProps>(({ isTonConnect }) => { <List.Item onPress={() => { nav.goBack(); - nav.navigate('ImportWalletStack', { - screen: 'ImportWallet', - params: { testnet: true }, + InteractionManager.runAfterInteractions(() => { + nav.navigate('ImportWalletStack', { + screen: 'ImportWallet', + params: { testnet: true }, + }); }); }} leftContentStyle={styles.iconContainer} diff --git a/packages/uikit/src/containers/Modal/SheetModal/SheetModal.tsx b/packages/uikit/src/containers/Modal/SheetModal/SheetModal.tsx index d59fb605b..2e330b876 100644 --- a/packages/uikit/src/containers/Modal/SheetModal/SheetModal.tsx +++ b/packages/uikit/src/containers/Modal/SheetModal/SheetModal.tsx @@ -19,6 +19,7 @@ import { useTheme } from '../../../styles'; import { useSheetInternal } from '@tonkeeper/router'; import { Easing, ReduceMotion, useReducedMotion } from 'react-native-reanimated'; +import { Handle, InteractionManager } from 'react-native'; export type SheetModalRef = BottomSheetModal; @@ -64,10 +65,15 @@ export const SheetModal = memo( return initialState === 'closed' ? -1 : 0; }, []); + const interactionHandle = useRef<Handle | null>(null); + useEffect(() => { delegateMethods({ present: () => bottomSheetRef.current?.snapToIndex(0), - close: () => bottomSheetRef.current?.close(), + close: () => { + interactionHandle.current = InteractionManager.createInteractionHandle(); + bottomSheetRef.current?.close(); + }, }); }, []); @@ -83,6 +89,10 @@ export const SheetModal = memo( ); const handleClose = useCallback(async () => { + if (interactionHandle.current !== null) { + InteractionManager.clearInteractionHandle(interactionHandle.current); + interactionHandle.current = null; + } if (ignoreOnClose) { return; } From 9206f746620b0a3a2b9f6c9e1e15ffefdfcf4825 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Tue, 26 Mar 2024 20:45:21 +0300 Subject: [PATCH 41/45] fix line separator on Android, emojis on other locales --- packages/shared/i18n/locales/tonkeeper/en.json | 4 ++-- packages/shared/i18n/locales/tonkeeper/ru-RU.json | 6 +++--- packages/shared/i18n/locales/tonkeeper/tr-TR.json | 6 +++--- packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/shared/i18n/locales/tonkeeper/en.json b/packages/shared/i18n/locales/tonkeeper/en.json index b6e7d39a6..b2f2a1944 100644 --- a/packages/shared/i18n/locales/tonkeeper/en.json +++ b/packages/shared/i18n/locales/tonkeeper/en.json @@ -620,7 +620,7 @@ "settings_delete_account": "Delete account", "settings_delete_alert_button": "Delete account and data", "settings_delete_alert_caption": "This action will delete your account and all data from this application.", - "settings_delete_alert_title": "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\u2028Are you sure you want to delete your account?", + "settings_delete_alert_title": "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\nAre you sure you want to delete your account?", "settings_jettons_list": "Tokens", "settings_legal_documents": "Legal", "language": { @@ -650,7 +650,7 @@ "settings_reset_alert_caption": "This will erase key to your wallet. Make sure you have backed up your recovery phrase.", "settings_reset_alert_title": "\uD83D\uDEA7 \uD83D\uDEA8 Sign Out? \uD83D\uDEA8 \uD83D\uDEA7", "settings_reset_alert_caption_all": "This will erase keys to all wallets. Make sure you have backed up your recovery phrases.", - "settings_reset_alert_title_all": "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\u2028Sign Out of All Wallets?", + "settings_reset_alert_title_all": "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\nSign Out of All Wallets?", "settings_delete_watch_account": "Delete Watch Account?", "settings_delete_watch_account_button": "Delete", "settings_search_engine": "Search", diff --git a/packages/shared/i18n/locales/tonkeeper/ru-RU.json b/packages/shared/i18n/locales/tonkeeper/ru-RU.json index 3a2bbe300..098cb9a3a 100644 --- a/packages/shared/i18n/locales/tonkeeper/ru-RU.json +++ b/packages/shared/i18n/locales/tonkeeper/ru-RU.json @@ -611,7 +611,7 @@ "settings_delete_account" : "Удалить аккаунт", "settings_delete_alert_button" : "Удалить аккаунт и данные", "settings_delete_alert_caption" : "Это действие приведет к удалению вашего аккаунта и всех данных из этого приложения.", - "settings_delete_alert_title" : "Вы уверены, что хотите удалить аккаунт?", + "settings_delete_alert_title": "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\nВы уверены, что хотите удалить аккаунт?", "settings_jettons_list" : "Токены", "settings_legal_documents" : "Юридические документы", "language": { @@ -639,9 +639,9 @@ "settings_reset" : "Выйти", "settings_reset_alert_button" : "Выйти", "settings_reset_alert_caption" : "Доступ к кошельку будет отключен. Убедитесь, что вы сохранили секретный ключ.", - "settings_reset_alert_title" : "Выйти из кошелька?", + "settings_reset_alert_title" : "\uD83D\uDEA7 \uD83D\uDEA8 Выйти из кошелька? \uD83D\uDEA8 \uD83D\uDEA7", "settings_reset_alert_caption_all": "Доступ к кошелькам будет отключён. Убедитесь, что вы сохранили все секретные ключи.", - "settings_reset_alert_title_all": "Выйти из всех кошельков?", + "settings_reset_alert_title_all": "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\nВыйти из всех кошельков?", "settings_delete_watch_account": "Удалить аккаунт для просмотра?", "settings_delete_watch_account_button": "Удалить", "settings_search_engine" : "Поиск", diff --git a/packages/shared/i18n/locales/tonkeeper/tr-TR.json b/packages/shared/i18n/locales/tonkeeper/tr-TR.json index 4de4f1935..5f7e7e09a 100644 --- a/packages/shared/i18n/locales/tonkeeper/tr-TR.json +++ b/packages/shared/i18n/locales/tonkeeper/tr-TR.json @@ -670,7 +670,7 @@ "settings_delete_account" : "Hesabı sil", "settings_delete_alert_button" : "Hesabı ve verileri sil", "settings_delete_alert_caption" : "Bu işlem hesabınızı ve bu uygulamadaki tüm verilerinizi silecektir.", - "settings_delete_alert_title" : "Hesabınızı silmek istediğinize emin misiniz?", + "settings_delete_alert_title" : "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\nHesabınızı silmek istediğinize emin misiniz?", "settings_jettons_list" : "Token'lar", "settings_legal_documents" : "Hukuki açıklama", "settings_network_alert_title" : "Ağ seçin", @@ -682,7 +682,7 @@ "settings_reset" : "Oturumu kapat", "settings_reset_alert_button" : "Oturumu kapat", "settings_reset_alert_caption" : "Bu işlem, cüzdan anahtarlarını siler. Gizli kurtarma ifadesini yedeklediğinizden emin olun.", - "settings_reset_alert_title" : "Oturum kapatılsın mı?", + "settings_reset_alert_title": "\uD83D\uDEA7 \uD83D\uDEA8 Oturum kapatılsın mı? \uD83D\uDEA8 \uD83D\uDEA7", "settings_search_engine" : "Arama", "settings_security" : "Güvenlik", "settings_subscriptions" : "Üyelikler", @@ -1060,4 +1060,4 @@ "wallet_swap" : "Takas", "wallet_title" : "Cüzdan", "yesterday" : "Dün" -} \ No newline at end of file +} diff --git a/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json b/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json index 167d08782..f33864f51 100644 --- a/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json +++ b/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json @@ -539,7 +539,7 @@ "settings_delete_account" : "删除账户", "settings_delete_alert_button" : "删除账户和数据", "settings_delete_alert_caption" : "此操作将从此应用中删除您的账户和所有数据。", - "settings_delete_alert_title" : "您确定要删除您的账户吗?", + "settings_delete_alert_title" : "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\n您确定要删除您的账户吗?", "settings_jettons_list" : "代币", "settings_legal_documents" : "法律文件", "settings_network_alert_title" : "选择网络", @@ -552,7 +552,7 @@ "settings_reset" : "退出登录", "settings_reset_alert_button" : "退出登录", "settings_reset_alert_caption" : "这将删除钱包的密钥。确保您已备份了您的秘密恢复短语。", - "settings_reset_alert_title" : "退出登录?", + "settings_reset_alert_title": "\uD83D\uDEA7 \uD83D\uDEA8 退出登录? \uD83D\uDEA8 \uD83D\uDEA7", "settings_search_engine" : "搜索", "settings_security" : "安全", "settings_subscriptions" : "订阅", @@ -882,4 +882,4 @@ "wallet_toncommunity_chat_link" : "https://t.me/toncoin_chat", "wallet_toncommunity_link" : "https://t.me/toncoin", "yesterday" : "昨天" -} \ No newline at end of file +} From 5efee9c131081a0f9d50e44dd14883b2214d28ed Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Wed, 27 Mar 2024 16:18:08 +0300 Subject: [PATCH 42/45] Fix android alert --- .../mobile/src/core/Settings/Settings.tsx | 30 +++++++++++-------- .../shared/i18n/locales/tonkeeper/en.json | 2 +- .../shared/i18n/locales/tonkeeper/ru-RU.json | 2 +- .../shared/i18n/locales/tonkeeper/tr-TR.json | 2 +- .../i18n/locales/tonkeeper/zh-Hans-CN.json | 2 +- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/mobile/src/core/Settings/Settings.tsx b/packages/mobile/src/core/Settings/Settings.tsx index 6f7bbc70e..ffd77082d 100644 --- a/packages/mobile/src/core/Settings/Settings.tsx +++ b/packages/mobile/src/core/Settings/Settings.tsx @@ -211,20 +211,24 @@ export const Settings: FC = () => { }, []); const handleDeleteAccount = useCallback(() => { - Alert.alert(t('settings_delete_alert_title'), t('settings_delete_alert_caption'), [ - { - text: t('cancel'), - style: 'cancel', - }, - { - text: t('settings_delete_alert_button'), - style: 'destructive', - onPress: () => { - trackEvent('delete_wallet'); - openDeleteAccountDone(); + Alert.alert( + t('settings_delete_alert_title', { space: Platform.OS === 'ios' ? '\n' : ' ' }), + t('settings_delete_alert_caption'), + [ + { + text: t('cancel'), + style: 'cancel', }, - }, - ]); + { + text: t('settings_delete_alert_button'), + style: 'destructive', + onPress: () => { + trackEvent('delete_wallet'); + openDeleteAccountDone(); + }, + }, + ], + ); }, []); const handleCustomizePress = useCallback( diff --git a/packages/shared/i18n/locales/tonkeeper/en.json b/packages/shared/i18n/locales/tonkeeper/en.json index b2f2a1944..9e2e1c139 100644 --- a/packages/shared/i18n/locales/tonkeeper/en.json +++ b/packages/shared/i18n/locales/tonkeeper/en.json @@ -620,7 +620,7 @@ "settings_delete_account": "Delete account", "settings_delete_alert_button": "Delete account and data", "settings_delete_alert_caption": "This action will delete your account and all data from this application.", - "settings_delete_alert_title": "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\nAre you sure you want to delete your account?", + "settings_delete_alert_title": "\uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8%{space}Are you sure you want to delete your account?", "settings_jettons_list": "Tokens", "settings_legal_documents": "Legal", "language": { diff --git a/packages/shared/i18n/locales/tonkeeper/ru-RU.json b/packages/shared/i18n/locales/tonkeeper/ru-RU.json index 098cb9a3a..b4b3389f5 100644 --- a/packages/shared/i18n/locales/tonkeeper/ru-RU.json +++ b/packages/shared/i18n/locales/tonkeeper/ru-RU.json @@ -611,7 +611,7 @@ "settings_delete_account" : "Удалить аккаунт", "settings_delete_alert_button" : "Удалить аккаунт и данные", "settings_delete_alert_caption" : "Это действие приведет к удалению вашего аккаунта и всех данных из этого приложения.", - "settings_delete_alert_title": "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\nВы уверены, что хотите удалить аккаунт?", + "settings_delete_alert_title": "\uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8%{space}Вы уверены, что хотите удалить аккаунт?", "settings_jettons_list" : "Токены", "settings_legal_documents" : "Юридические документы", "language": { diff --git a/packages/shared/i18n/locales/tonkeeper/tr-TR.json b/packages/shared/i18n/locales/tonkeeper/tr-TR.json index 5f7e7e09a..401fb8610 100644 --- a/packages/shared/i18n/locales/tonkeeper/tr-TR.json +++ b/packages/shared/i18n/locales/tonkeeper/tr-TR.json @@ -670,7 +670,7 @@ "settings_delete_account" : "Hesabı sil", "settings_delete_alert_button" : "Hesabı ve verileri sil", "settings_delete_alert_caption" : "Bu işlem hesabınızı ve bu uygulamadaki tüm verilerinizi silecektir.", - "settings_delete_alert_title" : "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\nHesabınızı silmek istediğinize emin misiniz?", + "settings_delete_alert_title" : "\uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8%{space}Hesabınızı silmek istediğinize emin misiniz?", "settings_jettons_list" : "Token'lar", "settings_legal_documents" : "Hukuki açıklama", "settings_network_alert_title" : "Ağ seçin", diff --git a/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json b/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json index f33864f51..b5aea1cd4 100644 --- a/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json +++ b/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json @@ -539,7 +539,7 @@ "settings_delete_account" : "删除账户", "settings_delete_alert_button" : "删除账户和数据", "settings_delete_alert_caption" : "此操作将从此应用中删除您的账户和所有数据。", - "settings_delete_alert_title" : "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7\n您确定要删除您的账户吗?", + "settings_delete_alert_title" : "\uD83D\uDEA7 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 \uD83D\uDEA7%{space}您确定要删除您的账户吗?", "settings_jettons_list" : "代币", "settings_legal_documents" : "法律文件", "settings_network_alert_title" : "选择网络", From 46aaa33ccfccb5bc558da9622f3b0f4f120f6732 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Fri, 5 Apr 2024 22:06:49 +0300 Subject: [PATCH 43/45] fix(mobile): bunch of fixes for fake logout --- packages/mobile/src/wallet/AppVault.ts | 5 +++++ packages/mobile/src/wallet/Tonkeeper.ts | 16 ++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/mobile/src/wallet/AppVault.ts b/packages/mobile/src/wallet/AppVault.ts index 75156f359..37f747946 100644 --- a/packages/mobile/src/wallet/AppVault.ts +++ b/packages/mobile/src/wallet/AppVault.ts @@ -56,6 +56,11 @@ export class AppVault implements Vault { } public async import(identifier: string, mnemonic: string, passcode: string) { + try { + if (!this.decryptedVaultState) { + this.decryptedVaultState = await this.getDecryptedVaultState(passcode); + } + } catch {} /** check passcode */ if (Object.keys(this.decryptedVaultState).length > 0) { await this.verifyPasscode(passcode); diff --git a/packages/mobile/src/wallet/Tonkeeper.ts b/packages/mobile/src/wallet/Tonkeeper.ts index e1380bac5..12d2890a6 100644 --- a/packages/mobile/src/wallet/Tonkeeper.ts +++ b/packages/mobile/src/wallet/Tonkeeper.ts @@ -154,6 +154,10 @@ export class Tonkeeper { ), ); + if (!this.wallet && this.walletsStore.data.wallets.length) { + return await this.switchWallet(this.walletsStore.data.wallets[0].identifier); + } + this.emitChangeWallet(); } catch (err) { console.log('TK:init', err); @@ -262,7 +266,7 @@ export class Tonkeeper { ); walletsInstances.map((instance) => instance.tonProof.obtainProof(keyPair)); - this.setWallet(walletsInstances[0]); + await this.setWallet(walletsInstances[0]); return walletsInstances.map((item) => item.identifier); } @@ -357,7 +361,7 @@ export class Tonkeeper { wallets: [...wallets, config], })); const wallet = await this.createWalletInstance(config); - this.setWallet(wallet); + await this.setWallet(wallet); return [wallet.identifier]; } @@ -489,13 +493,13 @@ export class Tonkeeper { this.walletSubscribers.forEach((subscriber) => subscriber(this.wallet)); } - public switchWallet(identifier: string) { + public async switchWallet(identifier: string) { const wallet = this.wallets.get(identifier); - this.setWallet(wallet!); + await this.setWallet(wallet!); } - private setWallet(wallet: Wallet | null) { - this.walletsStore.set({ selectedIdentifier: wallet?.identifier ?? '' }); + private async setWallet(wallet: Wallet | null) { + await this.walletsStore.setAsync({ selectedIdentifier: wallet?.identifier ?? '' }); this.emitChangeWallet(); } From f66f9c0dcf0dbaff8e3b9ae97b5a135ac9923aef Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Sat, 6 Apr 2024 16:59:24 +0300 Subject: [PATCH 44/45] bump(mobile): 4.1.2 --- packages/mobile/android/app/build.gradle | 2 +- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index b36ee490c..c94df8b10 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -92,7 +92,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 433 - versionName "4.1.1" + versionName "4.1.2" missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'store', 'play' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index a5a93362c..764467a97 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1294,7 +1294,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.1.1; + MARKETING_VERSION = 4.1.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1328,7 +1328,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.1.1; + MARKETING_VERSION = 4.1.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", From e32fb1571d91808218969aa5638c0ee16e55005d Mon Sep 17 00:00:00 2001 From: Max Voloshinskii <me@voloshinskii.ru> Date: Fri, 12 Apr 2024 04:05:03 +0300 Subject: [PATCH 45/45] Release 4.2.0 (#795) * fix(mobile): IAP updates (#727) * fix(mobile): IAP updates * fix(mobile): strings * fix(mobile): IAP updates * fix(mobile): strings * Fix other locales for compatibility * bump(mobile): 4.1.0 * Update IAP feature flag * battery fixes * fix logger manager name * fix(mobile): fix interactions while closing modal (#778) * feature(mobile): battery polishing (#780) * feature(mobile): battery support for jetton deeplinks * fix(mobile): Go to previous step when funds are insufficient * fix(mobile): Battery messages queue * feature(mobile): Experimental battery support for sign-raw * polishing wip * wip * wip * Drop other locales * feature(mobile): Support locked tokens (#782) * Hotfix 4.1.2 (#787) * fix(mobile): Battery QA fixes (#784) * fix(mobile): Don't reset battery balance in case of error * fix(mobile): Touch zone * fix(mobile): supported transaction setting from battery config * fix disable send with relayer for SingRaw * feature(mobile): Animated battery icon * fix(mobile): switch attemptWithRelayer default value to false * fix(mobile): update order * fix(mobile): Show locked jettons with zero balances (#783) * update TonAPI JettonBalance type * fix(mobile): Show in app locked jettons with zero balance * Remove unnecessary logic * Fix crash * fix(mobile): Dot color * fix(mobile): Battery fixes second revision (#790) * fix(mobile): Battery fixes second revision * fix battery size * fix(mobile): Add battery beta label (#791) * fix(mobile): Add battery beta label * fix reservedBalance * fix(mobile): Hide toggles (#792) * fix(mobile): Battery text update (#793) * fix(mobile): Promocode feature flag (#794) * fix(mobile): Promocode feature flag * fix(mobile): Move Spacer --------- Co-authored-by: Andrey Sorokin <sorokin0andrey@gmail.com> --- .../src/BatteryAPI/BatteryGenerated.ts | 58 +++++- .../@core-js/src/TonAPI/TonAPIGenerated.ts | 191 +++++++++++++----- .../@core-js/src/service/contractService.ts | 9 +- .../src/service/transactionService.ts | 73 ++++++- packages/mobile/android/app/build.gradle | 2 +- .../ios/ton_keeper.xcodeproj/project.pbxproj | 8 +- packages/mobile/src/blockchain/wallet.ts | 33 +-- .../SendScreenBatteryWidget.tsx | 47 +++++ .../SendScreenBatteryWidget/index.ts | 1 + packages/mobile/src/components/index.ts | 1 + packages/mobile/src/config/AppConfig.ts | 2 +- packages/mobile/src/config/index.ts | 24 ++- .../src/core/DevMenu/DevConfigScreen.tsx | 22 -- .../src/core/HideableAmount/ShowBalance.tsx | 7 +- packages/mobile/src/core/Jetton/Jetton.tsx | 35 +++- .../InsufficientFunds/InsufficientFunds.tsx | 5 +- .../NFTOperations/Modals/SignRawModal.tsx | 38 ++-- .../NFTOperations/TxRequest.types.ts | 1 + packages/mobile/src/core/NFTSend/NFTSend.tsx | 36 +++- .../src/core/RefillBattery/RefillBattery.tsx | 24 ++- packages/mobile/src/core/Send/Send.tsx | 18 +- .../AmountStep/CoinDropdown/CoinDropdown.tsx | 2 +- .../mobile/src/core/Settings/Settings.tsx | 8 +- .../src/core/StakingSend/StakingSend.tsx | 7 + packages/mobile/src/core/Swap/Swap.tsx | 5 + .../mobile/src/hooks/useJettonBalances.ts | 17 +- .../navigation/AppStack/AppStack.interface.ts | 1 + packages/mobile/src/navigation/ModalStack.tsx | 4 + .../SettingsStack/SettingsStack.interface.ts | 1 - .../SettingsStack/SettingsStack.tsx | 4 - packages/mobile/src/navigation/helper.ts | 5 +- .../hooks/useDeeplinkingResolvers.ts | 3 +- .../mobile/src/navigation/navigationNames.ts | 2 +- packages/mobile/src/store/models.ts | 1 + .../src/store/zustand/batteryUI/index.ts | 2 + .../src/store/zustand/batteryUI/types.ts | 6 + .../zustand/batteryUI/useBatteryUIStore.ts | 27 +++ .../mobile/src/tabs/Wallet/WalletScreen.tsx | 2 +- .../Wallet/components/WalletContentList.tsx | 37 +++- .../mobile/src/tabs/Wallet/hooks/useTokens.ts | 6 + .../mobile/src/uikit/TransitionOpacity.tsx | 18 +- packages/mobile/src/wallet/Tonkeeper.ts | 4 +- .../src/wallet/managers/BatteryManager.ts | 79 +++++++- .../src/wallet/managers/JettonsManager.ts | 4 +- .../src/wallet/managers/TonProofManager.ts | 1 - .../JettonBalanceModel/JettonBalanceModel.ts | 9 + .../components/BatteryIcon/BatteryIcon.tsx | 55 +++-- .../BatterySupportedTransactions.tsx | 109 ++++++++++ .../BatterySupportedTransactions/index.ts | 1 + .../RefillBattery/RefillBattery.tsx | 106 ++++++---- .../RefillBattery/RefillBatteryIAP.tsx | 153 ++++++++++---- .../RefillBatterySettingsWidget.tsx | 39 ++++ .../RefillBattery/RestorePurchases.tsx | 68 +++++++ packages/shared/hooks/index.ts | 1 + .../shared/hooks/useIsEnabledForBattery.ts | 12 ++ .../shared/i18n/locales/tonkeeper/en.json | 64 ++++-- .../shared/i18n/locales/tonkeeper/id.json | 37 +--- .../shared/i18n/locales/tonkeeper/ru-RU.json | 64 ++++-- .../shared/i18n/locales/tonkeeper/tr-TR.json | 34 ---- .../i18n/locales/tonkeeper/zh-Hans-CN.json | 14 +- packages/shared/modals/RefillBatteryModal.tsx | 96 +++++++-- packages/shared/utils/battery.ts | 16 +- packages/shared/utils/blockchain.ts | 16 +- .../png/ic-almost-empty-battery-128@4x.png | Bin 11881 -> 11692 bytes .../png/ic-almost-empty-battery-34@4x.png | Bin 0 -> 4892 bytes .../assets/icons/png/ic-battery-100-44@4x.png | Bin 0 -> 17350 bytes .../assets/icons/png/ic-battery-25-44@4x.png | Bin 0 -> 3961 bytes .../assets/icons/png/ic-battery-50-44@4x.png | Bin 0 -> 8456 bytes .../png/ic-battery-almost-empty-24@4x.png | Bin 0 -> 3412 bytes .../icons/png/ic-empty-battery-128@4x.png | Bin 11506 -> 13234 bytes .../ic-empty-battery-accent-flash-34@4x.png | Bin 0 -> 6054 bytes .../png/ic-empty-battery-flash-34@4x.png | Bin 0 -> 6046 bytes .../icons/png/ic-full-battery-128@4x.png | Bin 11853 -> 11787 bytes .../icons/png/ic-full-battery-34@4x.png | Bin 0 -> 5041 bytes .../icons/png/ic-medium-battery-128@4x.png | Bin 0 -> 11996 bytes .../icons/png/ic-medium-battery-34@4x.png | Bin 0 -> 5088 bytes .../svg/128/ic-almost-empty-battery-128.svg | 4 +- .../icons/svg/128/ic-empty-battery-128.svg | 4 +- .../icons/svg/128/ic-full-battery-128.svg | 4 +- .../icons/svg/128/ic-medium-battery-128.svg | 4 + .../svg/24/ic-battery-almost-empty-24.svg | 11 + .../svg/34/ic-almost-empty-battery-34.svg | 4 + .../34/ic-empty-battery-accent-flash-34.svg | 4 + .../svg/34/ic-empty-battery-flash-34.svg | 4 + .../icons/svg/34/ic-full-battery-34.svg | 4 + .../icons/svg/34/ic-medium-battery-34.svg | 4 + .../assets/icons/svg/44/ic-battery-100-44.svg | 4 + .../assets/icons/svg/44/ic-battery-25-44.svg | 4 + .../assets/icons/svg/44/ic-battery-50-44.svg | 4 + .../src/components/AnimatedBatteryIcon.tsx | 152 ++++++++++++++ packages/uikit/src/components/Icon/Icon.tsx | 2 +- .../uikit/src/components/Icon/Icon.types.ts | 30 +++ .../src/components/Icon/IconList.native.ts | 10 + .../uikit/src/components/List/ListItem.tsx | 7 +- .../uikit/src/components/Text/TextStyles.ts | 5 + packages/uikit/src/index.ts | 1 + 96 files changed, 1586 insertions(+), 455 deletions(-) create mode 100644 packages/mobile/src/components/SendScreenBatteryWidget/SendScreenBatteryWidget.tsx create mode 100644 packages/mobile/src/components/SendScreenBatteryWidget/index.ts create mode 100644 packages/mobile/src/store/zustand/batteryUI/index.ts create mode 100644 packages/mobile/src/store/zustand/batteryUI/types.ts create mode 100644 packages/mobile/src/store/zustand/batteryUI/useBatteryUIStore.ts create mode 100644 packages/shared/components/BatterySupportedTransactions/BatterySupportedTransactions.tsx create mode 100644 packages/shared/components/BatterySupportedTransactions/index.ts create mode 100644 packages/shared/components/RefillBattery/RefillBatterySettingsWidget.tsx create mode 100644 packages/shared/components/RefillBattery/RestorePurchases.tsx create mode 100644 packages/shared/hooks/useIsEnabledForBattery.ts create mode 100644 packages/uikit/assets/icons/png/ic-almost-empty-battery-34@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-battery-100-44@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-battery-25-44@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-battery-50-44@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-battery-almost-empty-24@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-empty-battery-accent-flash-34@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-empty-battery-flash-34@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-full-battery-34@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-medium-battery-128@4x.png create mode 100644 packages/uikit/assets/icons/png/ic-medium-battery-34@4x.png create mode 100644 packages/uikit/assets/icons/svg/128/ic-medium-battery-128.svg create mode 100644 packages/uikit/assets/icons/svg/24/ic-battery-almost-empty-24.svg create mode 100644 packages/uikit/assets/icons/svg/34/ic-almost-empty-battery-34.svg create mode 100644 packages/uikit/assets/icons/svg/34/ic-empty-battery-accent-flash-34.svg create mode 100644 packages/uikit/assets/icons/svg/34/ic-empty-battery-flash-34.svg create mode 100644 packages/uikit/assets/icons/svg/34/ic-full-battery-34.svg create mode 100644 packages/uikit/assets/icons/svg/34/ic-medium-battery-34.svg create mode 100644 packages/uikit/assets/icons/svg/44/ic-battery-100-44.svg create mode 100644 packages/uikit/assets/icons/svg/44/ic-battery-25-44.svg create mode 100644 packages/uikit/assets/icons/svg/44/ic-battery-50-44.svg create mode 100644 packages/uikit/src/components/AnimatedBatteryIcon.tsx diff --git a/packages/@core-js/src/BatteryAPI/BatteryGenerated.ts b/packages/@core-js/src/BatteryAPI/BatteryGenerated.ts index 3bc6985c2..02ee15352 100644 --- a/packages/@core-js/src/BatteryAPI/BatteryGenerated.ts +++ b/packages/@core-js/src/BatteryAPI/BatteryGenerated.ts @@ -14,6 +14,12 @@ export interface Error { error: string; } +export interface Status { + pending_transactions: { + id: string; + }[]; +} + export interface Config { /** * when building a message to transfer an NFT or Jetton, use this address to send excess funds back to Battery Service. @@ -25,6 +31,8 @@ export interface Config { export interface Balance { /** @example "10.250" */ balance: string; + /** @example "usd" */ + units: BalanceUnitsEnum; } export interface Purchases { @@ -87,7 +95,7 @@ export interface PromoCodeBatteryPurchaseStatus { error?: { /** @example "Temporary error. Try again later." */ msg: string; - /** @example "promo-code-is-already-used" */ + /** @example "promo-code-not-found" */ code: PromoCodeBatteryPurchaseStatusCodeEnum; }; } @@ -114,6 +122,12 @@ export interface Transactions { }[]; } +/** @example "usd" */ +export enum BalanceUnitsEnum { + Usd = 'usd', + Ton = 'ton', +} + /** @example "android" */ export enum PurchasesTypeEnum { RegularPurchase = 'regular-purchase', @@ -139,10 +153,10 @@ export enum IOsBatteryPurchaseStatusCodeEnum { Unknown = 'unknown', } -/** @example "promo-code-is-already-used" */ +/** @example "promo-code-not-found" */ export enum PromoCodeBatteryPurchaseStatusCodeEnum { - PromoCodeIsAlreadyUsed = 'promo-code-is-already-used', PromoCodeNotFound = 'promo-code-not-found', + PromoExceededAttempts = 'promo-exceeded-attempts', TemporaryError = 'temporary-error', } @@ -153,6 +167,23 @@ export enum TransactionsStatusEnum { Failed = 'failed', } +export interface GetBalanceParams { + /** @default "usd" */ + units?: UnitsEnum; +} + +/** @default "usd" */ +export enum UnitsEnum { + Usd = 'usd', + Ton = 'ton', +} + +/** @default "usd" */ +export enum GetBalanceParams1UnitsEnum { + Usd = 'usd', + Ton = 'ton', +} + export interface GetPurchasesParams { /** * @max 1000 @@ -421,7 +452,21 @@ export class BatteryGenerated<SecurityDataType extends unknown> { } /** - * @description This method returns information about the battery service. + * @description This method returns information about the current status of Battery Service. + * + * @name GetStatus + * @request GET:/status + */ + getStatus = (params: RequestParams = {}) => + this.http.request<Status, Error>({ + path: `/status`, + method: 'GET', + format: 'json', + ...params, + }); + + /** + * @description This method returns information about Battery Service. * * @name GetConfig * @request GET:/config @@ -435,15 +480,16 @@ export class BatteryGenerated<SecurityDataType extends unknown> { }); /** - * @description This method returns information about a battery + * @description This method returns information about a user's balance. * * @name GetBalance * @request GET:/balance */ - getBalance = (params: RequestParams = {}) => + getBalance = (query: GetBalanceParams, params: RequestParams = {}) => this.http.request<Balance, Error>({ path: `/balance`, method: 'GET', + query: query, format: 'json', ...params, }); diff --git a/packages/@core-js/src/TonAPI/TonAPIGenerated.ts b/packages/@core-js/src/TonAPI/TonAPIGenerated.ts index a73a3cd2a..e9a622567 100644 --- a/packages/@core-js/src/TonAPI/TonAPIGenerated.ts +++ b/packages/@core-js/src/TonAPI/TonAPIGenerated.ts @@ -63,6 +63,13 @@ export interface BlockValueFlow { minted: BlockCurrencyCollection; } +export interface ServiceStatus { + /** @default true */ + rest_online: boolean; + /** @example 100 */ + indexing_latency: number; +} + export interface BlockchainBlock { /** @example 130 */ tx_quantity: number; @@ -296,7 +303,7 @@ export interface ComputePhase { */ gas_used?: number; /** - * @format uint32 + * @format int32 * @example 5 */ vm_steps?: number; @@ -811,8 +818,15 @@ export interface BlockchainRawAccount { last_transaction_lt: number; /** @example "088b436a846d92281734236967970612f87fbd64a2cd3573107948379e8e4161" */ last_transaction_hash?: string; + /** @example "088b436a846d92281734236967970612f87fbd64a2cd3573107948379e8e4161" */ + frozen_hash?: string; status: AccountStatus; storage: AccountStorageInfo; + libraries?: { + /** @example true */ + public: boolean; + root: string; + }[]; } export interface Account { @@ -1244,6 +1258,15 @@ export interface JettonBalance { price?: TokenRates; wallet_address: AccountAddress; jetton: JettonPreview; + lock?: { + /** @example 597968399 */ + amount: string; + /** + * @format int64 + * @example 1678223064 + */ + till: number; + }; } export interface JettonsBalances { @@ -1692,7 +1715,7 @@ export interface TraceID { /** @example "55e8809519cd3c49098c9ee45afdafcea7a894a74d0f628d94a115a50e045122" */ id: string; /** - * @format uint64 + * @format int64 * @example 1645544908 */ utime: number; @@ -1893,17 +1916,17 @@ export interface DecodedMessage { ext_in_msg_decoded?: { wallet_v3?: { /** - * @format uint32 + * @format int64 * @example 1 */ subwallet_id: number; /** - * @format uint32 + * @format int64 * @example 1 */ valid_until: number; /** - * @format uint32 + * @format int64 * @example 1 */ seqno: number; @@ -1911,22 +1934,22 @@ export interface DecodedMessage { }; wallet_v4?: { /** - * @format uint32 + * @format int64 * @example 1 */ subwallet_id: number; /** - * @format uint32 + * @format int64 * @example 1 */ valid_until: number; /** - * @format uint32 + * @format int64 * @example 1 */ seqno: number; /** - * @format int8 + * @format int32 * @example 1 */ op: number; @@ -1934,7 +1957,7 @@ export interface DecodedMessage { }; wallet_highload_v2?: { /** - * @format uint32 + * @format int64 * @example 1 */ subwallet_id: number; @@ -2211,23 +2234,20 @@ export interface AccountInfoByStateInit { } export interface Seqno { - /** @format uint32 */ + /** @format int32 */ seqno: number; } export interface BlockRaw { /** - * @format uint32 + * @format int32 * @example 4294967295 */ workchain: number; + /** @example 800000000000000 */ + shard: string; /** - * @format uint64 - * @example 9223372036854776000 - */ - shard: number; - /** - * @format uint32 + * @format int32 * @example 30699640 */ seqno: number; @@ -2239,7 +2259,7 @@ export interface BlockRaw { export interface InitStateRaw { /** - * @format uint32 + * @format int32 * @example 4294967295 */ workchain: number; @@ -2285,6 +2305,18 @@ export interface TokenRates { diff_30d?: Record<string, string>; } +export interface MarketTonRates { + /** @example "OKX" */ + market: string; + /** @example 5.2 */ + usd_price: number; + /** + * @format int64 + * @example 1668436763 + */ + last_date_update: number; +} + /** @example "int_msg" */ export enum MessageMsgTypeEnum { IntMsg = 'int_msg', @@ -2372,9 +2404,10 @@ export enum JettonSwapActionDexEnum { } export enum NftPurchaseActionAuctionTypeEnum { + DNSTon = 'DNS.ton', DNSTg = 'DNS.tg', + NUMBERTg = 'NUMBER.tg', Getgems = 'getgems', - Basic = 'basic', } /** @example "ton20" */ @@ -2448,12 +2481,21 @@ export interface EmulateMessageToTraceParams { ignore_signature_check?: boolean; } +export interface EmulateMessageToAccountEventParams { + ignore_signature_check?: boolean; + /** + * account ID + * @example "0:97264395BD65A255A429B11326C84128B7D70FFED7949ABAE3036D506BA38621" + */ + accountId: string; +} + export interface GetAccountJettonsBalancesParams { /** * accept ton and all possible fiat currencies, separated by commas - * @example "ton,usd,rub" + * @example ["ton","usd","rub"] */ - currencies?: string; + currencies?: string[]; /** * account ID * @example "0:97264395BD65A255A429B11326C84128B7D70FFED7949ABAE3036D506BA38621" @@ -2604,8 +2646,8 @@ export interface GetAccountEventsParams { before_lt?: number; /** * @min 1 - * @max 1000 - * @example 100 + * @max 100 + * @example 20 */ limit: number; /** @@ -2926,14 +2968,16 @@ export interface GetStakingPoolsParams { export interface GetRatesParams { /** * accept ton and jetton master addresses, separated by commas - * @example "ton" + * @maxItems 100 + * @example ["ton"] */ - tokens: string; + tokens: string[]; /** * accept ton and all possible fiat currencies, separated by commas - * @example "ton,usd,rub" + * @maxItems 50 + * @example ["ton","usd","rub"] */ - currencies: string; + currencies: string[]; } export interface GetChartRatesParams { @@ -2963,7 +3007,7 @@ export interface GetChartRatesParams { export interface GetRawMasterchainInfoExtParams { /** * mode - * @format uint32 + * @format int32 * @example 0 */ mode: number; @@ -2972,7 +3016,7 @@ export interface GetRawMasterchainInfoExtParams { export interface GetRawBlockchainBlockHeaderParams { /** * mode - * @format uint32 + * @format int32 * @example 0 */ mode: number; @@ -2999,13 +3043,13 @@ export interface GetRawAccountStateParams { export interface GetRawShardInfoParams { /** * workchain - * @format uint32 + * @format int32 * @example 1 */ workchain: number; /** * shard - * @format uint64 + * @format int64 * @example 1 */ shard: number; @@ -3024,13 +3068,13 @@ export interface GetRawShardInfoParams { export interface GetRawTransactionsParams { /** * count - * @format uint32 + * @format int32 * @example 100 */ count: number; /** * lt - * @format uint64 + * @format int64 * @example 23814011000000 */ lt: number; @@ -3049,13 +3093,13 @@ export interface GetRawTransactionsParams { export interface GetRawListBlockTransactionsParams { /** * mode - * @format uint32 + * @format int32 * @example 0 */ mode: number; /** * count - * @format uint32 + * @format int32 * @example 100 */ count: number; @@ -3066,7 +3110,7 @@ export interface GetRawListBlockTransactionsParams { account_id?: string; /** * lt - * @format uint64 + * @format int64 * @example 23814011000000 */ lt?: number; @@ -3090,7 +3134,7 @@ export interface GetRawBlockProofParams { target_block?: string; /** * mode - * @format uint32 + * @format int32 * @example 0 */ mode: number; @@ -3099,7 +3143,7 @@ export interface GetRawBlockProofParams { export interface GetRawConfigParams { /** * mode - * @format uint32 + * @format int32 * @example 0 */ mode: number; @@ -3357,6 +3401,22 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { this.http = http; } + status = { + /** + * @description Reduce indexing latency + * + * @tags Blockchain + * @name ReduceIndexingLatency + * @request GET:/v2/status + */ + reduceIndexingLatency: (params: RequestParams = {}) => + this.http.request<ServiceStatus, Error>({ + path: `/v2/status`, + method: 'GET', + format: 'json', + ...params, + }), + }; blockchain = { /** * @description Get blockchain block data @@ -3895,7 +3955,7 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { */ timestamp: number; domain: { - /** @format uint32 */ + /** @format int32 */ length_bytes?: number; value: string; }; @@ -3945,7 +4005,7 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { * @request POST:/v2/accounts/{account_id}/events/emulate */ emulateMessageToAccountEvent: ( - accountId: string, + { accountId, ...query }: EmulateMessageToAccountEventParams, data: { /** @example "te6ccgECBQEAARUAAkWIAWTtae+KgtbrX26Bep8JSq8lFLfGOoyGR/xwdjfvpvEaHg" */ boc: string; @@ -3955,6 +4015,7 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { this.http.request<AccountEvent, Error>({ path: `/v2/accounts/${accountId}/events/emulate`, method: 'POST', + query: query, body: data, format: 'json', ...params, @@ -4723,6 +4784,26 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { format: 'json', ...params, }), + + /** + * @description Get the TON price from markets + * + * @tags Rates + * @name GetMarketsRates + * @request GET:/v2/rates/markets + */ + getMarketsRates: (params: RequestParams = {}) => + this.http.request< + { + markets: MarketTonRates[]; + }, + Error + >({ + path: `/v2/rates/markets`, + method: 'GET', + format: 'json', + ...params, + }), }; tonconnect = { /** @@ -4821,28 +4902,28 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { this.http.request< { /** - * @format uint32 + * @format int32 * @example 0 */ mode: number; /** - * @format uint32 + * @format int32 * @example 257 */ version: number; /** - * @format uint64 + * @format int64 * @example 7 */ capabilities: number; last: BlockRaw; /** - * @format uint32 + * @format int32 * @example 1687938199 */ last_utime: number; /** - * @format uint32 + * @format int32 * @example 1687938204 */ now: number; @@ -4870,7 +4951,7 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { this.http.request< { /** - * @format uint32 + * @format int32 * @example 1687146728 */ time: number; @@ -4946,7 +5027,7 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { { id: BlockRaw; /** - * @format uint32 + * @format int32 * @example 0 */ mode: number; @@ -4978,7 +5059,7 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { this.http.request< { /** - * @format uint32 + * @format int32 * @example 200 */ code: number; @@ -5117,7 +5198,7 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { { id: BlockRaw; /** - * @format uint32 + * @format int32 * @example 100 */ req_count: number; @@ -5125,13 +5206,13 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { incomplete: boolean; ids: { /** - * @format uint32 + * @format int32 * @example 0 */ mode: number; /** @example "131D0C65055F04E9C19D687B51BC70F952FD9CA6F02C2801D3B89964A779DF85" */ account?: string; - /** @format uint64 */ + /** @format int64 */ lt?: number; /** @example "131D0C65055F04E9C19D687B51BC70F952FD9CA6F02C2801D3B89964A779DF85" */ hash?: string; @@ -5185,9 +5266,9 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { /** @example "131D0C65055F04E9C19D687B51BC70F952FD9CA6F02C2801D3B89964A779DF85" */ config_proof: string; signatures: { - /** @format uint32 */ + /** @format int64 */ validator_set_hash: number; - /** @format uint32 */ + /** @format int32 */ catchain_seqno: number; signatures: { /** @example "131D0C65055F04E9C19D687B51BC70F952FD9CA6F02C2801D3B89964A779DF85" */ @@ -5222,7 +5303,7 @@ export class TonAPIGenerated<SecurityDataType extends unknown> { this.http.request< { /** - * @format uint32 + * @format int32 * @example 0 */ mode: number; diff --git a/packages/@core-js/src/service/contractService.ts b/packages/@core-js/src/service/contractService.ts index ebdb48760..dd1d47ea0 100644 --- a/packages/@core-js/src/service/contractService.ts +++ b/packages/@core-js/src/service/contractService.ts @@ -8,6 +8,11 @@ import { import { WalletContractV3R1, WalletContractV3R2, WalletContractV4 } from '@ton/ton'; import nacl from 'tweetnacl'; +export enum OpCodes { + JETTON_TRANSFER = 0xf8a7ea5, + NFT_TRANSFER = 0x5fcc3d14, +} + export enum WalletVersion { v3R1 = 0, v3R2 = 1, @@ -89,7 +94,7 @@ export class ContractService { static createNftTransferBody(createNftTransferBodyParams: CreateNftTransferBodyParams) { return beginCell() - .storeUint(0x5fcc3d14, 32) + .storeUint(OpCodes.NFT_TRANSFER, 32) .storeUint( createNftTransferBodyParams.queryId || ContractService.getWalletQueryId(), 64, @@ -106,7 +111,7 @@ export class ContractService { createJettonTransferBodyParams: CreateJettonTransferBodyParams, ) { return beginCell() - .storeUint(0xf8a7ea5, 32) // request_transfer op + .storeUint(OpCodes.JETTON_TRANSFER, 32) .storeUint( createJettonTransferBodyParams.queryId || ContractService.getWalletQueryId(), 64, diff --git a/packages/@core-js/src/service/transactionService.ts b/packages/@core-js/src/service/transactionService.ts index c03ed1616..2762b95bc 100644 --- a/packages/@core-js/src/service/transactionService.ts +++ b/packages/@core-js/src/service/transactionService.ts @@ -10,7 +10,7 @@ import { loadStateInit, } from '@ton/core'; import { Address as AddressFormatter } from '../formatters/Address'; -import { WalletContract } from './contractService'; +import { OpCodes, WalletContract } from './contractService'; import { SignRawMessage } from '@tonkeeper/mobile/src/core/ModalContainer/NFTOperations/TxRequest.types'; export type AnyAddress = string | Address | AddressFormatter; @@ -71,16 +71,75 @@ export class TransactionService { return { code, data }; } - static parseSignRawMessages(messages: SignRawMessage[]) { - return messages.map((message) => - internal({ + static parseSignRawMessages( + messages: SignRawMessage[], + customExcessesAccount?: string | null, + ) { + return messages.map((message) => { + let payload = message.payload && Cell.fromBase64(message.payload); + + if (payload && customExcessesAccount) { + payload = TransactionService.rebuildBodyWithCustomExcessesAccount( + payload, + customExcessesAccount, + ); + } + + return internal({ to: message.address, value: BigInt(message.amount), - body: message.payload && Cell.fromBase64(message.payload), + body: payload, bounce: this.getBounceFlagFromAddress(message.address), init: TransactionService.parseStateInit(message.stateInit), - }), - ); + }); + }); + } + + static rebuildBodyWithCustomExcessesAccount( + payload: Cell, + customExcessesAccount: string, + ) { + const slice = payload.beginParse(); + const opCode = slice.loadUint(32); + let builder = beginCell(); + + switch (opCode) { + case OpCodes.NFT_TRANSFER: + builder = builder + .storeUint(OpCodes.NFT_TRANSFER, 32) + .storeUint(slice.loadUint(64), 64) + .storeAddress(slice.loadAddress()); + + slice.loadMaybeAddress(); + + while (slice.remainingRefs) { + builder = builder.storeRef(slice.loadRef()); + } + + return builder + .storeAddress(Address.parse(customExcessesAccount)) + .storeBits(slice.loadBits(slice.remainingBits)) + .endCell(); + case OpCodes.JETTON_TRANSFER: + builder = builder + .storeUint(OpCodes.JETTON_TRANSFER, 32) + .storeUint(slice.loadUint(64), 64) + .storeCoins(slice.loadCoins()) + .storeAddress(slice.loadAddress()); + + slice.loadMaybeAddress(); + + while (slice.remainingRefs) { + builder = builder.storeRef(slice.loadRef()); + } + + return builder + .storeAddress(Address.parse(customExcessesAccount)) + .storeBits(slice.loadBits(slice.remainingBits)) + .endCell(); + default: + return payload; + } } static createTransfer(contract, transferParams: TransferParams) { diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index c94df8b10..22aeaee46 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -92,7 +92,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 433 - versionName "4.1.2" + versionName "4.2.0" missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'store', 'play' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 764467a97..f3ca23162 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ 203A6A1F2760BD6800817B71 /* Montserrat-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 203A6A0D2760BD6800817B71 /* Montserrat-Regular.ttf */; }; 5008E612299F9D1400D4850B /* SFMono-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 5008E610299F9D1400D4850B /* SFMono-Bold.otf */; }; 5008E614299F9D1400D4850B /* SFMono-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 5008E611299F9D1400D4850B /* SFMono-Medium.otf */; }; + 506008722B7F9EF10061C2CD /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 506008712B7F9EF10061C2CD /* StoreKit.framework */; }; 76F9B0131AB7FE11009E6D10 /* libPods-ton_keeper-ton_keeperTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB5E03C54A755139D3788F39 /* libPods-ton_keeper-ton_keeperTests.a */; }; 78B9AC5F426C0F28766DEC94 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D7306F36185132B3874D4C /* ExpoModulesProvider.swift */; }; 8D962335543EE7C6E1C6AB28 /* libPods-ton_keeper.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 39C1C20FA83478354251FB2A /* libPods-ton_keeper.a */; }; @@ -130,6 +131,7 @@ 39C1C20FA83478354251FB2A /* libPods-ton_keeper.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ton_keeper.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 5008E610299F9D1400D4850B /* SFMono-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SFMono-Bold.otf"; path = "../assets/fonts/SFMono-Bold.otf"; sourceTree = "<group>"; }; 5008E611299F9D1400D4850B /* SFMono-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SFMono-Medium.otf"; path = "../assets/fonts/SFMono-Medium.otf"; sourceTree = "<group>"; }; + 506008712B7F9EF10061C2CD /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; 50AD04972AD96A5C00E0648C /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/PassCode.strings; sourceTree = "<group>"; }; 50AD04982AD96A5D00E0648C /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/PassCode.stringsdict; sourceTree = "<group>"; }; 50AD04992AD96AFA00E0648C /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/PassCode.strings"; sourceTree = "<group>"; }; @@ -225,6 +227,7 @@ buildActionMask = 2147483647; files = ( 8D962335543EE7C6E1C6AB28 /* libPods-ton_keeper.a in Frameworks */, + 506008722B7F9EF10061C2CD /* StoreKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -260,6 +263,7 @@ 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { isa = PBXGroup; children = ( + 506008712B7F9EF10061C2CD /* StoreKit.framework */, C4FB2EE22912AF2700CB35D0 /* OpenSSL.xcframework */, C4FB2EE32912AF2700CB35D0 /* TON.xcframework */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */, @@ -1294,7 +1298,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.1.2; + MARKETING_VERSION = 4.2.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1328,7 +1332,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.1.2; + MARKETING_VERSION = 4.2.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/packages/mobile/src/blockchain/wallet.ts b/packages/mobile/src/blockchain/wallet.ts index b08c929fb..c90a63b81 100644 --- a/packages/mobile/src/blockchain/wallet.ts +++ b/packages/mobile/src/blockchain/wallet.ts @@ -26,16 +26,14 @@ import { import { tk } from '$wallet'; import { Address, Cell, internal } from '@ton/core'; -import { - emulateWithBattery, - sendBocWithBattery, -} from '@tonkeeper/shared/utils/blockchain'; +import { emulateBoc, sendBoc } from '@tonkeeper/shared/utils/blockchain'; import { OperationEnum, TonAPI, TypeEnum } from '@tonkeeper/core/src/TonAPI'; import { setBalanceForEmulation } from '@tonkeeper/shared/utils/wallet'; import { WalletNetwork } from '$wallet/WalletTypes'; import { createTonApiInstance } from '$wallet/utils'; import { config } from '$config'; import { toNano } from '$utils'; +import { BatterySupportedTransaction } from '$wallet/managers/BatteryManager'; const TonWeb = require('tonweb'); @@ -212,10 +210,6 @@ export class TonWallet { } } - private async sendBoc(boc: string): Promise<void> { - await sendBocWithBattery(boc); - } - async createSubscription( unlockedVault: UnlockedVault | Vault, beneficiaryAddress: string, @@ -327,8 +321,12 @@ export class TonWallet { return await this.vault.tonWallet.methods.isPluginInstalled(subscriptionAddress); } - private async calcFee(boc: string, params?): Promise<[BigNumber, boolean]> { - const { emulateResult, battery } = await emulateWithBattery(boc, params); + private async calcFee( + boc: string, + params?, + withRelayer = true, + ): Promise<[BigNumber, boolean]> { + const { emulateResult, battery } = await emulateBoc(boc, params, withRelayer); return [new BigNumber(emulateResult.event.extra).multipliedBy(-1), battery]; } @@ -422,6 +420,9 @@ export class TonWallet { let [feeNano, isBattery] = await this.calcFee( boc, [setBalanceForEmulation(toNano('2'))], // Emulate with higher balance to calculate fair amount to send + tk.wallet.battery.state.data.supportedTransactions[ + BatterySupportedTransaction.Jetton + ], ); return [Ton.fromNano(feeNano.toString()), isBattery]; @@ -486,7 +487,13 @@ export class TonWallet { let feeNano: BigNumber; let isBattery = false; try { - const [fee, battery] = await this.calcFee(boc); + const [fee, battery] = await this.calcFee( + boc, + undefined, + tk.wallet.battery.state.data.supportedTransactions[ + BatterySupportedTransaction.Jetton + ], + ); feeNano = fee; isBattery = battery; } catch (e) { @@ -509,7 +516,7 @@ export class TonWallet { } try { - await this.sendBoc(boc); + await sendBoc(boc, isBattery); } catch (e) { if (!store.getState().main.isTimeSynced) { throw new Error('wrong_time'); @@ -721,7 +728,7 @@ export class TonWallet { } try { - await this.sendBoc(boc); + await sendBoc(boc, false); } catch (e) { if (!store.getState().main.isTimeSynced) { throw new Error('wrong_time'); diff --git a/packages/mobile/src/components/SendScreenBatteryWidget/SendScreenBatteryWidget.tsx b/packages/mobile/src/components/SendScreenBatteryWidget/SendScreenBatteryWidget.tsx new file mode 100644 index 000000000..a5646b46e --- /dev/null +++ b/packages/mobile/src/components/SendScreenBatteryWidget/SendScreenBatteryWidget.tsx @@ -0,0 +1,47 @@ +import React, { memo, useMemo } from 'react'; +import { useBatteryState } from '@tonkeeper/shared/query/hooks/useBatteryState'; +import { Icon, Spacer, Steezy, Text, View } from '@tonkeeper/uikit'; +import { t } from '@tonkeeper/shared/i18n'; +import { BatteryState } from '@tonkeeper/shared/utils/battery'; + +export interface SendScreenBatteryWidgetProps { + isSendingWithBattery: boolean; +} + +export const SendScreenBatteryWidget = memo<SendScreenBatteryWidgetProps>((props) => { + const batteryState = useBatteryState(); + + const content = useMemo(() => { + if (props.isSendingWithBattery && batteryState === BatteryState.AlmostEmpty) { + return ( + <> + <Text color="textSecondary" type="body2"> + {t('battery.send_widget.battery')} + </Text> + <Spacer x={8} /> + <Icon + style={styles.iconSize.static} + imageStyle={styles.iconSize.static} + name={'ic-battery-almost-empty-24'} + /> + </> + ); + } + return null; + }, [batteryState, props.isSendingWithBattery]); + + return <View style={styles.container}>{content}</View>; +}); + +const styles = Steezy.create({ + container: { + paddingVertical: 8, + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'center', + }, + iconSize: { + width: 15, + height: 24, + }, +}); diff --git a/packages/mobile/src/components/SendScreenBatteryWidget/index.ts b/packages/mobile/src/components/SendScreenBatteryWidget/index.ts new file mode 100644 index 000000000..797105082 --- /dev/null +++ b/packages/mobile/src/components/SendScreenBatteryWidget/index.ts @@ -0,0 +1 @@ +export * from './SendScreenBatteryWidget'; diff --git a/packages/mobile/src/components/index.ts b/packages/mobile/src/components/index.ts index cdb256533..d63c51d3d 100644 --- a/packages/mobile/src/components/index.ts +++ b/packages/mobile/src/components/index.ts @@ -1 +1,2 @@ export * from './CardsWidget'; +export * from './SendScreenBatteryWidget'; diff --git a/packages/mobile/src/config/AppConfig.ts b/packages/mobile/src/config/AppConfig.ts index b61c764e6..6c8685bfb 100644 --- a/packages/mobile/src/config/AppConfig.ts +++ b/packages/mobile/src/config/AppConfig.ts @@ -14,7 +14,7 @@ type AppConfigOptions<TConfig> = { }; export class AppConfig<TConfig = {}> { - static readonly CONFIG_VERSION = 2; // change it, if config needs to be force updated + static readonly CONFIG_VERSION = 3; // change it, if config needs to be force updated private clientConfigStorageKey = `__client-config__v${AppConfig.CONFIG_VERSION}`; private serverConfigStorageKey = `__server-config__v${AppConfig.CONFIG_VERSION}`; diff --git a/packages/mobile/src/config/index.ts b/packages/mobile/src/config/index.ts index 1af3a7a0f..d826cfaee 100644 --- a/packages/mobile/src/config/index.ts +++ b/packages/mobile/src/config/index.ts @@ -43,17 +43,25 @@ export type AppConfigVars = { tonapiTestnetHost: string; tronapiHost: string; tronapiTestnetHost: string; + batteryHost: string; batteryTestnetHost: string; batteryMeanFees: string; + batteryReservedAmount: string; + batteryMeanPrice_swap: string; + batteryMeanPrice_jetton: string; + batteryMeanPrice_nft: string; + holdersAppEndpoint: string; holdersService: string; aptabaseEndpoint: string; aptabaseAppKey: string; disable_battery: boolean; + battery_beta: boolean; disable_battery_iap_module: boolean; disable_battery_send: boolean; disable_show_unverified_token: boolean; + disable_battery_promo_module: boolean; disable_tonstakers: boolean; disable_holders_cards: boolean; exclude_jetton_chart_periods: boolean; @@ -80,12 +88,20 @@ const defaultConfig: Partial<AppConfigVars> = { holdersService: 'https://card-dev.whales-api.com', tronapiHost: 'https://tron.tonkeeper.com', tronapiTestnetHost: 'https://testnet-tron.tonkeeper.com', + batteryHost: 'https://battery.tonkeeper.com', batteryTestnetHost: 'https://testnet-battery.tonkeeper.com', - batteryMeanFees: '0.08', - disable_battery: true, - disable_battery_iap_module: true, - disable_battery_send: true, + batteryMeanFees: '0.0055', + batteryReservedAmount: '0.3', + batteryMeanPrice_swap: '0.22', + batteryMeanPrice_jetton: '0.06', + batteryMeanPrice_nft: '0.03', + battery_beta: true, + disable_battery: false, + disable_battery_send: false, + disable_battery_iap_module: Platform.OS === 'android', // Enable for iOS, disable for Android + disable_battery_promo_module: true, + disable_show_unverified_token: false, disable_tonstakers: false, disable_holders_cards: true, diff --git a/packages/mobile/src/core/DevMenu/DevConfigScreen.tsx b/packages/mobile/src/core/DevMenu/DevConfigScreen.tsx index 79a539a0b..1236ba1ff 100644 --- a/packages/mobile/src/core/DevMenu/DevConfigScreen.tsx +++ b/packages/mobile/src/core/DevMenu/DevConfigScreen.tsx @@ -68,28 +68,6 @@ export const DevConfigScreen = memo(() => { }) } /> - <List.Item - title="Enable battery" - onPress={handleBooleanSwitch('disable_battery')} - rightContent={ - <Switch - trackColor={{ true: theme.accentBlue }} - value={!config.get('disable_battery')} - onChange={handleBooleanSwitch('disable_battery')} - /> - } - /> - <List.Item - title="Enable send using battery" - onPress={handleBooleanSwitch('disable_battery_send')} - rightContent={ - <Switch - trackColor={{ true: theme.accentBlue }} - value={!config.get('disable_battery_send')} - onChange={handleBooleanSwitch('disable_battery_send')} - /> - } - /> </List> </Screen.ScrollView> </Screen> diff --git a/packages/mobile/src/core/HideableAmount/ShowBalance.tsx b/packages/mobile/src/core/HideableAmount/ShowBalance.tsx index d409a0f56..abea7aa99 100644 --- a/packages/mobile/src/core/HideableAmount/ShowBalance.tsx +++ b/packages/mobile/src/core/HideableAmount/ShowBalance.tsx @@ -34,7 +34,7 @@ export const ShowBalance: React.FC<{ amount: string }> = ({ amount }) => { </View> ) : ( <TouchableOpacity activeOpacity={0.6} onPress={handleToggleHideAmounts}> - <Text type="num2">{amount}</Text> + <Text type="num3">{amount}</Text> </TouchableOpacity> )} </View> @@ -43,9 +43,12 @@ export const ShowBalance: React.FC<{ amount: string }> = ({ amount }) => { const styles = Steezy.create(({ colors }) => ({ container: { - height: 36, + flexDirection: 'row', + height: 54, + alignItems: 'center', }, starsContainer: { + height: 40, backgroundColor: colors.backgroundSecondary, borderRadius: 100, }, diff --git a/packages/mobile/src/core/Jetton/Jetton.tsx b/packages/mobile/src/core/Jetton/Jetton.tsx index 24c9634a5..28aac5c49 100644 --- a/packages/mobile/src/core/Jetton/Jetton.tsx +++ b/packages/mobile/src/core/Jetton/Jetton.tsx @@ -29,6 +29,7 @@ import { useWallet, useWalletCurrency } from '@tonkeeper/shared/hooks'; import { tk } from '$wallet'; import { Chart } from '$shared/components/Chart/new/Chart'; import { ChartPeriod } from '$store/zustand/chart'; +import { formatDate } from '@tonkeeper/shared/utils/date'; const unverifiedTokenHitSlop = { top: 4, left: 4, bottom: 4, right: 4 }; @@ -37,6 +38,10 @@ export const Jetton: React.FC<JettonProps> = ({ route }) => { const jetton = useJetton(route.params.jettonAddress); const jettonActivityList = useJettonActivityList(jetton.jettonAddress); const jettonPrice = useTokenPrice(jetton.jettonAddress, jetton.balance); + const lockedJettonPrice = useTokenPrice( + jetton.jettonAddress, + jetton.lock?.amount.toString() ?? '0', + ); const wallet = useWallet(); const isWatchOnly = wallet && wallet.isWatchOnly; @@ -95,10 +100,34 @@ export const Jetton: React.FC<JettonProps> = ({ route }) => { > {jettonPrice.formatted.totalFiat} </HideableAmount> + {jetton.lock ? ( + <> + <Spacer y={12} /> + <Text variant="body2" color="textSecondary"> + {formatter.format(jetton.lock.amount, { + decimals: jetton.metadata.decimals, + currency: jetton.metadata.symbol, + currencySeparator: 'wide', + })} + <Text variant="body2" color="textTertiary"> + {' '}·{' '} + </Text> + {lockedJettonPrice.formatted.totalFiat} + </Text> + <Text variant="body2" color="textTertiary"> + {t('jetton_locked_till', { + date: formatDate(jetton.lock.till * 1000, 'd MMM yyyy'), + })} + </Text> + </> + ) : null} {jettonPrice.formatted.fiat ? ( - <Text style={{ marginTop: 12 }} variant="body2" color="foregroundSecondary"> - {t('jetton_price')} {jettonPrice.formatted.fiat} - </Text> + <> + <Spacer y={12} /> + <Text variant="body2" color="foregroundSecondary"> + {t('jetton_price')} {jettonPrice.formatted.fiat} + </Text> + </> ) : null} </S.JettonAmountWrapper> {jetton.metadata.image ? ( diff --git a/packages/mobile/src/core/ModalContainer/InsufficientFunds/InsufficientFunds.tsx b/packages/mobile/src/core/ModalContainer/InsufficientFunds/InsufficientFunds.tsx index e4a2401d3..80b68734a 100644 --- a/packages/mobile/src/core/ModalContainer/InsufficientFunds/InsufficientFunds.tsx +++ b/packages/mobile/src/core/ModalContainer/InsufficientFunds/InsufficientFunds.tsx @@ -1,20 +1,17 @@ import React, { memo, useCallback, useMemo } from 'react'; import { t } from '@tonkeeper/shared/i18n'; import { Modal, Spacer } from '@tonkeeper/uikit'; -import { openExploreTab } from '$navigation'; +import { openExploreTab, openRefillBatteryModal } from '$navigation'; import { SheetActions, useNavigation } from '@tonkeeper/router'; import { Button, Icon, Text } from '$uikit'; import * as S from './InsufficientFunds.style'; import { delay, fromNano } from '$utils'; import { debugLog } from '$utils/debugLog'; import BigNumber from 'bignumber.js'; -import { store } from '$store'; import { formatter } from '$utils/formatter'; import { push } from '$navigation/imperative'; import { useBatteryBalance } from '@tonkeeper/shared/query/hooks/useBatteryBalance'; import { config } from '$config'; -import { openRefillBatteryModal } from '@tonkeeper/shared/modals/RefillBatteryModal'; -import { tk } from '$wallet'; import { Wallet } from '$wallet/Wallet'; import { AmountFormatter } from '@tonkeeper/core'; diff --git a/packages/mobile/src/core/ModalContainer/NFTOperations/Modals/SignRawModal.tsx b/packages/mobile/src/core/ModalContainer/NFTOperations/Modals/SignRawModal.tsx index 7342c3b02..071b32479 100644 --- a/packages/mobile/src/core/ModalContainer/NFTOperations/Modals/SignRawModal.tsx +++ b/packages/mobile/src/core/ModalContainer/NFTOperations/Modals/SignRawModal.tsx @@ -39,11 +39,10 @@ import { AnyActionItem, } from '$wallet/models/ActivityModel'; import { JettonTransferAction, NftItemTransferAction } from 'tonapi-sdk-js'; -import { - TokenDetailsParams, - TokenDetailsProps, -} from '../../../../components/TokenDetails/TokenDetails'; +import { TokenDetailsParams } from '../../../../components/TokenDetails/TokenDetails'; import { ModalStackRouteNames } from '$navigation'; +import { CanceledActionError } from '$core/Send/steps/ConfirmStep/ActionErrors'; +import { emulateBoc, sendBoc } from '@tonkeeper/shared/utils/blockchain'; interface SignRawModalProps { consequences?: MessageConsequences; @@ -82,7 +81,14 @@ export const SignRawModal = memo<SignRawModalProps>((props) => { const handleOpenTokenDetails = (tokenDetailsParams: TokenDetailsParams) => () => nav.navigate(ModalStackRouteNames.TokenDetails, tokenDetailsParams); + const handleConfirm = onConfirm(async ({ startLoading }) => { + const pendingTransactions = await tk.wallet.battery.getStatus(); + if (pendingTransactions.length) { + Toast.fail(t('transfer_pending_by_battery_error')); + await delay(200); + throw new CanceledActionError(); + } const vault = await unlockVault(wallet.identifier); const privateKey = await vault.getTonPrivateKey(); @@ -93,19 +99,18 @@ export const SignRawModal = memo<SignRawModalProps>((props) => { Buffer.from(vault.tonPublicKey), vault.workchain, ); + const boc = TransactionService.createTransfer(contract, { - messages: TransactionService.parseSignRawMessages(params.messages), + messages: TransactionService.parseSignRawMessages( + params.messages, + isBattery ? await tk.wallet.battery.getExcessesAccount() : undefined, + ), seqno: await getWalletSeqno(wallet), sendMode: 3, secretKey: Buffer.from(privateKey), }); - await wallet.tonapi.blockchain.sendBlockchainMessage( - { - boc, - }, - { format: 'text' }, - ); + await sendBoc(boc, isBattery); if (onSuccess) { trackEvent(Events.SendSuccess, { from: SendAnalyticsFrom.SignRaw }); @@ -118,7 +123,7 @@ export const SignRawModal = memo<SignRawModalProps>((props) => { return () => { onDismiss?.(); }; - }, []); + }, [onDismiss]); const actions = useMemo(() => { if (consequences) { @@ -319,9 +324,14 @@ export const openSignRawModal = async ( seqno: await getWalletSeqno(wallet), secretKey: Buffer.alloc(64), }); - consequences = await wallet.tonapi.wallet.emulateMessageToWallet({ + + const { emulateResult, battery } = await emulateBoc( boc, - }); + undefined, + options.experimentalWithBattery, + ); + consequences = emulateResult; + isBattery = battery; if (!isBattery) { const totalAmount = calculateMessageTransferAmount(params.messages); diff --git a/packages/mobile/src/core/ModalContainer/NFTOperations/TxRequest.types.ts b/packages/mobile/src/core/ModalContainer/NFTOperations/TxRequest.types.ts index 6ad3871d4..200a82282 100644 --- a/packages/mobile/src/core/ModalContainer/NFTOperations/TxRequest.types.ts +++ b/packages/mobile/src/core/ModalContainer/NFTOperations/TxRequest.types.ts @@ -135,6 +135,7 @@ export type TxResponseOptions = { }; export type TxRequestBody<TParams = TxParams> = { + experimentalWithBattery?: boolean; type: TxTypes; expires_sec?: number; response_options?: TxResponseOptions; diff --git a/packages/mobile/src/core/NFTSend/NFTSend.tsx b/packages/mobile/src/core/NFTSend/NFTSend.tsx index d7e44eaf7..79ae333fc 100644 --- a/packages/mobile/src/core/NFTSend/NFTSend.tsx +++ b/packages/mobile/src/core/NFTSend/NFTSend.tsx @@ -35,10 +35,7 @@ import { delay } from '$utils'; import { Toast } from '$store'; import axios from 'axios'; import { useUnlockVault } from '$core/ModalContainer/NFTOperations/useUnlockVault'; -import { - emulateWithBattery, - sendBocWithBattery, -} from '@tonkeeper/shared/utils/blockchain'; +import { emulateBoc, sendBoc } from '@tonkeeper/shared/utils/blockchain'; import { checkIsInsufficient, openInsufficientFundsModal, @@ -48,9 +45,10 @@ import { Keyboard } from 'react-native'; import nacl from 'tweetnacl'; import { useInstance } from '$hooks/useInstance'; import { AccountsApi, Configuration } from '@tonkeeper/core/src/legacy'; -import { useWallet } from '@tonkeeper/shared/hooks'; +import { useIsEnabledForBattery, useWallet } from '@tonkeeper/shared/hooks'; import { tk } from '$wallet'; import { config } from '$config'; +import { BatterySupportedTransaction } from '$wallet/managers/BatteryManager'; interface Props { route: RouteProp<AppStackParamList, AppStackRouteNames.NFTSend>; @@ -63,6 +61,9 @@ export const NFTSend: FC<Props> = (props) => { }, } = props; + const isNFTSendEnabledWithRelayer = useIsEnabledForBattery( + BatterySupportedTransaction.NFT, + ); const wallet = useWallet(); const stepViewRef = useRef<StepViewRef>(null); @@ -155,9 +156,10 @@ export const NFTSend: FC<Props> = (props) => { secretKey: Buffer.alloc(64), }); - const response = await emulateWithBattery( + const response = await emulateBoc( boc, [setBalanceForEmulation(toNano('2'))], // Emulate with higher balance to calculate fair amount to send + isNFTSendEnabledWithRelayer, ); setConsequences(response.emulateResult); @@ -178,7 +180,17 @@ export const NFTSend: FC<Props> = (props) => { } finally { setPreparing(false); } - }, [comment, isCommentEncrypted, nftAddress, recipient, wallet]); + }, [ + comment, + isCommentEncrypted, + isNFTSendEnabledWithRelayer, + nftAddress, + recipient, + wallet.address.ton.raw, + wallet.config.version, + wallet.config.workchain, + wallet.pubkey, + ]); const accountsApi = useInstance(() => { const tonApiConfiguration = new Configuration({ @@ -237,6 +249,13 @@ export const NFTSend: FC<Props> = (props) => { try { setSending(true); + const pendingTransactions = await tk.wallet.battery.getStatus(); + if (pendingTransactions.length) { + Toast.fail(t('transfer_pending_by_battery_error')); + await delay(200); + throw new CanceledActionError(); + } + const vault = await unlockVault(); const privateKey = await vault.getTonPrivateKey(); @@ -262,6 +281,7 @@ export const NFTSend: FC<Props> = (props) => { const checkResult = await checkIsInsufficient(totalAmount.toString(), tk.wallet); if (!isBattery && checkResult.insufficient) { + stepViewRef.current?.go(NFTSendSteps.ADDRESS); openInsufficientFundsModal({ totalAmount: totalAmount.toString(), balance: checkResult.balance, @@ -299,7 +319,7 @@ export const NFTSend: FC<Props> = (props) => { secretKey: Buffer.from(privateKey), }); - await sendBocWithBattery(boc); + await sendBoc(boc, isBattery); } catch (e) { throw e; } finally { diff --git a/packages/mobile/src/core/RefillBattery/RefillBattery.tsx b/packages/mobile/src/core/RefillBattery/RefillBattery.tsx index 4b1a2df04..8ba7e8289 100644 --- a/packages/mobile/src/core/RefillBattery/RefillBattery.tsx +++ b/packages/mobile/src/core/RefillBattery/RefillBattery.tsx @@ -1,17 +1,23 @@ import { memo } from 'react'; import { RefillBattery as RefillBatteryComponent } from '@tonkeeper/shared/components/RefillBattery/RefillBattery'; import { t } from '@tonkeeper/shared/i18n'; -import { ScrollHandler } from '$uikit'; +import { Screen, Steezy, View } from '@tonkeeper/uikit'; export const RefillBattery = memo(() => { return ( - <ScrollHandler - isLargeNavBar={false} - navBarTitle={t('battery.screen_title')} - navBarSubtitle={'Beta'} - subtitleProps={{ color: 'accentOrange' }} - > - <RefillBatteryComponent /> - </ScrollHandler> + <Screen> + <Screen.Header title={t('battery.screen_title')} /> + <Screen.ScrollView> + <View style={styles.container}> + <RefillBatteryComponent /> + </View> + </Screen.ScrollView> + </Screen> ); }); + +const styles = Steezy.create({ + container: { + marginBottom: 16, + }, +}); diff --git a/packages/mobile/src/core/Send/Send.tsx b/packages/mobile/src/core/Send/Send.tsx index 03b53a4de..fab73db27 100644 --- a/packages/mobile/src/core/Send/Send.tsx +++ b/packages/mobile/src/core/Send/Send.tsx @@ -5,7 +5,7 @@ import { StepView, StepViewItem, StepViewRef } from '$shared/components'; import { CryptoCurrencies, CryptoCurrency, Decimals } from '$shared/constants'; import { walletActions } from '$store/wallet'; import { NavBar, Text } from '$uikit'; -import { parseLocaleNumber } from '$utils'; +import { delay, parseLocaleNumber } from '$utils'; import React, { FC, useCallback, @@ -68,6 +68,7 @@ export const Send: FC<SendProps> = ({ route }) => { from, expiryTimestamp, redirectToActivity = true, + isBattery: initialIsBattery = false, } = route.params; const initialAddress = @@ -123,7 +124,7 @@ export const Send: FC<SendProps> = ({ route }) => { const [fee, setFee] = useState(initialFee); const [isInactive, setInactive] = useState(initialIsInactive); - const [isBattery, setBattery] = useState(false); + const [isBattery, setBattery] = useState(initialIsBattery); const [insufficientFundsParams, setInsufficientFundsParams] = useState<InsufficientFundsParams | null>(null); @@ -279,6 +280,13 @@ export const Send: FC<SendProps> = ({ route }) => { return onFail(new DismissedActionError()); } + const pendingTransactions = await tk.wallet.battery.getStatus(); + if (pendingTransactions.length) { + Toast.fail(t('transfer_pending_by_battery_error')); + await delay(200); + return onFail(new CanceledActionError()); + } + if (expiryTimestamp && expiryTimestamp < getTimeSec()) { Toast.fail(t('transfer_deeplink_expired_error')); @@ -286,6 +294,7 @@ export const Send: FC<SendProps> = ({ route }) => { } if (insufficientFundsParams) { + goToAmount(); openInsufficientFundsModal(insufficientFundsParams); return onFail(new CanceledActionError()); @@ -333,13 +342,13 @@ export const Send: FC<SendProps> = ({ route }) => { } }, [ - fee, recipient, expiryTimestamp, insufficientFundsParams, trcPayload.value, - isBattery, + goToAmount, dispatch, + fee, currencyAdditionalParams, currency, parsedAmount, @@ -349,6 +358,7 @@ export const Send: FC<SendProps> = ({ route }) => { tokenType, jettonWalletAddress, decimals, + isBattery, from, unlock, ], diff --git a/packages/mobile/src/core/Send/steps/AmountStep/CoinDropdown/CoinDropdown.tsx b/packages/mobile/src/core/Send/steps/AmountStep/CoinDropdown/CoinDropdown.tsx index 36233f4c0..38af5a9f1 100644 --- a/packages/mobile/src/core/Send/steps/AmountStep/CoinDropdown/CoinDropdown.tsx +++ b/packages/mobile/src/core/Send/steps/AmountStep/CoinDropdown/CoinDropdown.tsx @@ -56,7 +56,7 @@ const CoinDropdownComponent: FC<Props> = (props) => { const balances = useBalancesState(); - const { enabled: jettons } = useJettonBalances(false, true); + const { enabled: jettons } = useJettonBalances(true); const inscriptions = useTonInscriptions(); const { format } = useHideableFormatter(); diff --git a/packages/mobile/src/core/Settings/Settings.tsx b/packages/mobile/src/core/Settings/Settings.tsx index ffd77082d..da00ea3d4 100644 --- a/packages/mobile/src/core/Settings/Settings.tsx +++ b/packages/mobile/src/core/Settings/Settings.tsx @@ -22,10 +22,10 @@ import { openLegalDocuments, openManageTokens, openNotifications, - openRefillBattery, openSecurity, openSelectLanguage, openSubscriptions, + openRefillBatteryModal, } from '$navigation'; import { walletActions } from '$store/wallet'; import { @@ -207,7 +207,7 @@ export const Settings: FC = () => { }, []); const handleBattery = useCallback(() => { - openRefillBattery(); + openRefillBatteryModal(); }, []); const handleDeleteAccount = useCallback(() => { @@ -372,7 +372,9 @@ export const Settings: FC = () => { name={'ic-battery-28'} /> } - title={t('battery.settings')} + title={t('battery.settings', { + betaLabel: config.get('battery_beta') ? '(Beta)' : '', + })} onPress={handleBattery} /> )} diff --git a/packages/mobile/src/core/StakingSend/StakingSend.tsx b/packages/mobile/src/core/StakingSend/StakingSend.tsx index 46d565839..c9efff22e 100644 --- a/packages/mobile/src/core/StakingSend/StakingSend.tsx +++ b/packages/mobile/src/core/StakingSend/StakingSend.tsx @@ -286,6 +286,13 @@ export const StakingSend: FC<Props> = (props) => { try { setSending(true); + const pendingTransactions = await tk.wallet.battery.getStatus(); + if (pendingTransactions.length) { + Toast.fail(t('transfer_pending_by_battery_error')); + await delay(200); + throw new CanceledActionError(); + } + const totalAmount = calculateMessageTransferAmount(messages.current); const checkResult = await checkIsInsufficient(totalAmount, tk.wallet); if (checkResult.insufficient) { diff --git a/packages/mobile/src/core/Swap/Swap.tsx b/packages/mobile/src/core/Swap/Swap.tsx index c5a4f817d..4d03b21cf 100644 --- a/packages/mobile/src/core/Swap/Swap.tsx +++ b/packages/mobile/src/core/Swap/Swap.tsx @@ -16,6 +16,7 @@ import { ShouldStartLoadRequest } from 'react-native-webview/lib/WebViewTypes'; import { Linking } from 'react-native'; import { useDeeplinking } from '$libs/deeplinking'; import DeviceInfo from 'react-native-device-info'; +import { BatterySupportedTransaction } from '$wallet/managers/BatteryManager'; interface Props { jettonAddress?: string; @@ -83,6 +84,10 @@ export const Swap: FC<Props> = (props) => { openSignRawModal( request, { + experimentalWithBattery: + tk.wallet.battery.state.data.supportedTransactions[ + BatterySupportedTransaction.Swap + ], expires_sec: valid_until, response_options: { broadcast: false, diff --git a/packages/mobile/src/hooks/useJettonBalances.ts b/packages/mobile/src/hooks/useJettonBalances.ts index 0ba46b0a5..f9b99b6bd 100644 --- a/packages/mobile/src/hooks/useJettonBalances.ts +++ b/packages/mobile/src/hooks/useJettonBalances.ts @@ -9,10 +9,7 @@ export interface IBalances { enabled: JettonBalanceModel[]; disabled: JettonBalanceModel[]; } -export const useJettonBalances = ( - withZeroBalances?: boolean, - showStakingJettons = false, -) => { +export const useJettonBalances = (showStakingJettons = false) => { const { jettonBalances } = useJettons(); const approvalStatuses = useTokenApproval(); const stakingJettons = useStakingState(getStakingJettons); @@ -28,10 +25,6 @@ export const useJettonBalances = ( const approvalStatus = approvalStatuses.tokens[jettonAddress]; const isBlacklisted = jetton.verification === JettonVerification.BLACKLIST; - if (!withZeroBalances && jetton.balance === '0') { - return; - } - if ( !showStakingJettons && stakingJettons.includes(Address.parse(jetton.jettonAddress).toRaw()) @@ -50,13 +43,7 @@ export const useJettonBalances = ( }); return balances; - }, [ - approvalStatuses.tokens, - jettonBalances, - showStakingJettons, - stakingJettons, - withZeroBalances, - ]); + }, [approvalStatuses.tokens, jettonBalances, showStakingJettons, stakingJettons]); return jettons; }; diff --git a/packages/mobile/src/navigation/AppStack/AppStack.interface.ts b/packages/mobile/src/navigation/AppStack/AppStack.interface.ts index b5155d1bc..2ddee9ba8 100644 --- a/packages/mobile/src/navigation/AppStack/AppStack.interface.ts +++ b/packages/mobile/src/navigation/AppStack/AppStack.interface.ts @@ -17,6 +17,7 @@ export type AppStackParamList = { nftAddress: string; }; [AppStackRouteNames.Send]: { + isBattery?: boolean; currency?: CryptoCurrency | string; address?: string; comment?: string; diff --git a/packages/mobile/src/navigation/ModalStack.tsx b/packages/mobile/src/navigation/ModalStack.tsx index 5e2bc1c36..18e5db475 100644 --- a/packages/mobile/src/navigation/ModalStack.tsx +++ b/packages/mobile/src/navigation/ModalStack.tsx @@ -59,6 +59,10 @@ export const ModalStack = React.memo(() => ( path={AppStackRouteNames.ReceiveInscription} /> <Stack.Modal component={NFT} path="NFTItemDetails" /> + <Stack.Modal + component={RefillBatteryModal} + path={AppStackRouteNames.RefillBattery} + /> {/* <Stack.Modal component={Receive} path={AppStackRouteNames.Receive} /> */} <Stack.Modal component={Send} path={AppStackRouteNames.Send} /> <Stack.Modal component={RenewAllDomainModal} path="RenewAllDomains" /> diff --git a/packages/mobile/src/navigation/SettingsStack/SettingsStack.interface.ts b/packages/mobile/src/navigation/SettingsStack/SettingsStack.interface.ts index 968cc655e..a14e1b11f 100644 --- a/packages/mobile/src/navigation/SettingsStack/SettingsStack.interface.ts +++ b/packages/mobile/src/navigation/SettingsStack/SettingsStack.interface.ts @@ -9,7 +9,6 @@ export type SettingsStackParamList = { [SettingsStackRouteNames.FontLicense]: {}; [SettingsStackRouteNames.Notifications]: {}; [SettingsStackRouteNames.ChooseCurrency]: {}; - [SettingsStackRouteNames.RefillBattery]: {}; [SettingsStackRouteNames.Language]: {}; [SettingsStackRouteNames.Backup]: {}; }; diff --git a/packages/mobile/src/navigation/SettingsStack/SettingsStack.tsx b/packages/mobile/src/navigation/SettingsStack/SettingsStack.tsx index 857f2d617..5c75f65eb 100644 --- a/packages/mobile/src/navigation/SettingsStack/SettingsStack.tsx +++ b/packages/mobile/src/navigation/SettingsStack/SettingsStack.tsx @@ -40,10 +40,6 @@ export const SettingsStack: FC = () => { component={LegalDocuments} /> <Stack.Screen name={SettingsStackRouteNames.FontLicense} component={FontLicense} /> - <Stack.Screen - name={SettingsStackRouteNames.RefillBattery} - component={RefillBattery} - /> <Stack.Screen name={SettingsStackRouteNames.Notifications} component={Notifications} diff --git a/packages/mobile/src/navigation/helper.ts b/packages/mobile/src/navigation/helper.ts index b12c296ef..5a076becb 100644 --- a/packages/mobile/src/navigation/helper.ts +++ b/packages/mobile/src/navigation/helper.ts @@ -34,6 +34,7 @@ export interface OpenSendParams { expiryTimestamp?: number | null; redirectToActivity?: boolean; currencyAdditionalParams?: CurrencyAdditionalParams; + isBattery?: boolean; } export function openSend(params: OpenSendParams = {}) { @@ -131,8 +132,8 @@ export function openSelectLanguage() { push(SettingsStackRouteNames.Language); } -export function openRefillBattery() { - push(SettingsStackRouteNames.RefillBattery); +export function openRefillBatteryModal() { + push(AppStackRouteNames.RefillBattery); } export function openManageTokens(initialTab?: string) { diff --git a/packages/mobile/src/navigation/hooks/useDeeplinkingResolvers.ts b/packages/mobile/src/navigation/hooks/useDeeplinkingResolvers.ts index a7854f292..fa75b385b 100644 --- a/packages/mobile/src/navigation/hooks/useDeeplinkingResolvers.ts +++ b/packages/mobile/src/navigation/hooks/useDeeplinkingResolvers.ts @@ -399,12 +399,13 @@ export function useDeeplinkingResolvers() { amount, address, comment, - jettonWalletAddress: query.jetton, + jettonWalletAddress: jettonBalance.walletAddress, tokenType: TokenType.Jetton, onInsufficientFunds: openInsufficientFundsModal, onNext: (details) => { const options = { currency: query.jetton, + isBattery: details.isBattery, address, comment, amount, diff --git a/packages/mobile/src/navigation/navigationNames.ts b/packages/mobile/src/navigation/navigationNames.ts index fdbdfc507..b4858e154 100644 --- a/packages/mobile/src/navigation/navigationNames.ts +++ b/packages/mobile/src/navigation/navigationNames.ts @@ -6,6 +6,7 @@ export enum ModalStackRouteNames { export enum AppStackRouteNames { MainStack = 'MainStack', + RefillBattery = 'RefillBattery', Receive = 'Receive', Send = 'Send', ChooseCountry = 'ChooseCountry', @@ -78,7 +79,6 @@ export enum SettingsStackRouteNames { FontLicense = 'FontLicense', Notifications = 'Notifications', ChooseCurrency = 'ChooseCurrency', - RefillBattery = 'RefillBattery', Language = 'Language', Backup = 'Backup', } diff --git a/packages/mobile/src/store/models.ts b/packages/mobile/src/store/models.ts index 5747245f3..4107ed66d 100644 --- a/packages/mobile/src/store/models.ts +++ b/packages/mobile/src/store/models.ts @@ -281,6 +281,7 @@ export interface JettonBalanceModel { currency: CryptoCurrency; metadata: JettonMetadata; balance: string; + lock?: { amount: string; till: number }; jettonAddress: string; walletAddress: string; verification: JettonVerification; diff --git a/packages/mobile/src/store/zustand/batteryUI/index.ts b/packages/mobile/src/store/zustand/batteryUI/index.ts new file mode 100644 index 000000000..18de375cd --- /dev/null +++ b/packages/mobile/src/store/zustand/batteryUI/index.ts @@ -0,0 +1,2 @@ +export * from './useBatteryUIStore'; +export * from './types'; diff --git a/packages/mobile/src/store/zustand/batteryUI/types.ts b/packages/mobile/src/store/zustand/batteryUI/types.ts new file mode 100644 index 000000000..975247518 --- /dev/null +++ b/packages/mobile/src/store/zustand/batteryUI/types.ts @@ -0,0 +1,6 @@ +export interface IBatteryUIStore { + isViewedBatteryScreen: boolean; + actions: { + setIsViewedBatteryScreen: (isViewed: boolean) => void; + }; +} diff --git a/packages/mobile/src/store/zustand/batteryUI/useBatteryUIStore.ts b/packages/mobile/src/store/zustand/batteryUI/useBatteryUIStore.ts new file mode 100644 index 000000000..6b4dfab50 --- /dev/null +++ b/packages/mobile/src/store/zustand/batteryUI/useBatteryUIStore.ts @@ -0,0 +1,27 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { create } from 'zustand'; +import { createJSONStorage, persist } from 'zustand/middleware'; +import { IBatteryUIStore } from './types'; + +const initialState: Omit<IBatteryUIStore, 'actions'> = { + isViewedBatteryScreen: false, +}; + +export const useBatteryUIStore = create( + persist<IBatteryUIStore>( + (set) => ({ + ...initialState, + actions: { + setIsViewedBatteryScreen: (isViewedBatteryScreen) => { + set({ isViewedBatteryScreen }); + }, + }, + }), + { + name: 'battery-ui', + storage: createJSONStorage(() => AsyncStorage), + partialize: ({ isViewedBatteryScreen }) => + ({ isViewedBatteryScreen } as IBatteryUIStore), + }, + ), +); diff --git a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx index e9511cc93..5b95de5c7 100644 --- a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx +++ b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx @@ -159,7 +159,7 @@ export const WalletScreen = memo(({ navigation }) => { <View style={styles.amount} pointerEvents="box-none"> <View style={styles.balanceWithBattery}> <ShowBalance amount={balance.total.fiat} /> - <Spacer x={4} /> + <Spacer x={8} /> <BatteryIcon /> </View> <View style={styles.addressContainer}> diff --git a/packages/mobile/src/tabs/Wallet/components/WalletContentList.tsx b/packages/mobile/src/tabs/Wallet/components/WalletContentList.tsx index 41106cca4..a969cabfb 100644 --- a/packages/mobile/src/tabs/Wallet/components/WalletContentList.tsx +++ b/packages/mobile/src/tabs/Wallet/components/WalletContentList.tsx @@ -1,4 +1,4 @@ -import React, { memo, useMemo } from 'react'; +import React, { memo, ReactNode, useMemo } from 'react'; import { t } from '@tonkeeper/shared/i18n'; import { Screen, @@ -55,7 +55,7 @@ type TokenItem = { onPress?: () => void; title: string; subtitle?: string; - value?: string; + value?: string | ReactNode; subvalue?: string; rate?: Rate; picture?: string; @@ -142,11 +142,15 @@ const RenderItem = ({ item }: { item: Content }) => { } picture={item.picture} value={ - <HideableAmount - style={styles.valueText.static} - variant="label1" - stars=" * * *" - >{` ${item.value}`}</HideableAmount> + typeof item.value === 'string' ? ( + <HideableAmount + style={styles.valueText.static} + variant="label1" + stars=" * * *" + >{` ${item.value}`}</HideableAmount> + ) : ( + item.value + ) } subvalue={ item.subvalue && ( @@ -303,7 +307,24 @@ export const WalletContentList = memo<BalancesListProps>( onPress: () => openJetton(item.address.rawAddress), picture: item.iconUrl, title: item.symbol, - value: item.quantity.formatted, + value: item.lock ? ( + <HideableAmount + style={styles.valueText.static} + variant="label1" + stars=" * * *" + > + {item.quantity.formatted} + <Text type="label1" color="textTertiary"> + {' '} + ·{' '} + </Text> + <Text type="label1" color="textSecondary"> + {item.lock.formatted} + </Text> + </HideableAmount> + ) : ( + item.quantity.formatted + ), subvalue: item.rate.total, rate: item.rate.price ? { diff --git a/packages/mobile/src/tabs/Wallet/hooks/useTokens.ts b/packages/mobile/src/tabs/Wallet/hooks/useTokens.ts index f09e2e94d..9e8a8d87f 100644 --- a/packages/mobile/src/tabs/Wallet/hooks/useTokens.ts +++ b/packages/mobile/src/tabs/Wallet/hooks/useTokens.ts @@ -24,6 +24,9 @@ type TokenInfo = { value: string; formatted: string; }; + lock?: { + formatted: string; + }; price: TokenPrice; rate: { price: string | null; @@ -65,6 +68,9 @@ export const useTonkens = (): { value: item.balance, formatted: formatter.format(item.balance), }, + lock: item.lock && { + formatted: formatter.format(item.lock.amount), + }, price: rate, rate: { price: rate.formatted.fiat, diff --git a/packages/mobile/src/uikit/TransitionOpacity.tsx b/packages/mobile/src/uikit/TransitionOpacity.tsx index d5638fd2c..a5fd2e483 100644 --- a/packages/mobile/src/uikit/TransitionOpacity.tsx +++ b/packages/mobile/src/uikit/TransitionOpacity.tsx @@ -15,7 +15,7 @@ interface TransitionOpacity { style?: StyleProp<ViewStyle>; duration?: number; children: React.ReactNode; -} +} export const TransitionOpacity: React.FC<TransitionOpacity> = (props) => { const { @@ -32,12 +32,16 @@ export const TransitionOpacity: React.FC<TransitionOpacity> = (props) => { React.useEffect(() => { if (isVisible) { - setIsShown(true); - opacity.value = withDelay( - 250, - withTiming(1, { + opacity.value = withTiming( + 1, + { duration, - }), + }, + (isComplete) => { + if (isComplete) { + runOnJS(setIsShown)(true); + } + }, ); } else { opacity.value = withTiming( @@ -52,7 +56,7 @@ export const TransitionOpacity: React.FC<TransitionOpacity> = (props) => { }, ); } - }, [isVisible]); + }, [duration, isVisible, opacity]); const opacityStyle = useAnimatedStyle(() => ({ opacity: opacity.value, diff --git a/packages/mobile/src/wallet/Tonkeeper.ts b/packages/mobile/src/wallet/Tonkeeper.ts index 12d2890a6..5bdd475ac 100644 --- a/packages/mobile/src/wallet/Tonkeeper.ts +++ b/packages/mobile/src/wallet/Tonkeeper.ts @@ -264,7 +264,9 @@ export class Tonkeeper { const walletsInstances = await Promise.all( sortedWallets.map((wallet) => this.createWalletInstance(wallet)), ); - walletsInstances.map((instance) => instance.tonProof.obtainProof(keyPair)); + walletsInstances.map((instance) => + instance.tonProof.obtainProof(keyPair).then(() => instance.battery.load()), + ); await this.setWallet(walletsInstances[0]); diff --git a/packages/mobile/src/wallet/managers/BatteryManager.ts b/packages/mobile/src/wallet/managers/BatteryManager.ts index e6434cb68..3d408e64d 100644 --- a/packages/mobile/src/wallet/managers/BatteryManager.ts +++ b/packages/mobile/src/wallet/managers/BatteryManager.ts @@ -1,20 +1,37 @@ -import { BatteryAPI } from '@tonkeeper/core/src/BatteryAPI'; +import { BatteryAPI, UnitsEnum } from '@tonkeeper/core/src/BatteryAPI'; import { MessageConsequences } from '@tonkeeper/core/src/TonAPI'; import { Storage } from '@tonkeeper/core/src/declarations/Storage'; import { State } from '@tonkeeper/core/src/utils/State'; import { TonProofManager } from '$wallet/managers/TonProofManager'; +import { logger, NamespacedLogger } from '$logger'; + +export enum BatterySupportedTransaction { + Swap = 'swap', + Jetton = 'jetton', + NFT = 'nft', +} export interface BatteryState { isLoading: boolean; balance?: string; + reservedBalance?: string; + supportedTransactions: Record<BatterySupportedTransaction, boolean>; } export class BatteryManager { public state = new State<BatteryState>({ isLoading: false, balance: undefined, + reservedBalance: '0', + supportedTransactions: { + [BatterySupportedTransaction.Swap]: true, + [BatterySupportedTransaction.Jetton]: true, + [BatterySupportedTransaction.NFT]: true, + }, }); + private logger: NamespacedLogger; + constructor( private persistPath: string, private tonProof: TonProofManager, @@ -22,10 +39,15 @@ export class BatteryManager { private storage: Storage, ) { this.state.persist({ - partialize: ({ balance }) => ({ balance }), + partialize: ({ balance, reservedBalance, supportedTransactions }) => ({ + balance, + reservedBalance, + supportedTransactions, + }), storage: this.storage, key: `${this.persistPath}/battery`, }); + this.logger = logger.extend('BatteryManager'); } public async fetchBalance() { @@ -34,14 +56,23 @@ export class BatteryManager { throw new Error('No proof token'); } this.state.set({ isLoading: true }); - const data = await this.batteryapi.getBalance({ - headers: { - 'X-TonConnect-Auth': this.tonProof.tonProofToken, + const data = await this.batteryapi.getBalance( + { units: UnitsEnum.Ton }, + { + headers: { + 'X-TonConnect-Auth': this.tonProof.tonProofToken, + }, }, + ); + + this.state.set({ + isLoading: false, + balance: data.balance, + // @ts-expect-error reservedAmount will be implemented in API later. Remove then + reservedBalance: data.reservedBalance ?? '0', }); - this.state.set({ isLoading: false, balance: data.balance }); } catch (err) { - this.state.set({ isLoading: false, balance: '0' }); + this.state.set({ isLoading: false }); return null; } } @@ -108,10 +139,23 @@ export class BatteryManager { return data.transactions; } catch (err) { - console.log('[ios battery in-app purchase]', err); + this.logger.error(err); } } + public async setSupportedTransaction( + transaction: BatterySupportedTransaction, + supported: boolean, + ) { + this.state.set((state) => ({ + ...state, + supportedTransactions: { + ...state.supportedTransactions, + [transaction]: supported, + }, + })); + } + public async makeAndroidPurchase(purchases: { token: string; product_id: string }[]) { try { if (!this.tonProof.tonProofToken) { @@ -135,6 +179,25 @@ export class BatteryManager { } } + public async getStatus() { + try { + if (!this.tonProof.tonProofToken) { + throw new Error('No proof token'); + } + + const data = await this.batteryapi.getStatus({ + headers: { + 'X-TonConnect-Auth': this.tonProof.tonProofToken, + }, + }); + + return data.pending_transactions; + } catch (err) { + logger.error('getStatus error'); + return []; + } + } + public async sendMessage(boc: string) { try { if (!this.tonProof.tonProofToken) { diff --git a/packages/mobile/src/wallet/managers/JettonsManager.ts b/packages/mobile/src/wallet/managers/JettonsManager.ts index ffe8c6a20..5ac2cc6aa 100644 --- a/packages/mobile/src/wallet/managers/JettonsManager.ts +++ b/packages/mobile/src/wallet/managers/JettonsManager.ts @@ -7,8 +7,6 @@ import { JettonBalanceModel } from '../models/JettonBalanceModel'; import { Address } from '@tonkeeper/core/src/formatters/Address'; import { TokenApprovalManager } from './TokenApprovalManager'; import { TonPriceManager } from './TonPriceManager'; -import BigNumber from 'bignumber.js'; -import { AmountFormatter } from '@tonkeeper/core'; import { sortByPrice } from '@tonkeeper/core/src/utils/jettons'; export type JettonsState = { @@ -82,7 +80,7 @@ export class JettonsManager { const jettonBalances = accountJettons.balances .filter((item) => { - return item.balance !== '0'; + return item.balance !== '0' || (item.lock && item.lock.amount !== '0'); }) .sort((a, b) => { // Unverified or blacklisted tokens have to be at the end of array diff --git a/packages/mobile/src/wallet/managers/TonProofManager.ts b/packages/mobile/src/wallet/managers/TonProofManager.ts index 0355b0f27..f30506a7a 100644 --- a/packages/mobile/src/wallet/managers/TonProofManager.ts +++ b/packages/mobile/src/wallet/managers/TonProofManager.ts @@ -32,7 +32,6 @@ export class TonProofManager { const { token } = await this.tonapi.wallet.tonConnectProof(proof); this.tonProofToken = token; - await SecureStore.setItemAsync(`proof-${this.identifier}`, token); } catch (err) { console.log('TonProofManager.obtainProof', err); diff --git a/packages/mobile/src/wallet/models/JettonBalanceModel/JettonBalanceModel.ts b/packages/mobile/src/wallet/models/JettonBalanceModel/JettonBalanceModel.ts index 3574dde41..e934e3323 100644 --- a/packages/mobile/src/wallet/models/JettonBalanceModel/JettonBalanceModel.ts +++ b/packages/mobile/src/wallet/models/JettonBalanceModel/JettonBalanceModel.ts @@ -8,6 +8,7 @@ export class JettonBalanceModel { jettonAddress: string; walletAddress: string; verification: JettonVerification; + lock?: { amount: string; till: number }; constructor(jettonBalance: JettonBalance) { this.metadata = jettonBalance.jetton; @@ -15,6 +16,14 @@ export class JettonBalanceModel { jettonBalance.balance, jettonBalance.jetton.decimals, ); + + this.lock = jettonBalance.lock && { + amount: AmountFormatter.fromNanoStatic( + jettonBalance.lock.amount, + jettonBalance.jetton.decimals, + ), + till: jettonBalance.lock.till, + }; this.jettonAddress = new Address(jettonBalance.jetton.address).toFriendly(); this.walletAddress = new Address(jettonBalance.wallet_address.address).toFriendly(); this.verification = jettonBalance.jetton diff --git a/packages/shared/components/BatteryIcon/BatteryIcon.tsx b/packages/shared/components/BatteryIcon/BatteryIcon.tsx index 052931752..5080a0842 100644 --- a/packages/shared/components/BatteryIcon/BatteryIcon.tsx +++ b/packages/shared/components/BatteryIcon/BatteryIcon.tsx @@ -1,28 +1,57 @@ import React, { memo } from 'react'; import { useBatteryBalance } from '../../query/hooks/useBatteryBalance'; -import { Icon, IconNames, TouchableOpacity } from '@tonkeeper/uikit'; +import { + AnimatedBatteryIcon, + AnimatedBatterySize, + Icon, + Steezy, + TouchableOpacity, +} from '@tonkeeper/uikit'; import { BatteryState, getBatteryState } from '../../utils/battery'; -import { openRefillBatteryModal } from '../../modals/RefillBatteryModal'; import { config } from '@tonkeeper/mobile/src/config'; +import { useBatteryUIStore } from '@tonkeeper/mobile/src/store/zustand/batteryUI'; +import { openRefillBatteryModal } from '@tonkeeper/mobile/src/navigation'; -const iconNames: { [key: string]: IconNames } = { - [BatteryState.Empty]: 'ic-empty-battery-28', - [BatteryState.AlmostEmpty]: 'ic-empty-battery-28', - [BatteryState.Medium]: 'ic-almost-empty-battery-28', - [BatteryState.Full]: 'ic-full-battery-28', -}; - -const hitSlop = { top: 8, bottom: 8, right: 8, left: 8 }; +const hitSlop = { top: 12, bottom: 12, right: 24, left: 8 }; export const BatteryIcon = memo(() => { const { balance } = useBatteryBalance(); - if (!balance || balance === '0' || config.get('disable_battery')) return null; + const isViewedBatteryScreen = useBatteryUIStore((state) => state.isViewedBatteryScreen); + if (config.get('disable_battery')) { + return null; + } - const iconName = iconNames[getBatteryState(balance)]; + // Hide battery icon if it's empty and beta is enabled + if (config.get('battery_beta') && (!balance || balance === '0')) { + return null; + } return ( <TouchableOpacity onPress={openRefillBatteryModal} hitSlop={hitSlop}> - <Icon colorless name={iconName} /> + {getBatteryState(balance) === BatteryState.Empty ? ( + <Icon + imageStyle={styles.iconSize.static} + style={styles.iconSize.static} + colorless + name={ + isViewedBatteryScreen + ? 'ic-empty-battery-flash-34' + : 'ic-empty-battery-accent-flash-34' + } + /> + ) : ( + <AnimatedBatteryIcon + progress={parseFloat(balance)} + size={AnimatedBatterySize.Small} + /> + )} </TouchableOpacity> ); }); + +const styles = Steezy.create({ + iconSize: { + width: 20, + height: 34, + }, +}); diff --git a/packages/shared/components/BatterySupportedTransactions/BatterySupportedTransactions.tsx b/packages/shared/components/BatterySupportedTransactions/BatterySupportedTransactions.tsx new file mode 100644 index 000000000..efb1bab78 --- /dev/null +++ b/packages/shared/components/BatterySupportedTransactions/BatterySupportedTransactions.tsx @@ -0,0 +1,109 @@ +import React, { memo, useCallback } from 'react'; +import { List, Steezy, Switch, Text, View } from '@tonkeeper/uikit'; +import BigNumber from 'bignumber.js'; +import { config } from '@tonkeeper/mobile/src/config'; +import { t } from '@tonkeeper/shared/i18n'; +import { capitalizeFirstLetter } from '../../utils/date'; +import { useExternalState } from '../../hooks/useExternalState'; +import { tk } from '@tonkeeper/mobile/src/wallet'; +import { BatterySupportedTransaction } from '@tonkeeper/mobile/src/wallet/managers/BatteryManager'; +import { Platform } from 'react-native'; + +export interface SupportedTransaction { + type: BatterySupportedTransaction; + name: string; + nameSingle: string; +} + +export const supportedTransactions: SupportedTransaction[] = [ + { + type: BatterySupportedTransaction.Swap, + name: 'battery.transactions.types.swap', + nameSingle: 'battery.transactions.type.swap', + }, + { + type: BatterySupportedTransaction.NFT, + name: 'battery.transactions.types.nft', + nameSingle: 'battery.transactions.type.transfer', + }, + { + type: BatterySupportedTransaction.Jetton, + name: 'battery.transactions.types.jetton', + nameSingle: 'battery.transactions.type.transfer', + }, +]; + +const calculateChargesAmount = (transactionCost: string, chargeCost: string) => + new BigNumber(transactionCost).div(chargeCost).decimalPlaces(0).toNumber(); + +export interface BatterySupportedTransactionsProps { + editable?: boolean; +} + +export const BatterySupportedTransactions = memo<BatterySupportedTransactionsProps>( + (props) => { + const supportedTransactionsValues = useExternalState( + tk.wallet.battery.state, + (state) => state.supportedTransactions, + ); + + const handleSwitchSupport = useCallback( + (transactionType: BatterySupportedTransaction) => (supported: boolean) => { + tk.wallet.battery.setSupportedTransaction(transactionType, supported); + }, + [], + ); + + return ( + <View> + {props.editable && ( + <View style={styles.textContainer}> + <Text textAlign="center" type="h2"> + {t('battery.transactions.settings')} + </Text> + <Text textAlign="center" color="textSecondary" type="body2"> + {t('battery.transactions.description')} + </Text> + </View> + )} + <List> + {supportedTransactions.map((transaction) => ( + <List.Item + key={transaction.type} + disabled={!props.editable} + onPress={() => + handleSwitchSupport(transaction.type)( + !supportedTransactionsValues[transaction.type], + ) + } + title={capitalizeFirstLetter(t(transaction.name))} + subtitle={t('battery.transactions.charges_per_action', { + count: calculateChargesAmount( + config.get(`batteryMeanPrice_${transaction.type}`), + config.get('batteryMeanFees'), + ), + transactionName: t(transaction.nameSingle), + })} + rightContent={ + props.editable ? ( + <Switch + disabled={Platform.OS === 'android'} // Temp fix. Should refactor screen with react-native-pager-view + onChange={handleSwitchSupport(transaction.type)} + value={supportedTransactionsValues[transaction.type]} + /> + ) : null + } + /> + ))} + </List> + </View> + ); + }, +); + +const styles = Steezy.create({ + textContainer: { + paddingHorizontal: 32, + marginBottom: 32, + }, +}); diff --git a/packages/shared/components/BatterySupportedTransactions/index.ts b/packages/shared/components/BatterySupportedTransactions/index.ts new file mode 100644 index 000000000..79ce008b1 --- /dev/null +++ b/packages/shared/components/BatterySupportedTransactions/index.ts @@ -0,0 +1 @@ +export * from './BatterySupportedTransactions'; diff --git a/packages/shared/components/RefillBattery/RefillBattery.tsx b/packages/shared/components/RefillBattery/RefillBattery.tsx index 6251637b1..367c5f395 100644 --- a/packages/shared/components/RefillBattery/RefillBattery.tsx +++ b/packages/shared/components/RefillBattery/RefillBattery.tsx @@ -6,78 +6,105 @@ import { import { memo } from 'react'; import { useBatteryBalance } from '../../query/hooks/useBatteryBalance'; -import { Icon, IconNames, Spacer, Steezy, Text, View } from '@tonkeeper/uikit'; -import { navigation, SheetActions } from '@tonkeeper/router'; +import { + AnimatedBatteryIcon, + AnimatedBatterySize, + Icon, + Spacer, + Steezy, + Text, + TouchableOpacity, + View, +} from '@tonkeeper/uikit'; import { RefillBatteryIAP } from './RefillBatteryIAP'; import { t } from '@tonkeeper/shared/i18n'; import { config } from '@tonkeeper/mobile/src/config'; import { RechargeByPromoButton } from './RechargeByPromoButton'; +import { RestorePurchases } from './RestorePurchases'; +import { RefillBatterySettingsWidget } from './RefillBatterySettingsWidget'; +import Animated from 'react-native-reanimated'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { Tag } from '@tonkeeper/mobile/src/uikit'; -const iconNames: { [key: string]: IconNames } = { - [BatteryState.Empty]: 'ic-empty-battery-128', - [BatteryState.AlmostEmpty]: 'ic-empty-battery-128', - [BatteryState.Medium]: 'ic-almost-empty-battery-128', - [BatteryState.Full]: 'ic-full-battery-128', -}; +export interface RefillBatteryProps { + navigateToTransactions: () => void; +} -export const RefillBattery = memo(() => { +export const RefillBattery = memo<RefillBatteryProps>((props) => { const { balance } = useBatteryBalance(); const batteryState = getBatteryState(balance ?? '0'); - const iconName = iconNames[batteryState]; const availableNumOfTransactionsCount = calculateAvailableNumOfTransactions( balance ?? '0', ); + const bottomInsets = useSafeAreaInsets().bottom; const isInAppPurchasesDisabled = config.get('disable_battery_iap_module'); + const isPromoDisabled = config.get('disable_battery_promo_module'); return ( - <> + <Animated.ScrollView + showsVerticalScrollIndicator={false} + contentContainerStyle={{ paddingBottom: bottomInsets + 16 }} + > <View style={styles.contentContainer}> - <Icon colorless name={iconName} /> - <Spacer y={24} /> + {batteryState === BatteryState.Empty ? ( + <Icon colorless name={'ic-empty-battery-128'} /> + ) : ( + <View style={styles.animatedBatteryContainer}> + <AnimatedBatteryIcon + progress={parseFloat(balance)} + size={AnimatedBatterySize.Large} + /> + </View> + )} + <Spacer y={16} /> + {config.get('battery_beta') && ( + <> + <Tag type="warning">Beta</Tag> + <Spacer y={4} /> + </> + )} <Text textAlign="center" type="h2"> - {t(`battery.title.${batteryState.toLowerCase()}`)} + {t(`battery.title`)} </Text> - <Spacer y={4} /> <Text textAlign="center" type="body2" color="textSecondary"> {t( `battery.description.${ - batteryState === BatteryState.Empty - ? 'empty' - : availableNumOfTransactionsCount - ? 'other' - : 'less_10' + batteryState === BatteryState.Empty ? 'empty' : 'other' }`, { - cnt: availableNumOfTransactionsCount, + count: availableNumOfTransactionsCount.toNumber(), }, )} </Text> - <Spacer y={16} /> + {batteryState === BatteryState.Empty && ( + <TouchableOpacity onPress={props.navigateToTransactions}> + <Text textAlign="center" type="body2" color="textAccent"> + {t('battery.transactions.supported')} + </Text> + </TouchableOpacity> + )} + <Spacer y={32} /> </View> + {batteryState !== BatteryState.Empty && ( + <RefillBatterySettingsWidget onPress={props.navigateToTransactions} /> + )} <View style={styles.indent}> {!isInAppPurchasesDisabled ? <RefillBatteryIAP /> : null} - <RechargeByPromoButton /> - <Spacer y={16} /> - <Text type="label2" textAlign="center" color="textTertiary"> - {t('battery.packages.disclaimer')} - </Text> + {!isPromoDisabled ? ( + <> + <RechargeByPromoButton /> + <Spacer y={16} /> + </> + ) : null} + <RestorePurchases /> </View> - </> + </Animated.ScrollView> ); }); -export function openRefillBatteryModal() { - navigation.push('SheetsProvider', { - $$action: SheetActions.ADD, - component: RefillBattery, - path: '/refill-battery', - }); -} - export const styles = Steezy.create({ contentContainer: { - paddingTop: 48, alignItems: 'center', paddingHorizontal: 32, }, @@ -87,4 +114,9 @@ export const styles = Steezy.create({ indent: { paddingHorizontal: 16, }, + animatedBatteryContainer: { + paddingHorizontal: 30, + paddingTop: 6, + paddingBottom: 8, + }, }); diff --git a/packages/shared/components/RefillBattery/RefillBatteryIAP.tsx b/packages/shared/components/RefillBattery/RefillBatteryIAP.tsx index 58f0b81ad..27fee6f61 100644 --- a/packages/shared/components/RefillBattery/RefillBatteryIAP.tsx +++ b/packages/shared/components/RefillBattery/RefillBatteryIAP.tsx @@ -1,33 +1,67 @@ -import { Button, List, Spacer, Steezy, Text, Toast, View } from '@tonkeeper/uikit'; +import { + Button, + Icon, + IconNames, + List, + Spacer, + Steezy, + Text, + Toast, + View, +} from '@tonkeeper/uikit'; import { memo, useCallback, useEffect, useState } from 'react'; import { t } from '@tonkeeper/shared/i18n'; import { tk } from '@tonkeeper/mobile/src/wallet'; import { Platform } from 'react-native'; -import { goBack } from '@tonkeeper/mobile/src/navigation/imperative'; import { useIAP } from 'react-native-iap'; import { SkeletonLine } from '@tonkeeper/mobile/src/uikit/Skeleton/SkeletonLine'; +import { useTokenPrice } from '@tonkeeper/mobile/src/hooks/useTokenPrice'; +import { CryptoCurrencies } from '@tonkeeper/mobile/src/shared/constants'; +import BigNumber from 'bignumber.js'; +import { config } from '@tonkeeper/mobile/src/config'; +import { useExternalState } from '../../hooks/useExternalState'; -const packages = [ +export interface InAppPackage { + icon: IconNames; + key: string; + // TODO: move to backend + userProceed: number; + packageId: string; +} + +const packages: InAppPackage[] = [ { + icon: 'ic-battery-100-44', key: 'large', - transactions: 700, - packageId: 'Battery700', + userProceed: 7.5, + packageId: 'LargePack', }, { + icon: 'ic-battery-50-44', key: 'medium', - transactions: 400, - packageId: 'Battery400', + userProceed: 5, + packageId: 'MediumPack', }, { + icon: 'ic-battery-25-44', key: 'small', - transactions: 100, - packageId: 'Battery100', + userProceed: 2.5, + packageId: 'SmallPack', }, ]; export const RefillBatteryIAP = memo(() => { const [purchaseInProgress, setPurchaseInProgress] = useState<boolean>(false); - const { products, getProducts, requestPurchase, connected } = useIAP(); + const { products, getProducts, requestPurchase, finishTransaction } = useIAP(); + const tonPriceInUsd = useTokenPrice(CryptoCurrencies.Ton).usd; + const batteryBalance = useExternalState( + tk.wallet.battery.state, + (state) => state.balance, + ); + const reservedBalance = useExternalState( + tk.wallet.battery.state, + (state) => state.reservedBalance, + ); useEffect(() => { getProducts({ @@ -37,24 +71,51 @@ export const RefillBatteryIAP = memo(() => { const makePurchase = useCallback( (packageId: string) => async () => { - setPurchaseInProgress(true); - let product = await requestPurchase({ sku: packageId }); - if (!product) return; + try { + setPurchaseInProgress(true); + let requestedPurchase = await requestPurchase({ sku: packageId }); - if (Array.isArray(product)) product = product[0]; + if (!requestedPurchase) { + return; + } + + const purchasesArray = Array.isArray(requestedPurchase) + ? requestedPurchase + : [requestedPurchase]; + + if (Platform.OS === 'ios') { + const processedTransactions = await tk.wallet.battery.makeIosPurchase( + purchasesArray.map((purchase) => ({ id: purchase.transactionId })), + ); - if (Platform.OS === 'ios') { - if (!product.transactionId) return; - await tk.wallet.battery.makeIosPurchase([{ id: product.transactionId }]); - } else { - if (!product.purchaseToken) return; - await tk.wallet.battery.makeAndroidPurchase([ - { token: product.purchaseToken, product_id: product.productId }, - ]); + for (let purchase of purchasesArray) { + if ( + !processedTransactions || + !processedTransactions.find( + (processedTransaction) => + purchase.transactionId === processedTransaction.transaction_id, + ) + ) { + continue; + } + await finishTransaction({ purchase, isConsumable: true }); + } + } else if (Platform.OS === 'android') { + await tk.wallet.battery.makeAndroidPurchase( + purchasesArray.map((purchase) => ({ + token: purchase.purchaseToken, + product_id: purchase.productId, + })), + ); + } + + Toast.success(t('battery.refilled')); + setPurchaseInProgress(false); + } catch (e) { + console.log(e); + setPurchaseInProgress(false); + Toast.fail(e.message); } - goBack(); - Toast.success(t('battery.refilled')); - setPurchaseInProgress(false); }, [], ); @@ -68,20 +129,35 @@ export const RefillBatteryIAP = memo(() => { ); return ( <List.Item + leftContent={ + <Icon + style={styles.listItemIcon.static} + imageStyle={styles.listItemIcon.static} + colorless + name={item.icon} + /> + } title={ <View> - <View style={styles.titleContainer}> - <Text type="label1"> - {t(`battery.packages.title`, { - price: product?.localizedPrice ?? '', - cnt: item.transactions, - })} + <View style={styles.priceContainer}> + <Text type="label1" numberOfLines={1} ellipsizeMode="middle"> + {t(`battery.packages.title.${item.key}`)} </Text> - <Spacer x={4} /> - {!product?.localizedPrice && <SkeletonLine height={24} width={40} />} </View> <Text type="body2" color="textSecondary"> - {t(`battery.packages.subtitle.${item.key}`)} + {t(`battery.packages.subtitle`, { + count: new BigNumber(item.userProceed) + .div(tonPriceInUsd) + .minus( + reservedBalance === '0' && + (!batteryBalance || batteryBalance === '0') + ? config.get('batteryReservedAmount') + : 0, + ) + .div(config.get('batteryMeanFees')) + .decimalPlaces(0) + .toNumber(), + })} </Text> </View> } @@ -92,7 +168,7 @@ export const RefillBatteryIAP = memo(() => { disabled={purchaseInProgress || !product} onPress={makePurchase(item.packageId)} size="small" - title={t('battery.packages.buy')} + title={product?.localizedPrice ?? 'Loading'} /> } /> @@ -105,10 +181,15 @@ export const RefillBatteryIAP = memo(() => { const styles = Steezy.create({ valueContainerStyle: { + flex: 1, justifyContent: 'center', }, - titleContainer: { + priceContainer: { flexDirection: 'row', alignItems: 'center', }, + listItemIcon: { + width: 26, + height: 44, + }, }); diff --git a/packages/shared/components/RefillBattery/RefillBatterySettingsWidget.tsx b/packages/shared/components/RefillBattery/RefillBatterySettingsWidget.tsx new file mode 100644 index 000000000..f2ea3dd45 --- /dev/null +++ b/packages/shared/components/RefillBattery/RefillBatterySettingsWidget.tsx @@ -0,0 +1,39 @@ +import React, { memo, useMemo } from 'react'; +import { List } from '@tonkeeper/uikit'; +import { t } from '@tonkeeper/shared/i18n'; +import { useExternalState } from '../../hooks/useExternalState'; +import { tk } from '@tonkeeper/mobile/src/wallet'; + +export interface RefillBatterySettingsWidgetProps { + onPress: () => void; +} + +export const RefillBatterySettingsWidget = memo<RefillBatterySettingsWidgetProps>( + (props) => { + const enabledTransactions = useExternalState( + tk.wallet.battery.state, + (state) => state.supportedTransactions, + ); + + const enabledTransactionsNames = useMemo(() => { + return Object.entries(enabledTransactions) + .filter(([, enabled]) => enabled) + .map(([type]) => t(`battery.transactions.types.${type}`)) + .join(', '); + }, [enabledTransactions]); + + return ( + <List> + <List.Item + onPress={props.onPress} + chevron + title={t('battery.transactions.settings')} + subtitle={t('battery.transactions.will_be_paid', { + enabledTransactions: enabledTransactionsNames, + })} + subtitleNumberOfLines={2} + /> + </List> + ); + }, +); diff --git a/packages/shared/components/RefillBattery/RestorePurchases.tsx b/packages/shared/components/RefillBattery/RestorePurchases.tsx new file mode 100644 index 000000000..ce6339bf7 --- /dev/null +++ b/packages/shared/components/RefillBattery/RestorePurchases.tsx @@ -0,0 +1,68 @@ +import React, { memo, useCallback } from 'react'; +import { Steezy, Text, Toast, TouchableOpacity } from '@tonkeeper/uikit'; +import { t } from '../../i18n'; +import { getPendingPurchasesIOS, finishTransaction } from 'react-native-iap'; +import { Platform } from 'react-native'; +import { tk } from '@tonkeeper/mobile/src/wallet'; + +export const RestorePurchases = memo(() => { + const handleRestorePurchases = useCallback(async () => { + try { + const purchases = await getPendingPurchasesIOS(); + + if (!purchases.length) { + return Toast.fail('Nothing to restore'); + } + + if (Platform.OS === 'ios') { + const processedTransactions = await tk.wallet.battery.makeIosPurchase( + purchases.map((purchase) => ({ id: purchase.transactionId })), + ); + + for (let purchase of purchases) { + if ( + !processedTransactions || + !processedTransactions.find( + (processedTransaction) => + purchase.transactionId === processedTransaction.transaction_id, + ) + ) { + continue; + } + await finishTransaction({ purchase, isConsumable: true }); + } + } else if (Platform.OS === 'android') { + await tk.wallet.battery.makeAndroidPurchase( + purchases.map((purchase) => ({ + token: purchase.purchaseToken, + product_id: purchase.productId, + })), + ); + } + Toast.success(t('battery.refilled')); + } catch (e) { + Toast.fail(e.message); + } + }, []); + + return ( + <Text style={styles.text.static} type="body2" textAlign="center" color="textTertiary"> + {t('battery.packages.disclaimer')}{' '} + <Text + onPress={handleRestorePurchases} + type="body2" + textAlign="center" + color="textSecondary" + > + {t('battery.packages.restore')} + </Text> + . + </Text> + ); +}); + +const styles = Steezy.create({ + text: { + paddingHorizontal: 16, + }, +}); diff --git a/packages/shared/hooks/index.ts b/packages/shared/hooks/index.ts index 3fb30d02a..faf7be6ea 100644 --- a/packages/shared/hooks/index.ts +++ b/packages/shared/hooks/index.ts @@ -11,3 +11,4 @@ export * from './useBalancesState'; export * from './useBiometrySettings'; export * from './useLockSettings'; export * from './useWalletSetup'; +export * from './useIsEnabledForBattery'; diff --git a/packages/shared/hooks/useIsEnabledForBattery.ts b/packages/shared/hooks/useIsEnabledForBattery.ts new file mode 100644 index 000000000..9501343a1 --- /dev/null +++ b/packages/shared/hooks/useIsEnabledForBattery.ts @@ -0,0 +1,12 @@ +import { BatterySupportedTransaction } from '@tonkeeper/mobile/src/wallet/managers/BatteryManager'; +import { useExternalState } from './useExternalState'; +import { tk } from '@tonkeeper/mobile/src/wallet'; + +export function useIsEnabledForBattery(transactionType: BatterySupportedTransaction) { + const enabledTransactions = useExternalState( + tk.wallet.battery.state, + (state) => state.supportedTransactions, + ); + + return enabledTransactions[transactionType]; +} diff --git a/packages/shared/i18n/locales/tonkeeper/en.json b/packages/shared/i18n/locales/tonkeeper/en.json index 9e2e1c139..0de2f1f34 100644 --- a/packages/shared/i18n/locales/tonkeeper/en.json +++ b/packages/shared/i18n/locales/tonkeeper/en.json @@ -124,19 +124,45 @@ "auth_failed": "Authentication failed", "balances_setup_wallet": "Set up wallet", "battery": { + "send_widget": { + "battery": "Battery" + }, + "transactions": { + "types": { + "nft": "NFT transfers", + "swap": "swaps via Tonkeeper", + "jetton": "token transfers", + "ton": "TON transfers" + }, + "type": { + "swap": "swap", + "transfer": "transfer" + }, + "title": "Transactions", + "settings": "Battery Settings", + "will_be_paid": "Will be paid: %{enabledTransactions}", + "description": "Selected transactions will be paid by Tonkeeper Battery.", + "supported": "Supported transactions", + "charges_per_action": { + "few": "≈ %{count} charges per %{transactionName}", + "other": "≈ %{count} charges per %{transactionName}", + "one": "≈ %{count} charge per %{transactionName}", + "many": "≈ %{count} charges per %{transactionName}" + } + }, + "refilled": "Battery refilled", "screen_title": "Battery", - "settings": "Battery", + "settings": "Battery %{betaLabel}", "ok": "OK", - "title": { - "empty": "Recharge your battery for gas fees", - "almost_empty": "Battery for gas fees is almost empty", - "medium": "Battery for gas fees is half full", - "full": "Battery for gas fees is full" - }, + "title": "Tonkeeper Battery", "description": { - "empty": "Send tokens and NFTs, pay for staking actions with empty main balance.", - "other": "You have enough charge battery for more than %{cnt} transactions.", - "less_10": "You have enough charge battery for less than 10 transactions." + "empty": "Swap via Tonkeeper, send tokens and NFTs.", + "other": { + "few": "%{count} charges", + "many": "%{count} charges", + "one": "%{count} charge", + "other": "%{count} charges" + } }, "promocode": { "button": "Recharge by Promo Code", @@ -146,13 +172,19 @@ "success": "Your battery is charged" }, "packages": { - "title": "{{cnt}} transactions for {{price}}", + "title": { + "large": "Large", + "medium": "Medium", + "small": "Small" + }, "subtitle": { - "large": "Large pack", - "medium": "Medium pack", - "small": "Small pack" + "few": "%{count} charges", + "other": "%{count} charges", + "one": "%{count} charge", + "many": "%{count} charges" }, - "disclaimer": "This is approximate transactions number. Some your transactions may cost more.", + "disclaimer": "One charge covers the average transaction fee. Some transactions may cost more.", + "restore": "Restore Purchases", "buy": "Buy", "ok": "OK", "refilled": "Your battery is charged" @@ -370,6 +402,7 @@ "jetton_name": "%{name} Token", "jetton_open_explorer": "View details", "jetton_price": "Price:", + "jetton_locked_till": "Locked until %{date}", "jettons_list_title": "Tokens", "jettons_manage_tokens": "Manage tokens", "jettons_show_jettons": "Show tokens in wallet", @@ -920,6 +953,7 @@ "transaction_view_in_explorer": "View in explorer", "transaction_wallet_initialized_date": "%{date}", "transaction_your_bid": "Your bid", + "transfer_pending_by_battery_error": "Another transaction is handling by battery", "transfer_deeplink_unknown_jetton_error" : "Unknown token", "transfer_deeplink_address_error": "Incorrect recipient address", "transfer_deeplink_unknown_token": "Unknown token", diff --git a/packages/shared/i18n/locales/tonkeeper/id.json b/packages/shared/i18n/locales/tonkeeper/id.json index ebb3a35c1..19848e43e 100644 --- a/packages/shared/i18n/locales/tonkeeper/id.json +++ b/packages/shared/i18n/locales/tonkeeper/id.json @@ -131,41 +131,6 @@ }, "auth_failed": "Otentikasi gagal", "balances_setup_wallet": "Mengatur dompet", - "battery": { - "description": { - "empty": "Kirim token dan NFT, bayar untuk aksi staking dengan saldo utama kosong.", - "other": "Anda memiliki cukup daya baterai untuk lebih dari %{cnt} transaksi.", - "less_10": "Anda memiliki cukup daya baterai untuk kurang dari 10 transaksi." - }, - "ok": "BAIK", - "packages": { - "buy": "Beli", - "disclaimer": "Ini adalah perkiraan jumlah transaksi. Beberapa transaksi Anda mungkin berbiaya lebih banyak.", - "ok": "OKE", - "refilled": "Baterai Anda sudah terisi", - "subtitle": { - "large": "Paket besar", - "medium": "Paket Medium", - "small": "Paket kecil" - }, - "title": "{{cnt}} transaksi seharga {{price}}" - }, - "promocode": { - "apply": "Terapkan", - "button": "Isi ulang dengan Kode Promo", - "placeholder": "Kode", - "success": "Baterai Anda sudah terisi", - "title": "Kode Promo" - }, - "screen_title": "Baterai", - "settings": "Baterai", - "title": { - "almost_empty": "Baterai untuk biaya gas hampir habis", - "empty": "Isi ulang baterai Anda untuk biaya gas", - "full": "Baterai untuk biaya gas sudah penuh", - "medium": "Baterai untuk biaya gas setengah penuh" - } - }, "browser": { "about_dapps_caption": "Jelajahi aplikasi dan layanan di mana Anda dapat menggunakan Tonkeeper untuk masuk dan pembayaran.", "about_dapps_learn_more": "Pelajari lebih lanjut", @@ -1193,4 +1158,4 @@ "with_passcode": "Masukkan Kode Sandi", "with_biometry": "Lanjutkan dengan %{type}" } -} \ No newline at end of file +} diff --git a/packages/shared/i18n/locales/tonkeeper/ru-RU.json b/packages/shared/i18n/locales/tonkeeper/ru-RU.json index b4b3389f5..961df975d 100644 --- a/packages/shared/i18n/locales/tonkeeper/ru-RU.json +++ b/packages/shared/i18n/locales/tonkeeper/ru-RU.json @@ -348,6 +348,7 @@ "jetton_name" : "%{name} Токен", "jetton_open_explorer" : "Подробнее", "jetton_price" : "Цена:", + "jetton_locked_till": "Заблокированы до %{date}", "jettons_list_title" : "Токены", "jettons_manage_tokens" : "Настроить токены", "jetton_token" : "Токен", @@ -1057,19 +1058,45 @@ "region_nokyc": "Нейтральные воды", "nokyc": "без KYC", "battery": { + "send_widget": { + "battery": "Батарейка" + }, + "transactions": { + "types": { + "nft": "отправка NFT", + "swap": "обмен через Tonkeeper", + "jetton": "отправка токенов", + "ton": "отправка TON" + }, + "type": { + "swap": "обмен", + "transfer": "отправку" + }, + "title": "Транзакции", + "settings": "Настройки батарейки", + "will_be_paid": "Будут оплачены: %{enabledTransactions}", + "description": "Выбранные транзакции будут оплачены батарейкой Tonkeeper.", + "supported": "Поддерживаемые транзакции", + "charges_per_action": { + "few": "≈ %{count} заряда за %{transactionName}", + "other": "≈ %{count} зарядов за %{transactionName}", + "one": "≈ %{count} заряд за %{transactionName}", + "many": "≈ %{count} зарядов за %{transactionName}" + } + }, + "refilled": "Батарейка заряжена", "screen_title": "Батарейка", - "settings": "Батарейка", + "settings": "Батарейка %{betaLabel}", "ok": "OK", - "title": { - "empty": "Перезарядите свою батарейку для оплаты комиссий в сети", - "almost_empty": "Батарейка для оплаты комиссий почти разряжена", - "medium": "Батарейка для оплаты комиссий заряжена наполовину", - "full": "Батарейка для оплаты комиссий полностью заряжена" - }, + "title": "Батарейка Tonkeeper", "description": { - "empty": "Отправляйте токены и NFT, взаимодействуйте со стейкингом при нулевом балансе кошелька.", - "other": "У вас достаточно заряда для более чем {{cnt}} транзакций.", - "less_10": "У вас достаточно заряда для менее чем 10 транзакций." + "empty": "Обменивайте через Tonkeeper, отправляйте токены.", + "other": { + "few": "%{count} заряда", + "many": "%{count} зарядов", + "one": "%{count} заряд", + "other": "%{count} зарядов" + } }, "promocode": { "button": "Зарядить с помощью промокода", @@ -1079,14 +1106,19 @@ "success": "Ваша батарейка заряжена" }, "packages": { - "title": "{{cnt}} транзакций за {{price}}", + "title": { + "large": "Большой", + "medium": "Средний", + "small": "Малый" + }, "subtitle": { - "large": "Большой пакет", - "medium": "Средний пакет", - "small": "Малый пакет" + "few": "%{count} заряда", + "other": "%{count} зарядов", + "one": "%{count} заряд", + "many": "%{count} зарядов" }, - "disclaimer": "Указано примерное количество транзакций. Некоторые транзакции могут стоить дороже.", - "buy": "Купить", + "disclaimer": "Один заряд покрывает среднюю комиссию за транзакцию. Некоторые транзакции могут стоить дороже.", + "restore": "Восстановить покупки", "ok": "OK", "refilled": "Ваша батарейка заряжена" } diff --git a/packages/shared/i18n/locales/tonkeeper/tr-TR.json b/packages/shared/i18n/locales/tonkeeper/tr-TR.json index 401fb8610..eed35b047 100644 --- a/packages/shared/i18n/locales/tonkeeper/tr-TR.json +++ b/packages/shared/i18n/locales/tonkeeper/tr-TR.json @@ -124,40 +124,6 @@ }, "auth_failed" : "Kimlik doğrulama başarısız oldu", "balances_setup_wallet" : "Cüzdanı ayarlayın", - "battery" : { - "description" : { - "empty" : "Ana bakiyeniz boş olsa bile token ve NFT gönderin, staking işlemleri gerçekleştirin.", - "other" : "%{cnt} işlem için bataryanızda yeterli şarj seviyesi mevcut." - }, - "ok" : "TAMAM", - "packages" : { - "buy" : "Satın al", - "disclaimer" : "Bu yaklaşık işlem sayısıdır. Bazı işlemlerinizin maliyeti daha yüksek olabilir.", - "ok" : "TAMAM", - "refilled" : "Bataryanız şarj oldu", - "subtitle" : { - "large" : "Büyük paket", - "medium" : "Orta paket", - "small" : "Küçük paket" - }, - "title" : "{{price}} karşılığı {{cnt}} işlem" - }, - "promocode" : { - "apply" : "Uygula", - "button" : "Promosyon Kodu ile yükleme yapın", - "placeholder" : "Kod", - "success" : "Bataryanız şarj oldu", - "title" : "Promosyon Kodu" - }, - "screen_title" : "Batarya", - "settings" : "Batarya", - "title" : { - "almost_empty" : "Gas ücretleri için batarya neredeyse boş", - "empty" : "Gaz ücretleri için bataryanızı şarj edin", - "full" : "Gas ücretleri için batarya dolu", - "medium" : "Gas ücretleri için bataryanın yarısı dolu" - } - }, "browser" : { "about_dapps_caption" : "Oturum açma ve ödemeler için Tonkeeper'ı kullanabileceğiniz uygulamaları ve hizmetleri keşfedin.", "about_dapps_learn_more" : "Daha fazla bilgi edinin", diff --git a/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json b/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json index b5aea1cd4..743b07d27 100644 --- a/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json +++ b/packages/shared/i18n/locales/tonkeeper/zh-Hans-CN.json @@ -90,8 +90,7 @@ "balances_setup_wallet" : "注册钱包", "battery" : { "description" : { - "empty" : "发送代币和 NFT,用空的主账户支付质押操作费用。", - "other" : "您已有足够的电量以进行 %{cnt} 次交易" + "empty" : "发送代币和 NFT,用空的主账户支付质押操作费用。" }, "ok" : "确定", "packages" : { @@ -103,8 +102,7 @@ "large" : "大杯", "medium" : "中杯", "small" : "小杯" - }, - "title" : "{{cnt}} 笔交易,总共 {{price}}" + } }, "promocode" : { "apply" : "应用", @@ -114,13 +112,7 @@ "title" : "优惠码" }, "screen_title" : "电池", - "settings" : "电池", - "title" : { - "almost_empty" : "电池即将耗尽", - "empty" : "为你的电池充电", - "full" : "电池已充满", - "medium" : "电池已经充满一半" - } + "settings" : "电池" }, "browser" : { "about_dapps_caption" : "探索可以使用Tonkeeper进行登录和支付的应用和服务。", diff --git a/packages/shared/modals/RefillBatteryModal.tsx b/packages/shared/modals/RefillBatteryModal.tsx index 48c5f5d16..a18cdc5c3 100644 --- a/packages/shared/modals/RefillBatteryModal.tsx +++ b/packages/shared/modals/RefillBatteryModal.tsx @@ -1,24 +1,84 @@ -import { memo } from 'react'; -import { Modal } from '@tonkeeper/uikit'; -import { navigation, SheetActions } from '@tonkeeper/router'; +import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { RefillBattery } from '../components/RefillBattery/RefillBattery'; +import { NavBar } from '@tonkeeper/mobile/src/uikit'; +import { + StepView, + StepViewItem, + StepViewRef, +} from '@tonkeeper/mobile/src/shared/components'; +import { BatterySupportedTransactions } from '../components/BatterySupportedTransactions'; +import { useBatteryUIStore } from '@tonkeeper/mobile/src/store/zustand/batteryUI'; +import { useBatteryState } from '../query/hooks/useBatteryState'; +import { BatteryState } from '../utils/battery'; +import { t } from '@tonkeeper/shared/i18n'; + +export enum RefillBatterySteps { + REFILL_BATTERY, + TRANSACTIONS, +} export const RefillBatteryModal = memo(() => { + const stepViewRef = useRef<StepViewRef>(null); + const setIsViewed = useBatteryUIStore( + (state) => state.actions.setIsViewedBatteryScreen, + ); + const batteryState = useBatteryState(); + const [currentStep, setCurrentStep] = useState<{ + id: RefillBatterySteps; + index: number; + }>({ + id: RefillBatterySteps.REFILL_BATTERY, + index: 0, + }); + + useEffect(() => { + setIsViewed(true); + }, [setIsViewed]); + + const handleNavigateToTransactions = useCallback(() => { + stepViewRef.current?.go(RefillBatterySteps.TRANSACTIONS); + }, []); + + const navbarTitle = useMemo(() => { + switch (currentStep.id) { + case RefillBatterySteps.TRANSACTIONS: + return batteryState === BatteryState.Empty + ? t('battery.transactions.title') + : null; + default: + return null; + } + }, [currentStep, batteryState]); + + const hideBackButton = currentStep.index === 0; + + const handleChangeStep = useCallback((id: string | number, index: number) => { + setCurrentStep({ id: id as RefillBatterySteps, index }); + }, []); + + const handleBack = useCallback(() => stepViewRef.current?.goBack(), []); + + // TODO: rewrite to react-native-pager-view return ( - <Modal> - <Modal.Header /> - <Modal.Content> - <RefillBattery /> - </Modal.Content> - <Modal.Footer /> - </Modal> + <> + <NavBar + isModal + isClosedButton + isForceBackIcon + hideBackButton={hideBackButton} + hideTitle={currentStep.index === 0} + onBackPress={handleBack} + > + {navbarTitle} + </NavBar> + <StepView onChangeStep={handleChangeStep} ref={stepViewRef} useBackHandler> + <StepViewItem id={RefillBatterySteps.REFILL_BATTERY}> + <RefillBattery navigateToTransactions={handleNavigateToTransactions} /> + </StepViewItem> + <StepViewItem id={RefillBatterySteps.TRANSACTIONS}> + <BatterySupportedTransactions editable={batteryState !== BatteryState.Empty} /> + </StepViewItem> + </StepView> + </> ); }); - -export function openRefillBatteryModal() { - navigation.push('SheetsProvider', { - $$action: SheetActions.ADD, - component: RefillBatteryModal, - path: '/refill-battery', - }); -} diff --git a/packages/shared/utils/battery.ts b/packages/shared/utils/battery.ts index 44b3d5919..63641c7a5 100644 --- a/packages/shared/utils/battery.ts +++ b/packages/shared/utils/battery.ts @@ -9,13 +9,11 @@ export enum BatteryState { } const valuesForBatteryState = { - [BatteryState.Medium]: '2', - [BatteryState.AlmostEmpty]: '1', + [BatteryState.Medium]: '1', + [BatteryState.AlmostEmpty]: '0.4', [BatteryState.Empty]: '0.03', }; -export const MEAN_FEES = config.get('batteryMeanFees'); - export function getBatteryState(batteryBalance: string) { const balance = new BigNumber(batteryBalance); const medium = new BigNumber(valuesForBatteryState[BatteryState.Medium]); @@ -40,12 +38,6 @@ export function getBatteryState(batteryBalance: string) { export function calculateAvailableNumOfTransactions(batteryBalance: string) { const balance = new BigNumber(batteryBalance); - // return balance divided by mean fees rounded down to nearest 10 - return balance - .div(MEAN_FEES) - .decimalPlaces(0, BigNumber.ROUND_DOWN) - .div(10) - .decimalPlaces(0, BigNumber.ROUND_DOWN) - .times(10) - .toNumber(); + // return balance divided by mean fees rounded down + return balance.div(config.get('batteryMeanFees')).decimalPlaces(0, BigNumber.ROUND_UP); } diff --git a/packages/shared/utils/blockchain.ts b/packages/shared/utils/blockchain.ts index 71be2655e..c65fb7cc2 100644 --- a/packages/shared/utils/blockchain.ts +++ b/packages/shared/utils/blockchain.ts @@ -2,9 +2,13 @@ import { config } from '@tonkeeper/mobile/src/config'; import { tk } from '@tonkeeper/mobile/src/wallet'; import { ContentType } from '@tonkeeper/core/src/TonAPI'; -export async function sendBocWithBattery(boc) { +export async function sendBoc(boc, attemptWithRelayer = true) { try { - if (config.get('disable_battery') || config.get('disable_battery_send')) { + if ( + !attemptWithRelayer || + config.get('disable_battery') || + config.get('disable_battery_send') + ) { throw new Error('Battery disabled'); } if ( @@ -24,9 +28,13 @@ export async function sendBocWithBattery(boc) { } } -export async function emulateWithBattery(boc, params?) { +export async function emulateBoc(boc, params?, attemptWithRelayer = false) { try { - if (config.get('disable_battery') || config.get('disable_battery_send')) { + if ( + !attemptWithRelayer || + config.get('disable_battery') || + config.get('disable_battery_send') + ) { throw new Error('Battery disabled'); } if ( diff --git a/packages/uikit/assets/icons/png/ic-almost-empty-battery-128@4x.png b/packages/uikit/assets/icons/png/ic-almost-empty-battery-128@4x.png index 51f717e489293e46759c08b2a4e96b8c751553e6..f44f5a2b19739b8424c629768a4d6ebaa0e4c17b 100644 GIT binary patch literal 11692 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 literal 11881 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 I_5QQ}0<EW4^#A|> diff --git a/packages/uikit/assets/icons/png/ic-almost-empty-battery-34@4x.png b/packages/uikit/assets/icons/png/ic-almost-empty-battery-34@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..f08f576e6ca5a7393717885f6005b5faa9c5a263 GIT binary patch literal 4892 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 F{{TVv{2u@S literal 0 HcmV?d00001 diff --git a/packages/uikit/assets/icons/png/ic-battery-100-44@4x.png b/packages/uikit/assets/icons/png/ic-battery-100-44@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..f131e0f4e0f31dd6346e3a6cafa75329aeae6a96 GIT binary patch literal 17350 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 literal 0 HcmV?d00001 diff --git a/packages/uikit/assets/icons/png/ic-battery-25-44@4x.png b/packages/uikit/assets/icons/png/ic-battery-25-44@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..a5b9e110b5647fae550ae549de44bcff6eb33312 GIT binary patch literal 3961 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 W00w*?dw=~)1GLn2)v8o1gZ>9v>6sV+ literal 0 HcmV?d00001 diff --git a/packages/uikit/assets/icons/png/ic-battery-50-44@4x.png b/packages/uikit/assets/icons/png/ic-battery-50-44@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..794c11cf4c081b4a4748552c37333c369692664e GIT binary patch literal 8456 zcmd^l<y(|b)c-EAAh{r&OG$SrxpWG$q=a;{pn!Cji=<MDgmiazDxDHi3rI^!EzPsv z>-YNyp4ZQHznHksoH=vuxj$#-GZXzvON9`R1`hxL5UQyv!qDj;007j*1)*~R@d~Br z1Z1V5q6m2W@A=+Qlmq}k0BVYIdOjJ4EB^H<wjKN@$0*;OjC~?}Y%(1|Y(0)+<B}Fb zhAHbv^5-=?A2s+!YO$8$$DeP+3Qs(@KGr&fhj_AKPp>GYez&#FZ7a?!`TDWY0VzK- z3TWY-OLrbe1qSU98=21g`DV4uN%EJk+7k>U{_wy=9^bal@385;e(Bt08Cyf=#`Du? zP=|~TJ68_tWr&SeidAU87H(^#&q#>d>lpFqnHc(!$HU{4`=<oF<>vq*?>f7&(p5*} z39>ahqYMJ~s}LHp1yFbw)|`}BYr)m#pC2*)AjjfTVw%JV)AwU{(WDEUbANaezbZ<Q za8iY{Q>Eh^wcpe7vrZ3yi-6Roqd`u{7Tgm#pcaq)K-8&F#oeFrt(XTa=<ot+T@zDC zwK7ZixPNk(o##c(=MBioN40++yo1c`lY71cslS&T9>!f*{$dB3a=_?=vstl7g0icY zt{G8$uc!6*{>{{uUg1>eGj`b_HwBWNv7~CJt@rSmzEy0Y3?D3d>QWdqD)6|ZLySeO zFVpzyNMzV;tP2LFWyNU1@t<WrFjSh{4xXhjyyTsx?$`CdMNPX=W_5x3{kf$n7$fd1 z6>nxt(s)`LjrMA0NHgW6Y&=>tgU+1A!=%clt^W;doO*2x?PR_iwsxMcB;=M(H_idR zOCH-2I{iCGD<|ACZOuuq5>+mFy)4Z4xUtyaEW%180wJnhaOti*kAwYM|E<*h34420 z(6{$pLb*wk)^QqLSjjoS3HUAHKEJ@%zF<Jm=ZdeMPPlA3uNYe*oUS+a#Tj2scfERo z^~t}8p`CX7rB-%^Yv%$L*qA3M`qTYPdyT>7Mhb%pjP<j2kjv%W2A6caQz39q#e^@7 z)Vd0j)Nity>x(CrKitHGPk--|9yg(?ta4}TVfgnKozokt=^-1++0}=q>Ir$qj?kO8 z7qjNnay7c#(($!9rtdChvuSnqEcI9jRT)2CNWac0DLa2Y{3Gnb@|lNi!GKPj2n6S9 zca%S&aPS&!#KI@a@r>)OsT2m~hN%!b2zdl!^;%-v9b7JnxOe0~%kqE=Dq{X-b>L&P zA}*B&$L-OtFH;zlM5jUy`Wk0Cc1}9WX|)~xwuZAzRGdFAjRx5;hNh(ZMM#r~UQz{5 zi$L0Jcg7{<3E1`g=u(t>O8kbr`3VPI<uxl)y@$EgRHOD<)zFqSOokjN$DoH1<zyN6 zV0Jzoa`4Q+Gw*lYOb9v;_#(<}sI8mHoumg+8A}%}>qPq-L<0s$>SkJ>-&ymM2qetq zp?%;akdMjCobkfLE-7NYSv{d(F{1m4u>H6T=_jU+jp7B_0I_c)GxOe!6o27mho20R z<ja(1ut0?1C5S~{%`|Qw2$v>8l?3J%3B=m#r%0ADjV6PV5wejAXv^W|(i*f@SW+^X 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 literal 0 HcmV?d00001 diff --git a/packages/uikit/assets/icons/png/ic-battery-almost-empty-24@4x.png b/packages/uikit/assets/icons/png/ic-battery-almost-empty-24@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..3d3b26747e63845bf984299e09b9a8a1989e9096 GIT binary patch literal 3412 zcmdT{hcn!b^Zwj9oIB<88cq+9AWm<mhHzZ8;G!ggc=b0#=LjN7qNNaB2vMU&I9!xy z(QEXU=$xJi?(3cT{S&|0+1=Tho!Q-Ic6Oe9jy1YP2jhYP06?d&r)_fS?JqSQN_A;1 zk}$U}9n}MaTiO8mKgntOkaC$xqpz)D=AW^h>F#AV#@R_GS2so`Lhe+4g)JE%+>trO zB5}DX78v5^;xX)5PTBVFn<1R8&Kri#O0U*^l&1P?j9jOSw=GM|2Pc(cyW*^6IH0*0 zZZPx#hXExD#>&K$?6wnf3M%{2r|9SR1Jm#HICOp|q`$X%fJ~ST+Ue*2MngwOmpe=S z!dz@o79jz`!j{|s`~vdoF363oqn16>rmF1Ir=jXi?|%mCD7uSEOXJ%``syho_<*<X z9@L!+?5C_pJ$)%BD@&J97?zei0+z52J`$KWEz$U{+#zu6uV%3|Rd+wg;!3Xo|1|wu zOOCR?=`p9*V$wKSWVG%y?-y71(l>>9*#(Vf^a?17p>=95<e+?%h;9N(S8oQZRI+71 z5gtPRP9SV0Ki0zP_|6^y*;>ckor(K<9mnq6<4#{@;=9B^J^4q@kq}Sd>XicVxfHM8 zGRo-_p$fNuen_a=CVFW`P)i4XrN*cA9Pn~<?2m{z2cjJv&#gW|j|HzSha$Cd6V+Gk z-r1?4dXC;}_aEXdwRhUrsQG|aE!#C$2dwe!m3RPXQE`&JV-as|)7|x)zv$yXKSr4k z)!Qd!p<zffsqMZ#$;0?NA-bhBhy*znUWOx#(|1WG;XAP^GOv%|UozBiLV1C4O=9QQ zuP2CptGamET?gDll)bx^{WJ1AYo1|O8pMF#1eU{r2I0l5%H?r~AP!)VVm~C@=gx{M zOo=iWet&0ck`O8hbX!Dm!m(BX=kZ^=77`#HKqUu}Gp+H}2oI>9vi%_GYuAChyDPo3 z$)p$@8yaxlTDf!r*>?mM4Kp}8+adckF{eNJ$B(XjFhNg&%`9wvttIk8AtYQ^v3aUF zq&-`8LAlXd?o2*2-h~GHsop~^xEX41WW9soIo9g-y@He5F_hKnx$YloK08MGLCb!I z{6%!`ZGANBTkND)Ol_w9W)7UxnRIHo%jl28e+B|@MGgG_rY3sCZZPKIkRfEh2$sF` z{9>g{x}s4O<DZoicr1&hz@Y&o1NJXhM>dQU40Z%(JR6-Kq}Ny(e=(j=l5(0P@wN41 zfvjc#rRbZNx{$T#E^#JTLFq74-1N0h6Q3vdDvRo#q|d9n&=i+5C*gopq3)aLMe}Lg zggmt{jK%(9$&vl){5;HF7hPo=d*)x{MOw7MU^-J2Ua_Z6jI%wX5((oLP)Czw`tI8m zZ)D=ew`GK(2>SwS>}=I5qT@HAh$lL8lg+;8Eh&weVG<v01Is1%d17=5QQ)(X&hbaG zmBSN15*(&lHzFE=8JF@bS3+y}<`Y!QekhmPD(mi*H5Da?oh{KZY(1D5)qsNZXDR+& zXtbeLnx;KYGBX=xy$>T(9GGsqCQUA$*45QbiUnjjr@oOlG!dTVhK_P2^E_`C$s8H4 zE0i3{v5RIaW|g82tB7oq|FRI)T5gl@RLz?i-xMu0Lf!UsYGS)^72imAq7&}-b2#gt z^2!p&wlJ|XC0fZ1y_teWbxphho1F}`K=#;OLF3P!HuF9k9>3)OLkm9mNa}81OR2|` zr9MV5ZNMD6A?xq<ikl=)O*9|FJ2(E0srDd2h!jQ$qU2ZSHk17PF?09}cWzti9GjEB z>60%IXy`U+IgXHC{FphWW!=Msc^U$;5C^Zi#tGRDL^v>mVe;$OZ?ao*vLDpP>5Sn9 zKvGt3%%u-?U!U~`sq7uEU6+ukaLJNAGs*zeyu*tJLs{3qM_*PEh{g<1K<PeKElzlY zq3wrO_Qp4YTcvFux!X+8p~(-De~*%S6+l-}grn4*t$MlK!Ec7d^|p%TAbgk4qg-ZW zNR70haCuVaKo_c+?A>O3`j*T6Ui`A?{UOgAh^~^^GU-+%0DorJF#tmIqhhQu&p6w7 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 BH0uBW literal 0 HcmV?d00001 diff --git a/packages/uikit/assets/icons/png/ic-empty-battery-128@4x.png b/packages/uikit/assets/icons/png/ic-empty-battery-128@4x.png index 743330ec98443c7cfeeaa51ca9fd4e7a43ddaf7d..6585d24a05f617b5ddbfe335f8de1b5c48fd26b9 100644 GIT binary patch literal 13234 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 Z>Cdk}XI`&;@rSSw>dyV!IX4Y`{uf=S0mT3S literal 11506 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 LIw~c1EJFVS?+%AI diff --git a/packages/uikit/assets/icons/png/ic-empty-battery-accent-flash-34@4x.png b/packages/uikit/assets/icons/png/ic-empty-battery-accent-flash-34@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..677d724fa40b370ef185e3c0f5019ef8c7db08fa GIT binary patch literal 6054 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 literal 0 HcmV?d00001 diff --git a/packages/uikit/assets/icons/png/ic-empty-battery-flash-34@4x.png b/packages/uikit/assets/icons/png/ic-empty-battery-flash-34@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..f6b3fdfdce0cdc72487a0a5f4f5edcc6e37b34bb GIT binary patch literal 6046 zcmX9?XHb((7kv^!2u(wWfDn4e&;$e`0i;Wl-g}cOpmah47^FxOP&xtvQJOTB4$`~y zUR6|(E(*MSIN$8--kF_q&%JkMf1EvW`nu}W6bK3c0H`%JR1L2p;c9<_L9b3J-8ZnS z0Cmt-R|PKrt%8=<X#l`<UsDxh6qK`*8ya9dmD}6a7XE7(zo$?BtcZXsVALou)d9Py z#PFCAxopa&)xk+SI+^iy^^)P0I`WRM?_2hnxlZd-mrt6s=ra%%x)JI;<cMeh4S0<g zD1f6~*?;F<WZ$8Mck<4CZ5?_4_2;OCNNVWs|8{QX{Q8sI@_Wve14sEn*0~48tuuqy zZ|AAxTBShQ!U}9nQyhV7Q8koc4!9#;CR&CSF&*Yz=&6k)lJZctMgGMmkA~jNY9m)h z4_z+sD%C^&ctS%XY;D`VyKx1~Q}?nutp`eyvDp9NMtvdq#`?Otm-*D%LSTo9n);@6 zuVAU@zL_Y}00IU*t6JG3qwW`moeE*=WMlRC3zR|<e6F`T8J+!C`cVcf&DML`(2>I8 zbY8s8-Orr|6}ZE)LsK2ayE4_}x3iKuZX@BBgX!5~7>28k0!;$__c$Qqi#_vGRlM() zqSwo`b-W|St+*_bPLHLB#>m=pRc^9hG=_&hlC~I&Q1(s0JfzUt1@_1Q8%k#w+?z!q z?C9()`%#N)VN%l?L3=3rJXM+8h8nr#A!=Pm^EpBEm^fo8%41@<jNCZzX6EYqNZJl3 z)(1GY-HawLN5AKL!wBc*={uA9>3R)dW;-Yx@Sv$loq+ZW3beAeQ=9zw7L}UQT;yXR z%Ef4SLYtO$KRpIrN7=4VtUu~>HJ`kEIzMkmoKi00be4oUPdv7Z^at9j)512<#Jj56 zT8QH6>aoFSvbWQP6nJ(D2v|_ABsmo-H<@vZt{p`T%vzneaj&=3Qj`l}IDrIq>nyP5 zJcA$KOv9-KU6V(Q?n%Cxwr2!C2N(o@U*<l<t*k6b(6lQN16466(fvMKW}=-Ug065{ zhoUJ&QOt{0hkEF{Sr>LEPt5*ZE>9~Z_EA#s<cb|Tk|4?B2mtW0{5S<b3nox!;uV0> zC$W<jXQg5z1u&DH1-C$K&PCi3Y`1YoM_raPZDF$(^|?I5NK@<3u68QU%2c>1{+m{6 z7%7R0QWGTi{?@+L{ubmm@RQW>I^8c`S`N^uQuRKD(0#V>V1`XAmJrS2w7B`=Rdo7i z;3dVL5%7w3Dnt7Ztl${g+`qQ>!*a#uw;vZGj)>{e&5bDU-5Vi2v;zJM>X|+QkIMsj zY4(4H8X9LJ5RKLQFp1XHo_)xMJ!%(QPx(j+3)y>`eqh_^6?U9!hX3T0yok#dbGc>= zZW5g~d)d^5v{<9*!989HIezDcR}GECK0Jv3FNHIo-;S$J?k5a%Dj~Tfvzcn3ej*({ z3HO5E-`pDhxuW$@@b<e{zPomvgLy|MA8*Y})@7Bu4j?xupH^~}J_ppY4PVz#%4>wQ z);Mnn%Nq#`usVg=q~sG!K+N>dr4p@WA$y65fe(HdQwX#0@cX!+)2vjKE@$hGHDK)+ z_%+-(v{Mr`56a;wP&6ckDys%4M7?x~eQ`#+8h>j2Rp=ZINB~Z_WKfS1FsHHVmK^n} zyQrxq!r0o!IaFF>WV6UG`3SmxSUgVyurdebw|``=``q<VMnZ*b$c8m{B)YSca_l>+ zQ!+DVbsQr8vcpsoC8x{(om0a1BBOD}^H;h4GwA+Vd>j42+Kuj7*(cSxEPBC^7WCis zh-_R(0ZpI?H8=|Es{Yxa%}IyE_d3Wn361!*ALC}I_sk_{1{u+|!gN3#a${9+ypC#B z4)kaC%Lmhyr$w!5DT~di<*BD2Fqq)|!M^3@<avE;Y^;9q%~AR55<#lFuYl3Gq7E;S zppohM`nKhTBfrJcFzJ-JM%Nksvuv~lI_NYD!IcNGtL}$qBJ$AY|1Md#2MI}o?;B2U z!zSA|LuecncBy4<D&(_D$9?+s`>b>M=UtuC%KnPftX9Of!MHz_p_$n?#mS6i*s2E| zH8Y?%Yu5O;O^4z$4>qTHofLVFaB&$RU#My>j`aSGUYBE8_YLg{%aT+lqgkmKw&y_n zm@xt?Q06|+nzT!}*!%p-dFc*X7$4yL>=|HH!It@F!Sct9m;qyvCSv1ITZ_DTQuX-~ zlXIP|!TtMm=;ID^mQDRoHGppjv`XW3?X0%mJ5U;{u4BaFSFPwc{emN)ty|y@S6_4{ z>yPR4S=9{$rP~|JLr1=5&!Pcyc3H3Fxce?tB!{L`4!*)H=fLf+@=_<UCB3Ko0KX9h z-Qm%{M~*%|bH_(_1<)CVw(vykcHg+j88|uqfwmuu6kuc;Dtz%eQtp@}ih_ZJ?E#Z} zn{=U`BZ-sRf<u6}__A=q%}O5IBMj_mCe!&^SEGrLbaBn~g$Eu<BtdE=B2lf6zPo|~ z#2~nJYB`q(2}Bg)2MsRbwON4KZoNzrNY@!&`h#A0cgdpd(S7=Psatl3u~WZ**($>f z#e>@ggUIfR#)S9Kkj>Nq6v(IS`6e++3}3Bp>8QoyQDj_NLpEge%IrvMHJ(aBO|)r| zEmtJbI$3$2`Jw9sJSHZ_2FNU*6em1V6V`FOJ|RCbWl~3{_V*$p-_8BlI_XfPVn@xt zHcw!NC#jMNS<a?zYiV%;vxil!humLgh({&9)xVvA3(7*e3n+e7)(Lg|d^KC0UP*c~ zmnGbwLCc;`_V1FXH?Bjg*RXbok+uTL=>>nHq)Nsx=uGKgGB2JydHdE&daSu99);3J z)fhrKUBdArf~a(MQ(m<OWKl#56De~}s*mtkPCWc+Z|aySO5pxmk$DHhk{W8N!jM{5 z3a~Isx2k^Pu)Ri!W4}_Vom#Axy@QG?{0Wj?@6q=mr2ZhX^Hu)9V_0muVU1fUwg{t9 z>v4srQO**F5v?5vN6MT}?ADz|=>b8nKquH%U2%<E1;0F<0{CfmlD?TM{B=P{Egc?0 z{~MsWjhBVe@qg4GLE0~o`BCxdJ}S(^6suoVyJFx6nJk_rP~~FSaB*qYFuo}?;b7L> z(O=`3whM`=2v4paLF#sNqfA@UD)<L-@b&`N{D%V!r7Km#*;O~RH2%=O^5gwW`)Zpe z7bB%uBuOx*5EfcInm0XAc7^kPSw3qQ*xpxox+E$C9T<45t@+O6Ji_4F%?&@``ytZX zMRyM8AgGYt-o&7Uz>eciGfjSIfjjSQO9+7v85f6i2FJtWsXC1w(DO1aU4Dy_J7mv+ z_PirBM-E_LTvtfW^Ssxu#=5Bh`Es*4!@LzX7CiGaWMSb|xPS0K0X1#Z``^!Psr##d zZ`(DK)0r6}e&=0I^T?&Fz|qHKS|q}tqlO<CI;(2lvRi#Di;<TX=&IgEwswY=%rB+U zl(nFFq^qw$&ao_3xujEd9_hV)QhUGwKfEJgn%vAE_0SW43x-|L9`xZ}UC}Y7J+jAb z&Qi?irITt6@?-btb@D_~|6D*cYZ)p>kWmv)p5Ur!l``f&bY|{jz&vx;W)Q`7bzCHq z`wG}K1EWikrp-+C^|$>=O;gAKT2Vb9ox|Ge;ioN9Uw4kplUmX{4y%?1WOi<N+2@rs zgKa&u_<Br^W*v9NF1%x#GDdgfC0av-JHwy6jVUx?vYi1M=hr=vd(lzy>;^$4C%v5- z8t2vhP^_|_9ZUHaDgMmtL&wj&|HxMNsA#hqeHo)15H&fJ?}hNP(^t(NklWeB%3iWA zN#Q_j9?7RkW*;{4XsA||@ZG^ztS&N%cEDE_p5?3Rp4$3W3+rVDIt6depX{^M1Uvyu zMQCr+^*SZDH^;mK&P7tZG*&lMdm6cuP<=oDT-Vj<CX~n*Q>TVNv^dy>io<BgS!!~q z#XIA?yS$6*Ft^o33Yn6#Xa?mpist&=dUcB*@;}!dTjK`}Xj|Y{KY|Yj&XgxEU6}J^ zGDSm=RBPuOo~BD{{3{Ra>V$M9SYpB+WXrHoq(7g*2P>wP9R@os<>!?xW(Fvd^YKzY z40ZqGrLjl23*x|*BVTISMN=zZB|E~GxyGOwK@Wa9lQftb85JKfez}rXL2oIv;oKd} z{i`1_?I%m3>R{(BR)W4T@03;gqayGAR9;Tv-ItstR1bT@DPyCld7Qgx`Jq5of5HpZ zgswt#FNyP3B=1<he3orkVTfP+6gadi$<<yJ9QCxWx|jhFtGCdmIwJLz#1q4#TcRdn z^oGPPmW@r~zsIGE!)lYxWti{3$)Hi6z$n-z*Eo+fALO@}W|NmjW56YpOxU2-soS)w z2+4>?p>sIfD*;Q}Ak&u<O!*prYCsI+L4co+<~8mih=BpU(@^qsv_iVMn99?@(pS+0 z=Yy|-Asr1+atN4RUjR0CrBrz+!HIPiDvv!Mzy}Z~@YSSY552BQ&-&=y*$JfouV7y) zI-xhyDV~H=X`*yMZw534$Fc2BV)<kuhyA3o&O-IEr)%<D$;wBH?nd*s+}o%K``(XT z4U&X{J+|3^34th2#pThUMGu;k>Rb%7)U*e+CLa%W!uXluw0C{7#_R|Z*JnM4KUE(U zvu^J9VENkh2nsq>Bl7a3JnyK`vK*yd7(w1h;0M@hEz~<Xf5OCfV}@;EcJlaU{gx)u z?nXe?yFLRl0p1%9QPfC>5QS#6i>y%77H@e$%sNeyX2vgQ^|t4(1ax(g$9QkY_fMZv z-DsJo0MEz<LCiUR%Q)WDTvUZ&0kKIhT*25v_r}I5c7uru^jXJ21Zo)Y>{@57o$*%H zn+*Hd^!JJER3-s{+0rptR4o})yg3b&r&Z7o*y2Y_Gv?_l2UD4red;OR6(?Xme6H8) zjkk`}x3Ea@Q)X+8<tarOfUseJ%A3g`Xg=<<i?OL`ow;bYh~R_d-GGa)Rloj{09lGY zYZ{!v6xgaL6m>g{vN$(cd9ilA-*%_IU?<Bj0mD`EZ;EeWT#+mQM2jf{d+H7_@oM6n zdK888&WAs0dfeQdZB0Yy;ilqE@cX|sJr7<F5}7XL;&wjWFlB)NusTV>9aP>mG3#>; ziFqxaN=X={oa|a6T)M6_x0&NszM5&rcz7}aWu3Rmh*n{4Y&NRjbiyrYQdABiRTYGy z*7P(uy(!T3ed1L*fw>s7iA*{M#Oj}N1&6q3;+4!YSR-8<Q0?X`<si#6L1z<YP1c3* zRDU=31c2@RByH7>uN6!eT+4ntSRxgeaB6EqKv=Z!Ypr2vw0W$gpW0_Q3bs;yxgmW; zHz=WDC13Uy_s4`k(Tr<N2oLABd1i!acOEq{ZN2e2sNs_|bbC=0+uO~ZabH~|#WxY7 z`en~xKWKTTpF2aNsRubRSYvC$hQ8u@Kn4C{CSR7X9O4t=9_RlL{R{(7!&s0W{<I79 zsHBJ|WnzJts+*5W|4Z#eGB7ZJ3(C|`-iR92P6p`Q@}vUTz;k7%;a{KjaEf>Vv71&i z>ergc#Qq^}{C4O&y7xg5)x9&NSF-FcvfsnNqFP&S?9>a1tCGK9j~A*$!Dy`Cm>J<t zD_w~Z?|2JwhA>lR$f|a_7ELSDn~J(6lXR1HTD*W0yp%uFRMaL1A}$!SK}`!YDh4r6 zYS%Dbx4N||-#?b-A-jxZuDe66xchOS%5|S~<xP2+?~-<!v2Qb`R`=SqjWB<fgv6cn z2yG$n!DJCOpX5sX3jG@Ko;}M4Kj*1PcI}2zqt1OO2teHlpRQ{6XR-fvJbLH&6hq`G zf&IMe`Zw@sw}(vZ;~xK9KAU^&<{+or)<MNx-9<%l8jYI(dAnnMs_MiPsD4YE#OoSm z)45A}sd}-JD|FnAt5A|q%5O%24TO&8kU4;oN02WM3(t@**$sZ-QSV-T)mn`1<u>Bi zn35#K6(F*?ON`_*s2J^XF^f>jPieVGQVmhO0NK<myGwaUtu|daeCrCekutz9PyCIU z#Cv!q9SNLZhMeL5q|HwQ#f<MP(Z>Qk;z3tJs#F&rP)5&Kp~9WGV{qdO?JH~Sc!i;v ztwRn8rJ>@SC24^h{P>E_aox#>4ryXFKHOTy;O-fdsky&X5MT%tr(+;-S&w0pvm}~1 z<tC|?r0rVJ6n*N78hMDJLdK+!lCt{@`ikM>tB_jBCS&aTyEWCf2rBxDbEF3Gz#*ul zla{H6Pur1|ke<XV$pMELhI~jMh4z$cIWiICEKAt%*D3hCO`Z<4co!CE(qx%!J2B}D zD{B}^z~}_u>!wkZ-O$!t6qzL{LBq_vSo!a16$FSVDoatS#6Bj{^Vu08;l4+b&E5q+ zn3IeIwb}oEoHe<eN282lKWuaCq93|@9!QxS+I$`#1eNL3b42_eHYS4rDErrj0oQpq zbKkw(T$4;+VQr@SHm?+0-~!JOAQ)!$@M%Q#Ymt(3fL}jm=HkUCx7P^-x_g2_F<M)j zEG&?AzLIYV3T>CH;YEg}H90vsMIul>K?cTiu`y@3t-v`HP${&qP`BuPYal#nM&Uf+ z1sF{qGp8@tuz!=#Q0e->4Wys&^T+w9ztwjX)F}@5$vj*4zO<B7HZ-$q=D7u@Ri=Eo ztj$e<X`VR0U|u;u&p=RnNcwdI+6(UTm*`;Gt?UEK^avtw7>kLt?sdB3`!+77LHY|a z>H0ZorV0zu=){=KFHEEMcCilo_@43qz9O8?PVC6A1rLeQD@oZ#QdaMqduV&^2_Z(5 zN<4z07d3;Fz=xf>q3-AUE0f-3dlN!eP8cxNBi+9>ASm<uFz6dEpu#{zai8CDjquID zG)z}s0IS9W6wHqD6AF4ivV%^z;jo~I#0FR9KI5vpk#i=EyBCso<i6Sq^skelYJ6D4 zTmS2Lt&R%;52H6X>792@H@`i^dP^DHqp&}(JA3k#*~#+ayPAtpHjQe5d12icYeeLJ z=FsHlZjQFLQ|&%lX&sb&;h)Vf)K%WQ|JiT$3WhH~0m58_6b_|1IllWJK0Urb#P<yg z=P@1>vJcH<5&J_`)y{r%hx;$PO7(ta(&M+2E?W3@`K%Sr?%`&37)&21&G&zYP=uXy zElHq)l`7ryt>rthiA+UwcXqbv(M=ut9b*3ixtU-TujP;FW@yXS<3*Ny(@iGj#Zi%8 zYIWBeXu{iS9vxSWoCW$!`5$U$h@V;-NdHF~>AD)oHQBx@!zxpGAzx=dq`bj@uPvuJ zue>{}r(Fu*c8Di9C)PjMy?ed4XvPaTk7%pn7Cj4g2vg(}ckOr!neR}Q?`Y1>$~GsE zPz8^56@9V`rs`aX^6ib2+IHXRJ5pPWQVP+FZkU;5dzN2$>c5qqeO8jPmUxt#DKF7= zBM0PT!RRDAtWuPZIRN9BH4Lvg6nVlEfkS4jx_T}z*CCT_*`J1g&Vyb|`bnIC0L&?G z^;INPpO(oTK{S7U_ri5%8X6kzYg<k-KY3&$8`^G)<7)AeQrd8sd0j)*lOMMB<^9!o z4@?Cs6_n+CR}~#?KQE15XJ6)lHL+id9L*)McR3Gx5Z-|sy>pp1u#N%@*f(>|Q?o%f z6stvU-yfuoaALKuKKQLp4Gu<UNz&7AZMG@x*~D!KrLBCysMFxCy@bRbHYGMOIyq8a z8SI}-;Jl`1qMN4Zv0Y!J_hOStAg9?TYJcLhJG}@H#D^tx-M-gJP7*|#D(ooTBe=di zKJ>y$DvO!|UI=Rs#4ZQ%b+AF#F3r}B4}7cry5=wTsq$7+G-CStt8$eJl*vc9?WC~c zKrQV;rK23kj*LVeR7tm4K4+NSNLr>b`(R6irQ*xxaC0<LU1e`;NGd%l3WDhCp6gaf zR+^(KCixaW(w6&SdXoqH2<RnubM=*^yTMcH0_ET-a=|T<X-4Arc4iIFG8Ow7vn2dk zNJ*oHHuBO~cvR5W`dftpAtk}QF=xDnnRdU#`)}Akuid2UH9u7jPegkOy~}#EdI`$5 WtjYGL!s4zBcR=&5u4<*S9qxaB<o_=K literal 0 HcmV?d00001 diff --git a/packages/uikit/assets/icons/png/ic-full-battery-128@4x.png b/packages/uikit/assets/icons/png/ic-full-battery-128@4x.png index b647ff43889cfbfc3743e22fd64102af57ebe883..2a14115ce842a64040905473e8cdb6630c23779e 100644 GIT binary patch literal 11787 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 b^6ivZwv_ii=Ul<_vx(q$bk&M(KMwmJE|R$w literal 11853 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 diff --git a/packages/uikit/assets/icons/png/ic-full-battery-34@4x.png b/packages/uikit/assets/icons/png/ic-full-battery-34@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..aa18b9fe5a0c58347ab78ed56b0b101d0d34824e GIT binary patch literal 5041 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 BIIjQz literal 0 HcmV?d00001 diff --git a/packages/uikit/assets/icons/png/ic-medium-battery-128@4x.png b/packages/uikit/assets/icons/png/ic-medium-battery-128@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..f0fc792a6574972f9e3a02d36f42269514bb9995 GIT binary patch literal 11996 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 literal 0 HcmV?d00001 diff --git a/packages/uikit/assets/icons/png/ic-medium-battery-34@4x.png b/packages/uikit/assets/icons/png/ic-medium-battery-34@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..323eecba0498b93b827eefb2f7440639f0ffecd5 GIT binary patch literal 5088 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 literal 0 HcmV?d00001 diff --git a/packages/uikit/assets/icons/svg/128/ic-almost-empty-battery-128.svg b/packages/uikit/assets/icons/svg/128/ic-almost-empty-battery-128.svg index bf7e7b928..f693b5fd1 100644 --- a/packages/uikit/assets/icons/svg/128/ic-almost-empty-battery-128.svg +++ b/packages/uikit/assets/icons/svg/128/ic-almost-empty-battery-128.svg @@ -1,4 +1,4 @@ <svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> - <path fill-rule="evenodd" clip-rule="evenodd" d="M51.3438 18.0073C43.1563 18.0486 38.5598 18.3254 34.9202 20.1799C31.1569 22.0973 28.0973 25.1569 26.1799 28.9202C24 33.1984 24 38.7989 24 50V88C24 99.201 24 104.802 26.1799 109.08C28.0973 112.843 31.1569 115.903 34.9202 117.82C39.1984 120 44.799 120 56 120H72C83.2011 120 88.8016 120 93.0798 117.82C96.8431 115.903 99.9027 112.843 101.82 109.08C104 104.802 104 99.201 104 88V50C104 38.799 104 33.1984 101.82 28.9202C99.9027 25.1569 96.8431 22.0973 93.0798 20.1799C89.4402 18.3254 84.8437 18.0486 76.6562 18.0073C74.4107 13.2734 69.5877 10 64 10C58.4124 10 53.5893 13.2734 51.3438 18.0073ZM32 45.2C32 38.4794 32 35.1191 33.3079 32.5521C34.4584 30.2942 36.2942 28.4584 38.5521 27.3079C41.1191 26 44.4794 26 51.2 26H76.8C83.5206 26 86.8809 26 89.4479 27.3079C91.7058 28.4584 93.5416 30.2942 94.6921 32.5521C96 35.1191 96 38.4794 96 45.2V92.8C96 99.5206 96 102.881 94.6921 105.448C93.5416 107.706 91.7058 109.542 89.4479 110.692C86.8809 112 83.5206 112 76.8 112H51.2C44.4794 112 41.1191 112 38.5521 110.692C36.2942 109.542 34.4584 107.706 33.3079 105.448C32 102.881 32 99.5206 32 92.8V45.2Z" fill="#2E3847"/> - <path d="M38 75.6C38 72.2397 38 70.5595 38.654 69.2761C39.2292 68.1471 40.1471 67.2292 41.2761 66.654C42.5595 66 44.2397 66 47.6 66H80.4C83.7603 66 85.4405 66 86.7239 66.654C87.8529 67.2292 88.7708 68.1471 89.346 69.2761C90 70.5595 90 72.2397 90 75.6V96.4C90 99.7603 90 101.44 89.346 102.724C88.7708 103.853 87.8529 104.771 86.7239 105.346C85.4405 106 83.7603 106 80.4 106H47.6C44.2397 106 42.5595 106 41.2761 105.346C40.1471 104.771 39.2292 103.853 38.654 102.724C38 101.44 38 99.7603 38 96.4V75.6Z" fill="#F5A73B"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M53.0435 16.0162C53.5411 10.402 58.2565 6 64.0001 6C69.7437 6 74.4591 10.402 74.9567 16.0162C83.2387 16.0931 87.9328 16.5333 91.4046 19.0557C92.7629 20.0426 93.9574 21.2371 94.9443 22.5954C98 26.8013 98 32.8009 98 44.8V91.2C98 103.199 98 109.199 94.9443 113.405C93.9574 114.763 92.7629 115.957 91.4046 116.944C87.1987 120 81.1991 120 69.2 120H58.8C46.8009 120 40.8013 120 36.5954 116.944C35.2371 115.957 34.0426 114.763 33.0557 113.405C30 109.199 30 103.199 30 91.2V44.8C30 32.8009 30 26.8013 33.0557 22.5954C34.0426 21.2371 35.2371 20.0426 36.5954 19.0557C40.0673 16.5333 44.7614 16.0931 53.0435 16.0162ZM34 41.6C34 32.6006 34 28.101 36.2918 24.9466C37.032 23.9278 37.9278 23.032 38.9466 22.2918C42.101 20 46.6006 20 55.6 20H72.4C81.3994 20 85.899 20 89.0534 22.2918C90.0722 23.032 90.968 23.9278 91.7082 24.9466C94 28.101 94 32.6006 94 41.6V94.4C94 103.399 94 107.899 91.7082 111.053C90.968 112.072 90.0722 112.968 89.0534 113.708C85.899 116 81.3994 116 72.4 116H55.6C46.6006 116 42.101 116 38.9466 113.708C37.9278 112.968 37.032 112.072 36.2918 111.053C34 107.899 34 103.399 34 94.4V41.6Z" fill="#2E3847"/> +<path d="M38.0767 104C38.08 105.569 38.3878 106.682 38.8719 107.632C39.6389 109.137 40.8627 110.361 42.368 111.128C44.0793 112 46.3195 112 50.7999 112H77.1999C81.6803 112 83.9206 112 85.6318 111.128C87.1371 110.361 88.361 109.137 89.128 107.632C89.6121 106.682 89.92 105.568 89.9232 103.999C89.92 102.431 89.612 101.318 89.1279 100.368C88.361 98.8628 87.1371 97.6389 85.6318 96.8719C83.9205 96 81.6803 96 77.1999 96H50.7999C46.3195 96 44.0793 96 42.368 96.8719C40.8627 97.6389 39.6388 98.8628 38.8718 100.368C38.3878 101.318 38.08 102.431 38.0767 104Z" fill="#F5A73B"/> </svg> diff --git a/packages/uikit/assets/icons/svg/128/ic-empty-battery-128.svg b/packages/uikit/assets/icons/svg/128/ic-empty-battery-128.svg index 58901b892..412e0088e 100644 --- a/packages/uikit/assets/icons/svg/128/ic-empty-battery-128.svg +++ b/packages/uikit/assets/icons/svg/128/ic-empty-battery-128.svg @@ -1,4 +1,4 @@ <svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> - <path fill-rule="evenodd" clip-rule="evenodd" d="M51.3438 18.0073C43.1563 18.0486 38.5598 18.3254 34.9202 20.1799C31.1569 22.0973 28.0973 25.1569 26.1799 28.9202C24 33.1984 24 38.7989 24 50V88C24 99.201 24 104.802 26.1799 109.08C28.0973 112.843 31.1569 115.903 34.9202 117.82C39.1984 120 44.799 120 56 120H72C83.2011 120 88.8016 120 93.0798 117.82C96.8431 115.903 99.9027 112.843 101.82 109.08C104 104.802 104 99.201 104 88V50C104 38.799 104 33.1984 101.82 28.9202C99.9027 25.1569 96.8431 22.0973 93.0798 20.1799C89.4402 18.3254 84.8437 18.0486 76.6562 18.0073C74.4107 13.2734 69.5877 10 64 10C58.4124 10 53.5893 13.2734 51.3438 18.0073ZM32 45.2C32 38.4794 32 35.1191 33.3079 32.5521C34.4584 30.2942 36.2942 28.4584 38.5521 27.3079C41.1191 26 44.4794 26 51.2 26H76.8C83.5206 26 86.8809 26 89.4479 27.3079C91.7058 28.4584 93.5416 30.2942 94.6921 32.5521C96 35.1191 96 38.4794 96 45.2V92.8C96 99.5206 96 102.881 94.6921 105.448C93.5416 107.706 91.7058 109.542 89.4479 110.692C86.8809 112 83.5206 112 76.8 112H51.2C44.4794 112 41.1191 112 38.5521 110.692C36.2942 109.542 34.4584 107.706 33.3079 105.448C32 102.881 32 99.5206 32 92.8V45.2Z" fill="#2E3847"/> - <path d="M38 100C38 96.6863 40.6863 94 44 94H84C87.3137 94 90 96.6863 90 100C90 103.314 87.3137 106 84 106H44C40.6863 106 38 103.314 38 100Z" fill="#FF4766"/> + <path fill-rule="evenodd" clip-rule="evenodd" d="M53.0435 16.0162C53.5411 10.402 58.2565 6 64.0001 6C69.7437 6 74.4591 10.402 74.9567 16.0162C83.2387 16.0931 87.9328 16.5333 91.4046 19.0557C92.7629 20.0426 93.9574 21.2371 94.9443 22.5954C98 26.8013 98 32.8009 98 44.8V91.2C98 103.199 98 109.199 94.9443 113.405C93.9574 114.763 92.7629 115.957 91.4046 116.944C87.1987 120 81.1991 120 69.2 120H58.8C46.8009 120 40.8013 120 36.5954 116.944C35.2371 115.957 34.0426 114.763 33.0557 113.405C30 109.199 30 103.199 30 91.2V44.8C30 32.8009 30 26.8013 33.0557 22.5954C34.0426 21.2371 35.2371 20.0426 36.5954 19.0557C40.0673 16.5333 44.7614 16.0931 53.0435 16.0162ZM34 41.6C34 32.6006 34 28.101 36.2918 24.9466C37.032 23.9278 37.9278 23.032 38.9466 22.2918C42.101 20 46.6006 20 55.6 20H72.4C81.3994 20 85.899 20 89.0534 22.2918C90.0722 23.032 90.968 23.9278 91.7082 24.9466C94 28.101 94 32.6006 94 41.6V94.4C94 103.399 94 107.899 91.7082 111.053C90.968 112.072 90.0722 112.968 89.0534 113.708C85.899 116 81.3994 116 72.4 116H55.6C46.6006 116 42.101 116 38.9466 113.708C37.9278 112.968 37.032 112.072 36.2918 111.053C34 107.899 34 103.399 34 94.4V41.6Z" fill="#2E3847"/> + <path d="M53.8021 63.4375L62.9172 52.4994C65.7678 49.0787 67.1931 47.3683 68.2361 47.8466C69.2791 48.3249 68.9131 50.521 68.1811 54.9132L67.3739 59.7563C67.1968 60.8188 67.1082 61.3501 67.3758 61.7466C67.6433 62.1432 68.1691 62.26 69.2206 62.4937L70.8899 62.8647C75.1897 63.8202 77.3396 64.2979 77.8835 65.8926C78.4274 67.4872 77.0175 69.1791 74.1977 72.5629L65.0826 83.501C62.232 86.9217 60.8067 88.6321 59.7637 88.1538C58.7207 87.6755 59.0867 85.4794 59.8187 81.0872L60.6259 76.2441C60.803 75.1816 60.8915 74.6503 60.624 74.2538C60.3565 73.8572 59.8307 73.7404 58.7792 73.5067L57.1099 73.1358C52.8101 72.1802 50.6602 71.7025 50.1163 70.1078C49.5724 68.5132 50.9823 66.8213 53.8021 63.4375Z" fill="#45AEF5"/> </svg> diff --git a/packages/uikit/assets/icons/svg/128/ic-full-battery-128.svg b/packages/uikit/assets/icons/svg/128/ic-full-battery-128.svg index 74746ab11..df51d8f15 100644 --- a/packages/uikit/assets/icons/svg/128/ic-full-battery-128.svg +++ b/packages/uikit/assets/icons/svg/128/ic-full-battery-128.svg @@ -1,4 +1,4 @@ <svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> - <path fill-rule="evenodd" clip-rule="evenodd" d="M51.3438 18.0073C43.1563 18.0486 38.5598 18.3254 34.9202 20.1799C31.1569 22.0973 28.0973 25.1569 26.1799 28.9202C24 33.1984 24 38.7989 24 50V88C24 99.201 24 104.802 26.1799 109.08C28.0973 112.843 31.1569 115.903 34.9202 117.82C39.1984 120 44.799 120 56 120H72C83.2011 120 88.8016 120 93.0798 117.82C96.8431 115.903 99.9027 112.843 101.82 109.08C104 104.802 104 99.201 104 88V50C104 38.799 104 33.1984 101.82 28.9202C99.9027 25.1569 96.8431 22.0973 93.0798 20.1799C89.4402 18.3254 84.8437 18.0486 76.6562 18.0073C74.4107 13.2734 69.5876 10 64 10C58.4123 10 53.5893 13.2734 51.3438 18.0073ZM32 45.2C32 38.4794 32 35.1191 33.3079 32.5521C34.4584 30.2942 36.2942 28.4584 38.5521 27.3079C41.1191 26 44.4794 26 51.2 26H76.8C83.5206 26 86.8809 26 89.4479 27.3079C91.7058 28.4584 93.5416 30.2942 94.6921 32.5521C96 35.1191 96 38.4794 96 45.2V92.8C96 99.5206 96 102.881 94.6921 105.448C93.5416 107.706 91.7058 109.542 89.4479 110.692C86.8809 112 83.5206 112 76.8 112H51.2C44.4794 112 41.1191 112 38.5521 110.692C36.2942 109.542 34.4584 107.706 33.3079 105.448C32 102.881 32 99.5206 32 92.8V45.2Z" fill="#2E3847"/> - <path d="M38 41.6C38 38.2397 38 36.5595 38.654 35.2761C39.2292 34.1471 40.1471 33.2292 41.2761 32.654C42.5595 32 44.2397 32 47.6 32H80.4C83.7603 32 85.4405 32 86.7239 32.654C87.8529 33.2292 88.7708 34.1471 89.346 35.2761C90 36.5595 90 38.2397 90 41.6V96.4C90 99.7603 90 101.44 89.346 102.724C88.7708 103.853 87.8529 104.771 86.7239 105.346C85.4405 106 83.7603 106 80.4 106H47.6C44.2397 106 42.5595 106 41.2761 105.346C40.1471 104.771 39.2292 103.853 38.654 102.724C38 101.44 38 99.7603 38 96.4V41.6Z" fill="#45AEF5"/> + <path fill-rule="evenodd" clip-rule="evenodd" d="M53.0435 16.0162C53.5411 10.402 58.2565 6 64.0001 6C69.7437 6 74.4591 10.402 74.9567 16.0162C83.2387 16.0931 87.9328 16.5333 91.4046 19.0557C92.7629 20.0426 93.9574 21.2371 94.9443 22.5954C98 26.8013 98 32.8009 98 44.8V91.2C98 103.199 98 109.199 94.9443 113.405C93.9574 114.763 92.7629 115.957 91.4046 116.944C87.1987 120 81.1991 120 69.2 120H58.8C46.8009 120 40.8013 120 36.5954 116.944C35.2371 115.957 34.0426 114.763 33.0557 113.405C30 109.199 30 103.199 30 91.2V44.8C30 32.8009 30 26.8013 33.0557 22.5954C34.0426 21.2371 35.2371 20.0426 36.5954 19.0557C40.0673 16.5333 44.7614 16.0931 53.0435 16.0162ZM34 41.6C34 32.6006 34 28.101 36.2918 24.9466C37.032 23.9278 37.9278 23.032 38.9466 22.2918C42.101 20 46.6006 20 55.6 20H72.4C81.3994 20 85.899 20 89.0534 22.2918C90.0722 23.032 90.968 23.9278 91.7082 24.9466C94 28.101 94 32.6006 94 41.6V94.4C94 103.399 94 107.899 91.7082 111.053C90.968 112.072 90.0722 112.968 89.0534 113.708C85.899 116 81.3994 116 72.4 116H55.6C46.6006 116 42.101 116 38.9466 113.708C37.9278 112.968 37.032 112.072 36.2918 111.053C34 107.899 34 103.399 34 94.4V41.6Z" fill="#2E3847"/> + <path d="M38 36.8C38 32.3196 38 30.0794 38.8719 28.3681C39.6389 26.8628 40.8628 25.6389 42.3681 24.8719C44.0794 24 46.3196 24 50.8 24H77.2C81.6804 24 83.9206 24 85.6319 24.8719C87.1372 25.6389 88.3611 26.8628 89.1281 28.3681C90 30.0794 90 32.3196 90 36.8V99.2C90 103.68 90 105.921 89.1281 107.632C88.3611 109.137 87.1372 110.361 85.6319 111.128C83.9206 112 81.6804 112 77.2 112H50.8C46.3196 112 44.0794 112 42.3681 111.128C40.8628 110.361 39.6389 109.137 38.8719 107.632C38 105.921 38 103.68 38 99.2V36.8Z" fill="#45AEF5"/> </svg> diff --git a/packages/uikit/assets/icons/svg/128/ic-medium-battery-128.svg b/packages/uikit/assets/icons/svg/128/ic-medium-battery-128.svg new file mode 100644 index 000000000..2aff92bd1 --- /dev/null +++ b/packages/uikit/assets/icons/svg/128/ic-medium-battery-128.svg @@ -0,0 +1,4 @@ +<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M53.0435 16.0162C53.5411 10.402 58.2565 6 64.0001 6C69.7437 6 74.4591 10.402 74.9567 16.0162C83.2387 16.0931 87.9328 16.5333 91.4046 19.0557C92.7629 20.0426 93.9574 21.2371 94.9443 22.5954C98 26.8013 98 32.8009 98 44.8V91.2C98 103.199 98 109.199 94.9443 113.405C93.9574 114.763 92.7629 115.957 91.4046 116.944C87.1987 120 81.1991 120 69.2 120H58.8C46.8009 120 40.8013 120 36.5954 116.944C35.2371 115.957 34.0426 114.763 33.0557 113.405C30 109.199 30 103.199 30 91.2V44.8C30 32.8009 30 26.8013 33.0557 22.5954C34.0426 21.2371 35.2371 20.0426 36.5954 19.0557C40.0673 16.5333 44.7614 16.0931 53.0435 16.0162ZM34 41.6C34 32.6006 34 28.101 36.2918 24.9466C37.032 23.9278 37.9278 23.032 38.9466 22.2918C42.101 20 46.6006 20 55.6 20H72.4C81.3994 20 85.899 20 89.0534 22.2918C90.0722 23.032 90.968 23.9278 91.7082 24.9466C94 28.101 94 32.6006 94 41.6V94.4C94 103.399 94 107.899 91.7082 111.053C90.968 112.072 90.0722 112.968 89.0534 113.708C85.899 116 81.3994 116 72.4 116H55.6C46.6006 116 42.101 116 38.9466 113.708C37.9278 112.968 37.032 112.072 36.2918 111.053C34 107.899 34 103.399 34 94.4V41.6Z" fill="#2E3847"/> + <path d="M38 80.8C38 76.3196 38 74.0794 38.8719 72.3681C39.6389 70.8628 40.8628 69.6389 42.3681 68.8719C44.0794 68 46.3196 68 50.8 68H77.2C81.6804 68 83.9206 68 85.6319 68.8719C87.1372 69.6389 88.3611 70.8628 89.1281 72.3681C90 74.0794 90 76.3196 90 80.8V99.2C90 103.68 90 105.921 89.1281 107.632C88.3611 109.137 87.1372 110.361 85.6319 111.128C83.9206 112 81.6804 112 77.2 112H50.8C46.3196 112 44.0794 112 42.3681 111.128C40.8628 110.361 39.6389 109.137 38.8719 107.632C38 105.921 38 103.68 38 99.2V80.8Z" fill="#45AEF5"/> +</svg> diff --git a/packages/uikit/assets/icons/svg/24/ic-battery-almost-empty-24.svg b/packages/uikit/assets/icons/svg/24/ic-battery-almost-empty-24.svg new file mode 100644 index 000000000..bcfedeb03 --- /dev/null +++ b/packages/uikit/assets/icons/svg/24/ic-battery-almost-empty-24.svg @@ -0,0 +1,11 @@ +<svg width="15" height="24" viewBox="0 0 15 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <g clip-path="url(#clip0_29368_97527)"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M0.499999 8.30128C0.499999 5.67647 0.499999 4.36406 1.16844 3.44403C1.38432 3.1469 1.64562 2.8856 1.94275 2.66972C2.66251 2.14679 3.62241 2.03296 5.26296 2.00818C5.3841 0.879783 6.33943 0.00109838 7.5 0.00109833C8.66057 0.00109828 9.6159 0.879785 9.73704 2.00818C11.3776 2.03296 12.3375 2.14679 13.0572 2.66972C13.3544 2.8856 13.6157 3.1469 13.8316 3.44403C14.5 4.36406 14.5 5.67647 14.5 8.30128L14.5 17.7013C14.5 20.3261 14.5 21.6385 13.8316 22.5585C13.6157 22.8557 13.3544 23.117 13.0572 23.3328C12.1372 24.0013 10.8248 24.0013 8.2 24.0013L6.8 24.0013C4.17519 24.0013 2.86278 24.0013 1.94275 23.3328C1.64562 23.117 1.38432 22.8557 1.16844 22.5585C0.5 21.6385 0.5 20.3261 0.5 17.7013L0.499999 8.30128ZM1.50012 7.50135C1.50012 5.62649 1.50012 4.68905 1.97758 4.03189C2.13178 3.81965 2.31842 3.63301 2.53066 3.47881C3.18782 3.00135 4.12525 3.00135 6.00012 3.00135L9.00012 3.00136C10.875 3.00136 11.8124 3.00136 12.4696 3.47881C12.6818 3.63301 12.8685 3.81966 13.0227 4.03189C13.5001 4.68906 13.5001 5.62649 13.5001 7.50136L13.5001 18.5014C13.5001 20.3762 13.5001 21.3137 13.0227 21.9708C12.8685 22.1831 12.6818 22.3697 12.4696 22.5239C11.8124 23.0014 10.875 23.0014 9.00012 23.0014L6.00012 23.0014C4.12525 23.0014 3.18782 23.0014 2.53066 22.5239C2.31842 22.3697 2.13178 22.1831 1.97758 21.9708C1.50012 21.3137 1.50012 20.3762 1.50012 18.5014L1.50012 7.50135Z" fill="#2E3847"/> + <path d="M2.5 20.5013C2.5 19.6729 3.17157 19.0013 4 19.0013L11 19.0013C11.8284 19.0013 12.5 19.6729 12.5 20.5013C12.5 21.3297 11.8284 22.0013 11 22.0013L4 22.0013C3.17157 22.0013 2.5 21.3297 2.5 20.5013Z" fill="#F5A73B"/> + </g> + <defs> + <clipPath id="clip0_29368_97527"> + <rect width="14" height="24" fill="white" transform="translate(0.5 0.00128174)"/> + </clipPath> + </defs> +</svg> diff --git a/packages/uikit/assets/icons/svg/34/ic-almost-empty-battery-34.svg b/packages/uikit/assets/icons/svg/34/ic-almost-empty-battery-34.svg new file mode 100644 index 000000000..2e42335b8 --- /dev/null +++ b/packages/uikit/assets/icons/svg/34/ic-almost-empty-battery-34.svg @@ -0,0 +1,4 @@ +<svg width="20" height="34" viewBox="0 0 20 34" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M6.5331 3.01463C6.76939 1.31135 8.23141 0 9.99971 0C11.768 0 13.23 1.31135 13.4663 3.01461C15.6462 3.05886 16.9509 3.23708 17.9389 3.95492C18.3634 4.26331 18.7367 4.6366 19.0451 5.06107C20 6.3754 20 8.25027 20 12V25C20 28.7497 20 30.6246 19.0451 31.9389C18.7367 32.3634 18.3634 32.7367 17.9389 33.0451C16.6246 34 14.7497 34 11 34H9C5.25027 34 3.3754 34 2.06107 33.0451C1.6366 32.7367 1.26331 32.3634 0.954915 31.9389C0 30.6246 0 28.7497 0 25V12C0 8.25027 0 6.3754 0.954915 5.06107C1.26331 4.6366 1.6366 4.26331 2.06107 3.95492C3.049 3.23715 4.35362 3.05889 6.5331 3.01463ZM1.5 10.8C1.5 8.17519 1.5 6.86278 2.16844 5.94275C2.38432 5.64562 2.64562 5.38432 2.94275 5.16844C3.86278 4.5 5.17519 4.5 7.8 4.5H12.2C14.8248 4.5 16.1372 4.5 17.0572 5.16844C17.3544 5.38432 17.6157 5.64562 17.8316 5.94275C18.5 6.86278 18.5 8.17519 18.5 10.8V26.2C18.5 28.8248 18.5 30.1372 17.8316 31.0572C17.6157 31.3544 17.3544 31.6157 17.0572 31.8316C16.1372 32.5 14.8248 32.5 12.2 32.5H7.8C5.17519 32.5 3.86278 32.5 2.94275 31.8316C2.64562 31.6157 2.38432 31.3544 2.16844 31.0572C1.5 30.1372 1.5 28.8248 1.5 26.2V10.8Z" fill="#2E3847"/> + <path d="M3 29C3 27.8954 3.89543 27 5 27H15C16.1046 27 17 27.8954 17 29C17 30.1046 16.1046 31 15 31H5C3.89543 31 3 30.1046 3 29Z" fill="#F5A73B"/> +</svg> diff --git a/packages/uikit/assets/icons/svg/34/ic-empty-battery-accent-flash-34.svg b/packages/uikit/assets/icons/svg/34/ic-empty-battery-accent-flash-34.svg new file mode 100644 index 000000000..9c5e912f8 --- /dev/null +++ b/packages/uikit/assets/icons/svg/34/ic-empty-battery-accent-flash-34.svg @@ -0,0 +1,4 @@ +<svg width="20" height="34" viewBox="0 0 20 34" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M6.5331 3.01463C6.76939 1.31135 8.23141 0 9.99971 0C11.768 0 13.23 1.31135 13.4663 3.01461C15.6462 3.05886 16.9509 3.23708 17.9389 3.95492C18.3634 4.26331 18.7367 4.6366 19.0451 5.06107C20 6.3754 20 8.25027 20 12V25C20 28.7497 20 30.6246 19.0451 31.9389C18.7367 32.3634 18.3634 32.7367 17.9389 33.0451C16.6246 34 14.7497 34 11 34H9C5.25027 34 3.3754 34 2.06107 33.0451C1.6366 32.7367 1.26331 32.3634 0.954915 31.9389C0 30.6246 0 28.7497 0 25V12C0 8.25027 0 6.3754 0.954915 5.06107C1.26331 4.6366 1.6366 4.26331 2.06107 3.95492C3.049 3.23715 4.35362 3.05889 6.5331 3.01463ZM1.5 10.8C1.5 8.17519 1.5 6.86278 2.16844 5.94275C2.38432 5.64562 2.64562 5.38432 2.94275 5.16844C3.86278 4.5 5.17519 4.5 7.8 4.5H12.2C14.8248 4.5 16.1372 4.5 17.0572 5.16844C17.3544 5.38432 17.6157 5.64562 17.8316 5.94275C18.5 6.86278 18.5 8.17519 18.5 10.8V26.2C18.5 28.8248 18.5 30.1372 17.8316 31.0572C17.6157 31.3544 17.3544 31.6157 17.0572 31.8316C16.1372 32.5 14.8248 32.5 12.2 32.5H7.8C5.17519 32.5 3.86278 32.5 2.94275 31.8316C2.64562 31.6157 2.38432 31.3544 2.16844 31.0572C1.5 30.1372 1.5 28.8248 1.5 26.2V10.8Z" fill="#2E3847"/> + <path d="M6.60074 16.9791L9.63911 13.3331C10.5893 12.1928 11.0644 11.6227 11.4121 11.7821C11.7597 11.9416 11.6377 12.6736 11.3937 14.1377L11.1247 15.752C11.0656 16.1062 11.0361 16.2833 11.1253 16.4155C11.2145 16.5477 11.3897 16.5866 11.7402 16.6645L12.2967 16.7882C13.7299 17.1067 14.4466 17.2659 14.6279 17.7975C14.8092 18.329 14.3392 18.893 13.3993 20.0209L10.3609 23.6669C9.41069 24.8072 8.93559 25.3773 8.58792 25.2179C8.24026 25.0585 8.36226 24.3264 8.60627 22.8624L8.87533 21.248C8.93436 20.8938 8.96388 20.7167 8.87471 20.5845C8.78553 20.4523 8.61028 20.4134 8.25977 20.3355L7.70332 20.2119C6.27005 19.8934 5.55342 19.7341 5.37212 19.2026C5.19083 18.671 5.6608 18.107 6.60074 16.9791Z" fill="#45AEF5"/> +</svg> diff --git a/packages/uikit/assets/icons/svg/34/ic-empty-battery-flash-34.svg b/packages/uikit/assets/icons/svg/34/ic-empty-battery-flash-34.svg new file mode 100644 index 000000000..bc4b6ab82 --- /dev/null +++ b/packages/uikit/assets/icons/svg/34/ic-empty-battery-flash-34.svg @@ -0,0 +1,4 @@ +<svg width="20" height="34" viewBox="0 0 20 34" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M6.5331 3.01463C6.76939 1.31135 8.23141 0 9.99971 0C11.768 0 13.23 1.31135 13.4663 3.01461C15.6462 3.05886 16.9509 3.23708 17.9389 3.95492C18.3634 4.26331 18.7367 4.6366 19.0451 5.06107C20 6.3754 20 8.25027 20 12V25C20 28.7497 20 30.6246 19.0451 31.9389C18.7367 32.3634 18.3634 32.7367 17.9389 33.0451C16.6246 34 14.7497 34 11 34H9C5.25027 34 3.3754 34 2.06107 33.0451C1.6366 32.7367 1.26331 32.3634 0.954915 31.9389C0 30.6246 0 28.7497 0 25V12C0 8.25027 0 6.3754 0.954915 5.06107C1.26331 4.6366 1.6366 4.26331 2.06107 3.95492C3.049 3.23715 4.35362 3.05889 6.5331 3.01463ZM1.5 10.8C1.5 8.17519 1.5 6.86278 2.16844 5.94275C2.38432 5.64562 2.64562 5.38432 2.94275 5.16844C3.86278 4.5 5.17519 4.5 7.8 4.5H12.2C14.8248 4.5 16.1372 4.5 17.0572 5.16844C17.3544 5.38432 17.6157 5.64562 17.8316 5.94275C18.5 6.86278 18.5 8.17519 18.5 10.8V26.2C18.5 28.8248 18.5 30.1372 17.8316 31.0572C17.6157 31.3544 17.3544 31.6157 17.0572 31.8316C16.1372 32.5 14.8248 32.5 12.2 32.5H7.8C5.17519 32.5 3.86278 32.5 2.94275 31.8316C2.64562 31.6157 2.38432 31.3544 2.16844 31.0572C1.5 30.1372 1.5 28.8248 1.5 26.2V10.8Z" fill="#2E3847"/> + <path d="M6.60071 16.9791L9.63907 13.3331C10.5893 12.1928 11.0644 11.6227 11.412 11.7821C11.7597 11.9416 11.6377 12.6736 11.3937 14.1377L11.1246 15.752C11.0656 16.1062 11.0361 16.2833 11.1253 16.4155C11.2144 16.5477 11.3897 16.5866 11.7402 16.6645L12.2966 16.7882C13.7299 17.1067 14.4465 17.2659 14.6278 17.7975C14.8091 18.329 14.3392 18.893 13.3992 20.0209L10.3609 23.6669C9.41066 24.8072 8.93556 25.3773 8.58789 25.2179C8.24022 25.0585 8.36223 24.3264 8.60624 22.8624L8.8753 21.248C8.93433 20.8938 8.96385 20.7167 8.87467 20.5845C8.7855 20.4523 8.61025 20.4134 8.25974 20.3355L7.70329 20.2119C6.27002 19.8934 5.55339 19.7341 5.37209 19.2026C5.1908 18.671 5.66077 18.107 6.60071 16.9791Z" fill="#556170"/> +</svg> diff --git a/packages/uikit/assets/icons/svg/34/ic-full-battery-34.svg b/packages/uikit/assets/icons/svg/34/ic-full-battery-34.svg new file mode 100644 index 000000000..a2daa7923 --- /dev/null +++ b/packages/uikit/assets/icons/svg/34/ic-full-battery-34.svg @@ -0,0 +1,4 @@ +<svg width="20" height="34" viewBox="0 0 20 34" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M6.5331 3.01463C6.76939 1.31135 8.23141 0 9.99971 0C11.768 0 13.23 1.31135 13.4663 3.01461C15.6462 3.05886 16.9509 3.23708 17.9389 3.95492C18.3634 4.26331 18.7367 4.6366 19.0451 5.06107C20 6.3754 20 8.25027 20 12V25C20 28.7497 20 30.6246 19.0451 31.9389C18.7367 32.3634 18.3634 32.7367 17.9389 33.0451C16.6246 34 14.7497 34 11 34H9C5.25027 34 3.3754 34 2.06107 33.0451C1.6366 32.7367 1.26331 32.3634 0.954915 31.9389C0 30.6246 0 28.7497 0 25V12C0 8.25027 0 6.3754 0.954915 5.06107C1.26331 4.6366 1.6366 4.26331 2.06107 3.95492C3.049 3.23715 4.35362 3.05889 6.5331 3.01463ZM1.5 10.8C1.5 8.17519 1.5 6.86278 2.16844 5.94275C2.38432 5.64562 2.64562 5.38432 2.94275 5.16844C3.86278 4.5 5.17519 4.5 7.8 4.5H12.2C14.8248 4.5 16.1372 4.5 17.0572 5.16844C17.3544 5.38432 17.6157 5.64562 17.8316 5.94275C18.5 6.86278 18.5 8.17519 18.5 10.8V26.2C18.5 28.8248 18.5 30.1372 17.8316 31.0572C17.6157 31.3544 17.3544 31.6157 17.0572 31.8316C16.1372 32.5 14.8248 32.5 12.2 32.5H7.8C5.17519 32.5 3.86278 32.5 2.94275 31.8316C2.64562 31.6157 2.38432 31.3544 2.16844 31.0572C1.5 30.1372 1.5 28.8248 1.5 26.2V10.8Z" fill="#2E3847"/> + <path d="M3 9.2C3 8.0799 3 7.51984 3.21799 7.09202C3.40973 6.71569 3.71569 6.40973 4.09202 6.21799C4.51984 6 5.0799 6 6.2 6H13.8C14.9201 6 15.4802 6 15.908 6.21799C16.2843 6.40973 16.5903 6.71569 16.782 7.09202C17 7.51984 17 8.0799 17 9.2V27.8C17 28.9201 17 29.4802 16.782 29.908C16.5903 30.2843 16.2843 30.5903 15.908 30.782C15.4802 31 14.9201 31 13.8 31H6.2C5.07989 31 4.51984 31 4.09202 30.782C3.71569 30.5903 3.40973 30.2843 3.21799 29.908C3 29.4802 3 28.9201 3 27.8V9.2Z" fill="#45AEF5"/> +</svg> diff --git a/packages/uikit/assets/icons/svg/34/ic-medium-battery-34.svg b/packages/uikit/assets/icons/svg/34/ic-medium-battery-34.svg new file mode 100644 index 000000000..5cd278e0d --- /dev/null +++ b/packages/uikit/assets/icons/svg/34/ic-medium-battery-34.svg @@ -0,0 +1,4 @@ +<svg width="20" height="34" viewBox="0 0 20 34" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M6.5331 3.01463C6.76939 1.31135 8.23141 0 9.99971 0C11.768 0 13.23 1.31135 13.4663 3.01461C15.6462 3.05886 16.9509 3.23708 17.9389 3.95492C18.3634 4.26331 18.7367 4.6366 19.0451 5.06107C20 6.3754 20 8.25027 20 12V25C20 28.7497 20 30.6246 19.0451 31.9389C18.7367 32.3634 18.3634 32.7367 17.9389 33.0451C16.6246 34 14.7497 34 11 34H9C5.25027 34 3.3754 34 2.06107 33.0451C1.6366 32.7367 1.26331 32.3634 0.954915 31.9389C0 30.6246 0 28.7497 0 25V12C0 8.25027 0 6.3754 0.954915 5.06107C1.26331 4.6366 1.6366 4.26331 2.06107 3.95492C3.049 3.23715 4.35362 3.05889 6.5331 3.01463ZM1.5 10.8C1.5 8.17519 1.5 6.86278 2.16844 5.94275C2.38432 5.64562 2.64562 5.38432 2.94275 5.16844C3.86278 4.5 5.17519 4.5 7.8 4.5H12.2C14.8248 4.5 16.1372 4.5 17.0572 5.16844C17.3544 5.38432 17.6157 5.64562 17.8316 5.94275C18.5 6.86278 18.5 8.17519 18.5 10.8V26.2C18.5 28.8248 18.5 30.1372 17.8316 31.0572C17.6157 31.3544 17.3544 31.6157 17.0572 31.8316C16.1372 32.5 14.8248 32.5 12.2 32.5H7.8C5.17519 32.5 3.86278 32.5 2.94275 31.8316C2.64562 31.6157 2.38432 31.3544 2.16844 31.0572C1.5 30.1372 1.5 28.8248 1.5 26.2V10.8Z" fill="#2E3847"/> + <path d="M3 21.7C3 20.5799 3 20.0198 3.21799 19.592C3.40973 19.2157 3.71569 18.9097 4.09202 18.718C4.51984 18.5 5.0799 18.5 6.2 18.5H13.8C14.9201 18.5 15.4802 18.5 15.908 18.718C16.2843 18.9097 16.5903 19.2157 16.782 19.592C17 20.0198 17 20.5799 17 21.7V27.8C17 28.9201 17 29.4802 16.782 29.908C16.5903 30.2843 16.2843 30.5903 15.908 30.782C15.4802 31 14.9201 31 13.8 31H6.2C5.07989 31 4.51984 31 4.09202 30.782C3.71569 30.5903 3.40973 30.2843 3.21799 29.908C3 29.4802 3 28.9201 3 27.8V21.7Z" fill="#45AEF5"/> +</svg> diff --git a/packages/uikit/assets/icons/svg/44/ic-battery-100-44.svg b/packages/uikit/assets/icons/svg/44/ic-battery-100-44.svg new file mode 100644 index 000000000..a6d78c3e9 --- /dev/null +++ b/packages/uikit/assets/icons/svg/44/ic-battery-100-44.svg @@ -0,0 +1,4 @@ +<svg width="26" height="44" viewBox="0 0 26 44" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path opacity="0.64" fill-rule="evenodd" clip-rule="evenodd" d="M8.52545 4.01844C8.76576 1.75959 10.6774 0 13 0C15.3226 0 17.2342 1.75958 17.4745 4.01841C20.3278 4.07483 22.032 4.30472 23.3213 5.24145C23.8731 5.64236 24.3584 6.12764 24.7593 6.67945C26.0007 8.38808 26.0007 10.8254 26.0007 15.7001V32.3001C26.0007 37.1747 26.0007 39.612 24.7593 41.3207C24.3584 41.8725 23.8731 42.3577 23.3213 42.7587C21.6126 44.0001 19.1753 44.0001 14.3007 44.0001H11.7007C6.82602 44.0001 4.38869 44.0001 2.68007 42.7587C2.12825 42.3577 1.64298 41.8725 1.24206 41.3207C0.000671387 39.612 0.000671387 37.1747 0.000671387 32.3001V15.7001C0.000671387 10.8254 0.000671387 8.38808 1.24206 6.67945C1.64298 6.12764 2.12825 5.64236 2.68007 5.24145C3.96916 4.30487 5.67303 4.0749 8.52545 4.01844ZM1.50018 13.5001C1.50018 10.6998 1.50018 9.29966 2.04515 8.2301C2.52452 7.28929 3.28942 6.52439 4.23023 6.04502C5.29979 5.50006 6.69992 5.50006 9.50018 5.50006H16.5002C19.3004 5.50006 20.7006 5.50006 21.7701 6.04502C22.7109 6.52439 23.4758 7.28929 23.9552 8.2301C24.5002 9.29966 24.5002 10.6998 24.5002 13.5001V34.5001C24.5002 37.3003 24.5002 38.7005 23.9552 39.77C23.4758 40.7108 22.7109 41.4757 21.7701 41.9551C20.7006 42.5001 19.3004 42.5001 16.5002 42.5001H9.50018C6.69992 42.5001 5.29979 42.5001 4.23023 41.9551C3.28942 41.4757 2.52452 40.7108 2.04515 39.77C1.50018 38.7005 1.50018 37.3003 1.50018 34.5001V13.5001Z" fill="#556170"/> + <path d="M3 12.4C3 10.1502 3 9.02524 3.57295 8.23664C3.75799 7.98196 3.98196 7.75799 4.23664 7.57295C5.02524 7 6.15016 7 8.4 7H17.6C19.8498 7 20.9748 7 21.7634 7.57295C22.018 7.75799 22.242 7.98196 22.4271 8.23664C23 9.02524 23 10.1502 23 12.4V35.6C23 37.8498 23 38.9748 22.4271 39.7634C22.242 40.018 22.018 40.242 21.7634 40.4271C20.9748 41 19.8498 41 17.6 41H8.4C6.15016 41 5.02524 41 4.23664 40.4271C3.98196 40.242 3.75799 40.018 3.57295 39.7634C3 38.9748 3 37.8498 3 35.6V12.4Z" fill="#45AEF5"/> +</svg> diff --git a/packages/uikit/assets/icons/svg/44/ic-battery-25-44.svg b/packages/uikit/assets/icons/svg/44/ic-battery-25-44.svg new file mode 100644 index 000000000..ed92b106f --- /dev/null +++ b/packages/uikit/assets/icons/svg/44/ic-battery-25-44.svg @@ -0,0 +1,4 @@ +<svg width="26" height="44" viewBox="0 0 26 44" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path opacity="0.64" fill-rule="evenodd" clip-rule="evenodd" d="M8.52545 4.02045C8.76576 1.76161 10.6774 0.00201416 13 0.00201416C15.3226 0.00201416 17.2342 1.76159 17.4745 4.02042C20.3278 4.07684 22.032 4.30673 23.3213 5.24346C23.8731 5.64438 24.3584 6.12965 24.7593 6.68147C26.0007 8.39009 26.0007 10.8274 26.0007 15.7021V32.3021C26.0007 37.1767 26.0007 39.614 24.7593 41.3227C24.3584 41.8745 23.8731 42.3598 23.3213 42.7607C21.6126 44.0021 19.1753 44.0021 14.3007 44.0021H11.7007C6.82602 44.0021 4.38869 44.0021 2.68007 42.7607C2.12825 42.3598 1.64298 41.8745 1.24206 41.3227C0.000671387 39.614 0.000671387 37.1767 0.000671387 32.3021V15.7021C0.000671387 10.8274 0.000671387 8.39009 1.24206 6.68147C1.64298 6.12965 2.12825 5.64438 2.68007 5.24346C3.96916 4.30688 5.67303 4.07692 8.52545 4.02045ZM1.50018 13.5021C1.50018 10.7018 1.50018 9.30168 2.04515 8.23212C2.52452 7.29131 3.28942 6.52641 4.23023 6.04704C5.29979 5.50207 6.69992 5.50207 9.50018 5.50207H16.5002C19.3004 5.50207 20.7006 5.50207 21.7701 6.04704C22.7109 6.52641 23.4758 7.29131 23.9552 8.23212C24.5002 9.30168 24.5002 10.7018 24.5002 13.5021V34.5021C24.5002 37.3023 24.5002 38.7025 23.9552 39.772C23.4758 40.7128 22.7109 41.4777 21.7701 41.9571C20.7006 42.5021 19.3004 42.5021 16.5002 42.5021H9.50018C6.69992 42.5021 5.29979 42.5021 4.23023 41.9571C3.28942 41.4777 2.52452 40.7128 2.04515 39.772C1.50018 38.7025 1.50018 37.3023 1.50018 34.5021V13.5021Z" fill="#556170"/> + <path d="M3 36.002C3 37.3985 3 38.0967 3.19641 38.658C3.5482 39.6634 4.33866 40.4538 5.34402 40.8056C5.90533 41.002 6.60355 41.002 8 41.002H18C19.3964 41.002 20.0947 41.002 20.656 40.8056C21.6613 40.4538 22.4518 39.6634 22.8036 38.658C23 38.0967 23 37.3985 23 36.002V36.002C23 34.6056 23 33.9073 22.8036 33.346C22.4518 32.3407 21.6613 31.5502 20.656 31.1984C20.0947 31.002 19.3964 31.002 18 31.002H8C6.60355 31.002 5.90533 31.002 5.34402 31.1984C4.33866 31.5502 3.5482 32.3407 3.19641 33.346C3 33.9073 3 34.6056 3 36.002V36.002Z" fill="#45AEF5"/> +</svg> diff --git a/packages/uikit/assets/icons/svg/44/ic-battery-50-44.svg b/packages/uikit/assets/icons/svg/44/ic-battery-50-44.svg new file mode 100644 index 000000000..295f26ff8 --- /dev/null +++ b/packages/uikit/assets/icons/svg/44/ic-battery-50-44.svg @@ -0,0 +1,4 @@ +<svg width="26" height="44" viewBox="0 0 26 44" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path opacity="0.64" fill-rule="evenodd" clip-rule="evenodd" d="M8.52545 4.01941C8.76576 1.76057 10.6774 0.000976562 13 0.000976562C15.3226 0.000976562 17.2342 1.76055 17.4745 4.01938C20.3278 4.0758 22.032 4.30569 23.3213 5.24242C23.8731 5.64334 24.3584 6.12861 24.7593 6.68043C26.0007 8.38906 26.0007 10.8264 26.0007 15.701V32.301C26.0007 37.1757 26.0007 39.613 24.7593 41.3216C24.3584 41.8735 23.8731 42.3587 23.3213 42.7596C21.6126 44.001 19.1753 44.001 14.3007 44.001H11.7007C6.82602 44.001 4.38869 44.001 2.68007 42.7596C2.12825 42.3587 1.64298 41.8735 1.24206 41.3216C0.000671387 39.613 0.000671387 37.1757 0.000671387 32.301V15.701C0.000671387 10.8264 0.000671387 8.38906 1.24206 6.68043C1.64298 6.12861 2.12825 5.64334 2.68007 5.24242C3.96916 4.30584 5.67303 4.07588 8.52545 4.01941ZM1.50018 13.501C1.50018 10.7008 1.50018 9.30064 2.04515 8.23108C2.52452 7.29027 3.28942 6.52537 4.23023 6.046C5.29979 5.50103 6.69992 5.50103 9.50018 5.50103H16.5002C19.3004 5.50103 20.7006 5.50103 21.7701 6.046C22.7109 6.52537 23.4758 7.29027 23.9552 8.23108C24.5002 9.30064 24.5002 10.7008 24.5002 13.501V34.501C24.5002 37.3013 24.5002 38.7014 23.9552 39.771C23.4758 40.7118 22.7109 41.4767 21.7701 41.9561C20.7006 42.501 19.3004 42.501 16.5002 42.501H9.50018C6.69992 42.501 5.29979 42.501 4.23023 41.9561C3.28942 41.4767 2.52452 40.7118 2.04515 39.771C1.50018 38.7014 1.50018 37.3013 1.50018 34.501V13.501Z" fill="#556170"/> + <path d="M3 30.301C3 27.6762 3 26.3638 3.66844 25.4437C3.88432 25.1466 4.14562 24.8853 4.44275 24.6694C5.36278 24.001 6.67519 24.001 9.3 24.001H16.7C19.3248 24.001 20.6372 24.001 21.5572 24.6694C21.8544 24.8853 22.1157 25.1466 22.3316 25.4437C23 26.3638 23 27.6762 23 30.301V34.701C23 37.3258 23 38.6382 22.3316 39.5582C22.1157 39.8554 21.8544 40.1167 21.5572 40.3325C20.6372 41.001 19.3248 41.001 16.7 41.001H9.3C6.67519 41.001 5.36278 41.001 4.44275 40.3325C4.14562 40.1167 3.88432 39.8554 3.66844 39.5582C3 38.6382 3 37.3258 3 34.701V30.301Z" fill="#45AEF5"/> +</svg> diff --git a/packages/uikit/src/components/AnimatedBatteryIcon.tsx b/packages/uikit/src/components/AnimatedBatteryIcon.tsx new file mode 100644 index 000000000..95ea409ed --- /dev/null +++ b/packages/uikit/src/components/AnimatedBatteryIcon.tsx @@ -0,0 +1,152 @@ +import { View } from './View'; +import Svg, { Path } from 'react-native-svg'; +import Animated, { + interpolate, + useAnimatedStyle, + withTiming, +} from 'react-native-reanimated'; +import { Steezy } from '../styles'; + +export enum AnimatedBatterySize { + Large = 'large', + Medium = 'medium', + Small = 'small', + ExtraSmall = 'extra-small', +} + +export interface AnimatedBatteryIconProps { + size: AnimatedBatterySize; + progress: number; +} + +const mapIconConfigBySize = { + [AnimatedBatterySize.Large]: { + width: 68, + height: 114, + top: 18, + bottom: 8, + left: 8, + right: 8, + borderRadius: 8, + }, + [AnimatedBatterySize.Small]: { + width: 20, + height: 34, + bottom: 3, + left: 3, + right: 3, + top: 6, + borderRadius: 2, + }, +}; + +export interface MappedSvgComponentProps { + size: AnimatedBatterySize; +} + +export function MappedSvgComponent(props: MappedSvgComponentProps) { + switch (props.size) { + case AnimatedBatterySize.Small: + return ( + <Svg width="20" height="34" viewBox="0 0 20 34" fill="none"> + <Path + fillRule="evenodd" + clipRule="evenodd" + d="M6.5331 3.01463C6.76939 1.31135 8.23141 0 9.99971 0C11.768 0 13.23 1.31135 13.4663 3.01461C15.6462 3.05886 16.9509 3.23708 17.9389 3.95492C18.3634 4.26331 18.7367 4.6366 19.0451 5.06107C20 6.3754 20 8.25027 20 12V25C20 28.7497 20 30.6246 19.0451 31.9389C18.7367 32.3634 18.3634 32.7367 17.9389 33.0451C16.6246 34 14.7497 34 11 34H9C5.25027 34 3.3754 34 2.06107 33.0451C1.6366 32.7367 1.26331 32.3634 0.954915 31.9389C0 30.6246 0 28.7497 0 25V12C0 8.25027 0 6.3754 0.954915 5.06107C1.26331 4.6366 1.6366 4.26331 2.06107 3.95492C3.049 3.23715 4.35362 3.05889 6.5331 3.01463ZM1.5 10.8C1.5 8.17519 1.5 6.86278 2.16844 5.94275C2.38432 5.64562 2.64562 5.38432 2.94275 5.16844C3.86278 4.5 5.17519 4.5 7.8 4.5H12.2C14.8248 4.5 16.1372 4.5 17.0572 5.16844C17.3544 5.38432 17.6157 5.64562 17.8316 5.94275C18.5 6.86278 18.5 8.17519 18.5 10.8V26.2C18.5 28.8248 18.5 30.1372 17.8316 31.0572C17.6157 31.3544 17.3544 31.6157 17.0572 31.8316C16.1372 32.5 14.8248 32.5 12.2 32.5H7.8C5.17519 32.5 3.86278 32.5 2.94275 31.8316C2.64562 31.6157 2.38432 31.3544 2.16844 31.0572C1.5 30.1372 1.5 28.8248 1.5 26.2V10.8Z" + fill="#2E3847" + /> + </Svg> + ); + case AnimatedBatterySize.Large: + return ( + <Svg width="68" height="114" viewBox="0 0 68 114" fill="none"> + <Path + fillRule="evenodd" + clipRule="evenodd" + d="M23.0435 10.0162C23.5411 4.40199 28.2565 0 34.0001 0C39.7437 0 44.4591 4.40199 44.9567 10.0162C53.2387 10.0931 57.9328 10.5333 61.4046 13.0557C62.7629 14.0426 63.9574 15.2371 64.9443 16.5954C68 20.8013 68 26.8009 68 38.8V85.2C68 97.1991 68 103.199 64.9443 107.405C63.9574 108.763 62.7629 109.957 61.4046 110.944C57.1987 114 51.1991 114 39.2 114H28.8C16.8009 114 10.8013 114 6.59544 110.944C5.23712 109.957 4.0426 108.763 3.05573 107.405C0 103.199 0 97.1991 0 85.2V38.8C0 26.8009 0 20.8013 3.05573 16.5954C4.0426 15.2371 5.23712 14.0426 6.59544 13.0557C10.0673 10.5333 14.7614 10.0931 23.0435 10.0162ZM4 35.6C4 26.6006 4 22.101 6.2918 18.9466C7.03195 17.9278 7.92784 17.032 8.94658 16.2918C12.101 14 16.6006 14 25.6 14H42.4C51.3994 14 55.899 14 59.0534 16.2918C60.0722 17.032 60.968 17.9278 61.7082 18.9466C64 22.101 64 26.6006 64 35.6V88.4C64 97.3994 64 101.899 61.7082 105.053C60.968 106.072 60.0722 106.968 59.0534 107.708C55.899 110 51.3994 110 42.4 110H25.6C16.6006 110 12.101 110 8.94658 107.708C7.92784 106.968 7.03195 106.072 6.2918 105.053C4 101.899 4 97.3994 4 88.4V35.6Z" + fill="#2E3847" + /> + </Svg> + ); + default: + return null; + } +} + +const inRange = (value: number, start: number, end: number) => + Math.min(end, Math.max(start, value)); + +const MIN_PROGRESS_RANGE = 0.14; + +export function AnimatedBatteryIcon(props: AnimatedBatteryIconProps) { + const iconConfig = mapIconConfigBySize[props.size]; + const bodyStyle = Steezy.useStyle(styles.batteryBody); + const emptyBodyStyle = Steezy.useStyle(styles.emptyBatteryBody); + const progress = inRange(props.progress ?? 0, MIN_PROGRESS_RANGE, 1); + + const batteryBodyAnimatedStyle = useAnimatedStyle( + () => ({ + height: withTiming( + interpolate( + progress, + [0, 1], + [0, iconConfig.height - iconConfig.top - iconConfig.bottom], + ), + { duration: 400 }, + ), + }), + [iconConfig, progress], + ); + + return ( + <View + style={[ + styles.relativeContainer, + { height: iconConfig.height, width: iconConfig.width }, + ]} + > + <MappedSvgComponent size={props.size} /> + <View + style={[ + styles.batteryBodyContainer, + { + position: 'absolute', + left: iconConfig.left, + right: iconConfig.right, + bottom: iconConfig.bottom, + top: iconConfig.top, + justifyContent: 'flex-end', + }, + ]} + > + <Animated.View + style={[ + bodyStyle, + { + width: '100%', + borderRadius: iconConfig.borderRadius, + }, + batteryBodyAnimatedStyle, + progress === MIN_PROGRESS_RANGE && emptyBodyStyle, + ]} + /> + </View> + </View> + ); +} + +const styles = Steezy.create(({ colors }) => ({ + relativeContainer: { + position: 'relative', + }, + batteryBodyContainer: { + position: 'absolute', + justifyContent: 'flex-end', + }, + batteryBody: { + backgroundColor: colors.accentBlue, + }, + emptyBatteryBody: { + backgroundColor: colors.accentOrange, + }, +})); diff --git a/packages/uikit/src/components/Icon/Icon.tsx b/packages/uikit/src/components/Icon/Icon.tsx index 9bc71bb4a..1df0c98f7 100644 --- a/packages/uikit/src/components/Icon/Icon.tsx +++ b/packages/uikit/src/components/Icon/Icon.tsx @@ -50,7 +50,7 @@ export const Icon = memo((props: IconProps) => { if (icon) { return ( - <View style={[style, sizeStyle]}> + <View style={[sizeStyle, style]}> <Image style={[imageStyle, sizeStyle, customImageStyle]} source={icon} /> </View> ); diff --git a/packages/uikit/src/components/Icon/Icon.types.ts b/packages/uikit/src/components/Icon/Icon.types.ts index dc24385ab..214cc7aea 100644 --- a/packages/uikit/src/components/Icon/Icon.types.ts +++ b/packages/uikit/src/components/Icon/Icon.types.ts @@ -15,6 +15,7 @@ export type IconNames = | 'ic-full-battery-128' | 'ic-gear-128' | 'ic-lock-128' + | 'ic-medium-battery-128' | 'ic-notification-128' | 'ic-appearance-16' | 'ic-arrow-down-16' @@ -52,6 +53,7 @@ export type IconNames = | 'ic-xmark-circle-16' | 'ic-backspace-bold-24' | 'ic-backup-24' + | 'ic-battery-almost-empty-24' | 'ic-key-24' | 'ic-reset-24' | 'ic-ton-disabled-24' @@ -122,10 +124,18 @@ export type IconNames = | 'ic-xmark-outline-28' | 'ic-checkmark-circle-32' | 'ic-exclamationmark-circle-32' + | 'ic-almost-empty-battery-34' + | 'ic-empty-battery-accent-flash-34' + | 'ic-empty-battery-flash-34' + | 'ic-full-battery-34' + | 'ic-medium-battery-34' | 'ic-delete-36' | 'ic-exclamationmark-triangle-36' | 'ic-faceid-36' | 'ic-fingerprint-36' + | 'ic-battery-100-44' + | 'ic-battery-25-44' + | 'ic-battery-50-44' | 'ic-cards-stack-44' | 'ic-ton-old-wallets-44' | 'ic-logo-48' @@ -161,6 +171,7 @@ export const AllIcons = [ 'ic-full-battery-128', 'ic-gear-128', 'ic-lock-128', + 'ic-medium-battery-128', 'ic-notification-128', 'ic-appearance-16', 'ic-arrow-down-16', @@ -198,6 +209,7 @@ export const AllIcons = [ 'ic-xmark-circle-16', 'ic-backspace-bold-24', 'ic-backup-24', + 'ic-battery-almost-empty-24', 'ic-key-24', 'ic-reset-24', 'ic-ton-disabled-24', @@ -268,10 +280,18 @@ export const AllIcons = [ 'ic-xmark-outline-28', 'ic-checkmark-circle-32', 'ic-exclamationmark-circle-32', + 'ic-almost-empty-battery-34', + 'ic-empty-battery-accent-flash-34', + 'ic-empty-battery-flash-34', + 'ic-full-battery-34', + 'ic-medium-battery-34', 'ic-delete-36', 'ic-exclamationmark-triangle-36', 'ic-faceid-36', 'ic-fingerprint-36', + 'ic-battery-100-44', + 'ic-battery-25-44', + 'ic-battery-50-44', 'ic-cards-stack-44', 'ic-ton-old-wallets-44', 'ic-logo-48', @@ -308,6 +328,7 @@ export const IconSizes = { 'ic-full-battery-128': 128, 'ic-gear-128': 128, 'ic-lock-128': 128, + 'ic-medium-battery-128': 128, 'ic-notification-128': 128, 'ic-appearance-16': 16, 'ic-arrow-down-16': 16, @@ -345,6 +366,7 @@ export const IconSizes = { 'ic-xmark-circle-16': 16, 'ic-backspace-bold-24': 24, 'ic-backup-24': 24, + 'ic-battery-almost-empty-24': 24, 'ic-key-24': 24, 'ic-reset-24': 24, 'ic-ton-disabled-24': 24, @@ -415,10 +437,18 @@ export const IconSizes = { 'ic-xmark-outline-28': 28, 'ic-checkmark-circle-32': 32, 'ic-exclamationmark-circle-32': 32, + 'ic-almost-empty-battery-34': 34, + 'ic-empty-battery-accent-flash-34': 34, + 'ic-empty-battery-flash-34': 34, + 'ic-full-battery-34': 34, + 'ic-medium-battery-34': 34, 'ic-delete-36': 36, 'ic-exclamationmark-triangle-36': 36, 'ic-faceid-36': 36, 'ic-fingerprint-36': 36, + 'ic-battery-100-44': 100, + 'ic-battery-25-44': 25, + 'ic-battery-50-44': 50, 'ic-cards-stack-44': 44, 'ic-ton-old-wallets-44': 44, 'ic-logo-48': 48, diff --git a/packages/uikit/src/components/Icon/IconList.native.ts b/packages/uikit/src/components/Icon/IconList.native.ts index 54c933e42..e6219fc78 100644 --- a/packages/uikit/src/components/Icon/IconList.native.ts +++ b/packages/uikit/src/components/Icon/IconList.native.ts @@ -15,6 +15,7 @@ export const IconList = { 'ic-full-battery-128': require('../../../assets/icons/png/ic-full-battery-128.png'), 'ic-gear-128': require('../../../assets/icons/png/ic-gear-128.png'), 'ic-lock-128': require('../../../assets/icons/png/ic-lock-128.png'), + 'ic-medium-battery-128': require('../../../assets/icons/png/ic-medium-battery-128.png'), 'ic-notification-128': require('../../../assets/icons/png/ic-notification-128.png'), 'ic-appearance-16': require('../../../assets/icons/png/ic-appearance-16.png'), 'ic-arrow-down-16': require('../../../assets/icons/png/ic-arrow-down-16.png'), @@ -52,6 +53,7 @@ export const IconList = { 'ic-xmark-circle-16': require('../../../assets/icons/png/ic-xmark-circle-16.png'), 'ic-backspace-bold-24': require('../../../assets/icons/png/ic-backspace-bold-24.png'), 'ic-backup-24': require('../../../assets/icons/png/ic-backup-24.png'), + 'ic-battery-almost-empty-24': require('../../../assets/icons/png/ic-battery-almost-empty-24.png'), 'ic-key-24': require('../../../assets/icons/png/ic-key-24.png'), 'ic-reset-24': require('../../../assets/icons/png/ic-reset-24.png'), 'ic-ton-disabled-24': require('../../../assets/icons/png/ic-ton-disabled-24.png'), @@ -122,10 +124,18 @@ export const IconList = { 'ic-xmark-outline-28': require('../../../assets/icons/png/ic-xmark-outline-28.png'), 'ic-checkmark-circle-32': require('../../../assets/icons/png/ic-checkmark-circle-32.png'), 'ic-exclamationmark-circle-32': require('../../../assets/icons/png/ic-exclamationmark-circle-32.png'), + 'ic-almost-empty-battery-34': require('../../../assets/icons/png/ic-almost-empty-battery-34.png'), + 'ic-empty-battery-accent-flash-34': require('../../../assets/icons/png/ic-empty-battery-accent-flash-34.png'), + 'ic-empty-battery-flash-34': require('../../../assets/icons/png/ic-empty-battery-flash-34.png'), + 'ic-full-battery-34': require('../../../assets/icons/png/ic-full-battery-34.png'), + 'ic-medium-battery-34': require('../../../assets/icons/png/ic-medium-battery-34.png'), 'ic-delete-36': require('../../../assets/icons/png/ic-delete-36.png'), 'ic-exclamationmark-triangle-36': require('../../../assets/icons/png/ic-exclamationmark-triangle-36.png'), 'ic-faceid-36': require('../../../assets/icons/png/ic-faceid-36.png'), 'ic-fingerprint-36': require('../../../assets/icons/png/ic-fingerprint-36.png'), + 'ic-battery-100-44': require('../../../assets/icons/png/ic-battery-100-44.png'), + 'ic-battery-25-44': require('../../../assets/icons/png/ic-battery-25-44.png'), + 'ic-battery-50-44': require('../../../assets/icons/png/ic-battery-50-44.png'), 'ic-cards-stack-44': require('../../../assets/icons/png/ic-cards-stack-44.png'), 'ic-ton-old-wallets-44': require('../../../assets/icons/png/ic-ton-old-wallets-44.png'), 'ic-logo-48': require('../../../assets/icons/png/ic-logo-48.png'), diff --git a/packages/uikit/src/components/List/ListItem.tsx b/packages/uikit/src/components/List/ListItem.tsx index 01fbb995e..5ffa5d09a 100644 --- a/packages/uikit/src/components/List/ListItem.tsx +++ b/packages/uikit/src/components/List/ListItem.tsx @@ -15,6 +15,7 @@ import { View } from '../View'; export interface ListItemProps { titleType?: 'primary' | 'secondary'; title?: string | React.ReactNode; + titleContainerStyle?: StyleProp<ViewStyle>; titleTextType?: TTextTypes; subtitle?: string | React.ReactNode; subtitleStyle?: StyleProp<TextStyle>; @@ -37,6 +38,7 @@ export interface ListItemProps { rightContent?: React.ReactNode; valueMultiline?: boolean; onPress?: () => void; + disabled?: boolean; } function isString<T>(str: T) { @@ -45,6 +47,7 @@ function isString<T>(str: T) { export const ListItem = memo<ListItemProps>((props) => { const { + disabled, onPress, navigate, chevronColor = 'iconTertiary', @@ -88,7 +91,7 @@ export const ListItem = memo<ListItemProps>((props) => { return ( <TouchableComponent underlayColor={theme.backgroundContentTint} - disabled={!onPress && !navigate} + disabled={disabled || (!onPress && !navigate)} onPressOut={onPressOut} onPressIn={onPressIn} onPress={handlePress} @@ -113,7 +116,7 @@ export const ListItem = memo<ListItemProps>((props) => { )} <View style={styles.lines}> <View style={styles.topLine}> - <View style={styles.titleContainer}> + <View style={[styles.titleContainer, props.titleContainerStyle]}> {isString(props.title) ? ( <Text type={ diff --git a/packages/uikit/src/components/Text/TextStyles.ts b/packages/uikit/src/components/Text/TextStyles.ts index 3f6ed6bce..3675e2a68 100644 --- a/packages/uikit/src/components/Text/TextStyles.ts +++ b/packages/uikit/src/components/Text/TextStyles.ts @@ -31,6 +31,11 @@ export const TextTypes = StyleSheet.create({ lineHeight: nfs(36), fontFamily: Font.Medium, }, + num3: { + fontSize: nfs(44), + lineHeight: nfs(56), + fontFamily: Font.Medium, + }, h1: { fontSize: nfs(32), lineHeight: nfs(40), diff --git a/packages/uikit/src/index.ts b/packages/uikit/src/index.ts index 0559047b9..9958c728b 100644 --- a/packages/uikit/src/index.ts +++ b/packages/uikit/src/index.ts @@ -28,6 +28,7 @@ export { TransitionOpacity } from './components/TransitionOpacity'; export * from './components/Flash'; export * from './components/BlockingLoader'; export { Switch } from './components/Switch'; +export * from './components/AnimatedBatteryIcon'; // Containers export { HeaderButtonHitSlop } from './containers/Screen/utils/constants';