From adde2eeaa28fc1c7f2e0be5433f2f1a0c3fd3d35 Mon Sep 17 00:00:00 2001 From: mytonwalletorg Date: Thu, 21 Dec 2023 04:11:45 +0100 Subject: [PATCH] v1.17.0 --- .env.example | 7 +- .eslintignore | 3 + .eslintrc | 1 + .github/workflows/package-and-publish.yml | 187 +- ...statoscope-upload-reference-statistics.yml | 2 +- .github/workflows/statoscope.yml | 2 +- .gitignore | 1 + .stylelintrc.json | 4 +- capacitor.config.ts | 36 + changelogs/.gitkeep | 0 changelogs/1.17.0.txt | 1 + deploy/update_version.js | 21 + mobile/Gemfile | 6 + mobile/Gemfile.lock | 218 + mobile/android/.gitignore | 109 + mobile/android/app/.gitignore | 2 + mobile/android/app/build.gradle | 55 + mobile/android/app/capacitor.build.gradle | 27 + mobile/android/app/proguard-rules.pro | 21 + .../myapp/ExampleInstrumentedTest.java | 26 + .../android/app/src/main/AndroidManifest.xml | 61 + .../app/src/main/ic_launcher-playstore.png | Bin 0 -> 129639 bytes .../org/mytonwallet/app/MainActivity.java | 49 + .../src/main/res/drawable-hdpi/splash.9.png | Bin 0 -> 23686 bytes .../src/main/res/drawable-ldpi/splash.9.png | Bin 0 -> 7019 bytes .../src/main/res/drawable-mdpi/splash.9.png | Bin 0 -> 12112 bytes .../main/res/drawable-night-hdpi/splash.9.png | Bin 0 -> 24677 bytes .../main/res/drawable-night-ldpi/splash.9.png | Bin 0 -> 7265 bytes .../main/res/drawable-night-mdpi/splash.9.png | Bin 0 -> 12478 bytes .../res/drawable-night-xhdpi/splash.9.png | Bin 0 -> 48470 bytes .../res/drawable-night-xxhdpi/splash.9.png | Bin 0 -> 70502 bytes .../res/drawable-night-xxxhdpi/splash.9.png | Bin 0 -> 96417 bytes .../src/main/res/drawable-night/splash.9.png | Bin 0 -> 7510 bytes .../drawable-v24/ic_launcher_foreground.xml | 34 + .../src/main/res/drawable-xhdpi/splash.9.png | Bin 0 -> 46255 bytes .../src/main/res/drawable-xxhdpi/splash.9.png | Bin 0 -> 67033 bytes .../main/res/drawable-xxxhdpi/splash.9.png | Bin 0 -> 90091 bytes .../res/drawable/ic_launcher_background.xml | 170 + .../app/src/main/res/drawable/icon_svg.xml | 29 + .../src/main/res/drawable/launch_splash.xml | 6 + .../app/src/main/res/drawable/splash.9.png | Bin 0 -> 12115 bytes .../app/src/main/res/layout/activity_main.xml | 12 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 4400 bytes .../mipmap-hdpi/ic_launcher_background.webp | Bin 0 -> 2474 bytes .../mipmap-hdpi/ic_launcher_foreground.webp | Bin 0 -> 2822 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 6444 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 2562 bytes .../mipmap-mdpi/ic_launcher_background.webp | Bin 0 -> 684 bytes .../mipmap-mdpi/ic_launcher_foreground.webp | Bin 0 -> 1650 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 3682 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 6320 bytes .../mipmap-xhdpi/ic_launcher_background.webp | Bin 0 -> 3398 bytes .../mipmap-xhdpi/ic_launcher_foreground.webp | Bin 0 -> 4024 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 9576 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 10540 bytes .../mipmap-xxhdpi/ic_launcher_background.webp | Bin 0 -> 18544 bytes .../mipmap-xxhdpi/ic_launcher_foreground.webp | Bin 0 -> 6428 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 15444 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 13916 bytes .../ic_launcher_background.webp | Bin 0 -> 40804 bytes .../ic_launcher_foreground.webp | Bin 0 -> 9000 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 21816 bytes .../app/src/main/res/values-v31/styles.xml | 8 + .../res/values/ic_launcher_background.xml | 4 + .../app/src/main/res/values/strings.xml | 8 + .../app/src/main/res/values/styles.xml | 22 + .../app/src/main/res/xml/file_paths.xml | 5 + .../getcapacitor/myapp/ExampleUnitTest.java | 18 + mobile/android/build.gradle | 29 + mobile/android/capacitor.settings.gradle | 30 + mobile/android/fastlane/Appfile | 2 + mobile/android/fastlane/Fastfile | 78 + mobile/android/fastlane/Pluginfile | 5 + mobile/android/gradle.properties | 22 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61608 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + mobile/android/gradlew | 244 + mobile/android/gradlew.bat | 92 + mobile/android/settings.gradle | 5 + mobile/android/variables.gradle | 17 + mobile/ios/.gitignore | 20 + mobile/ios/App/App.xcodeproj/project.pbxproj | 425 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/MyTonWallet.xcscheme | 77 + .../App.xcworkspace/contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 5 + mobile/ios/App/App/AppDelegate.swift | 49 + .../AppIcon.appiconset/AppIcon-20x20@1x.png | Bin 0 -> 877 bytes .../AppIcon.appiconset/AppIcon-20x20@2x-1.png | Bin 0 -> 2736 bytes .../AppIcon.appiconset/AppIcon-20x20@2x.png | Bin 0 -> 2736 bytes .../AppIcon.appiconset/AppIcon-20x20@3x.png | Bin 0 -> 4795 bytes .../AppIcon.appiconset/AppIcon-29x29@1x.png | Bin 0 -> 1695 bytes .../AppIcon.appiconset/AppIcon-29x29@2x-1.png | Bin 0 -> 4526 bytes .../AppIcon.appiconset/AppIcon-29x29@2x.png | Bin 0 -> 4526 bytes .../AppIcon.appiconset/AppIcon-29x29@3x.png | Bin 0 -> 8611 bytes .../AppIcon.appiconset/AppIcon-40x40@1x.png | Bin 0 -> 2736 bytes .../AppIcon.appiconset/AppIcon-40x40@2x-1.png | Bin 0 -> 7474 bytes .../AppIcon.appiconset/AppIcon-40x40@2x.png | Bin 0 -> 7474 bytes .../AppIcon.appiconset/AppIcon-40x40@3x.png | Bin 0 -> 13238 bytes .../AppIcon.appiconset/AppIcon-512@2x.png | Bin 0 -> 138786 bytes .../AppIcon.appiconset/AppIcon-60x60@2x.png | Bin 0 -> 13238 bytes .../AppIcon.appiconset/AppIcon-60x60@3x.png | Bin 0 -> 22872 bytes .../AppIcon.appiconset/AppIcon-76x76@1x.png | Bin 0 -> 6930 bytes .../AppIcon.appiconset/AppIcon-76x76@2x.png | Bin 0 -> 18661 bytes .../AppIcon-83.5x83.5@2x.png | Bin 0 -> 20822 bytes .../AppIcon.appiconset/Contents.json | 122 + .../ios/App/App/Assets.xcassets/Contents.json | 6 + .../Splash.imageset/Contents.json | 56 + .../Default@1x~universal~anyany-dark.png | Bin 0 -> 12772 bytes .../Default@1x~universal~anyany.png | Bin 0 -> 12367 bytes .../Default@2x~universal~anyany-dark.png | Bin 0 -> 19987 bytes .../Default@2x~universal~anyany.png | Bin 0 -> 19395 bytes .../Default@3x~universal~anyany-dark.png | Bin 0 -> 39467 bytes .../Default@3x~universal~anyany.png | Bin 0 -> 37691 bytes .../App/Base.lproj/LaunchScreen.storyboard | 32 + mobile/ios/App/App/Base.lproj/Main.storyboard | 19 + mobile/ios/App/App/Info.plist | 67 + mobile/ios/App/MyTonWallet.entitlements | 10 + mobile/ios/App/Podfile | 44 + mobile/ios/App/Podfile.lock | 168 + mobile/ios/App/fastlane/Appfile | 8 + mobile/ios/App/fastlane/Fastfile | 107 + .../capacitor-splash-screen/.eslintignore | 2 + .../capacitor-splash-screen/.gitignore | 61 + .../capacitor-splash-screen/.prettierignore | 2 + .../capacitor-splash-screen/CHANGELOG.md | 488 ++ .../CapacitorSplashScreen.podspec | 17 + .../plugins/capacitor-splash-screen/LICENSE | 23 + .../plugins/capacitor-splash-screen/README.md | 262 + .../android/.gitignore | 1 + .../android/build.gradle | 81 + .../android/gradle.properties | 22 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 62076 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../capacitor-splash-screen/android/gradlew | 245 + .../android/gradlew.bat | 92 + .../android/proguard-rules.pro | 21 + .../android/settings.gradle | 2 + .../android/ExampleInstrumentedTest.java | 26 + .../android/src/main/AndroidManifest.xml | 2 + .../plugins/splashscreen/SplashListener.java | 6 + .../plugins/splashscreen/SplashScreen.java | 694 +++ .../splashscreen/SplashScreenConfig.java | 129 + .../splashscreen/SplashScreenPlugin.java | 166 + .../splashscreen/SplashScreenSettings.java | 41 + .../android/src/main/res/.gitkeep | 0 .../android/src/main/res/values/styles.xml | 11 + .../com/getcapacitor/ExampleUnitTest.java | 18 + .../capacitor-splash-screen/dist/docs.json | 416 ++ .../dist/esm/definitions.d.ts | 215 + .../dist/esm/definitions.js | 3 + .../dist/esm/definitions.js.map | 1 + .../dist/esm/index.d.ts | 4 + .../capacitor-splash-screen/dist/esm/index.js | 7 + .../dist/esm/index.js.map | 1 + .../capacitor-splash-screen/dist/esm/web.d.ts | 6 + .../capacitor-splash-screen/dist/esm/web.js | 10 + .../dist/esm/web.js.map | 1 + .../ios/Plugin.xcodeproj/project.pbxproj | 579 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Plugin.xcscheme | 77 + .../xcschemes/PluginTests.xcscheme | 68 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../ios/Plugin/Info.plist | 24 + .../ios/Plugin/SplashScreen.swift | 161 + .../ios/Plugin/SplashScreenConfig.swift | 11 + .../ios/Plugin/SplashScreenPlugin.h | 10 + .../ios/Plugin/SplashScreenPlugin.m | 9 + .../ios/Plugin/SplashScreenPlugin.swift | 82 + .../ios/Plugin/SplashScreenSettings.swift | 8 + .../ios/PluginTests/Info.plist | 22 + .../PluginTests/SplashScreenPluginTests.swift | 14 + .../capacitor-splash-screen/ios/Podfile | 16 + .../capacitor-splash-screen/package-lock.json | 4152 +++++++++++++ .../capacitor-splash-screen/package.json | 80 + .../capacitor-splash-screen/rollup.config.js | 22 + .../src/definitions.ts | 249 + .../capacitor-splash-screen/src/index.ts | 10 + .../capacitor-splash-screen/src/web.ts | 17 + .../capacitor-splash-screen/tsconfig.json | 20 + .../plugins/native-bottom-sheet/.eslintignore | 2 + mobile/plugins/native-bottom-sheet/.gitignore | 61 + .../native-bottom-sheet/.prettierignore | 2 + .../native-bottom-sheet/CONTRIBUTING.md | 52 + .../NativeBottomSheet.podspec | 18 + mobile/plugins/native-bottom-sheet/README.md | 248 + .../native-bottom-sheet/android/.gitignore | 1 + .../native-bottom-sheet/android/build.gradle | 58 + .../android/gradle.properties | 22 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61608 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../native-bottom-sheet/android/gradlew | 244 + .../native-bottom-sheet/android/gradlew.bat | 92 + .../android/proguard-rules.pro | 21 + .../android/settings.gradle | 2 + .../android/ExampleInstrumentedTest.java | 26 + .../android/src/main/AndroidManifest.xml | 2 + .../nativebottomsheet/BottomSheet.java | 11 + .../nativebottomsheet/BottomSheetPlugin.java | 22 + .../android/src/main/res/.gitkeep | 0 .../com/getcapacitor/ExampleUnitTest.java | 18 + .../native-bottom-sheet/dist/docs.json | 377 ++ .../dist/esm/definitions.d.ts | 50 + .../dist/esm/definitions.js | 2 + .../dist/esm/definitions.js.map | 1 + .../native-bottom-sheet/dist/esm/index.d.ts | 4 + .../native-bottom-sheet/dist/esm/index.js | 5 + .../native-bottom-sheet/dist/esm/index.js.map | 1 + .../ios/Plugin.xcodeproj/project.pbxproj | 573 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Plugin.xcscheme | 77 + .../xcschemes/PluginTests.xcscheme | 68 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../ios/Plugin/BottomSheetPlugin.h | 10 + .../ios/Plugin/BottomSheetPlugin.m | 16 + .../ios/Plugin/BottomSheetPlugin.swift | 394 ++ .../native-bottom-sheet/ios/Plugin/Info.plist | 24 + .../ios/PluginTests/BottomSheetTests.swift | 16 + .../ios/PluginTests/Info.plist | 22 + .../plugins/native-bottom-sheet/ios/Podfile | 18 + .../native-bottom-sheet/package-lock.json | 3452 +++++++++++ .../plugins/native-bottom-sheet/package.json | 78 + .../native-bottom-sheet/rollup.config.js | 22 + .../native-bottom-sheet/src/definitions.ts | 66 + .../plugins/native-bottom-sheet/src/index.ts | 8 + .../plugins/native-bottom-sheet/tsconfig.json | 20 + package-lock.json | 4046 ++++++++++--- package.json | 36 +- public/electronVersion.txt | 1 + .../.well-known/apple-app-site-association | 11 + public/get/.well-known/assetlinks.json | 15 + public/logo.svg | 2 +- public/version.txt | 1 + src/api/blockchains/ton/address.ts | 4 + src/api/blockchains/ton/constants.ts | 19 +- src/api/blockchains/ton/index.ts | 14 +- src/api/blockchains/ton/nfts.ts | 31 +- src/api/blockchains/ton/other.ts | 12 + src/api/blockchains/ton/staking.ts | 336 +- src/api/blockchains/ton/tokens.ts | 18 +- src/api/blockchains/ton/transactions.ts | 413 +- src/api/blockchains/ton/types.ts | 8 +- .../ton/util/CustomHttpProvider.ts | 35 +- src/api/blockchains/ton/util/index.ts | 6 +- src/api/blockchains/ton/util/metadata.ts | 112 +- src/api/blockchains/ton/util/tonapiio.ts | 12 +- src/api/blockchains/ton/util/tonweb.ts | 168 +- src/api/blockchains/ton/wallet.ts | 46 +- src/api/common/accounts.ts | 24 +- src/api/common/backend.ts | 15 +- src/api/common/helpers.ts | 99 +- src/api/common/utils.ts | 17 + src/api/db.ts | 24 + src/api/environment.ts | 4 +- src/api/errors.ts | 25 +- src/api/extensionMethods/init.ts | 2 +- src/api/extensionMethods/legacy.ts | 18 +- src/api/hooks.ts | 1 + src/api/methods/accounts.ts | 10 +- src/api/methods/auth.ts | 86 +- src/api/methods/index.ts | 3 + src/api/methods/init.ts | 7 +- src/api/methods/nfts.ts | 49 + src/api/methods/other.ts | 47 + src/api/methods/polling.ts | 343 +- src/api/methods/prices.ts | 12 + src/api/methods/staking.ts | 130 +- src/api/methods/swap.ts | 280 + src/api/methods/tokens.ts | 11 +- src/api/methods/transactions.ts | 67 +- src/api/methods/wallet.ts | 11 +- .../providers/extension/connectorForPopup.ts | 1 - src/api/providers/worker/connector.ts | 1 - src/api/storages/idb.ts | 21 +- src/api/storages/types.ts | 5 +- src/api/tonConnect/index.ts | 66 +- src/api/tonConnect/sse.ts | 31 +- src/api/types/activity.ts | 9 +- src/api/types/backend.ts | 137 + src/api/types/errors.ts | 9 +- src/api/types/index.ts | 1 + src/api/types/misc.ts | 48 +- src/api/types/payload.ts | 50 +- src/api/types/storage.ts | 7 +- src/api/types/updates.ts | 55 +- src/assets/blockchain/chain_avalanche.png | Bin 0 -> 1810 bytes src/assets/blockchain/chain_bitcoin.png | Bin 0 -> 2186 bytes src/assets/blockchain/chain_bitcoincash.png | Bin 0 -> 2225 bytes src/assets/blockchain/chain_bnb.png | Bin 0 -> 2557 bytes src/assets/blockchain/chain_cardano.png | Bin 0 -> 2978 bytes src/assets/blockchain/chain_cosmos.png | Bin 0 -> 4162 bytes src/assets/blockchain/chain_dash.png | Bin 0 -> 1917 bytes src/assets/blockchain/chain_doge.png | Bin 0 -> 8848 bytes src/assets/blockchain/chain_eos.png | Bin 0 -> 2646 bytes src/assets/blockchain/chain_ethereum.png | Bin 0 -> 2256 bytes .../blockchain/chain_ethereumclassic.png | Bin 0 -> 2684 bytes .../blockchain/chain_internetcomputer.png | Bin 0 -> 3323 bytes src/assets/blockchain/chain_iota.png | Bin 0 -> 3974 bytes src/assets/blockchain/chain_litecoin.png | Bin 0 -> 1791 bytes src/assets/blockchain/chain_monero.png | Bin 0 -> 1887 bytes src/assets/blockchain/chain_polkadot.png | Bin 0 -> 2402 bytes src/assets/blockchain/chain_ripple.png | Bin 0 -> 2077 bytes src/assets/blockchain/chain_solana.png | Bin 0 -> 3107 bytes src/assets/blockchain/chain_stellar.png | Bin 0 -> 2477 bytes src/assets/blockchain/chain_ton.png | Bin 0 -> 1840 bytes src/assets/blockchain/chain_tron.png | Bin 0 -> 2455 bytes src/assets/blockchain/chain_zcash.png | Bin 0 -> 1570 bytes src/assets/coins/btc.png | Bin 13736 -> 0 bytes src/assets/coins/coin_btc.png | Bin 0 -> 2672 bytes src/assets/coins/coin_ton.png | Bin 0 -> 2116 bytes src/assets/coins/ton.png | Bin 4902 -> 0 bytes src/assets/font-icons/accept.svg | 2 +- src/assets/font-icons/arrow-down.svg | 2 +- src/assets/font-icons/arrow-right-swap.svg | 1 + src/assets/font-icons/arrow-up-swap.svg | 1 + src/assets/font-icons/backspace.svg | 3 + src/assets/font-icons/caret-down.svg | 2 +- src/assets/font-icons/changelly.svg | 1 + src/assets/font-icons/chevron-down.svg | 2 +- src/assets/font-icons/chevron-left.svg | 2 +- src/assets/font-icons/clock.svg | 2 +- src/assets/font-icons/cog.svg | 2 +- src/assets/font-icons/coinmarket.svg | 2 +- src/assets/font-icons/copy.svg | 2 +- src/assets/font-icons/dot.svg | 2 +- src/assets/font-icons/earn.svg | 2 +- src/assets/font-icons/eye-closed.svg | 2 +- src/assets/font-icons/eye.svg | 2 +- src/assets/font-icons/face-id.svg | 1 + src/assets/font-icons/flashlight.svg | 1 + src/assets/font-icons/github.svg | 2 +- src/assets/font-icons/laptop.svg | 2 +- src/assets/font-icons/ledger.svg | 2 +- src/assets/font-icons/params.svg | 2 +- src/assets/font-icons/paste.svg | 2 +- src/assets/font-icons/pen.svg | 2 +- src/assets/font-icons/percent.svg | 2 +- src/assets/font-icons/plus.svg | 2 +- src/assets/font-icons/qr-scanner-alt.svg | 1 + src/assets/font-icons/qr-scanner.svg | 1 + src/assets/font-icons/qrcode.svg | 1 - src/assets/font-icons/question.svg | 2 +- src/assets/font-icons/receive.svg | 2 +- src/assets/font-icons/search.svg | 2 +- src/assets/font-icons/share.svg | 2 +- src/assets/font-icons/star-filled.svg | 2 +- src/assets/font-icons/star.svg | 2 +- src/assets/font-icons/telegram.svg | 2 +- src/assets/font-icons/tonscan.svg | 2 +- src/assets/font-icons/touch-id.svg | 1 + src/assets/font-icons/trash.svg | 2 +- src/assets/font-icons/windows-maximize.svg | 2 +- src/assets/font-icons/windows-minimize.svg | 2 +- src/assets/lottie/duck_guard.tgs | Bin 0 -> 39162 bytes src/assets/lottie/duck_yeee.tgs | Bin 0 -> 54567 bytes src/assets/lottiePreview/duck_guard.png | Bin 0 -> 8127 bytes src/assets/lottiePreview/duck_yeee.png | Bin 0 -> 11105 bytes src/assets/sale.png | Bin 4587 -> 559 bytes src/assets/settings/settings_biometrics.svg | 1 + src/assets/settings/settings_face-id.svg | 1 + src/components/App.module.scss | 15 + src/components/App.tsx | 91 +- src/components/Dialogs.tsx | 26 +- src/components/auth/Auth.module.scss | 147 +- src/components/auth/Auth.tsx | 99 +- src/components/auth/AuthBackupWalletModal.tsx | 127 + src/components/auth/AuthBackupWarning.tsx | 50 + src/components/auth/AuthConfirmPin.tsx | 111 + src/components/auth/AuthCreateBackup.tsx | 108 +- src/components/auth/AuthCreateBiometrics.tsx | 143 + .../auth/AuthCreateNativeBiometrics.tsx | 77 + src/components/auth/AuthCreatePassword.tsx | 225 +- src/components/auth/AuthCreatePin.tsx | 82 + src/components/auth/AuthDisclaimer.tsx | 150 +- src/components/auth/AuthImportMnemonic.tsx | 6 + src/components/auth/MnemonicCheck.tsx | 14 +- src/components/auth/MnemonicList.tsx | 9 +- src/components/auth/helpers/getFormId.ts | 13 + src/components/common/Countdown.module.scss | 12 + src/components/common/Countdown.tsx | 69 + src/components/common/SwapResult.module.scss | 60 + src/components/common/SwapResult.tsx | 124 + .../common/SwapTokensInfo.module.scss | 94 + src/components/common/SwapTokensInfo.tsx | 71 + src/components/common/TokenPriceChart.tsx | 3 +- .../common/TokenSelector.module.scss | 288 + src/components/common/TokenSelector.tsx | 519 ++ .../common/TransferResult.module.scss | 3 + src/components/common/TransferResult.tsx | 7 +- src/components/dapps/Dapp.module.scss | 181 +- src/components/dapps/DappConnectModal.tsx | 75 +- src/components/dapps/DappLedgerWarning.tsx | 3 +- src/components/dapps/DappPassword.tsx | 54 + src/components/dapps/DappTransaction.tsx | 18 +- src/components/dapps/DappTransactionModal.tsx | 75 +- src/components/dapps/DappTransferInitial.tsx | 7 +- src/components/electron/ElectronHeader.tsx | 8 + src/components/electron/UpdateApp.tsx | 69 +- .../ledger/LedgerConfirmOperation.tsx | 26 +- src/components/ledger/LedgerConnect.tsx | 59 +- src/components/ledger/LedgerModal.module.scss | 22 +- src/components/ledger/LedgerModal.tsx | 17 +- src/components/ledger/LedgerSelectWallets.tsx | 30 +- src/components/main/Main.module.scss | 4 + src/components/main/Main.tsx | 159 +- .../main/modals/AddAccountModal.module.scss | 21 +- .../main/modals/AddAccountModal.tsx | 82 +- .../main/modals/AddAccountPasswordModal.tsx | 56 + .../main/modals/BackupModal.module.scss | 4 +- src/components/main/modals/BackupModal.tsx | 27 +- .../modals/DisconnectDappModal.module.scss | 5 +- .../main/modals/QrScannerModal.module.scss | 197 + src/components/main/modals/QrScannerModal.tsx | 230 + src/components/main/modals/SignatureModal.tsx | 9 +- .../main/modals/SwapActivityModal.tsx | 359 ++ .../main/modals/TransactionModal.module.scss | 63 +- .../main/modals/TransactionModal.tsx | 230 +- .../Actions/LandscapeActions.module.scss | 12 +- .../sections/Actions/LandscapeActions.tsx | 61 +- .../Actions/PortraitActions.module.scss | 8 +- .../main/sections/Actions/PortraitActions.tsx | 51 +- .../sections/Card/AccountSelector.module.scss | 28 +- .../main/sections/Card/AccountSelector.tsx | 88 +- src/components/main/sections/Card/Card.tsx | 96 +- .../main/sections/Card/StickyCard.module.scss | 19 + .../main/sections/Card/StickyCard.tsx | 35 +- .../main/sections/Card/TokenCard.tsx | 42 +- .../sections/Content/Activities.module.scss | 29 +- .../main/sections/Content/Activities.tsx | 428 +- .../main/sections/Content/Assets.module.scss | 14 +- .../main/sections/Content/Assets.tsx | 68 +- .../main/sections/Content/Content.module.scss | 21 +- .../main/sections/Content/Content.tsx | 152 +- .../main/sections/Content/Nft.module.scss | 32 +- src/components/main/sections/Content/Nfts.tsx | 36 +- src/components/main/sections/Content/Swap.tsx | 220 + .../main/sections/Content/Token.module.scss | 10 +- .../main/sections/Content/Token.tsx | 25 +- .../sections/Content/Transaction.module.scss | 113 +- .../main/sections/Content/Transaction.tsx | 18 +- .../main/sections/Warnings/BackupWarning.tsx | 15 +- .../main/sections/Warnings/Warnings.tsx | 23 +- src/components/receive/Content.tsx | 93 +- src/components/receive/InvoiceModal.tsx | 30 +- src/components/receive/QrModal.tsx | 101 - .../receive/ReceiveModal.module.scss | 83 +- src/components/receive/ReceiveModal.tsx | 50 +- src/components/receive/ReceiveStatic.tsx | 22 +- src/components/settings/SelectTokens.tsx | 348 -- src/components/settings/Settings.module.scss | 357 +- src/components/settings/Settings.tsx | 315 +- src/components/settings/SettingsAbout.tsx | 16 +- .../settings/SettingsAppearance.tsx | 53 +- src/components/settings/SettingsAssets.tsx | 80 +- src/components/settings/SettingsDapps.tsx | 20 +- .../settings/SettingsDisclaimer.tsx | 13 +- src/components/settings/SettingsLanguage.tsx | 10 +- src/components/settings/SettingsModal.tsx | 7 +- src/components/settings/SettingsTokenList.tsx | 35 + src/components/settings/SettingsTokens.tsx | 79 +- .../biometrics/Biometrics.module.scss | 47 + .../settings/biometrics/Biometrics.tsx | 53 + .../biometrics/NativeBiometricsToggle.tsx | 123 + .../biometrics/NativeBiometricsTurnOn.tsx | 116 + .../settings/biometrics/TurnOff.tsx | 146 + .../settings/biometrics/TurnOffWarning.tsx | 46 + src/components/settings/biometrics/TurnOn.tsx | 189 + src/components/staking/StakeModal.tsx | 44 +- src/components/staking/Staking.module.scss | 49 +- src/components/staking/StakingInfoContent.tsx | 27 +- src/components/staking/StakingInfoModal.tsx | 11 +- src/components/staking/StakingInitial.tsx | 104 +- src/components/staking/UnstakeModal.tsx | 102 +- src/components/swap/Swap.module.scss | 795 +++ src/components/swap/SwapBlockchain.tsx | 235 + src/components/swap/SwapComplete.tsx | 76 + src/components/swap/SwapInitial.tsx | 626 ++ src/components/swap/SwapModal.tsx | 306 + src/components/swap/SwapPassword.tsx | 60 + src/components/swap/SwapSettingsModal.tsx | 239 + src/components/swap/SwapWaitTokens.tsx | 133 + .../swap/components/SwapSelectToken.tsx | 109 + .../swap/components/SwapSubmitButton.tsx | 114 + src/components/transfer/Transfer.module.scss | 68 +- src/components/transfer/TransferComplete.tsx | 96 + src/components/transfer/TransferConfirm.tsx | 140 + src/components/transfer/TransferInitial.tsx | 152 +- src/components/transfer/TransferModal.tsx | 253 +- src/components/transfer/TransferPassword.tsx | 56 + .../ui/AmountWithFeeTextField.module.scss | 6 +- src/components/ui/AnimatedSticker.tsx | 2 +- src/components/ui/Button.module.scss | 82 +- src/components/ui/Button.tsx | 18 +- .../ui/ConfettiContainer.module.scss | 13 + src/components/ui/ConfettiContainer.tsx | 199 + src/components/ui/CreatePasswordForm.tsx | 258 + src/components/ui/Draggable.tsx | 175 +- src/components/ui/Dropdown.module.scss | 25 +- src/components/ui/Dropdown.tsx | 35 +- src/components/ui/IconWithTooltip.module.scss | 2 +- src/components/ui/InfiniteScroll.tsx | 279 + src/components/ui/Input.module.scss | 27 +- src/components/ui/Input.tsx | 7 +- src/components/ui/InteractiveTextField.tsx | 14 +- src/components/ui/Menu.tsx | 7 + src/components/ui/MenuItem.module.scss | 21 +- src/components/ui/Modal.module.scss | 259 +- src/components/ui/Modal.tsx | 123 +- src/components/ui/ModalHeader.tsx | 7 +- src/components/ui/Notification.tsx | 2 +- src/components/ui/PasswordForm.module.scss | 78 +- src/components/ui/PasswordForm.tsx | 208 +- src/components/ui/PinPad.module.scss | 280 + src/components/ui/PinPad.tsx | 152 + src/components/ui/PinPadButton.tsx | 43 + src/components/ui/RichNumberInput.tsx | 57 +- src/components/ui/Switcher.tsx | 1 + src/components/ui/TabList.tsx | 3 +- src/components/ui/Transition.scss | 80 +- src/components/ui/Transition.tsx | 177 +- src/components/ui/helpers/animatedAssets.ts | 8 + src/components/ui/helpers/assetLogos.ts | 4 +- src/config.ts | 127 +- src/electron/autoUpdates.ts | 108 +- src/electron/config.yml | 1 + src/electron/deeplink.ts | 9 +- src/electron/main.ts | 5 +- src/electron/preload.ts | 11 + src/electron/secrets.ts | 20 + src/electron/storageUtils.ts | 152 + src/electron/tray.ts | 123 + src/electron/types.ts | 22 + src/electron/utils.ts | 27 + src/electron/window.ts | 91 +- src/extension/pageScript/deeplinkHook.ts | 24 +- src/global/actions/api/auth.ts | 620 +- src/global/actions/api/dapps.ts | 112 +- src/global/actions/api/initial.ts | 4 +- src/global/actions/api/staking.ts | 117 +- src/global/actions/api/swap.ts | 814 +++ src/global/actions/api/wallet.ts | 362 +- src/global/actions/apiUpdates/activities.ts | 92 +- src/global/actions/apiUpdates/dapp.ts | 79 +- src/global/actions/apiUpdates/initial.ts | 120 +- src/global/actions/index.ts | 2 + src/global/actions/ui/initial.ts | 40 +- src/global/actions/ui/misc.ts | 308 +- src/global/actions/ui/settings.ts | 28 + src/global/cache.ts | 56 +- src/global/init.ts | 4 +- src/global/initialState.ts | 27 +- src/global/reducers/activities.ts | 86 +- src/global/reducers/index.ts | 1 + src/global/reducers/misc.ts | 69 +- src/global/reducers/staking.ts | 53 +- src/global/reducers/swap.ts | 24 + src/global/reducers/wallet.ts | 18 +- src/global/selectors/index.ts | 167 +- src/global/types.ts | 298 +- src/hooks/freezeWhenClosed.ts | 20 + src/hooks/useDelegatedBottomSheet.ts | 207 + src/hooks/useDelegatingBottomSheet.ts | 103 + src/hooks/useDeviceScreen.ts | 3 + src/hooks/useElectronDrag.ts | 3 +- src/hooks/useHistoryBack.ts | 266 + src/hooks/useInfiniteScroll.ts | 135 + src/hooks/useInterval.ts | 16 +- src/hooks/usePasswordValidation.ts | 28 +- src/hooks/usePrevious.ts | 1 + src/hooks/usePrevious2.ts | 15 + src/hooks/useScrolledState.ts | 7 +- src/hooks/useTimeout.ts | 16 +- src/hooks/useTraceUpdatedProps.ts | 33 + src/hooks/useTransitionFixes.ts | 39 - src/hooks/useWindowSize.ts | 14 +- src/i18n/en.yaml | 162 +- src/i18n/es.yaml | 161 +- src/i18n/ru.yaml | 167 +- src/i18n/zh-Hans.yaml | 164 +- src/i18n/zh-Hant.yaml | 165 +- src/index.tsx | 96 +- src/lib/dexie/dexie.d.ts | 1028 ++++ src/lib/dexie/dexie.js | 5200 +++++++++++++++++ src/lib/rlottie/RLottie.ts | 4 +- src/lib/teact/dom-events.ts | 24 +- src/lib/teact/teact-dom.ts | 115 +- src/lib/teact/teact.ts | 2 +- src/lib/teact/teactn.tsx | 121 +- src/styles/_variables.scss | 28 +- src/styles/brilliant-icons.css | 122 +- src/styles/brilliant-icons.woff | Bin 5984 -> 7404 bytes src/styles/brilliant-icons.woff2 | Bin 5052 -> 6320 bytes src/styles/index.scss | 41 +- src/util/activeTabMonitor.ts | 14 +- src/util/animation.ts | 40 +- src/util/arePropsShallowEqual.ts | 2 +- src/util/authApi/index.ts | 100 + src/util/authApi/types.ts | 21 + src/util/authApi/webAuthn.ts | 223 + src/util/betterView.ts | 92 + src/util/cacheApi.ts | 6 +- src/util/callbacks.ts | 13 +- src/util/capacitor.ts | 156 + src/util/captureEvents.ts | 455 ++ src/util/captureSwipe.ts | 158 - src/util/clipboard.ts | 20 +- src/util/createPostMessageInterface.ts | 2 +- src/util/cssAnimationEndListeners.ts | 14 +- src/util/cssColorToHex.ts | 11 + src/util/debugOverlay.ts | 28 +- src/util/deepDiff.ts | 63 + src/util/deepMerge.ts | 34 + src/util/formatNumber.ts | 35 +- src/util/getIsAppUpdateNeeded.ts | 7 + src/util/iteratees.ts | 2 +- src/util/langProvider.ts | 4 +- src/util/ledger/index.ts | 49 +- src/util/ledger/tab.ts | 19 +- src/util/lethargy.ts | 99 + src/util/logs.ts | 2 +- src/util/math.ts | 10 + src/util/metadata.ts | 29 + src/util/modalSwipeManager.ts | 13 + src/util/multitab.ts | 80 + src/util/processDeeplink.ts | 30 + src/util/random.ts | 5 + src/util/resetScroll.ts | 17 + src/util/resolveModalTransitionName.ts | 5 + src/util/saveCaretPosition.ts | 2 +- src/util/schedulers.ts | 2 +- src/util/swap/buildSwapId.ts | 3 + src/util/swap/getBlockchainNetworkIcon.ts | 53 + src/util/swap/getBlockchainNetworkName.ts | 31 + src/util/swap/getSwapRate.ts | 49 + src/util/swipeController.ts | 186 + src/util/switchTheme.ts | 15 +- src/util/ton/deeplinks.ts | 26 + src/util/windowEnvironment.ts | 38 +- src/util/windowSize.ts | 65 +- src/util/withCacheAsync.ts | 4 +- tsconfig.json | 2 +- webpack-electron.config.ts | 17 +- webpack.config.ts | 60 +- 651 files changed, 49784 insertions(+), 5794 deletions(-) create mode 100644 capacitor.config.ts create mode 100644 changelogs/.gitkeep create mode 100644 changelogs/1.17.0.txt create mode 100644 deploy/update_version.js create mode 100644 mobile/Gemfile create mode 100644 mobile/Gemfile.lock create mode 100644 mobile/android/.gitignore create mode 100644 mobile/android/app/.gitignore create mode 100644 mobile/android/app/build.gradle create mode 100644 mobile/android/app/capacitor.build.gradle create mode 100644 mobile/android/app/proguard-rules.pro create mode 100644 mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java create mode 100644 mobile/android/app/src/main/AndroidManifest.xml create mode 100644 mobile/android/app/src/main/ic_launcher-playstore.png create mode 100644 mobile/android/app/src/main/java/org/mytonwallet/app/MainActivity.java create mode 100644 mobile/android/app/src/main/res/drawable-hdpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-ldpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-mdpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-night-hdpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-night-ldpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-night-mdpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-night-xhdpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-night-xxhdpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-night-xxxhdpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-night/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 mobile/android/app/src/main/res/drawable-xhdpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-xxhdpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable-xxxhdpi/splash.9.png create mode 100644 mobile/android/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 mobile/android/app/src/main/res/drawable/icon_svg.xml create mode 100644 mobile/android/app/src/main/res/drawable/launch_splash.xml create mode 100644 mobile/android/app/src/main/res/drawable/splash.9.png create mode 100644 mobile/android/app/src/main/res/layout/activity_main.xml create mode 100644 mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp create mode 100644 mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp create mode 100644 mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp create mode 100644 mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp create mode 100644 mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp create mode 100644 mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 mobile/android/app/src/main/res/values-v31/styles.xml create mode 100644 mobile/android/app/src/main/res/values/ic_launcher_background.xml create mode 100644 mobile/android/app/src/main/res/values/strings.xml create mode 100644 mobile/android/app/src/main/res/values/styles.xml create mode 100644 mobile/android/app/src/main/res/xml/file_paths.xml create mode 100644 mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java create mode 100644 mobile/android/build.gradle create mode 100644 mobile/android/capacitor.settings.gradle create mode 100644 mobile/android/fastlane/Appfile create mode 100644 mobile/android/fastlane/Fastfile create mode 100644 mobile/android/fastlane/Pluginfile create mode 100644 mobile/android/gradle.properties create mode 100644 mobile/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 mobile/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 mobile/android/gradlew create mode 100644 mobile/android/gradlew.bat create mode 100644 mobile/android/settings.gradle create mode 100644 mobile/android/variables.gradle create mode 100644 mobile/ios/.gitignore create mode 100644 mobile/ios/App/App.xcodeproj/project.pbxproj create mode 100644 mobile/ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/ios/App/App.xcodeproj/xcshareddata/xcschemes/MyTonWallet.xcscheme create mode 100644 mobile/ios/App/App.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/ios/App/App.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 mobile/ios/App/App/AppDelegate.swift create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png create mode 100644 mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 mobile/ios/App/App/Assets.xcassets/Contents.json create mode 100644 mobile/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json create mode 100644 mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@1x~universal~anyany-dark.png create mode 100644 mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@1x~universal~anyany.png create mode 100644 mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany-dark.png create mode 100644 mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany.png create mode 100644 mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@3x~universal~anyany-dark.png create mode 100644 mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@3x~universal~anyany.png create mode 100644 mobile/ios/App/App/Base.lproj/LaunchScreen.storyboard create mode 100644 mobile/ios/App/App/Base.lproj/Main.storyboard create mode 100644 mobile/ios/App/App/Info.plist create mode 100644 mobile/ios/App/MyTonWallet.entitlements create mode 100644 mobile/ios/App/Podfile create mode 100644 mobile/ios/App/Podfile.lock create mode 100644 mobile/ios/App/fastlane/Appfile create mode 100644 mobile/ios/App/fastlane/Fastfile create mode 100644 mobile/plugins/capacitor-splash-screen/.eslintignore create mode 100644 mobile/plugins/capacitor-splash-screen/.gitignore create mode 100644 mobile/plugins/capacitor-splash-screen/.prettierignore create mode 100644 mobile/plugins/capacitor-splash-screen/CHANGELOG.md create mode 100644 mobile/plugins/capacitor-splash-screen/CapacitorSplashScreen.podspec create mode 100644 mobile/plugins/capacitor-splash-screen/LICENSE create mode 100644 mobile/plugins/capacitor-splash-screen/README.md create mode 100644 mobile/plugins/capacitor-splash-screen/android/.gitignore create mode 100644 mobile/plugins/capacitor-splash-screen/android/build.gradle create mode 100644 mobile/plugins/capacitor-splash-screen/android/gradle.properties create mode 100644 mobile/plugins/capacitor-splash-screen/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 mobile/plugins/capacitor-splash-screen/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 mobile/plugins/capacitor-splash-screen/android/gradlew create mode 100644 mobile/plugins/capacitor-splash-screen/android/gradlew.bat create mode 100644 mobile/plugins/capacitor-splash-screen/android/proguard-rules.pro create mode 100644 mobile/plugins/capacitor-splash-screen/android/settings.gradle create mode 100644 mobile/plugins/capacitor-splash-screen/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java create mode 100644 mobile/plugins/capacitor-splash-screen/android/src/main/AndroidManifest.xml create mode 100644 mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashListener.java create mode 100644 mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreen.java create mode 100644 mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenConfig.java create mode 100644 mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenPlugin.java create mode 100644 mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenSettings.java create mode 100644 mobile/plugins/capacitor-splash-screen/android/src/main/res/.gitkeep create mode 100644 mobile/plugins/capacitor-splash-screen/android/src/main/res/values/styles.xml create mode 100644 mobile/plugins/capacitor-splash-screen/android/src/test/java/com/getcapacitor/ExampleUnitTest.java create mode 100644 mobile/plugins/capacitor-splash-screen/dist/docs.json create mode 100644 mobile/plugins/capacitor-splash-screen/dist/esm/definitions.d.ts create mode 100644 mobile/plugins/capacitor-splash-screen/dist/esm/definitions.js create mode 100644 mobile/plugins/capacitor-splash-screen/dist/esm/definitions.js.map create mode 100644 mobile/plugins/capacitor-splash-screen/dist/esm/index.d.ts create mode 100644 mobile/plugins/capacitor-splash-screen/dist/esm/index.js create mode 100644 mobile/plugins/capacitor-splash-screen/dist/esm/index.js.map create mode 100644 mobile/plugins/capacitor-splash-screen/dist/esm/web.d.ts create mode 100644 mobile/plugins/capacitor-splash-screen/dist/esm/web.js create mode 100644 mobile/plugins/capacitor-splash-screen/dist/esm/web.js.map create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.pbxproj create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin/Info.plist create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreen.swift create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenConfig.swift create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.h create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.m create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.swift create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenSettings.swift create mode 100644 mobile/plugins/capacitor-splash-screen/ios/PluginTests/Info.plist create mode 100644 mobile/plugins/capacitor-splash-screen/ios/PluginTests/SplashScreenPluginTests.swift create mode 100644 mobile/plugins/capacitor-splash-screen/ios/Podfile create mode 100644 mobile/plugins/capacitor-splash-screen/package-lock.json create mode 100644 mobile/plugins/capacitor-splash-screen/package.json create mode 100644 mobile/plugins/capacitor-splash-screen/rollup.config.js create mode 100644 mobile/plugins/capacitor-splash-screen/src/definitions.ts create mode 100644 mobile/plugins/capacitor-splash-screen/src/index.ts create mode 100644 mobile/plugins/capacitor-splash-screen/src/web.ts create mode 100644 mobile/plugins/capacitor-splash-screen/tsconfig.json create mode 100644 mobile/plugins/native-bottom-sheet/.eslintignore create mode 100644 mobile/plugins/native-bottom-sheet/.gitignore create mode 100644 mobile/plugins/native-bottom-sheet/.prettierignore create mode 100644 mobile/plugins/native-bottom-sheet/CONTRIBUTING.md create mode 100644 mobile/plugins/native-bottom-sheet/NativeBottomSheet.podspec create mode 100644 mobile/plugins/native-bottom-sheet/README.md create mode 100644 mobile/plugins/native-bottom-sheet/android/.gitignore create mode 100644 mobile/plugins/native-bottom-sheet/android/build.gradle create mode 100644 mobile/plugins/native-bottom-sheet/android/gradle.properties create mode 100644 mobile/plugins/native-bottom-sheet/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 mobile/plugins/native-bottom-sheet/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 mobile/plugins/native-bottom-sheet/android/gradlew create mode 100644 mobile/plugins/native-bottom-sheet/android/gradlew.bat create mode 100644 mobile/plugins/native-bottom-sheet/android/proguard-rules.pro create mode 100644 mobile/plugins/native-bottom-sheet/android/settings.gradle create mode 100644 mobile/plugins/native-bottom-sheet/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java create mode 100644 mobile/plugins/native-bottom-sheet/android/src/main/AndroidManifest.xml create mode 100644 mobile/plugins/native-bottom-sheet/android/src/main/java/org/mytonwallet/plugins/nativebottomsheet/BottomSheet.java create mode 100644 mobile/plugins/native-bottom-sheet/android/src/main/java/org/mytonwallet/plugins/nativebottomsheet/BottomSheetPlugin.java create mode 100644 mobile/plugins/native-bottom-sheet/android/src/main/res/.gitkeep create mode 100644 mobile/plugins/native-bottom-sheet/android/src/test/java/com/getcapacitor/ExampleUnitTest.java create mode 100644 mobile/plugins/native-bottom-sheet/dist/docs.json create mode 100644 mobile/plugins/native-bottom-sheet/dist/esm/definitions.d.ts create mode 100644 mobile/plugins/native-bottom-sheet/dist/esm/definitions.js create mode 100644 mobile/plugins/native-bottom-sheet/dist/esm/definitions.js.map create mode 100644 mobile/plugins/native-bottom-sheet/dist/esm/index.d.ts create mode 100644 mobile/plugins/native-bottom-sheet/dist/esm/index.js create mode 100644 mobile/plugins/native-bottom-sheet/dist/esm/index.js.map create mode 100644 mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.pbxproj create mode 100644 mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme create mode 100644 mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme create mode 100644 mobile/plugins/native-bottom-sheet/ios/Plugin.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/plugins/native-bottom-sheet/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.h create mode 100644 mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.m create mode 100644 mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.swift create mode 100644 mobile/plugins/native-bottom-sheet/ios/Plugin/Info.plist create mode 100644 mobile/plugins/native-bottom-sheet/ios/PluginTests/BottomSheetTests.swift create mode 100644 mobile/plugins/native-bottom-sheet/ios/PluginTests/Info.plist create mode 100644 mobile/plugins/native-bottom-sheet/ios/Podfile create mode 100644 mobile/plugins/native-bottom-sheet/package-lock.json create mode 100644 mobile/plugins/native-bottom-sheet/package.json create mode 100644 mobile/plugins/native-bottom-sheet/rollup.config.js create mode 100644 mobile/plugins/native-bottom-sheet/src/definitions.ts create mode 100644 mobile/plugins/native-bottom-sheet/src/index.ts create mode 100644 mobile/plugins/native-bottom-sheet/tsconfig.json create mode 100644 public/electronVersion.txt create mode 100644 public/get/.well-known/apple-app-site-association create mode 100644 public/get/.well-known/assetlinks.json create mode 100644 public/version.txt create mode 100644 src/api/db.ts create mode 100644 src/api/methods/other.ts create mode 100644 src/api/methods/prices.ts create mode 100644 src/api/methods/swap.ts create mode 100644 src/api/types/backend.ts create mode 100644 src/assets/blockchain/chain_avalanche.png create mode 100644 src/assets/blockchain/chain_bitcoin.png create mode 100644 src/assets/blockchain/chain_bitcoincash.png create mode 100644 src/assets/blockchain/chain_bnb.png create mode 100644 src/assets/blockchain/chain_cardano.png create mode 100644 src/assets/blockchain/chain_cosmos.png create mode 100644 src/assets/blockchain/chain_dash.png create mode 100644 src/assets/blockchain/chain_doge.png create mode 100644 src/assets/blockchain/chain_eos.png create mode 100644 src/assets/blockchain/chain_ethereum.png create mode 100644 src/assets/blockchain/chain_ethereumclassic.png create mode 100644 src/assets/blockchain/chain_internetcomputer.png create mode 100644 src/assets/blockchain/chain_iota.png create mode 100644 src/assets/blockchain/chain_litecoin.png create mode 100644 src/assets/blockchain/chain_monero.png create mode 100644 src/assets/blockchain/chain_polkadot.png create mode 100644 src/assets/blockchain/chain_ripple.png create mode 100644 src/assets/blockchain/chain_solana.png create mode 100644 src/assets/blockchain/chain_stellar.png create mode 100644 src/assets/blockchain/chain_ton.png create mode 100644 src/assets/blockchain/chain_tron.png create mode 100644 src/assets/blockchain/chain_zcash.png delete mode 100644 src/assets/coins/btc.png create mode 100644 src/assets/coins/coin_btc.png create mode 100644 src/assets/coins/coin_ton.png delete mode 100644 src/assets/coins/ton.png create mode 100644 src/assets/font-icons/arrow-right-swap.svg create mode 100644 src/assets/font-icons/arrow-up-swap.svg create mode 100644 src/assets/font-icons/backspace.svg create mode 100644 src/assets/font-icons/changelly.svg create mode 100644 src/assets/font-icons/face-id.svg create mode 100644 src/assets/font-icons/flashlight.svg create mode 100644 src/assets/font-icons/qr-scanner-alt.svg create mode 100644 src/assets/font-icons/qr-scanner.svg delete mode 100644 src/assets/font-icons/qrcode.svg create mode 100644 src/assets/font-icons/touch-id.svg create mode 100644 src/assets/lottie/duck_guard.tgs create mode 100644 src/assets/lottie/duck_yeee.tgs create mode 100644 src/assets/lottiePreview/duck_guard.png create mode 100644 src/assets/lottiePreview/duck_yeee.png create mode 100644 src/assets/settings/settings_biometrics.svg create mode 100644 src/assets/settings/settings_face-id.svg create mode 100644 src/components/auth/AuthBackupWalletModal.tsx create mode 100644 src/components/auth/AuthBackupWarning.tsx create mode 100644 src/components/auth/AuthConfirmPin.tsx create mode 100644 src/components/auth/AuthCreateBiometrics.tsx create mode 100644 src/components/auth/AuthCreateNativeBiometrics.tsx create mode 100644 src/components/auth/AuthCreatePin.tsx create mode 100644 src/components/auth/helpers/getFormId.ts create mode 100644 src/components/common/Countdown.module.scss create mode 100644 src/components/common/Countdown.tsx create mode 100644 src/components/common/SwapResult.module.scss create mode 100644 src/components/common/SwapResult.tsx create mode 100644 src/components/common/SwapTokensInfo.module.scss create mode 100644 src/components/common/SwapTokensInfo.tsx create mode 100644 src/components/common/TokenSelector.module.scss create mode 100644 src/components/common/TokenSelector.tsx create mode 100644 src/components/dapps/DappPassword.tsx create mode 100644 src/components/main/modals/AddAccountPasswordModal.tsx create mode 100644 src/components/main/modals/QrScannerModal.module.scss create mode 100644 src/components/main/modals/QrScannerModal.tsx create mode 100644 src/components/main/modals/SwapActivityModal.tsx create mode 100644 src/components/main/sections/Content/Swap.tsx delete mode 100644 src/components/receive/QrModal.tsx delete mode 100644 src/components/settings/SelectTokens.tsx create mode 100644 src/components/settings/SettingsTokenList.tsx create mode 100644 src/components/settings/biometrics/Biometrics.module.scss create mode 100644 src/components/settings/biometrics/Biometrics.tsx create mode 100644 src/components/settings/biometrics/NativeBiometricsToggle.tsx create mode 100644 src/components/settings/biometrics/NativeBiometricsTurnOn.tsx create mode 100644 src/components/settings/biometrics/TurnOff.tsx create mode 100644 src/components/settings/biometrics/TurnOffWarning.tsx create mode 100644 src/components/settings/biometrics/TurnOn.tsx create mode 100644 src/components/swap/Swap.module.scss create mode 100644 src/components/swap/SwapBlockchain.tsx create mode 100644 src/components/swap/SwapComplete.tsx create mode 100644 src/components/swap/SwapInitial.tsx create mode 100644 src/components/swap/SwapModal.tsx create mode 100644 src/components/swap/SwapPassword.tsx create mode 100644 src/components/swap/SwapSettingsModal.tsx create mode 100644 src/components/swap/SwapWaitTokens.tsx create mode 100644 src/components/swap/components/SwapSelectToken.tsx create mode 100644 src/components/swap/components/SwapSubmitButton.tsx create mode 100644 src/components/transfer/TransferComplete.tsx create mode 100644 src/components/transfer/TransferConfirm.tsx create mode 100644 src/components/transfer/TransferPassword.tsx create mode 100644 src/components/ui/ConfettiContainer.module.scss create mode 100644 src/components/ui/ConfettiContainer.tsx create mode 100644 src/components/ui/CreatePasswordForm.tsx create mode 100644 src/components/ui/InfiniteScroll.tsx create mode 100644 src/components/ui/PinPad.module.scss create mode 100644 src/components/ui/PinPad.tsx create mode 100644 src/components/ui/PinPadButton.tsx create mode 100644 src/electron/secrets.ts create mode 100644 src/electron/storageUtils.ts create mode 100644 src/electron/tray.ts create mode 100644 src/global/actions/api/swap.ts create mode 100644 src/global/actions/ui/settings.ts create mode 100644 src/global/reducers/swap.ts create mode 100644 src/hooks/freezeWhenClosed.ts create mode 100644 src/hooks/useDelegatedBottomSheet.ts create mode 100644 src/hooks/useDelegatingBottomSheet.ts create mode 100644 src/hooks/useHistoryBack.ts create mode 100644 src/hooks/useInfiniteScroll.ts create mode 100644 src/hooks/usePrevious2.ts create mode 100644 src/hooks/useTraceUpdatedProps.ts delete mode 100644 src/hooks/useTransitionFixes.ts create mode 100644 src/lib/dexie/dexie.d.ts create mode 100644 src/lib/dexie/dexie.js create mode 100644 src/util/authApi/index.ts create mode 100644 src/util/authApi/types.ts create mode 100644 src/util/authApi/webAuthn.ts create mode 100644 src/util/betterView.ts create mode 100644 src/util/capacitor.ts create mode 100644 src/util/captureEvents.ts delete mode 100644 src/util/captureSwipe.ts create mode 100644 src/util/cssColorToHex.ts create mode 100644 src/util/deepDiff.ts create mode 100644 src/util/deepMerge.ts create mode 100644 src/util/getIsAppUpdateNeeded.ts create mode 100644 src/util/lethargy.ts create mode 100644 src/util/metadata.ts create mode 100644 src/util/modalSwipeManager.ts create mode 100644 src/util/multitab.ts create mode 100644 src/util/processDeeplink.ts create mode 100644 src/util/resetScroll.ts create mode 100644 src/util/resolveModalTransitionName.ts create mode 100644 src/util/swap/buildSwapId.ts create mode 100644 src/util/swap/getBlockchainNetworkIcon.ts create mode 100644 src/util/swap/getBlockchainNetworkName.ts create mode 100644 src/util/swap/getSwapRate.ts create mode 100644 src/util/swipeController.ts create mode 100644 src/util/ton/deeplinks.ts diff --git a/.env.example b/.env.example index f32d3801..19ff981c 100644 --- a/.env.example +++ b/.env.example @@ -1,11 +1,14 @@ NODE_ENV=development -TONHTTPAPI_MAINNET_URL=https://tonhttpapi.mytonwallet.org/jsonRPC +TONHTTPAPI_MAINNET_URL=https://tonhttpapi.mytonwallet.org/api/v2/jsonRPC TONHTTPAPI_MAINNET_API_KEY= -TONHTTPAPI_TESTNET_URL=https://tonhttpapi-testnet.mytonwallet.org/jsonRPC +TONHTTPAPI_TESTNET_URL=https://tonhttpapi-testnet.mytonwallet.org/api/v2/jsonRPC TONHTTPAPI_TESTNET_API_KEY= +TONINDEXER_MAINNET_URL=https://tonhttpapi.mytonwallet.org/api/v3 +TONINDEXER_TESTNET_URL=https://tonhttpapi-testnet.mytonwallet.org/api/v3 TONAPIIO_MAINNET_URL=https://tonapiio.mytonwallet.org TONAPIIO_TESTNET_URL=https://tonapiio-testnet.mytonwallet.org PROXY_HOSTS="tonproxy.io:38080 tonproxy.io:38081 tonproxy.io:38082" BRILLIANT_API_BASE_URL= STAKING_POOLS= +CSP_CONNECT_SRC_EXTRA_URL= diff --git a/.eslintignore b/.eslintignore index ed061118..26c54681 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,10 +1,12 @@ dev public +mobile src/lib/big.js/ src/lib/rlottie/rlottie-wasm.js src/lib/aes-js/index.js src/lib/noble-ed25519/index.js +src/lib/dexie/ jest.config.js playwright.config.ts postcss.config.js @@ -15,3 +17,4 @@ trash deploy dist +dist-electron diff --git a/.eslintrc b/.eslintrc index 0c96d448..78b98429 100644 --- a/.eslintrc +++ b/.eslintrc @@ -156,6 +156,7 @@ "^react", "^ton", "^tonweb(/.*|$)", + "^qr-code-styling(/.*|$)", "^@?\\w", "dist(/.*|$)", "^(\\.+/)+(lib/teact)(/.*|$)", diff --git a/.github/workflows/package-and-publish.yml b/.github/workflows/package-and-publish.yml index 2de78b79..711f63a3 100644 --- a/.github/workflows/package-and-publish.yml +++ b/.github/workflows/package-and-publish.yml @@ -13,6 +13,11 @@ on: push: branches: - master + - mobile-release + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true env: APP_NAME: MyTonWallet @@ -22,6 +27,7 @@ jobs: electron-release: name: Build, package and publish Electron runs-on: macos-latest + timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@v3 @@ -59,6 +65,10 @@ jobs: security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k actions $KEY_CHAIN security find-identity -v -p codesigning $KEY_CHAIN + - name: Get branch name for current workflow run + id: branch-name + uses: tj-actions/branch-names@v7 + - name: Build, package and publish env: TONHTTPAPI_MAINNET_URL: ${{ vars.TONHTTPAPI_MAINNET_URL }} @@ -74,6 +84,8 @@ jobs: PUBLISH_REPO: ${{ vars.PUBLISH_REPO }} GH_TOKEN: ${{ secrets.GH_TOKEN }} + BASE_URL: ${{ vars.BASE_URL }} + IS_PREVIEW: ${{ steps.branch-name.outputs.current_branch != 'master' }} run: | if [ -z "$PUBLISH_REPO" ]; then npm run electron:package:staging @@ -109,6 +121,7 @@ jobs: name: Sign and re-publish Windows package needs: electron-release runs-on: windows-latest + timeout-minutes: 10 if: vars.PUBLISH_REPO != '' env: GH_TOKEN: ${{ secrets.GH_TOKEN }} @@ -200,6 +213,7 @@ jobs: extensions-package: name: Build and package extensions runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@v3 @@ -257,6 +271,7 @@ jobs: name: Publish extensions needs: extensions-package runs-on: ubuntu-latest + timeout-minutes: 5 if: vars.PUBLISH_REPO != '' steps: - name: Checkout @@ -292,33 +307,33 @@ jobs: with: name: ${{ env.FIREFOX_FILE_NAME }} -# - name: Publish to Firefox addons -# env: -# WEB_EXT_API_KEY: ${{ secrets.FIREFOX_API_KEY }} -# WEB_EXT_API_SECRET: ${{ secrets.FIREFOX_API_SECRET }} -# # App env -# TONHTTPAPI_MAINNET_URL: ${{ vars.TONHTTPAPI_MAINNET_URL }} -# TONAPIIO_MAINNET_URL: ${{ vars.TONAPIIO_MAINNET_URL }} -# TONHTTPAPI_TESTNET_URL: ${{ vars.TONHTTPAPI_TESTNET_URL }} -# TONAPIIO_TESTNET_URL: ${{ vars.TONAPIIO_TESTNET_URL }} -# PROXY_HOSTS: ${{ vars.PROXY_HOSTS }} -# STAKING_POOLS: ${{ vars.STAKING_POOLS }} -# if: ${{ env.WEB_EXT_API_KEY != '' }} -# run: | -# npm i jsonwebtoken@9 web-ext-submit@7 -# UNZIP_DIR=/tmp/${{ env.APP_NAME }}-firefox -# mkdir $UNZIP_DIR -# unzip ${{ env.FIREFOX_FILE_NAME }} -d $UNZIP_DIR -# web-ext-submit --source-dir=$UNZIP_DIR/dist -# echo "APP_NAME=\"${APP_NAME}\" -# TONHTTPAPI_MAINNET_URL=\"${TONHTTPAPI_MAINNET_URL}\" -# TONHTTPAPI_TESTNET_URL=\"${TONHTTPAPI_TESTNET_URL}\" -# TONAPIIO_MAINNET_URL=\"${TONAPIIO_MAINNET_URL}\" -# TONAPIIO_TESTNET_URL=\"${TONAPIIO_TESTNET_URL}\" -# PROXY_HOSTS=\"${PROXY_HOSTS}\" -# STAKING_POOLS=\"${STAKING_POOLS}\"" >.env -# bash deploy/firefox_pack_sources.sh -# node deploy/firefoxPatchVersion.js + - name: Publish to Firefox addons + env: + WEB_EXT_API_KEY: ${{ secrets.FIREFOX_API_KEY }} + WEB_EXT_API_SECRET: ${{ secrets.FIREFOX_API_SECRET }} + # App env + TONHTTPAPI_MAINNET_URL: ${{ vars.TONHTTPAPI_MAINNET_URL }} + TONAPIIO_MAINNET_URL: ${{ vars.TONAPIIO_MAINNET_URL }} + TONHTTPAPI_TESTNET_URL: ${{ vars.TONHTTPAPI_TESTNET_URL }} + TONAPIIO_TESTNET_URL: ${{ vars.TONAPIIO_TESTNET_URL }} + PROXY_HOSTS: ${{ vars.PROXY_HOSTS }} + STAKING_POOLS: ${{ vars.STAKING_POOLS }} + if: ${{ env.WEB_EXT_API_KEY != '' }} + run: | + npm i jsonwebtoken@9 web-ext-submit@7 + UNZIP_DIR=/tmp/${{ env.APP_NAME }}-firefox + mkdir $UNZIP_DIR + unzip ${{ env.FIREFOX_FILE_NAME }} -d $UNZIP_DIR + web-ext-submit --source-dir=$UNZIP_DIR/dist + echo "APP_NAME=\"${APP_NAME}\" + TONHTTPAPI_MAINNET_URL=\"${TONHTTPAPI_MAINNET_URL}\" + TONHTTPAPI_TESTNET_URL=\"${TONHTTPAPI_TESTNET_URL}\" + TONAPIIO_MAINNET_URL=\"${TONAPIIO_MAINNET_URL}\" + TONAPIIO_TESTNET_URL=\"${TONAPIIO_TESTNET_URL}\" + PROXY_HOSTS=\"${PROXY_HOSTS}\" + STAKING_POOLS=\"${STAKING_POOLS}\"" >.env + bash deploy/firefox_pack_sources.sh + node deploy/firefoxPatchVersion.js calculate-hash: name: Calculate sha256 hashes @@ -343,3 +358,121 @@ jobs: with: name: ${{ env.HASH_FILENAME }} path: ${{ env.HASH_FILENAME }} + + mobile-release: + name: Build, package and publish mobile apps + runs-on: macos-latest + timeout-minutes: 30 + if: vars.PUBLISH_REPO == '' && github.event_name != 'workflow_dispatch' + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Use Node.js 18.x + uses: actions/setup-node@v3 + with: + node-version: 18.x + + - name: Cache node modules + id: npm-cache + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-build-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build- + + - name: Install dependencies + if: steps.npm-cache.outputs.cache-hit != 'true' + run: npm ci + + - name: Build and sync mobile projects + env: + TONHTTPAPI_MAINNET_URL: ${{ vars.TONHTTPAPI_MAINNET_URL }} + TONAPIIO_MAINNET_URL: ${{ vars.TONAPIIO_MAINNET_URL }} + TONHTTPAPI_TESTNET_URL: ${{ vars.TONHTTPAPI_TESTNET_URL }} + TONAPIIO_TESTNET_URL: ${{ vars.TONAPIIO_TESTNET_URL }} + PROXY_HOSTS: ${{ vars.PROXY_HOSTS }} + STAKING_POOLS: ${{ vars.STAKING_POOLS }} + PUBLISH_REPO: ${{ vars.PUBLISH_REPO }} + run: | + if [ "$GITHUB_REF_NAME" == "mobile-release" ]; then + npm run mobile:build:production + else + npm run mobile:build:staging + fi + + - name: Use Ruby and install dependencies + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2 + bundler-cache: true + working-directory: mobile + + - name: Install the Apple certificate and provisioning profile + env: + IOS_CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE_BASE64 }} + IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }} + IOS_PROVISION_PROFILE_BASE64: ${{ secrets.IOS_PROVISION_PROFILE_BASE64 }} + IOS_KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }} + run: | + # create variables + CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 + PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + + # import certificate and provisioning profile from secrets + echo -n "$IOS_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH + echo -n "$IOS_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH + + # create temporary keychain + security create-keychain -p "$IOS_KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$IOS_KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # import certificate to keychain + security import $CERTIFICATE_PATH -P "$IOS_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + # apply provisioning profile + mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles + cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles + + - name: "iOS: Package and publish" + env: + IOS_AUTH_KEY_BASE64: ${{ secrets.IOS_AUTH_KEY_BASE64 }} + run: | + cd mobile/ios/App + echo -n "$IOS_AUTH_KEY_BASE64" | base64 --decode -o ./AuthKey.p8 + if [ "$GITHUB_REF_NAME" == "mobile-release" ]; then + bundle exec fastlane release + else + bundle exec fastlane beta + fi + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: "Android: Package and publish" + env: + ANDROID_API_KEY_BASE64: ${{ secrets.ANDROID_API_KEY_BASE64 }} + ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + run: | + cd mobile/android + + echo -n "$ANDROID_API_KEY_BASE64" | base64 --decode -o ./api-key.json + echo -n "$ANDROID_KEYSTORE_BASE64" | base64 --decode -o ./android.keystore + + if [ "$GITHUB_REF_NAME" == "mobile-release" ]; then + bundle exec fastlane release + else + bundle exec fastlane beta + fi diff --git a/.github/workflows/statoscope-upload-reference-statistics.yml b/.github/workflows/statoscope-upload-reference-statistics.yml index 3309c335..4df6b092 100644 --- a/.github/workflows/statoscope-upload-reference-statistics.yml +++ b/.github/workflows/statoscope-upload-reference-statistics.yml @@ -24,7 +24,7 @@ jobs: - name: Install run: npm ci - name: Build - run: npm run build:production; cp ./public/statoscope-build-statistics.json ./statoscope-reference.json + run: npm run build:production; mv ./public/statoscope-build-statistics.json ./statoscope-reference.json - uses: actions/upload-artifact@v3 with: name: statoscope-reference diff --git a/.github/workflows/statoscope.yml b/.github/workflows/statoscope.yml index 8dd428ee..34d02145 100644 --- a/.github/workflows/statoscope.yml +++ b/.github/workflows/statoscope.yml @@ -67,7 +67,7 @@ jobs: path: ./ continue-on-error: true - name: Prepare statoscope input - run: cp public/statoscope-build-statistics.json input.json; mv statoscope-reference.json reference.json + run: mv public/statoscope-build-statistics.json input.json; mv statoscope-reference.json reference.json - name: Validate run: npm run statoscope:validate-diff - name: Query stats diff --git a/.gitignore b/.gitignore index 965fc6a6..164cd4d1 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ trash/ coverage/ src/i18n/en.json notarization-error.log +.patch-version diff --git a/.stylelintrc.json b/.stylelintrc.json index 0cbd610b..d9b45534 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -6,7 +6,9 @@ "ignoreFiles": [ "dist/*.css", "src/styles/brilliant-icons.css", - "coverage/**" + "coverage/**", + "dist-electron/*.css", + "mobile/**/public/*.css" ], "plugins": [ "stylelint-declaration-block-no-ignored-properties", diff --git a/capacitor.config.ts b/capacitor.config.ts new file mode 100644 index 00000000..09ba03d5 --- /dev/null +++ b/capacitor.config.ts @@ -0,0 +1,36 @@ +import type { CapacitorConfig } from '@capacitor/cli'; + +const config: CapacitorConfig = { + appId: 'org.mytonwallet.app', + appName: 'MyTonWallet', + webDir: 'dist', + server: { + androidScheme: 'https', + hostname: 'mytonwallet.local', + }, + android: { + path: 'mobile/android', + includePlugins: [ + '@capacitor-mlkit/barcode-scanning', + '@capacitor/app', + '@capacitor/dialog', + '@capacitor/haptics', + '@capacitor/status-bar', + '@capgo/capacitor-native-biometric', + '@mauricewegner/capacitor-navigation-bar', + 'capacitor-plugin-safe-area', + 'native-bottom-sheet', + ], + }, + ios: { + path: 'mobile/ios', + scheme: 'MyTonWallet', + }, + plugins: { + SplashScreen: { + launchAutoHide: false, + }, + }, +}; + +export default config; diff --git a/changelogs/.gitkeep b/changelogs/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/changelogs/1.17.0.txt b/changelogs/1.17.0.txt new file mode 100644 index 00000000..5f2164e4 --- /dev/null +++ b/changelogs/1.17.0.txt @@ -0,0 +1 @@ +Some hotfixes diff --git a/deploy/update_version.js b/deploy/update_version.js new file mode 100644 index 00000000..81ac7919 --- /dev/null +++ b/deploy/update_version.js @@ -0,0 +1,21 @@ +const path = require('path'); +const fs = require('fs'); + +const ROOT_PATH = `${path.dirname(__filename)}/..`; +const PATCH_VERSION_PATH = `${ROOT_PATH}/.patch-version`; +const PACKAGE_JSON_PATH = `${ROOT_PATH}/package.json`; +const VERSION_TXT_PATH = `${ROOT_PATH}/public/version.txt`; + +// This patch value is used to override the one from package.json +const currentPatch = fs.existsSync(PATCH_VERSION_PATH) ? Number(fs.readFileSync(PATCH_VERSION_PATH, 'utf-8')) : -1; +const packageJsonContent = fs.readFileSync(PACKAGE_JSON_PATH, 'utf-8'); +const currentVersion = JSON.parse(packageJsonContent).version; +const [major, minor] = currentVersion.split('.'); + +const newPatch = currentPatch + 1; +const newVersion = [major, minor, newPatch].join('.'); +const newPackageJsonContent = packageJsonContent.replace(`"version": "${currentVersion}"`, `"version": "${newVersion}"`); + +fs.writeFileSync(PATCH_VERSION_PATH, String(newPatch), 'utf-8'); +fs.writeFileSync(PACKAGE_JSON_PATH, newPackageJsonContent, 'utf-8'); +fs.writeFileSync(VERSION_TXT_PATH, newVersion, 'utf-8'); diff --git a/mobile/Gemfile b/mobile/Gemfile new file mode 100644 index 00000000..5c376f04 --- /dev/null +++ b/mobile/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gem "fastlane" + +plugins_path = File.join(File.dirname(__FILE__), 'android', 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/mobile/Gemfile.lock b/mobile/Gemfile.lock new file mode 100644 index 00000000..eca0b673 --- /dev/null +++ b/mobile/Gemfile.lock @@ -0,0 +1,218 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.6) + rexml + addressable (2.8.5) + public_suffix (>= 2.0.2, < 6.0) + artifactory (3.0.15) + atomos (0.1.3) + aws-eventstream (1.2.0) + aws-partitions (1.853.0) + aws-sdk-core (3.187.0) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.72.0) + aws-sdk-core (~> 3, >= 3.184.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.137.0) + aws-sdk-core (~> 3, >= 3.181.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.6) + aws-sigv4 (1.6.1) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + declarative (0.0.20) + digest-crc (0.6.5) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20231109) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.104.0) + faraday (1.10.3) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) + faraday (~> 1.0) + fastimage (2.2.7) + fastlane (2.217.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander (~> 4.6) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (>= 2.0.0, < 3.0.0) + naturally (~> 2.2) + optparse (~> 0.1.1) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 3) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + fastlane-plugin-versioning_android (0.1.1) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.52.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.2) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + rexml + webrick + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.29.0) + google-apis-core (>= 0.11.0, < 2.a) + google-cloud-core (1.6.0) + google-cloud-env (~> 1.0) + google-cloud-errors (~> 1.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.3.1) + google-cloud-storage (1.45.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.29.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) + mini_mime (~> 1.0) + googleauth (1.8.1) + faraday (>= 0.17.3, < 3.a) + jwt (>= 1.4, < 3.0) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.5) + domain_name (~> 0.5) + httpclient (2.8.3) + jmespath (1.6.2) + json (2.6.3) + jwt (2.7.1) + mini_magick (4.12.0) + mini_mime (1.1.5) + multi_json (1.15.0) + multipart-post (2.3.0) + nanaimo (0.3.0) + naturally (2.2.1) + optparse (0.1.1) + os (1.1.4) + plist (3.7.0) + public_suffix (5.0.4) + rake (13.1.0) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rexml (3.2.6) + rouge (2.0.7) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + security (0.1.3) + signet (0.18.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + terminal-notifier (2.0.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + trailblazer-option (0.1.2) + tty-cursor (0.7.1) + tty-screen (0.8.1) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unicode-display_width (2.5.0) + webrick (1.8.1) + word_wrap (1.0.0) + xcodeproj (1.23.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + x86_64-darwin-20 + x86_64-darwin-21 + x86_64-darwin-22 + x86_64-darwin-23 + +DEPENDENCIES + fastlane + fastlane-plugin-versioning_android + +BUNDLED WITH + 2.4.21 diff --git a/mobile/android/.gitignore b/mobile/android/.gitignore new file mode 100644 index 00000000..8522b3ef --- /dev/null +++ b/mobile/android/.gitignore @@ -0,0 +1,109 @@ +# Custom +app/release/ +app/debug/ +api-key.json +android.keystore +fastlane/README.md +fastlane/report.xml + +# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore + +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +# Android Profiling +*.hprof + +# Cordova plugins for Capacitor +capacitor-cordova-android-plugins + +# Copied web assets +app/src/main/assets/public + +# Generated Config files +app/src/main/assets/capacitor.config.json +app/src/main/assets/capacitor.plugins.json +app/src/main/res/xml/config.xml diff --git a/mobile/android/app/.gitignore b/mobile/android/app/.gitignore new file mode 100644 index 00000000..043df802 --- /dev/null +++ b/mobile/android/app/.gitignore @@ -0,0 +1,2 @@ +/build/* +!/build/.npmkeep diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle new file mode 100644 index 00000000..9258dead --- /dev/null +++ b/mobile/android/app/build.gradle @@ -0,0 +1,55 @@ +apply plugin: 'com.android.application' + +android { + namespace "org.mytonwallet.app" + compileSdkVersion rootProject.ext.compileSdkVersion + defaultConfig { + applicationId "org.mytonwallet.app" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + aaptOptions { + // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. + // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61 + ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~' + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +repositories { + flatDir{ + dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" + implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" + implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" + implementation project(':capacitor-android') + testImplementation "junit:junit:$junitVersion" + androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" + implementation project(':capacitor-cordova-android-plugins') + implementation "com.google.mlkit:barcode-scanning:$mlkitBarcodeScanningVersion" +} + +apply from: 'capacitor.build.gradle' + +try { + def servicesJSON = file('google-services.json') + if (servicesJSON.text) { + apply plugin: 'com.google.gms.google-services' + } +} catch(Exception e) { + logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work") +} diff --git a/mobile/android/app/capacitor.build.gradle b/mobile/android/app/capacitor.build.gradle new file mode 100644 index 00000000..037cb0ff --- /dev/null +++ b/mobile/android/app/capacitor.build.gradle @@ -0,0 +1,27 @@ +// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN + +android { + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } +} + +apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" +dependencies { + implementation project(':capacitor-mlkit-barcode-scanning') + implementation project(':capacitor-app') + implementation project(':capacitor-dialog') + implementation project(':capacitor-haptics') + implementation project(':capacitor-status-bar') + implementation project(':capgo-capacitor-native-biometric') + implementation project(':mauricewegner-capacitor-navigation-bar') + implementation project(':capacitor-plugin-safe-area') + implementation project(':native-bottom-sheet') + +} + + +if (hasProperty('postBuildExtras')) { + postBuildExtras() +} diff --git a/mobile/android/app/proguard-rules.pro b/mobile/android/app/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/mobile/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java b/mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java new file mode 100644 index 00000000..f2c2217e --- /dev/null +++ b/mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.getcapacitor.myapp; + +import static org.junit.Assert.*; + +import android.content.Context; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("com.getcapacitor.app", appContext.getPackageName()); + } +} diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..0787a7b3 --- /dev/null +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/app/src/main/ic_launcher-playstore.png b/mobile/android/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..c2608f1d7c4301217b3e94572e53358b429e3f8e GIT binary patch literal 129639 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

B~tXD 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�V1L*I*rU%z)4uFlv2<} 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 Kk1Dn7qW%wnrS|{; literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/java/org/mytonwallet/app/MainActivity.java b/mobile/android/app/src/main/java/org/mytonwallet/app/MainActivity.java new file mode 100644 index 00000000..691604f4 --- /dev/null +++ b/mobile/android/app/src/main/java/org/mytonwallet/app/MainActivity.java @@ -0,0 +1,49 @@ +package org.mytonwallet.app; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.os.Bundle; +import android.os.Handler; +import android.view.View; +import androidx.core.splashscreen.SplashScreen; +import androidx.interpolator.view.animation.FastOutSlowInInterpolator; +import com.getcapacitor.BridgeActivity; + +public class MainActivity extends BridgeActivity { + private boolean keep = true; + private final int DELAY = 1000; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + SplashScreen splashScreen = SplashScreen.installSplashScreen(this); + splashScreen.setKeepOnScreenCondition(() -> keep); + splashScreen.setOnExitAnimationListener(splashScreenView -> { + AnimatorSet animationSet = new AnimatorSet(); + + View view = splashScreenView.getView(); + ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y, 4f); + ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, View.SCALE_X, 4f); + ObjectAnimator opacity = ObjectAnimator.ofFloat(view, View.ALPHA, 0.0f); + + animationSet.setInterpolator(new FastOutSlowInInterpolator()); + animationSet.setDuration(350L); + animationSet.playTogether(scaleX, scaleY, opacity); + + animationSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + splashScreenView.remove(); + } + }); + + animationSet.start(); + }); + + Handler handler = new Handler(); + handler.postDelayed(() -> keep = false, DELAY); + } +} diff --git a/mobile/android/app/src/main/res/drawable-hdpi/splash.9.png b/mobile/android/app/src/main/res/drawable-hdpi/splash.9.png new file mode 100644 index 0000000000000000000000000000000000000000..325fe87ed1d1cbfa417816a0927855d8305faf11 GIT binary patch literal 23686 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 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-night-ldpi/splash.9.png b/mobile/android/app/src/main/res/drawable-night-ldpi/splash.9.png new file mode 100644 index 0000000000000000000000000000000000000000..3e458038995ec086523106728208a7cf1ed6f87c GIT binary patch literal 7265 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 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-night-mdpi/splash.9.png b/mobile/android/app/src/main/res/drawable-night-mdpi/splash.9.png new file mode 100644 index 0000000000000000000000000000000000000000..88056e3a0dca4a846bc88880cd7fa9687afb0a11 GIT binary patch literal 12478 zcmeHuS5#A7*RH+_qJUzd2+~AA=^z~e0YT|XuR#bM0up*jc%`Z|NeBTUAV`rCLQ5b5 zK|y*6QbTAWAT{(J0*CLvIQRcJcV~PT>t>I=$C_iVXU+M{IiLOhrO|WdYn<0ET)4oj z`{J3&g$oxo&(|LY`tv8Ksy6V23sSMV&oslfrlTH;)Fx zchxACf5R{HUpQdUys&;V=E9@^!^QgN|9#=Ve)#_q50Y0~nh_C~E;RD}KfwIthS|sJ z;#~Nw_ajq+rWtr*__%t3D6xOnYpx#jR{g5RyE9J4Cycd�Lt^ZP78pH_hC@6?eB&m!Bhn{j_(Y%71&#$A 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 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-night-xhdpi/splash.9.png b/mobile/android/app/src/main/res/drawable-night-xhdpi/splash.9.png new file mode 100644 index 0000000000000000000000000000000000000000..c53c2e16c1681df7a0b7bc53bf344549f123f666 GIT binary patch literal 48470 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ℜ(Z^dL!hJ*qfQ%-@1tKpg;*2!by)rb$gRO3 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 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-night-xxhdpi/splash.9.png b/mobile/android/app/src/main/res/drawable-night-xxhdpi/splash.9.png new file mode 100644 index 0000000000000000000000000000000000000000..2eb9d37ca815a40bd5c4e22ee631d0250112b73c GIT binary patch literal 70502 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 G%l`n(x3K{L literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-night-xxxhdpi/splash.9.png b/mobile/android/app/src/main/res/drawable-night-xxxhdpi/splash.9.png new file mode 100644 index 0000000000000000000000000000000000000000..08b8ace94f5f81e6ed41882806bcfaec6e8117e8 GIT binary patch literal 96417 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 LaIa3&Ci?#YMwpT+ literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-night/splash.9.png b/mobile/android/app/src/main/res/drawable-night/splash.9.png new file mode 100644 index 0000000000000000000000000000000000000000..b12b8beccf480eda6040de3fe717601ff0f6dd88 GIT binary patch literal 7510 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 V1!i$Z>b#WZv5uj3m8Nave*s0GMJ50M literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..c7bd21db --- /dev/null +++ b/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/mobile/android/app/src/main/res/drawable-xhdpi/splash.9.png b/mobile/android/app/src/main/res/drawable-xhdpi/splash.9.png new file mode 100644 index 0000000000000000000000000000000000000000..7e1da8994ba2ea53905327a0289b0b3b7d867fa8 GIT binary patch literal 46255 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 V_Zop&3D{!LgS!TI%5U4h`XBBp208!$ literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable-xxxhdpi/splash.9.png b/mobile/android/app/src/main/res/drawable-xxxhdpi/splash.9.png new file mode 100644 index 0000000000000000000000000000000000000000..69d18a9df2ba68ae41e5cd12a3b2c424a5b6869d GIT binary patch literal 90091 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 OftH${YMJu0H~$B_5_EL{ literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..d5fccc53 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/app/src/main/res/drawable/icon_svg.xml b/mobile/android/app/src/main/res/drawable/icon_svg.xml new file mode 100644 index 00000000..e01a862b --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/icon_svg.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + diff --git a/mobile/android/app/src/main/res/drawable/launch_splash.xml b/mobile/android/app/src/main/res/drawable/launch_splash.xml new file mode 100644 index 00000000..26c89c78 --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/launch_splash.xml @@ -0,0 +1,6 @@ + + diff --git a/mobile/android/app/src/main/res/drawable/splash.9.png b/mobile/android/app/src/main/res/drawable/splash.9.png new file mode 100644 index 0000000000000000000000000000000000000000..288a149b7c57c7b24cfa44bdfa08eb00e430b472 GIT binary patch literal 12115 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 R6w9;dKQwtzu45nZe*m0 + + + + diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..4ae7d123 --- /dev/null +++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..4ae7d123 --- /dev/null +++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c5a38260e9a8ba545c1b6784de895e5b2acc840a GIT binary patch literal 4400 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 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..dcd9e4c77380061659d61754d7e1ec264fcd42ef GIT binary patch literal 2822 zcmV+h3;Fa?Nk&Hg3IG6CMM6+kP&iET3IG5vp+G1Q@29qH8;1Ws?b#{&{LjCDphS`= zgAT)K4FBN`+xB-#vu)dF01EOiW6M z{-@z?Hzn(kpQ`)wvL?58|5FI>n(pChd(yi``~$cy#Y3gu-0zPX3RZ7DZvZ^AuM2UV zR`{_8wl5fXbC}wdLO%E&k*jgHSwELy_h)wwVXbolm@iEamlVAZw#NJpg*QlY4LxN5 zE2njcicPJcs@rdu>r*JcQ4Nm4u4vu-BUSaK#)hIq55U3c0en3-1A!&UVD*#K;S#Kx zjd2&jtJo zYfE3jBuP!PE#P2vgBIC-ZzbtgD5@d%*g_yTK9uw*2J-Hb6jMr|x$NTs|AU6eT;M&o zB)RrN4@r%_1B!cwjtM@1@-6R-xMdJ*kZ~G-r=e{zz_&8K3Yj+yUAWN;;F}OCk}ay} zho?MZhM6d__c*C%8S3B51v!K6Y%La=x&SsuoD07 zeE?Q+D41YDIc!zbeNV6W8=z>UOZqo#&&4qh$g!q}mo|P2a;qy62$bYM$w@niY7%49lUn)TsZX2*9H^3F9I#p}ml~l1}ejQ!iYEKo~u4+YVhgt=2^tTUQ8T&{I zlzdlG$<7YC`VGIVy3P;pTU@Gb zXm3$kZ!uVp>)PK{0$U=t!9*F?!omRl>l|n{6vu3W@do`0zXQ)I7$3Y0YoI9F5N}Z$ z_cf$APlN{+>Pc8<2?>#ryuI_*Blw;)5#UZ|ENN7x)H* zk14>o++p%;{*SMdbbATpJqzm;+_T}cpTIv{QqAN-+hJ#k_5;7Sm+_uUV8M(a>x?}x zUdFijfIr4j5&&}Rne<2*K${?Qp9ZX{GKa4M=;$a(Hr7R&TyF#C+(D)sU`@2im4>~q zqfCU0KKLqJQdpnO0KX~NUj`_Asg7+HZ~`Vd$}6yDj0G94Y9sbyOb@ytHYl0XNRL^t z-jnHS^cz|^2ifKBc` zTQL4!L!;n5N6CQCq_ADKoM`E7crxf(!4Apwj0Kc8lSV*>qohH$GzsJS-mW`{y!|pbUz6T9OV^Q^N<&QtI>4s zC5-7oHyESu<2HI8c;h*E#Ze|gVKHp4Evf!%=WV|Q-7K3o{RT-&1&+W(M@hndq=}5R zmjUP8!L|z6)LcgQa_mWtl3+V?i_ABl0B93r?o)t&xJlK2wIw)8v>#Yr+jMVOm>)d zK$mfUV?6fZ|0XYjvL->~+77=zGxw7n7>i$wIS%a|#_fTZ6gl&pt@5QoG?XfP=hjna z@VFgD>9Er!rB%&;XVAoYs&>dFr9irzwkyzHHH+YpwvP;%mhyog*jFo9J_1 zy`}zXxKCmE(A9CmJ&>v)uuH~=v47+WjL2yS2Y^{|G;|0oI0;?lDp-y!9hM`RZW}s#L4{((?cZo`Bs_SosL^*gSkSxVo zoRRT+ct~wF8?NTj2UWeC(lEmp&`0W63Yl{9s9f;7_h^kVc@$zN_GurTsG zL>WGU!D^BZxlxX$_+q$tM+-P)It=@OixW@49i2v|TDHB9-ye4xo;w8`EQS__r7${n zI~2#u;d}Vt{2@l95zdJ zyTAOs1(eMQllsCMO@TnKpWtBhEO<|H4Jowkd3WTkZoLg&uqG%3CR+f1e|*rwxG7$e zYM0~dei>$~X(24z2)!k#$$v1P|6wzLm8_7MY*F>8GX4f$_%mGUXJJ`fj<%_7C1xvG zA##GxP`Y;B!0E8YlnijDnItU(_)*4}AnV@b>;lW?o1l={vlu9RKvJ9+e-pzPAkaROX;D?(e-DFV`DfhT@KyJK3 zYE-5LoER5A7JgOOlSLTk8u|omcxTnnvyi3xHv(2>ltOKy2Vmc{0KRS;fk4J{5J;5N z?gD)E#%18ZqnlvicNXADR0xhu^-$+iwVLUpVNNaJ4IiW8iy4uBJU-`PFOiujr)E ze%)*jm3sI6cociVf3_D>sVC(+h5m@H=^n$?^rUwU*FQve`^i~({wwew%9_&7t$&!& Y2}u)QS&){Iw&0bCNeR*VumAr~c9X(wQ~&?~ literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..dedf69059ccce7d4866a6ca350afa5681eafddfd GIT binary patch literal 6444 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 G90LF`%#jcP literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..cd91d49454f6b20a0a91874cd5e4eae8d0dfd17b GIT binary patch literal 2562 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 YvuO&Ni7a3$E;)go+J z+~y%gVS(aZk-h4oxzao&Ijd646#)#&u!o|qN#`M{wu$teJ-t!Dhf#r(@%(bNOUv3 z5&+qCp;*>pzvdxb&c9{PzgrCqM7mqvY?U*z20Wup!=7gH(QGUww8N!_M^}{ znZ$fXl{VfY9^8z609eq$T`BvpX;B2vIr z=p?B+F-G3bpI~F4PY@af0I$+KIVBCp&gDSO!o{DGY;SlC08ybnKwuPbDRS2+h z|B-kAT(f@oJ^2kz0^mKawFmDeL25qhSOu=!l4kIC-hK|=h5yW{6~7DP3PiG9lHY4s zWu1B1dBI3Yc^qju_wcyT&z)SRbKK7Sg4&Xr^4w+5ZWWQlH0QP?ZQ?#}3;*odWxPc+ zHza8bBeyZmDxw+X_lhbCE;tC>b$Xd`bXdfj1}E z8vy5Lj>z_c3f3@=GdWi{z)XmJ%KHpn>E~5m-Fo8&_Lg1Fcx#9vIvM97)7?&6y*=|* z+)<4r4#eTU!x3xLJ`T(+#Wfa3N4qnpclf=cXFRKn%=7FJZ?l2|kX%iYreJcy+jCq2 z{B4tz?+FyNf(F29Lz33PL~m&g7yKn@G|a4&#`9aBhrlFFVVqSo zc0z2=fM0;(2yJ~882tr{CaNT{{ysd5`y~_i;Q3;=r;9}s-Fg0wHL|t6#xGhWrdNRa zn~s}ww`xZ_{UjOdVS=}`f-7I`Xe!XaA`M9@DoMROaNJv#11k(kSsp znz`B zHvqW%=`2+wt)-`$dt71G*UlZnIrdfU{AL~8=hfPC$-uRB*Le#2F3{ww+Ie8bL?V2l zC$Nb9k}3lUeKMpwGL%XtfzgVgN}KmUcS-YD^OlW<^&Olj;6#^Ym(t-_LFp)6ZnL(b zC%p}CY|60%JkkFQTn9u2`cMzY>GEWkG>T+yhG-!U{+49CA<2h&I8xw7@XVbbq^YINn0)Z!^@|4O9cZD&Jsn5TLVa77rt)l0n4 zCf!kf7hFuCqZFQQUW8*M9@2NWNo<%e_DJ1)>SyatQpp1En4$KsaQI@6R8qdxcECNY wVu1eO_E){%;=|Jb`>K3C+0%}U21vVi3gFa@B!K8~+5bho`hU|ui2rgO0{Nvp0{{R3 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..f519778385ad82c1bb0f03a25ca69e12e2753e27 GIT binary patch literal 3682 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 AvH$=8 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..f3cb39a3f1bdc25feb6c06629a3b3b6b86dfc2b9 GIT binary patch literal 6320 zcmV;h7*FR?Nk&Gf7ytlQMM6+kP&iDR7ytk-U%(d-6^DYhZKQboS>FpGA|@am(WPi+ zreKags!nj)FMg#c(NRgY{(sSwe_TzwNOX5=dI#Rz-I*C&)5;hQjc(V#6_B}}`Rglb z(+_DANp97h>X!Cwd&v%$FzG#1&NgJy+65rdU799$cXv3)jU?Nua`wNdXVgFEeK#B; zcbAC3k!`zatLx8FW}GpOm>DG{M+(ESZSC0e+qP}{|AR6s6EH@Tl`?(mRLN*;Rkq5H zhk@I+jjCI{$@>N!ZQB-0N-t=xS%bR^PUQZN*7yD8dYq7$1Y2q zs6GQ(N4PUC5w2|+?oQ;9!)P`Uu455I&iLRGxtkpOxWm8>%y^ zrk5FyQG)wIjwDHuGy<#uXZyO}y>OYCW~#aiv(;JEWk-@ES&{KZ8}6G%l0|57Be%OnJs2AgP0|gn3+#X+uarQ9R#in+Hh>E+^SBZ zQSvVC|NqsU3fS$eSqq)r zVgf+`z$CFkMt9rYgq+=q7)XuM>%RlHkrb)3eB`4~Kmu#l@VA0qFr?fV`=*jG@+j9YXx7J_KQIYkg6RK(;RBB;-888OChfzd{r5@q&-Q7j?MIuR zAA+}n$9)HW&7gm6(9mV1r07zDDZvyXorZN@r*aEZQc`aH2;L%1EL4y-rTn;civ`xB zk0HB`r`HfVd<(kBr^{|qbgH1rB%LPXH2Lxw@=Xi^Qm#Z3o_qJl_?$~DYSU^%hnND~KK-U8)B-_@S~zE(u57`odAr}b0LHF-oX zbU*y?!&%}=h-uMlA5P^ts7+D#nr!UD?buk?HQ{k}C? z#n4^r>=rK%=OuEkJ1#~m4N)?{ym`lVaM_#LRniwRZ)b{5viKwl4muliNs|)YG$wr~ z4*teU_H|KqS3QjX7Nm?hwfEc4nL?2bOAe6@-BZDGsJEYb3eT=mjBg+9@?6HY^^%LI zqL5muq(J5>OwPys&6_sLX%lHLYgA_kr)ph;eW?0!tou!H!1%w2oEuu%{99>7~8+j>XHk2k4mMkh0wg>C35D*$miSr%v)65EWL36XmtoS`aJx$0iVQnb)-zvSbuXu1wq}SKP{AN!`w^^ zrv8Ri1OXtVKn`ZT6Wd6LKf5>%`0SjFKBLlPT$4ei!k29$rM{6?){ z^EL*hC!VvhXqi?FtkqY*+vqMhXnWn$f|>8z65gQbAV)a+!S;F1sIlC9!shNQVN+&# z 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 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp new file mode 100644 index 0000000000000000000000000000000000000000..09d4751a49ed0758aef6262a635514f0cfab63d1 GIT binary patch literal 3398 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 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..d5392d5ee5db99aadda1fef016f8fa1dbf28dbb0 GIT binary patch literal 4024 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 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp new file mode 100644 index 0000000000000000000000000000000000000000..2430b97d2ff27718e12632836a9d17a4c4128f18 GIT binary patch literal 18544 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 MhE%3*lpr*uCK#CcS^xk5 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..5af97ceaad89c6795425c993a3471ce52424b99f GIT binary patch literal 6428 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 KXwb`cT@3(h=E}PO literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..06f9cfa3ae102e159340da36998ec77df1aa67a2 GIT binary patch literal 13916 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 B_lN)h literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..ec2e27e321057f103af5544386114d3afcc7ed1b GIT binary patch literal 9000 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�QZsDf32GSH}g$9 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 literal 0 HcmV?d00001 diff --git a/mobile/android/app/src/main/res/values-v31/styles.xml b/mobile/android/app/src/main/res/values-v31/styles.xml new file mode 100644 index 00000000..6f91d8b9 --- /dev/null +++ b/mobile/android/app/src/main/res/values-v31/styles.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/mobile/android/app/src/main/res/values/ic_launcher_background.xml b/mobile/android/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 00000000..c5d5899f --- /dev/null +++ b/mobile/android/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + \ No newline at end of file diff --git a/mobile/android/app/src/main/res/values/strings.xml b/mobile/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..42dac8fa --- /dev/null +++ b/mobile/android/app/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + + MyTonWallet + MyTonWallet + org.mytonwallet.app + ton + tc + diff --git a/mobile/android/app/src/main/res/values/styles.xml b/mobile/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..be874e54 --- /dev/null +++ b/mobile/android/app/src/main/res/values/styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/mobile/android/app/src/main/res/xml/file_paths.xml b/mobile/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 00000000..bd0c4d80 --- /dev/null +++ b/mobile/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java b/mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java new file mode 100644 index 00000000..02973278 --- /dev/null +++ b/mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java @@ -0,0 +1,18 @@ +package com.getcapacitor.myapp; + +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} diff --git a/mobile/android/build.gradle b/mobile/android/build.gradle new file mode 100644 index 00000000..9cc72cb6 --- /dev/null +++ b/mobile/android/build.gradle @@ -0,0 +1,29 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.0.0' + classpath 'com.google.gms:google-services:4.3.15' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +apply from: "variables.gradle" + +allprojects { + repositories { + google() + mavenCentral() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/mobile/android/capacitor.settings.gradle b/mobile/android/capacitor.settings.gradle new file mode 100644 index 00000000..9242b899 --- /dev/null +++ b/mobile/android/capacitor.settings.gradle @@ -0,0 +1,30 @@ +// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN +include ':capacitor-android' +project(':capacitor-android').projectDir = new File('../../node_modules/@capacitor/android/capacitor') + +include ':capacitor-mlkit-barcode-scanning' +project(':capacitor-mlkit-barcode-scanning').projectDir = new File('../../node_modules/@capacitor-mlkit/barcode-scanning/android') + +include ':capacitor-app' +project(':capacitor-app').projectDir = new File('../../node_modules/@capacitor/app/android') + +include ':capacitor-dialog' +project(':capacitor-dialog').projectDir = new File('../../node_modules/@capacitor/dialog/android') + +include ':capacitor-haptics' +project(':capacitor-haptics').projectDir = new File('../../node_modules/@capacitor/haptics/android') + +include ':capacitor-status-bar' +project(':capacitor-status-bar').projectDir = new File('../../node_modules/@capacitor/status-bar/android') + +include ':capgo-capacitor-native-biometric' +project(':capgo-capacitor-native-biometric').projectDir = new File('../../node_modules/@capgo/capacitor-native-biometric/android') + +include ':mauricewegner-capacitor-navigation-bar' +project(':mauricewegner-capacitor-navigation-bar').projectDir = new File('../../node_modules/@mauricewegner/capacitor-navigation-bar/android') + +include ':capacitor-plugin-safe-area' +project(':capacitor-plugin-safe-area').projectDir = new File('../../node_modules/capacitor-plugin-safe-area/android') + +include ':native-bottom-sheet' +project(':native-bottom-sheet').projectDir = new File('../plugins/native-bottom-sheet/android') diff --git a/mobile/android/fastlane/Appfile b/mobile/android/fastlane/Appfile new file mode 100644 index 00000000..29fba405 --- /dev/null +++ b/mobile/android/fastlane/Appfile @@ -0,0 +1,2 @@ +json_key_file("./api-key.json") +package_name("org.mytonwallet.app") diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile new file mode 100644 index 00000000..b026c738 --- /dev/null +++ b/mobile/android/fastlane/Fastfile @@ -0,0 +1,78 @@ +default_platform(:android) + +def update_version_and_build_number + beta_build_number = google_play_track_version_codes( + package_name: CredentialsManager::AppfileConfig.try_fetch_value(:package_name), + track: "beta", + json_key: CredentialsManager::AppfileConfig.try_fetch_value(:json_key_file), + )[0] + + production_build_number = google_play_track_version_codes( + package_name: CredentialsManager::AppfileConfig.try_fetch_value(:package_name), + track: "production", + json_key: CredentialsManager::AppfileConfig.try_fetch_value(:json_key_file), + )[0] + + build_number = [beta_build_number, production_build_number].max() + 1 + version = JSON.parse(File.read("../../../package.json"))["version"] + + android_set_version_name(version_name: version) + android_set_version_code(version_code: build_number) +end + +platform :android do + desc "Deploy a new Beta Build to the Google Play" + + $gradle_data = nil + + before_all do |lane| + $gradle_data = File.read("../app/build.gradle") + end + + lane :beta do + update_version_and_build_number + + keystore_path = Dir.pwd + "/../android.keystore" + + gradle( + task: "clean bundle", + build_type: "Release", + properties: { + "android.injected.signing.store.file" => keystore_path, + "android.injected.signing.store.password" => ENV["ANDROID_KEYSTORE_PASSWORD"], + "android.injected.signing.key.alias" => "key0", + "android.injected.signing.key.password" => ENV["ANDROID_KEYSTORE_PASSWORD"] + } + ) + + upload_to_play_store(track: "beta") + end + + desc "Release a new version to the Google Play" + lane :release do + update_version_and_build_number + + keystore_path = Dir.pwd + "/../android.keystore" + + gradle( + task: "clean bundle", + build_type: "Release", + properties: { + "android.injected.signing.store.file" => keystore_path, + "android.injected.signing.store.password" => ENV["ANDROID_KEYSTORE_PASSWORD"], + "android.injected.signing.key.alias" => "key0", + "android.injected.signing.key.password" => ENV["ANDROID_KEYSTORE_PASSWORD"] + } + ) + + upload_to_play_store(track: "production") + end + + after_all do |lane| + File.write("../app/build.gradle", $gradle_data) + end + + error do |lane, exception| + File.write("../app/build.gradle", $gradle_data) + end +end diff --git a/mobile/android/fastlane/Pluginfile b/mobile/android/fastlane/Pluginfile new file mode 100644 index 00000000..052b0905 --- /dev/null +++ b/mobile/android/fastlane/Pluginfile @@ -0,0 +1,5 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-versioning_android' diff --git a/mobile/android/gradle.properties b/mobile/android/gradle.properties new file mode 100644 index 00000000..2e87c52f --- /dev/null +++ b/mobile/android/gradle.properties @@ -0,0 +1,22 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true diff --git a/mobile/android/gradle/wrapper/gradle-wrapper.jar b/mobile/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..ccebba7710deaf9f98673a68957ea02138b60d0a GIT binary patch literal 61608 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 literal 0 HcmV?d00001 diff --git a/mobile/android/gradle/wrapper/gradle-wrapper.properties b/mobile/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..761b8f08 --- /dev/null +++ b/mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/mobile/android/gradlew b/mobile/android/gradlew new file mode 100755 index 00000000..79a61d42 --- /dev/null +++ b/mobile/android/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/mobile/android/gradlew.bat b/mobile/android/gradlew.bat new file mode 100644 index 00000000..93e3f59f --- /dev/null +++ b/mobile/android/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mobile/android/settings.gradle b/mobile/android/settings.gradle new file mode 100644 index 00000000..3b4431d7 --- /dev/null +++ b/mobile/android/settings.gradle @@ -0,0 +1,5 @@ +include ':app' +include ':capacitor-cordova-android-plugins' +project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/') + +apply from: 'capacitor.settings.gradle' \ No newline at end of file diff --git a/mobile/android/variables.gradle b/mobile/android/variables.gradle new file mode 100644 index 00000000..409c2575 --- /dev/null +++ b/mobile/android/variables.gradle @@ -0,0 +1,17 @@ +ext { + minSdkVersion = 22 + compileSdkVersion = 33 + targetSdkVersion = 33 + androidxActivityVersion = '1.7.0' + androidxAppCompatVersion = '1.6.1' + androidxCoordinatorLayoutVersion = '1.2.0' + androidxCoreVersion = '1.10.0' + androidxFragmentVersion = '1.5.6' + coreSplashScreenVersion = '1.0.0' + androidxWebkitVersion = '1.6.1' + junitVersion = '4.13.2' + androidxJunitVersion = '1.1.5' + androidxEspressoCoreVersion = '3.5.1' + cordovaAndroidVersion = '10.1.1' + mlkitBarcodeScanningVersion = '17.2.0' +} diff --git a/mobile/ios/.gitignore b/mobile/ios/.gitignore new file mode 100644 index 00000000..4e9639a4 --- /dev/null +++ b/mobile/ios/.gitignore @@ -0,0 +1,20 @@ +App/build +App/Pods +App/output +App/App/public +DerivedData +xcuserdata + +# Cordova plugins for Capacitor +capacitor-cordova-ios-plugins + +# Generated Config files +App/App/capacitor.config.json +App/App/config.xml + +# fastlane files +App/*.ipa +App/*.zip +App/*.p8 +App/fastlane/report.xml +App/fastlane/README.md diff --git a/mobile/ios/App/App.xcodeproj/project.pbxproj b/mobile/ios/App/App.xcodeproj/project.pbxproj new file mode 100644 index 00000000..53c2f5fc --- /dev/null +++ b/mobile/ios/App/App.xcodeproj/project.pbxproj @@ -0,0 +1,425 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 0EB354256D6B4E26B50364CD /* Pods_MyTonWallet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1BABB2300AAC81FD0F3E47EB /* Pods_MyTonWallet.framework */; }; + 2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; }; + 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; }; + 504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; }; + 504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; }; + 504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; }; + 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; + 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1BABB2300AAC81FD0F3E47EB /* Pods_MyTonWallet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MyTonWallet.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = ""; }; + 50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = ""; }; + 504EC3041FED79650016851F /* MyTonWallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyTonWallet.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; + 65D4981FD7A0308A5E60DEDC /* Pods-MyTonWallet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MyTonWallet.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MyTonWallet/Pods-MyTonWallet.debug.xcconfig"; sourceTree = ""; }; + 66B73F8B8E24A3D107892F83 /* Pods-MyTonWallet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MyTonWallet.release.xcconfig"; path = "Pods/Target Support Files/Pods-MyTonWallet/Pods-MyTonWallet.release.xcconfig"; sourceTree = ""; }; + AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = ""; }; + CE138AE22AC7209B00BE5802 /* MyTonWallet.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MyTonWallet.entitlements; sourceTree = ""; }; + FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 504EC3011FED79650016851F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0EB354256D6B4E26B50364CD /* Pods_MyTonWallet.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 27E2DDA53C4D2A4D1A88CE4A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1BABB2300AAC81FD0F3E47EB /* Pods_MyTonWallet.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 504EC2FB1FED79650016851F = { + isa = PBXGroup; + children = ( + CE138AE22AC7209B00BE5802 /* MyTonWallet.entitlements */, + 504EC3061FED79650016851F /* App */, + 504EC3051FED79650016851F /* Products */, + 7F8756D8B27F46E3366F6CEA /* Pods */, + 27E2DDA53C4D2A4D1A88CE4A /* Frameworks */, + ); + sourceTree = ""; + }; + 504EC3051FED79650016851F /* Products */ = { + isa = PBXGroup; + children = ( + 504EC3041FED79650016851F /* MyTonWallet.app */, + ); + name = Products; + sourceTree = ""; + }; + 504EC3061FED79650016851F /* App */ = { + isa = PBXGroup; + children = ( + 50379B222058CBB4000EE86E /* capacitor.config.json */, + 504EC3071FED79650016851F /* AppDelegate.swift */, + 504EC30B1FED79650016851F /* Main.storyboard */, + 504EC30E1FED79650016851F /* Assets.xcassets */, + 504EC3101FED79650016851F /* LaunchScreen.storyboard */, + 504EC3131FED79650016851F /* Info.plist */, + 2FAD9762203C412B000D30F8 /* config.xml */, + 50B271D01FEDC1A000F3C39B /* public */, + ); + path = App; + sourceTree = ""; + }; + 7F8756D8B27F46E3366F6CEA /* Pods */ = { + isa = PBXGroup; + children = ( + FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */, + AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */, + 65D4981FD7A0308A5E60DEDC /* Pods-MyTonWallet.debug.xcconfig */, + 66B73F8B8E24A3D107892F83 /* Pods-MyTonWallet.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 504EC3031FED79650016851F /* MyTonWallet */ = { + isa = PBXNativeTarget; + buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "MyTonWallet" */; + buildPhases = ( + 6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */, + 504EC3001FED79650016851F /* Sources */, + 504EC3011FED79650016851F /* Frameworks */, + 504EC3021FED79650016851F /* Resources */, + 9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MyTonWallet; + productName = App; + productReference = 504EC3041FED79650016851F /* MyTonWallet.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 504EC2FC1FED79650016851F /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 920; + LastUpgradeCheck = 920; + TargetAttributes = { + 504EC3031FED79650016851F = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 504EC2FB1FED79650016851F; + productRefGroup = 504EC3051FED79650016851F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 504EC3031FED79650016851F /* MyTonWallet */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 504EC3021FED79650016851F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */, + 50B271D11FEDC1A000F3C39B /* public in Resources */, + 504EC30F1FED79650016851F /* Assets.xcassets in Resources */, + 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */, + 504EC30D1FED79650016851F /* Main.storyboard in Resources */, + 2FAD9763203C412B000D30F8 /* config.xml in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-MyTonWallet-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MyTonWallet/Pods-MyTonWallet-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 504EC3001FED79650016851F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 504EC3081FED79650016851F /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 504EC30B1FED79650016851F /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 504EC30C1FED79650016851F /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 504EC3101FED79650016851F /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 504EC3111FED79650016851F /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 504EC3141FED79650016851F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 504EC3151FED79650016851F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 504EC3171FED79650016851F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 65D4981FD7A0308A5E60DEDC /* Pods-MyTonWallet.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + CODE_SIGN_ENTITLEMENTS = MyTonWallet.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = Y54Z4K69Z9; + INFOPLIST_FILE = App/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MARKETING_VERSION = 1.16.1; + OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; + PRODUCT_BUNDLE_IDENTIFIER = org.mytonwallet.app; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 504EC3181FED79650016851F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 66B73F8B8E24A3D107892F83 /* Pods-MyTonWallet.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + CODE_SIGN_ENTITLEMENTS = MyTonWallet.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y54Z4K69Z9; + INFOPLIST_FILE = App/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MARKETING_VERSION = 1.16.1; + PRODUCT_BUNDLE_IDENTIFIER = org.mytonwallet.app; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "MyTonWallet Production profile"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 504EC3141FED79650016851F /* Debug */, + 504EC3151FED79650016851F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "MyTonWallet" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 504EC3171FED79650016851F /* Debug */, + 504EC3181FED79650016851F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 504EC2FC1FED79650016851F /* Project object */; +} diff --git a/mobile/ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/mobile/ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..2db664d7 --- /dev/null +++ b/mobile/ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/mobile/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/mobile/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/ios/App/App.xcodeproj/xcshareddata/xcschemes/MyTonWallet.xcscheme b/mobile/ios/App/App.xcodeproj/xcshareddata/xcschemes/MyTonWallet.xcscheme new file mode 100644 index 00000000..2691e580 --- /dev/null +++ b/mobile/ios/App/App.xcodeproj/xcshareddata/xcschemes/MyTonWallet.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/ios/App/App.xcworkspace/contents.xcworkspacedata b/mobile/ios/App/App.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..b301e824 --- /dev/null +++ b/mobile/ios/App/App.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/mobile/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/mobile/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/ios/App/App.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/mobile/ios/App/App.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..0c67376e --- /dev/null +++ b/mobile/ios/App/App.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/mobile/ios/App/App/AppDelegate.swift b/mobile/ios/App/App/AppDelegate.swift new file mode 100644 index 00000000..c3cd83b5 --- /dev/null +++ b/mobile/ios/App/App/AppDelegate.swift @@ -0,0 +1,49 @@ +import UIKit +import Capacitor + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { + // Called when the app was launched with a url. Feel free to add additional processing here, + // but if you want the App API to support tracking app url opens, make sure to keep this call + return ApplicationDelegateProxy.shared.application(app, open: url, options: options) + } + + func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { + // Called when the app was launched with an activity, including Universal Links. + // Feel free to add additional processing here, but if you want the App API to support + // tracking app url opens, make sure to keep this call + return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler) + } + +} diff --git a/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png b/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..bd28e34c75b017222e958bed56ea008cce757e94 GIT binary patch literal 877 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 literal 0 HcmV?d00001 diff --git a/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png b/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..e6503aecd72cb3d5fbb1b4e62674bcf10f990c5b GIT binary patch literal 2736 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�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%0D(1*>8Uq$Wp^#ei0VHch% z?4mj{8>x=Q#>n@Xay!=Ao^^C`BH62~M^RFRNSVGg2FT{rEC4Q{#OkGWcrUBDKR44@ zkRUzZp{r2o^^0K7sxXA4Q+D>x$7(OR1ns@^lH9SvT&=}^4{|EAs2zV18e80hcYtj? z@n85=h)}|HSC)0Pg6_^dHhBn*4@DU8Mn1f!Vrjf`Ywpu)C%UY@@EIMFzTb4EVN*EP z*?uRk8Rrh4TTM2dfghq%$xZ&T2>*8^VAeMVt%RDiakwMF2uI`HOcZv%D6N%u;;3x6mTY}r_OvU!my(nDcZaX@m3xGXyqGk-ml%I~WhFx9-;)#6TF5`Y%k9uI zsN9y8X@7bI3l1{K%RwLgM$b>Vg`>twCB|(}R_b7%lk@tl!s^-+e<14{DfS>5qL+hM zq~Kw8Vc%t_^l@0k;jH}DIz6Mru|x#I55ownqsED1o0z#7zwm7HwUz9N`Nz56{j&I? zJWKX|pT|cu;l4l|+uL-nYf!@@!J`aSSxHl)JLoCXgV2PWn>vxjY z5|5DqOQUdZ5-+XDZ>?Ggsi-Q@!b$+iQD-ArB7Z*2L|<=|t>a;|F8>EJyGYWCPjOyZ z8`AZQhY5qD1Qx34MvH1lqP1Th2J2ZH4r%)zw(8-Q1N04`QrH@lDB6RUC;HK##1A{0 zx2Qve#^02N0!OEV>Qu;l$GwU>ZXPU4A~n!oL$+?Qp1+y3cx3oaAhoK4e2RUs?ex=RD`}%h9;~rEzulnL(+p-+oyc|TR(~~r_82S!!+o(f9VC#o-2G&%QN|9VIowDo7QWzP(Tj7HoxEhZUsHZG_!$QIZLOb zIP6a6jyV1-EHBk1N8)*5lYr7Tg`(-MZKA-JtEVU-T5fMu^6N;?!XfJK&}ULi1-lCk zzVB`e3~)gmuyx~qe^8{!!8u1IoU8$E9n~lMndHApEZjIWi?jRO;c2ybIKRM*l@nwh zsB?7=_k1>Djfth@HCcoZ{C#QH1v_*3MvIb+?}cahk)Kzd91ZTiVu5i;{eioS*~i0v z?ehn+bLg_@dB%g-y`ZX}mC=_O6H2pnm=oslbc6Psu6;lihvkb>>MF)k2bL7uv2oNN z$3X|WH;Npkbd5*UWAR^~^uANYya_MqFLMWTk3x%m-+~Ly`=dT0By?2J7}YnA%ZEX^ z+`5lYw$r~9v;UxnRh$ZOCd2-F?QDU-J$3FVdjkNI%79B@d5bm zPJDeM$cc1~I6Ch>mOo26l$=3s07{{rVYoohAN6&4KGnaP2FK(8B=~10RPm zJ}2@R^=0^jQ3F)suDR3O+kH?}rZkrV=<(1E#rUNK8t85UC=)eE-y-{r`c78mf`=qT8IHG9y{ewyBuJ?XTb0A__qR?*m3b@O9)BUFFfL) zEf`PM#bCx9Ncu?JM&&ocR~y!qV%63x<)Ua(yj=_6h#C~J*f1hTEMOAx1)LWJ*m|DIRE^~kI>=Z*V1MO**2 z8a8-0z25kClE~RPb&ls_KePq-5%6O-AHUtD=aXl!&Z4-r<7WnYVPMWBZqgXN=EAIB z!zyUZMCQh@{lu-o5@6a%FoqyY>k}E67ys*wY+K8^C+3)9@r?Z7Vqca1(LzZ56f+b9 za7?(m^zi=vGIYj$^%7TN-l%1Re!J9PiYQOYAcf|2@cD%A&^Nhpt&+>ok`e#-ym*+n zLK`^TlKLv{uf-bT95&iGseo=0EqV+JRRjW9odwuW@URBc6OQZ!XD|a7OB) z&r~vB)4gkVAr_Z19cQIg9k(^UNvI#lY?lM#v6Du|DM`{lQP~=CZ{XI}K~tv*1sX{f6du7B z$6rXR>-`@3j`c91E4IrtDhk%9P^L z>3~+!uIcW~d#j-^Gui4--{keWxZxDEcp1;`2}rW8-q5^!l|Robw1A1G{Cc~-Y+}Bx zl1?O6>1*|SR7aP9F>AKaFDEGHU~d`SV5DPg?vjw<4PWMk#yMF(`i}J~w=D&N-5JA( zcQ(zBp0!AF7(UXSk^XcU5%EogazocN7!d-;4Flr0szs%Wcg{UccrYdRW zMZMHi3hT-a8^Z?-80lhC=A5d>3BCTiR)>w=Jpz{yG?8@fE-O^)2wb2-;Py$~zK9X| z=5vBEQw{qb3L<1!Hj5h`hYu5MB(59^Ah^5;*#0hC>{5>+{L^4F*VLz^*5I3$%?Ow+ zWQY~qyM|D#)BL(}70zm_fbwJAqjNgs%HD&Z1&5XgYN5pCYjm~O=XDHP1P@Zqk8%j3 z;uo{k@%#jDOyGzVa8Smc*QdrYO^XL=23CQoWG143;iB(AiQCc=How<-l788?l*Y7v zB(Lo9DH2(bwT0vgG29w4>H0bJTw?XgZ|QEtzk}7o8dx^SXGeJSsiNB-8V&X211;vQ z=a)CgO$1DZLAIaCm8#4Ed=q;1=z^eAC*}L4h*C+kqA~?3R9;We&3z@k*4=>}S$Qt| z+oLYp95qCV3|ymmc4-zsqyF_?-nm*PH^4k?8P?qa*cR3QZ)<^BVb_WLV=!KprYJ%h zAoefrHDt2H>F$Bjrmsx@kRAi#b%JsuZ2oGUn4Z8+FmCbPJ4WGo9~0h-DXddMu-R2q z=c@Sf><||Y-RB|f(VW-^Tx*OM<5~KZYI&S1jUfnP>1TLLJ=Nvv;|NIyXJd>O zz`4CZmWhV4#)aGi$kj(29|m;gEuKL33oR92g_Cz6d8s7=j2}NxwTQ~JEAh3x+`RP~ zN!lWz2@lX_yAejf4@&w(|N1!wvlq6nnY!gUa(|Lq(l5))aDOQx1^vq$@x;F)atemq`+QRB?8)h271mF(_vfwJH=>@%6msUrR(%kY>~B znn~aUuuDR8(7w$32C_pX|2=jq{1xb>&Hwjhe?!IS$4j3#b!52J1Li7k1WnrQ7JUJe zHj!`tglalD0VxI_B|IIDU5{D9C430T^sY_KM4k(qm6N?OYQ`A?*1oNG_qqUrd5t&$ z%(}`{iU6slxI^3FM_q~FUlJGCg2Y+~Md%YO`I~ovW+!Ksd1&a}&3syxnz^$>NJ$fZ zOGQWq|9ES6g!=2wPB#c~tr&XuiKNy?pma?_+79xUODU>Vr~@kydZSOEjKu$fQ8bG@ z)R$nILgVD$N*)VjQ`VRrm&yNQU76qBz_xpxf}57yWcc0>`1w=zMK}S%d}Og>AmQE1 zE)~s4z~JY$q`eNO_5Slr^lC3py5%OH!=p;%lh&UuZqOXtg}qmi<=HQ(=Yk($7Tk|g z_ITTf^tTODvH>r@y}h+EFjXfsev15B(Ci?1gy||j2lykHi>=-0b;d>`jyC;AMfwjS zO_iyqMQ%Ngcz){P0(kei$q-m_m)1c@%qg`xh`-VIN>#|0l0(CVGJh4A+bhF(7;Hv-tNovdr>)qAjO1PT9{OuG5G?}rTgN$|AA zu0$ExzqTk241UXz(!&8E3?Vpd^}7#V%!*jDS@$nLEDqWhcDbuFHCy(tPK4y`Hk=qd zGpB{Vr3v20od=Zw%g5$}Db+(!p>9(v&<&omflFiIGVG|IMUrboHn8k@L0st`!;RBz z_o@_Xdinz_E$T9>#rL+yMq?;vciJoFi%WPxdpGEBOHk|LZ{zbC-L*5P#rt5=8U;=65g&Zm`vMN*`8Wr3{=|bx!-he}*L9^urYCfi0}2VUPqOl#6&p;+|l30^GCr4Ym8J!>l<5tG?_IZ~a9uQ;$*sP54aS4}6+$P$hU)NJoF=-DCB2@@- zo^?nv2SDT9J$AC|X=5C~eO&u*m4rd|SC`bh|2^#2$yO`88NO{p!ij%Y;9e3L(}oTD z2pcSXZU%?%3CXcDhR9}F9k-56%HB*a^Yr8~&z)2T0A12mhH@et3+;Y8#WC=JAKX7Z zQ2e)TAa*YA4)3|)lrsc9<-ftqhyQhA9)U`dvM4oW4H0=+iA(>E<=OKRA+B%_9FN5K ztI+n@ADr(cwwdxrT011^QdqSmWH8bUN3I`$Iy>dYAT6Qg%(5YwI49XBwChdy>~6qP zaUdrw#Ga539lp2JZFsC+PA!XHz&4+cFV!-#q6BvZJhtj_H#3(zc9(h?!wSCjshOla z8ElzpOal-aQ1h!~k%v3Zg34T^7wtS4qT6g;Db&^;@!!={h+1b};`Ir3w%hfyd&1RZT$SeD{3RJr#$h@a`dio5ZKdvN&i(z|JFqmxBg2K07Wfo z#xt{*rm9J?nGypL_4#+uRlD`RHN z0Okq=vBz6&`o%lK12g-lGFSGxSFXGZgJ0jJw*Cn)P{xzlX3x6?m#q+z+u+u5nP+1I zOG>($X6Re#lX<6Z3E(mI{tvlPiDnx0 zsZS0Tg&2!i?c1>Olg6h0%MEnePg#lsQH2!;G8Ytfm{iGisH5n*fOn*@-sd2b8hd)p5IC}N(M zr+D$7iu|=;mxkQ}FC-(TeJQ-Itm@q?-q@-pQ}SnKI7UVnsC>P*%^0_91&KfD3?Crr zBw9PuCTq@T*p^M7Q2ax)RA%|<5}gTn?U!+6cuS!M!NJu8f2BAJ&yDF-h#@0&hU=nt zp~A1fD1u(6T;d>h zdp1bg1~mGJ!8r7^UCqP@y24>qBUzw^}yFA-T935867f1ivxDRhn{Y zWn{t+oXdo|>AkONB_ZcXjs$;;H0oGqxKe(S05#d^O#_6;S?|w2N&>h`R!LLz@sBsL zQyv?>3Us|&UY8zkpyP&GZ>|<-0|}gm^eVi9?+*$8o0_(#m@`8O85dvH$p_R<8dorC z;(4g+_WEWD)gFpsnqK|v$idEi9@x{n9z%49s4ZK6oLtA2*dzb{nEDF0sJbs)8fhdY z1Zn98DM9IO5RmQ$0g)U!rI8Nl6p)r08tIfqTDp4}=G^oBp6A}@{sD7lpS9lguC?C1 z_SyBUk;qsSWTWe9`4fj>PC-RRA09^#{jUk7i>yxivGD|{a}^S#I;FE~*UBB$mdW&x zDM7^$8&&~b3mO-TpH}OjJDy$NO6|hq_$s3aiHTunfZf7vukazJtj<2z_(5f!eQj9I z&*-G_Kbre_pev`0`Fy(miHuW0Y`c;4EDoFIiKP!@(EVdfKIQYUixPumHd-N?@P3zZ z0>K1#rB-|N5A<9Hr|@^H#O6X3Ry&U7n@4REuZ%*cUU;KuVE1XOOsIdxVNXLpVgU^{ z52<>s-ziW6&mlY@U{wUOD?b z3e~)912UZgh1NP0&xyw1W!-aS1Tix*ych%xHgIKwD+7c6w99BX>a$A3wcXu$NP+0m zRT#vn_qIdnZUR8G{)-SeOt{E5eV!g(jH+ate_-s;ngs28rJhF|Z@uPFI9+Khv*hW; zOU)kKK{{NdC_NLqZ=$1^Vam`h%Be>Y<=|vdyQyTW`Kn5-5B@b zu_ROaP7a^(s3w79p+tH~$G(X!a;FztR}kVGpXJ1l6t8`!ZuufTGd1<)csq&f9)Sv? z1Ku0WtwM?HYx~c}9#sm?sy?ts(#^!>oKp3?cAG!t6hL=y!k4Ib3Ey`AgA-ngQw2A1#S-TA9%KBHlttoja&f}rgMOpV<1)-s;K!UnjD79^Br zEQ(JR_?YSjH#0hJsP7N6uebd8OV0Cw%8F9_2lWgzx2zD<4L&&VyVdSrMS$#Qd_K}a z7sY>U6qVlvd<9f)dxaT50k5n}Gm*nwhDNR+Re>O+;$%u1Yh#5;~D2rDV9h4nZ#F z)yH51eQ(A{N4>8RuWc0K^3nE5fVHc99)D^uJ-)#Or2W#UN!EvDt%Jg3Q}c_q+XTVe{i{QZ|O3#;PS!MrMFTMtAS zzSnL})U66iVP~_kXL&rlq&^Ietw>b67xK%SCN8+{hB!RE?GZRy8r_cs$0n+ zZ|Gh{RggCut)z}Mn$tU zx=i{x@x^;R47NVGe+S!1w)2?z#i;L)eN^6r0x&k>W4Nd3_ zw3;mv; z>(z;*S?(^st&4?(YJdAn@Nq!Omq&$Y*vAHo#)a3NSLhM|GCisCH=X( zA|j6ivtvZrl`@YgDgN8$?gnMCF1M=T77!tS`?vXFVDgE`Lc(LU_Gi z;PD~cQ7`@m4S0E;KTfB!a0G_A{w8D|KeI?RO-~i}2)^h)qyM6OMNFb4V4)&xe;rsY z0A2OCMk*3oyv5Wzru*k;v&jD2+2xWTq9m%G#7wAvV|-p2rf5-xp*E})rzM%WYZDEz z`&!|4tk|s5s;U<9t}^-i*Hn5hmVGZ)$s;nQCu;^`;|s^ zZ1%u+jGjL^e$#wdQt_BCmRh}HuB%9=o6PrS?TsK7`7d_p^g%d?c6|c4cLd~-*IMLs zC#UrF7d>;EllCRj-~#K42%^YS8URCbWTPszWB?=2nQLT2S;6NRQt!^Dq1NYHfPW2G zZvH?@w%Ox4rWScxJdzK*Xn#6AUfy0wXzc^M^}iyv?jC8soK9`m()xg24!FvCCxAr= zE&9U_&%WVdV|z2!soI0QA8@~pF!PG*ya|ynvK`t$c^c|&%YFZANnucK^yBSDomhO= zLG6&v_x)Ow(y8fl}GgfK!)oaxXvvvG3fnT zV26vm>q`=xG*ptCUJK*UU92<1H~xS}r8sT{o?#(o*`J}J5#1XI zVe0cN!HY=YlaMgk*E zf)_M%<(DwXY&-_0dmG(Tg7i%7HpC6E#Xs>HKd0@qsXN%HOfMB$coYl;#TuR}=k`G; zBL!m{;kzSqkoG2Leki`tUShLrEM|v&`HjcVD7wQXYI8`2(8swp{c_h263$n${n|M4 zeFozAusR;T#l?A>CBo_GPg*}TU;FqzCpmHnyhZHcE9%DN1l!^>$I9;+C4vADzlA0J z-C7rDMLT^qx{w#5^c$-uD(W>^;XX?oFq)oXr7{L_vYAp0GI zz@6)1U%XzhVqwZQM#G$LNx%gJw013FafU^~C^t6!dRA~GT9J3Kv~_?4S2 zOW>iDQIM*>!Ev5c*M4Z)c(O`er%i;aJm;S;c{#uQp4QW|!yHEasxQu5dc12AsiFY# zzY@HrUZ8@Gbt%H#rR?Qc1J0W%5#ob6D(F?^B`hXQn$j5W073EBch}}-_H7DY3 z-x<8Toha`p8M0>B>!sn1t-^T%79D4=0*DXR>!7G`F+8UqzV`LM?rVpsH7}Wi7~t8i z(3?zD-IgwOEjJ|J#_SJ@-^}Yrs^1b_0#+yY{^LZoZcV+&kCQZK>8v2gY}~DS826dM%$^(@i35ASR4(bv78`sagy+2`C#$`Um z43ioy=a54389h6^;pNef)G*Pv!onXDCqt7HMJo7wBA{Jv`}3;!(l{VP7V0C(M%2SJ zz%N(^cn|#kF-iSpb8TDI9i!e_Qtf>5-vS-uW{epsgC3jY$e={XTxIb6(4WyqCea5` zfCJrf=37LOHeA`_LG)1RM8EWBj!p&2o+y{ZI|A@V9ca77^^Gyh9@X;$$jNW+z?Ry` zd+(uhdcU;~@C9rF&ALe@L^#99`G@Q--yJS61+r|S@wh~XXd7-HEd`Htv^{g(`&5dm z@^rlo3kOm>k4jN3P=hJa7gAzzQ$jzzLQy%J0cBppS=Iy6S0+ru)nknfpG8&)uFHL~ zok;r3(8Bh&V(#A^%JymSM1s*Fd+1DTr@rnL0D< zrFxKo$t>6PN9hvwqgdeXKmSB-_5%$&A@Ea67f&|7tk>@eFOlvHY)4Iv zqpp;3+&S}vC`O4}Sfe()fiIX|w)(j&MO>$>Tf7jB+!9?EnClob~n%m zhR>smN_+}~crvUEgc)iQu;HO<&&c7+(5@W~?_4J?L@rD= zw?q{pVMq#86j-jd{#R6m5!btO$zmI{UVT=kS@S+({3QEzJS3+r}cJqb}>*$QxzT~R?k|VFabzh0pc-WJR4M1>_Fw}HJJaXe} z%=%}T@23Gt6ETwl0z%ibe^>x?k!Q2-zW}+xI@K^b%LEv0Y7Xax--3XuYLviZ(NGR$ zppTG_WpXPDNetRQD}~bJ*1G#1-!NcUDu!E5WrqY(3#Bjy3!h7bSPY)N$7wG-+4~Uw zj9jA<;^XKLq5TPN(brXJqcBvqSFiXv@?JQ@c_PM+e{|0IySfY-<%KJn@k>egNFl~% zKB*<*jZ`}m8G6Z(sBz83q(#v&XM_mmOB%#AB-}{K#$#>b;y=9n+k(0rL3EZu`u%%O zo4<4EZ+q|g=ZEWOFu~{_>`~o57bS`_5$rIKu!~~+`3|M>V`Uu1H}f_Jg}tmA4SXW^ zpAU+YW_=_FsU_P_Qu!iC6sS2{0xxKAx{}k+W7l;D5h?nC#@p*onz){sF(l)UM`?pv zmu|<7FBdf6>|_?)$kJ2s#HB`XPp3CVI(loDz-BA2Ah}5rS!B zVXL>aYWQ5PfwAy2xxJt5y+9?>#}=t$!Wu~frV!Q9goSwh2?V&j(sZT`&1fvipS4HP zpJP4vBeG+GYG3j3Z&_c_Mw?O%&ZmsvR2o)(V3 znsZQAop!e=9?6X$>uR@{Gvg$(oHFoZ&!GerB4%S;P+ph&`Qlky564gMi$yaJN|Z^} zerP~&`taN24k?Sp=LlqD9xIyDYSRztMMO`gCkpa0oh|p?ZI8Ad7ZAWBe{A7RK?_2B z1In6Q0g8oE1ie^m)LPv$-vb<4NrMhTk>n0-qD++}NwdrkRwIUc1dIiiR*Klv zcmgh01Awb&VYsx3i=1qFB%a}it5W`;(k*DJ|1s10%G-K>3)`CKctm#{q-z@hi{qW% zjb8aBvTrP8p&cSb4xHgaL`~0R*4{7N>)%B@Nr|a}KI7>;zaut6Pqg2wC6(aY&QIs z3^;{n?w^DYZb6%CfnEVI?;c4+3cI=FR6M8#iK@h0*Ok&k!e7%a=9o-*h1sm3Kh+n~ zfiH5h3>jSA*$OT3*3z8&(x9%d2i=(N1A{v0p?JJFLdJ&k{C?sxNy#F3Tic_JWt$op zfYx6p1;q5I8QJjAWDJ2kqDRWLM&BcRgaYiR{Z;sT$~6hgfAX3&t@PpivP;X1!>CSANasuA9xSlEk*d^(b-UAbtb+mSoU{21BB(~}}T;TlnosuVFV;5Bw8(=vQqkg9{ z!OgxS?DTBPgdE2lYVnjTv9M-Rwfm8Qktrlm>9e$7zyd39e+GZe2n!i|$jF})coY>t z29|3hDh=O;ko{qRr+@Jr`o^;NZ69(_-jA69YG1K<#GSpsQM;Px>Qlq;y=%<$3oV@Gn9 z84?`2IL(g)!`9}+Gt>3c2|3X6c8!ts4-3e?MGFwlLFZ2&4V6?$@UofPer;>#QyFHB z%D+96vR{tCitAY@#y{QnBH<1m)kJHzAd_WU5A8c%Cr9j3WoihswUcI(8?g0j zrCgY9-D;t{o9eYgxD3~n7}*p>vXPq*hCl^ zIAxa0`Li8g|9@uzD4HD3P5UMp7bL;ec^Y0s_QlA(_Ai1|pleZV6a>E4PrOx}fOM21 zyQ2I&03ZB&x54ejNX$quY1TFQ$oOE4DRzG>n-oMTe!Uf!)8B9D87YbuM2DFTDAMhR z)A%{J5y2Z|>4MFkaawUH*%iE*Gb|$?5L!RyKc{DMN$$Mur zo*Gj)Wif3e4F~-D-U94$gJu7cOY&ZBukV6ZrHTVes{zgbR#J`zeEZN>3aXE(YMPCq z+Riqe928E|VTtgjCqgrG-_5h3aXk4Lt&!nme#PmGKXLC^XA5&qF&*mgFqG0izu>X{QVo{wxsi zafTs+>gX##tNWAg!==eb+k4Xs%t-EEh%e(`JTBI&B_s)n#J7PJu7#spN08`#UBi&{ zJihE4(V;2IlYhdwj`|k?q<9Z>b^7;r_bqCwetV`I;Kb z8GgKq-j4{_GCh1bkj=!QsAV0)*V|1b6eCuzS6OhMe$O>-!!<59mv+CfEZh5lL9a$- zJ*zbgt>i?oFOl45LTOHLfzwJDtb15)d)@eWm{q(h_XNI40T@+%d0F${rf6=pO;U9$ zn)R(I#uU={K5HNuO4nPrnaQ?__XD;S608tDLRS7*3mpB(qbI5HuV}2QzrVHS~s3^bpt5M8o06%pBjnaV#c*A>EN=MWYar;GsBS6vM&M80|{R{KEFXcL)?W=hSeA%_@cSU346j)$fNa zT?Iq#U%}pd()Y|AEpmI$o3dJ1fbvnkb@|E*i6~2(Fn;dbW+R`t-r>_f&q03vAj=wVS$2;U?)nJYbfFWJk=!h0o<+?n@16Obm7mN)omfbkbScBqW?e8udOU%#Ii5 zN0*`#ixi{$(s?tbo|#N~H43G-SBCbo+e4;tx#3T(qX*BQQT>*0qI|pUJ*(c05RCF2 zlzhO-_uw&u=thK14*t>b6Fnh$CLZpLx|kIULD8&8)`~1#X1he=C)t4R2c35Z$SYaa zN(rNquIRvlFXI`uv}XPerz@io&gW-b@O+g8VRGP9PmZbiua}Oa|KXL=IdHPux2bFP z2>OAq?;rT}n+qx5)mgIV!_g{mUsXP3PB$A8hP>)|1M(4o|zyJsXiwVV!p`!sI*&A^@-H7TZmh8JTc{G#K%Li z4BEOj*ccSJo&z75_nz(NQfvA<08Ay0WcW-Z5}KnyUmyNr`X z9g{U#?F6dQ!+I(>4N7!qcwrpxC-Lwr0*PsHzh`Xq@$788mI|CSrk#{|>_1X6t$A_kg&9H~r1z{IB%ahp~dr88p$ zNdQg4o?3!egF)NPZ`Pg~rtiJ?C`*VLHr!y$Qt&NPFy&VTae$@K@j~sD9q~A>Bk8e^ zqW_kQsTv89l)85d_Un>gQm+g%X3NQbTzd^+R%Ix#wDB7%c7HX_-0<<+endQ)1!vuc z16ywoyV7xZ&0K7TyzH0}O(Ut_8l+M;L$URZDz1pCD#rvSN;jWEAd`NfgWPBdKYzHj zKkoHe#tlN-f4s5tev^w96{SrIGE8GdK4>RV-Pd|_!F;oEcw>%zxZz`jibt@EI@vh@!F;LHmiKa@4wZ!UwGbKbvZcnbtBZAL0_ix4iLu1CIAdwO98D(?*U}hfp;sE)Nq6K3@#c&b8Hfv-9Z$g)-F&3f^ z6WIy0Y|QNC8)mk62+Dkp+&)H^#^ZMjiB}mBz*U);-=?6}luWXCHu836i9AMxydAP8 zQ?c8W|DaQOUmv5CL)p{Bf-7v4mraq)+4VmCflT_xhn!btj$_HGlPO3#J|nD(sya-v zRm?>#&+p}};>+I2=s6wP5zu4Ud~YDq5Xp2r!nSgCE|<6m9=3_!1_asZ(v|;loOnMm z%fFWu50Mv@#J(NA_qILtJ`vOY|CZdcCBmV1kj#S`2;GFy$=-uK^X z!u(gwqwIZj?`_+o^#r3s6jWu)-&-Z7HOgluDD;BOeN70Z9EDQrp%?3d$vGg;(?jQC9 z^ffjv9LLvso9lhJ4gV<_MC1Qbw<{C%z~*fo@Za*J^{9wbGaU{(3-RlN&i;aRfl5vt zg^`x18_nEovW_nrGQj90VGjEz@%NfeYg=|A5_B|!1 zlslk<*S+fr{Btb&O{5#NIU7w}xG#Q-*Y~%1=9PsbFHKFl0dwqCj)r4U!;v~oz-GmHYYAv*@I{a0AoN3Z)?~BT0PUv*oe?`UA-a1V- z&-Qw*<_fH38{{Wjv>ydvS%H2anYYZnWD`oNUzvt_s_`y3-g{5?K%QWajzTZi+3WUX zWnDa1SywAH;;5R;avV}X-#j;0Zsi@zY`e$h@3>T#e`1MN_X2j7gG0968@;hU$t z$M(P}y1QU>CWTy=f&A+T&9u0m#a8|f6G?7)52Ko{%0QXzM1YvyI7Z3=E$}b!=doBH zXGHQit`FAvb;KEKgj+Yr;@QNOiXUt(TC!Z8|zcc zy{%tN`OTOATN4OM_>WGJ5~D zU;^ugLdOpbJ*PtHj;|4GO940arsliQ-A|8jE5RmPxY*ieBLT=cUpXGSB+~!3>?J>1 z@G!BQi;DzjXfJ5yJ#OJvqUn4O{PM-a`XM&bzQ_%4JNCb4mg9px-_;C~)-Dr9U>ga7 z3K&0F2*@$fcuBcSz16-EShKAo@~V&DRp{O$^bT>?)9A%P(r|>hsI{JNC^5zm-O^fC zUGh4?KGeHT`3hOn8Eq+J+W(_XN}M(833q&>S@N^DK!M*I6Xh$ic<=VP*XBE9Kyz>e{L z%%ZyuKm4AY*0!97PZ>kLeE6RG%~y$Mj$W*e+nlnhCFV#-hd2cchOq#|Z|rCY**8P4 z<4fxS=>I1OE4+Pe-Qu7Q0)O-$?{o(P*CK{Yp^mxdQiBc?TXm_i_RI9>5fnpE{yLZaAEvb-VCss3 z5UUO@lfZ zT)-T0$KYW~1QML+$+_+Z+xdF`n_Tde8PZ#cHs|FqO~0V!tI)?g_|#K!vUh}x%mK~G0WhsW`Tg)X?<=!21zZ7co$PqXXeC7d3f4ezNk1TLxy1V#d zc#XxvwK&}AGh9Bpu`QL^4n5#And!UUuhMo}nHJgj-gS}S zi*{|y<>p4-Up9DbAFD(6a@*9AnTIE=74=tz&EW&y4;Oj_9nW=|)|=^?U}jUtR<;;F z0d42bldPx>#cn7Q0&;);fnjc$Q~ktI6eOf4SFV?bxIl||)1>A%o4pDBLLghPVwV#P z;zYK0&RR0xUJz<;Tw<*BIb43ZQ!oDd;Uw*{`PkyRwvPa%!zcC7z*DX0$K1}5ArckR zE4OOFSAV2teqU`!B}D|oCV2+A5e{a^`KhT85do<%cB3WOk zSBMlIJPV5ooiXG+F8FyLnWOkr(jm{4b6|0ZA+@fOZ{HI|{~`Pv?tRMua7j)uz96PL zAhs+>37j4y98p=?d!Vyx0Q?A$J6k#dsV7Jg^a=@25VZqIlMKTv)q*&IFom_sheZSs zNf1{;@MoOOfqYcI>(|^LOk{9txK3%7!UGAyc8wr6F z&qDEhA?t@uQ~0cjRIb8TWTBJA40~C7cNqTpOAxl6rwM;EXO zo{0%#1!}|LS3l`*5+H1l-fsrlJ~ zKa~8aIv{_6m>$UQG@M@k2yZufi=F=XIRbDM{rjYTNa78HHVd_Zf(25}ylXZa^;k0H zx6p8A=SMJh*B{q0jqf~(2!irjREstS0JmnY^V|aUo1a+;{w_-xRHdYrKeg0wFa|v{ z5mE5Z-n|FIer7sVh3FzebL%e=nm?>&OYr~JNbfvrI-v%vjW{)le_I&9R%GB*G+~#| zWM-#cUaP8J<5%g`&*2~gxtsraZvM(TXaU0}D<0ULXa*pnCLdOq5Nn;-9MveFs(>S? z)&c|@A4$!XOgyga@4@I zxizCG;03?WICf2;%c+isifgnrtd|y)X3<@9k+Ad8F2jrFD_?B5cQmAVmWk5*dx3RA z>Bjo+w#%A=oKQI<`*4FX5_*v;k8X(meeK?YTHyVZ-G4QQ)$2{qP>8qY597y)`vt(9 zln@*w;rRhzn=cM(y?8R3$=sL4gdWEPAzE%3n>U%;t+E9)LOW*GZN z_{E+2P!WDXKwBs7_rGq=T!^)c>EJJqK;FiBQY@dCcW?w;d><%vaybG+JV-|6Z0*NO zthzZ7nC@gIO48%7+jQT;LeUqb00B5owiFke2z)3dUK+*e2M2+hrRZ#y$-<5C!^|aB zfskgnZHvE$V%#mNY8t}QoVkhVJ)!#%Ixv&;K53@|?Q_*3Nlmc5f7IJ5ZMX;YAJT6n zg}fKrj8)Di#Lrmf+9I|k1GxxzzHy#uw==kgBTJ&FWpLr&pP>Lvz7!j2EO&x0fm5v2LsP;Ad5K9!0yxTV4Y^o56jW-Ln!A*-k(gTg-0a}sE-qNl`%xm zOYY2xgYd(pr+2Hmr{gESEK>C=801F(KK-dVlIr%6ULvcASsm(Q)kSkKEz|kwX3h{z zu?$fq40pV3;*qQ*^}}%#@ePCg6*i+|5zY@`I|+~2UWc!Wzr}>a@qg4(D1J@pQ-=R& zi41i1LIO-MqW%eGCqU#^0*iYOo+H*|n+g+Yd+3r(5^ey_3Hl5?(EEgP_>vi%7~;QV z31l~B#H*E!5L@OcG$hMj(TU^63a^%5e${Qd4WJtk;t-23A+ed3W&5-b;vO-S@|X^F z+duGi5~A*;(eI)w*`39Kr@+a)8X7NrsO{7VF4oO+#q8q;Y6ZI?1o#GZQMnsqVST)v zWasN*@LGu1r!EY8hkvUz0k(fBV4kDv@sdyfl6JYN+xDQ~F}Iz!;?u^K@OI0>>8HWxv_X8&1HM&f+IvYgI=DS zew90giS^}AEWA~=u7FMVwX^y1*wH&3smmbmtHaLVVOUTxiN-@*5S z83XgrAHm=S+rcUps5gBd{*o2JxvO=U@26{gK!Q+bGJA;?;dd2f@(a17-s@lOZ5`rA zKOmfrFRa7cD*6<09*V)<~@wda5;Hmf=w`Hfmc_A zqJvLfw#wIu@LjUy#fAsEmsv?g3_x%C;v4oU;c{^yxRraY{LnWWUYlR zWv@P$Jxdl4^oXzYOG8#*P}QP7jhOnADVK-~6%9O}vKu?QI3j6id=X`6pnx`)koZ0kGV7r8+t{VUUx_`&}EQip2fm7XouIl&S3%9+g0w z2I`u#H*rAyt3bWBH(;#w4&1fh=2GP6WIHfHTjmgkrnZc__1G9GXUG6O$IW-aQA&do zZrzH#d*LaK=s7+a1A-3o_j0YS1;)bS%Jd@&$;lrWw_LujrKyGYz^UMvjIYUZz`eM0 z$W3zKhi5yYOf0g zu4feu-cW4&!r;V*erPbBGP6=;c4nDqFLWuN_c}y`7)XnX4YgD7AUnNxVm2}rxdgX-> zGDlOJrLJbz>Np15a-X{{v{-uSNDTNL8lcZjfnUYzm~29o;SO%46#M8mUqz}vndYm1 z*2@k-B`kpic&=98J5|T_2$u-^W`u>Dp^{#rdNd}Cll?8N%|7I|o_0PC6N8h#{XMvA zR`~5RSs}}Pi))uPw2r8OdqfU#JKkcTXY|J1)h=XnP|G9@Syt!-@BWMkFsIXSHbpuT zK`cN;Ps%Tc#b$-Fn)P9l8$zJcrqDd{aKGgzIdOjRbD*F*6@dd$LOfC4`|&;;`97(` zUGQoeL?;i^avYcHv1h4Lm>p$1CHzP1+26CNX!Gw`{Fy`*oc-zqusPrVgurcmF8xUH z|8T-O>&iPcc?ej0L=e%08GC_olKPn!Saw;8h{mGu`0pZ_rC1y0=ph3NzA0OHO(ZR% za`s&2PtoPGs-*w22+z-`0axM8S+l3yK^DooAfPH8_tz@U?-oRUuGm?*2p)I{APTko z&fot+f|p|}1siugF8W+{2slE{UqM9raBV+FQXJ5UW z>Gd&3r_}hLX{vbEXD;70`D~t{!`u4-m~)EmVb6RSZ8gnBC~&iq({V2>=8ZTUkj}dS z>VKH?Pw}AgNYc_x5`7lQtL{^K7JspBB-FMh#kpogj`u|TrYmn&8RACuFZBkmn zA?rfp*ihnzKRZCC0FG3~2HP5C5}ADtgWwook?NuR6!yENx9AM1Pe_az31_%5Zu@eR z|NmwIH0(bcZhRkfsCz&~NNlg)36eMjt@DH457!c`+N0s@IH6V4=Io~XXWb~Qlj7+x_xo>FmqigPMfFRxCQ&Q6@eX){M z)ewtuz@j)KtLk{p?uPC7nsOcd?>2N>-c;v}{gm`jHznW`K_d>{9xS^SRTA2Dq`Rvu z(U=i|t<8iCH1$C4Ii#2l7AjCT6oGal^c_<`at(HaIJLvJ1+Y8qUdE}lK5xU0%Z{Z!S3tGaaZ=gIGkK>U*C9dD98~^_({Gcomue;Y zSVZI^26Dhk#JrT10y|=Q?3MK64%GXMGZH!x4foirtG7^Pa7fZyQF&D<^`o5e^NYQ7 zw-O2T7lzH=i(Sf-4P`tpMuUpIt12B%btw-i&P_=XeJkC%CEgKlpAd(dlAxCJ>E8m? z9Vm7{U7i|st6m26AEbKH7Yb^iURvUQ@Ga!}cCg$zaq+07+;AglBil_xZh_hl*XN|f zzR0VbsiK{D_4NBQ(^hz6y3Zmw|8R+kD`P=tE`88N<8_ptMlk@ExkNsUYs zU8peaoT!cLX1tlJK%MmFbB@0}I-Ue!oOmgIY*NzSP!Q}xP{$*(k%F6lpdrpZan$|3>QrmG&Z6vmVbNbJMc_ z6!z?(mTh0D)N6V+ygrW;0)Czk?&$xm2#QY|Ss^+;kqbE-XBaMgMGgZVCq5%ueJ>Ji zpP+xo+3{2rLBkjc3P0|3 zNL*J?#Bp5%PO+w_cD1O9Qa=cp27?Sl*u5?!XvOdfhq&Ty>21>GuEAIckNb3#0ginmF(pG*#>JG(ECiU_FNs(B%EeVfa=)dohV9pDa&L_#Hg9N`LwmoNagz39tGhT z1(8SN{og+yK;&^cLBZ{D0jL$hADtIzT0l0l9r}{9miy$_5*UBlX~eyG3cuYMnWR=1 za$fQguXVfC_>gar^-W;&RO-p`>xXCJ6k#&_4lkM)?j`!6zmn}Y**q`?I#71F&~83H zddES2;KPb^9|qhjs%1O;kK4Xh+Ty?Mo^2fm)1eO>ZGtkO{mS*wvB z{hbDT@PA|pRFM%hiPRZR3e=sAfy8sG1c)83Tk&h;!yv}7Ej13csOQ{ALmoIk2r<<6 z)gwa3QAe(W8cbbtx?zS6_;cHJmDBTxqT1#~x*D&0C}EgQb-l#tb6_l#P*X2v<(%Yc zA=V(j7zf_R?z8zXr_rLfCCrNSl;n?JLoh z%4Imo`PmD=oalSr!5;$kK*%43I~ZhiVw}QvzLqd8a!vl zZmBnTtrfd_{pYn&$ZNctFDdzD2CC(^>tKpPj+Bws2~XAZtFGiq@&$jE;aK12^4(Zfq~3)c9J^ zw9EB>bc9N53yP6T%@cY71)bbTBv};7FP5xZ9i|hn5~P=KaRrU57C1Ek(Xy7i{#vo3z+H*E%Q#y$B@ab_whMVZBcFZo^0bTKwVna zd9?u*y3fxNF=~S04yXPXu-dNa2A^RPF64q7Yrel55T*wODL=fy>|`-x60M%y>A=BEtTqzOFxcc>x+rs>o!b zZfH`+sv|N0PB+gg2H73qXk3BZ{;&kQ03)RJNRps>Md)TOKcNG*IkqC5&rI{)G2J)a z4Yir3egTL#$UDEpu8X`LEFlTq6iw{ramN#$dJUGua*b zv7EPCc?q@N@T>=I$lGK|u3C?f;~fjb&M4R!PQHCHJn;6vcV745_;;7T`jn3u*c^Ba z)9uqAgwuFdG58CEQ^^5aWk(jA|HIQ)#zpxBZ7+>@R^v* zvpn>Z(v?>)m%r447{cR(hk<0IbjJ$|3mn zi5->~aCGEH#^oQ9PB5Y-hd1Rq6_&Yj)!&I`e&0Gqsnm}&Gh+O^S=Z`s!D6KzHbpGPisnH#=~R?n!Rz3prmn z+xF8vgw9q-H%3go$y@r)zLV?uu=$&oR6XTD0Zof|5+3xRV!gZsi9y2d z7h+z#y@3@G#pi0Bu%n7eykv;JSh)YDw+0ddT1&F2swlB>LA`Q@pW?`Mvh! zgk`>$R>=P3FP>02X}c+G%%=+XziZvAw>fpZXZkG8$VIn(5}*$GZ2OZ#1Q%4#p5Pn4 zUANv<{gmK~wQ2vdsoDR5&~6d)`VbrVaKhBn4^il6u`x$Q(k4TL zvLImF)BOMxS8;F@zbx9f74~djpYRT-Qx4r;%ro18;64A*Vv<4mn}U{qgp(a-_zD-I z*%qNt?_+phBTJI-9O~z2oA8@CTt3g+!RV-1zsz^H;+u6Bfumcrsc}cYtOSR99CdAB zspH8!l|pzGfm;Mm{pdDdVJD~N4*gHir6V+LIHeQ+7Ch`0z;zp1zY@2Ke>P#w) zc~wp%bFjqS91pjM?N=i zj(WOH3E^J6_o|END7vcP!_EaU<>VWNO>zKCri#2w-Ad5BZ$4plhx}F@A&%PkNJTNS z(s^-#t+-#kyU7yPNB`;zRr}f3Usur^S$<{D7*-GiN}+%CxZYw7-(w66XL-KSp0;ud zB~mj~L>=CKY6Iy6S9*VBm49hbl{C-pkW%%U0a0xx4y6m@6sH^`i!clyy&?_gueGo!lLTS5G=Ydy058S+`)xxb1?X9Qos?f9FJ_jAu6zZ>c1k~vwK z9SKx|%92bAUhL#l1UcFX|2qM$*sVZyQEC3vCB~RcRX_YdIS6_=c`&pzD`nUiscmoR zB*V#9+4_SLxu*VBUVm!XjB>$Af$yopwVa4G3!6op?qaR*F;d*GUL5oJ*8Al#`zL8d z(@!zyuuTOmW?NJn&#VbJ9Ni~yI96e6aM-^5>nzAmRB~rHu+VXqt6ldC#LEj)6Ut?p zaQq%VWMj}bv(EThO9Ht|r$^D*WI5>#doQvb9En_epetY#1Z&UU*9&8r_U{--w0pi_+2F0F`rnXD z?*>)Gm_N#Zo04aH`ZWGzm3$!lP7g#4bfKDqLvJVl*VMz`k6$tc@( zI5FQvuWk~cQR-AI-qNetn?L9GDdQktOO+P(g?`Q%Vj$i+GIdGJcLbzBtumLs>3=;p z+>QhyKn6~rvRGNSHq6(-m;)>w9xW$*+(%z=3fp+!@e*cw#o@ z|4>I<-S}MCX^a8}x8Ct>tfP6)*BYc7{Gp*6mGPY1b4|%#K(agY@hL|zu_JunlJ}uo zSObmp6YLEekkcvCao#J96k7y&tvU=+tBB9R4?X|AJfL~5hlMM2nKx@q7IiA|c@CVG zJ8+2w%qanmimX`gK+pGFa?-U)gKmCap^I@YRu<6gDmeyoKVZ0d-Y+4iJ? z1MaN#A+Th|sE+*pLnI(G1bY{xlI0J6HERX~*=YT29jcmQEIV}xTyeiHiEw+r?FuHl zLP)Q`M-w#f1D`=gXylCq$oG}@1}tX6I)C*H6b$S|+trR@!L7cP={&*CP5CKPCA@W) z(|^4G>S1)P(I)!s+?)E zpHp}vP3U(EYIS5hM{XZ%El_;|V))Z zqR@ziy==tVqd~}ATT`#z`0XPj(`!j{(%eS@iO|t~oL(oDRP;}v;S2&fPjAp3W=}Xe zdVwp$CZKs_lIINi`x92-RfKomUWP3FbuwVQD7-MujI|2><&;vepg~@t%KV%+?45-! zbZm_h%Z%#+#U$E1xZRvZT*U87I!e(j!&p7eNUebTkQrg?fV#6+y9_YVoO)(Weh(v|(xOH1E#IkY zEnk?LxORN%oHNag%aj?rcNiAn%xVEYP9{ zN_%>uSMru~wuYO2N!qCQJ(=@DnKsscke0Ff+Rcij%hyI|e|9=jF{gu!Hu%mK9DfTmG@;nwClHUIm~wPhmH+}m?&uXr9YPXncko|_g4{7q`52S-!=Zy z+HB1rXY;TBLM(k?)VsfU@al7)ORiIpRn)z|Z?}UtN!|j`uLGgs9p*J_RRl%EzoU@Q zdJ!+LFM}w-=i0xlZ;x!2I%BN6+)ufGp6Ld%$+TF%%i5(w#2K$0P;>UuJj>Js!6^%83GRkF*?2Fhmt zY*7Mok$teHR#a^jELy{x{JQ-fArn&#eb(qC`h84b^oye%6P4V~^MV9LIWut8VG2Mn2yCf8+5)YaA1$cKGhh7e02pFE#SF6p#Wfa4}ZHK$q${Ngbo6hMK_ae zET0Ea;5z~D91vMO96~f0DIbX9c^m`JyT}P-DGp@8_e$+*9lT<~bE=WQpY&$2Y}sUb za)VOUjWiw~KUb#5@4HrKH2)B;1!(mtH!+IFP%aqI+}Y)Mf=17P*$@iuAz)m z!{Zjn-O_1H41Yt5u+QR)yTc(od^YvvphPbg`W()A&FCnv78BXKx(NZ>C1?$PA~ zVBPH9Mh)aNL#^S~En7CwZw!d897V8+vkSTI1-{)g6HZ5xqq7U|Pp!^~r+7X&*;gbK zEJtRrk1q}@-xJx}w353kB<$8*psafh>*H;zc~`1`y4Au|?qCh*QZap(h#Rp<4g5)2 zPdrv`QX)OmOrWkC#ixr)acp^xVTiZj$_L*$P?1JYc{Ns#Ixx4#rVkJm=!Fcc1lGYdlpFh%E!|cE&%e_&JlVhJ^W z8BJg;W3gF{LdhLuBqolc7kwWT;#Bi4>!s!wy33W*uqx7;{XD4T?(XNhmv3o)wOdqk zuQ@9f@5>KNpDLG7-u23c;Ox89bbc1y*Z!H1FvNbLmOuK4HX)dUKla(%=AO@Y+-t^( z&_m)gWD$GuKwbG?Rl!(H64H>R@&w%NVI|I4hY9Rl6L%AAB7+{NBG zOBty9&oIN8^V$5;vl(C03X!BbPD@#ToWLr>_ZN=qIEtaBu$0<_w7pwON+EW0m|#H` z=-?Nd50~H!)1WZSqS;KN{3husbn%|;PuYvnxd^Bq8CV3{9`S)HMEjbSiAI6RwH_s2z9>_y!+o6V;+Qeap{-Fw{~H;+npmDv70 zwXz<&rdz>$K1Rw%93b7~S8gqwj1$V>#zY76Dq#Kb22ioNdA+ zxDui6&A^{kn2%=<=;&_;fX3oyRsRY6(WJd|D!-hG!-u`!!rZU?-Iwb&JWVw>jHheq zu`xqjpZ$cPY-XV&xiNJF?#t+C?9LO@Llo)TVV{}Va0*XN(ZHeBSM-)z{dE;mVUEf2 zsuWxaCw*A8->E$tIgq+P=%tt6D~nRsT=ICudZX$>mvBYFmYNkagrr%vESSJacW_DS zIsb=+Ck$O?EnB3ea9Blw?NUoE@E8(!UNiN!mnpAEth2yWHSDKyVezy2{EzZMejO}n zcWQ;pdc!>B@*1Da7P)2FEJEfcXFBZL#ejp0G9I4(yFgvP^2+E{?ioM3JNKiGZp$>bfhY|Z@Zd?jD218&(N zC-J2qA(J9@!#1rN+YE3XY)Uq(lPmg`OQa}qLJv0mBWKq~m9T{Q0IIXN)?$YH+zfRQ z3)%VP7YSExB-84q)hBlXhOThWLHD6pHlTp!DJha;HN4L*Kz|}`?Y9reX*|Hfs^ff#&Fa$F z?ka4bxw&XRrs+sB?Y%3Nl&sKEAda26`rD;Z1q`i~S$P%3h`K^|eWOJ(t=?g)Q+GmrLCQ3Y0%Hv|3thJb}YO62B zk!M$uLOuCx&*l55`@RX+-s4rNZ|9QR^gw%HseqHmbM;?YUe9l+k&Wgu77Xr1o?~m8 zfOXr_>;d!&{MI9cjFG-qL9lJ+6gTFpk-I^~gETbH<7kqEvc?^6+`)0Uj`pHPD4i)I z>GZQ(Ywgru=gdWy=@X~N-}S`)vH6ccPWQ)bvhsDwGs2I&z%kFoFO=7ornM9u!i5WVQiVdv6~3ikie(pdv`pJwRh4 z81LSKG1W702m>PceyM1h^`C<)3YuV}qq~|K`A${}+_j~@<=Fzd<&~Hi+4hH~8x;(9 zae)eftdL(l8Y3*i^Hl!Q?iTk1fF13^aRT%%Q{I!OrqY_tvwUZgp;}Y0i~f}1v{7^Q z4OXC-`1Q&O_!*a5@6WUw-#*`GJ3q);o#0z`Xonv|4(G$)kWe~CX8F_z_CVLHe?-YK zx$3S~`VKswD7Hi(dcS!#@v3o;kjXDckLr%c3d;XBylp4gl$c55;Z~p)J}0ww-Bqz1 z8Kbfmd}C=@%|9k|pzte%;yOpy${lb^nsTiKfx}g$(G14`okjrz z5|w0%WBU3(8(c*)#rakEo1Ka~amU{#s=M(D%|?sxBrXP!gcR++%r(fm>iz8#B2V`8lW_^ z)2dPaMhcWOeV{KRh3rX!42g?BzI?1ip_owzhK>Yo;(lWqDduiRQt~+!HUkT~V5_ii zk2=*8nkO7In>q}ZXL9{vktw53l)vTn)7F1J?*hi!D>M2qSU$bnQep7QfF~>gb zv@r&FySz*f_+zQ>IqV-1tez2XxD&+C|DadhrPMlPvxcviGNW4)cUU>9xI`UACsZ{! z`TQk8#B@(UqZJq@quKf~w@a{jDmp$EVtW1fjIM2P!ep zoB6Fvkf!G7QrA&3K4B_Oer)^|f=f+EERwN2s(N619`l4DIxfVcvgAC z1vA#z9aANwHgupV@~y*&51y)U$aW&4(##Wp(vOcDHaAAq|GXLpKmX*VbO-}m z0X%P~460ND7qK|N{U-j_C+L4B33}!?i-m_S%hf#0J>4^2mn=^d_FFQ{*<;1j7JOR_RwRJ^@!dIWrPS*xpiwIO~NQTJi*OrEBe>$^(S+)~tw!hDp zBbQsfuRe~!vFbC*gj;;fdQ~m6Vf3qJy#m~1JYuS~KPB%cd|yLrWvq|uXle+&%NvMt zx7`ylyN6Wk5|L^KOY14p)umg1UwCE0y-rv$Xp& z0<`i;Yq))BpOiG82a%L=s3uiHYgsc2fCBLJl{WU;nZ14BqjlpzerK00YE-Rgv=W~M zCTB_BbZI;sLOz7w@*kdK;|Dnqr6J!7$?w4y$nohS?`n#l)Mc6*M12mkl``kg6XEi* z4v_h8BGWbwAI^Rq*01RM?qBauKP6io0>s#SkEv^0Imfv;Rgq;$Uy*eE0h8(chQB|F8n zz>^>Xf608Xn`e4WAdx-~^AQ!#Ke*kW_VZ$>;-mzsq7`sb-K4F)p&Gv<9KZcuW{Du# z)1NX8dH2y$)S#5y^w#(Z-kZ0&O9soIFcm4Ir~R|-ItFCt`gl5Y9GliLbD!Ils(spU zJMcu)bXbgn@<_x$qjxb@!e;a>FlrJVaUX$9!Pf^}e*a7VdPM^v`|thiPx#VEDbgC` z)LW#M^)8SGnI{mPXzeHF#Lovmb68CUef7ruU4gFckP{;3O+vM#@@#s3sggj-_Y`*( zw=J{*K1i8(8;@0PeY4WH53W8j(C)Aiob%b?8L@^2gI7jMQk@=;k%P1z8vHlnBp)6@ z+yt@weL(Yo5+h}oB+ct{WRC^_Fi`rPJqe!%#6D6NoUF`%7QA%>PH_KIcPel8}V^8F~cbYmJ}Sr_%CAn=A!Yc1)AA($n)a%@gZNH{3&sQaE=(*(3pzLnuypwg>J?jMOr8e2f>bjV*MD z`C5rD%L7d8dcb#dk>=_VbJ)=U)Ufnkq}n($ZoK;v_&j!Zs}BM1k~I=(hleVTJ*%Vs z!Z#t(`l55#UoX&d?0QggzXi)>O*LA8mSsuVQ73amxmQ4nS60VwB!E+$)6GPDIi?cHDQze%@a$SXif+Z@sD(29jBT@qCXF-4AQv9DuO8kjggMi6y>T- z1y86}_piUKF_g#c5qwmZa` z=+;r7-r09^C{!uB%sv?6H99Lh?@&|mC1ysj>57Vr8hs?BgCcqhGoDxDwY-GrK7AXj zn<Hic6EgS$Xl^KbN^Q>Uuk!0 z1^LovTrKXOhHD+3F26&9@6KW?)qA)P2x7>Kp9%fRLZP)$+0VOW@^{=z!J?V*8QzCJ z@o{EXqEX)GXxe#96fB7kO|NP^m7fz^R&N;`qb;?IwyIjUpLrN2= zA(hRTG*QCDh4Sx0ic`+`g}KdH%9m>Xa5vgQrT6O!|sDk+r|&-5k>?o z{*~4BIE2N)8)fg)zWg%z^1T|lVnj?J{ZBs8ZUI9z9~?|W-E=~NpVo6;E9=o|=(u-- zOM0J{PR-(&nUZMqX>wyj7cN;4u3E~HBH`jbW_o9s0WXJR`ZO_r-uA-ex94g0&ZrDt z(Ud7%&X9bJZ>pfb@T5eJlf*a{DqGGIn;H8$Vj@7lzYgOH2>&j0|0fafS^{(*761eS z!6%k{KLgON zgvnii)F|+Q;oZ|uQj=ZbGq4^o@UGDc6<8rxP<`&yvi?ozG$(|)^qZ2enVk?+eVOt=yv@llYq(F54yueya1n$C)e{UyA{5^%W z?_c#F>#ayYf;|5?3(db&OHL_j-`-NZNYMb*`m-RlX(r}cVtAS=yMdN>KaX~(zHqGb zq9FY7@TU(SYPc@rfv4ubN%69A>b|<@j-Oe7aW6|0A)+ZF>8OqG`hbeOCj<;VdIX>O z1^R4bwxR={@0Ps2A4poP0uw56L7G+I4dHMkc1(M2Rv4lCh-u zq;Geht^DzG>yC2WxORQ7dnuIvp82t$Jjw9fO6o`xA(r&O!7zJP3p85(Y5PyyNc%@(Zdo89T$a{L8?$URdXLoi)LHaAB!LT|6?C-%Sy&Ld`CQ7+ymLa#!u zU33_N1qk`J;+(Irs3UIS!CDe*uh7KZ<48U@U>m;0tBMniSCcys%H<{G<-4$z-^9G6ME?F}@O|>3@Yn9zzhLiHM(be@q9kCPqR6>j&L1U_PxnU+ zvqPO$Vc|*=lWVED{l0bgt3z&<8ib(Ef~WX{ut%Kl{-&P(CE9z{9xhiw)eaJ?WMm2j zVkYK}WAAHQDrx{)oVW`hHOS5%F=HP70&O*jY4Gdc6-- zUCd@m&{c#r@P%D-_RKj}hh`(*xh+j9RF@`McIYdVUG>E^`NDMi?mt&7*$0Rn-@A0@ zx-Q;--&lym+yI6|SjM+LzXi+N55daXdQb@n!?CnS+@p~hUG5sfZ_w%2KKk9LPHG^> zsCsEJM#r&WDRAoIIMpnuv+DI^A@{DI=QCr-qp0~y--RxOGk7e?7@AvtSzROUl~RIk zY~!N%QnVFLM9W)i#?6N!ga+~sI-z}(RlAhNV&@6%t#oLWyqqc%Zh-~kVmN?=E~6!T ztH4p}H(}vtPmN>jo(Fu)=J)bR>HLc=xLtpIT3Q4zccI$7LY)eF2lPuc zJlVC~*T%B>eym^Xl$hp_yoErcI%h2!EmN_Vd7IT-@=!=w#Ai1Dxy+0yHLb01sy{H(M#`aUk-z;UrBX;BzUKk(2=OmcG}*R_E<-K5yb(T z>RPKwp7s1ym7&_S(owqQHFK?u8o1jR(Z1FA7ru#sAuh0wd^|UU??QlVG@W~ST^c(Z zwhwrO-wCWD{UOMmhIh|U%kWvyG0eKZ-3O57D{63a5vu%cEf*+75v>Lflec_BtjL&8 zs}3OJg^=4y=T-u5Y_4g68b#pQd) zXN{I9sZs0QWlS`%&3m(r?hc|dXl4V8hDWs%?kl(}z1q;S?plb}eiC%)z6^`}RwuKZ zTt!ETTrrnChaF;qCX}uI|IY#HOsKp6ZuI`U*B^c>D8f3>T1e)`{Y4Nfu6y_%0KBTd zTduLF1djxAYL(>oU>+t(RXqAfE>RgD2%C086jv&3f6cE=cKZO|W3z0P->YOOaf%3{ zN0$`@Cw9jii!+jP!9rZdDG~g%xlzNpI6a@t)E#!BV*}=LicE&(rB_Q^3@PxYaMtOK zHB7azT{sgHA;&#oe$%f*o!@y8%~F9IV!9&CY(Z>E29_SaYEF79xvCF%y-c7q69r8> zJrXUB!vh+Zkj4kB4nF0`~S6(`dos%QE|h-AxEueQejkbQ31E=cu- z=Cy}7+M}(0%ulbN@hogt|KbWYL1xxrUmh+2R*%B~vbz}vi*WP0Rb%e_p}W>fIHImX~&1pKuVGU3|s%8oCf6&yZ@J zrZ~!sR|FsjoZta@vjHrOl47ei;io6hk{@>&w)>~OQ??ip^zyoc>Uu+HvNNlKrKN;5 z-}71~Oq6t2Wq&(dU%}ETcQ+LvA5^`1nGn*#6Pp{T@6o+8YufRokU-R=^KYb3Rrppv zLb?~W+^g>(Z?Y=_ReHpO@1O5f;@5+RV5vP?@gh1An}?)xPzi@EJJ^P z^B=ZC38?+;g7|{e#*geQ@9(n*Oh`@XnW2yX*thD};gFF~8nIVbp`CVKO0bA+Y+zE)K!h0Q}9DJWlzf$|=1zBDzvKQ%C)! zA<%@>UYs-`{V-qehneJg_-hFhD7+8)UWz`G>5_s5g2`xvtDpe-Su2 zdhycilO6@Zx|C(y^k-?@Wh}XI@|A9TjZUrUq7gr*=pK3Lz^DDbPMpU;FB#|W+xyz~ z9Fpb=q%#V0XQ+EK$Ky2@9gkgN)c$cZFWA>zgb#pBd^mKZ@0vf|jI=&&I*CIgtShrM zDs|7jlnIl0<~R2VS^7_fLXaQ%8ADWKSvccg2*`raczpam+n$75Z~9AUuS%Pl^r=j= zrY~YX?Ywy>!uQziQbGFyYwyQsg8rFOzQIOO?I%@){e_}=uV{)@UVeQlaQRJVp;utHI7-~N z+7(E7D#~Mfm@sziG5sD{;-aYTsL3qI8A8#K?NC%(goa3>WK$y(9nf)}lQUCD^{|)c zi!8vwg72Kzg+pp-URr-@5TfMFajkh4>d8O!Pol?b(BA&>#YZzT|D4!3{%!^m=A^EC zSXy6{^c@^M%nGM0J9Nj2JVz6%;wR)P;Ci-P$%D`2XM`Y!(cZmhc$5pbu+3io5~vHX zN+YJ3NU`x_Cx7IOOQbqFO`KYn8R7>xZ3tju_583%GW=>m(7=0;PK78qhf{`X)V(b~ zV0-H5ZGcDBBnAHyJ^1L~Of43|d;@3+E$Sex2@lx<20i7{E&<1zJLHmAD_`{oz>+G2 zmvhew(8k0nBi@VYss^-|Y;RW%;j#-E#foJ$rRRuM2_+R|T1&UwrC86SNDhQU_ci0S+tI8fwuWN8Ul2HU5CiMO zt05pGZ0GC>YC&Zw?lCGu{VeE3*{S%5&K6SxPZZ@p&uzI5!@p+sLF?{x(7w4_0d^wB z?^ZBEyPfvvOTl1ACPcEi)EQFYDAdvWj%TOz%?v(WRPqC6;KUwgv-Zo z%6hLXNMdrFfSY;0g%itCncA*`iZ@eMi>}7yRLgG?SB}Sq@kU(i1%c%6cE3{FqpSkw z6LDQ1F754!&%cqr4PFh>o3h?-W68HB5N9(wfSaUQ1^176&gDEv;zz?|vS-iOk%Kmv zuR>4>cTdiaIDN@BNvnvN;vg}tCI1`^lYzb1V*!tc%FV!c zeaO_COssdzx>HRGy-c^ya56|t=1?H4Nvw|NUFsjs_h78^in!iVN7=7_H~tNAUDNX# z9ZeJZ#7ScM;u9ywkWw*;{LtG(v6OgdMm&iEUEeZBZ`l)Lkms~`HYOG@*N$5zs*JFs z@uQ(R4^0xvHR;H$iDa?Z2wQZ^_6*@MT+ui7a_Ce6ZU2e90l3^-WAM8?Lmqc*w69%5 znGew1k6?o@<06vDej)(@PZRQLe{b~FG~5O5^@J6>C&K4Ck9u2u+2v<}JMDgaoG-{V zMS%cxjCOZs?gs(ash2@A3e!6-)-N6*yY}ENzyfza!24SLS5HsD2@V<|OPx8_4NvV;MztKG706T*C)s za`EB57}(AYeb@3-2=ys;5ule| zX;dMy31h&pnE$G2uth%{I0VUKU560RDR~N{G@Y0J=N6xGO;qak$t{ zgPyFUI)@hnN6=pAZR06g+9o3-R(;x@{>?@8WS&if!vu_I1d8-O%tLo}P1GO+AOrWe zzjmq%?qUXav0Z`4vYevw^`!Wx-YsvmlZob81WpO-2df{=M!ReVzl!ils=#EFasou) zH^L}$k30Az#)s-MUEWsLr@cbTlNvSyX6brJHGVAkr zd~X!V1DhkG@Wx^Mk~Zk(t*4Q;nz2~ zXQLUNdqpdiJ9oRULM~$SZ!#&_6NV@Sx zCOaNHgjeJ6INekUF_DRAg$&UlnjQZsRSgd8tb7x4P5RrzzZ%mS5wQKyx+D>X&EGRr zG3jN6n4j-2Tm%|`EBQvhs(O^jl&f<2ZG92;?YqD!lGQ`SNy^y`w z&w6*`ZRA0JRSpHAX=6kLo9{7f3Dp6FcJEuD-TTS=hn2h61TPd0Tp;P8E^x|@AF8L; z!AlSK3707CLDdBx@qshG7g0vp*6p$Fy87*F+un{(R-7AF67xPdaq6v`Tsf`W@gI_7 zjrib~mi_4_SCm!mgKlBgsBLMf#}&E`jrQgd#zX6{f(*-T)nDxXP$P=234HPhF?Pi$ z{F)i%RY?o^Eay0jtyx_68@qnxObfy=rBdr=_Q|Qb;IbOdmYY z0j;qgqPn0yW($ec2ABgj#E=hzt$=xFJ5E&$O3!4eOj^JEi|QBH4oO1%Z!b13$KoYB z`sle74yo|DWL=pf7_TN}DTWvF&ox8EClvkX@mB(BWJ)NSUjvT-@LQp%39wh6~MxLdA9r zReZddhKCt?oT*HF>`eLkmLYAg-ZLszHATZi>nAeL{}~_yl9#>knq$EZu`)}DHcceK zyYn<+xipRc!$aGARnw9)U*-dF>t*lNN>%PJ2pJBV$UtQ6s>V6YK4Kv67n#R^EI`;e z+;}#|TrC3mk$=kK^Qv-^?=+?6Ss}yycp2SUuPE)LdS2zIHK2=zF|#8vxc1ft>Bco}C7f*8nRCOS7IqgU=Uj5HTwpF~%Q(PI7)jKJCo zKg4Ff`@q8z9RrdF8bM@bXMhDl8@4nAVUytyP zTGm%c$#fk#n46+lCe(;oC0Ue@Nl1aqIq_kX@mgd6er+8pZ}brjF}T@ofJy$<`Ek$V z*gv>iN`gC-Ef?iQQC*K>q?;K^Dr$0vNAL`G#p`=(f5t#*>eW7%$J^L=pAF3Y14~*o z6xVoh<%iXGux8^+@4NFwh~*Ce6`UWal|>~2`Mq8qPv~sAml&e@P1pp|MBzh!WsZ$f zRg{-skYPAFw*QBG6RF9Q8JPI-x25mz#tEgrY-PGzRdJd*XMJ6SrQTVPo}<+Ps<1)N z#MUU$LfIR*F)Uwgkc#JOg^Z>_Y0M)3=rHycs<^YHYyJ<0EIi&)oo@i3i+ts}HtkPP z-5BxL8F;q{NP39+ym19igkAauG_lYkJNd01J^$K399}uZ+kJzLLzmKSFM`@5j^+Hq zw-UF|hB!>@8%W0C*TWpMh!=4MU#)rVSVzB5Uk4w|(a z&i$C;B5mp5J1_IE6{vmFM+eL5uW{)QYA$;&314IlOTd)yD|&iRU&>T%NFaUs?=7_C zC=ZCwd|y-OQ@RLAyJ-LtIJPHv=l2?L)0V(0fQ3 zIol*$fo$3&-T%`-Zo2@gIJa-bt+iy5_woAg!mR6G>_|0#UtDv-E8r*Ji&{3%#7!Gog1dm0p+{vfY|;TBrvn>*DL#V`AVfBxdxT`(h9ck;w>qWo8okSOKJOwT9Pif+c2 z(KFVT%#-3(I3OiI5;{VhUX2W@3f3* zQJjOT*m}{Ln`v~r_8+~BNcDeqG|qH-LYx^C%PIR)8@q`V)GJmWZ1CRWybTjS3Yd6} z0(-UAM|A|GL48IgAWdI@pej(IRkAl`H_=PPMtF)e-Z$+CY!rz0+r(YG+pxw?0mzTL zq;IM$bjK^$BabBTd)(hDI9#_1vm@h#1{1kQiHUzG5pML!ddV2K=daM5s`!QtlBN_o zNokOVTKk8ht;wQiu|)j4VbuXZNY&^FHFE8AWtM++fu%Hg8L3K-Le=>I6T*NraC+W- z?|I5~SOCs<@k4^!K&x}1OXU0gLel&2msJo4!drAhovL#~wnh6zaLBmOYv*ijVZ_DE zm|WZ3H)RjD{jbzwR!GQ1jPIw!xIISDu3GEKBt7<~{)r zBfIbu6RMsCz~m72lRs4W9$-Ox%MaSOau5B?`QOKVSs5&W_#@SjxM3n@*u9-VIt}c@ zN6Uq~*MwNiZhy4j(~`x~me^j5m-CDE0SQfX$D6vRd~Qw_KEH`*=+7wLZ9sXXNldi+ z0Fzef5;$~@5fZ#Tyx)EbyCP(G+CbG!F8tzlqwmku{KdhP!k=OINx;YFnh-hJPiT(jhAoWJdvteWj_Ed2LC@qI_H$Hc8hupVq;MXeK@+kjzKW>sFEwa2LZREe77eUWZd&)%SN^y!eO*dV0c#;<3;}*~m zu9&OIeE@Xahy68~4x6dIePs7K^LVV=gy-0&w`j5~xN;rqRh!#tauPH_y4-r8F8G2x zD&@IxBk!ylA_ zZ0Ay0Q}2=py>CxS7AIOcF*$2w5A;Xhkc}oBiU?gAu|Q}{XE+-%jqyu+i9`lv1+&e5 zwWiBu$>Wj?y@sN2*=-fGWf8V(uEi>tF{udJF;>evhQX^pzp9A#p5%)E@Lw*t3e?Ib z%RF{=Y5ARDB zlTjg%uluRbZO%zuvG=FWt!r?iMwAC-J)*alFK{2p7&#@CyRp3ds*nw;JGe4rzKM70b0^EHrm|$E_U*xBVE6Jo z730`lso&Avi-p#8+mo(BXEnU(|O2d-U-AITu zDvh*Dhopeg4T93Wz_QQx@cypvUoO3No;fpf?z!ha=X@FyIvGAa9(%TO^Z7O2Myjj6 zin0TL4U<@WR`_vciI?O_p?O>abmIu$eh=IGGy3NgZm&42P$;7O_qZ(b`A*-1G{t6IKwdvL zdQJs_WdHVc6OaEv^jztZ{^|*MqnzeDy7W4P%1dp-ujVgsm(Yt^z9@pHHoUJqcLV*2>$tXkq*E(RnyM&+}e zQV?Mg(Vfb_q3U@0uhBZm@1fnS);cfaOcr%!HDzbjLSi(i2C24Sc9TrMj2@>$w5lI} zw8D4jtbQI=ZC;7_0Y7sc8~yKvBOFU@D=vI&(3)Hy51liqmN4R~NXhYInUP}T^73(PWBG@vBl+zO}wh?@6($6H2;n($SWs;@Z+&zww zOdG^c(BXx{@LEYBJaMME$g3s=-ncjGIud-F{3qJ8G4Bj+MMw+f0(gq?0`zKF=wz&_ z_=;jep+}eI-eE8Bh%G#aNz{~W+8(fC>ZzL;DlsJ>vvlM0LS%Dvu6m#zpg2?h26Rni zzQu%e&Yeai=5tPVJ`WG)MW^_2qqQ##@teS8Q(EpJW7#{%&M=fql=3~@|D&N41XhL2 zo+$ek8=gwjD0D$@+A?|f)km*U6OerC?-7wW$#^60S=2VRD$w`XX&tKy-Mf^EJg z8;nx+i`ZGHz&`e;?keN0wPuW$WM*DBQCd?h-x*g3xyOYKt#i+HGX<~T zI~Q$HAtsT8j~VyuI^NTnBWe4~Cgv+6LqZ^vZWG&#JoxYpTaKuo);$N+{V)B7>u*FB zF*E93r21JuQ@_Xh@j~Z^g{Ia3M2vwNU>Y*B@9l;aE($rYnmW@WB&a2k4BD!?=sxld zW4ZIsmK*oJqxL4ACL8jut6(PC6Q4@{#C}zi^3YmRAZQaI##=zrn^CN!;6;sp$3JQ< z;qxFJ7(@SG61P)BI76^$Y22QVZ%K~~)iATiqAR5*Ap3+kh;`Rw z2AzE^01Kt~{|cozWUBC2%2uRn8+KX+^C6A=ZmMAK5Q9EX5m)#99Bhl!xcQJp%!lczT+LW$`@5Ii3P69%@0q&v>-HJ{{EE$_SxK zNyJRy$d@1@+GS#lhgiKkjGM=6=kNVZaS%YAq+Qq_7YcM&qvM%ACtv;W&LA-kN{VQ$ zp>8FA=OR)GiP-ZZ=2z;4ewB2MP7HJTk5!@vexr07pR6f?D|WO5TkV4GBZ>L$#@tMf(bF9P16{@Lwk@^e*!_qqORxl2PHV={&9n!Y}E4p-(x%7Sg z?(hc@ql9=9!O!+-`H37jtdHrX*y;2*#EOa6SX9>1cEu(M5@~E_@1E?NcK=@_Mf((; zw-r~F<8f8 z-dX`QML&oZcWk#89d{mr8*KyVuGb!VTM51I9V&UiH9=ES*^8EaJ{$Xzm{i&KK(XBI zG6uxs0H6nZ8a+t2#ShzzzfE`$!?~KsL}QGN*7Sq5%(BdPq?qW^zix))*#P?JBV`i& zaGazlfW(x4Dz{|aQY>bTq1Hm;z}X6qhu~i~stW_l_@oQA)^@To2M#dv9vV;oZaI(x zixe51T!6U5WAvRbx)O9bzYta^3EF(xBslQj&ns>S&SMHcM@wuQ!iT(9KMocZG$!!B zZgmp(-EjGyoeRYZMcCGJ+A!G)MdCDCo%fB4Q@5AB$mgR2-ywqz$(p@q(LlTWE9?1w zeT{d_FE#6gu_i7OTA5ff3XN~PF2e#eLyG3+N5gE3l17o*d=St9Fjfpm0TJZA`>s#F z$&#vljCQ;aE5jUZKf;{anqkh*L#%3&)7$4j^!{z&tiZByqZ1ld9QvTd{Pe1JFVySA zjepXDR`d_Ni0hxs1&h<6?L|XDPWgJ$BS_PyNNo)2(V@4Olf?1M1KQjcaB?1eVwpr- zh|n6f1F_Y`Bp;c|zZRrW>d@OquQ!X{$MApJ#sSJz94V2un2-~}f>{~YTm?=@$hY%D zRSd`$_()vGPPRN)TK7l4n}p^6-TcnxyxAJ3&QMTYM{eyUk-z>=moFTzI-UQ zW2wI+{%CoXEgbTaK)8UOl+`&19U8zY#K(6+MGCabF+L+EHIJ$6ONkkMQQPJ)t-nF? zWO;*NR(V5gmPUZn9A=tV%HHN0t67XZ~r*ZQDyeJ_d^obnBE_Je+6L*XeCZI-uE8r=^YA# zs$%lQlAe*&(o+G?Dd|pTit0-5g&cRZ>fDLguG~HbU`>Zce<7Ol-H>@;rm%Vj1qOZ> z7~Rmo*x(oLS8FWvH{fmpa%@%4?DZvDG1sfmjswZ?ts|A*A@0fELX=(6AbCaDXB{i6 zXKO6@b^b%!if#^32yU~GJ{9Vj(=Eo3lhc0|q6ry>bW}i}Q|luEwo8UeXb;ays5dE< z2tIn+;v-iTHsWcP>9#QX?1!xyA9+0Qv-k|+=XkxKaNlQ1Dj9j-f&)R)%phelQ9}(M zW&4v?rAcsxw>GK8G!yH+dvT)|vg}=WPw!PRx+MPFz~7Pcy+o`3(%*h$-vhQ5nJhqp zdA{WO=^%_+O4nDXbOjxsuH_Hhw}C_mbz|lF6V7@E9tUwLJA342QGqskxtu=obB9`6 zq#-x%q_D;!cXQbwZO_}+OkwY)C(=sG9C}4O>+AI*>8nvl-?ejG{l|zs0*GkFn8A>k zzQU0K32fGmJ1@%b-*@WCA{K!H-XI|#)hqia-yA_-KIG$plxM2A?BpOdyk=0dLN~&AhSXeO(Vzkd_^}i&j6E#JXsr|o-fI`O#B>}Y}N_dEfaKLXIEkQoy`w& z=O&ka=KBbolLhkRu9m7{RY1a8buQOz{%Ck-6+U%EuCVxzp+O2mw^7&5=3|Fz@1%S; z_8IL*AT4j?P5jn>O#gVU%@z}jm008?%l?J_JY)g6xKT7jG=^I;6M9_Hh`4v{pa9xG zO|4MBEO$^dfA7cVD^eQ5|2xew(Ot*-9F?o7?gyDLNf8e&E2#j#)|6SfA@pHpGX+1k zL%z$aP{drEd9b|s-dX?Y4@!^^+S}zw}Pbn;8D_(;;S{Ar0#UA z)nq4ti0)1ugi_$VL;XDa(V(nyxELkLjP3NW-#hDkNa%^+(J}Z>nnZsRpnT-}&{7cc zulr7q0TTpczf`};TH`eCb{(jm2B@?NBgK0R@*b~hebvFol!QC{NKMy8b`RES{CpEb zUmZ;O>#P70r(IX9SxsV4jiJv%)zmsVliWPZ^FWYzBb0sIHMv0=hUidwzvZF1GTf%J z+QnnUScJwOY_l%BKmK_DsPh! zzSBllFfY!RVf_NBexRk>=Otpcede*^*Z6rG<{LWU{36X~X*w)+|%+~KY zgu*qm#Ex{uq{w%_#aZ+xL;1BrgL#~lO8yd(7D7h)Y7{j86^Dz-m`)M7H48mUeW)O$ zNY-RG(WKkx1?Y@-DTnea7l6mmWS6Y8WC18KS6Bz;#@pru8??h2nruvVER`FVcW%TZ zL*%r4`ZQz?zPXHo>>-}GI!X+4tF`@UZeRV767<{oSKU&L&$9BB+7;~_6~g#?Zky=j zQ5(BW%I=D)w0#yBR(`~25;Vx4iB<)zXa8Q9*QhE`ZV5H(Ov81mjn@^$dmzb=Wj>SlmqC7YWg@80qs3#tSSMCB#Zsn7b_4?d3|;u^HS zFOj~Dq@L6b8o4B|OVkjZtI! zgBYsfxS}t_e&fGaj97ub!p4^QEH;WF$l(PU^{g-uOGMb{)`od_nxaHX)Rb*~xW{H%Rj& zp6#VdzSA(Enqp)ipIKcV@mQVo7nIgDA2trrKzJ!>g)t{`v|hB~GYr}&BGNlf)FZ{v z89U|fRL}^8hGZTgW6xq?rH&-)M-gBHpri2c#?OBG%KU5S)__3xk zIMPV_urCvLU$mYl`x9x}@7CffPBMe6^+|03l^0;cf_vKG`8_PCHKIS;>-x{LSiyqW z<=}I(0|Q_Ok0R&h&w=KKY7O%n=>3mw!B#5!YH{%~&)CrIzXr^iqtTWiP_-*!IV@40 z=lo`sL5@)W${j6W93%f!I`+GJ6KfTXy7Y78O{&X;oedcMb;Ge z&_uFR<=h``Ss0n3m$NP;!4d+_G5Vu+uU8}~1J~B_@5#s_NTVZnR3^#uqEot->(lXE z%)~PY)^%_d{YblI6Dt2-3sCHBW0Uz>Siz@KSIHKk=R6E^cdl&ocMfMq%v8#X?)1{* zym(ERnIz5!6?ulh`$8kZC%^gnQLL3b%bT`vOpgcmDc4D?N zKLobm!d>U!TY%%zg6S>b#-GFf(zSu|C-S=8_&NAO9cR(dw1)A`@xNW8 zFV|_&Ip0~I-$9+2P2*${QWYL>qn?kA{&@a;UXB~JUC**4LrSvDS=RFXJmgh{63nV{ zz9&XOxhl5n(W&_4Eti7i!c5$I?U+UciTWrwbQU&>uyBF~xsH_;zN@aKro>i7-AM*~ zB|VPiXCo&?#E1Mhm;rA^3Ss`&`#Y!h?0zGQ%;yTo8qxyQ34ASG6tra*90C2!0Ql1& z@AP3;YlW|a$(;$F^RlJgy-&kO^)d?XdA+|$(1=%uIkWJT9eJyJ$@rHi9d}<46f$^^NvlZQXHN@{Fvr(3$OYl_)dUIiqkxBRPJxkEY_6=?}td-)=-c zJ&O4&?I`_E;^{X&;+MkcjJsBl?_?5Mv&sDthQs>Lb`JkH-~-2L znw1T}gXx}S_}g|78273OFn?-J0MUv_I+np7?!QRRhBlH3bUw3swrM6IpqqKjTia#r zv$1!!>+`0KonwvNS@XE;`_bH!Qe7tf@VlcgB09WcjAT!)g|coZ85)zKP{2&H*qB<# z)-}iY;C=fvb-@VR@SO+xP3YMiZrq8LBm#}=Aiuq_?MP{zG%`$Wu_4i*FN{?#k&E3r zrdmt(A8H)W+l-=6FIQvn%q)tM4a<&N7ebp~b^F}SwkzR;m9gdO*sO_M zpQMn{$#MQiXyF3N+RB#!zh430^v}=XqdRI`XaSl&{)JxYUeGbI7wPPpl4vE`Ng0N*_q57mFG~rGg&r-#H^YaIE@5783dj z^)G8%SeYNw`i8mor;alVq00XdDjkrD)y4DU-(qF~mdscsfPMg=TaX5wuyV|6MNpe9 z5dM8=d+5!hKtkXn%qVyV?^RZObbve!eg}6Nc^~XZD(I(3&d3qf%{!R*-4Kz|$cR-h zW*!&ZEEP**JC98>CVd9PA*+GgHcQ#7Mmp%*$quzrnAlG=H9iS`vfQ~WSuZjVSKMK1 zIP)rVdd!dMVJ|7CqCoD((?Y^FQQyA(`EfLKtT8v4qdK%Su8BsZj*U!KYn~Ec>(dx9 zIom3k5E{1TLxt9XA2+u>{rW{F(?4uv6Ii=Slp#3gRs8Iri9NmAM+t_d@+TKwt-p?t zIvHh#!ZFC1dp9#b(U4a@(=)6iSO7vJn{(%l|DeF&I#_X>fp;)AH!M$)jL1~*VN^Qz z=bOgZ!SmJPI6EWY=LD3GTgw9DI4tkp|8@US=i!{S6l?<%dwVMW8(#6mc@S@anw4C* z6ra@2B_>iHJ-qX=2Hmt*&WA|y-tB-VeKuq#mS18rKL-Xl8pw9}5S&@zFqzX8yu_k+z1 zJS zqF8i&wLX=(5s!-KknVj^Xp7wDry+3Actls6PH(%(C=!|Q+U1Y&dc1&rDFw4vZGLFa z6Cu5m3`cwQnGK!ZT9%F%WB^^gQ{-BYcwI{jh@3VnaqkD>aVd|1c z>G7mpFM^^h6z%_P1i+h|oG)*CCH>dCe4^phO1gc|n6f;vJ=8eUk>JibV!79{+Bg>X_ zMB?fn;CI;R+OwmmN+|0oO!O^&UAxbK4bBkAW`Hd=R(ms%!4O&~5|w1JJ-%xS6Y*JQz-D#k)~?dJrGMCy4Fa0u(^p-{Yv@wT^hvbf8g|UL#XbSyn!%U<{)V^0 zuZ1I5P9+xUAc?+l_G3G%qbm$yW*tCwWv2? z4$l)Y42dgU!Bjl|>LJ`q%fx!I2TQ_{YP~BMe2;RMe2+2uWwPt1AaTPxvi;K;Z(79M zwI6nRjg+PW<+WE{$7TAZ2ngmiAMfD@np`_=ptBL5)lO?G*#5_NZUU?H4gny|XY}C| z=D$>xbw<(D@GS1$GWm`o%JIQh{HHYSN(8vf!^Aka(iQawf~DZ#zY23!QX6BJG0&vc zoGkI@7*ix^J3$mo?{))TQC2~Sw2$r9LYPsiW@KZM0HX?!!|9noNFKXp*lPIFfh#>k85N zE^D+n)Ys?Ma6ET6o{nCCc{X8f0cW3WuAoQsCK zDXR9pO^H-WyaEZ!|8?mEJ5~bV`cQlHGN(Vkh6Rp+mx@)M3v=qAi0$xirk z@yj*waE{yP=Nu-#B#X+GP{d2+SD)0SJp`H+Le$z3yyi-D_Z9GL6X;nvzA!PkV!Tra zm8(`u7i1P7LX~VdmJEQfwX;grZ@*&KCb4BEd?vwTiRpUt>ARpl&X_UNpJHNREqZu! z|8%jJP60+H$>Ec1!@9TY6a>@+dnF{XEu1DkH56|zxG7N&q^B{D3dG=)l4O0P6%{50 zdHEqMT9@4ukE9%1PA5&dels32^4IRkjRTN9T^2}4=X`SPbF%u+2 zDvBJ0C1SH_nxqz=4LTg(3nQCB9>4k3QHjM>KmZ{e2QF`OZ)oiswP6ywjYs*DGNNOKmJLTmuwn$^ zMCqC}yFbCsTGWS@rSf*83KT#6p%xcmY!X_jrV>QyE1;O#@v=PE^JA!*yuOeD+?QUNz2Z&S^*f%ZLmPUO zqs@u19PNkcEjDnM94ydtu;IN46UWlAtgD;Z zNJ2K|zWn`CzsiSGra@S0~*}5HIyKIW=OrSiXeh=PuQZ^nrYRZqrDHhBiC$ zGi#dk$O?+e4=8tGmWN0fQ{b?% z83$r1M8;P342pfx46C`XpH?Kn1Hp$H=4FS=)Qo&EF{9qoK$gTfQ3wdk=2D@g#&`9P234ZN9H z;4zG8>}QVS6>M&0dI7@p!P`R@yVXDC>-Jz%MRt7=st^H`yt@t05@dEgQr|F@JeERZ z&?-Nb;Rjy+C2-JW`b!d9K2PbtrusOu@Z0rC)E|6R@p`n(k$m)R2kgXOumowPZkgg_ zY{?o@$*cla6szP0Q@=W0dco7u>nQsMkDIDq7$)Y|nW(MV3j4?iYi-xslLysMZ6A~@ z!3QeQ16BwQq+5~1!Wfkm@Kf7n5in(NnK_$~Q_n^VEjlv$lbnTxesRh=R+2;@BP z_6`=#(2yn1@cu6)yAY>YP18$K;Y5RBdy*MSE?PT=^@Tx&I=+6B)h6*IHp}+NC0&AW z!KgVqF`*>mQQ+X@*ddVt$79IAyl9lz&n5Bhc~i>i+TR|SWnjJ!v9_vrZ$M%%4rk}B z{EOCyZQ(gW)M4}8cgDR(;ErG5K-1dbvD-rgb|F~4njvgIYP`iEerMfxHOI{9vKE?* zU(*?Q7GnR%NHrV>)sEEOVSElhNngi~R@Vp^i$3NjTR_`Rjr*F4#VyrF?8$3J{(PG5 zstAqr&*%VKB$ibOT^wr;|Ecz#^gkVOdJIa;Vci}Y1x2SDPY;bC+*95wCTe@@J^@rg zCOFOz*MZpb{KIlHB54(+;yptDf-Ze*lX^CMJPE?{WTDKt#4^ylVGGKN!5_FqR&CEc z2^k_HvX{Rf+SU3$&@qQz9I=$OW!M*6f6Vt@srH6|8rvUiO^-0z3+4(9+a`5I-oo+* zk>~a5gw@0yM8T!W*@3TGg^{rpIEs{BMysQZw<}nC%3g6aHRUKk%08Y|1Wzwe%|Ui5 zWL|8l-taJX3OrqtUClj|l1YTSEvkn@+s`#L?qKKZ(OpMb;0czSB+-QL!`i6_!I`_t zok5!&6qVX|z8hmHsXfX$AAh|eE6C-ct2O9=@d}AjpX`Z@yZ(F)neCrzT3MqEG3f7s z!S4JhaHloJ8O1ScSy24}z{6ST~x*MPy|nv_uOwKrdZ%k)v(4IWJ8a{+(a9If1y&foqIf$qtP z$j}=hUc;ybX8(ldOx+_D+Xbi$+Y(Udc-s~y$)(sy3~7bWO&0Q;YVUvX5*a>7JCFU0 zck2Iq9CXKd4V!=?i&JIg9`;u(JnH3F4|p!*0er`KgjV%K$t+06t|86efZg^x($FL< z2&b_^a+X_xS@(=O+O3ilGx^;dtL=5uoc$d!(a9-!sp+R@VXLa0rN19@U6!05ulSeG zt#7Vk%6fCo2>+yrf5P57-&qMNQ@Yptq$@EG68kmUYIE~M3kVVE^^Y!vNL+2+1|HgKaN>vkmAXP7x6h&9A~ zq$TOoBNS%`z^%w-bK;rpQ+gsuoDmB83FbOY&niuGX{o=2QdcLU%@spo_t2WkB(UqE z?1yif^9JSH_mZ&|&zLPn_qd#-?{+&>{V=(2Na~Z>K0SUapBMqPu8CQbbs>#&JpC3k zDUl}vz8M;vs9VE=hEdpWr#V*MOb>kcpFNeiO~U-$7aPz9UTp3wNaAaN@!&!o(#{CE zFzRtW$`?B(#_VQXd3ABzzcm2PEgz3+R23NtV6nJtNK2QBjMw1AOW#=js;o-ewFFU? zGsfMdy@R>CI}0Z+zp*EaNH-1&di&S@939io@N=ybO7{^T60MmhK-$0B;LUjR z$IJeA4~vA01UZ6#&84%05c2^5-4=4~GbeThj9}Js(5e6RgXn0!4>RBQe>#$SmP$6* zPp+?d8)eCnda`^yNAu*|++pio3 zgCejmvEj9VyOvCb{=c5TeKL=x=Z+=CUaExVb=o;oGhvfdf%2hV8BF62PNQJ_sh5bg z?{5UCTZXToZhaQ)LRZtLko76~Z+|?m#=j`9jY-!fFTgIC$CA2d;)=|oN*6_qWMGn2 zgVL>cZxa4!@LgD~vl^h?u*wOc(copWN+upfs&V8VRwBs{^5A6FY_HgbxR*t)in2jC zXxPq`8CvoB#Y*q@P-iwKHutE^p*|Qo52y3X;(eWJEFHld%~Bl5zJp^vA>yuXgN@l) z|3%DZFBkjc4 zK=#u6K{<7h&5*X?3W9vpnc5zftyo!Spxu`Cf3t2~=zl)d>p>EF?%IT$Ns-{`f7b)C}w{*3rGr06ovN<#<$jiz|6iM;KY=$B}+5c^oBGrAA|1#Bb*K`~?Ixbs(&x0?jPqS;< zNhWn=-stiTo~_!5t7wxH+oZ#hmRT!&S}-W1G5m{+@GCDT+@vBp6wi0E@W?$9i(;2+*Pg^2R~_%4G(Z z$|e*_p1iH4ehuO(MXGg=lZpFtR|THHt$>eJ*sgzu_e$sS!suu1uZo7s7M?PFVSgLZ zY9G$ooFS7G1-&)jcMK5F9+VYT$}hThYn@(9fa=8NoDGflv~Zur(~&x|4ecwhh=*{V z!BZ}*9>N!*>t#_YsEa=(j&LiK5I+@#WSBw*K#Kav_~>1iw+v)2LaA6_?uN{2c}eb8 zH}OSVMGY_m?&txDPe3K(?cJaEI=SvIUQ~F6qAR_gtut8`G<+@AEuQc1$1r!*2<^0r z&9`Xb`=JlGEKy%&|B8boTDTnUh zFLUgxabEC}vKy^|UEwzrTVM-oGcDdCNAr$Y?@@{5yA-r>wZ*GdjKZMvtTMvRf+o&P;oRN@T^D|EFd8ion95uhGgF-5>@kgzon~2wj3JIo|sVtlC;Q)thGx0v!IK`AH;P3V4 z{a*SW@uILd@xO0EsW+!@>wq8%;#u8a^RMh1!e|;Jx*am|cIi#3=!?iufZ^fI87@`# zyw1*Vj!&Xk>ej=*PHOjwi_F?-IGZLyjd3#6d`=W>$t$u0(7DLa(Zsh~xk8%^c)glK z!N01=u0NE6-PSi+<^GcDohI=BSZj`?rl0;Rdb4+CV0Q-uZLO(D1%RKD@}?unJ@lic zJsr2c93(Tf365<&WLY4-kQj=1U|7(0w#nx0?9%KOjTWBR2xA{^brMSzWnc?LU=$k2 ze|_AGG@7fS%I(GSfhES4s2sDog}tF`{u3$Pkedo};tWYM$|UkB#R3zFdGBczC8>#% z#k9sOO=F;YpRs|*(#fo0>IS$Go+)O0bXLSCkG4c;iho{=6hG`#ZbHEB&JNDOwx1)i zF>4bF$_zvjZ;imHZU)o|jIXTB!5wRx79}-0G8%SK#q_HEKzt)Pciqszl+V)}{698t zgesU0c%dQag!)Pw6Ivb(*c>ueCGU}+Xig#r2KI=y!LZWw;z&5UL;TcML5npJGgpdC z+gTqOig4FM%ut_<;Y}-LW_n#c)ymn(GGSeCZpa_2dV_<+Aqv&+{;B*ae}7-eP1e|e zh_tQ340B?IUOB-t@D)gEXbU14`91*WGXp<9wo2Wm*U{VC5(jXAP zidKA@b%X`!vN$g1iSJKI#Y&J7=~osg;z&HpwsIb|um|gg2F{gGjs-TDoLGA`=s=I| zhm5=w25XI-ft$(4r}jH!bK{+DTkFx^0%uV%Sl*7CmFHW$w*5%+ervtRu3}F-je}be zb;i8)=67t&`mfAI@g(7);{m^9Cv)?$6PckEc zsv&@x_8dI>!mj*B!6sDd=38ds5P;UPnp9*)`?gy$?;hqfYU76cLmwToP)bRoOLnV^ z_gKxWpLN0f?OSs12TsJN#gwz0$&AQGD<&ON?dhX-w6=J_cAMefJd|I>^KMzy)XuX+ zLo#fWykGp|GO9c-EegQKtD;iD$k=OHAQ43XrB<@#qSWFwNelLO<^D9t#xSss)Dxr%86Z)7BexU zy&oUaBp=j48R%q;ST)rJLZ$IyG&&qG@fMy8Y|Cj)Ivgqw!faEP?M4y5)TPB@w<*jb zJ|!6A*vDiD;Q;dNX_A{NqMf+HK8A(t-MI~ANUiUwa0aamoJZg9i{`fpJp`(-zqH^pEV+ zQy^$bU&t&s4zdmck0`ZMxR=`3QNk#1bJ6-C9@qwo;Jp?UlN9j*QBdz#DuI#}Fk6-cVGLgwGG29-M-dfa)Ab31avL66yhu6VC4>-#}XYLE@@pX zT2dH&NBo^Yjyk^-plyQ=Ajsb)YJZIyQx>?Ct}7WtDI*}*rGa} zRQ(D(6K>S&6n3OAvtqW+W7hL{91h&tb49GssmRyHntybO@Xb;VA;tQ=munYAhO8h0 zl&TCp?%X(Uz}mnO8Xx-};2`Xr+rF22@uS>T;=3dGZdB6Fs&|GH4&)4*RNASKDlbrg zQ20>Xdq{#z&b}iGH@V2!qByNfZDud#XXKsi6)!65+T#Ux@0<$HJ^?xtu8EQpF*pa? zuL!cU^fjSqW#MHM#|oUM9NnXf8*5&?iSBpJTx;e|G4BBrg1JvxM{mCc<%gV(g5c5~ zu_o=1`DE)HDu(1BLko@a`3;9!T`!$l7C1>q^f3U*O+lE-ez{o?tK-ys+b$Ftgvyn_ zp16gXXRwbJAQ6B#D_ZRg`37%J+z)l>3*AqhuI8OH!Yy`=R8}i!#B20AJLj;;0Gad} zvX4R(nSNS+B_AicwmUn`opNp&YbFs!l|rSuF?QuQesJD9`X5=pKF%h*E>DZl+W7U! zwDlRQXmygUto(A|dE6-7p@i4`x&UclVF$N!T6$NF(J~FrM2C||9bdn1rZnLZH z?HCGp8eIPcZ;{wZ&3!2eBa(%H)c5Nt&|TB=TrfV^V`p?2zUBqr`k&u_T&u8h zs;p=q)b3TZDZY>$D;l|~K_jFh_}F=P`>HZajpKy*%LV=fTM<2$Ow3v(r~}~aq-@{~ zS8iIOW`d65Oc7PAqdv*b_gJM&ax;J=B|wi{a&MF_!gX~?MPzrs4so=;yC9X*a@f9h8Hm)YQg)oN2#MXWkT?$ z)|_pIoLCWn_wL^b9ia0-9N}SyfORfc9ek|-r$68_}p1 z-{wJ=SusD~Iu$XhTDMDJPo_BY9`QrLVm9p!@5dX=AMY9G@3T^=Kt7vgK_Cxrwp+=y zNPXDgd%lHD640$ZK%OJ`A_G;2#V3|3Kjv(zsj21!6_bTOe4dIGNxl2|f(S|~oZR@~ z?3(!T?vTu}#Kb>iidToHCSz;}Us}K?ovZOi25lWG0M5+%Vm&s2t2VOJwH)f=(1GRD z6X@?h%`yZ}XGkF#ZfRSql}&i$4%H`YZB4h@7QZx2DFRk_KD%TW&94=SY~9yk;g@C% zYil1AJ-wU6TG*GEbEMF@k`Kj@o_g}fPl*2g&wUj*k!;jSl#?tugRh}Z?^tbq+7EOT zu>(N}a)e4NS1wwnxaoCOO?7g=t4nKT7`%cyxcK_lFp3D4GNHi(4k1Dw&i^|&o+O9^ z;HqmubXHFvFs;qEkx5ZdkmQ=4;0K3Q&5XSXKnr!v8i4#cxaG@ZN9Bj6EnP~ldqkm3 z>(r*qMD0#$*^Cp6+9s2QB@m2B1=p9{L+PU$@FEJt5)Uv?a$gLG z-PA|H{nrqg=Oyc~rP;Mhdkut4-KcXuEbMh0YG{HLft|6-7v>jfA8=pM6%C`7z(IKG z2~BxsW#5=)iiG;99wwSyaRg+&hn?wX$u>7(a=?uRuKkkqK0zs2Z54G!vsik`TMZZzfrf=o!a zMS(CXQ0J${fQBCifcdh=2X=p>qeaStK$7ZHg?#C*dt!wqqqk729Fxb?I5>mni+% zJun~tlkP|P?~5_WaRBo{*h;?k`c8B|P!6U+TQeKgwEQPzf;5TfWq9(nf+ zImB*1l(JW2X+FW$!2(NUOdTzsrp)!|FuktvVeYi|2o=e1&X)1QTLDM8{~X+sQ1}Y9 z9ix~&<oC1w-j z0Gt8HFR@V04YZE6Lau*B*~4&{Q=TD2+Z-}gWCK2br*Nb;pU6D>wmrF^%q3*O3mz^) zlTr`NbC-ezbx^b0-EMAz*obg!kBkyvW?WDbtRI>%d%csxwZ<{|;bjyx0np0ju{ouq znFNj{iiD7dS-@35m!{m-sRvc6S$+WirfpCLg@NjOD8}yk&{DuOqp)8}25L?|z;&;8adL$#@SQA-$&iKJ}qoz#K)k z?Nd;sw#p_tV(Hgh7(timp29pZI_e@<*qu(_4_>v}U?tk-qI_k)snfSRo|hbVm3k^; zwbN@NwKVD4H^JC0KdI6pW5qfYomWrRj2HKr6vIRQTM%80eNb}oEw$RSx_P`l6x+0JnCo<33Xkwx2 zjs63uKspW99s*ftYt|&Lgs9bi)h`TwYV91}Qi*+#&lJ3Q8q~d3*h$o#)&ZxNcT!oV zlJ)npU%SOOOq)y!aa>A54(=8)|M{C`g3SK8Z8pSn2}lMx(LY)mw7mGd@!XbFe|JHQ z$|7^tVoInj+^As=Iq47cRYddP5l<^VT$D?Q@%eK;WO$g@2p;=hM5-ICYFdX5BF!h; z2|d?p2N(&c5c!CX6FuV{{)eH_8QWh|FaZ@i9VP#+;=@)6Rj$+YwBYK ztL~(YcUUaBcTRPG`{?*Chx_OZajLX z5S~glztU#)Yv)%j1{IfqtpV}V9<82W6x8BPqfbHAfSTX5(w|9A8xJyzYk@x=o3Jlm zQ7fXWb{$ADKK&qPEIL^uKzt|%7-df0q9fKCz5cBps6GfNhps8Beac51+4BD!ADqI( zfY9nDS zK9Wzlklb22Kk;TMLaz7i?`=AnuZ(`lHEwFlCN>IS&TAHbelf(Zs2copj@*7y*0Xrd zo&BWsj`&TZ+S%P;w+z0fBGc)_&eu?mK4jECHMpAu@^Bge0ZPdR;4#WqJ_0tyAEcC} zzdMbh)&rt%b4Ar>2f-8cf4OI ziPO!CC=`slY$~OkA~Jf+Pb&*+pF~*Cv$H`tMx_y&e0x*ZV6Qll9=uE(Sq#aGWH%4( ziRc!PW9O?b)=$&ZjzKBtxd_a9smn%`%_tbC#DuOCpRSuL`ucuAig9}gl^jicaSjyp ze$fnP#I?m9v+6L5;ZzpJ0iWQ2{gs@j+?9!sh0YWwU}k~cJN2dIBh8kd;Ggf*+e;RK zyNUyHs~K8|-Q5?w_n}aQB-9e61g?YCxC;lM<5D1WA=rUD`R-OqLQ8wH+fd|uSL{M| zNh`~CnwqP0{~t|P85hMH zt(TCLF6ojG36WG{0g>*O{sYn_(zSpfAl)Th3R2P_EZq&#-JonN66UGOeiZ?ALaf zxFh{C>R!YbjLwq?@s(tYyHmTqD}Vg57BCMsV# zE)?+GS`!33IF*4oDnP{P-F)2@LPAV^{8;QBfl84GhX$hdIl_0LM#i`2n=^kWfK8;0 zkJU~iAor_4WgARfM`YTA*4d@qw*KVAynLKK zl?F~Z`F{j+%Af^r2NR~yDB>K^>X#R-wKp~-U8?Tya7HQYL?Z=on7-trrC27uP+Kvg z-DLC!NyX;=%rldIQq}R6zy$EfDn#!*>U}3TWHd}to(q2cwok_mox?~Ay$V}Y-5h)x zIehg34;A_OmGT6ZkLVNc_`=A9(=|$D)^3j5g(xxV9vOIy0AYu(KQI*g>Ez@U@(jEI zQ&Uqhzm4*Pjhp&=zt8i4>c@T_%z;%1@e-;^eMh(oR3tEt&pj3;u=`?rC8H3>G2wn& zjr=Er;uhEGGB<;!G!qY#LCizF^~2yBf>`ncnA!xt-)i9LLttq2eTAQA&gbqtDo&q)Hwq+E?`!~1NzJ5 z+O3(tCu5QcXZPYh!1?*!D|jPW3C8;mJNS(|9-AW(R3BICM@Uy!-pJzc{pf|-a0NSm0yn$zo#0wHYRsv? z3qtu=46)o|7I*3=!_34b;R9yiK(z9iOQxft@omA^uNQBXR^zllhq7mC&2;Y~co`$_ zVKEQTYd1s8!7#{w)4Zvz{A21lq;X5)*7;ot-Q>}e?)MWfJ+pRW)i;Etow%LZAnIwU zk)FT9=G-R0j=a%IPM&4JM&lJod$mt7a681!-p2LTP_7+cWiHyQtFGcw`==fuHkVc4VmM4L}$ z z_e*Khu66dCXuas@C{(#E3p&2`iuZnn&qRjGiiQ0dtjcSEw~ht*;}-ba z_`GvgA$f#0=a^VhoPMhE)J3BKx)9pL~dUZG&NP zk6jxgC3%9KkMyHXrO&Fer6|Atez|V=UGO~AL&mH88%3a~_hTJh1#ec*__4oXK7GWR z^^wz$Qcs*J>B6=A$q63QFM%ODCg4X16Sworz~ccGzNz8?`|#5CWHfB)_1jv2 zPyK8;{D1(m{t_a&NYRwecdDXX{3SKSjxxHC`ra8)yKwi%%cF< zzsg}`YJ=U^iN&il2{hz&A5IxVTKHpyqruFJSZw9$*(z^!;wkMTLd=*Qfv=UMsJGcF zn1J2KW@{}#li~HbKXu#u<=Mk?Q1&FI@34Y?sd#{NN@$PMiY_gWTO!14cEF(jd_McK zqMb)$lamNTiZzjh*djIStER-s7drsPOH-;i>=Om`9V; zfeZ$SHNnQ@zdmFjY1uL8Db(f1kE3jTZCy6{&Tz9QtDp$itC8I2Hc z#z+!C6RNcky3Xn8DB;|`8|opnFpKbFyF6MfQ}Ius@SlrHgr=w z<3r%)V16E@I=Zga)5e4{2Y zWC#O=j1)K+HH%qTo1Q??ChY#p0J;EkrQT>Ab=Z)1?Z{|G#t}=OWu6a*+tkldhU2fE zFXS_wdI+ItKac(5`CWhWS5#={!$r^VLu=ZnHnBVN$1`&;Jdu9<&y=+OVA(Gmrk|Y< zSVuHGs6MH?m+@|v|#{sp0JaPVs_@m<6-Y3#~B^Anv-bP8lvsTs9b6`Dn^Zwjc_=I$ZOLf}xVWqoXbFX)J ziztdl0pUW7nx4%~;^G^FvA+r+1Kq(uk`shx2?d2-MbVZ@K=${BKhB-I9$23fg-D|6 z&*dO0L|Z4D89NDkag~eX=w0aD{RZSYIf}g4`no(vj6R_IPbMl*ed)>eja5U|1gq~7 z7gDg5i~IReItly~%R-^h%nT&#IBu_MtOxxSR-%>vr<495 zL#i8-qV8>z8!sdnO@|(ir^LefY6I4VPArB1^3uj21Mc%wLzTI7IBzt*k7&UKMSno1}Jv7lYt&bzxD^w*k`w< zE>>dFiH5#jxRlty?UU+Tjv%riRUN8Kcese$Ie)a6!Dx|FD4TY!( zM-CjDlsZ7`Y^dZiu?{#(AW45cm@>uGM&Op#Kkvw(gzQT#}U1sa`Y!aVJohdU|E|duY-(Wju&)+iL!; zI<@;HvxmKb{kwv%XP`D`Y9X#1tTK`S%4O=3b zCWd*ut(h?rA5MxUYW%jVKMP|lItVt@>!Z;9t|^tBs?Ovltsg_F(X`Oy&txrUJ6#wn3vgz`}2erY^7eL1P@*G^OCwpCPyUw za(H@8i4EkO)OX#$$(=`TAsMko|M+JovwTC(E*w=YeU_S2i&lf3iEsy3{_`+C(3a?6 z(w$_Azc=S0B$>p=U?35w7kY}!+Jrh4&3_e)7eXEVOa((!kaO&0)wz|{&qxUmG7(A* z!wVICanwxe#EHEha)#$V&pFu?^%%lPvMl^VpzX58=S@ddxVL-4C|JgYyJ_mk^9#X? zb!|fdLp>8eL#OKPD%C%-->n7d^6iWU@5_dM#5lPY5Qbrknt#K>H11eGcxDoe3!I(i z3Zq=qnqhN4g-|h1l-oZu|9n_;zIeGcn?erJA+shetKJv+pGD6YY*(<27UC5LZm4b-jD%^lB03~26QB}-?$l`wFkrjP6s!jbhgF8V-sxGC*` zOwx&=@m6@o*rSH2WTHw@-vM>giC0m^q!E(Up^?ns>goPmTs{y6QY=L0_E5M%H<>W` z0~vccj2;Gt<~Sqfn=^gG+SL#u-N1A>RQM;b!bvs9Yi8W~tLeNKv{kv#mb=T1I0mR| zp!J%V*NLdlO>C0fJ?v{6!iyETW^;Ww<&RPa`+Q2L9Y7s5QACr{1YsF!ebPrwsflc- z($KFjbkb=gbaNEl;ykHTIZ4l)XMw7<249dV9oKa$^%e{&K|+*_aM;b*^`{eP0HRP_ z#=zi~&19A>b8%svZp25bS$5FvIrCO=KyEZf5pwu*cRK|BRG07o(K*<@(?zGHuXOSyrKZ6ikk0cS3eW2lUU|wi-US}vrR^ZQtj26`wtd6 zAta`RuM5b3V*>V&zM>%)yx@c=9Xry)*%kCo);ZD{xev_f3{lA)M_-6!OUkkBKJ`J> zOmG2q1jr{!^a?)Cktqpt_fQvpwYRbMe(*ket-ljKIqw04K^8n+Eqj3O`_-B(fNTxb zNZ+x=^9<4dUz2>FyYj;u+y;lMTim4h_WH(ww(*eI3j!Z_~}Ic7*FDeo~^N z8h<;wBh3K@I3&Prm_kNuNUPDe*V{Bfy!IA|aTg`2O)jrMa$AT8F-}bAaOB9hk0kFt z^k;ADek>#8ij*XDvHHt~j3WU?%_K57PUzwPo-7GpyBS*%Q(tZHsCk8e-JQzI(aeJh zpR>(1=RW_OZ(ke%qrFj8(5fTIUk^HUhs&Q^jrmI%lX8=C?EO^>eJ2&~ufssD^}0_p zz8eykyMj3ZCPY2HZ1yD^^6nK&ndQZH?9w*zPkc)63c>$=+wQqrPsNJpbQR+K>iP4qg`*wbat9pvcqro?{LhMM^lTCecH_PNK zy)>~q-G~(pr2bx>QF0r`UCk72(Say?aNBx^#{2BaBd$ax{&i=~Lo_KG{lrt~OBASe z^Tcl&TRd-jL6df@KIU@*s`trKz5mi|?@|MA0}kLh47hngd68=k9+PTgh-={Zh$AL* zdI|(aA6P5609gj+_)HnfV5{2o5Ahn(o;?!rag3sj9M#g_rO7{vQQ?nd)TYeWEyU!# zcX+WpjZd19veuqnxOR&KMwrJvwH~K}bu3iY#p=yfjLY4Hp;0WXwP!8HXqAo%#Z!7;+0zJ& zXYjzC?G(`ZX1x&IGQ!!J7tf`IXjIB)yV2K*2Bm%pT= z?4BOln_UFmz39|{M=07ZR@(n$6?WZUiaDzxn>NCRQ`J z=V>DKsb+tpIabH}#jt(FKj_KccK|$CNbGwY#&pJB-#A0wQ zXtlH;Qc+pYA{hM+hZ8LOWYM5H=snHyjjQRxW_T*K&QgJY-OGQ6ceq}v*P5V)*^!!W zBh`gYxN|-{(|$C;$*RwBAbWf^gw+}C2*?q9Wy(6jGu$8}OjmML)GU!e?F2sMFy{+P zbk%urn2uXZM+3x`+*7M=Pnu%+C2p7)&ZzU6Z38BX zTwD7U0KNBHZKMM{BjG6fmJjP|zAY}`RNm6p-|i*XspOD>$y&ej@*3|^<%BxM%l*v^ zB$B$DL)Bna%8}AGc|X0AI~K}E;|#zJV(-Zk6lfqs_*}Ep55wM1ghYz0uRjPFAWdA_ zQZ18vA>yrXv;v&TQ;;;+z<}#>HG2H$cTM35_w<872r9#kms(nPjJ~W)Fb{_psSk!r zXMAIwQY^28WY~hNc!u?+#1cEvR3SbVZ~jqTz@41GGiw75lz;1qfpL1F0OpQwh{mjA zvD%X(#5L@`^CmiTdKFZT6GASUoTkYQgPzY6i&R#sLnk1%x;tWdQxM3Go%?$0N(ywV zo0j!uIt3)*He5N}GgcE0k>(UjpD5SVUa^8D6PI&oo=nAJLyD4y{vPO$=AlX-Sr(D5 zUv$4U=&5mmpDjHO`3jjMv6&rqJQcn0`TU58S|c58p`La~Ov`LJ=AwLT&EygrlIRnx zwP-wD!UO-zB@N-{*9n_X)!%KnV5eF^!#(fM+9nIGb>P<39V#zyfSLT;M-&?ra98bv$_e$`-`(eKB()?FW317|;Mu0mJ(XSius| zXT8sEqy0WgmePY3tYcz+FNT^Kp9Azf&t@TbhdQ) zZ0(ru=Y#qMtOpbXr-o=u)lQdZ+JAb?lZ}Xxrzg|D`d5^k-$V%7yKMBrc3A0C z=IF%RsIGJ+ZR^Q^kn0|XxlF$6%+g@PH<#_idj9O-C?9OeSljcVYJq6R$!9?gxzvVB z-VWx;kxNHu>U#i4V=}tX)1V3F1#*PGKhBFiv!#^1xD>BU;f;K;$^}1A@ogFSO3?a@ z7fd(h+v8>$5`8?Mk9w`VAt1;Mx7HO})1FHCZ`sU#jGF~@g?qEPGgKhao6RIn6bn-= z^W{45Y7FG5#{^`Yj2DSR*kq13{#&XX%s4d*2S8s}%OgArp2`$F41?y*B6bfvyOr`t$GN6-g28BrZIJpE3mA$y;@3)q4m z3Qg=WBKM=Zd4-SI18#J6%C^MPyS|k-dJM7E4{+I_&o4H61VhRAU|}>E&t2w5R-pB6 zAclUB{z#-GkyqajACLR|(QJ%R0?7mcB>8sqvuAnZI2>qEzZuT{^stKGKXbFWpLv0q8} z%1;7XG2a2op4Es%`deX)Z(^hLUV9%uYCH&%;Z{8mbmb{jGmn%mepn8#okMH652WQH z<9Y>z9PKt{)?@w*W3o9ZWuhmm#p(IDlAi$$&ncqNoubAJrpTt2v_!97mAPlBxGwJj{Rtt{|W*3 zaKLYPm>dGboFx#2{CCYLerGL;I$D-kJmxESaN#RsO(usRh5{bmURDL(Z=m)v|MCHl zt{LcFW`@4NHbok_*`31Z*ht`L6?%Wv^tpN{75Zh`D*A#sMigAFI^0aiH}DIBYtHEG zMJ-=|2lJ@@il+E#bJz2l&NbtjG1v9VXJTC*Nv(KX`C5;}n~_L2pxM;n|3FvpsO@)t z+>FUnv+pessWZM4?LhX!?`3OdZ|DtwlZC=oL(4WG6MRMpBO(sew8;myb|%AZrBE$nZc3NMYbWoemC zJJ6Gv9K}#*eo{1B`pn;5>(fJZ17x$Xo^Dg16Nui7tCx2{eQ&<^!<0*3YGp22BLVYa zTDglVu=^XQ2TuQZ%oKSq`xoS;b}|J0)N`=BNIXMYxpgbW8gdkZZ?L1>{%~5pMAS}e zvyn{fxo5X{j>uK3qQ-Synti*!c+ma$obyFfcwc#4@9(uQilWNQPd@}d+h!=JJK@AP z%|LwF$S45{!ze+pYLg*QD)^NaW8r5y0$as!fM~<9rnS1@AXEYa&}8{4X8s5;S1H6Z zm(7;aP+a_WuC-%VRZjzalOI^Y&wNW-{G#}fG&emA;z%shcN}pRMSn_cE!%07jBlOorsGbFrL z#J0=)0$JguhA8JegzW}-tG1$bzT`m5waVnnH`TuU`NKhql!PSAMR}+cqc9PmoF<(c z`DHJnba8%OeD9fk+`Ihba#FB9br3@_7w>-jN|&*l7gBoG{+X+nAJR)(#D~sMz1xYvWnp}3o+X3qw*tXhoA=E% zndcQ4=ew7mx!7Fiiuu-10-nz*9M=*=Hn*Uoz;`gj#mW5Mm1Fe^7c(-1a_c-V)ine; z@KK)cn&mGxo(~Z#uP5b3+WmOvOoWO9;wgb6epI?f>f8PT*G3&nH!_~5$ekq;U*S$C&qul#1|y5u9XI0U2A z+j_B=!hysDZGtynHKtf~BADbo@PDF`>mdZmT}f#1UyzF0i=q^MPOr}xFW5fc7F4-F z9n)VAUMN+7ZJ_vQW!&I0vsSCysEb@Ru&9SpcLJ|xAnAg=WmZo9{3{tFZE|SWjzc(h*{`f`VpZbKU#=GRBv~JCS?f873#(MJ26F$3GK~K6Z$2BXFrqsDoYf-S)KC%g zFVYT|%e>t|T`(ZC+JiWkoLGcWu<9LxR!WZ&Fk{*72t_d5uoTo{TZSN2c1OtLHc>Gh zK#n9bUqP=j!{@j258-ia9O2A6zZMca6^5p-oQfQ-gwiFx7q?G(u45Q!C-xVCPr`%=>-KnTBO9P!xKAZw}QJ31HAb7Z7;Dnz^IY8XZakOi+z4Ib*^s&U1w16+rc!1qwK9T#~ZTlXXwcJBm6#W zGz8iEW~T2@?Ibb%N~8UeroOHf=&Zn3rswe-yHEpoy6RYSlV`><|E@(4^3i|wqhLKN zpG-*6)#b}al?TLG(c@mT?=aG7!!FTQ?CXk7_!H4d4V#jZVe$G%R>%}LcW&rY0~Y89 zf42>>XkZ{CZ0}<95`sE9U$!-9%|2ury*p&W#Gkk6B{PUGj@W#3SdG$MIAnsCc;eC!>_YZ~XC#8B^6iKn+6E6|`+0O^x z^g6jKmJ!1;>9TlgTRUfgiWk1xedlI-HF!9iu7ogmop( zI1;gJcJf1DdtbU78uwqISZiT9GfmbdCyRgk7=JpQ7)8UTsn8=3un#Cc66-qff9({e zi~k+;{BnaxVv;K;58;^fn@4g@DrX$WNYLs&cq)9t`L-&{ecNk1Q-t8dX`q<$lv{mT ze4yaUgFr6(@FUdpd9i6v;+`W?l8+GeAh-flDN(Y4^DW9rUw&YoT7_XG-4OJ{zY!F~ z6EC1=^(~~xFR=t)+_dj4s%=6g@2PGw1y*)g)`;rb5RFl>vm)&q1cEz5@01+S5skDK zhX#sh9p=Jpr~KFVxGsc{hx-Rk{CCGo9+^0nBL2I`PAR;nV>d}FO1z6Qw*6qqtX5H+4a03l!1dZxvqgx9P^j=M@^!LCPQP65o2-m65XLfKW zye7k<#)GUASybK+rya;D;BqGy64W<%;Na8dJo)I88DGTx_Xo(mKQi58T-VrsHo79F z7P>x719azGEYBFPp4voYg2S<;1nO0h^5WS>%AF5gkMpV7>dL?WIzj-Qg)aZbw*@f@ z2(D#xvv|dwz>-mFAcdG~Q2NBa+%3KnIP^4tzh`Ko1tB&>)%iz?BfB^tP0ROSHSrd)Lm+V1JAaQDRb{RJ~A-y}lgMu>NEg68GOK4T|PJs0LM)5s~C) zD+}~#H;e$mKc51%TE=}va&*m~=hp_y_Tyg2EMH>f;*&E1s2+RO0=4jW>n8ChRi7+y zl#IRr(pd*Cjy!yGr)KzAhHz3G3SFOP*`+m3oq+5-+$NtYxW}`0(mJu)f&O0;K7~La zPvf1`vx+8!h?CCEzu-hAiD>)t;Gk9qjfI(th$^dRr!(RQqq+COy16 z_3z-%vbEbBo)Y-OhG`S@MXxM_jySvv1ZhyWJIuJvX#0rugWthOZJukxU|3B9NjGT? z7ZmkI>wbT(W7@r`K+T2X-|c-kjw#V{n42{Vov;Q|VaT<%w%locbwC{*u)95x0wcFK z295f6xsrcP2VdMMMJn8!Jg06lLks+qa$bf~hqPCh=_kYPVOt{)h{4z`x!oo@V$7pW z8qN{vVqJe!9^(Yny7zBwf%Dt002|Yq)d#YOJ#v{|&SwM98^?NAN5SdNEYjO5(D7C} zD7Z`=m-htW6%BFsZ0YLCi;8o`uj{txuyibj!;FoJ$A2{#=e{3Ld@Ym`BG7q8j7*CC zH2-{u*(-QY|J>5q!l*A@J%5eVvnv6Y4UCNSsYciC>ruldN41V#b-Y% zYBzLqgANV)zhW=EE4F;{u;yF7Ov9(_k$(2wLLbp}mQMA1ie*jNe$0<b)&WDvtDdqh44oX(e|VCoV{qnjI3gZlPmRhRsiUYAPQQ0Aqy{@G zZ#mXJa3Tf1E5IV9nJU90@UV%AKVF^2v2Q|YnU~Xhczxb%uDL-c;2>D`tWvI3{U=H# z30C@GjQQCR&lu`!AaO0}&uVkB@w}?e*ZaAViPVwHdVlc_`7{$-!28>b6UW7L$6U!&QXr+@3)V@1=* zZ6E=JEU;B!c#=NENbDPSDybPr`buX~ay1!k^uMp!4+=&Dw2CMOcK;x?^aU*0fix}^ z$;us;TTmW=zTlr(KOgIb6J2j7k)!9x8mvFr#ueA)`GKsM>k8Qgq_+N0w}<;A53v&_ zhiPyIjXsh+^6Pa1xhDU#$ZM1?Pr~p!>$I|&i=-kS;hpZ> z)N6;r_XvH5=RI;HIGQ@ttdI7s43Oc^g3Hq`BfJY=<7S2bFT{h9ST!GwD)gB*AF0-c*Q$+ql|40vMGiQH{5--9ySZkxYUajX+Bus47 zAo<=iHwOo`q!5$&?_{wVVGKVU3UlknR0d1xo-=BU;ao0BuNdPA@cp@@LSF?sgRJ}VZ0v;|A1 z)Jx2CynTTR&>yt$i!h0p!qU{_MylJRy45}D81YeX_>RZYgOpJeAbJmr7AO|NMK=0| zZDanEJnlO!b0%v@3)v~!bqh|7Br(t?)Mbo_m^B#U z5L>@24fr-5bjoNKRpw0YdEPAveHQB*nUUJ~qm`cY1#S89x-H@+_nEm})Ax&W`cLfM zGwaO~h^p^ArkDW~X@6`jdXXN-fD zdZ{&B*xhw>Q47n%g2;j_$FZSz(RIo1`W^SEvYfwF2X$Sr1&=S!^Bt|^Si zG?R+dcvlTh)deF*-QXVR@38SWG!j@8(E`to;AHFfaqTEr8bAa1oLu?Bk6dn{fygCQ zR}+IIdWUAuE_Vb^++YY>4?xQAqy{abocDNVoKu(T_FvmTm8-EXINbX_>Bw zz#&$O3^JY{j_yjd%!}^}ocL>QLKf<}ym(RcO6ZR|hU$iXiT-~tz%zrWF1sd*bQG{ zf-KkNc_83^;Tyx8{Q@lHB)}=Mfji#T8iZ2w>~5z-VEf*O%jm_ARVTbIy-Xdz@{L}M zjQFR3kyhf`=ZpACS-%K#X0CZg3RwQ4o;7sgO5w)Oat;Qe+<=AE&>=w5lNlX_8z^3B zSm?~W=UNrlGNKwBZyP`Rtzq5luOxJXYB++#&wUYH;BCEwGRZ(s<5LzoOU{`%wX+^ z@4Eg+5$Z3tUSl_z&Sb;DLNV=VQBj!E#y5D8P=|RT(erwV=UV`8;ecBs*XAqkSMcW( z+UcH|oZ{zfXtRhpWQ+pddl@3PcsIWNr*fW$Uv!*$;MlnqPP*AU))9t&^`0?QKoMyT zRa2MH^@%-MC-F2&{j^yUG%ai6*X;tma69Mcj0KL*P_*|lL8TYLYfZ>KqrPeP(tqkX zbB+|SjcI?qZNhFP<99p`d!x!2rs zCo2U;o~sFCAqKD6w2>dZiGlpH#{^GlSf5tp;Z>!|lE4hY+Cz|jC~}y0yBUYOe%xhd zD~k(GO&D!<#F0U5t+$pSsEma?pT^T&Ow0QAi-pY$-ikoS_}6%m4!+kmow#A7CZg(O znVY#k=+njTXckHaAK z`Njg%VMZ{Gz z+R~n_bx@%%XTHB9a-2 zJv*oF_KLZPi_^DVl4oyvGNZ0>2mKk>srvgi2_jwoz8jR&?|6~OFZU#rnr}YZ(NO>q zO_xc=Pjn}@R#yA6w#-k?L}EqYHj?mdc`4s--FF$eBSU35KTy_dEc56IVe*s@y!5E6 zUN8PBz74hn|9g(DR=dV8C(SN3>7O90i5-(!1tRDll26U9GEc8QU3_9KOYMShWK(^5 zZB^c@VY#u=5$G>c|H|}XdcLIYsqcgL39UZ5`0?C0JO;A}2xoD9WUUlLdmElehW{C6 z#3t^m%2i0}F?cF@Hh6iUxp6c_VZ!iL&+t~nMPobf@7pWthw{C{4$pV)96*UzTxjt3 z=S)z<=e&m7E`zA-V${j3SG$%d1{8BF2>*7R8Bbg5mM_^i4y%rS2(AbWALeFA(FOgO z^-w$>`Fe!=8lypChVVc>#ChuoxbEl=o5DfsdXBHSPB1sawm;`F(tSGOr-N zK~^~WIZn>&W7{2vp!eTcmn6X$A|civigA~~(c$EvJ(?nX<2l;GFx1gk8Cx*o>zTA( z5@Qx`;uB(gd&M2I71f)x30-cxZi|swSi^Z$tKvGLardP4&Qmmap+&iGr@2ql&8Q6E z^`RJu%?^INv$n$2u^huV^c)DoyL}Yd9=p9(39un^aeL4PUVICxd$NCBru;|i*eFd@ zuJ?*;OT7$0<{tQEg(>3+8k%Wf=wO^@vV3DRfmfIHl)RtE&R6IL+%oj``J$|*BOMdg z*MgSb7QyciB#t(FE+weWt0I-S_4B`1r)%ADFJfXTmr(QQ7=Cm}l|u3S&O(@WXilPc z*m7D&omXL}*K@u1ffm_D*uStu|F4&Qk}8hvGIJ5)@rSLfNEf zIvPR<&}G|+r!#o4Qfi6)e{lY6Pp{m_1{EWBTUYyL`Y6rf$cwH}V=!;U*^{w0*b4$sMLlKFYOBa_^Zd}JS`!7wNaD>czG zc0*uXZn=7>4Fbi7?xFvliw8Tfc*T}0ddAE5dQC@txiwQm!C>FCzSQNaWd|<-*Nx=| zJ8NgYd5mc~s8&R_d>9estnmQ!D6virPn4|X7cq9n3X>Eqtxag+Xu~0GFEwV9$-Uj$ z_beDqD+8u)iv62M!qGR>6pS+|w!3m69uuzMPbOTs4U0B}OY^RP7V8xZ|?-*ega zQJHAh898xvd1KBb^6^%ES~t6LYBHP{n@O1|rq7<>^{?NRY?dAx`MX%pe*DfF{>=!S zOnoAoHhbvLSOQ;bOfMl=cyb_~2bpRJ6fEtM=;ygq(6qq7J1~5JrDq6a4?%OvOT)Zl zs7N#T`!={KqmXET@O zOP0B9iG7YRdpogM;{skpLht@^X%%OZ$YU_yTH&30xt2HWmJR z=_+boMAfV@yIw&HEm~+oPct6{OwW5vVw4Cn-~0*QiCIStY@KB&OOcX+mVNK~N&OSdIR(3&d!!FftD`*sKdD_{vSTo3X zlihOb0=uC5wC(Y#K^X5qu=14A#u@)W9jVbVyR%mPJ^4&c%yO!#A+q;K+w|T@6#V0# zUq8-IoU86mW+lh8?vOt^%9qaZ`jTJv6wLBZ^tbV^%}v_NtPXUnRkko-x-$Pesp!BG zZUhRniOzF@hXf+yS)C~f$VR{XP(UDD6jC|+TDI>~y_Ky;LhPs(H8$F!OyNQcDB;a_ zg>H{gf}ZrFn@akYzZTtS^y3v^clkjT1F+IQ_MFhP!G@F|_q)N?Y)Lj{>+w;o3Gx-n z2gd1WLS=%9nJSggq6$2f?*Jg^*=C%-mLrQThl!W2Gy#46?g6eD5?E!pksS4|bqie4 zH%_++5j8VqQjAQ;dJwCTU?nqizNYr?Ux$zLNFbooW(l|bb|N)BZ)uH>RZ!FyNgE^L zB?^P|&7WR+JxJ288SgFky`v8EY=UU&X3_47!s&(shpX2LjDZ?{=oL${J)ANiiuC)-`;UP% zH4jd*kf96edW>$>53wSanxHi(2gYj#?Qy+r#wWc#DEzcvgT|PoMMUTQok&v|)eTcZ ztkL(N?T=ATTl(Iw;KS z#0U}7$a4pqY_Jt2RDVHVI@c#})oUyrXN_5$2s`GuHq)*ug7YTkPLNcZ18Ob`Cw2$B zr_E8@yCq7te^Mq_=H+Xv#wo?l-jc#hq7Mn$oAO_wB+QxD%?&mpQx-~M335YFnOvPq zPxs^}1Ww`8Q#r`;+=c`p46}#8MZ?X99n9NO2?Y<4?6mM~LQlDw zH-)S;J7Mzk22{U@4!5p33Z@^6XW5+U1b665wM1x3W?D$s4^dLA;^{qVfpTFPb<+h; z7;3vX_lELh z0Z|1+nbVsz$#@cd>G6$uE1?)9Eem^fmRHxmyQ0~IZ1dwtiD8ijSpAJms;HrhC9d9` zpv8>uw3UXcJT~9SeQ9lF{kX=pJvWhXm}r4b4*qz2%9o!<*ho#-rcXBH6>9Vwy;a$Q zz<1$7X=$2B`P7^p0>$4Ht#U0V|K;C5KA0=V@nN0k_^;ob`3lM(px$Trs4cwSS3nc> zU9X6~K>$^&Jd5%o`T%jG!Cd#Z(C$bSx$Y@qw){?Q?y;LwGrkNjkeUb(2}*%!YYb(E z$ux@|mCqnh-DJW3)Wjl3sOmCsNI6S7Z9wl6AbA+a`(LiJNBEN# z^=#~u>N%1!{h&5xH;hyv8%obQ^6mwya%y%iAF3yMR|9WcE+Rz_UTG_DnE% z`MTYuHaXH}C^0W2Fvtz#@n1?nnZ>{}>ivDn&M5oq9Cn1N2fBj=nMf*ZIuebN!LfxW zW~eqr@hhfyRfkU7kE$-xgv-iDv=aQtsPlT)82S_{JK`T~OJ&#&Ueal@wRt_$98BS?jnQW4|QH@83%{&dR36 zd3!a_QFvmg>(;I6ZWg?G zcovdLO=V2%Y(39f<{xlnHg@#AbZ#~%e3|DV%SGWK_04}M@MVj(rtI-pIcc6tKGfNY z&VKsrP1M}STC~3VvFMlh9!+hqDXECuz_;OrsHbwyj%ByA2fY0>jJOXX_M%UI*K-Jwx&B@GZALSVdX_ov5=`_S zQ=BMd;fz%N886$xjq)woY1236R*DYw6Y#$6vvI`8kGtWP>BDrk{B9SSR*qAZbg4!4Qf$I{+%+Uhwe>&QPCAhCX`wqF@wJN7}CzRWP z!5Ljv%@_vHf0iwP3}C?f7H;um5??sT@DEF|W}O2N*YN;6h!F98UBIv7rd`W76sisD zrIQc1e}CLkb|9J*(d2pakGo}|^y8d+ne;>Ezi}%zKojX~W^G@u-jwUqd#*$HgDrtX zpmdSjJz>Qpe1z#U=<+qBIKXp_&&4WM08Rvh*R>oj+UUeSC0=me!^>Bo-4dX=S}a=N z`SxMZA0!0{>3ca6Nf0zAWm@B&={VOf9zrwt5A9nmM%J@;!*s2tL*HP)>MF~P-ygl< zTj7@_I236y#cg4;-DoSZx1VxeuCPII3lZVQmC4xaRjf}~%>ewxC~3Y#JR_y)J>_#h z5n1FtINBfk`_Lr5@!OShKe)K60PZhE=GU1YY>pHL^Yz}BYIQsPgsLeUZjbJAqO5e( zcKaLc8;eP#847B${rHw>L;Fh2<#oW2mMXtdJE>N5MsJ|(QB9ar!na?~m5X+>v}L7# za(@S3YO1i-YFV8TCn>g3p4hhrosq}s2By{)!5oX|LXUnpc83Qv(n2NVIw%OWi(igV+Be>k921+A#F4&)tlOAUiJiT%uwYBX0FDh@6Nf9#Ld5kjY(L*1(` z2p*M&1Zuk;OG3sMWW8i+%zW4Gq6w+jh(~+)xwf%tD;IsoI`;yi-o(c~lrdW#zuarX zB{3-J+}((~{Lcdc!?GuBe?0Z~aJXsjeXV(TdfAEpu7`{@B(&#!$G?2!^j5wlZPCqD zzaITS*obp1#_8%leZ;G*6hMeY>M8N|hLM2XTE~@lo_GJ&bI7!1zR4jPowMb7^o2N3 zuovB5q;RS6Tezq3yYxk+u;tj3qVn=g`PHS*pnHw3bZvOy4Br!ii5!<*$3}E{#7EwK?$U$=W7~jYQT&1%7jg6Ns397YDHYktTmRDSxu}_-{f-XMo<8>bwkgGn45d! zNJfN%=Q8QpL^EHVqv^S~-qZV=gUuHxxO)CA{8vZj{(x@RQ*Ym!L1^_6zN`b28Ho41hTyK^>o$2|%S1 z?%s4ZerCIWaH0L(@FceeiYP!sjIb(jMEtp{`dR)Q7ogObi~PC}-5_1r4}9pst*!Z> z`7O)6kfF<0>a`HIgJb8HamvB`R$$ib<9C~{?4ywghOv>K>5OXn)3kRI7{qa71 zVgDp{UxOvu+-7?ZqX=qnOFiAI5)k^~L5_)R1bl=(v1M3Q<#3M#Ao{}Ub6c?kT!EP5 z$q+O6Wx%=LKVK_@}4l#Vtx|bU>6J!gI`c& z6cc4+Fi*$QPzQ#vh8{nDoou`KKC}Lkc~kUKh^G-U=|>H03*ZKw7H1iU?0th?bC$^EM5*h zw(QE?Hvd&u`nhcav@sfU15xU9xN&(8uJ!~r8q2aA<;v?!>9R9U1ZMD&I~i&hSbX>f zBdT2nBlB)NDpFz|Os;bvBEmxB1o!bjTOjfpB?cffwIX(k)yK>Dd2`J|6G>@fnb+HF zZDug(8f?$_vZv6$Zdir>jQ!!V%cxRf4TkzhM%+<&Fd|H1u~-K(Fn<)W5@cN0S~x`? zrztWuJyO|?FzWpF;w{x1RZbH2M(Jd4bHBe}$bAjTFRz1-I{A#0J%Atjw~_dn}%T)A?)1QJo!jPczcT340mdU&xLbZMMvNzoi*pz z?@EaXI`(stZd_Bm?6huo`{T9!lfnRPG?A|1*uC@&I$wzdN~`c83BKv}uoUX&ulk+c zLzv;N!~(8`(h$7};{GkntY`h`Ll6?_65oAyGYfQCU8=&?3ievsn_06b^@8~244{c?gGbkTvV|)^m{_WY8U)EZp{Iqfl5mN%9yq^;N@UCc*4-h%wM~Bn z#iu(`{)zHj6UA~t(tRcgamO$m?PaT3$aE?S@w}Ex-*yDvFDx12Jp%tM;@_3n2NWIy znbzg7>@J;G+eHzo4Fxg9KVKYT58GcCj|WMhGT-}cRNhU9_5VeS4SP?(y_jnw2!0LpEy8l!ewACU76IvR}5o?Q0{GKAMi(dJ;AB{Z?$o zc>04bQ!}|a;!7hEv#H~f8~xKss0`N@?p*q-l(k%aJ3;mj-inDmw*dFE^e8Hb&}1+D zn)c3F!mCNV$@uqhMh@5^#6z)W*0yw|wlWCae9sz2H+0M{*D2Ca|bylX1%tSU{L*zR$)P=bc zZ6j;#tbafqG17(|Cj%NScwWM{D{REli9w$^AI2Q3LRn$7w-;WLNT(5hq=Mh*Z3`sSX&qIOFl&Guiz^*NQIz91DBF})s|36S35R{a=B5Gp z4^UiUWf}oR*Wk0 zdWBJ>$v3-zuDCzYwys~lF`YoDFbd3joByvDAdZ;Qy=qvZ#asW4{1Q^G_%aG5J^PLE z2Cp<@4%yE_cJZtW8Lvzg_I^8RW#aRea5d6@KVic~;Od!wFb`A96VwZ_dXBgI`cv6C z(|Vx~AGE%6<^R-$39Hn=ET)|Smjcsu>?}nz{<2UTgM_5Sz7DqWcAZ{s65gjop|GPq zn2PuQ(HH*lD+v(8yv?@f4}sLhT+8=}nXgvg=XMOZftr6srtSm^1ZpzhqCxCf@i~Od zO=J5c8jhM@XlfSETN@_`Pe6Sj%y;j&yBdM|^WE$0X>?4k)v$ON|Jccg%I5USYW~rLaP}HVpeL@$krv0eZskmIV>>c7nvi4Y&8mZ_ju2OLlJPH`f+%mmz-+mwz;^YkES${^B? zUtpUMlDX`HvaZNL&8w`4b5nB)NinXtW$6P=rOZCE&Zt|_bW#m&2#5l#3NDBFn^~0W zD-05stodE^A~oNaF88a(#r7S;%{P7dU41#GeSs;-c3yaI<(@Vib)(qal~hyW?ri}S zCa&~IuXfaP8DFIqo_Ef1csnKh7~CmdUUqTZW8tmf7#{5U|9K`$XDzR>)#NCw+WV8gdyL()Wil{t%M} zN==KXZ{tiWG{%!#GkfN(+8Qb=@189+xmbsO99|{^5p6<#nVFLK&B<*)^bgv@g1gK^ zl!2F-X}aKo%mbBi0U^$(jKj9x$PP?{dqzk!fO!0K)eO*sSi3om7|Ms{&n!AQs1a@|FZ^m{ideYJ15p^nx53IkV>s69NA;YH6nT`NYUmVJZ-_wLX6tj#W> ziyy!vF7X2FfJr*bZvm)%`i9}gw3Uzz~)feJx0YKYqRBiD~gM}ydNu8B?2K^ zjLu**L?=)L0x$0(exQ*QwG{((MZUIOj`IiX*AFN!86_iMzEt#=a}x==Kcuy!wG2Mg zO1~(`aQe_{7dVxqMR$09{~XoTSPA+axQmGVMdmG(&71*xvXz0hYcs;!kVf&R#!cSz znn7>|s&v{r5W*_BB3a1#EY@=Iz7L%TEx5Ktwcuq~*QnIS0l66#0JTspnG|rBgVIHo z^DX=ORrB`5W9enkBN;TT$D{NUy`}8@n=@uBAK&gMXvgbUz%L?r2NK5A?TMe13kvG~ zdzf&}=VYLJCw=Ovbftp~ZN|k~rM}b+S!EemY<@wX6kr$hX3Py~9A>4G#jP*)HDE=1 zYlsV9BT1K+4qEZdo&$H`G3CmHOmtfkSg26 z)Jq+av|J~mz=E_>b=Q#T(J6xPKg_iiLEhVhmh13~0W{8mTu-H}gV*1QNby!=c{xt@ zQviGLt9CT;z(Hst^bnD~HLZmT+ccmM&z+|{wdu73^BcqNxCf(d5bYv;?Zj#fCwyU# zkft%?T@N*q-~N^PeN?oS=UdOs%)Yl8bm?UBcW>b{WxL)Ck(mhDS&xxSZx+hFAt4+C z6@bRo1mPXUbL@E*0o|c}n=)DwCeyVw)>x=Q|2Dref9$yfI3TV*xg_|rcEDn|eRaWK zwx}{OZ-oukb<;}&IU7kdiONEX=)gssa@Wb26C8mPqBh4HVOgb8Ub-7fJx@ODPkZ2L zZFVrX_6(^&*H36$FB;pP*wHrI!M{$A6T~^Fu!L+K;s%mmaWIZ?A!>jV>R0{#N+@~s zH<>G2nHA&uapLu)=l_1z_N%^S*dhH^toSi(D>2^mT`}@LWX@ zD%m9yUE3^_3@QBYCL=Hfm;!q>yJ77ZkwHuXeSl+7xw;bXFH2fWPBLS6e<)-BSlug*8d*!&%++bliU0{tXI< ztVB7jEnbAStttHv^{1ITSJR1-P&P8q7S3Ge5J%cXNP*f3biLcX`+q`? zMWl|7%^jUQwffa%E4;UaPubg*&t~}z?junWkCR2=!KvoNJ#<7 zkZHS}wIh9qr&SIqm9TH?i!$?<8DK7cEdjQ~Ut1^bjqqR{jrLm~QyNepb0jivv5Hu{ zJhcd4N8r3xH#_MB(F6hgX7JN?w@F3b1OGeXIoTt*7J8F0??=N}DKSwF_cPngih#}P zDkP6R^<1v<6iiFwjY&&pB1};6wo_rPDq&1b<~L0n73**beIyU*_NwT=;@$44Z-?|i z0<8_heT9g>7nn>%3=P=)9e&(FKGKtd39mk&hbdaThao_EE7p%7;hh@3{-+yD>R_VP ze@dc?1Wt5gl3(F_yD*;i=bu|NZ#~WeJ*H{mCoIofB?b8)M~OGbpY&mpxJ0nu7)HfD zg$RIxLzY5U!onm*>oY&cp4$8*Hl8r5q`j!A%h8)lW#yhVe0KA;M9n=bm}nIq(384X zK34I)ByYp`?1uB?+oNP;x$zOy{2r|_urj`9R81^^LaG*5{>u|TCh0TSfMpunG%Mq_ z$Eom&4mVQ5q1PgF;HvWHrwHk{3rn`3eV@JJXEcj@Ys_ z;wJkkXEaCXyC8gWAh+b4pBaNFh96vFa>V0cb>g-+0}%Gw16ST>Zpi~DJ2rZ{{YT*b zxZt-8X*hA4D`v}Q>clwG30gTvOpMG#72|!qjy!gD~P}qs<+!^qL z2epaPt6Kx)(7@e=Yx%uY>UToh%G}BF@aTHHH!+>jO_-nVC-wonMyI{i(}lpzLAx~v zcDj6vT04lGGa9WvIu_!D5Oz2KF9rMJtiQOL%;i*iiO(}nWx-hULp3D$`S}61`5m7P zBdzY^ZCx+bVTT)N8CxJC@0~*4K?_a7&lFQUlHMU4AUFx_ZYNIn7U1Qpa^CnR>P-IY zNQ0GFGDnbh&Wf5JOQOBFXiaNxXunT1fZq7i$oRb9unFsX7XTY&$8`#p#RTcr4O7JL zXpogcZ=!;|!W9^AdsM|p&oIBpHRw|)sMSR9Sp%%t<$ML*d1F9E+BnBDO{U91(f~7SpfB&&X_QEqU|Bp`MfU4%D zP0kKCJA9%NwDhVsZOo!CUg1Lp4KxXM7nB6r=@W4I@QYX6IF~)rWbO!F3C+b!V}|3q zIVZX~{cur+0SkCk2=x4lYObUqiH*R|pXnMQjOy#1Qn#X{Y4Im(f#<9jdR!J*hwPs> z62_Yud~uR_!>FM7=1>%Cj?r%@Kc9M~&8#w))xFq5<$r&;zVBtvP|$apBevwt2HPI=<~O z5GxFv$V0MQKcZJXHUml!#r{NkW(yStvY9+dqZS;$4vw~G*R>Gth<-f|m6U3a!$cvC z;r~dK?y#?LY&bP0Q&jR8V8hZ}am+7qN1_xr`!SKGMxNbD?f*d{_ph6sNEU(LEvtO* zL!0j4@=J^n>YK8A8WjR5d|eG@R!%;qfD33!qnlGV@#qt-vBGnWM+xr``ul8%@)K%v z%I<(vh_gH(XMG7Al0oye;j>e2ZZJ&|{eU4Er20d2Rfk}+D8 zQqvzTCVlx>{BOQua4^-yE%q7HukAXJ9EdycsHc;fBbzKdBxL%o;>`W7CTd4u2z>{k zu`For)5>f~<5B#mkUhDd%VN+{|I0OBlFV1#Y*i0tLN@WLL31=aH9>}S`+j2 z{=RtO)v!uPwt^HpaVOjPhw{ld&^hyR{^!$%c zv^#+;c>_Lh-jOAr*O-2bLC|OuF zA)?Igy-~b><3fa-&a7@L+3g-{R+DF;)lJVA$Q@elxg)y&(0K5mqt0Cyzd3nV`W+^1 zoX<)Y2q*SxQaG z5Hm3xImuaZT5L|IF<4>siYEvrs#QrG`9hH=0q#r@sDYG0B|4RXH*c+KwWG0`ErBl6 z!QEDMytMtuJe;!55?rK9Q|V9rRyRL=KC5gV=BznaSm zJ>Q^pW!yGGCllJd)KjQ|t#xc6%Br$IG(B_&47lm*x~Vl_zAbLK;|L0BU6J`6Nz~B= z2g9%!HpR$1=RgRim9 zVoK7f5eR-U8hkaXdY4|bObnvGpcwaMS~^)3{2BSUP-_OWC~ehgfkb4$QW3|2yZH-( zWGlM39OabPS7;N}}fU1I4cjgX}76mOsw7@h4hB zA4`RqI(v>(*^Idxk$G5a`Mr93^@~66gRr&WF~+He6^JEG%@~iDwm#U$9ASzx!ga{! z5}dU@3cj61KEJ%i3+t^ zCV*wFEvhzIzVj`a3gd4#-?-&LXOX#?q1H$S$RouiGm?RWIA1k7Hwv`?)M)bj>aNw8 z#Q?c0%+4BqbBpzDCwPl}CDwo1{^1FK9Wjh(e&gZ=zXG(s;r<0IryUivL0ayBk4Ep0 zLBKad9ynu^$0QXi;sx%wcI6O*yUu++9X69;N5e>nd|WvWuECKFS1kMiN{R{DpO@{5 zf8zPHp}jGQiG;8aKzYItc62BqBGfAIwevSrw~IJ!!XlKA+=C8AZ!iE!pO==vU+nUH zUJP)~iyQC!CQs-4p$9;C@8R`5-H$H@_T=jrbZ?HIZ43}~PKiRq+&IVi67&7?bicX9 z^|a{w%-2C$qR?xvYt{-mJ`*&t&#!6EK0UY`fd+_+1nP$v7aKoQ!mQg-+ZIBK$Y!AAk2vAoXc9_cg~h_>Z9?#4N4EMW;Jo+nYcfS1^~6 z;-2J|Y((q)S(37+p(Uqra$@SkE@6HrmZr%0)S*KjSIF#^z3${JU*FDeH#SUi2|4^c zJ$HP`Y>T1*P3_O8@8=OTGl zfi6ez>2oeI9%x192;P?@Bo~x*7>ULOG^Fl_KoR27Ryb7s8Tf~iBKx@IZ)&M3CIf$4 zw-~28L~qfEVsU>bcQz!lu~hKzMNX{2!e=;rx7|!-QsbS5fnG^^UO4GrQZ)8yuf)%! zS7MuT6dE7St}krE7?EF(j)I_gd?cKZ4r~#CL8~Iq$H13L(_oQ~>o=h42*b!YsN2=1 ztCwr=1i5`(lBGNEbRGKp{6*310~Da{AAhKB|9#E()2HsR0xJCHB5k`2j~jImemnAU zF_Uo@E2pLy;>n^qz(J{GeC$k|ETCLaFS5Za0sJ$UfKe%&wa!^u;+4Fxf6h2Dm#o%elf-K^)+GA^&! z7rkC$ShmJ{U{NiX=di!L|6G?Br%q1l?AVK;Is{yrmGw72X1W@mO>yhfP|s%wd9)^i z2o7iF^6igk&c=>6+C$_Br z5wz#SOF`B%4$v2V{+mGnb99cuo6fc79#%|cq`5Ezra z7VKi+{WA?xozfRdu$=Cx; zsPbU$oOl8|8RWm1=0kyAep@1<I$SnL!e#Ch^p8aEbWFDijqfnt2AsU_vG7$1~N0Lt=jBW~{AFlK2m<@z~g;)JLVI#=+%x8g z9^O0-_*WK)bw$XU>=OCB75`fJ(oq%!NRvr7qhqF!GidXF_%)Pcohvx;9x%;w3x1ym+NjX>yd zm`^bg;HMhJdIBc8Rz}+{z;(kk>_dZbb~f`)%M3=#}l~U1?}%(RL=a%4+fmllJbd2SQ~gK=gvmb+Qti}D1n9} zri*bIp3lr=j@-$FtccdAzql~06a0OOKfm!SDvzl^47}mcSOe0(4eob>I&C^#4>Uub z#q^#m-1V1t7vCswVi!Dc9U8F#d%KXbpoPW|g0d?ugNx#A@!p>oqr@|zFfqU7=D$jPsK9J5d!j-Jvp29h>Y~@u zKLS^Y>YPrCs4u>qi2#-5z@Ij+C0kY&uXguWKD|>4jep!#1~BTj}i)zN}~8QgfLeE+!mEHb4&N25F)Ra z3I&)4S#q2;N=1$-$!+kCyxGL;hkmM+Qz+}0m|og8JyFs6(%x#|w|Cw1823v0xyaf?*5Dw>K6ciCmXx)LS6^UgosO3I?SHy*=Hv^#_H zhCdnRlFcsItM90j|0!#>CJ!`(L3HD(s1&&sx9a~Wv7O6pU=-}vx};F>3Ov;H8g#wa zZGw&N7{Gs8ecOnu5WQUf+My4cmkv}ANiA(Ira-+E+c}ely0bvze_o~vID-m)oM#@7 z*kA% z&xt>F4UJ^3{kETqrK~C0a|}Alo7)O#ZHe*-i#xbmTM9&rp4cNcw8--7UyVAGUI_L@Hukg&gZCuPu4K|+K`9V@z& zb$&sV&~JDZ8Vi}@`li-Esm&*Q%od{I`6Hw9;uC);p->_?70u7u2lath&vD=#`2+66 zP<0Bkd%>*j#|5a}Xb+GYeK_rdK`t;iK+%G^ztDuP?dynCblmGM4?3Uz|6TwT;t{fnCf9e| z2c>GDYKO9KqzFztHH~$%YC0B+S)sK-mP3;EHkgP{h`mx4>)yoCii&6A5c2)1Y`y0Y zJgl`o-d`Q1^alGsnhj2&N!OJ7;k4?lcu3n5llHVu@mHL{6+JHE2bj~CZvVdK1gryg zdC}j{T7m$NyC9MtlvEkQ)6J{bs5;o>`6n1DVaU2R31|+MS%p9D=SX#L@Y``RA_I2zht>$aotuHLe2(S^$kEZ z^rcfAT-y@S&BzDJs?KivFTf$FpkJ4mw_uVd{)o`-wvjC_z_ksmDj5uy@*xftIph1S zsDgn4ARid}d(#k7etr9e4Kszfra}?8p~-Wu1#7k1NuDnh6yPWA{VHyLdgiSphO7on z2kTyrv5M7ti_biL@jTCw{>>IH&gbMAx|idL)6ca#<$3=`plJQ6WCTN`RFm42Tx-CG zF|ISjI(;7(Ae-qbt=233+*k$un*CiH0ogu=?h>DRIX)VqtU4-E|0O674#0PIxtybv zEUn33QEq`2sv(xg2l=bB=cg78PRY=-?Y_;gOMuwXrSGFYgg*5L&Cn*Y-kmTz`jCol zshN6lZP~Jk9^{+utam4-d)p-xFXp2$vo|?g`@R9hz|9~L>o|KlfcD_wpqi2oijZk& z0vb2YZ^hhW4Whj)OP|Gx`5JN;NioF_rLvE^SZqjW+6S^5SQ$tFVMyT(Kg!QEy60@aa<5Frm)@#9 z@0-0|AM)LCD7=*>9Dr?^bKzYXlO=F zZ?%S6LuW`LdC#ej@2;oq%UTu+b{+Kkxh%j9@K;Lz@OIjvYrIqvOFhoX`!qz0lxdsD zHn%;`e`MV*-(L@aRMp{OOz=wJGM80L?uQ}yQ$b4n+inr$&+3T9UF>7vKI7gN4&zQL z?Y|iZ2NTl^@$^twZI(}?k|;(c12}DNCB?}iLHI1c7mSXgvkBt7TY|XBCmV6_f#&>M zqHSEVo(R6C>7!tuZF)oZvYRUuh=w&Kg@ij?>7?kN1*{;mW6<+HlKfIt)XL1lb6 zfyox_p%~l1?A4Dz-S{agb5OkNV@V*I8_?~~4_s+zLC!Sxo{TkSAcRJHw*XTR^>$7N zTMnt7O>iwtZfTakZ;bnS%}WXfALtwgBrVLSE$?IP!SlqMsdoi!d8}inpx^uMc>9z# ztgRAeWwPtbVL78BKW)Ds)GECG69e4E-O{%4+ci=*9`#~IA3`swX7bS$x6B|%x$x>( zj(cl+^OPnX>14qU+ua{DRpV)EyRPB?ndrf({x)T3aX7vX(~R_iHW-~Zg1Vcu`dkbB z$*_Vyxx);VuzYv_o;Xh&{hb-ET?{NdM6gQc-ik;P&6OMBi-OkOM5P}O(DE$5AbY{? z{dO>V%I;_Sezj2X8{YaClDcmd{D$wk2g=zC0|!2W2r;ZUd(SkkCu<?f9_ynnco)YkeOsa+AtDwEXmwTI^($q$CvQ! zV3WpyWx|V9EEOJ6CZZ(ua^(%gfH6ZpCHZ(k>KrpYYzEsu>cb0djF*}iFw=gLpTsYlYk8U|Jm|Lg8JCR63V&I-ev>S^sAY;uX&w>&x-TK4B zULn)sQK%l)(jO&+|IBm)U#ciV$VWF%3$+xlbM;QZwX1m!7wLkrf`#R4ttmOg8Plcm z6giKu_aqDA4a{3YnW(OtrP3M8j;zHetm~?{2+EkKST+f$OqF_xuaqCXI;NZ86$m|n z;8f$q9e&t^NiiBueK^DT5=xH2{6Pugf4QCe+i>&R*sCjc-PthwP!+iu$jzTgLc8w7 z^AP%6P&w}W-LQ=sxQN1=&p?KV`cf2!^;XhzPh4j&r5~1ts2JHLO&`nmYY0I;iUj?Z$)q5%< zLo&>HvX;*Cfdv*$z9#Bof;DRDqL|-QxJuQFDR6#kSf@hC`-x~7B5QT^9ukA?dqkhZ zWli7Ond0GQEStZPv`K!z$@_D>8e3QH3t}^U6;0-s5$|(?tK3rgeXa#}Xtw*m{K|Lu z)I`flzjxrm3ERdnK*OY;Zv-X}CKeb(QY62!X7^R!t3}^a#|E3yPOZAntpfgIX#jq( zQa%f35CuAqRX)pDyY_vEr2SD&@BQ-;eayGqWT>}v@hp0H)A__-imd}1ID2+g+^1*6Wqm*d5c8LC;K`QW3SRpK`D8h!l) zf+dVLXZ|8G95O~_^(6v3(*h}@@s`{E=a=!`FlEFT8y?|(yJ_@Ep#;IWer!T>7Ix3W znr8=^hj?ctFJ=gTi-v@e!1h~kO_ngNLYT3%{2wK%3R#TLsy>#u!WG&j3DATRX;ith zEfXk%O?;$N9y(|Ae&}55be6=xOFBa|t8i5(_c=VVBpFDe78MC&MiQ6988fjDgeVSDw62p;bD4V?&ywtsi4n)t+3Wow1_8n3UPmCxQ|u zVyFG2WK2BIrEeLB<#{J@<^rd$zQFdKoi*97**26vMfgE zU(@Id^2j7*{`PiKy<5tIoNxzzR6N7M2hOb{@3J76hHBFy{36J|Xl9WS9>EL|1bb>y63D}qoD=a6Q@IZ4xPS1n9umWqxy zEDMFOx3_{#3X>&ME+rDNOFqM_w!ijJNx|&H8$)YroIdoDMLiU%b4GG0B%N&IaPwy= zR1zg&0>e1k_Ui0ZCYh8RLQSx+TfM_y$BY8Av8uDNJo!U;Sf}O~|MU@A6$P7rZR}{H z%Wm7BJ6Q>qTk zexLucET(WkERt6K)=|>!lto|$B!5p%6NI7fQMW4^L%LSi+Jux#58ZB;g z32uKs9&+0gg6^z{mEyiL5UCo5Q)k`E9bB_Fz>WooPvE{}u$hml87;b^z!KW_K@foQ zDDvZ(HZyOFYPB0N;uqoX{KQBMIIkDp>F`wV%dL$~&S*zgi?NzlKFx0!=fx~Ga#lW1 z&$ngUrKKSWa_6=H!^>g?c`~M_5Es-Q7msAWj-%!y04m zg?M4LM|fI55DQSRaB|4jO&@|!XbTg&WxRnI3m6^KU5#t-I6jsB@TsPWRLq5rOkp~7 zyLxf)M&V7F?EEMVyqAK!2U}rtdT(8)3@7*b?>e9>;`91P>hw&UC{L6ssWl0ao>|(A z+u`=)Nn<04$5|WA6s0@SuszC=@LL`jzQ^YsF9RAd}^pkwXIT z_v^kqtEhBpgCUY706|CG6(Z2b)lP}O$cKazTY8yW`Zn0Aj32fTXq`u8ifSs^b2eNX zcqKO=`0PtUXrZNPK<}Dj9kia&z{CVuJFLAY>+>O$sg#MwzF9^#tz;14gpzB ztnZwTGyN2@`+Fr9=Ua@`bv5WsxTt92Qh8oFsCqJ?Kcx+5hEu*OoglDI{3M3p76wNp zcjx3+d8V&th+FHxRrl+$fi>G+iQMmjsC)ISZ5~XdXK#~fPaaHb-6hDhpmio@W)8Mb z%no`(!+nqGM`gBmMr~MC*5mk97IwizFp)z2u)^n=z5Ppv_wZ#fE&|mHFIreL?^@pD z((t?VkJAz94qsAUpF?n+JC?u==}MO!3U|a^?}e$ zEg*^!EH^gw_^;lSXoP1iWe;q=FGRF3XW2{>GWVbI?0c4Lw~|EBw4svWSuhGeEL5)) zLPZOhOXG__M@N@wAYqA0I%=YeIamL`^!&Eno7e)Qre;wc9U%_L6IwqJ**;+prA)~m zWSN4vqI|<}po4tx@@jN!)f`0)%3f5lc`J)6MD6_f3g{L|EoqW7F&1guH>@jlWrLny zg9BzafVjRXe7Ev)W5}vONzh4-)}U~&FUb2zmP+U-aC8=98E>!61Hs1XVb#Z)B9`Qz3do+qB zuIQGFdV(PpaDQ`Ost8*!>puWtLjdw&S@UeyBakKECoRt7^yR6*L!Fu45rh?U7bY@L$fJ6!x}MA+gZ9RQOR3bI0?$+5LXJ77NYp#9n{P zZbi+tCmMrP28t`XGs77+VAEXg~#sbpYMgwjjVKFasUR5M61zsOtk zp4I1RIw)x_Vk)ouTJ3RFDX$8ak7k*{_`J!V7TzIhsO5jgWEG3&VIr;btE-vL?jLAI z8@IG^#h=5hP_IEfpsFIo8x_Y0^~`D~sGPG1IfzL2#YRuo-pf70lWEUerYbhobfE;w zaWQERKt%d?txV9A%i_W+N4R>%9i<4iN(Fm~86$*{2o>!v+sg&t9QOveiAcl)UYBPF zfL$&{Rv}$t!cVxny>Mh6-w|x4KvT`Uy8IJnN~$ksZR9lfD$Tu~mh%w;lduJ3^6VhG z(~=*3H}Ts#W$y}H{QBSLl zE$+@d6hi6Ru^|kT-qTAxzY+dhaM2>jcvrmU>NsX$u9nbd`t0cQlVfBJhLT?@Jb$>W zjSF}&j!4X_!-R)V7sx+3>JGPqYdslb!_S~DZs-{9j&>Xq+dfHNhg|A3%Cm=Krd(E=#g&|&4}0;T_gDY+XX$E4;SBEaVksW ztPpoN>7?e&U$wCwRh(m4fnKTAa3*INQTYc6dkH7tdYL>az#th6!r1cC+J+AKn(3kPx*04UmdHBc$0l95*k8^zCm5h!QlwYRsnOd0z>G<{`QR9)LP zozf*CUDDEmFm$I#cXxLVB_Yz?jdX|HG$`HOA&r1^4m028d5`b+%(3@gSDsfc@{i2h z6~hhT21t7LQo2h0lD?+(8oVr$O$qF|v|c&mdjos<<|Gnz0iB2{9D4lyJ56nK!=UW` zJ}-vN-`rogS#5z_B^|odf>{!H@oQPS+RGOCh{PuTa*&Xv-=i!U`U+>Us&v;4;eTVf zVCExKz-As%{NoHteH&vr&Mx{kvYfBRZn}nTNqmp*@dkULy9vzi!`?)wySp z!FVjh=AYqRrnE)mZjc^%QBIJ~!OM7;8`$k{WHV|EL~;@lOY8EZlv{ z1!MXOxfYojwvQDCn2bM8&!OI^P_w!2dLe;&rRw3F@=3uMo;zi}Ji$RpqeC zo^^dG&qrv#b_MM{J$>{$xbuKP%c9BD$&hs{=DNj2U!%C6Y~)BS(|qSln~Lkzv5k(N-hAuHd1C8=&`FdnWvBh_JNfV> zd9_I>3G5x_#`#N9e&i~?Qc>9HT-os$wj`4t!{fZSam)#H&O;h=tj?8SB-1(Qqrh}C zl5QL#wU%j-vBmed&Wt?K%I_Bn>Ai~L2-R%;K9+SeSe;?8fWBRQ<{4-p9(QJ@3n%)^ zwezrzyc(6FN=+Q|6;&Zrlv^yU`sRHZi>2*{=5xdJ9H9Ba^HDZ6!iE&=eqwvHi+F_g zZsK{Y!hWMh$hq&&{FUkg_8(Ipxl!t%2aA*T(qz~OgTN!G<+gP%A770O!MbZS<*DcI z3ub(*1rux;J=}Y@gd-Np`Nb1m0SowKY%PTd;?PQU!_rxu#4?fh_VC&7UHVDP>ttpB zqfFwB&7T$$?=y4=x`E>&pWiEX%|R@S6I2*^mwmN`_nUBKk0NfLhhKBc+5nkbK`fIP z!=mU5sb|}Ht>e~ma0N_12^KQDQRgYOG_QA>Ay)6I`+^i}l(78vfb~lbo4U}6GTz72Cu@dnvOWEDa0Ev7-^?pR z9nbQ7vnO)H2akJE9183Iex!jb!Mu7X0-xF6*z^8M0xVF+&d>BXLnlS3?M{WwhlXhS z7qE|6iy%*MD$1k*ls#)bL^GdFFf=%n__+)&ZGrG%|jy#c*LKV86rZK0lq) z7RKhF{j5@CQ241_8&SAEusA)IUKVHOk+VGLRt3e)()C+ES&YhE+q(h7IJLpP)zdwy zeANhpqU7DI5+>Pf4xe%^bppuF@_y~S5wOgJ3+k(XMMaYR87!27WQpVc6X$rVb#H5? zWO;SK%I`7$nYvo+&xlF9h&+sh<$@}lDeo@b)P)|_5KFhgj*o8h?DN8TYMk&9@X{9X z4Hb3;Pju$QC*sDXgQ#@B$uhf^A42Bl@%C84vjjw$x_vEtwrl<%NKLUEV2mm*{FnbP z#2D#8?kB>?Wv|EYTSS(L=aDTFqL9F?yPwY&xWp0lK0DoaaSiD6;Ls#Easdc>o1hOT zvsG_Um_N(=r-k0gsW+3c%HG1aFw?nQ$Pm#HRd^szrTYSg)VSDMA^u5Rph3xiwRVSQ zY_g)gX}$szOiBCmzoL(6N+P@G=pqeA<0)7FSYSiO{?zJF@#+w;xFO;0>U5m@Q;%8* ztX^V$tf#D?S7g_k=5dBWWBLE!h~PDRisoE#Qt*yx2STV!LkN^;5){sxR6CvEa>A4xm%|3D`$!&jEgF+X4=Z187(kTWOz=kXvtM8G zc-gwWJG2jPB&-{L`PhfZPoy=`p#<9vrM4~=_i&_J^eqzd zmVT^PV2H%PaWIA?7crWAk@|fR>~s3ITP>kZ=Ai;Mxu0M7<03$5xA`F0)x$+q$zvWG z7Z$UT8w%v?U+NH;hC!0^1@kjL?81Zt_uV*vGbz55|JP)r^|%C~9o4|f#pBWrXq<&f zV?qS7Y5)s;odTv-*V8n&#$U{?2ZtyEFvTURp@PMVT#cuZ#*uwp5i!l@i%@I9g9PCO z?MJy8tQbg(3132J7oA_^ zWuI~5{v6uw(ZjqNCH|^)fXc#3-foNckUiALM2lf06LHcFbHJitecn3|j?IoiFy2TL z@YV3luqGRsM*%MmcV>8tF*WP=o$XW)bSZ|3ghAq4 zCXctcKS&>@XON`8x-vC^>Y}_H>?OC1TM@CFeJ13zcB{BxpH=(fF#sJ;nDyqDWmZA# z#n+jUb1HPaC?XV-;gAu9Lu}f0*ScRu`UKdnrrZSklhBuj9;H)w!5K2pjfu=Vv&|fc zeHQ5m`EAVhFgl_7h5%aYAHjt^)}D<-un&PF1QkL?s01hpA0SJ6tv_Q`glu(I3B$yH zVJq(zpAaGM9d-9nlIZR{nZJLN-;0{~F+_Yw>h29$zag=jiQlFB@z0MX?GZ(8#OYl4 z79QvW{(df#GdYoy+AsI!$&X_oO3yxZ)iXTmmiQ}^7rm}esyLHQ#7u9h8&At=i_8WR zKyjpWchy^{Ob4!4|KzA|Kkm!DoES!X@$&OZp3tbMuj#Y>$l{QmW`A6&8uRf@j7==C z2JkoakaM`WkOVMRAY-?xB|N8OuR|VT#!nq9`g4IQ&hY%{RFz;6QqM2~THIPP$rwTR zo8{vVs#|L+Ar5Vgvo20Dt~-(7)!5=Vbl>s_Z| zF!XETQg``c2<iz(;~{n z&rD!%>yB+NHY005svu_6;m>#Kr>B&M&qHhU35>)xsjt-miY_ZD@QAx^;8-o%ud9uW z56@eiti2d|gjM%T-fuX)457a4KbEXNJVzBUz2e=JU6F(*An)~+j$@>i25+{t{@O~W{pDa}8`q@Fm^C|OYCH#CCRO<+38gTothwofd z)4$XSYY)Dli+G~YK*F?<^&RVbq8g($nz*h<)r4Q>m%HF}>@Uki5-s1U z33}Qtg6wwWGzGsM+RpQHpxCw5ZGL5<;wk_wFo z>6Kt&`*&dr)tEWhgc0oVMpnc5?+@DU2aM~P!X`HK#DF@WYJCM8pL|LC9{aa;VpD%0 zv(MXHb(P+;C5XDm0ynpH^bh$V@*c5N4}#`D_gsVKz2Y83MVE&TnPP6xw@6gC$%dalpr082W!NT2G*Jn?VnC;Gi!Nfa6^j7U2yl zevpLMyF(xtM{KEFlU6(1tUK>sBO_F8LBnXZWq(T~$ecl(Jm=`%cb(K0+LHsRroO7% zll@n6i3h_L8Fn_wN9g1_eXl+dxlpSeC1k8w!MNGi61V47)L7<+<-{19D#Iy3`$v3o zN~-jy{XCFf0)Oox90Gw~C3jgV<4J>`1g7))dO{+w zg1)}t6M8#M-D|L*DJt?uHB2BRzChpN4RD_#$+NjB2C)2bVOMcFUZDvYtME-8oe{VD zi}1zIUa^R?s?42beAX(87YzCx68|4S%8nh10DD*5FtZxu(m;Jh)%s=JTQXB!_3`)S zvC)J>Uio}0mBDvo;x9k(4{TysnQEC0hZh*uB1zs6J#hZ`WL1q(Fr@p(sTNZ*0IKn9 zJy?d3bxs8c+tzvDUX{)jl`U6^URU;;*gC>?QREk3dnl50-?9hM$wV;h*o#$b|Gy_K z_?qS55V{7eh;MJhI|{rO%Za#&jzldBvnee}jlw<4vsvW~Zo)*JeTJ0Dyn(!WihvPS z8v+ZQ)5|i72Ik97Rk5N7v2&SDO$~_x^j56VOT~G-TTZV~vx;iJfR?sAxA>mzi0e)J z7z}+VD%in98+1^{MGPGavHhpd{JX3YK-9!)Q1#t@9)A<9@Bee&1vVppBi<@iuj4r) ziz0du(UOFIm{lia`jfjXXY&J|Tt2@e@Fx;aSYKmAOw?s|m=RZ_=x(`O9i{PXJ6>b) z3@)yz-t;$TDx=ep9ID;6<`@7nk07vU5f2GwPaziw#oIPl@*efs6=4c^KY~*3vzggW zb?ag|hyvdHxT}9$J^Bp4pU8+x%31W}AvpLGNpD562S1w`w24ri9Vh=}a5U>>#1FtoBqWs_uI z{J5LC@nxHMPH2@q{l)-OJ=GHaS7}h6MI$xz9C{E-|Hd)g$`q8xEq)U3UYyK7A z?g@U%*DMs@xU(*oPz!kFOS5<^-+T3Qlp(R~)cqGeU1WDN`G6_<4hcyG1~Lq|K1GAl z5>Z1-_$~qP)NEGs6=vRb^4h9epGC052yA>bEMT*1_-%GAipH8J%g7gV3Q8~jp~1bK z0q)a_at{THB#=%3GZU`#DcUEe=H{?xoqN6qE+&3!hUn)~Y(!lG%Cu^iqMhq>8k z%((y%EjU9R6NSls~W#^V4nhG9aHRQGnS;%z@gEDezn`8DK{l>5o=Bxa} z)4}_Le?+?Y)605hI#0nJko*^(1XjAdr<5KCr^F50uitv6t#xmDo}K}*d|# zQ;Q?IS6bvfW9EvzFYqTzigC!M0qzm(arbIzP-i?-jf%07YNIl6$wDRseR0Na!GE+0 z`Ji3_2ohs?)sIN4D^i3jCPX$uap<+dii_30`1^>}&XGRopPRDf9-$)^nDxFeL0_O< z{gL7`D_{Zv9Zge*31sysU@*AyBk8 zOo4sU$N}nCYd9QY{KSOL&wXEMagZPKF&{E2M0zfqbDrjB+}m)TJ&80^{;{P}FHteh zw=hg0&%l7tgjm-e9qF|!ufWGHIOh0n2HPEB=qiaxZJx;m%5RECADD8D> zh4Gyu>pes>FqH&U=xM9^I(k$Xj4~Y=aCPN<^Z0NJ26Ke5GIPFC0o_zEws<2YgaO65 zKt)#EAluOdOUWYP4?zMGt_6Gg^r!g~<=aOt+5Jd+cp?ZvNlWB}KvDE@`LTKoh)*zi zsz@w0c9AMBFsBD{n0d&>pPQga@CKPM^SE)uGcG4>Tf9_fiK?h!Mj|HZGS*1$1$J^N z!NiJzLhPHa&YQ~DQY&yo5ejp=(hJJh`q>k=49*%qoiXDizlHX90X8Uw zmw4NTu`9Oj$D2kEARYF}fd)6@wR}^3u^9FC?z6NlVZDgaB)6y8CkMn|@~TT$TE#Wn ziuxGa65%pwCYyxX!;fQdwfo@b3kj#%EP+dsvdO<5v1eL#Ye6nMT1H!zf8l_M&muT+ z=AmaJaiLO$;;W4Xm@3v}ic^U+wc4vLn@Q~+Yi#o4LUQAv3*PfRmt|jkZ=8L82|uQf zKU##vzsn1=VkT~?*#TUT`6YoPTw|jw<7`OC;(q< zb_&4y=o6&0@Q@Fs$eO$kpNx~B$gk^Lkwo|y#OD5Wn$};qRegvWN|`8Q4xyH@Y}(UC zlYRdS0X8z<*SZr>=mP8d+VR-=6~vg<0K}m`Q&o3f&?j(a)6~36ToL;sF8=WddCDTR zZAbuw_2ebf#2i$d8>LV0EpDr>EiycYg@SX{sBM6WDKtOSmZykAMC!6QnPk2&@rw4^ z7b4vs0`lh4&COquYQIsEF|;bksA*3x+jO~JX^g*X2wx_R4iO5@cQeNknp>@ez)bf( z=Z$DTRjWzX$|KQd$7I?8uiU4l+GV#?Nzd7tiOu>nF2Xn|JHAvodvtIhhmOAr z^V0TL(27YX!vUdvz(zCmU4FuHF>tXkzHKZ;ay~s>xiBVVw=j>lFG$i}f*8N88$?Q- zKEE?fIw=9|+RNO(FYdLLnZkGeg^9kTOa?Aqi$@py@Uaxw1H^=?AkPeaxm zY?)>>ECZtw&E7)G5KF)T%+w8asAVfSZ@6{ELH#%W@>e<~2WOK}|8=ohCBEh_$Oj+( zBX@BzJ@5;1=Xe%i-|_kCqO4-Fjed%++OU_v(|31rso+$X77aOUpO0>1do$O=%)~R_ zv#IkPWm@>)Xg+t{s77_^k#vTfH}RJSR%Z3!BSn4n4NN19C+UXsNFFd6N6PajF`i3Ba|x}u}% z-R^gVg)(ZL^4!sW`Fkip+N(d_Ecr+^5x7E?47r{$s73u<>%+?&SzdC`APf7JoqJ5H z=biV)+V$;zeyYfVeY4eP1YA@Fmt8leVAo)keeeaM?q7}RvDd(7*T;D$kAX2<37cHY zV)tcrq!&KwEH;Gfn97S;Ieb%g*w9bP%~CTgbW8 zqH(eCE=q@`lv$uSF6>CVSnl&8L(%yX)a!r3yNTGl1Cw%!7gnC|9<*~$L5Gc(LmdBePScsEwKeJ z{$Zw1=M7x`^hu6A$tmlJsqM+e%+CX1Uv_rvO^nBklphhIy@hLln{JY zCG@lk8zQz=6-t*++x+OyO7N(9s@v?ki=wR5NzW6YbOrkp6|ko`?fojL@o#f0^$;Sf z+qp?U5`xXNk+^l=^T$U`*wxe5A_+Gt;r5@rpo}V+kRoR1TGI0}_V*=H?n%zBRnl=% zjvWhk0Ap9eFM-yyJ$3JVb>&w^Kq6Se%N~48aW#!?sZ+?zyjho0S=D=6McT^j*!gC5 ze7|nl{kC(P+T5})X>kcrUx`3?SO+Gli!-DC>o|=axFQY|OZV_&-`LzGRr|+AcFKRq z1O4OzTtyjI|%el&&OCmd$ULA8toj{7(b^h2t#4t7;Q2dgH%`8 z`S39e^B?>A#;NwW4<=;lZ|c;Xx8yH%d*tc3RdLO=bqzCdZfpL?B?QKs_;v1oA>%{) zBkq5k&kFAoMP;wzlYTC%HB6GVbdzp~Z)p6TdC9~>N}qCLg7tlst8)X;O0=}P3LR8)wYinP%#R0gZbw2vB+XCvXqZx+{;dstTJOG`fE#1 z;B_FW|H9+NC~MlWt^VG3HqsY=mHo!W#c1R^?C9E;yO1Q_(1RV;>mHn1ttosdGA#qf z0BU(J$sG9fkWYL*qNtBcB92AUI~ro}34_@C7I2<+kSM16{C|hc(PFr-y_VD&q@SjA zbC-ca4ZUcr0TC4r){g_O@CXc~g?hT3%nxTXLG{T34jGil`ddM~1)po;RW>dU;kdrw zw2;t~_G$*hOMB3er!6{veTVKN8W+I&^!%qm@v&Oew`wui>2sdN@YLj}JoG*3Eomhq zVnG{OZV2#xDl+4b`W3f$w>ZU6s{Qw{jOpZyZ*CI9yY5i0V^yX%)@6eODQYu2qU9@oFW zi_?R(i5tZ5T`f>i#W+G_IE%FE6I7sRSBN_*dCBmhOkUIrnAD}o+&(87cY+ju#g6cC@|Q5}k*o=s~+Y`BETfSw?EFvBs$)*Iws;irg6 ztfMe4gco!B*-2Fvx-b5;D|3DB^96r%3Cg6DmaOS^4chPMyZj4anUr5FT<=1@Bt?6l z1kouP6wsHuFY_?5++SBDcu#Kn?d~mn>Up~${fJYBv8;M05Bu?NVoU#Q?6G@>ck6m1 z6})Ws6R(CNMBWy4?Hcuvbs=)2);y#@3@{$lCQAw8UQy_K`s12KB8uy)B3r!<&A#Iy zL;==+v_Kv3Ps6>+Fh!Zw`ZwCi@Rp_qf&kzq>U#Ic^~3g>5pfUS)5)RPZOpb}A$w-; z)M*q%DvvhjznR^$G!+9W56=fO@Edc*@8wrNW9i+2KcVXoKM|razw+lTo95rs>fjrz z?~L`0UU(`*)M2Y5@rV`UXQbL-k>nt6L2|@+Lhn)c-3+IHhdaQq$Z;6H}sCN*#AY7i#6!k=NS8L zV`2Ytk5caZab~69>DHYL?*=%GIxlT#K1h%%{1G7K3s2%w9PpV5ZPX5p)n3xjwx7tV z0oD@T7Yw*>UteT0u%dAG80Ib{uC7RIWm4X1BQ89e(wp8Xq?dTAL!f0J+mx!8z;-jg z{m4s{$M}7;j-u3fmYkL7i2!_^20O%=^w;n?{FiI&OZ}ay@N?i>%kI)yocdOvHY_Qh z#DwUBu#F65*fplE2Rgq(3c7UR-%Y37{Q3%-;Mc4@lg(CGn~Fg$9b)GaHBqv&W)g4R z5^GmR88)q<`Edo?+kONrLDE|=?WC5S*2$L4+;Z#py`D2SnPRcm?P9b4({v)11-~cV z?=o~*p`*b4f-e!9#$!In@2$5{hHrxJ4sgb|FbzUY$Sku7N4?xCm=NbF`3;9*$zg|K zDS0y$!aGWuiCj8^JQrl|9LV_&s9n#3IS3Q<5*t(wch zMQ%3MI*b~Q$s>1@E7?is47er0^a6tT^A-l0HFAREn6IGW<`>f<*D!5aRscmtMOpkI z0(cDgC2O02{RMURCtN0J%bYhCe;Q&OM_|d%Aiv9_(pT-a{!{knssFGw<%nv+?4KNw zlBY3d>88;qFZHx1JhW9{c^R+)v5eo!nQY@HK@!OxTFcYm=awKqfvKa9fH)1nF}^_D z*-ZPK97eTK`EqaNYm&ut4e5If-bY3MWX*aXSkyx!1zK{Tk~_yF3bq0JyTN3OHN3g2 zL_-Slfe=P)n#S%n*5faEYY&zOS*y2$Q{-Ts;x|X9@&y5wg~-&Cfu_`FhOGR+sztPi z7ys+~fN!vk8a+ww+w0LgQ_#V_&)+1HO8}Pv*MlFueKkd-H3X{-MB{!>&g4^PM+Y#s z(?3$BTAL#k2;x&fpOT+5)Tq)SKg0<~(dx%h-a1S@+Q2NNAmf#5%PhdRY$uz75C@gc zhEQ(##m+XDdv7Bd+@ga9dC8P_y2@9qqWNSFa}ythV@Ke( zu^x{9Ymp%q+AQdzN+Cr+2;l(`EG*#a1mJmtI%3+EecmTv)jcX&XJ9}=I8E^4w}0RB zqH^HnZm^&upditLE`)Vovx{OF+m49Ir(AvoTAu-vR zGL}9M%zH!fkLv`JNFI|Qqn}iCW(Xb)qE>*2I#f3%q>dnd@XB46Q2iE}1U#uDZfqwS zmbx_)-caW&3|h=@?5qUa0%Ei2-t@(DJ4HA4Hp7 z#8Yh2+8YjEx1K!+8j{nieNK>-!HvzF8K>enr&Jjz759$)`Wyo0;Edus%Ro8`LLPX- zJW$}=7mF{&^stYkg_Y!!C)Qi^IZ#kANJQ?|)VKZXQ89E(=q%y_1g4SuEq;PF^t|#p zlNQqtpDusmp{WaYyVNAWa41Xd@>--1{iIgq40+ts3E&Bh7>I^1qo#TXFDKc^jweXQ-F)%puF!xRA5wMc z&yg6N|DHK7xWE~(ppdGm=j0sgO&Zx<2U}ir<#X%gfa!$LjVh+f;F(VrhSaBU%#PIJ z@FCQyuPpL#H-l3sY#7>Grlv4Fj3hg5?L<%v6*t43={H}t*f)93{f>_I1P63-J^5+- z;ZPty-^Pv5Z>@!4o(3)_d^%~WE~=NJ9UqZ<;LBfz%>Zc(CxJMz|3x#@M+kuO_vCfS zU%S`8M^^OG3k~pq$GU4})H221yLJ^O$P{a9Y=XbjQhm5aVr01|CudJo6n6$JmXLls zh&Mi&mE9(&iq=y7ImXY`*n&2NT>C6Di2r>~-4sNUs2NzA?=ht|IOwF0s0u$7Cv zjUs(~|Ib-=UI~R869E>=41QzChO#Uz*h{Dfsa5gY#xyR!C zqp92WGu>eA=ulF?vr!-2ecuGQf(}~D&W6YeL$rHm%h6iH9kFg2 zu0xfyU(UDJrS`kgs>ReOUL}NZ6-zF8J>SiqRV-tcKn^U==q!%D2zb;dz(l}4g73A? zdO9|oLN|wMR>7#5FKMJ*zN6qQr|csd`v2hd-*@&5Pmydm3519Rzv`%(+IN*eQ!*?$ zN$gn-f>HYI8lk2a-H+^!8(hP9S>@{_)?x{-{t=ebqsSBRjS;5bnayzcqlr|ZaTr+r zYw^ENA_1JT>mL+|!@sTQ+ZW+al)Cn3ye?Mt42?*hs%HmfW=FEkxgJ$-3Wrt{e~TLW z{{lS7%i|ssH#haNLJ8Of(gYA$N9Ivg$n0e4VtcE-s0jZGnfL_Y57jGwq-FO|#!(K^ zE*@M^b?k2lUIf)`PW5e+1J;0NJ`C-q z!`z{@Yz*}Ehc`U};QJ{iknb$rOBWsNwD?wdIVkk&`~cQ>Vm~ujWMgxWm>S0Gfl_9% zqS$Y;!8rV2w6kXGxtEt<-Mk?rEV`Qp#X~ZPk z*3Qt4m?xNI_Xz`T3d-wgE!`2JJ00*T>0)UGI$+i*XJq7viw zz=wyyQn}g13~X)^FRi!t8D|F@n}ai5>1ZC#pwN!6Ca~z^$mRP>R6is@5FY>HK zJ4~HdrOAv@NJa<8LvsDzxYBe7!A}ZEg1fd;_^kbGN_JI*h+Y^$GT}@+w2o z>pilxTE-^S$+RsHp<78HyW;7be%Y>Z9KhraL0b+%S`MKQ%zz&;TWGg#XB&x0dwvPV zwW-t@vs9k?m-C;rr!)Rb2*Ec5IKdTT74H9}e`XRTlm&dm9InIYjO0LvW{w@Tn%ilY z61T=GwNXdkjUGq;H^(ww=5DA8&Sw(9VKemazTA7a#pVGCT!!74J#>S?*2k;Yl|xgH zFw(=Gyi--9PargzA2E<#YJUf_-%(nX$R;?OVb#qG(fxg$eXn&REw9GJ_^cqv#< zv*F&sD#-XL44J>^oXTCW?I4iI{WWk0MArFbVNgRMbe9`ge13QGV_~Qm1#}407KO=b zUP?1U;C{dZZ+IFF4oFs2-j#>ti_Y~-8B4mH3*Y!%WZL7kO1{_L@5U#>pYgEsEa?lN zVM6~{_a&Yx%~|!F#b{;qKqj=#`e&Q5=lyf>d5|Z$lE+TeHHwgE>7Gb@0=(9WeQ z&f+cwP4Y5!$&G5#sI!6VKeY>(r)KX>;hpFym`r&c{N!Y5-!b|W(-YtQ9aN3eMG$-q zi^+rKZg%zk-KcxWXNTLO0M3SiW%SQWn<}3ab?zyV5b=N}y`kr`WpIGm7T+_GevXB= zr1R32>R|`=hNiNSLzAI5Xh<~+t9`)U_|x?+=zwGM^yd8K?22l)f7_T`brWmz6#M=T zW>~Vb3EiG>0@pV+-qw{uug_D5Fvr@ayqqfwHFom}i#98mH=k7}VN)PjG_==N8Xhi3 zZ=mZ1_#1!5H>Sz9FoEb%dX(p=S;?}^;XRy|WT*Z(4Mk5jrxtS@@Ij2DLW0{N~;a}a79xvCPa!`rL9;KLd(i$l(?(>HHv&|f1NY`A4K>PQL zxszS_3a$#`r+e>{EYlkwkxGiaT`{Kb0iAEVsX8}nW+5icsWX9XyJsAR{!rlG`C3%o ze)mBSTWi-bJyV9&dJp_V9s3C))}bZm)s?z~`kNaZhrk|Ha2)3?>ZlkzTuKpWSy!jq z(Oj*p$w#6mV&Uqz{>cJw#%Chb(l6r zuYd&bPJe%~vNOjfL&A!M)R6i#+!-#riw+IT9FyGIMaGZq$+ozvUcI%S*l*cm?vYKfo~kZN&@;*<3nCpnSZ2eKs}Wbldz`o zJ9rEkcsRlu(DL!kxn+@REkVVs3|Y@QZ`w%QqX&`Y5p#M+v=*PNa@ZnFlW_fSvDZBp zx-b9mf5;K(@APB8lY(2!{cs!e%d($~gPK-DM$CC@n455nW;Ky@JU*H=9$1m`aUc7g zCJI^WxqQnzzs~=62M_Sg1QIA-6@T~xEUWZGqz#dUJo75mZ{+BFocT11>ySyCy#G)A z{-Hy1X%RZ5AE@bAO%wT~o!sskmGbw{QB;vy`66j5>p9*E29txQ|3ZpByVS-d|IpDq zCF=Jvr;ZKKi0Z~8=+TJ^%7P463kT-{ja$y7s#B%h`xtm_OCvnxn?!V3y2VH(Q zaC=&_rN|V;tBbO7Z0QW>Fo@UL@!7G9DUQ_fZ3;Gk`7{Wj^L417HLWV7R@JJ~E#30^ zls@wt?)WquFJ>>qc~yLDX3S*Bt}-ur8|jx)MrY7fE;B_{dkh_`nU_5az`eRol|0Lb z+ZE5~<`#h*q?Au$Txy)2-85-ffB6sbHX{bn)~!ZQ=Z*7IJN?+0cXW{xu&wRgFa=az z6eir|&#J#hC7M_NFB$pb)&&_Z_!$+6ast-*{wh*QLi401H20gIa%(npK9f;*IVRXm z9*>}keU$gWa)Q2g#AX@7Gtol(eUvxtR3>>(i8%eE=tg;4yH%!@Z{qL2>-l9~y3M@% z3dy_4I-h)>c^k)GzJwM0MIsta-p|J6baPK`S_twZ{ZF^%{u{IZUfryS(!FjDz7qq1 zv|WQ(*Wtu+<udjNy0VPd2}xB1Ts=<-usk!TJWvJD-EFM}u#|#4 z6#aKpety~b)7l!_PJT^4!epkIzkOZGU4Xb_rP+2b=4M5SE>TzQE=hCSY1TdI z`<$yvtsZ@@ZA)9L9`5f{QQ8l>zbUInjXR+F|`~;-B;U z)&lcGB`M?G@?S5X>B1?8VHJG^#Hz=P8;iHjZTre-EWFuc?F&rPZCA03NR_- zT0gnpv=F+k^o=K99%3?Z^3PrVZVq~M%}Away3Vf;j$&I9(@z(_4ckXp{UAJZ=JP{LV?-JVBVBg2wY-R_ z>rUB9Q1x_$xF^WJoAWzVVWnGm7qwT4*aA>tgT7$SeePMQD1LmAQEKvyyHN1zd*ZS# zJF@?TA|GvMS4^!LCY;L0b87=+7M;!Vf3o(nTHT<97iw)X=QtH*wiABuTsbSDk78lq zVRRDG)?GK!mrHBjk4>-CEc|0}sY@m9P7!N zQV>_4`aTU4k#wJ4t?^{HoR*W5ZS3kSqt@tP4Y;^cImZb{XNhR;ALeK0&gr70OZ=X9 zF3+qYc}|_!nCu`SVSh8?IL@SSQ@xwvvkLsp67q8yx6i&*-d@&4H~%(&|5ciU%^t!a zf2;uP<>H(Vvx0w60uJAuK{5cea9Ll2oKLo$it1IGPqdvlRL%E2T8=5=nYSV@Cwp*~ zEjr3Ru=OAgP34-(@wuDN6Yr-hR-!Aa=nI`w_@{o?;A1ck%4k^`k()I6k5lNm7F1!Y(JTDntI_AN3Q)9rv88_i`B}=;9|2Qz8 z8_#xfWd6Q3t1=+GW<0f#wbIC*3rk%>7ljNU{oP|Oiq?EAM(gHYXy2gy&yS#F=PBRL zPHUC$Ub3lc&gAY4eg+xdS$l(=<>tH;HPztDY~eHMQu=J|#&x*q>Kg zo7XsEM}w&k7&Ey~(owVB_4@f`TdQ@r8N%Dlfu8b5R@BTH7mdd3Fvq*xkF9rZ--Nw; z_R;8h8bcpVHYfTX=Dv_j@3Pd+M8GaJf2rH0MdN9=jeT_&LhtJ z%_H;td0dQwp_Mr=3%GlP4`)7YQlNwfv!e^f?<=HG%MWk-aLQ#Udsp%W)6L~zAXoR3 zMO=R^4NJm0-?$K#-kDdk&VRq#&tuO|ilKqsJygAkuZOrnarR1onj%hfmd<=X?xNlq z_jz^P@@ffwwYhXH{lj_Uf$*jJ?k9yoRCb*8n&x(g*+#eF@9shLJRM|b7FO0*OG5g=-lL{qN;@q(?67d z5u7%+!gBvfv31oyMkBKp7xBykddNIsST)OjY5v=+{e^J?${PP*-USy&4zPDbCbv9}aJPZ!IA1kiDme+mjzY zpV_s#xF;PKSSS3ewifj(@|UXY(yD_}J2M=3H`aJ%1juWaK!%Q~blJskLE8EiI9d_5 z?Vzi!5m$zRBJ7@zcXn+*!l>_k|BLXWNW#mjZROCbY+-QiS*`Mj{9CgY|8rH9b3dk1 zCV;n5r956bm0;pfaAU+u=hu{l&;ItXtC3E3i^ti@_QZK)e16;$_yZglB4FtVeE(kb zU)3;XXu*FtOId1fnryUf(kcre^7{KGt7MafnxEW}d%qbi@lufQ0_PcCM|d$t#L}0kPgNRA?Q>Ho+93WkfA9MFj~emvR$9k*Ntp zs35{J6eTz-2bYiNGS<(hzZEm{K`W3iJH06v%&o9r?M1B>}O_@j^^>Hhj!X*yl010kp~!YU7|HKl&!e`;?n}Z zm56hro02c0IDA}W_6b$_&dbIAE^i~HkqP+Ngm)FvS-waNNDrPyAUQs?U8XHXJ}8Kt z`(bUGa<{8ckcen)vC8!hPVVEUB=uRaz5LH}dm0liT(Awj9d=*6fM#6li>l)97{GgQ zy*nNUc9)0oOk8@#@S(`ajo(5rZ(6x3i`pH;A*}! ze||QSdau1?iv!_xolh@?auO5tqtl|JqO#tFV2|}V*4W_HP3dc;Yd?3xWaGmgKWM26 zTxWGY1Wi0ez4TdgMfHHRcWOa9wpFEg)l&Ac=0k5ggRPy7z{QR?a^pRO;&HbIrEmN;Bd?u@5edGfO%@OaJ(GjeQPkk5 zm^VqySlMGK31U>P~eEYb32+^z1qZ-;bt%x(XdgD9MB1`Y+ zl<=H7L!Hsx*=M=P*mlX_j8A-BAx!qqZBOw*3?wYDkm{e-9ZVIMjhJLugDMQGEpr;f zFv)3@9><$6;(tyzrKOs&y{76Ic@>0pl580nd%&Z7WJN27ClIEWj*fYlhYbulz-#zR zy+~ki73BgnlmjRWk=lT+rs@CIb)pXlUFG4#XV>^N`N5?&mh}!#*3IQENjm!iEm82y zcP2;hX3u6ltCCG0`-j8DGaGsWFwhciH64Q0+Ax(0^hF0~FxTv^AU5vh#}2oR1>C52 zG}?%l=~stgev??b`4rjt2fD6bN5Up?-KL*3Poq=Taw+Tekh%_R8!fkW>Q8+fx4~3R z3UGXQ?tdJ2eq;gUtArqnm3QB|et99#Fq;38Sj9Z9Q}T)lvVFXU_mjwE#HGP-nuI{# z&wPHQ^^1(}00p4e2u!9o@bIZZSBI}kUx0R;jgDXLm39WdtkuKCwb+zh$Rq4WHR3iO zz&TxQ7@Z(e0-GpvvXQpf65-pp9ulT*%Kp}nzSBRxGC+=<4u~??zMnH^LGTL56~GK= zEWid}Bmh`tnXHEI*O{$`V-`Txz%bc>iGm=HL!e9$l#vN+0D%7w{@02#EFW7!--U~2 T{b^-Mt1a~PI_g<-_;lvKZ5K^{ literal 0 HcmV?d00001 diff --git a/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png b/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..14822a1a871a9b07689cd4c55212361f5074ba3c GIT binary patch literal 13238 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

8@oz)&)xSfQ=0hgx*7B`q`;wWN>7% z_a2ZWIPp&gQEI)8EA!UN@e_Kyc3 zPcE}YC=BsQ#$cKLf~%J-fKB0H3VpCt52sNduR?4f*97rXIRBxaM_~Wz9=@SIUL7pe zg~~B0PL2y62_XH&TFIe`(Q8vh`Fk5kdWguDYp~N`nJ&(QB@H`3k{6{CR-bJ^oDBr3`)R>{Js1I5@<3hl8PkZMed)S7_%`x%&Y~Hg!?y=e?~( zGez2HwINy~M5)~rljJ`7i?oj|vFtIlA)In>`mka)QPK-<{?7$D021yPrZhx(7)GcJ z2$ejR8?u__?<*sQ>_PY2n+rnZKv4)+8gLRQ$(xaw3vRL_m279t@7y!ZE?%Gb^?OJ6 z3#;vZA}zXuDX>uhMF@b4&#$r)i`8 zId^`UQSy&pT?mrpMJV-fsW&#{D~?s}Gq$0p^GEo zHCTWOA#xo?l$u#H;z_$G{@+(X(uW|a>=^%Wdl57eq0&PmPzNYRV*m_cO1+;rckW`- zvQxEHo#XtsN_-JzNCPU=pe=QSrwV)4pKm;0;(g_TcZEQ!%;J`jB`0f+SUX?@zR2ngX2g&S)>34B9A-K2WNK zy~n2-zuD*d-r3dCca;Z^f;OYcRly%KBlcAVM!wS_a?x`o7bQo^=Pfaz>sb;lZ)YG# z3&H#(ZfI4)+mzzxr%lcG)G|#a8rxm+8Ls3e@PDh zd|{1s;epb1=ju<`J26K#T)d2FZ|wTd$Sna6ZLh@%{=DJd|M)Y~C`_pKhQX8h-J7x- z#fQpXU1y6+HUvm>XC!6^N`YS<>kFMms?a+kDpFj1m?Cec$la;(SY-tuq|oA(|G@>4 zKK$=UUL3udu5Wp1t!Xi1mwqp$jyAa=Cfk`TEv=Pl1ri^!(=`kiOQ9^_;%4Pja{{Ar*;6_Dpehh$5G{ zW`sI#l2jl7z;K1?Q5?t%S7}2P+8}vupe!dqnllR|!HFAYT!fd#Yh{s9Od0_poAR8~s;v9McosC3>E)PQoJS)c-_09!PW zNCHiw7~#bO=Wd)p8V`BTagl*d&2Sw|7%}hyr8zU;rjqQ0G{e_>i;q~_I|i@O=2(7N z`4GrIHQ3&wHOzeG+JTK1>fhgbP!pfy?-==L^b+$Govr2iqTza%j zdZbKotUNiha>be2tvQW{EN$mqJ^4*TjlGw#iUmVoyWwN|Lm>a|Rd{+TdHYyB1oAed zyTAQeM9BGI_kZotfeZiu0Kg!B>wA2q&^;27h(x4FL?RNA1f!yE5AeesBme*a07*qo IM6N<$f-i{RHUIzs literal 0 HcmV?d00001 diff --git a/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png b/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ae6f99ea30ea7ea422192fae8c104d0ed8b9923b GIT binary patch literal 20822 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�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 literal 0 HcmV?d00001 diff --git a/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@1x~universal~anyany.png b/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@1x~universal~anyany.png new file mode 100644 index 0000000000000000000000000000000000000000..842a0c78e26b76f970630c129d06453525478daf GIT binary patch literal 12367 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 RWT}5xmy>!gStbtk{x7#V)LQ@m literal 0 HcmV?d00001 diff --git a/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany-dark.png b/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..640696b6cd30446a30f1c86eec883830f4c38ffb GIT binary patch literal 19987 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 literal 0 HcmV?d00001 diff --git a/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany.png b/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@2x~universal~anyany.png new file mode 100644 index 0000000000000000000000000000000000000000..ed7cae3d37fda27b5cee59528b87ecbb19437757 GIT binary patch literal 19395 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 mdHAqk7xe$9yBw`}klLBQ!flva?t-NjASC8m5 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 a)B&Bx$V#DeNq+lctrDWLpUQ;weE%P<5g-)+ literal 0 HcmV?d00001 diff --git a/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@3x~universal~anyany.png b/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Default@3x~universal~anyany.png new file mode 100644 index 0000000000000000000000000000000000000000..2ca08c27c967ff9b95650b3a8c62ba4da13193b8 GIT binary patch literal 37691 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/ios/App/App/Base.lproj/Main.storyboard b/mobile/ios/App/App/Base.lproj/Main.storyboard new file mode 100644 index 00000000..b44df7be --- /dev/null +++ b/mobile/ios/App/App/Base.lproj/Main.storyboard @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/mobile/ios/App/App/Info.plist b/mobile/ios/App/App/Info.plist new file mode 100644 index 00000000..de86f1d9 --- /dev/null +++ b/mobile/ios/App/App/Info.plist @@ -0,0 +1,67 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + MyTonWallet + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleURLTypes + + + CFBundleURLName + org.mytonwallet.app + CFBundleURLSchemes + + ton + tc + + + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + ITSAppUsesNonExemptEncryption + + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen.storyboard + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UIRequiresFullScreen + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UIViewControllerBasedStatusBarAppearance + + NSCameraUsageDescription + The app enables the scanning of various barcodes. + NSFaceIDUsageDescription + For an easier and faster operation verification. + + diff --git a/mobile/ios/App/MyTonWallet.entitlements b/mobile/ios/App/MyTonWallet.entitlements new file mode 100644 index 00000000..e997e85e --- /dev/null +++ b/mobile/ios/App/MyTonWallet.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.associated-domains + + applinks:connect.mytonwallet.org + + + diff --git a/mobile/ios/App/Podfile b/mobile/ios/App/Podfile new file mode 100644 index 00000000..8424c7cc --- /dev/null +++ b/mobile/ios/App/Podfile @@ -0,0 +1,44 @@ +def assertDeploymentTarget(installer) + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + # ensure IPHONEOS_DEPLOYMENT_TARGET is at least 13.0 + deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f + should_upgrade = deployment_target < 13.0 && deployment_target != 0.0 + if should_upgrade + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' + end + end + end +end + +platform :ios, '13.0' +use_frameworks! + +# workaround to avoid Xcode caching of Pods that requires +# Product -> Clean Build Folder after new Cordova plugins installed +# Requires CocoaPods 1.6 or newer +install! 'cocoapods', :disable_input_output_paths => true + +def capacitor_pods + pod 'Capacitor', :path => '../../../node_modules/@capacitor/ios' + pod 'CapacitorCordova', :path => '../../../node_modules/@capacitor/ios' + pod 'CapacitorMlkitBarcodeScanning', :path => '../../../node_modules/@capacitor-mlkit/barcode-scanning' + pod 'CapacitorApp', :path => '../../../node_modules/@capacitor/app' + pod 'CapacitorDialog', :path => '../../../node_modules/@capacitor/dialog' + pod 'CapacitorHaptics', :path => '../../../node_modules/@capacitor/haptics' + pod 'CapacitorStatusBar', :path => '../../../node_modules/@capacitor/status-bar' + pod 'CapgoCapacitorNativeBiometric', :path => '../../../node_modules/@capgo/capacitor-native-biometric' + pod 'MauricewegnerCapacitorNavigationBar', :path => '../../../node_modules/@mauricewegner/capacitor-navigation-bar' + pod 'CapacitorPluginSafeArea', :path => '../../../node_modules/capacitor-plugin-safe-area' + pod 'CapacitorSplashScreen', :path => '../../plugins/capacitor-splash-screen' + pod 'NativeBottomSheet', :path => '../../plugins/native-bottom-sheet' +end + +target 'MyTonWallet' do + capacitor_pods + # Add your Pods here +end + +post_install do |installer| + assertDeploymentTarget(installer) +end diff --git a/mobile/ios/App/Podfile.lock b/mobile/ios/App/Podfile.lock new file mode 100644 index 00000000..c17de57d --- /dev/null +++ b/mobile/ios/App/Podfile.lock @@ -0,0 +1,168 @@ +PODS: + - Capacitor (5.5.1): + - CapacitorCordova + - CapacitorApp (5.0.6): + - Capacitor + - CapacitorCordova (5.5.1) + - CapacitorDialog (5.0.6): + - Capacitor + - CapacitorHaptics (5.0.6): + - Capacitor + - CapacitorMlkitBarcodeScanning (5.3.0): + - Capacitor + - GoogleMLKit/BarcodeScanning (= 4.0.0) + - CapacitorPluginSafeArea (2.0.5): + - Capacitor + - CapacitorSplashScreen (5.0.6.1): + - Capacitor + - CapacitorStatusBar (5.0.6): + - Capacitor + - CapgoCapacitorNativeBiometric (0.0.1): + - Capacitor + - FloatingPanel (2.8.0) + - GoogleDataTransport (9.2.5): + - GoogleUtilities/Environment (~> 7.7) + - nanopb (< 2.30910.0, >= 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - GoogleMLKit/BarcodeScanning (4.0.0): + - GoogleMLKit/MLKitCore + - MLKitBarcodeScanning (~> 3.0.0) + - GoogleMLKit/MLKitCore (4.0.0): + - MLKitCommon (~> 9.0.0) + - GoogleToolboxForMac/DebugUtils (2.3.2): + - GoogleToolboxForMac/Defines (= 2.3.2) + - GoogleToolboxForMac/Defines (2.3.2) + - GoogleToolboxForMac/Logger (2.3.2): + - GoogleToolboxForMac/Defines (= 2.3.2) + - "GoogleToolboxForMac/NSData+zlib (2.3.2)": + - GoogleToolboxForMac/Defines (= 2.3.2) + - "GoogleToolboxForMac/NSDictionary+URLArguments (2.3.2)": + - GoogleToolboxForMac/DebugUtils (= 2.3.2) + - GoogleToolboxForMac/Defines (= 2.3.2) + - "GoogleToolboxForMac/NSString+URLArguments (= 2.3.2)" + - "GoogleToolboxForMac/NSString+URLArguments (2.3.2)" + - GoogleUtilities/Environment (7.11.5): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.11.5): + - GoogleUtilities/Environment + - GoogleUtilities/UserDefaults (7.11.5): + - GoogleUtilities/Logger + - GoogleUtilitiesComponents (1.1.0): + - GoogleUtilities/Logger + - GTMSessionFetcher/Core (2.3.0) + - MauricewegnerCapacitorNavigationBar (2.0.3): + - Capacitor + - MLImage (1.0.0-beta4) + - MLKitBarcodeScanning (3.0.0): + - MLKitCommon (~> 9.0) + - MLKitVision (~> 5.0) + - MLKitCommon (9.0.0): + - GoogleDataTransport (~> 9.0) + - GoogleToolboxForMac/Logger (~> 2.1) + - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" + - "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)" + - GoogleUtilities/UserDefaults (~> 7.0) + - GoogleUtilitiesComponents (~> 1.0) + - GTMSessionFetcher/Core (< 3.0, >= 1.1) + - MLKitVision (5.0.0): + - GoogleToolboxForMac/Logger (~> 2.1) + - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" + - GTMSessionFetcher/Core (< 3.0, >= 1.1) + - MLImage (= 1.0.0-beta4) + - MLKitCommon (~> 9.0) + - nanopb (2.30909.0): + - nanopb/decode (= 2.30909.0) + - nanopb/encode (= 2.30909.0) + - nanopb/decode (2.30909.0) + - nanopb/encode (2.30909.0) + - NativeBottomSheet (0.0.1): + - Capacitor + - FloatingPanel + - PromisesObjC (2.3.1) + +DEPENDENCIES: + - "Capacitor (from `../../../node_modules/@capacitor/ios`)" + - "CapacitorApp (from `../../../node_modules/@capacitor/app`)" + - "CapacitorCordova (from `../../../node_modules/@capacitor/ios`)" + - "CapacitorDialog (from `../../../node_modules/@capacitor/dialog`)" + - "CapacitorHaptics (from `../../../node_modules/@capacitor/haptics`)" + - "CapacitorMlkitBarcodeScanning (from `../../../node_modules/@capacitor-mlkit/barcode-scanning`)" + - CapacitorPluginSafeArea (from `../../../node_modules/capacitor-plugin-safe-area`) + - CapacitorSplashScreen (from `../../plugins/capacitor-splash-screen`) + - "CapacitorStatusBar (from `../../../node_modules/@capacitor/status-bar`)" + - "CapgoCapacitorNativeBiometric (from `../../../node_modules/@capgo/capacitor-native-biometric`)" + - "MauricewegnerCapacitorNavigationBar (from `../../../node_modules/@mauricewegner/capacitor-navigation-bar`)" + - NativeBottomSheet (from `../../plugins/native-bottom-sheet`) + +SPEC REPOS: + trunk: + - FloatingPanel + - GoogleDataTransport + - GoogleMLKit + - GoogleToolboxForMac + - GoogleUtilities + - GoogleUtilitiesComponents + - GTMSessionFetcher + - MLImage + - MLKitBarcodeScanning + - MLKitCommon + - MLKitVision + - nanopb + - PromisesObjC + +EXTERNAL SOURCES: + Capacitor: + :path: "../../../node_modules/@capacitor/ios" + CapacitorApp: + :path: "../../../node_modules/@capacitor/app" + CapacitorCordova: + :path: "../../../node_modules/@capacitor/ios" + CapacitorDialog: + :path: "../../../node_modules/@capacitor/dialog" + CapacitorHaptics: + :path: "../../../node_modules/@capacitor/haptics" + CapacitorMlkitBarcodeScanning: + :path: "../../../node_modules/@capacitor-mlkit/barcode-scanning" + CapacitorPluginSafeArea: + :path: "../../../node_modules/capacitor-plugin-safe-area" + CapacitorSplashScreen: + :path: "../../plugins/capacitor-splash-screen" + CapacitorStatusBar: + :path: "../../../node_modules/@capacitor/status-bar" + CapgoCapacitorNativeBiometric: + :path: "../../../node_modules/@capgo/capacitor-native-biometric" + MauricewegnerCapacitorNavigationBar: + :path: "../../../node_modules/@mauricewegner/capacitor-navigation-bar" + NativeBottomSheet: + :path: "../../plugins/native-bottom-sheet" + +SPEC CHECKSUMS: + Capacitor: 9da0a2415e3b6098511f8b5ffdb578d91ee79f8f + CapacitorApp: 024e1b1bea5f883d79f6330d309bc441c88ad04a + CapacitorCordova: e128cc7688c070ca0bfa439898a5f609da8dbcfe + CapacitorDialog: 0f3c15dfe9414b83bc64aef4078f1b92bcfead26 + CapacitorHaptics: 1fffc1217c7e64a472d7845be50fb0c2f7d4204c + CapacitorMlkitBarcodeScanning: ea08ef246e5d3511d5a231a59fae36b16ad9acb3 + CapacitorPluginSafeArea: bfdd714827dbd89fb44fea286beec996e1b0c5c4 + CapacitorSplashScreen: 6fce4269c6f7dc7591cc28760d1f85255e5edb1b + CapacitorStatusBar: 565c0a1ebd79bb40d797606a8992b4a105885309 + CapgoCapacitorNativeBiometric: 44b0bb31118f6ed5171087a77a856a80a0cfa250 + FloatingPanel: f585be005983e66f8f4932f93ca46bf9f09dae3a + GoogleDataTransport: 54dee9d48d14580407f8f5fbf2f496e92437a2f2 + GoogleMLKit: 2bd0dc6253c4d4f227aad460f69215a504b2980e + GoogleToolboxForMac: 8bef7c7c5cf7291c687cf5354f39f9db6399ad34 + GoogleUtilities: 13e2c67ede716b8741c7989e26893d151b2b2084 + GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe + GTMSessionFetcher: 3a63d75eecd6aa32c2fc79f578064e1214dfdec2 + MauricewegnerCapacitorNavigationBar: 37f1308a961a8d8cd00f6362e30bad03a73728f7 + MLImage: 7bb7c4264164ade9bf64f679b40fb29c8f33ee9b + MLKitBarcodeScanning: 04e264482c5f3810cb89ebc134ef6b61e67db505 + MLKitCommon: c1b791c3e667091918d91bda4bba69a91011e390 + MLKitVision: 8baa5f46ee3352614169b85250574fde38c36f49 + nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 + NativeBottomSheet: edfc1f70d3517ea92e70392db8c2de5ea3e6f15e + PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 + +PODFILE CHECKSUM: f2e7c24dfc22bd949d0c502101768c1e911d066f + +COCOAPODS: 1.14.3 diff --git a/mobile/ios/App/fastlane/Appfile b/mobile/ios/App/fastlane/Appfile new file mode 100644 index 00000000..01add11a --- /dev/null +++ b/mobile/ios/App/fastlane/Appfile @@ -0,0 +1,8 @@ +app_identifier("org.mytonwallet.app") # The bundle identifier of your app +apple_id("troman.dev@icloud.com") # Your Apple Developer Portal username + +itc_team_id("126235213") # App Store Connect Team ID +team_id("Y54Z4K69Z9") # Developer Portal Team ID + +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile diff --git a/mobile/ios/App/fastlane/Fastfile b/mobile/ios/App/fastlane/Fastfile new file mode 100644 index 00000000..369a968b --- /dev/null +++ b/mobile/ios/App/fastlane/Fastfile @@ -0,0 +1,107 @@ +default_platform(:ios) + +def update_version_and_build_number + version = JSON.parse(File.read("../../../../package.json"))["version"] + production_build_number = app_store_build_number(version: version, initial_build_number: 0, live: true).to_i + beta_build_number = latest_testflight_build_number(version: version, initial_build_number: 0) + build_number = [production_build_number, beta_build_number].max() + 1 + + increment_version_number(version_number: version) + increment_build_number(skip_info_plist: true, build_number: build_number) + + return version, build_number +end + +platform :ios do + desc "Push a new beta build to TestFlight" + + $project_data = nil + $plist_data = nil + + before_all do |lane| + $project_data = File.read("../App.xcodeproj/project.pbxproj") + $plist_data = File.read("../App/Info.plist") + + app_store_connect_api_key( + key_id: "TL49GJ73DP", + issuer_id: "519080b4-bc5f-4d06-a889-a69254108348", + key_filepath: "./AuthKey.p8", + duration: 1200, + in_house: false + ) + + update_code_signing_settings( + path: "App.xcodeproj", + use_automatic_signing: false, + profile_name: "MyTonWallet Production profile", + build_configurations: ["Debug"], + sdk: "iphoneos*", + team_id: "Y54Z4K69Z9", + code_sign_identity: "iPhone Distribution" + ) + end + + lane :beta do + update_version_and_build_number + + build_app( + workspace: "App.xcworkspace", + scheme: "MyTonWallet", + export_method: "app-store", + export_options: { + provisioningProfiles: { + "org.mytonwallet.app" => "MyTonWallet Production profile", + } + } + ) + + changelog_from_git_commits(merge_commit_filtering: "exclude_merges", commits_count: 3) + upload_to_testflight(distribute_external: true, groups: "MTW external group") + end + + lane :release do + version, build_number = update_version_and_build_number + + changelog_path = "../../../../changelogs/" + version + ".txt" + if !File.exist?(changelog_path) + raise "There is no changelog for version " + version + end + + changelog = File.read(changelog_path) + + build_app( + workspace: "App.xcworkspace", + scheme: "MyTonWallet", + export_method: "app-store", + export_options: { + provisioningProfiles: { + "org.mytonwallet.app" => "MyTonWallet Production profile", + } + } + ) + + upload_to_app_store( + skip_screenshots: true, + skip_metadata: true, + precheck_include_in_app_purchases: false, + submit_for_review: true, + submission_information: { + add_id_info_uses_idfa: false + }, + release_notes: { + "default" => changelog + } + ) + end + + after_all do |lane| + File.write("../App.xcodeproj/project.pbxproj", $project_data) + File.write("../App/Info.plist", $plist_data) + end + + error do |lane, exception| + File.write("../App.xcodeproj/project.pbxproj", $project_data) + File.write("../App/Info.plist", $plist_data) + end + +end diff --git a/mobile/plugins/capacitor-splash-screen/.eslintignore b/mobile/plugins/capacitor-splash-screen/.eslintignore new file mode 100644 index 00000000..9d0b71a3 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/.eslintignore @@ -0,0 +1,2 @@ +build +dist diff --git a/mobile/plugins/capacitor-splash-screen/.gitignore b/mobile/plugins/capacitor-splash-screen/.gitignore new file mode 100644 index 00000000..d4c3b664 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/.gitignore @@ -0,0 +1,61 @@ +# node files +!dist +node_modules + +# iOS files +Pods +Podfile.lock +Build +xcuserdata + +# macOS files +.DS_Store + + + +# Based on Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore + +# Built application files +*.apk +*.ap_ + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin +gen +out + +# Gradle files +.gradle +build + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation + +# Android Studio captures folder +captures + +# IntelliJ +*.iml +.idea + +# Keystore files +# Uncomment the following line if you do not want to check your keystore files in. +#*.jks + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild diff --git a/mobile/plugins/capacitor-splash-screen/.prettierignore b/mobile/plugins/capacitor-splash-screen/.prettierignore new file mode 100644 index 00000000..9d0b71a3 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/.prettierignore @@ -0,0 +1,2 @@ +build +dist diff --git a/mobile/plugins/capacitor-splash-screen/CHANGELOG.md b/mobile/plugins/capacitor-splash-screen/CHANGELOG.md new file mode 100644 index 00000000..dd46eb11 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/CHANGELOG.md @@ -0,0 +1,488 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [5.0.6](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@5.0.5...@capacitor/splash-screen@5.0.6) (2023-07-12) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [5.0.5](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@5.0.4...@capacitor/splash-screen@5.0.5) (2023-06-29) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [5.0.4](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@5.0.3...@capacitor/splash-screen@5.0.4) (2023-06-08) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [5.0.3](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@5.0.2...@capacitor/splash-screen@5.0.3) (2023-06-08) + + +### Bug Fixes + +* **splash-screen:** Avoid crash when splash resources not found ([#1642](https://github.com/ionic-team/capacitor-plugins/issues/1642)) ([f94773c](https://github.com/ionic-team/capacitor-plugins/commit/f94773c914b5e7b2664f2c10a3ff1c4ac996b70e)) + + + + + +## [5.0.2](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@5.0.1...@capacitor/splash-screen@5.0.2) (2023-05-09) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [5.0.1](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@5.0.0...@capacitor/splash-screen@5.0.1) (2023-05-05) + + +### Bug Fixes + +* Use Capacitor 5 final ([#1574](https://github.com/ionic-team/capacitor-plugins/issues/1574)) ([139c18b](https://github.com/ionic-team/capacitor-plugins/commit/139c18b86a11d31246e952d1a74335ff8ce5dbc2)) + + + + + +# [5.0.0](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@5.0.0-beta.1...@capacitor/splash-screen@5.0.0) (2023-05-03) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +# [5.0.0-beta.1](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@5.0.0-beta.0...@capacitor/splash-screen@5.0.0-beta.1) (2023-04-21) + + +### Features + +* Update gradle to 8.0.2 and gradle plugin to 8.0.0 ([#1542](https://github.com/ionic-team/capacitor-plugins/issues/1542)) ([e7210b4](https://github.com/ionic-team/capacitor-plugins/commit/e7210b47867644f5983e37acdbf0247214ec232d)) + + + + + +# [5.0.0-beta.0](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@5.0.0-alpha.1...@capacitor/splash-screen@5.0.0-beta.0) (2023-03-31) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +# [5.0.0-alpha.1](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@4.2.0...@capacitor/splash-screen@5.0.0-alpha.1) (2023-03-16) + + +### Features + +* **android:** Removing enableJetifier ([d66f9cb](https://github.com/ionic-team/capacitor-plugins/commit/d66f9cbd9da7e3b1d8c64ca6a5b45156867d4a04)) + + + + + +# [4.2.0](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@4.1.4...@capacitor/splash-screen@4.2.0) (2023-02-22) + + +### Features + +* **splash-screen:** Add launchFadeOutDuration configuration option ([#1393](https://github.com/ionic-team/capacitor-plugins/issues/1393)) ([66929c2](https://github.com/ionic-team/capacitor-plugins/commit/66929c2177009f65758ccbacdc166ae95294ef37)) + + + + + +## [4.1.4](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@4.1.3...@capacitor/splash-screen@4.1.4) (2023-02-03) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [4.1.3](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@4.1.2...@capacitor/splash-screen@4.1.3) (2023-01-17) + + +### Bug Fixes + +* **splash-screen:** Don't show if WebView is older than supported ([#1317](https://github.com/ionic-team/capacitor-plugins/issues/1317)) ([c884c38](https://github.com/ionic-team/capacitor-plugins/commit/c884c38cb2d24105e4667e32ffb6bbe59c97b9b4)) + + + + + +## [4.1.2](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@4.1.1...@capacitor/splash-screen@4.1.2) (2022-11-16) + + +### Bug Fixes + +* **splash-screen:** Remove extension from storyboard name ([#1281](https://github.com/ionic-team/capacitor-plugins/issues/1281)) ([86ecc4f](https://github.com/ionic-team/capacitor-plugins/commit/86ecc4f0c45799b2a5a02700c18a3d5f0de00615)) + + + + + +## [4.1.1](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@4.1.0...@capacitor/splash-screen@4.1.1) (2022-10-21) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +# [4.1.0](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.2.2...@capacitor/splash-screen@4.1.0) (2022-09-29) + + + + + +## [4.0.1](https://github.com/ionic-team/capacitor-plugins/compare/4.0.0...4.0.1) (2022-07-28) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +# [4.0.0](https://github.com/ionic-team/capacitor-plugins/compare/4.0.0-beta.2...4.0.0) (2022-07-27) + + +### Features + +* **splash-screen:** Use Android 12 Splash Screen API ([#1011](https://github.com/ionic-team/capacitor-plugins/issues/1011)) ([79185ad](https://github.com/ionic-team/capacitor-plugins/commit/79185adf76bc4ff4bae1be5ec5b5881cfbe748b1)) + + + + + +# [4.0.0-beta.2](https://github.com/ionic-team/capacitor-plugins/compare/4.0.0-beta.0...4.0.0-beta.2) (2022-07-08) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +# 4.0.0-beta.0 (2022-06-27) + + +### Bug Fixes + +* **splash-screen:** avoid conditional downcast warning ([#776](https://github.com/ionic-team/capacitor-plugins/issues/776)) ([87ed912](https://github.com/ionic-team/capacitor-plugins/commit/87ed9128f43f0be498601f0ba89cadeba7a339b4)) +* **splash-screen:** pick first window when there is no key window ([#730](https://github.com/ionic-team/capacitor-plugins/issues/730)) ([0e335ad](https://github.com/ionic-team/capacitor-plugins/commit/0e335ad386da8ceadfdfaa3be982840547fc41b6)) +* **splash-screen:** Use Locale.ROOT on toLowerCase ([#813](https://github.com/ionic-team/capacitor-plugins/issues/813)) ([ecc55e1](https://github.com/ionic-team/capacitor-plugins/commit/ecc55e172acfe066977a4aac5018a55beef64f53)) +* add es2017 lib to tsconfig ([#180](https://github.com/ionic-team/capacitor-plugins/issues/180)) ([2c3776c](https://github.com/ionic-team/capacitor-plugins/commit/2c3776c38ca025c5ee965dec10ccf1cdb6c02e2f)) +* correct addListeners links ([#655](https://github.com/ionic-team/capacitor-plugins/issues/655)) ([f9871e7](https://github.com/ionic-team/capacitor-plugins/commit/f9871e7bd53478addb21155e148829f550c0e457)) +* export all TS definitions ([6cd2996](https://github.com/ionic-team/capacitor-plugins/commit/6cd299660fdeb27382ec7f45f0b3a55224cd0ad1)) +* inline source code in esm map files ([#760](https://github.com/ionic-team/capacitor-plugins/issues/760)) ([a960489](https://github.com/ionic-team/capacitor-plugins/commit/a960489a19db0182b90d187a50deff9dfbe51038)) +* remove postpublish scripts ([#656](https://github.com/ionic-team/capacitor-plugins/issues/656)) ([ed6ac49](https://github.com/ionic-team/capacitor-plugins/commit/ed6ac499ebf4a47525071ccbfc36c27503e11f60)) +* **splash-screen:** launchAutoHide not working on iOS ([#319](https://github.com/ionic-team/capacitor-plugins/issues/319)) ([2a83fcb](https://github.com/ionic-team/capacitor-plugins/commit/2a83fcb536cdfc5b601f363212353201de40ca5b)) +* **splash-screen:** Use configured storyboard instead of hardcoded value ([#548](https://github.com/ionic-team/capacitor-plugins/issues/548)) ([67dd67f](https://github.com/ionic-team/capacitor-plugins/commit/67dd67fc443ea5494e8482fd4346c5275a42841b)) +* support deprecated types from Capacitor 2 ([#139](https://github.com/ionic-team/capacitor-plugins/issues/139)) ([2d7127a](https://github.com/ionic-team/capacitor-plugins/commit/2d7127a488e26f0287951921a6db47c49d817336)) + + +### Features + +* add commonjs output format ([#179](https://github.com/ionic-team/capacitor-plugins/issues/179)) ([8e9e098](https://github.com/ionic-team/capacitor-plugins/commit/8e9e09862064b3f6771d7facbc4008e995d9b463)) +* set targetSDK default value to 31 ([#824](https://github.com/ionic-team/capacitor-plugins/issues/824)) ([3ee10de](https://github.com/ionic-team/capacitor-plugins/commit/3ee10de98067984c1a4e75295d001c5a895c47f4)) +* set targetSDK default value to 32 ([#970](https://github.com/ionic-team/capacitor-plugins/issues/970)) ([fa70d96](https://github.com/ionic-team/capacitor-plugins/commit/fa70d96f141af751aae53ceb5642c46b204f5958)) +* SplashScreen plugin ([#149](https://github.com/ionic-team/capacitor-plugins/issues/149)) ([c5f44be](https://github.com/ionic-team/capacitor-plugins/commit/c5f44bee46d06bd9a2623cd907862633ee5331eb)) +* Upgrade gradle to 7.4 ([#826](https://github.com/ionic-team/capacitor-plugins/issues/826)) ([5db0906](https://github.com/ionic-team/capacitor-plugins/commit/5db0906f6264287c4f8e69dbaecf19d4d387824b)) +* Use java 11 ([#910](https://github.com/ionic-team/capacitor-plugins/issues/910)) ([5acb2a2](https://github.com/ionic-team/capacitor-plugins/commit/5acb2a288a413492b163e4e97da46a085d9e4be0)) +* **splash-screen:** add useDialog and layoutName options for Android ([#519](https://github.com/ionic-team/capacitor-plugins/issues/519)) ([f48733f](https://github.com/ionic-team/capacitor-plugins/commit/f48733fd42a49d718a70c2fd36d28355a64b7a88)) +* **splash-screen:** Make splash work in apps that use scenes ([#631](https://github.com/ionic-team/capacitor-plugins/issues/631)) ([cf0d214](https://github.com/ionic-team/capacitor-plugins/commit/cf0d2143c225336984a6bc8fa7ef814a18b02bd1)) +* **splash-screen:** Use Launch Storyboard for splash ([#516](https://github.com/ionic-team/capacitor-plugins/issues/516)) ([0292dab](https://github.com/ionic-team/capacitor-plugins/commit/0292dab65ac9c0f81e632eaf711b13b051f4da92)) + + + + + +## [1.2.2](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.2.1...@capacitor/splash-screen@1.2.2) (2022-02-10) + + +### Bug Fixes + +* **splash-screen:** avoid conditional downcast warning ([#776](https://github.com/ionic-team/capacitor-plugins/issues/776)) ([87ed912](https://github.com/ionic-team/capacitor-plugins/commit/87ed9128f43f0be498601f0ba89cadeba7a339b4)) +* **splash-screen:** Use Locale.ROOT on toLowerCase ([#813](https://github.com/ionic-team/capacitor-plugins/issues/813)) ([ecc55e1](https://github.com/ionic-team/capacitor-plugins/commit/ecc55e172acfe066977a4aac5018a55beef64f53)) + + + + + +## [1.2.1](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.2.0...@capacitor/splash-screen@1.2.1) (2022-01-19) + + +### Bug Fixes + +* inline source code in esm map files ([#760](https://github.com/ionic-team/capacitor-plugins/issues/760)) ([a960489](https://github.com/ionic-team/capacitor-plugins/commit/a960489a19db0182b90d187a50deff9dfbe51038)) + + + + + +# [1.2.0](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.1.6...@capacitor/splash-screen@1.2.0) (2021-12-08) + + +### Bug Fixes + +* **splash-screen:** pick first window when there is no key window ([#730](https://github.com/ionic-team/capacitor-plugins/issues/730)) ([0e335ad](https://github.com/ionic-team/capacitor-plugins/commit/0e335ad386da8ceadfdfaa3be982840547fc41b6)) + + +### Features + +* **splash-screen:** Make splash work in apps that use scenes ([#631](https://github.com/ionic-team/capacitor-plugins/issues/631)) ([cf0d214](https://github.com/ionic-team/capacitor-plugins/commit/cf0d2143c225336984a6bc8fa7ef814a18b02bd1)) + + + + + +## [1.1.6](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.1.5...@capacitor/splash-screen@1.1.6) (2021-11-03) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [1.1.5](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.1.4...@capacitor/splash-screen@1.1.5) (2021-10-14) + + +### Bug Fixes + +* remove postpublish scripts ([#656](https://github.com/ionic-team/capacitor-plugins/issues/656)) ([ed6ac49](https://github.com/ionic-team/capacitor-plugins/commit/ed6ac499ebf4a47525071ccbfc36c27503e11f60)) + + + + + +## [1.1.4](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.1.3...@capacitor/splash-screen@1.1.4) (2021-10-13) + + +### Bug Fixes + +* correct addListeners links ([#655](https://github.com/ionic-team/capacitor-plugins/issues/655)) ([f9871e7](https://github.com/ionic-team/capacitor-plugins/commit/f9871e7bd53478addb21155e148829f550c0e457)) + + + + + +## [1.1.3](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.1.2...@capacitor/splash-screen@1.1.3) (2021-09-27) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [1.1.2](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.1.1...@capacitor/splash-screen@1.1.2) (2021-09-01) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [1.1.1](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.1.0...@capacitor/splash-screen@1.1.1) (2021-08-18) + + +### Bug Fixes + +* **splash-screen:** Use configured storyboard instead of hardcoded value ([#548](https://github.com/ionic-team/capacitor-plugins/issues/548)) ([67dd67f](https://github.com/ionic-team/capacitor-plugins/commit/67dd67fc443ea5494e8482fd4346c5275a42841b)) + + + + + +# [1.1.0](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.0.2...@capacitor/splash-screen@1.1.0) (2021-07-21) + + +### Features + +* **splash-screen:** add useDialog and layoutName options for Android ([#519](https://github.com/ionic-team/capacitor-plugins/issues/519)) ([f48733f](https://github.com/ionic-team/capacitor-plugins/commit/f48733fd42a49d718a70c2fd36d28355a64b7a88)) +* **splash-screen:** Use Launch Storyboard for splash ([#516](https://github.com/ionic-team/capacitor-plugins/issues/516)) ([0292dab](https://github.com/ionic-team/capacitor-plugins/commit/0292dab65ac9c0f81e632eaf711b13b051f4da92)) + + + + + +## [1.0.2](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.0.1...@capacitor/splash-screen@1.0.2) (2021-06-23) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [1.0.1](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@1.0.0...@capacitor/splash-screen@1.0.1) (2021-06-09) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +# [1.0.0](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.3.10...@capacitor/splash-screen@1.0.0) (2021-05-19) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [0.3.10](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.3.9...@capacitor/splash-screen@0.3.10) (2021-05-11) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [0.3.9](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.3.8...@capacitor/splash-screen@0.3.9) (2021-05-10) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [0.3.8](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.3.7...@capacitor/splash-screen@0.3.8) (2021-05-07) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [0.3.7](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.3.6...@capacitor/splash-screen@0.3.7) (2021-04-29) + + +### Bug Fixes + +* **splash-screen:** launchAutoHide not working on iOS ([#319](https://github.com/ionic-team/capacitor-plugins/issues/319)) ([2a83fcb](https://github.com/ionic-team/capacitor-plugins/commit/2a83fcb536cdfc5b601f363212353201de40ca5b)) + + + + + +## [0.3.6](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.3.5...@capacitor/splash-screen@0.3.6) (2021-03-10) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [0.3.5](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.3.4...@capacitor/splash-screen@0.3.5) (2021-03-02) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [0.3.4](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.3.3...@capacitor/splash-screen@0.3.4) (2021-02-27) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [0.3.3](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.3.2...@capacitor/splash-screen@0.3.3) (2021-02-10) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [0.3.2](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.3.1...@capacitor/splash-screen@0.3.2) (2021-02-05) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [0.3.1](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.3.0...@capacitor/splash-screen@0.3.1) (2021-01-26) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +# [0.3.0](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.2.0...@capacitor/splash-screen@0.3.0) (2021-01-14) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +# [0.2.0](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.1.3...@capacitor/splash-screen@0.2.0) (2021-01-13) + + +### Bug Fixes + +* add es2017 lib to tsconfig ([#180](https://github.com/ionic-team/capacitor-plugins/issues/180)) ([2c3776c](https://github.com/ionic-team/capacitor-plugins/commit/2c3776c38ca025c5ee965dec10ccf1cdb6c02e2f)) +* export all TS definitions ([6cd2996](https://github.com/ionic-team/capacitor-plugins/commit/6cd299660fdeb27382ec7f45f0b3a55224cd0ad1)) + + +### Features + +* add commonjs output format ([#179](https://github.com/ionic-team/capacitor-plugins/issues/179)) ([8e9e098](https://github.com/ionic-team/capacitor-plugins/commit/8e9e09862064b3f6771d7facbc4008e995d9b463)) + + + + + +## [0.1.3](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.1.2...@capacitor/splash-screen@0.1.3) (2021-01-13) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [0.1.2](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.1.1...@capacitor/splash-screen@0.1.2) (2021-01-08) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +## [0.1.1](https://github.com/ionic-team/capacitor-plugins/compare/@capacitor/splash-screen@0.1.0...@capacitor/splash-screen@0.1.1) (2020-12-27) + +**Note:** Version bump only for package @capacitor/splash-screen + + + + + +# 0.1.0 (2020-12-20) + + +### Bug Fixes + +* support deprecated types from Capacitor 2 ([#139](https://github.com/ionic-team/capacitor-plugins/issues/139)) ([2d7127a](https://github.com/ionic-team/capacitor-plugins/commit/2d7127a488e26f0287951921a6db47c49d817336)) + + +### Features + +* SplashScreen plugin ([#149](https://github.com/ionic-team/capacitor-plugins/issues/149)) ([c5f44be](https://github.com/ionic-team/capacitor-plugins/commit/c5f44bee46d06bd9a2623cd907862633ee5331eb)) diff --git a/mobile/plugins/capacitor-splash-screen/CapacitorSplashScreen.podspec b/mobile/plugins/capacitor-splash-screen/CapacitorSplashScreen.podspec new file mode 100644 index 00000000..a86dda7f --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/CapacitorSplashScreen.podspec @@ -0,0 +1,17 @@ +require 'json' + +package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) + +Pod::Spec.new do |s| + s.name = 'CapacitorSplashScreen' + s.version = package['version'] + s.summary = package['description'] + s.license = package['license'] + s.homepage = 'https://capacitorjs.com' + s.author = package['author'] + s.source = { :git => 'https://github.com/ionic-team/capacitor-plugins.git', :tag => package['name'] + '@' + package['version'] } + s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}', 'splash-screen/ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' + s.ios.deployment_target = '13.0' + s.dependency 'Capacitor' + s.swift_version = '5.1' +end diff --git a/mobile/plugins/capacitor-splash-screen/LICENSE b/mobile/plugins/capacitor-splash-screen/LICENSE new file mode 100644 index 00000000..6652495c --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/LICENSE @@ -0,0 +1,23 @@ +Copyright 2020-present Ionic +https://ionic.io + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/mobile/plugins/capacitor-splash-screen/README.md b/mobile/plugins/capacitor-splash-screen/README.md new file mode 100644 index 00000000..ebad66dc --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/README.md @@ -0,0 +1,262 @@ +# @capacitor/splash-screen + +The Splash Screen API provides methods for showing or hiding a Splash image. + +## Install + +```bash +npm install @capacitor/splash-screen +npx cap sync +``` + +### Android 12 Splash Screen API + +_**This only affects the launch splash screen and is not used when utilizing the programmatic `show()` method.**_ + +Capacitor 4 uses the **[Android 12 Splash Screen API](https://developer.android.com/guide/topics/ui/splash-screen)** and the `androidx.core:core-splashscreen` compatibility library to make it work on Android 11 and below. + +The compatibility library can be disabled by changing the parent of `AppTheme.NoActionBarLaunch` from `Theme.SplashScreen` to `AppTheme.NoActionBar` in `android/app/src/main/res/values/styles.xml`. +The Android 12 Splash Screen API can't be disabled on Android 12+ as it's part of the Android OS. + +```xml + +``` + +**NOTE**: On Android 12 and Android 12L devices the Splash Screen image is not showing when launched from third party launchers such as Nova Launcher, MIUI, Realme Launcher, OPPO Launcher, etc., from app info in Settings App, or from IDEs such as Android Studio. +**[Google Issue Tracker](https://issuetracker.google.com/issues/205021357)** +**[Google Issue Tracker](https://issuetracker.google.com/issues/207386164)** +Google have fixed those problems on Android 13 but they won't be backport the fixes to Android 12 and Android 12L. +Launcher related issues might get fixed by a launcher update. +If you still find issues related to the Splash Screen on Android 13, please, report them to [Google](https://issuetracker.google.com/). + +## Example + +```typescript +import { SplashScreen } from '@capacitor/splash-screen'; + +// Hide the splash (you should do this on app launch) +await SplashScreen.hide(); + +// Show the splash for an indefinite amount of time: +await SplashScreen.show({ + autoHide: false, +}); + +// Show the splash for two seconds and then automatically hide it: +await SplashScreen.show({ + showDuration: 2000, + autoHide: true, +}); +``` + +## Hiding the Splash Screen + +By default, the Splash Screen is set to automatically hide after 500 ms. + +If you want to be sure the splash screen never disappears before your app is ready, set `launchAutoHide` to `false`; the splash screen will then stay visible until manually hidden. For the best user experience, your app should call `hide()` as soon as possible. + +If, instead, you want to show the splash screen for a fixed amount of time, set `launchShowDuration` in your [Capacitor configuration file](https://capacitorjs.com/docs/config). + +## Background Color + +In certain conditions, especially if the splash screen does not fully cover the device screen, it might happen that the app screen is visible around the corners (due to transparency). Instead of showing a transparent color, you can set a `backgroundColor` to cover those areas. + +Possible values for `backgroundColor` are either `#RRGGBB` or `#RRGGBBAA`. + +## Spinner + +If you want to show a spinner on top of the splash screen, set `showSpinner` to `true` in your [Capacitor configuration file](https://capacitorjs.com/docs/config). + +You can customize the appearance of the spinner with the following configuration. + +For Android, `androidSpinnerStyle` has the following options: + +- `horizontal` +- `small` +- `large` (default) +- `inverse` +- `smallInverse` +- `largeInverse` + +For iOS, `iosSpinnerStyle` has the following options: + +- `large` (default) +- `small` + +To set the color of the spinner use `spinnerColor`, values are either `#RRGGBB` or `#RRGGBBAA`. + +## Configuration + + + + +These config values are available: + +| Prop | Type | Description | Default | Since | +| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ----- | +| **`launchShowDuration`** | number | How long to show the launch splash screen when autoHide is enabled (in ms) | 500 | 1.0.0 | +| **`launchAutoHide`** | boolean | Whether to auto hide the splash after launchShowDuration. | true | 1.0.0 | +| **`launchFadeOutDuration`** | number | Duration for the fade out animation of the launch splash screen (in ms) Only available for Android, when using the Android 12 Splash Screen API. | 200 | 4.2.0 | +| **`backgroundColor`** | string | Color of the background of the Splash Screen in hex format, #RRGGBB or #RRGGBBAA. Doesn't work if `useDialog` is true or on launch when using the Android 12 API. | | 1.0.0 | +| **`androidSplashResourceName`** | string | Name of the resource to be used as Splash Screen. Doesn't work on launch when using the Android 12 API. Only available on Android. | splash | 1.0.0 | +| **`androidScaleType`** | 'CENTER' \| 'CENTER_CROP' \| 'CENTER_INSIDE' \| 'FIT_CENTER' \| 'FIT_END' \| 'FIT_START' \| 'FIT_XY' \| 'MATRIX' | The [ImageView.ScaleType](https://developer.android.com/reference/android/widget/ImageView.ScaleType) used to scale the Splash Screen image. Doesn't work if `useDialog` is true or on launch when using the Android 12 API. Only available on Android. | FIT_XY | 1.0.0 | +| **`showSpinner`** | boolean | Show a loading spinner on the Splash Screen. Doesn't work if `useDialog` is true or on launch when using the Android 12 API. | | 1.0.0 | +| **`androidSpinnerStyle`** | 'horizontal' \| 'small' \| 'large' \| 'inverse' \| 'smallInverse' \| 'largeInverse' | Style of the Android spinner. Doesn't work if `useDialog` is true or on launch when using the Android 12 API. | large | 1.0.0 | +| **`iosSpinnerStyle`** | 'small' \| 'large' | Style of the iOS spinner. Doesn't work if `useDialog` is true. Only available on iOS. | large | 1.0.0 | +| **`spinnerColor`** | string | Color of the spinner in hex format, #RRGGBB or #RRGGBBAA. Doesn't work if `useDialog` is true or on launch when using the Android 12 API. | | 1.0.0 | +| **`splashFullScreen`** | boolean | Hide the status bar on the Splash Screen. Doesn't work on launch when using the Android 12 API. Only available on Android. | | 1.0.0 | +| **`splashImmersive`** | boolean | Hide the status bar and the software navigation buttons on the Splash Screen. Doesn't work on launch when using the Android 12 API. Only available on Android. | | 1.0.0 | +| **`layoutName`** | string | If `useDialog` is set to true, configure the Dialog layout. If `useDialog` is not set or false, use a layout instead of the ImageView. Doesn't work on launch when using the Android 12 API. Only available on Android. | | 1.1.0 | +| **`useDialog`** | boolean | Use a Dialog instead of an ImageView. If `layoutName` is not configured, it will use a layout that uses the splash image as background. Doesn't work on launch when using the Android 12 API. Only available on Android. | | 1.1.0 | + +### Examples + +In `capacitor.config.json`: + +```json +{ + "plugins": { + "SplashScreen": { + "launchShowDuration": 3000, + "launchAutoHide": true, + "launchFadeOutDuration": 3000, + "backgroundColor": "#ffffffff", + "androidSplashResourceName": "splash", + "androidScaleType": "CENTER_CROP", + "showSpinner": true, + "androidSpinnerStyle": "large", + "iosSpinnerStyle": "small", + "spinnerColor": "#999999", + "splashFullScreen": true, + "splashImmersive": true, + "layoutName": "launch_screen", + "useDialog": true + } + } +} +``` + +In `capacitor.config.ts`: + +```ts +/// + +import { CapacitorConfig } from '@capacitor/cli'; + +const config: CapacitorConfig = { + plugins: { + SplashScreen: { + launchShowDuration: 3000, + launchAutoHide: true, + launchFadeOutDuration: 3000, + backgroundColor: "#ffffffff", + androidSplashResourceName: "splash", + androidScaleType: "CENTER_CROP", + showSpinner: true, + androidSpinnerStyle: "large", + iosSpinnerStyle: "small", + spinnerColor: "#999999", + splashFullScreen: true, + splashImmersive: true, + layoutName: "launch_screen", + useDialog: true, + }, + }, +}; + +export default config; +``` + + + +### Android + +To use splash screen images named something other than `splash.png`, set `androidSplashResourceName` to the new resource name. Additionally, in `android/app/src/main/res/values/styles.xml`, change the resource name in the following block: + +```xml + +``` + +### Variables + +This plugin will use the following project variables (defined in your app's `variables.gradle` file): + +- `coreSplashScreenVersion` version of `androidx.core:core-splashscreen` (default: `1.0.1`) + +## Example Guides + +[Adding Your Own Icons and Splash Screen Images ›](https://www.joshmorony.com/adding-icons-splash-screens-launch-images-to-capacitor-projects/) + +[Creating a Dynamic/Adaptable Splash Screen for Capacitor (Android) ›](https://www.joshmorony.com/creating-a-dynamic-universal-splash-screen-for-capacitor-android/) + +## API + + + +* [`show(...)`](#show) +* [`hide(...)`](#hide) +* [Interfaces](#interfaces) + + + + + + +### show(...) + +```typescript +show(options?: ShowOptions | undefined) => Promise +``` + +Show the splash screen + +| Param | Type | +| ------------- | --------------------------------------------------- | +| **`options`** | ShowOptions | + +**Since:** 1.0.0 + +-------------------- + + +### hide(...) + +```typescript +hide(options?: HideOptions | undefined) => Promise +``` + +Hide the splash screen + +| Param | Type | +| ------------- | --------------------------------------------------- | +| **`options`** | HideOptions | + +**Since:** 1.0.0 + +-------------------- + + +### Interfaces + + +#### ShowOptions + +| Prop | Type | Description | Default | Since | +| --------------------- | -------------------- | ------------------------------------------------------------------- | ----------------- | ----- | +| **`autoHide`** | boolean | Whether to auto hide the splash after showDuration | | 1.0.0 | +| **`fadeInDuration`** | number | How long (in ms) to fade in. | 200 | 1.0.0 | +| **`fadeOutDuration`** | number | How long (in ms) to fade out. | 200 | 1.0.0 | +| **`showDuration`** | number | How long to show the splash screen when autoHide is enabled (in ms) | 3000 | 1.0.0 | + + +#### HideOptions + +| Prop | Type | Description | Default | Since | +| --------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ----- | +| **`fadeOutDuration`** | number | How long (in ms) to fade out. On Android, if using the Android 12 Splash Screen API, it's not being used. Use launchFadeOutDuration configuration option instead. | 200 | 1.0.0 | + + diff --git a/mobile/plugins/capacitor-splash-screen/android/.gitignore b/mobile/plugins/capacitor-splash-screen/android/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/.gitignore @@ -0,0 +1 @@ +/build diff --git a/mobile/plugins/capacitor-splash-screen/android/build.gradle b/mobile/plugins/capacitor-splash-screen/android/build.gradle new file mode 100644 index 00000000..91ea7a34 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/build.gradle @@ -0,0 +1,81 @@ +ext { + capacitorVersion = System.getenv('CAPACITOR_VERSION') + junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2' + androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1' + androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.5' + androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.5.1' + coreSplashScreenVersion = project.hasProperty('coreSplashScreenVersion') ? rootProject.ext.coreSplashScreenVersion : '1.0.1' +} + +buildscript { + repositories { + google() + mavenCentral() + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath 'com.android.tools.build:gradle:8.1.1' + if (System.getenv("CAP_PLUGIN_PUBLISH") == "true") { + classpath 'io.github.gradle-nexus:publish-plugin:1.3.0' + } + } +} + +apply plugin: 'com.android.library' +if (System.getenv("CAP_PLUGIN_PUBLISH") == "true") { + apply plugin: 'io.github.gradle-nexus.publish-plugin' + apply from: file('../../scripts/android/publish-root.gradle') + apply from: file('../../scripts/android/publish-module.gradle') +} + +android { + namespace "com.capacitorjs.plugins.splashscreen" + compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34 + defaultConfig { + minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + publishing { + singleVariant("release") + } +} + +repositories { + google() + mavenCentral() +} + + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + if (System.getenv("CAP_PLUGIN_PUBLISH") == "true") { + implementation "com.capacitorjs:core:$capacitorVersion" + } else { + implementation project(':capacitor-android') + } + + implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" + testImplementation "junit:junit:$junitVersion" + androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" + implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" +} diff --git a/mobile/plugins/capacitor-splash-screen/android/gradle.properties b/mobile/plugins/capacitor-splash-screen/android/gradle.properties new file mode 100644 index 00000000..2e87c52f --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/gradle.properties @@ -0,0 +1,22 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true diff --git a/mobile/plugins/capacitor-splash-screen/android/gradle/wrapper/gradle-wrapper.jar b/mobile/plugins/capacitor-splash-screen/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..c1962a79e29d3e0ab67b14947c167a862655af9b GIT binary patch literal 62076 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 literal 0 HcmV?d00001 diff --git a/mobile/plugins/capacitor-splash-screen/android/gradle/wrapper/gradle-wrapper.properties b/mobile/plugins/capacitor-splash-screen/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..8707e8b5 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/mobile/plugins/capacitor-splash-screen/android/gradlew b/mobile/plugins/capacitor-splash-screen/android/gradlew new file mode 100755 index 00000000..aeb74cbb --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/gradlew @@ -0,0 +1,245 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/mobile/plugins/capacitor-splash-screen/android/gradlew.bat b/mobile/plugins/capacitor-splash-screen/android/gradlew.bat new file mode 100644 index 00000000..93e3f59f --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mobile/plugins/capacitor-splash-screen/android/proguard-rules.pro b/mobile/plugins/capacitor-splash-screen/android/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/plugins/capacitor-splash-screen/android/settings.gradle b/mobile/plugins/capacitor-splash-screen/android/settings.gradle new file mode 100644 index 00000000..1e5b8431 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/settings.gradle @@ -0,0 +1,2 @@ +include ':capacitor-android' +project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') \ No newline at end of file diff --git a/mobile/plugins/capacitor-splash-screen/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java b/mobile/plugins/capacitor-splash-screen/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java new file mode 100644 index 00000000..58020e16 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.getcapacitor.android; + +import static org.junit.Assert.*; + +import android.content.Context; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("com.getcapacitor.android", appContext.getPackageName()); + } +} diff --git a/mobile/plugins/capacitor-splash-screen/android/src/main/AndroidManifest.xml b/mobile/plugins/capacitor-splash-screen/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a2f47b60 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashListener.java b/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashListener.java new file mode 100644 index 00000000..9271b679 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashListener.java @@ -0,0 +1,6 @@ +package com.capacitorjs.plugins.splashscreen; + +public interface SplashListener { + void completed(); + void error(); +} diff --git a/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreen.java b/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreen.java new file mode 100644 index 00000000..d2e14848 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreen.java @@ -0,0 +1,694 @@ +package com.capacitorjs.plugins.splashscreen; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.os.Build; +import android.os.Handler; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnPreDrawListener; +import android.view.Window; +import android.view.WindowInsetsController; +import android.view.WindowManager; +import android.view.animation.LinearInterpolator; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsCompat; +import com.getcapacitor.Logger; + +/** + * A Splash Screen service for showing and hiding a splash screen in the app. + */ +public class SplashScreen { + + private Dialog dialog; + private View splashImage; + private ProgressBar spinnerBar; + private WindowManager windowManager; + private boolean isVisible = false; + private boolean isHiding = false; + private Context context; + private View content; + private SplashScreenConfig config; + private OnPreDrawListener onPreDrawListener; + + SplashScreen(Context context, SplashScreenConfig config) { + this.context = context; + this.config = config; + } + + /** + * Show the splash screen on launch without fading in + * + * @param activity + */ + public void showOnLaunch(final AppCompatActivity activity) { + if (config.getLaunchShowDuration() == 0) { + return; + } + SplashScreenSettings settings = new SplashScreenSettings(); + settings.setShowDuration(config.getLaunchShowDuration()); + settings.setAutoHide(config.isLaunchAutoHide()); + + // Method can fail if styles are incorrectly set... + // If it fails, log error & fallback to old method + try { + showWithAndroid12API(activity, settings); + return; + } catch (Exception e) { + Logger.warn("Android 12 Splash API failed... using previous method."); + this.onPreDrawListener = null; + } + + settings.setFadeInDuration(config.getLaunchFadeInDuration()); + if (config.isUsingDialog()) { + showDialog(activity, settings, null, true); + } else { + show(activity, settings, null, true); + } + } + + /** + * Show the Splash Screen using the Android 12 API (31+) + * Uses Compat Library for backwards compatibility + * + * @param activity + * @param settings Settings used to show the Splash Screen + */ + private void showWithAndroid12API(final AppCompatActivity activity, final SplashScreenSettings settings) { + if (activity == null || activity.isFinishing()) return; + + activity.runOnUiThread( + () -> { + androidx.core.splashscreen.SplashScreen windowSplashScreen = androidx.core.splashscreen.SplashScreen.installSplashScreen( + activity + ); + windowSplashScreen.setKeepOnScreenCondition(() -> isVisible || isHiding); + + if (config.getLaunchFadeOutDuration() > 0) { + // Set Fade Out Animation + windowSplashScreen.setOnExitAnimationListener( + windowSplashScreenView -> { + final ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat( + windowSplashScreenView.getView(), + View.ALPHA, + 1f, + 0f + ); + fadeAnimator.setInterpolator(new LinearInterpolator()); + fadeAnimator.setDuration(config.getLaunchFadeOutDuration()); + + fadeAnimator.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + isHiding = false; + windowSplashScreenView.remove(); + } + } + ); + + fadeAnimator.start(); + + isHiding = true; + isVisible = false; + } + ); + } else { + windowSplashScreen.setOnExitAnimationListener( + windowSplashScreenView -> { + isHiding = false; + isVisible = false; + windowSplashScreenView.remove(); + } + ); + } + + // Set Pre Draw Listener & Delay Drawing Until Duration Elapses + content = activity.findViewById(android.R.id.content); + + this.onPreDrawListener = + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + // Start Timer On First Run + if (!isVisible && !isHiding) { + isVisible = true; + + new Handler(context.getMainLooper()) + .postDelayed( + () -> { + // Splash screen is done... start drawing content. + if (settings.isAutoHide()) { + isVisible = false; + onPreDrawListener = null; + content.getViewTreeObserver().removeOnPreDrawListener(this); + } + }, + settings.getShowDuration() + ); + } + + // Not ready to dismiss splash screen + return false; + } + }; + + content.getViewTreeObserver().addOnPreDrawListener(this.onPreDrawListener); + } + ); + } + + /** + * Show the Splash Screen + * + * @param activity + * @param settings Settings used to show the Splash Screen + * @param splashListener A listener to handle the finish of the animation (if any) + */ + public void show(final AppCompatActivity activity, final SplashScreenSettings settings, final SplashListener splashListener) { + if (config.isUsingDialog()) { + showDialog(activity, settings, splashListener, false); + } else { + show(activity, settings, splashListener, false); + } + } + + private void showDialog( + final AppCompatActivity activity, + final SplashScreenSettings settings, + final SplashListener splashListener, + final boolean isLaunchSplash + ) { + if (activity == null || activity.isFinishing()) return; + + if (isVisible) { + splashListener.completed(); + return; + } + + activity.runOnUiThread( + () -> { + if (config.isImmersive()) { + dialog = new Dialog(activity, R.style.capacitor_immersive_style); + } else if (config.isFullScreen()) { + dialog = new Dialog(activity, R.style.capacitor_full_screen_style); + } else { + dialog = new Dialog(activity, R.style.capacitor_default_style); + } + int splashId = 0; + if (config.getLayoutName() != null) { + splashId = context.getResources().getIdentifier(config.getLayoutName(), "layout", context.getPackageName()); + if (splashId == 0) { + Logger.warn("Layout not found, using default"); + } + } + if (splashId != 0) { + dialog.setContentView(splashId); + } else { + Drawable splash = getSplashDrawable(); + LinearLayout parent = new LinearLayout(context); + parent.setLayoutParams( + new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) + ); + parent.setOrientation(LinearLayout.VERTICAL); + if (splash != null) { + parent.setBackground(splash); + } + dialog.setContentView(parent); + } + + dialog.setCancelable(false); + if (!dialog.isShowing()) { + dialog.show(); + } + isVisible = true; + + if (settings.isAutoHide()) { + new Handler(context.getMainLooper()) + .postDelayed( + () -> { + hideDialog(activity, isLaunchSplash); + + if (splashListener != null) { + splashListener.completed(); + } + }, + settings.getShowDuration() + ); + } else { + // If no autoHide, call complete + if (splashListener != null) { + splashListener.completed(); + } + } + } + ); + } + + /** + * Hide the Splash Screen + * + * @param settings Settings used to hide the Splash Screen + */ + public void hide(SplashScreenSettings settings) { + hide(settings.getFadeOutDuration(), false); + } + + /** + * Hide the Splash Screen when showing it as a dialog + * + * @param activity the activity showing the dialog + */ + public void hideDialog(final AppCompatActivity activity) { + hideDialog(activity, false); + } + + public void onPause() { + tearDown(true); + } + + public void onDestroy() { + tearDown(true); + } + + private void buildViews() { + if (splashImage == null) { + int splashId = 0; + Drawable splash; + + if (config.getLayoutName() != null) { + splashId = context.getResources().getIdentifier(config.getLayoutName(), "layout", context.getPackageName()); + if (splashId == 0) { + Logger.warn("Layout not found, defaulting to ImageView"); + } + } + + if (splashId != 0) { + Activity activity = (Activity) context; + LayoutInflater inflator = activity.getLayoutInflater(); + ViewGroup root = new FrameLayout(context); + root.setLayoutParams( + new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + ); + splashImage = inflator.inflate(splashId, root, false); + } else { + splash = getSplashDrawable(); + if (splash != null) { + if (splash instanceof Animatable) { + ((Animatable) splash).start(); + } + + if (splash instanceof LayerDrawable) { + LayerDrawable layeredSplash = (LayerDrawable) splash; + + for (int i = 0; i < layeredSplash.getNumberOfLayers(); i++) { + Drawable layerDrawable = layeredSplash.getDrawable(i); + + if (layerDrawable instanceof Animatable) { + ((Animatable) layerDrawable).start(); + } + } + } + + splashImage = new ImageView(context); + // Stops flickers dead in their tracks + // https://stackoverflow.com/a/21847579/32140 + ImageView imageView = (ImageView) splashImage; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + imageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + } else { + legacyStopFlickers(imageView); + } + imageView.setScaleType(config.getScaleType()); + imageView.setImageDrawable(splash); + } else { + return; + } + } + + splashImage.setFitsSystemWindows(true); + + if (config.getBackgroundColor() != null) { + splashImage.setBackgroundColor(config.getBackgroundColor()); + } + } + + if (spinnerBar == null) { + if (config.getSpinnerStyle() != null) { + int spinnerBarStyle = config.getSpinnerStyle(); + spinnerBar = new ProgressBar(context, null, spinnerBarStyle); + } else { + spinnerBar = new ProgressBar(context); + } + spinnerBar.setIndeterminate(true); + + Integer spinnerBarColor = config.getSpinnerColor(); + if (spinnerBarColor != null) { + int[][] states = new int[][] { + new int[] { android.R.attr.state_enabled }, // enabled + new int[] { -android.R.attr.state_enabled }, // disabled + new int[] { -android.R.attr.state_checked }, // unchecked + new int[] { android.R.attr.state_pressed } // pressed + }; + int[] colors = new int[] { spinnerBarColor, spinnerBarColor, spinnerBarColor, spinnerBarColor }; + ColorStateList colorStateList = new ColorStateList(states, colors); + spinnerBar.setIndeterminateTintList(colorStateList); + } + } + } + + @SuppressWarnings("deprecation") + private void legacyStopFlickers(ImageView imageView) { + imageView.setDrawingCacheEnabled(true); + } + + private Drawable getSplashDrawable() { + int splashId = context.getResources().getIdentifier(config.getResourceName(), "drawable", context.getPackageName()); + try { + Drawable drawable = context.getResources().getDrawable(splashId, context.getTheme()); + return drawable; + } catch (Resources.NotFoundException ex) { + Logger.warn("No splash screen found, not displaying"); + return null; + } + } + + private void show( + final AppCompatActivity activity, + final SplashScreenSettings settings, + final SplashListener splashListener, + final boolean isLaunchSplash + ) { + windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); + + if (activity.isFinishing()) { + return; + } + + buildViews(); + + if (isVisible) { + splashListener.completed(); + return; + } + + final Animator.AnimatorListener listener = new Animator.AnimatorListener() { + @Override + public void onAnimationEnd(Animator animator) { + isVisible = true; + + if (settings.isAutoHide()) { + new Handler(context.getMainLooper()) + .postDelayed( + () -> { + hide(settings.getFadeOutDuration(), isLaunchSplash); + + if (splashListener != null) { + splashListener.completed(); + } + }, + settings.getShowDuration() + ); + } else { + // If no autoHide, call complete + if (splashListener != null) { + splashListener.completed(); + } + } + } + + @Override + public void onAnimationCancel(Animator animator) {} + + @Override + public void onAnimationRepeat(Animator animator) {} + + @Override + public void onAnimationStart(Animator animator) {} + }; + + Handler mainHandler = new Handler(context.getMainLooper()); + + mainHandler.post( + () -> { + WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + params.gravity = Gravity.CENTER; + params.flags = activity.getWindow().getAttributes().flags; + + // Required to enable the view to actually fade + params.format = PixelFormat.TRANSLUCENT; + + try { + windowManager.addView(splashImage, params); + } catch (IllegalStateException | IllegalArgumentException ex) { + Logger.debug("Could not add splash view"); + return; + } + + if (config.isImmersive()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + activity.runOnUiThread( + () -> { + Window window = activity.getWindow(); + WindowCompat.setDecorFitsSystemWindows(window, false); + WindowInsetsController controller = splashImage.getWindowInsetsController(); + controller.hide(WindowInsetsCompat.Type.systemBars()); + controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + } + ); + } else { + legacyImmersive(); + } + } else if (config.isFullScreen()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + activity.runOnUiThread( + () -> { + Window window = activity.getWindow(); + WindowCompat.setDecorFitsSystemWindows(window, false); + WindowInsetsController controller = splashImage.getWindowInsetsController(); + controller.hide(WindowInsetsCompat.Type.statusBars()); + } + ); + } else { + legacyFullscreen(); + } + } + + splashImage.setAlpha(0f); + + splashImage + .animate() + .alpha(1f) + .setInterpolator(new LinearInterpolator()) + .setDuration(settings.getFadeInDuration()) + .setListener(listener) + .start(); + + splashImage.setVisibility(View.VISIBLE); + + if (spinnerBar != null) { + spinnerBar.setVisibility(View.INVISIBLE); + + if (spinnerBar.getParent() != null) { + windowManager.removeView(spinnerBar); + } + + params.height = WindowManager.LayoutParams.WRAP_CONTENT; + params.width = WindowManager.LayoutParams.WRAP_CONTENT; + + windowManager.addView(spinnerBar, params); + + if (config.isShowSpinner()) { + spinnerBar.setAlpha(0f); + + spinnerBar + .animate() + .alpha(1f) + .setInterpolator(new LinearInterpolator()) + .setDuration(settings.getFadeInDuration()) + .start(); + + spinnerBar.setVisibility(View.VISIBLE); + } + } + } + ); + } + + @SuppressWarnings("deprecation") + private void legacyImmersive() { + final int flags = + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + splashImage.setSystemUiVisibility(flags); + } + + @SuppressWarnings("deprecation") + private void legacyFullscreen() { + splashImage.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN); + } + + private void hide(final int fadeOutDuration, boolean isLaunchSplash) { + // Warn the user if the splash was hidden automatically, which means they could be experiencing an app + // that feels slower than it actually is. + if (isLaunchSplash && isVisible) { + Logger.debug( + "SplashScreen was automatically hidden after the launch timeout. " + + "You should call `SplashScreen.hide()` as soon as your web app is loaded (or increase the timeout)." + + "Read more at https://capacitorjs.com/docs/apis/splash-screen#hiding-the-splash-screen" + ); + } + + if (isHiding) { + return; + } + + // Hide with Android 12 API + if (null != this.onPreDrawListener) { + if (fadeOutDuration != 200) { + Logger.warn( + "fadeOutDuration parameter doesn't work on initial splash screen, use launchFadeOutDuration configuration option" + ); + } + this.isVisible = false; + if (null != content) { + content.getViewTreeObserver().removeOnPreDrawListener(this.onPreDrawListener); + } + this.onPreDrawListener = null; + return; + } + + if (splashImage == null || splashImage.getParent() == null) { + return; + } + + isHiding = true; + + final Animator.AnimatorListener listener = new Animator.AnimatorListener() { + @Override + public void onAnimationEnd(Animator animator) { + tearDown(false); + } + + @Override + public void onAnimationCancel(Animator animator) { + tearDown(false); + } + + @Override + public void onAnimationStart(Animator animator) {} + + @Override + public void onAnimationRepeat(Animator animator) {} + }; + + Handler mainHandler = new Handler(context.getMainLooper()); + + mainHandler.post( + () -> { + if (spinnerBar != null) { + spinnerBar.setAlpha(1f); + + spinnerBar.animate().alpha(0).setInterpolator(new LinearInterpolator()).setDuration(fadeOutDuration).start(); + } + + splashImage.setAlpha(1f); + + splashImage + .animate() + .alpha(0) + .setInterpolator(new LinearInterpolator()) + .setDuration(fadeOutDuration) + .setListener(listener) + .start(); + } + ); + } + + private void hideDialog(final AppCompatActivity activity, boolean isLaunchSplash) { + // Warn the user if the splash was hidden automatically, which means they could be experiencing an app + // that feels slower than it actually is. + if (isLaunchSplash && isVisible) { + Logger.debug( + "SplashScreen was automatically hidden after the launch timeout. " + + "You should call `SplashScreen.hide()` as soon as your web app is loaded (or increase the timeout)." + + "Read more at https://capacitorjs.com/docs/apis/splash-screen#hiding-the-splash-screen" + ); + } + + if (isHiding) { + return; + } + + // Hide with Android 12 API + if (null != this.onPreDrawListener) { + this.isVisible = false; + if (null != content) { + content.getViewTreeObserver().removeOnPreDrawListener(this.onPreDrawListener); + } + this.onPreDrawListener = null; + return; + } + + isHiding = true; + + activity.runOnUiThread( + () -> { + if (dialog != null && dialog.isShowing()) { + if (!activity.isFinishing() && !activity.isDestroyed()) { + dialog.dismiss(); + } + dialog = null; + isHiding = false; + isVisible = false; + } + } + ); + } + + private void tearDown(boolean removeSpinner) { + if (spinnerBar != null && spinnerBar.getParent() != null) { + spinnerBar.setVisibility(View.INVISIBLE); + + if (removeSpinner) { + windowManager.removeView(spinnerBar); + } + } + + if (splashImage != null && splashImage.getParent() != null) { + splashImage.setVisibility(View.INVISIBLE); + + windowManager.removeView(splashImage); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && config.isFullScreen() || config.isImmersive()) { + // Exit fullscreen mode + Window window = ((Activity) context).getWindow(); + WindowCompat.setDecorFitsSystemWindows(window, true); + } + isHiding = false; + isVisible = false; + } +} diff --git a/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenConfig.java b/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenConfig.java new file mode 100644 index 00000000..a8dd69dd --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenConfig.java @@ -0,0 +1,129 @@ +package com.capacitorjs.plugins.splashscreen; + +import android.widget.ImageView.ScaleType; + +public class SplashScreenConfig { + + private Integer backgroundColor; + private Integer spinnerStyle; + private Integer spinnerColor; + private boolean showSpinner = false; + private Integer launchShowDuration = 500; + private boolean launchAutoHide = true; + private Integer launchFadeInDuration = 0; + private Integer launchFadeOutDuration = 200; + private String resourceName = "splash"; + private boolean immersive = false; + private boolean fullScreen = false; + private ScaleType scaleType = ScaleType.FIT_XY; + private boolean usingDialog = false; + private String layoutName; + + public Integer getBackgroundColor() { + return backgroundColor; + } + + public void setBackgroundColor(Integer backgroundColor) { + this.backgroundColor = backgroundColor; + } + + public Integer getSpinnerStyle() { + return spinnerStyle; + } + + public void setSpinnerStyle(Integer spinnerStyle) { + this.spinnerStyle = spinnerStyle; + } + + public Integer getSpinnerColor() { + return spinnerColor; + } + + public void setSpinnerColor(Integer spinnerColor) { + this.spinnerColor = spinnerColor; + } + + public boolean isShowSpinner() { + return showSpinner; + } + + public void setShowSpinner(boolean showSpinner) { + this.showSpinner = showSpinner; + } + + public Integer getLaunchShowDuration() { + return launchShowDuration; + } + + public void setLaunchShowDuration(Integer launchShowDuration) { + this.launchShowDuration = launchShowDuration; + } + + public boolean isLaunchAutoHide() { + return launchAutoHide; + } + + public void setLaunchAutoHide(boolean launchAutoHide) { + this.launchAutoHide = launchAutoHide; + } + + public Integer getLaunchFadeInDuration() { + return launchFadeInDuration; + } + + public String getResourceName() { + return resourceName; + } + + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + + public boolean isImmersive() { + return immersive; + } + + public void setImmersive(boolean immersive) { + this.immersive = immersive; + } + + public boolean isFullScreen() { + return fullScreen; + } + + public void setFullScreen(boolean fullScreen) { + this.fullScreen = fullScreen; + } + + public ScaleType getScaleType() { + return scaleType; + } + + public void setScaleType(ScaleType scaleType) { + this.scaleType = scaleType; + } + + public boolean isUsingDialog() { + return usingDialog; + } + + public void setUsingDialog(boolean usingDialog) { + this.usingDialog = usingDialog; + } + + public String getLayoutName() { + return layoutName; + } + + public void setLayoutName(String layoutName) { + this.layoutName = layoutName; + } + + public Integer getLaunchFadeOutDuration() { + return launchFadeOutDuration; + } + + public void setLaunchFadeOutDuration(Integer launchFadeOutDuration) { + this.launchFadeOutDuration = launchFadeOutDuration; + } +} diff --git a/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenPlugin.java b/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenPlugin.java new file mode 100644 index 00000000..7d803501 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenPlugin.java @@ -0,0 +1,166 @@ +package com.capacitorjs.plugins.splashscreen; + +import android.widget.ImageView; +import com.getcapacitor.Logger; +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; +import com.getcapacitor.util.WebColor; +import java.util.Locale; + +@CapacitorPlugin(name = "SplashScreen") +public class SplashScreenPlugin extends Plugin { + + private SplashScreen splashScreen; + private SplashScreenConfig config; + + public void load() { + config = getSplashScreenConfig(); + splashScreen = new SplashScreen(getContext(), config); + if (!bridge.isMinimumWebViewInstalled() && bridge.getConfig().getErrorPath() != null && !config.isLaunchAutoHide()) { + return; + } else { + splashScreen.showOnLaunch(getActivity()); + } + } + + @PluginMethod + public void show(final PluginCall call) { + splashScreen.show( + getActivity(), + getSettings(call), + new SplashListener() { + @Override + public void completed() { + call.resolve(); + } + + @Override + public void error() { + call.reject("An error occurred while showing splash"); + } + } + ); + } + + @PluginMethod + public void hide(PluginCall call) { + if (config.isUsingDialog()) { + splashScreen.hideDialog(getActivity()); + } else { + splashScreen.hide(getSettings(call)); + } + call.resolve(); + } + + @Override + protected void handleOnPause() { + splashScreen.onPause(); + } + + @Override + protected void handleOnDestroy() { + splashScreen.onDestroy(); + } + + private SplashScreenSettings getSettings(PluginCall call) { + SplashScreenSettings settings = new SplashScreenSettings(); + if (call.getInt("showDuration") != null) { + settings.setShowDuration(call.getInt("showDuration")); + } + if (call.getInt("fadeInDuration") != null) { + settings.setFadeInDuration(call.getInt("fadeInDuration")); + } + if (call.getInt("fadeOutDuration") != null) { + settings.setFadeOutDuration(call.getInt("fadeOutDuration")); + } + if (call.getBoolean("autoHide") != null) { + settings.setAutoHide(call.getBoolean("autoHide")); + } + return settings; + } + + private SplashScreenConfig getSplashScreenConfig() { + SplashScreenConfig config = new SplashScreenConfig(); + String backgroundColor = getConfig().getString("backgroundColor"); + if (backgroundColor != null) { + try { + config.setBackgroundColor(WebColor.parseColor(backgroundColor)); + } catch (IllegalArgumentException ex) { + Logger.debug("Background color not applied"); + } + } + Integer duration = getConfig().getInt("launchShowDuration", config.getLaunchShowDuration()); + config.setLaunchShowDuration(duration); + Integer fadeOutDuration = getConfig().getInt("launchFadeOutDuration", config.getLaunchFadeOutDuration()); + config.setLaunchFadeOutDuration(fadeOutDuration); + Boolean autohide = getConfig().getBoolean("launchAutoHide", config.isLaunchAutoHide()); + config.setLaunchAutoHide(autohide); + if (getConfig().getString("androidSplashResourceName") != null) { + config.setResourceName(getConfig().getString("androidSplashResourceName")); + } + Boolean immersive = getConfig().getBoolean("splashImmersive", config.isImmersive()); + config.setImmersive(immersive); + + Boolean fullScreen = getConfig().getBoolean("splashFullScreen", config.isFullScreen()); + config.setFullScreen(fullScreen); + + String spinnerStyle = getConfig().getString("androidSpinnerStyle"); + if (spinnerStyle != null) { + int spinnerBarStyle = android.R.attr.progressBarStyleLarge; + + switch (spinnerStyle.toLowerCase(Locale.ROOT)) { + case "horizontal": + spinnerBarStyle = android.R.attr.progressBarStyleHorizontal; + break; + case "small": + spinnerBarStyle = android.R.attr.progressBarStyleSmall; + break; + case "large": + spinnerBarStyle = android.R.attr.progressBarStyleLarge; + break; + case "inverse": + spinnerBarStyle = android.R.attr.progressBarStyleInverse; + break; + case "smallinverse": + spinnerBarStyle = android.R.attr.progressBarStyleSmallInverse; + break; + case "largeinverse": + spinnerBarStyle = android.R.attr.progressBarStyleLargeInverse; + break; + } + config.setSpinnerStyle(spinnerBarStyle); + } + String spinnerColor = getConfig().getString("spinnerColor"); + if (spinnerColor != null) { + try { + config.setSpinnerColor(WebColor.parseColor(spinnerColor)); + } catch (IllegalArgumentException ex) { + Logger.debug("Spinner color not applied"); + } + } + String scaleTypeName = getConfig().getString("androidScaleType"); + if (scaleTypeName != null) { + ImageView.ScaleType scaleType = null; + try { + scaleType = ImageView.ScaleType.valueOf(scaleTypeName); + } catch (IllegalArgumentException ex) { + scaleType = ImageView.ScaleType.FIT_XY; + } + config.setScaleType(scaleType); + } + + Boolean showSpinner = getConfig().getBoolean("showSpinner", config.isShowSpinner()); + config.setShowSpinner(showSpinner); + + Boolean useDialog = getConfig().getBoolean("useDialog", config.isUsingDialog()); + config.setUsingDialog(useDialog); + + if (getConfig().getString("layoutName") != null) { + config.setLayoutName(getConfig().getString("layoutName")); + } + + return config; + } +} diff --git a/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenSettings.java b/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenSettings.java new file mode 100644 index 00000000..ca35dad7 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/src/main/java/com/capacitorjs/plugins/splashscreen/SplashScreenSettings.java @@ -0,0 +1,41 @@ +package com.capacitorjs.plugins.splashscreen; + +public class SplashScreenSettings { + + private Integer showDuration = 3000; + private Integer fadeInDuration = 200; + private Integer fadeOutDuration = 200; + private boolean autoHide = true; + + public Integer getShowDuration() { + return showDuration; + } + + public void setShowDuration(Integer showDuration) { + this.showDuration = showDuration; + } + + public Integer getFadeInDuration() { + return fadeInDuration; + } + + public void setFadeInDuration(Integer fadeInDuration) { + this.fadeInDuration = fadeInDuration; + } + + public Integer getFadeOutDuration() { + return fadeOutDuration; + } + + public void setFadeOutDuration(Integer fadeOutDuration) { + this.fadeOutDuration = fadeOutDuration; + } + + public boolean isAutoHide() { + return autoHide; + } + + public void setAutoHide(boolean autoHide) { + this.autoHide = autoHide; + } +} diff --git a/mobile/plugins/capacitor-splash-screen/android/src/main/res/.gitkeep b/mobile/plugins/capacitor-splash-screen/android/src/main/res/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/mobile/plugins/capacitor-splash-screen/android/src/main/res/values/styles.xml b/mobile/plugins/capacitor-splash-screen/android/src/main/res/values/styles.xml new file mode 100644 index 00000000..3534cf91 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/mobile/plugins/capacitor-splash-screen/android/src/test/java/com/getcapacitor/ExampleUnitTest.java b/mobile/plugins/capacitor-splash-screen/android/src/test/java/com/getcapacitor/ExampleUnitTest.java new file mode 100644 index 00000000..a0fed0cf --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/android/src/test/java/com/getcapacitor/ExampleUnitTest.java @@ -0,0 +1,18 @@ +package com.getcapacitor; + +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} diff --git a/mobile/plugins/capacitor-splash-screen/dist/docs.json b/mobile/plugins/capacitor-splash-screen/dist/docs.json new file mode 100644 index 00000000..92adf93a --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/dist/docs.json @@ -0,0 +1,416 @@ +{ + "api": { + "name": "SplashScreenPlugin", + "slug": "splashscreenplugin", + "docs": "", + "tags": [], + "methods": [ + { + "name": "show", + "signature": "(options?: ShowOptions | undefined) => Promise", + "parameters": [ + { + "name": "options", + "docs": "", + "type": "ShowOptions | undefined" + } + ], + "returns": "Promise", + "tags": [ + { + "name": "since", + "text": "1.0.0" + } + ], + "docs": "Show the splash screen", + "complexTypes": [ + "ShowOptions" + ], + "slug": "show" + }, + { + "name": "hide", + "signature": "(options?: HideOptions | undefined) => Promise", + "parameters": [ + { + "name": "options", + "docs": "", + "type": "HideOptions | undefined" + } + ], + "returns": "Promise", + "tags": [ + { + "name": "since", + "text": "1.0.0" + } + ], + "docs": "Hide the splash screen", + "complexTypes": [ + "HideOptions" + ], + "slug": "hide" + } + ], + "properties": [] + }, + "interfaces": [ + { + "name": "ShowOptions", + "slug": "showoptions", + "docs": "", + "tags": [], + "methods": [], + "properties": [ + { + "name": "autoHide", + "tags": [ + { + "text": "1.0.0", + "name": "since" + } + ], + "docs": "Whether to auto hide the splash after showDuration", + "complexTypes": [], + "type": "boolean | undefined" + }, + { + "name": "fadeInDuration", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "200", + "name": "default" + } + ], + "docs": "How long (in ms) to fade in.", + "complexTypes": [], + "type": "number | undefined" + }, + { + "name": "fadeOutDuration", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "200", + "name": "default" + } + ], + "docs": "How long (in ms) to fade out.", + "complexTypes": [], + "type": "number | undefined" + }, + { + "name": "showDuration", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "3000", + "name": "default" + } + ], + "docs": "How long to show the splash screen when autoHide is enabled (in ms)", + "complexTypes": [], + "type": "number | undefined" + } + ] + }, + { + "name": "HideOptions", + "slug": "hideoptions", + "docs": "", + "tags": [], + "methods": [], + "properties": [ + { + "name": "fadeOutDuration", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "200", + "name": "default" + } + ], + "docs": "How long (in ms) to fade out.\n\nOn Android, if using the Android 12 Splash Screen API, it's not being used.\nUse launchFadeOutDuration configuration option instead.", + "complexTypes": [], + "type": "number | undefined" + } + ] + } + ], + "enums": [], + "typeAliases": [], + "pluginConfigs": [ + { + "name": "SplashScreen", + "slug": "splashscreen", + "properties": [ + { + "name": "launchShowDuration", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "500", + "name": "default" + }, + { + "text": "3000", + "name": "example" + } + ], + "docs": "How long to show the launch splash screen when autoHide is enabled (in ms)", + "complexTypes": [], + "type": "number | undefined" + }, + { + "name": "launchAutoHide", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "true", + "name": "default" + }, + { + "text": "true", + "name": "example" + } + ], + "docs": "Whether to auto hide the splash after launchShowDuration.", + "complexTypes": [], + "type": "boolean | undefined" + }, + { + "name": "launchFadeOutDuration", + "tags": [ + { + "text": "4.2.0", + "name": "since" + }, + { + "text": "200", + "name": "default" + }, + { + "text": "3000", + "name": "example" + } + ], + "docs": "Duration for the fade out animation of the launch splash screen (in ms)\n\nOnly available for Android, when using the Android 12 Splash Screen API.", + "complexTypes": [], + "type": "number | undefined" + }, + { + "name": "backgroundColor", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "\"#ffffffff\"", + "name": "example" + } + ], + "docs": "Color of the background of the Splash Screen in hex format, #RRGGBB or #RRGGBBAA.\nDoesn't work if `useDialog` is true or on launch when using the Android 12 API.", + "complexTypes": [], + "type": "string | undefined" + }, + { + "name": "androidSplashResourceName", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "splash", + "name": "default" + }, + { + "text": "\"splash\"", + "name": "example" + } + ], + "docs": "Name of the resource to be used as Splash Screen.\n\nDoesn't work on launch when using the Android 12 API.\n\nOnly available on Android.", + "complexTypes": [], + "type": "string | undefined" + }, + { + "name": "androidScaleType", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "FIT_XY", + "name": "default" + }, + { + "text": "\"CENTER_CROP\"", + "name": "example" + } + ], + "docs": "The [ImageView.ScaleType](https://developer.android.com/reference/android/widget/ImageView.ScaleType) used to scale\nthe Splash Screen image.\nDoesn't work if `useDialog` is true or on launch when using the Android 12 API.\n\nOnly available on Android.", + "complexTypes": [], + "type": "'CENTER' | 'CENTER_CROP' | 'CENTER_INSIDE' | 'FIT_CENTER' | 'FIT_END' | 'FIT_START' | 'FIT_XY' | 'MATRIX' | undefined" + }, + { + "name": "showSpinner", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "true", + "name": "example" + } + ], + "docs": "Show a loading spinner on the Splash Screen.\nDoesn't work if `useDialog` is true or on launch when using the Android 12 API.", + "complexTypes": [], + "type": "boolean | undefined" + }, + { + "name": "androidSpinnerStyle", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "large", + "name": "default" + }, + { + "text": "\"large\"", + "name": "example" + } + ], + "docs": "Style of the Android spinner.\nDoesn't work if `useDialog` is true or on launch when using the Android 12 API.", + "complexTypes": [], + "type": "'horizontal' | 'small' | 'large' | 'inverse' | 'smallInverse' | 'largeInverse' | undefined" + }, + { + "name": "iosSpinnerStyle", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "large", + "name": "default" + }, + { + "text": "\"small\"", + "name": "example" + } + ], + "docs": "Style of the iOS spinner.\nDoesn't work if `useDialog` is true.\n\nOnly available on iOS.", + "complexTypes": [], + "type": "'small' | 'large' | undefined" + }, + { + "name": "spinnerColor", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "\"#999999\"", + "name": "example" + } + ], + "docs": "Color of the spinner in hex format, #RRGGBB or #RRGGBBAA.\nDoesn't work if `useDialog` is true or on launch when using the Android 12 API.", + "complexTypes": [], + "type": "string | undefined" + }, + { + "name": "splashFullScreen", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "true", + "name": "example" + } + ], + "docs": "Hide the status bar on the Splash Screen.\n\nDoesn't work on launch when using the Android 12 API.\n\nOnly available on Android.", + "complexTypes": [], + "type": "boolean | undefined" + }, + { + "name": "splashImmersive", + "tags": [ + { + "text": "1.0.0", + "name": "since" + }, + { + "text": "true", + "name": "example" + } + ], + "docs": "Hide the status bar and the software navigation buttons on the Splash Screen.\n\nDoesn't work on launch when using the Android 12 API.\n\nOnly available on Android.", + "complexTypes": [], + "type": "boolean | undefined" + }, + { + "name": "layoutName", + "tags": [ + { + "text": "1.1.0", + "name": "since" + }, + { + "text": "\"launch_screen\"", + "name": "example" + } + ], + "docs": "If `useDialog` is set to true, configure the Dialog layout.\nIf `useDialog` is not set or false, use a layout instead of the ImageView.\n\nDoesn't work on launch when using the Android 12 API.\n\nOnly available on Android.", + "complexTypes": [], + "type": "string | undefined" + }, + { + "name": "useDialog", + "tags": [ + { + "text": "1.1.0", + "name": "since" + }, + { + "text": "true", + "name": "example" + } + ], + "docs": "Use a Dialog instead of an ImageView.\nIf `layoutName` is not configured, it will use\na layout that uses the splash image as background.\n\nDoesn't work on launch when using the Android 12 API.\n\nOnly available on Android.", + "complexTypes": [], + "type": "boolean | undefined" + } + ], + "docs": "These config values are available:" + } + ] +} \ No newline at end of file diff --git a/mobile/plugins/capacitor-splash-screen/dist/esm/definitions.d.ts b/mobile/plugins/capacitor-splash-screen/dist/esm/definitions.d.ts new file mode 100644 index 00000000..ae757415 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/dist/esm/definitions.d.ts @@ -0,0 +1,215 @@ +declare module '@capacitor/cli' { + interface PluginsConfig { + /** + * These config values are available: + */ + SplashScreen?: { + /** + * How long to show the launch splash screen when autoHide is enabled (in ms) + * + * @since 1.0.0 + * @default 500 + * @example 3000 + */ + launchShowDuration?: number; + /** + * Whether to auto hide the splash after launchShowDuration. + * + * @since 1.0.0 + * @default true + * @example true + */ + launchAutoHide?: boolean; + /** + * Duration for the fade out animation of the launch splash screen (in ms) + * + * Only available for Android, when using the Android 12 Splash Screen API. + * + * @since 4.2.0 + * @default 200 + * @example 3000 + */ + launchFadeOutDuration?: number; + /** + * Color of the background of the Splash Screen in hex format, #RRGGBB or #RRGGBBAA. + * Doesn't work if `useDialog` is true or on launch when using the Android 12 API. + * + * @since 1.0.0 + * @example "#ffffffff" + */ + backgroundColor?: string; + /** + * Name of the resource to be used as Splash Screen. + * + * Doesn't work on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.0.0 + * @default splash + * @example "splash" + */ + androidSplashResourceName?: string; + /** + * The [ImageView.ScaleType](https://developer.android.com/reference/android/widget/ImageView.ScaleType) used to scale + * the Splash Screen image. + * Doesn't work if `useDialog` is true or on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.0.0 + * @default FIT_XY + * @example "CENTER_CROP" + */ + androidScaleType?: 'CENTER' | 'CENTER_CROP' | 'CENTER_INSIDE' | 'FIT_CENTER' | 'FIT_END' | 'FIT_START' | 'FIT_XY' | 'MATRIX'; + /** + * Show a loading spinner on the Splash Screen. + * Doesn't work if `useDialog` is true or on launch when using the Android 12 API. + * + * @since 1.0.0 + * @example true + */ + showSpinner?: boolean; + /** + * Style of the Android spinner. + * Doesn't work if `useDialog` is true or on launch when using the Android 12 API. + * + * @since 1.0.0 + * @default large + * @example "large" + */ + androidSpinnerStyle?: 'horizontal' | 'small' | 'large' | 'inverse' | 'smallInverse' | 'largeInverse'; + /** + * Style of the iOS spinner. + * Doesn't work if `useDialog` is true. + * + * Only available on iOS. + * + * @since 1.0.0 + * @default large + * @example "small" + */ + iosSpinnerStyle?: 'large' | 'small'; + /** + * Color of the spinner in hex format, #RRGGBB or #RRGGBBAA. + * Doesn't work if `useDialog` is true or on launch when using the Android 12 API. + * + * @since 1.0.0 + * @example "#999999" + */ + spinnerColor?: string; + /** + * Hide the status bar on the Splash Screen. + * + * Doesn't work on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.0.0 + * @example true + */ + splashFullScreen?: boolean; + /** + * Hide the status bar and the software navigation buttons on the Splash Screen. + * + * Doesn't work on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.0.0 + * @example true + */ + splashImmersive?: boolean; + /** + * If `useDialog` is set to true, configure the Dialog layout. + * If `useDialog` is not set or false, use a layout instead of the ImageView. + * + * Doesn't work on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.1.0 + * @example "launch_screen" + */ + layoutName?: string; + /** + * Use a Dialog instead of an ImageView. + * If `layoutName` is not configured, it will use + * a layout that uses the splash image as background. + * + * Doesn't work on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.1.0 + * @example true + */ + useDialog?: boolean; + }; + } +} +export interface ShowOptions { + /** + * Whether to auto hide the splash after showDuration + * + * @since 1.0.0 + */ + autoHide?: boolean; + /** + * How long (in ms) to fade in. + * + * @since 1.0.0 + * @default 200 + */ + fadeInDuration?: number; + /** + * How long (in ms) to fade out. + * + * @since 1.0.0 + * @default 200 + */ + fadeOutDuration?: number; + /** + * How long to show the splash screen when autoHide is enabled (in ms) + * + * @since 1.0.0 + * @default 3000 + */ + showDuration?: number; +} +export interface HideOptions { + /** + * How long (in ms) to fade out. + * + * On Android, if using the Android 12 Splash Screen API, it's not being used. + * Use launchFadeOutDuration configuration option instead. + * + * @since 1.0.0 + * @default 200 + */ + fadeOutDuration?: number; +} +export interface SplashScreenPlugin { + /** + * Show the splash screen + * + * @since 1.0.0 + */ + show(options?: ShowOptions): Promise; + /** + * Hide the splash screen + * + * @since 1.0.0 + */ + hide(options?: HideOptions): Promise; +} +/** + * @deprecated Use `ShowOptions`. + * @since 1.0.0 + */ +export declare type SplashScreenShowOptions = ShowOptions; +/** + * @deprecated Use `HideOptions`. + * @since 1.0.0 + */ +export declare type SplashScreenHideOptions = HideOptions; diff --git a/mobile/plugins/capacitor-splash-screen/dist/esm/definitions.js b/mobile/plugins/capacitor-splash-screen/dist/esm/definitions.js new file mode 100644 index 00000000..7e3939b1 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/dist/esm/definitions.js @@ -0,0 +1,3 @@ +/// +export {}; +//# sourceMappingURL=definitions.js.map \ No newline at end of file diff --git a/mobile/plugins/capacitor-splash-screen/dist/esm/definitions.js.map b/mobile/plugins/capacitor-splash-screen/dist/esm/definitions.js.map new file mode 100644 index 00000000..63827acd --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/dist/esm/definitions.js.map @@ -0,0 +1 @@ +{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,wCAAwC","sourcesContent":["/// \n\ndeclare module '@capacitor/cli' {\n export interface PluginsConfig {\n /**\n * These config values are available:\n */\n SplashScreen?: {\n /**\n * How long to show the launch splash screen when autoHide is enabled (in ms)\n *\n * @since 1.0.0\n * @default 500\n * @example 3000\n */\n launchShowDuration?: number;\n\n /**\n * Whether to auto hide the splash after launchShowDuration.\n *\n * @since 1.0.0\n * @default true\n * @example true\n */\n launchAutoHide?: boolean;\n\n /**\n * Duration for the fade out animation of the launch splash screen (in ms)\n *\n * Only available for Android, when using the Android 12 Splash Screen API.\n *\n * @since 4.2.0\n * @default 200\n * @example 3000\n */\n launchFadeOutDuration?: number;\n\n /**\n * Color of the background of the Splash Screen in hex format, #RRGGBB or #RRGGBBAA.\n * Doesn't work if `useDialog` is true or on launch when using the Android 12 API.\n *\n * @since 1.0.0\n * @example \"#ffffffff\"\n */\n backgroundColor?: string;\n\n /**\n * Name of the resource to be used as Splash Screen.\n *\n * Doesn't work on launch when using the Android 12 API.\n *\n * Only available on Android.\n *\n * @since 1.0.0\n * @default splash\n * @example \"splash\"\n */\n androidSplashResourceName?: string;\n\n /**\n * The [ImageView.ScaleType](https://developer.android.com/reference/android/widget/ImageView.ScaleType) used to scale\n * the Splash Screen image.\n * Doesn't work if `useDialog` is true or on launch when using the Android 12 API.\n *\n * Only available on Android.\n *\n * @since 1.0.0\n * @default FIT_XY\n * @example \"CENTER_CROP\"\n */\n androidScaleType?:\n | 'CENTER'\n | 'CENTER_CROP'\n | 'CENTER_INSIDE'\n | 'FIT_CENTER'\n | 'FIT_END'\n | 'FIT_START'\n | 'FIT_XY'\n | 'MATRIX';\n\n /**\n * Show a loading spinner on the Splash Screen.\n * Doesn't work if `useDialog` is true or on launch when using the Android 12 API.\n *\n * @since 1.0.0\n * @example true\n */\n showSpinner?: boolean;\n\n /**\n * Style of the Android spinner.\n * Doesn't work if `useDialog` is true or on launch when using the Android 12 API.\n *\n * @since 1.0.0\n * @default large\n * @example \"large\"\n */\n androidSpinnerStyle?:\n | 'horizontal'\n | 'small'\n | 'large'\n | 'inverse'\n | 'smallInverse'\n | 'largeInverse';\n\n /**\n * Style of the iOS spinner.\n * Doesn't work if `useDialog` is true.\n *\n * Only available on iOS.\n *\n * @since 1.0.0\n * @default large\n * @example \"small\"\n */\n iosSpinnerStyle?: 'large' | 'small';\n\n /**\n * Color of the spinner in hex format, #RRGGBB or #RRGGBBAA.\n * Doesn't work if `useDialog` is true or on launch when using the Android 12 API.\n *\n * @since 1.0.0\n * @example \"#999999\"\n */\n spinnerColor?: string;\n\n /**\n * Hide the status bar on the Splash Screen.\n *\n * Doesn't work on launch when using the Android 12 API.\n *\n * Only available on Android.\n *\n * @since 1.0.0\n * @example true\n */\n splashFullScreen?: boolean;\n\n /**\n * Hide the status bar and the software navigation buttons on the Splash Screen.\n *\n * Doesn't work on launch when using the Android 12 API.\n *\n * Only available on Android.\n *\n * @since 1.0.0\n * @example true\n */\n splashImmersive?: boolean;\n\n /**\n * If `useDialog` is set to true, configure the Dialog layout.\n * If `useDialog` is not set or false, use a layout instead of the ImageView.\n *\n * Doesn't work on launch when using the Android 12 API.\n *\n * Only available on Android.\n *\n * @since 1.1.0\n * @example \"launch_screen\"\n */\n layoutName?: string;\n\n /**\n * Use a Dialog instead of an ImageView.\n * If `layoutName` is not configured, it will use\n * a layout that uses the splash image as background.\n *\n * Doesn't work on launch when using the Android 12 API.\n *\n * Only available on Android.\n *\n * @since 1.1.0\n * @example true\n */\n useDialog?: boolean;\n };\n }\n}\n\nexport interface ShowOptions {\n /**\n * Whether to auto hide the splash after showDuration\n *\n * @since 1.0.0\n */\n autoHide?: boolean;\n /**\n * How long (in ms) to fade in.\n *\n * @since 1.0.0\n * @default 200\n */\n fadeInDuration?: number;\n /**\n * How long (in ms) to fade out.\n *\n * @since 1.0.0\n * @default 200\n */\n fadeOutDuration?: number;\n /**\n * How long to show the splash screen when autoHide is enabled (in ms)\n *\n * @since 1.0.0\n * @default 3000\n */\n showDuration?: number;\n}\n\nexport interface HideOptions {\n /**\n * How long (in ms) to fade out.\n *\n * On Android, if using the Android 12 Splash Screen API, it's not being used.\n * Use launchFadeOutDuration configuration option instead.\n *\n * @since 1.0.0\n * @default 200\n */\n fadeOutDuration?: number;\n}\n\nexport interface SplashScreenPlugin {\n /**\n * Show the splash screen\n *\n * @since 1.0.0\n */\n show(options?: ShowOptions): Promise;\n /**\n * Hide the splash screen\n *\n * @since 1.0.0\n */\n hide(options?: HideOptions): Promise;\n}\n\n/**\n * @deprecated Use `ShowOptions`.\n * @since 1.0.0\n */\nexport type SplashScreenShowOptions = ShowOptions;\n\n/**\n * @deprecated Use `HideOptions`.\n * @since 1.0.0\n */\nexport type SplashScreenHideOptions = HideOptions;\n"]} \ No newline at end of file diff --git a/mobile/plugins/capacitor-splash-screen/dist/esm/index.d.ts b/mobile/plugins/capacitor-splash-screen/dist/esm/index.d.ts new file mode 100644 index 00000000..aad8c0d8 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/dist/esm/index.d.ts @@ -0,0 +1,4 @@ +import type { SplashScreenPlugin } from './definitions'; +declare const SplashScreen: SplashScreenPlugin; +export * from './definitions'; +export { SplashScreen }; diff --git a/mobile/plugins/capacitor-splash-screen/dist/esm/index.js b/mobile/plugins/capacitor-splash-screen/dist/esm/index.js new file mode 100644 index 00000000..2034a1a1 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/dist/esm/index.js @@ -0,0 +1,7 @@ +import { registerPlugin } from '@capacitor/core'; +const SplashScreen = registerPlugin('SplashScreen', { + web: () => import('./web').then(m => new m.SplashScreenWeb()), +}); +export * from './definitions'; +export { SplashScreen }; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/mobile/plugins/capacitor-splash-screen/dist/esm/index.js.map b/mobile/plugins/capacitor-splash-screen/dist/esm/index.js.map new file mode 100644 index 00000000..e7bd2907 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/dist/esm/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,YAAY,GAAG,cAAc,CAAqB,cAAc,EAAE;IACtE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;CAC9D,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { SplashScreenPlugin } from './definitions';\n\nconst SplashScreen = registerPlugin('SplashScreen', {\n web: () => import('./web').then(m => new m.SplashScreenWeb()),\n});\n\nexport * from './definitions';\nexport { SplashScreen };\n"]} \ No newline at end of file diff --git a/mobile/plugins/capacitor-splash-screen/dist/esm/web.d.ts b/mobile/plugins/capacitor-splash-screen/dist/esm/web.d.ts new file mode 100644 index 00000000..881de436 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/dist/esm/web.d.ts @@ -0,0 +1,6 @@ +import { WebPlugin } from '@capacitor/core'; +import type { HideOptions, ShowOptions, SplashScreenPlugin } from './definitions'; +export declare class SplashScreenWeb extends WebPlugin implements SplashScreenPlugin { + show(_options?: ShowOptions): Promise; + hide(_options?: HideOptions): Promise; +} diff --git a/mobile/plugins/capacitor-splash-screen/dist/esm/web.js b/mobile/plugins/capacitor-splash-screen/dist/esm/web.js new file mode 100644 index 00000000..bee33275 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/dist/esm/web.js @@ -0,0 +1,10 @@ +import { WebPlugin } from '@capacitor/core'; +export class SplashScreenWeb extends WebPlugin { + async show(_options) { + return undefined; + } + async hide(_options) { + return undefined; + } +} +//# sourceMappingURL=web.js.map \ No newline at end of file diff --git a/mobile/plugins/capacitor-splash-screen/dist/esm/web.js.map b/mobile/plugins/capacitor-splash-screen/dist/esm/web.js.map new file mode 100644 index 00000000..5caa39c4 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/dist/esm/web.js.map @@ -0,0 +1 @@ +{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAQ5C,MAAM,OAAO,eAAgB,SAAQ,SAAS;IAC5C,KAAK,CAAC,IAAI,CAAC,QAAsB;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAsB;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type {\n HideOptions,\n ShowOptions,\n SplashScreenPlugin,\n} from './definitions';\n\nexport class SplashScreenWeb extends WebPlugin implements SplashScreenPlugin {\n async show(_options?: ShowOptions): Promise {\n return undefined;\n }\n\n async hide(_options?: HideOptions): Promise {\n return undefined;\n }\n}\n"]} \ No newline at end of file diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.pbxproj b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.pbxproj new file mode 100644 index 00000000..01ea34e2 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.pbxproj @@ -0,0 +1,579 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */; }; + 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */; }; + 2F94DB20258B85A6003E0C43 /* SplashScreenSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F94DB1F258B85A6003E0C43 /* SplashScreenSettings.swift */; }; + 2F94DB24258B85BE003E0C43 /* SplashScreenConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F94DB23258B85BE003E0C43 /* SplashScreenConfig.swift */; }; + 2F98D68224C9AAE500613A4C /* SplashScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F98D68124C9AAE400613A4C /* SplashScreen.swift */; }; + 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFF88201F53D600D50D53 /* Plugin.framework */; }; + 50ADFF97201F53D600D50D53 /* SplashScreenPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* SplashScreenPluginTests.swift */; }; + 50ADFF99201F53D600D50D53 /* SplashScreenPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 50ADFF8B201F53D600D50D53 /* SplashScreenPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFFA52020D75100D50D53 /* Capacitor.framework */; }; + 50ADFFA82020EE4F00D50D53 /* SplashScreenPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFFA72020EE4F00D50D53 /* SplashScreenPlugin.m */; }; + 50E1A94820377CB70090CE1A /* SplashScreenPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1A94720377CB70090CE1A /* SplashScreenPlugin.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 50ADFF7F201F53D600D50D53 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 50ADFF87201F53D600D50D53; + remoteInfo = Plugin; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 2F94DB1F258B85A6003E0C43 /* SplashScreenSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenSettings.swift; sourceTree = ""; }; + 2F94DB23258B85BE003E0C43 /* SplashScreenConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenConfig.swift; sourceTree = ""; }; + 2F98D68124C9AAE400613A4C /* SplashScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashScreen.swift; sourceTree = ""; }; + 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF88201F53D600D50D53 /* Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF8B201F53D600D50D53 /* SplashScreenPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SplashScreenPlugin.h; sourceTree = ""; }; + 50ADFF8C201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50ADFF91201F53D600D50D53 /* PluginTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PluginTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF96201F53D600D50D53 /* SplashScreenPluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenPluginTests.swift; sourceTree = ""; }; + 50ADFF98201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50ADFFA52020D75100D50D53 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFFA72020EE4F00D50D53 /* SplashScreenPlugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SplashScreenPlugin.m; sourceTree = ""; }; + 50E1A94720377CB70090CE1A /* SplashScreenPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashScreenPlugin.swift; sourceTree = ""; }; + 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig"; sourceTree = ""; }; + 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.release.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig"; sourceTree = ""; }; + 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig"; sourceTree = ""; }; + F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.release.xcconfig"; sourceTree = ""; }; + F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PluginTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 50ADFF84201F53D600D50D53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */, + 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8E201F53D600D50D53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */, + 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 50ADFF7E201F53D600D50D53 = { + isa = PBXGroup; + children = ( + 50ADFF8A201F53D600D50D53 /* Plugin */, + 50ADFF95201F53D600D50D53 /* PluginTests */, + 50ADFF89201F53D600D50D53 /* Products */, + 8C8E7744173064A9F6D438E3 /* Pods */, + A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */, + ); + sourceTree = ""; + }; + 50ADFF89201F53D600D50D53 /* Products */ = { + isa = PBXGroup; + children = ( + 50ADFF88201F53D600D50D53 /* Plugin.framework */, + 50ADFF91201F53D600D50D53 /* PluginTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 50ADFF8A201F53D600D50D53 /* Plugin */ = { + isa = PBXGroup; + children = ( + 2F94DB23258B85BE003E0C43 /* SplashScreenConfig.swift */, + 2F94DB1F258B85A6003E0C43 /* SplashScreenSettings.swift */, + 50E1A94720377CB70090CE1A /* SplashScreenPlugin.swift */, + 2F98D68124C9AAE400613A4C /* SplashScreen.swift */, + 50ADFF8B201F53D600D50D53 /* SplashScreenPlugin.h */, + 50ADFFA72020EE4F00D50D53 /* SplashScreenPlugin.m */, + 50ADFF8C201F53D600D50D53 /* Info.plist */, + ); + path = Plugin; + sourceTree = ""; + }; + 50ADFF95201F53D600D50D53 /* PluginTests */ = { + isa = PBXGroup; + children = ( + 50ADFF96201F53D600D50D53 /* SplashScreenPluginTests.swift */, + 50ADFF98201F53D600D50D53 /* Info.plist */, + ); + path = PluginTests; + sourceTree = ""; + }; + 8C8E7744173064A9F6D438E3 /* Pods */ = { + isa = PBXGroup; + children = ( + 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */, + 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */, + 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */, + F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 50ADFFA52020D75100D50D53 /* Capacitor.framework */, + 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */, + F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 50ADFF85201F53D600D50D53 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF99201F53D600D50D53 /* SplashScreenPlugin.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 50ADFF87201F53D600D50D53 /* Plugin */ = { + isa = PBXNativeTarget; + buildConfigurationList = 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */; + buildPhases = ( + AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */, + 50ADFF83201F53D600D50D53 /* Sources */, + 50ADFF84201F53D600D50D53 /* Frameworks */, + 50ADFF85201F53D600D50D53 /* Headers */, + 50ADFF86201F53D600D50D53 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Plugin; + productName = Plugin; + productReference = 50ADFF88201F53D600D50D53 /* Plugin.framework */; + productType = "com.apple.product-type.framework"; + }; + 50ADFF90201F53D600D50D53 /* PluginTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */; + buildPhases = ( + 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */, + 50ADFF8D201F53D600D50D53 /* Sources */, + 50ADFF8E201F53D600D50D53 /* Frameworks */, + 50ADFF8F201F53D600D50D53 /* Resources */, + 8E97F58B69A94C6503FC9C85 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 50ADFF94201F53D600D50D53 /* PBXTargetDependency */, + ); + name = PluginTests; + productName = PluginTests; + productReference = 50ADFF91201F53D600D50D53 /* PluginTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 50ADFF7F201F53D600D50D53 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1160; + ORGANIZATIONNAME = "Max Lynch"; + TargetAttributes = { + 50ADFF87201F53D600D50D53 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + 50ADFF90201F53D600D50D53 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 50ADFF7E201F53D600D50D53; + productRefGroup = 50ADFF89201F53D600D50D53 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 50ADFF87201F53D600D50D53 /* Plugin */, + 50ADFF90201F53D600D50D53 /* PluginTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 50ADFF86201F53D600D50D53 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8F201F53D600D50D53 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PluginTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 8E97F58B69A94C6503FC9C85 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Capacitor/Capacitor.framework", + "${BUILT_PRODUCTS_DIR}/CapacitorCordova/Cordova.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Capacitor.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cordova.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Plugin-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 50ADFF83201F53D600D50D53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50E1A94820377CB70090CE1A /* SplashScreenPlugin.swift in Sources */, + 2F94DB20258B85A6003E0C43 /* SplashScreenSettings.swift in Sources */, + 2F94DB24258B85BE003E0C43 /* SplashScreenConfig.swift in Sources */, + 2F98D68224C9AAE500613A4C /* SplashScreen.swift in Sources */, + 50ADFFA82020EE4F00D50D53 /* SplashScreenPlugin.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8D201F53D600D50D53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF97201F53D600D50D53 /* SplashScreenPluginTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 50ADFF94201F53D600D50D53 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 50ADFF87201F53D600D50D53 /* Plugin */; + targetProxy = 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 50ADFF9A201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"${BUILT_PRODUCTS_DIR}/Capacitor\"", + "\"${BUILT_PRODUCTS_DIR}/CapacitorCordova\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 50ADFF9B201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"${BUILT_PRODUCTS_DIR}/Capacitor\"", + "\"${BUILT_PRODUCTS_DIR}/CapacitorCordova\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 50ADFF9D201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Plugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)"; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 50ADFF9E201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Plugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)"; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 50ADFFA0201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PluginTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 50ADFFA1201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PluginTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFF9A201F53D600D50D53 /* Debug */, + 50ADFF9B201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFF9D201F53D600D50D53 /* Debug */, + 50ADFF9E201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFFA0201F53D600D50D53 /* Debug */, + 50ADFFA1201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 50ADFF7F201F53D600D50D53 /* Project object */; +} diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme new file mode 100644 index 00000000..303f2621 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme new file mode 100644 index 00000000..3d8c88d2 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcworkspace/contents.xcworkspacedata b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..afad624e --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin/Info.plist b/mobile/plugins/capacitor-splash-screen/ios/Plugin/Info.plist new file mode 100644 index 00000000..1007fd9d --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreen.swift b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreen.swift new file mode 100644 index 00000000..b50b7c63 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreen.swift @@ -0,0 +1,161 @@ +import Foundation +import Capacitor + +@objc public class SplashScreen: NSObject { + + var parentView: UIView + var viewController = UIViewController() + var spinner = UIActivityIndicatorView() + var config: SplashScreenConfig = SplashScreenConfig() + var hideTask: Any? + var isVisible: Bool = false + + init(parentView: UIView, config: SplashScreenConfig) { + self.parentView = parentView + self.config = config + } + + public func showOnLaunch() { + buildViews() + if self.config.launchShowDuration == 0 { + return + } + var settings = SplashScreenSettings() + settings.showDuration = config.launchShowDuration + settings.fadeInDuration = config.launchFadeInDuration + settings.autoHide = config.launchAutoHide + showSplash(settings: settings, completion: {}, isLaunchSplash: true) + } + + public func show(settings: SplashScreenSettings, completion: @escaping () -> Void) { + self.showSplash(settings: settings, completion: completion, isLaunchSplash: false) + } + + public func hide(settings: SplashScreenSettings) { + hideSplash(fadeOutDuration: settings.fadeOutDuration, isLaunchSplash: false) + } + + private func showSplash(settings: SplashScreenSettings, completion: @escaping () -> Void, isLaunchSplash: Bool) { + DispatchQueue.main.async { [weak self] in + guard let strongSelf = self else { + return + } + if let backgroundColor = strongSelf.config.backgroundColor { + strongSelf.viewController.view.backgroundColor = backgroundColor + } + + if strongSelf.config.showSpinner { + if let style = strongSelf.config.spinnerStyle { + strongSelf.spinner.style = style + } + + if let spinnerColor = strongSelf.config.spinnerColor { + strongSelf.spinner.color = spinnerColor + } + } + + strongSelf.parentView.addSubview(strongSelf.viewController.view) + + if strongSelf.config.showSpinner { + strongSelf.parentView.addSubview(strongSelf.spinner) + strongSelf.spinner.centerXAnchor.constraint(equalTo: strongSelf.parentView.centerXAnchor).isActive = true + strongSelf.spinner.centerYAnchor.constraint(equalTo: strongSelf.parentView.centerYAnchor).isActive = true + } + + strongSelf.parentView.isUserInteractionEnabled = false + + UIView.transition(with: strongSelf.viewController.view, duration: TimeInterval(Double(settings.fadeInDuration) / 1000), options: .curveLinear, animations: { + strongSelf.viewController.view.alpha = 1 + + if strongSelf.config.showSpinner { + strongSelf.spinner.alpha = 1 + } + }) { (_: Bool) in + strongSelf.isVisible = true + + if settings.autoHide { + strongSelf.hideTask = DispatchQueue.main.asyncAfter( + deadline: DispatchTime.now() + (Double(settings.showDuration) / 1000) + ) { + strongSelf.hideSplash(fadeOutDuration: settings.fadeOutDuration, isLaunchSplash: isLaunchSplash) + completion() + } + } else { + completion() + } + } + } + } + + private func buildViews() { + let storyboardName = Bundle.main.infoDictionary?["UILaunchStoryboardName"] as? String ?? "LaunchScreen" + if let vc = UIStoryboard(name: storyboardName.replacingOccurrences(of: ".storyboard", with: ""), bundle: nil).instantiateInitialViewController() { + viewController = vc + } + + // Observe for changes on frame and bounds to handle rotation resizing + parentView.addObserver(self, forKeyPath: "frame", options: .new, context: nil) + parentView.addObserver(self, forKeyPath: "bounds", options: .new, context: nil) + + updateSplashImageBounds() + if config.showSpinner { + spinner.translatesAutoresizingMaskIntoConstraints = false + spinner.startAnimating() + } + } + + private func tearDown() { + isVisible = false + parentView.isUserInteractionEnabled = true + viewController.view.removeFromSuperview() + + if config.showSpinner { + spinner.removeFromSuperview() + } + } + + // Update the bounds for the splash image. This will also be called when + // the parent view observers fire + private func updateSplashImageBounds() { + var window: UIWindow? = UIApplication.shared.delegate?.window ?? nil + + if window == nil { + let scene: UIWindowScene? = UIApplication.shared.connectedScenes.first as? UIWindowScene + window = scene?.windows.filter({$0.isKeyWindow}).first + if window == nil { + window = scene?.windows.first + } + } + + if let unwrappedWindow = window { + viewController.view.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: unwrappedWindow.bounds.size) + } else { + CAPLog.print("Unable to find root window object for SplashScreen bounds. Please file an issue") + } + } + + override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change _: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { + updateSplashImageBounds() + } + + private func hideSplash(fadeOutDuration: Int, isLaunchSplash: Bool) { + if isLaunchSplash, isVisible { + CAPLog.print("SplashScreen.hideSplash: SplashScreen was automatically hidden after default timeout. " + + "You should call `SplashScreen.hide()` as soon as your web app is loaded (or increase the timeout). " + + "Read more at https://capacitorjs.com/docs/apis/splash-screen#hiding-the-splash-screen") + } + if !isVisible { return } + DispatchQueue.main.async { + UIView.transition(with: self.viewController.view, duration: TimeInterval(Double(fadeOutDuration) / 1000), options: .curveEaseInOut, animations: { + self.viewController.view.alpha = 0 + self.viewController.view.transform = CGAffineTransform.identity.scaledBy(x: 8, y: 8) + + if self.config.showSpinner { + self.spinner.alpha = 0 + } + }) { (_: Bool) in + self.tearDown() + } + } + } +} diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenConfig.swift b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenConfig.swift new file mode 100644 index 00000000..6a5d5f57 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenConfig.swift @@ -0,0 +1,11 @@ +import UIKit + +public struct SplashScreenConfig { + var backgroundColor: UIColor? + var spinnerStyle: UIActivityIndicatorView.Style? + var spinnerColor: UIColor? + var showSpinner = false + var launchShowDuration = 500 + var launchAutoHide = true + let launchFadeInDuration = 0 +} diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.h b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.h new file mode 100644 index 00000000..f2bd9e0b --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.h @@ -0,0 +1,10 @@ +#import + +//! Project version number for Plugin. +FOUNDATION_EXPORT double PluginVersionNumber; + +//! Project version string for Plugin. +FOUNDATION_EXPORT const unsigned char PluginVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.m b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.m new file mode 100644 index 00000000..01a7e894 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.m @@ -0,0 +1,9 @@ +#import +#import + +// Define the plugin using the CAP_PLUGIN Macro, and +// each method the plugin supports using the CAP_PLUGIN_METHOD macro. +CAP_PLUGIN(SplashScreenPlugin, "SplashScreen", + CAP_PLUGIN_METHOD(show, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(hide, CAPPluginReturnPromise); +) diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.swift b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.swift new file mode 100644 index 00000000..8c0e31a6 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenPlugin.swift @@ -0,0 +1,82 @@ +import Foundation +import Capacitor + +@objc(SplashScreenPlugin) +public class SplashScreenPlugin: CAPPlugin { + private var splashScreen: SplashScreen? + + override public func load() { + if let view = bridge?.viewController?.view { + splashScreen = SplashScreen(parentView: view, config: splashScreenConfig()) + splashScreen?.showOnLaunch() + } + } + + // Show the splash screen + @objc public func show(_ call: CAPPluginCall) { + if let splash = splashScreen { + let settings = splashScreenSettings(from: call) + splash.show(settings: settings, + completion: { + call.resolve() + }) + } else { + call.reject("Unable to show Splash Screen") + } + + } + + // Hide the splash screen + @objc public func hide(_ call: CAPPluginCall) { + if let splash = splashScreen { + let settings = splashScreenSettings(from: call) + splash.hide(settings: settings) + call.resolve() + } else { + call.reject("Unable to hide Splash Screen") + } + } + + private func splashScreenSettings(from call: CAPPluginCall) -> SplashScreenSettings { + var settings = SplashScreenSettings() + + if let showDuration = call.getInt("showDuration") { + settings.showDuration = showDuration + } + if let fadeInDuration = call.getInt("fadeInDuration") { + settings.fadeInDuration = fadeInDuration + } + if let fadeOutDuration = call.getInt("fadeOutDuration") { + settings.fadeOutDuration = fadeOutDuration + } + if let autoHide = call.getBool("autoHide") { + settings.autoHide = autoHide + } + return settings + } + + private func splashScreenConfig() -> SplashScreenConfig { + var config = SplashScreenConfig() + + if let backgroundColor = getConfig().getString("backgroundColor") { + config.backgroundColor = UIColor.capacitor.color(fromHex: backgroundColor) + } + if let spinnerStyle = getConfig().getString("iosSpinnerStyle") { + switch spinnerStyle.lowercased() { + case "small": + config.spinnerStyle = .medium + default: + config.spinnerStyle = .large + } + } + if let spinnerColor = getConfig().getString("spinnerColor") { + config.spinnerColor = UIColor.capacitor.color(fromHex: spinnerColor) + } + config.showSpinner = getConfig().getBoolean("showSpinner", config.showSpinner) + + config.launchShowDuration = getConfig().getInt("launchShowDuration", config.launchShowDuration) + config.launchAutoHide = getConfig().getBoolean("launchAutoHide", config.launchAutoHide) + return config + } + +} diff --git a/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenSettings.swift b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenSettings.swift new file mode 100644 index 00000000..483bdfbd --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Plugin/SplashScreenSettings.swift @@ -0,0 +1,8 @@ +import UIKit + +public struct SplashScreenSettings { + var showDuration = 3000 + var fadeInDuration = 200 + var fadeOutDuration = 200 + var autoHide = true +} diff --git a/mobile/plugins/capacitor-splash-screen/ios/PluginTests/Info.plist b/mobile/plugins/capacitor-splash-screen/ios/PluginTests/Info.plist new file mode 100644 index 00000000..6c40a6cd --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/PluginTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/mobile/plugins/capacitor-splash-screen/ios/PluginTests/SplashScreenPluginTests.swift b/mobile/plugins/capacitor-splash-screen/ios/PluginTests/SplashScreenPluginTests.swift new file mode 100644 index 00000000..e40584a3 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/PluginTests/SplashScreenPluginTests.swift @@ -0,0 +1,14 @@ +import XCTest +@testable import Plugin + +class SplashScreenTests: XCTestCase { + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } +} diff --git a/mobile/plugins/capacitor-splash-screen/ios/Podfile b/mobile/plugins/capacitor-splash-screen/ios/Podfile new file mode 100644 index 00000000..dee40960 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/ios/Podfile @@ -0,0 +1,16 @@ +platform :ios, '13.0' + +def capacitor_pods + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + pod 'Capacitor', :path => '../node_modules/@capacitor/ios' + pod 'CapacitorCordova', :path => '../node_modules/@capacitor/ios' +end + +target 'Plugin' do + capacitor_pods +end + +target 'PluginTests' do + capacitor_pods +end diff --git a/mobile/plugins/capacitor-splash-screen/package-lock.json b/mobile/plugins/capacitor-splash-screen/package-lock.json new file mode 100644 index 00000000..99a8c14c --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/package-lock.json @@ -0,0 +1,4152 @@ +{ + "name": "capacitor-splash-screen", + "version": "5.0.6.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "capacitor-splash-screen", + "version": "5.0.6.1", + "license": "MIT", + "devDependencies": { + "@capacitor/android": "^5.0.0", + "@capacitor/cli": "^5.0.0", + "@capacitor/core": "^5.0.0", + "@capacitor/docgen": "0.2.0", + "@capacitor/ios": "^5.0.0", + "@ionic/eslint-config": "^0.3.0", + "@ionic/prettier-config": "~1.0.1", + "@ionic/swiftlint-config": "^1.1.2", + "eslint": "^7.11.0", + "prettier": "~2.3.0", + "prettier-plugin-java": "~1.0.2", + "rimraf": "^3.0.2", + "rollup": "^2.32.0", + "swiftlint": "^1.0.1", + "typescript": "~4.1.5" + }, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@capacitor/android": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@capacitor/android/-/android-5.5.0.tgz", + "integrity": "sha512-ipJijb3M0FA6DvotS9zrbJ8p/mTEVg9EVtBmvUexogm8g5se1mc7i1gvOr3MQ/iTZ3PnNrRC/P7kHxa2R55iqg==", + "dev": true, + "peerDependencies": { + "@capacitor/core": "^5.5.0" + } + }, + "node_modules/@capacitor/cli": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-5.5.0.tgz", + "integrity": "sha512-JkF7p+EV1mEFObp3e/3snKZiiDPbHTAXlch9jKcvvuCjm92Be7ka8sG4M3fH8BPajSE3jRNPZa/xt7bITDvAAA==", + "dev": true, + "dependencies": { + "@ionic/cli-framework-output": "^2.2.5", + "@ionic/utils-fs": "^3.1.6", + "@ionic/utils-subprocess": "^2.1.11", + "@ionic/utils-terminal": "^2.3.3", + "commander": "^9.3.0", + "debug": "^4.3.4", + "env-paths": "^2.2.0", + "kleur": "^4.1.4", + "native-run": "^1.7.3", + "open": "^8.4.0", + "plist": "^3.0.5", + "prompts": "^2.4.2", + "rimraf": "^4.4.1", + "semver": "^7.3.7", + "tar": "^6.1.11", + "tslib": "^2.4.0", + "xml2js": "^0.5.0" + }, + "bin": { + "cap": "bin/capacitor", + "capacitor": "bin/capacitor" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@capacitor/cli/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "dev": true, + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.5.0.tgz", + "integrity": "sha512-w59io0ctwnb7JRng7yO2H0YLHG8uz7XARUugRfp5aYTNiG55FqdSmSMOOqGCMPRg4sEnKjJTvAa4ImCYh3Kk1w==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@capacitor/docgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@capacitor/docgen/-/docgen-0.2.0.tgz", + "integrity": "sha512-JoKuEx4Ep9LVnTY1I4zaoQPlxy+AWVMwwOSou/U9f/nXsL391aBRH7hC12lKNZGetiComnt369vQ+/r7n9iXfQ==", + "dev": true, + "dependencies": { + "@types/node": "^14.18.0", + "colorette": "^2.0.16", + "github-slugger": "^1.4.0", + "minimist": "^1.2.5", + "typescript": "~4.2.4" + }, + "bin": { + "docgen": "bin/docgen" + }, + "engines": { + "node": ">=14.5.0" + } + }, + "node_modules/@capacitor/docgen/node_modules/typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@capacitor/ios": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@capacitor/ios/-/ios-5.5.0.tgz", + "integrity": "sha512-kApjblUOlLY91+1OrWIx+vaVfEN1bl1kh1jSgK1/IdGfS9kFs1hxUE/okRoLJGT6tYeSOa6GA/19MLOs64wb6A==", + "dev": true, + "peerDependencies": { + "@capacitor/core": "^5.5.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@ionic/cli-framework-output": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.6.tgz", + "integrity": "sha512-YLPRwnk5Lw0XQ9pKWG+p2KoR5HjMBigZ6yv+/XtL3TGOnCS1+oAz56ABbAORCjTWhSJQisr8APNFiELAecY6QA==", + "dev": true, + "dependencies": { + "@ionic/utils-terminal": "2.3.4", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/eslint-config": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@ionic/eslint-config/-/eslint-config-0.3.0.tgz", + "integrity": "sha512-Uf1hS2YIoHlcvXPF5LnsPM6auMewEdChQhR117Rt3sVEAutbyKMpFP4slNC2a6up3a5Q34zepqlf61Qgkf9XeQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "^4.1.0", + "@typescript-eslint/parser": "^4.1.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-import": "^2.22.0" + }, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/@ionic/prettier-config": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@ionic/prettier-config/-/prettier-config-1.0.1.tgz", + "integrity": "sha512-/v8UOW7rxkw/hvrRe/QfjlQsdjkm3sfAHoE3uqffO5BoNGijQMARrT32JT9Ei0g6KySXPyxxW+7LzPHrQmfzCw==", + "dev": true, + "peerDependencies": { + "prettier": "^2.0.0" + } + }, + "node_modules/@ionic/swiftlint-config": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ionic/swiftlint-config/-/swiftlint-config-1.1.2.tgz", + "integrity": "sha512-UbE1AIlTowt9uR7fMzRtbQX4URcyuok7mcpdJfFDHAIGM6nDjohYMke+6xOr6ZYlLnEyVmBGNEg0+grEYRgcVg==", + "dev": true + }, + "node_modules/@ionic/utils-array": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz", + "integrity": "sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.7.tgz", + "integrity": "sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==", + "dev": true, + "dependencies": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-object": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.6.tgz", + "integrity": "sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-process": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.11.tgz", + "integrity": "sha512-Uavxn+x8j3rDlZEk1X7YnaN6wCgbCwYQOeIjv/m94i1dzslqWhqIHEqxEyeE8HsT5Negboagg7GtQiABy+BLbA==", + "dev": true, + "dependencies": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.4", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.6.tgz", + "integrity": "sha512-4+Kitey1lTA1yGtnigeYNhV/0tggI3lWBMjC7tBs1K9GXa/q7q4CtOISppdh8QgtOhrhAXS2Igp8rbko/Cj+lA==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-subprocess": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.12.tgz", + "integrity": "sha512-N05Y+dIXBHofKWJTheCMzVqmgY9wFmZcRv/LdNnfXaaA/mxLTyGxQYeig8fvQXTtDafb/siZXcrTkmQ+y6n3Yg==", + "dev": true, + "dependencies": { + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.11", + "@ionic/utils-stream": "3.1.6", + "@ionic/utils-terminal": "2.3.4", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-terminal": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.4.tgz", + "integrity": "sha512-cEiMFl3jklE0sW60r8JHH3ijFTwh/jkdEKWbylSyExQwZ8pPuwoXz7gpkWoJRLuoRHHSvg+wzNYyPJazIHfoJA==", + "dev": true, + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/fs-extra": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.3.tgz", + "integrity": "sha512-7IdV01N0u/CaVO0fuY1YmEg14HQN3+EW8mpNgg6NEfxEl/lzCa5OxlBu3iFsCAdamnYOcTQ7oEi43Xc/67Rgzw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", + "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "4.33.0", + "@typescript-eslint/scope-manager": "4.33.0", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^4.0.0", + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", + "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", + "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "debug": "^4.3.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", + "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", + "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "dev": true, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", + "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "dev": true, + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chevrotain": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-6.5.0.tgz", + "integrity": "sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==", + "dev": true, + "dependencies": { + "regexp-to-ast": "0.4.0" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/elementtree": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==", + "dev": true, + "dependencies": { + "sax": "1.1.4" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", + "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.1", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", + "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", + "dev": true, + "dependencies": { + "get-stdin": "^6.0.0" + }, + "bin": { + "eslint-config-prettier-check": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=3.14.1" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", + "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.findlastindex": "^1.2.2", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.8.0", + "has": "^1.0.3", + "is-core-module": "^2.13.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.6", + "object.groupby": "^1.0.0", + "object.values": "^1.1.6", + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/github-slugger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", + "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", + "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/java-parser": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-parser/-/java-parser-1.0.2.tgz", + "integrity": "sha512-lBXc+F62ds2W83eH5MwGnzuWdb6kgGBV0x0R7w0B4JKGDrJzolMUEhRMzzzlIX68HvRU7XtfPon22YaB+dVg+A==", + "dev": true, + "dependencies": { + "chevrotain": "6.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/native-run": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/native-run/-/native-run-1.7.3.tgz", + "integrity": "sha512-vEw8X3Yu8TAbP4/uCJV3nCsCrhfHgUecRRDc69ZU9EK0QXHHc7YDzmIeI7SfA08ywzPlC9YcpITcB6bgMbrtwQ==", + "dev": true, + "dependencies": { + "@ionic/utils-fs": "^3.1.6", + "@ionic/utils-terminal": "^2.3.3", + "bplist-parser": "^0.3.2", + "debug": "^4.3.4", + "elementtree": "^0.1.7", + "ini": "^3.0.1", + "plist": "^3.0.6", + "split2": "^4.1.0", + "through2": "^4.0.2", + "tslib": "^2.4.0", + "yauzl": "^2.10.0" + }, + "bin": { + "native-run": "bin/native-run" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/object-inspect": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.0.tgz", + "integrity": "sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dev": true, + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/prettier-plugin-java": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-java/-/prettier-plugin-java-1.0.2.tgz", + "integrity": "sha512-YgcN1WGZlrH0E+bHdqtIYtfDp6k2PHBnIaGjzdff/7t/NyDWAA6ypAmnD7YQVG2OuoIaXYkC37HN7cz68lLWLg==", + "dev": true, + "dependencies": { + "java-parser": "1.0.2", + "lodash": "4.17.21", + "prettier": "2.2.1" + } + }, + "node_modules/prettier-plugin-java/node_modules/prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/regexp-to-ast": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.4.0.tgz", + "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sax": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", + "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==", + "dev": true + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swiftlint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/swiftlint/-/swiftlint-1.0.2.tgz", + "integrity": "sha512-YhcS0N3vkwBatnZf/iJPg89LGQk7MbFnz67Cg3EZ6Ppqm2H8y6x7A1t6KMQ0jYVQpea9wQiFiFRFhkoChaQ29Q==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@ionic/utils-fs": "3.1.6", + "@ionic/utils-subprocess": "2.1.11", + "cosmiconfig": "^6.0.0" + }, + "bin": { + "node-swiftlint": "bin.js" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-array": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.5.tgz", + "integrity": "sha512-HD72a71IQVBmQckDwmA8RxNVMTbxnaLbgFOl+dO5tbvW9CkkSFCv41h6fUuNsSEVgngfkn0i98HDuZC8mk+lTA==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-fs": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.6.tgz", + "integrity": "sha512-eikrNkK89CfGPmexjTfSWl4EYqsPSBh0Ka7by4F0PLc1hJZYtJxUZV3X4r5ecA8ikjicUmcbU7zJmAjmqutG/w==", + "dev": true, + "dependencies": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-object": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.5.tgz", + "integrity": "sha512-XnYNSwfewUqxq+yjER1hxTKggftpNjFLJH0s37jcrNDwbzmbpFTQTVAp4ikNK4rd9DOebX/jbeZb8jfD86IYxw==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-process": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.10.tgz", + "integrity": "sha512-mZ7JEowcuGQK+SKsJXi0liYTcXd2bNMR3nE0CyTROpMECUpJeAvvaBaPGZf5ERQUPeWBVuwqAqjUmIdxhz5bxw==", + "dev": true, + "dependencies": { + "@ionic/utils-object": "2.1.5", + "@ionic/utils-terminal": "2.3.3", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-stream": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.5.tgz", + "integrity": "sha512-hkm46uHvEC05X/8PHgdJi4l4zv9VQDELZTM+Kz69odtO9zZYfnt8DkfXHJqJ+PxmtiE5mk/ehJWLnn/XAczTUw==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-subprocess": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.11.tgz", + "integrity": "sha512-6zCDixNmZCbMCy5np8klSxOZF85kuDyzZSTTQKQP90ZtYNCcPYmuFSzaqDwApJT4r5L3MY3JrqK1gLkc6xiUPw==", + "dev": true, + "dependencies": { + "@ionic/utils-array": "2.1.5", + "@ionic/utils-fs": "3.1.6", + "@ionic/utils-process": "2.1.10", + "@ionic/utils-stream": "3.1.5", + "@ionic/utils-terminal": "2.3.3", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-terminal": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.3.tgz", + "integrity": "sha512-RnuSfNZ5fLEyX3R5mtcMY97cGD1A0NVBbarsSQ6yMMfRJ5YHU7hHVyUfvZeClbqkBC/pAqI/rYJuXKCT9YeMCQ==", + "dev": true, + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.6.tgz", + "integrity": "sha512-pxnwLxeb/Z5SP80JDRzVjh58KsM6jZHRAOtTpS7sXLS4ogXNKC9ANxHHZqLLeVHZN35jCtI4JdmLLbLiC1kBow==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/mobile/plugins/capacitor-splash-screen/package.json b/mobile/plugins/capacitor-splash-screen/package.json new file mode 100644 index 00000000..add762df --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/package.json @@ -0,0 +1,80 @@ +{ + "name": "capacitor-splash-screen", + "version": "5.0.6.1", + "description": "The Splash Screen API provides methods for showing or hiding a Splash image.", + "main": "dist/plugin.cjs.js", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", + "files": [ + "android/src/main/", + "android/build.gradle", + "dist/", + "ios/Plugin/", + "CapacitorSplashScreen.podspec" + ], + "author": "Ionic ", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/korenskoy/capacitor-splash-screen.git" + }, + "bugs": { + "url": "https://github.com/ionic-team/capacitor-plugins/issues" + }, + "keywords": [ + "capacitor", + "plugin", + "native" + ], + "scripts": { + "verify": "npm run verify:ios && npm run verify:android && npm run verify:web", + "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..", + "verify:android": "cd android && ./gradlew clean build test && cd ..", + "verify:web": "npm run build", + "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint", + "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format", + "eslint": "eslint . --ext ts", + "prettier": "prettier \"**/*.{css,html,ts,js,java}\"", + "swiftlint": "node-swiftlint", + "docgen": "docgen --api SplashScreenPlugin --output-readme README.md --output-json dist/docs.json", + "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js", + "clean": "rimraf ./dist", + "watch": "tsc --watch", + "prepublishOnly": "npm run build", + "publish:cocoapod": "pod trunk push ./CapacitorSplashScreen.podspec --allow-warnings" + }, + "devDependencies": { + "@capacitor/android": "^5.0.0", + "@capacitor/cli": "^5.0.0", + "@capacitor/core": "^5.0.0", + "@capacitor/docgen": "0.2.0", + "@capacitor/ios": "^5.0.0", + "@ionic/eslint-config": "^0.3.0", + "@ionic/prettier-config": "~1.0.1", + "@ionic/swiftlint-config": "^1.1.2", + "eslint": "^7.11.0", + "prettier": "~2.3.0", + "prettier-plugin-java": "~1.0.2", + "rimraf": "^3.0.2", + "rollup": "^2.32.0", + "swiftlint": "^1.0.1", + "typescript": "~4.1.5" + }, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + }, + "prettier": "@ionic/prettier-config", + "swiftlint": "@ionic/swiftlint-config", + "eslintConfig": { + "extends": "@ionic/eslint-config/recommended" + }, + "capacitor": { + "ios": { + "src": "ios" + }, + "android": { + "src": "android" + } + } +} diff --git a/mobile/plugins/capacitor-splash-screen/rollup.config.js b/mobile/plugins/capacitor-splash-screen/rollup.config.js new file mode 100644 index 00000000..a50ed81f --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/rollup.config.js @@ -0,0 +1,22 @@ +export default { + input: 'dist/esm/index.js', + output: [ + { + file: 'dist/plugin.js', + format: 'iife', + name: 'capacitorSplashScreen', + globals: { + '@capacitor/core': 'capacitorExports', + }, + sourcemap: true, + inlineDynamicImports: true, + }, + { + file: 'dist/plugin.cjs.js', + format: 'cjs', + sourcemap: true, + inlineDynamicImports: true, + }, + ], + external: ['@capacitor/core'], +}; diff --git a/mobile/plugins/capacitor-splash-screen/src/definitions.ts b/mobile/plugins/capacitor-splash-screen/src/definitions.ts new file mode 100644 index 00000000..2b0ab5ad --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/src/definitions.ts @@ -0,0 +1,249 @@ +/// + +declare module '@capacitor/cli' { + export interface PluginsConfig { + /** + * These config values are available: + */ + SplashScreen?: { + /** + * How long to show the launch splash screen when autoHide is enabled (in ms) + * + * @since 1.0.0 + * @default 500 + * @example 3000 + */ + launchShowDuration?: number; + + /** + * Whether to auto hide the splash after launchShowDuration. + * + * @since 1.0.0 + * @default true + * @example true + */ + launchAutoHide?: boolean; + + /** + * Duration for the fade out animation of the launch splash screen (in ms) + * + * Only available for Android, when using the Android 12 Splash Screen API. + * + * @since 4.2.0 + * @default 200 + * @example 3000 + */ + launchFadeOutDuration?: number; + + /** + * Color of the background of the Splash Screen in hex format, #RRGGBB or #RRGGBBAA. + * Doesn't work if `useDialog` is true or on launch when using the Android 12 API. + * + * @since 1.0.0 + * @example "#ffffffff" + */ + backgroundColor?: string; + + /** + * Name of the resource to be used as Splash Screen. + * + * Doesn't work on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.0.0 + * @default splash + * @example "splash" + */ + androidSplashResourceName?: string; + + /** + * The [ImageView.ScaleType](https://developer.android.com/reference/android/widget/ImageView.ScaleType) used to scale + * the Splash Screen image. + * Doesn't work if `useDialog` is true or on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.0.0 + * @default FIT_XY + * @example "CENTER_CROP" + */ + androidScaleType?: + | 'CENTER' + | 'CENTER_CROP' + | 'CENTER_INSIDE' + | 'FIT_CENTER' + | 'FIT_END' + | 'FIT_START' + | 'FIT_XY' + | 'MATRIX'; + + /** + * Show a loading spinner on the Splash Screen. + * Doesn't work if `useDialog` is true or on launch when using the Android 12 API. + * + * @since 1.0.0 + * @example true + */ + showSpinner?: boolean; + + /** + * Style of the Android spinner. + * Doesn't work if `useDialog` is true or on launch when using the Android 12 API. + * + * @since 1.0.0 + * @default large + * @example "large" + */ + androidSpinnerStyle?: + | 'horizontal' + | 'small' + | 'large' + | 'inverse' + | 'smallInverse' + | 'largeInverse'; + + /** + * Style of the iOS spinner. + * Doesn't work if `useDialog` is true. + * + * Only available on iOS. + * + * @since 1.0.0 + * @default large + * @example "small" + */ + iosSpinnerStyle?: 'large' | 'small'; + + /** + * Color of the spinner in hex format, #RRGGBB or #RRGGBBAA. + * Doesn't work if `useDialog` is true or on launch when using the Android 12 API. + * + * @since 1.0.0 + * @example "#999999" + */ + spinnerColor?: string; + + /** + * Hide the status bar on the Splash Screen. + * + * Doesn't work on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.0.0 + * @example true + */ + splashFullScreen?: boolean; + + /** + * Hide the status bar and the software navigation buttons on the Splash Screen. + * + * Doesn't work on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.0.0 + * @example true + */ + splashImmersive?: boolean; + + /** + * If `useDialog` is set to true, configure the Dialog layout. + * If `useDialog` is not set or false, use a layout instead of the ImageView. + * + * Doesn't work on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.1.0 + * @example "launch_screen" + */ + layoutName?: string; + + /** + * Use a Dialog instead of an ImageView. + * If `layoutName` is not configured, it will use + * a layout that uses the splash image as background. + * + * Doesn't work on launch when using the Android 12 API. + * + * Only available on Android. + * + * @since 1.1.0 + * @example true + */ + useDialog?: boolean; + }; + } +} + +export interface ShowOptions { + /** + * Whether to auto hide the splash after showDuration + * + * @since 1.0.0 + */ + autoHide?: boolean; + /** + * How long (in ms) to fade in. + * + * @since 1.0.0 + * @default 200 + */ + fadeInDuration?: number; + /** + * How long (in ms) to fade out. + * + * @since 1.0.0 + * @default 200 + */ + fadeOutDuration?: number; + /** + * How long to show the splash screen when autoHide is enabled (in ms) + * + * @since 1.0.0 + * @default 3000 + */ + showDuration?: number; +} + +export interface HideOptions { + /** + * How long (in ms) to fade out. + * + * On Android, if using the Android 12 Splash Screen API, it's not being used. + * Use launchFadeOutDuration configuration option instead. + * + * @since 1.0.0 + * @default 200 + */ + fadeOutDuration?: number; +} + +export interface SplashScreenPlugin { + /** + * Show the splash screen + * + * @since 1.0.0 + */ + show(options?: ShowOptions): Promise; + /** + * Hide the splash screen + * + * @since 1.0.0 + */ + hide(options?: HideOptions): Promise; +} + +/** + * @deprecated Use `ShowOptions`. + * @since 1.0.0 + */ +export type SplashScreenShowOptions = ShowOptions; + +/** + * @deprecated Use `HideOptions`. + * @since 1.0.0 + */ +export type SplashScreenHideOptions = HideOptions; diff --git a/mobile/plugins/capacitor-splash-screen/src/index.ts b/mobile/plugins/capacitor-splash-screen/src/index.ts new file mode 100644 index 00000000..98b918eb --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/src/index.ts @@ -0,0 +1,10 @@ +import { registerPlugin } from '@capacitor/core'; + +import type { SplashScreenPlugin } from './definitions'; + +const SplashScreen = registerPlugin('SplashScreen', { + web: () => import('./web').then(m => new m.SplashScreenWeb()), +}); + +export * from './definitions'; +export { SplashScreen }; diff --git a/mobile/plugins/capacitor-splash-screen/src/web.ts b/mobile/plugins/capacitor-splash-screen/src/web.ts new file mode 100644 index 00000000..bf9a9f66 --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/src/web.ts @@ -0,0 +1,17 @@ +import { WebPlugin } from '@capacitor/core'; + +import type { + HideOptions, + ShowOptions, + SplashScreenPlugin, +} from './definitions'; + +export class SplashScreenWeb extends WebPlugin implements SplashScreenPlugin { + async show(_options?: ShowOptions): Promise { + return undefined; + } + + async hide(_options?: HideOptions): Promise { + return undefined; + } +} diff --git a/mobile/plugins/capacitor-splash-screen/tsconfig.json b/mobile/plugins/capacitor-splash-screen/tsconfig.json new file mode 100644 index 00000000..f2e88e6a --- /dev/null +++ b/mobile/plugins/capacitor-splash-screen/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "allowUnreachableCode": false, + "declaration": true, + "esModuleInterop": true, + "inlineSources": true, + "lib": ["dom", "es2017"], + "module": "esnext", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "outDir": "dist/esm", + "pretty": true, + "sourceMap": true, + "strict": true, + "target": "es2017" + }, + "files": ["src/index.ts"] +} diff --git a/mobile/plugins/native-bottom-sheet/.eslintignore b/mobile/plugins/native-bottom-sheet/.eslintignore new file mode 100644 index 00000000..9d0b71a3 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/.eslintignore @@ -0,0 +1,2 @@ +build +dist diff --git a/mobile/plugins/native-bottom-sheet/.gitignore b/mobile/plugins/native-bottom-sheet/.gitignore new file mode 100644 index 00000000..d4c3b664 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/.gitignore @@ -0,0 +1,61 @@ +# node files +!dist +node_modules + +# iOS files +Pods +Podfile.lock +Build +xcuserdata + +# macOS files +.DS_Store + + + +# Based on Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore + +# Built application files +*.apk +*.ap_ + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin +gen +out + +# Gradle files +.gradle +build + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation + +# Android Studio captures folder +captures + +# IntelliJ +*.iml +.idea + +# Keystore files +# Uncomment the following line if you do not want to check your keystore files in. +#*.jks + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild diff --git a/mobile/plugins/native-bottom-sheet/.prettierignore b/mobile/plugins/native-bottom-sheet/.prettierignore new file mode 100644 index 00000000..9d0b71a3 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/.prettierignore @@ -0,0 +1,2 @@ +build +dist diff --git a/mobile/plugins/native-bottom-sheet/CONTRIBUTING.md b/mobile/plugins/native-bottom-sheet/CONTRIBUTING.md new file mode 100644 index 00000000..3f875180 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/CONTRIBUTING.md @@ -0,0 +1,52 @@ +# Contributing + +This guide provides instructions for contributing to this Capacitor plugin. + +## Developing + +### Local Setup + +1. Fork and clone the repo. +1. Install the dependencies. + + ```shell + npm install + ``` + +1. Install SwiftLint if you're on macOS. + + ```shell + brew install swiftlint + ``` + +### Scripts + +#### `npm run build` + +Build the plugin web assets and generate plugin API documentation using [`@capacitor/docgen`](https://github.com/ionic-team/capacitor-docgen). + +It will compile the TypeScript code from `src/` into ESM JavaScript in `dist/esm/`. These files are used in apps with bundlers when your plugin is imported. + +Then, Rollup will bundle the code into a single file at `dist/plugin.js`. This file is used in apps without bundlers by including it as a script in `index.html`. + +#### `npm run verify` + +Build and validate the web and native projects. + +This is useful to run in CI to verify that the plugin builds for all platforms. + +#### `npm run lint` / `npm run fmt` + +Check formatting and code quality, autoformat/autofix if possible. + +This template is integrated with ESLint, Prettier, and SwiftLint. Using these tools is completely optional, but the [Capacitor Community](https://github.com/capacitor-community/) strives to have consistent code style and structure for easier cooperation. + +## Publishing + +There is a `prepublishOnly` hook in `package.json` which prepares the plugin before publishing, so all you need to do is run: + +```shell +npm publish +``` + +> **Note**: The [`files`](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#files) array in `package.json` specifies which files get published. If you rename files/directories or add files elsewhere, you may need to update it. diff --git a/mobile/plugins/native-bottom-sheet/NativeBottomSheet.podspec b/mobile/plugins/native-bottom-sheet/NativeBottomSheet.podspec new file mode 100644 index 00000000..f2fafeb3 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/NativeBottomSheet.podspec @@ -0,0 +1,18 @@ +require 'json' + +package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) + +Pod::Spec.new do |s| + s.name = 'NativeBottomSheet' + s.version = package['version'] + s.summary = package['description'] + s.license = package['license'] + s.homepage = package['repository']['url'] + s.author = package['author'] + s.source = { :git => package['repository']['url'], :tag => s.version.to_s } + s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' + s.ios.deployment_target = '13.0' + s.dependency 'Capacitor' + s.dependency 'FloatingPanel' + s.swift_version = '5.1' +end diff --git a/mobile/plugins/native-bottom-sheet/README.md b/mobile/plugins/native-bottom-sheet/README.md new file mode 100644 index 00000000..6b92d0d0 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/README.md @@ -0,0 +1,248 @@ +# native-bottom-sheet + +Allows to open a native BottomSheet/FloatingPanel on iOS + +## Install + +```bash +npm install native-bottom-sheet +npx cap sync +``` + +## API + + + +* [`prepare()`](#prepare) +* [`delegate(...)`](#delegate) +* [`release(...)`](#release) +* [`openSelf(...)`](#openself) +* [`closeSelf(...)`](#closeself) +* [`setSelfSize(...)`](#setselfsize) +* [`callActionInMain(...)`](#callactioninmain) +* [`callActionInNative(...)`](#callactioninnative) +* [`openInMain(...)`](#openinmain) +* [`addListener('delegate', ...)`](#addlistenerdelegate) +* [`addListener('move', ...)`](#addlistenermove) +* [`addListener('callActionInMain', ...)`](#addlistenercallactioninmain) +* [`addListener('callActionInNative', ...)`](#addlistenercallactioninnative) +* [`addListener('openInMain', ...)`](#addlisteneropeninmain) +* [Interfaces](#interfaces) +* [Type Aliases](#type-aliases) + + + + + + +### prepare() + +```typescript +prepare() => Promise +``` + +-------------------- + + +### delegate(...) + +```typescript +delegate(options: { key: BottomSheetKeys; globalJson: string; }) => Promise +``` + +| Param | Type | +| ------------- | ----------------------------------------------------------------------------------------- | +| **`options`** | { key: BottomSheetKeys; globalJson: string; } | + +-------------------- + + +### release(...) + +```typescript +release(options: { key: BottomSheetKeys | '*'; }) => Promise +``` + +| Param | Type | +| ------------- | ---------------------------------------------------------------------------- | +| **`options`** | { key: BottomSheetKeys \| '*'; } | + +-------------------- + + +### openSelf(...) + +```typescript +openSelf(options: { key: BottomSheetKeys; height: string; backgroundColor: string; }) => Promise +``` + +| Param | Type | +| ------------- | -------------------------------------------------------------------------------------------------------------- | +| **`options`** | { key: BottomSheetKeys; height: string; backgroundColor: string; } | + +-------------------- + + +### closeSelf(...) + +```typescript +closeSelf(options: { key: BottomSheetKeys; }) => Promise +``` + +| Param | Type | +| ------------- | --------------------------------------------------------------------- | +| **`options`** | { key: BottomSheetKeys; } | + +-------------------- + + +### setSelfSize(...) + +```typescript +setSelfSize(options: { size: 'half' | 'full'; }) => Promise +``` + +| Param | Type | +| ------------- | ---------------------------------------- | +| **`options`** | { size: 'half' \| 'full'; } | + +-------------------- + + +### callActionInMain(...) + +```typescript +callActionInMain(options: { name: string; optionsJson: string; }) => Promise +``` + +| Param | Type | +| ------------- | --------------------------------------------------- | +| **`options`** | { name: string; optionsJson: string; } | + +-------------------- + + +### callActionInNative(...) + +```typescript +callActionInNative(options: { name: string; optionsJson: string; }) => Promise +``` + +| Param | Type | +| ------------- | --------------------------------------------------- | +| **`options`** | { name: string; optionsJson: string; } | + +-------------------- + + +### openInMain(...) + +```typescript +openInMain(options: { key: BottomSheetKeys; }) => Promise +``` + +| Param | Type | +| ------------- | --------------------------------------------------------------------- | +| **`options`** | { key: BottomSheetKeys; } | + +-------------------- + + +### addListener('delegate', ...) + +```typescript +addListener(eventName: 'delegate', handler: (options: { key: BottomSheetKeys; globalJson: string; }) => void) => Promise & PluginListenerHandle +``` + +| Param | Type | +| --------------- | --------------------------------------------------------------------------------------------------------------- | +| **`eventName`** | 'delegate' | +| **`handler`** | (options: { key: BottomSheetKeys; globalJson: string; }) => void | + +**Returns:** Promise<PluginListenerHandle> & PluginListenerHandle + +-------------------- + + +### addListener('move', ...) + +```typescript +addListener(eventName: 'move', handler: () => void) => Promise & PluginListenerHandle +``` + +| Param | Type | +| --------------- | -------------------------- | +| **`eventName`** | 'move' | +| **`handler`** | () => void | + +**Returns:** Promise<PluginListenerHandle> & PluginListenerHandle + +-------------------- + + +### addListener('callActionInMain', ...) + +```typescript +addListener(eventName: 'callActionInMain', handler: (options: { name: string; optionsJson: string; }) => void) => Promise & PluginListenerHandle +``` + +| Param | Type | +| --------------- | ------------------------------------------------------------------------- | +| **`eventName`** | 'callActionInMain' | +| **`handler`** | (options: { name: string; optionsJson: string; }) => void | + +**Returns:** Promise<PluginListenerHandle> & PluginListenerHandle + +-------------------- + + +### addListener('callActionInNative', ...) + +```typescript +addListener(eventName: 'callActionInNative', handler: (options: { name: string; optionsJson: string; }) => void) => Promise & PluginListenerHandle +``` + +| Param | Type | +| --------------- | ------------------------------------------------------------------------- | +| **`eventName`** | 'callActionInNative' | +| **`handler`** | (options: { name: string; optionsJson: string; }) => void | + +**Returns:** Promise<PluginListenerHandle> & PluginListenerHandle + +-------------------- + + +### addListener('openInMain', ...) + +```typescript +addListener(eventName: 'openInMain', handler: (options: { key: BottomSheetKeys; }) => void) => Promise & PluginListenerHandle +``` + +| Param | Type | +| --------------- | ------------------------------------------------------------------------------------------- | +| **`eventName`** | 'openInMain' | +| **`handler`** | (options: { key: BottomSheetKeys; }) => void | + +**Returns:** Promise<PluginListenerHandle> & PluginListenerHandle + +-------------------- + + +### Interfaces + + +#### PluginListenerHandle + +| Prop | Type | +| ------------ | ----------------------------------------- | +| **`remove`** | () => Promise<void> | + + +### Type Aliases + + +#### BottomSheetKeys + +'initial' | 'receive' | 'invoice' | 'transfer' | 'swap' | 'stake' | 'unstake' | 'staking-info' | 'transaction-info' | 'swap-activity' | 'backup' | 'add-account' | 'settings' | 'qr-scanner' | 'dapp-connect' | 'dapp-transaction' | 'disclaimer' | 'backup-warning' + + diff --git a/mobile/plugins/native-bottom-sheet/android/.gitignore b/mobile/plugins/native-bottom-sheet/android/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/.gitignore @@ -0,0 +1 @@ +/build diff --git a/mobile/plugins/native-bottom-sheet/android/build.gradle b/mobile/plugins/native-bottom-sheet/android/build.gradle new file mode 100644 index 00000000..d27d08bd --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/build.gradle @@ -0,0 +1,58 @@ +ext { + junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2' + androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1' + androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.5' + androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.5.1' +} + +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.0.0' + } +} + +apply plugin: 'com.android.library' + +android { + namespace "org.mytonwallet.plugins.nativebottomsheet" + compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 33 + defaultConfig { + minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 33 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } +} + +repositories { + google() + mavenCentral() +} + + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation project(':capacitor-android') + implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" + testImplementation "junit:junit:$junitVersion" + androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" +} diff --git a/mobile/plugins/native-bottom-sheet/android/gradle.properties b/mobile/plugins/native-bottom-sheet/android/gradle.properties new file mode 100644 index 00000000..2e87c52f --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/gradle.properties @@ -0,0 +1,22 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true diff --git a/mobile/plugins/native-bottom-sheet/android/gradle/wrapper/gradle-wrapper.jar b/mobile/plugins/native-bottom-sheet/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..ccebba7710deaf9f98673a68957ea02138b60d0a GIT binary patch literal 61608 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 literal 0 HcmV?d00001 diff --git a/mobile/plugins/native-bottom-sheet/android/gradle/wrapper/gradle-wrapper.properties b/mobile/plugins/native-bottom-sheet/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..761b8f08 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/mobile/plugins/native-bottom-sheet/android/gradlew b/mobile/plugins/native-bottom-sheet/android/gradlew new file mode 100755 index 00000000..79a61d42 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/mobile/plugins/native-bottom-sheet/android/gradlew.bat b/mobile/plugins/native-bottom-sheet/android/gradlew.bat new file mode 100644 index 00000000..93e3f59f --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mobile/plugins/native-bottom-sheet/android/proguard-rules.pro b/mobile/plugins/native-bottom-sheet/android/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/plugins/native-bottom-sheet/android/settings.gradle b/mobile/plugins/native-bottom-sheet/android/settings.gradle new file mode 100644 index 00000000..1e5b8431 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/settings.gradle @@ -0,0 +1,2 @@ +include ':capacitor-android' +project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') \ No newline at end of file diff --git a/mobile/plugins/native-bottom-sheet/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java b/mobile/plugins/native-bottom-sheet/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java new file mode 100644 index 00000000..58020e16 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.getcapacitor.android; + +import static org.junit.Assert.*; + +import android.content.Context; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("com.getcapacitor.android", appContext.getPackageName()); + } +} diff --git a/mobile/plugins/native-bottom-sheet/android/src/main/AndroidManifest.xml b/mobile/plugins/native-bottom-sheet/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a2f47b60 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/mobile/plugins/native-bottom-sheet/android/src/main/java/org/mytonwallet/plugins/nativebottomsheet/BottomSheet.java b/mobile/plugins/native-bottom-sheet/android/src/main/java/org/mytonwallet/plugins/nativebottomsheet/BottomSheet.java new file mode 100644 index 00000000..ae8b697c --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/src/main/java/org/mytonwallet/plugins/nativebottomsheet/BottomSheet.java @@ -0,0 +1,11 @@ +package org.mytonwallet.plugins.nativebottomsheet; + +import android.util.Log; + +public class BottomSheet { + + public String echo(String value) { + Log.i("Echo", value); + return value; + } +} diff --git a/mobile/plugins/native-bottom-sheet/android/src/main/java/org/mytonwallet/plugins/nativebottomsheet/BottomSheetPlugin.java b/mobile/plugins/native-bottom-sheet/android/src/main/java/org/mytonwallet/plugins/nativebottomsheet/BottomSheetPlugin.java new file mode 100644 index 00000000..701bc663 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/src/main/java/org/mytonwallet/plugins/nativebottomsheet/BottomSheetPlugin.java @@ -0,0 +1,22 @@ +package org.mytonwallet.plugins.nativebottomsheet; + +import com.getcapacitor.JSObject; +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; + +@CapacitorPlugin(name = "BottomSheet") +public class BottomSheetPlugin extends Plugin { + + private BottomSheet implementation = new BottomSheet(); + + @PluginMethod + public void echo(PluginCall call) { + String value = call.getString("value"); + + JSObject ret = new JSObject(); + ret.put("value", implementation.echo(value)); + call.resolve(ret); + } +} diff --git a/mobile/plugins/native-bottom-sheet/android/src/main/res/.gitkeep b/mobile/plugins/native-bottom-sheet/android/src/main/res/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/mobile/plugins/native-bottom-sheet/android/src/test/java/com/getcapacitor/ExampleUnitTest.java b/mobile/plugins/native-bottom-sheet/android/src/test/java/com/getcapacitor/ExampleUnitTest.java new file mode 100644 index 00000000..a0fed0cf --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/android/src/test/java/com/getcapacitor/ExampleUnitTest.java @@ -0,0 +1,18 @@ +package com.getcapacitor; + +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} diff --git a/mobile/plugins/native-bottom-sheet/dist/docs.json b/mobile/plugins/native-bottom-sheet/dist/docs.json new file mode 100644 index 00000000..a0595e64 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/dist/docs.json @@ -0,0 +1,377 @@ +{ + "api": { + "name": "BottomSheetPlugin", + "slug": "bottomsheetplugin", + "docs": "", + "tags": [], + "methods": [ + { + "name": "prepare", + "signature": "() => Promise", + "parameters": [], + "returns": "Promise", + "tags": [], + "docs": "", + "complexTypes": [], + "slug": "prepare" + }, + { + "name": "delegate", + "signature": "(options: { key: BottomSheetKeys; globalJson: string; }) => Promise", + "parameters": [ + { + "name": "options", + "docs": "", + "type": "{ key: BottomSheetKeys; globalJson: string; }" + } + ], + "returns": "Promise", + "tags": [], + "docs": "", + "complexTypes": [ + "BottomSheetKeys" + ], + "slug": "delegate" + }, + { + "name": "release", + "signature": "(options: { key: BottomSheetKeys | '*'; }) => Promise", + "parameters": [ + { + "name": "options", + "docs": "", + "type": "{ key: BottomSheetKeys | '*'; }" + } + ], + "returns": "Promise", + "tags": [], + "docs": "", + "complexTypes": [ + "BottomSheetKeys" + ], + "slug": "release" + }, + { + "name": "openSelf", + "signature": "(options: { key: BottomSheetKeys; height: string; backgroundColor: string; }) => Promise", + "parameters": [ + { + "name": "options", + "docs": "", + "type": "{ key: BottomSheetKeys; height: string; backgroundColor: string; }" + } + ], + "returns": "Promise", + "tags": [], + "docs": "", + "complexTypes": [ + "BottomSheetKeys" + ], + "slug": "openself" + }, + { + "name": "closeSelf", + "signature": "(options: { key: BottomSheetKeys; }) => Promise", + "parameters": [ + { + "name": "options", + "docs": "", + "type": "{ key: BottomSheetKeys; }" + } + ], + "returns": "Promise", + "tags": [], + "docs": "", + "complexTypes": [ + "BottomSheetKeys" + ], + "slug": "closeself" + }, + { + "name": "setSelfSize", + "signature": "(options: { size: 'half' | 'full'; }) => Promise", + "parameters": [ + { + "name": "options", + "docs": "", + "type": "{ size: 'half' | 'full'; }" + } + ], + "returns": "Promise", + "tags": [], + "docs": "", + "complexTypes": [], + "slug": "setselfsize" + }, + { + "name": "callActionInMain", + "signature": "(options: { name: string; optionsJson: string; }) => Promise", + "parameters": [ + { + "name": "options", + "docs": "", + "type": "{ name: string; optionsJson: string; }" + } + ], + "returns": "Promise", + "tags": [], + "docs": "", + "complexTypes": [], + "slug": "callactioninmain" + }, + { + "name": "callActionInNative", + "signature": "(options: { name: string; optionsJson: string; }) => Promise", + "parameters": [ + { + "name": "options", + "docs": "", + "type": "{ name: string; optionsJson: string; }" + } + ], + "returns": "Promise", + "tags": [], + "docs": "", + "complexTypes": [], + "slug": "callactioninnative" + }, + { + "name": "openInMain", + "signature": "(options: { key: BottomSheetKeys; }) => Promise", + "parameters": [ + { + "name": "options", + "docs": "", + "type": "{ key: BottomSheetKeys; }" + } + ], + "returns": "Promise", + "tags": [], + "docs": "", + "complexTypes": [ + "BottomSheetKeys" + ], + "slug": "openinmain" + }, + { + "name": "addListener", + "signature": "(eventName: 'delegate', handler: (options: { key: BottomSheetKeys; globalJson: string; }) => void) => Promise & PluginListenerHandle", + "parameters": [ + { + "name": "eventName", + "docs": "", + "type": "'delegate'" + }, + { + "name": "handler", + "docs": "", + "type": "(options: { key: BottomSheetKeys; globalJson: string; }) => void" + } + ], + "returns": "Promise & PluginListenerHandle", + "tags": [], + "docs": "", + "complexTypes": [ + "PluginListenerHandle", + "BottomSheetKeys" + ], + "slug": "addlistenerdelegate" + }, + { + "name": "addListener", + "signature": "(eventName: 'move', handler: () => void) => Promise & PluginListenerHandle", + "parameters": [ + { + "name": "eventName", + "docs": "", + "type": "'move'" + }, + { + "name": "handler", + "docs": "", + "type": "() => void" + } + ], + "returns": "Promise & PluginListenerHandle", + "tags": [], + "docs": "", + "complexTypes": [ + "PluginListenerHandle" + ], + "slug": "addlistenermove" + }, + { + "name": "addListener", + "signature": "(eventName: 'callActionInMain', handler: (options: { name: string; optionsJson: string; }) => void) => Promise & PluginListenerHandle", + "parameters": [ + { + "name": "eventName", + "docs": "", + "type": "'callActionInMain'" + }, + { + "name": "handler", + "docs": "", + "type": "(options: { name: string; optionsJson: string; }) => void" + } + ], + "returns": "Promise & PluginListenerHandle", + "tags": [], + "docs": "", + "complexTypes": [ + "PluginListenerHandle" + ], + "slug": "addlistenercallactioninmain" + }, + { + "name": "addListener", + "signature": "(eventName: 'callActionInNative', handler: (options: { name: string; optionsJson: string; }) => void) => Promise & PluginListenerHandle", + "parameters": [ + { + "name": "eventName", + "docs": "", + "type": "'callActionInNative'" + }, + { + "name": "handler", + "docs": "", + "type": "(options: { name: string; optionsJson: string; }) => void" + } + ], + "returns": "Promise & PluginListenerHandle", + "tags": [], + "docs": "", + "complexTypes": [ + "PluginListenerHandle" + ], + "slug": "addlistenercallactioninnative" + }, + { + "name": "addListener", + "signature": "(eventName: 'openInMain', handler: (options: { key: BottomSheetKeys; }) => void) => Promise & PluginListenerHandle", + "parameters": [ + { + "name": "eventName", + "docs": "", + "type": "'openInMain'" + }, + { + "name": "handler", + "docs": "", + "type": "(options: { key: BottomSheetKeys; }) => void" + } + ], + "returns": "Promise & PluginListenerHandle", + "tags": [], + "docs": "", + "complexTypes": [ + "PluginListenerHandle", + "BottomSheetKeys" + ], + "slug": "addlisteneropeninmain" + } + ], + "properties": [] + }, + "interfaces": [ + { + "name": "PluginListenerHandle", + "slug": "pluginlistenerhandle", + "docs": "", + "tags": [], + "methods": [], + "properties": [ + { + "name": "remove", + "tags": [], + "docs": "", + "complexTypes": [], + "type": "() => Promise" + } + ] + } + ], + "enums": [], + "typeAliases": [ + { + "name": "BottomSheetKeys", + "slug": "bottomsheetkeys", + "docs": "", + "types": [ + { + "text": "'initial'", + "complexTypes": [] + }, + { + "text": "'receive'", + "complexTypes": [] + }, + { + "text": "'invoice'", + "complexTypes": [] + }, + { + "text": "'transfer'", + "complexTypes": [] + }, + { + "text": "'swap'", + "complexTypes": [] + }, + { + "text": "'stake'", + "complexTypes": [] + }, + { + "text": "'unstake'", + "complexTypes": [] + }, + { + "text": "'staking-info'", + "complexTypes": [] + }, + { + "text": "'transaction-info'", + "complexTypes": [] + }, + { + "text": "'swap-activity'", + "complexTypes": [] + }, + { + "text": "'backup'", + "complexTypes": [] + }, + { + "text": "'add-account'", + "complexTypes": [] + }, + { + "text": "'settings'", + "complexTypes": [] + }, + { + "text": "'qr-scanner'", + "complexTypes": [] + }, + { + "text": "'dapp-connect'", + "complexTypes": [] + }, + { + "text": "'dapp-transaction'", + "complexTypes": [] + }, + { + "text": "'disclaimer'", + "complexTypes": [] + }, + { + "text": "'backup-warning'", + "complexTypes": [] + } + ] + } + ], + "pluginConfigs": [] +} \ No newline at end of file diff --git a/mobile/plugins/native-bottom-sheet/dist/esm/definitions.d.ts b/mobile/plugins/native-bottom-sheet/dist/esm/definitions.d.ts new file mode 100644 index 00000000..9af22629 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/dist/esm/definitions.d.ts @@ -0,0 +1,50 @@ +import { PluginListenerHandle } from '@capacitor/core'; +export declare type BottomSheetKeys = 'initial' | 'receive' | 'invoice' | 'transfer' | 'swap' | 'stake' | 'unstake' | 'staking-info' | 'transaction-info' | 'swap-activity' | 'backup' | 'add-account' | 'settings' | 'qr-scanner' | 'dapp-connect' | 'dapp-transaction' | 'disclaimer' | 'backup-warning'; +export interface BottomSheetPlugin { + prepare(): Promise; + delegate(options: { + key: BottomSheetKeys; + globalJson: string; + }): Promise; + release(options: { + key: BottomSheetKeys | '*'; + }): Promise; + openSelf(options: { + key: BottomSheetKeys; + height: string; + backgroundColor: string; + }): Promise; + closeSelf(options: { + key: BottomSheetKeys; + }): Promise; + setSelfSize(options: { + size: 'half' | 'full'; + }): Promise; + callActionInMain(options: { + name: string; + optionsJson: string; + }): Promise; + callActionInNative(options: { + name: string; + optionsJson: string; + }): Promise; + openInMain(options: { + key: BottomSheetKeys; + }): Promise; + addListener(eventName: 'delegate', handler: (options: { + key: BottomSheetKeys; + globalJson: string; + }) => void): Promise & PluginListenerHandle; + addListener(eventName: 'move', handler: () => void): Promise & PluginListenerHandle; + addListener(eventName: 'callActionInMain', handler: (options: { + name: string; + optionsJson: string; + }) => void): Promise & PluginListenerHandle; + addListener(eventName: 'callActionInNative', handler: (options: { + name: string; + optionsJson: string; + }) => void): Promise & PluginListenerHandle; + addListener(eventName: 'openInMain', handler: (options: { + key: BottomSheetKeys; + }) => void): Promise & PluginListenerHandle; +} diff --git a/mobile/plugins/native-bottom-sheet/dist/esm/definitions.js b/mobile/plugins/native-bottom-sheet/dist/esm/definitions.js new file mode 100644 index 00000000..497acb52 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/dist/esm/definitions.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=definitions.js.map \ No newline at end of file diff --git a/mobile/plugins/native-bottom-sheet/dist/esm/definitions.js.map b/mobile/plugins/native-bottom-sheet/dist/esm/definitions.js.map new file mode 100644 index 00000000..105a052d --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/dist/esm/definitions.js.map @@ -0,0 +1 @@ +{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import { PluginListenerHandle } from '@capacitor/core';\n\nexport type BottomSheetKeys =\n 'initial'\n | 'receive'\n | 'invoice'\n | 'transfer'\n | 'swap'\n | 'stake'\n | 'unstake'\n | 'staking-info'\n | 'transaction-info'\n | 'swap-activity'\n | 'backup'\n | 'add-account'\n | 'settings'\n | 'qr-scanner'\n | 'dapp-connect'\n | 'dapp-transaction'\n | 'disclaimer'\n | 'backup-warning';\n\nexport interface BottomSheetPlugin {\n prepare(): Promise;\n\n delegate(options: { key: BottomSheetKeys, globalJson: string }): Promise;\n\n release(options: { key: BottomSheetKeys | '*' }): Promise;\n\n openSelf(options: { key: BottomSheetKeys, height: string, backgroundColor: string }): Promise;\n\n closeSelf(options: { key: BottomSheetKeys }): Promise;\n\n setSelfSize(options: { size: 'half' | 'full' }): Promise;\n\n callActionInMain(options: { name: string, optionsJson: string }): Promise;\n\n callActionInNative(options: { name: string, optionsJson: string }): Promise;\n\n openInMain(options: { key: BottomSheetKeys }): Promise;\n\n addListener(\n eventName: 'delegate',\n handler: (options: { key: BottomSheetKeys, globalJson: string }) => void,\n ): Promise & PluginListenerHandle;\n\n addListener(\n eventName: 'move',\n handler: () => void,\n ): Promise & PluginListenerHandle;\n\n addListener(\n eventName: 'callActionInMain',\n handler: (options: { name: string, optionsJson: string }) => void,\n ): Promise & PluginListenerHandle;\n\n addListener(\n eventName: 'callActionInNative',\n handler: (options: { name: string, optionsJson: string }) => void,\n ): Promise & PluginListenerHandle;\n\n addListener(\n eventName: 'openInMain',\n handler: (options: { key: BottomSheetKeys }) => void,\n ): Promise & PluginListenerHandle;\n}\n"]} \ No newline at end of file diff --git a/mobile/plugins/native-bottom-sheet/dist/esm/index.d.ts b/mobile/plugins/native-bottom-sheet/dist/esm/index.d.ts new file mode 100644 index 00000000..beb13107 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/dist/esm/index.d.ts @@ -0,0 +1,4 @@ +import type { BottomSheetPlugin } from './definitions'; +declare const BottomSheet: BottomSheetPlugin; +export * from './definitions'; +export { BottomSheet }; diff --git a/mobile/plugins/native-bottom-sheet/dist/esm/index.js b/mobile/plugins/native-bottom-sheet/dist/esm/index.js new file mode 100644 index 00000000..0b0286c7 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/dist/esm/index.js @@ -0,0 +1,5 @@ +import { registerPlugin } from '@capacitor/core'; +const BottomSheet = registerPlugin('BottomSheet'); +export * from './definitions'; +export { BottomSheet }; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/mobile/plugins/native-bottom-sheet/dist/esm/index.js.map b/mobile/plugins/native-bottom-sheet/dist/esm/index.js.map new file mode 100644 index 00000000..78558ebe --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/dist/esm/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,WAAW,GAAG,cAAc,CAAoB,aAAa,CAAC,CAAC;AAErE,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { BottomSheetPlugin } from './definitions';\n\nconst BottomSheet = registerPlugin('BottomSheet');\n\nexport * from './definitions';\nexport { BottomSheet };\n"]} \ No newline at end of file diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.pbxproj b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.pbxproj new file mode 100644 index 00000000..93cbe6c9 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.pbxproj @@ -0,0 +1,573 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */; }; + 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */; }; + 2F98D68224C9AAE500613A4C /* BottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F98D68124C9AAE400613A4C /* BottomSheet.swift */; }; + 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFF88201F53D600D50D53 /* Plugin.framework */; }; + 50ADFF97201F53D600D50D53 /* BottomSheetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* BottomSheetTests.swift */; }; + 50ADFF99201F53D600D50D53 /* BottomSheetPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 50ADFF8B201F53D600D50D53 /* BottomSheetPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFFA52020D75100D50D53 /* Capacitor.framework */; }; + 50ADFFA82020EE4F00D50D53 /* BottomSheetPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFFA72020EE4F00D50D53 /* BottomSheetPlugin.m */; }; + 50E1A94820377CB70090CE1A /* BottomSheetPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1A94720377CB70090CE1A /* BottomSheetPlugin.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 50ADFF7F201F53D600D50D53 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 50ADFF87201F53D600D50D53; + remoteInfo = Plugin; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 2F98D68124C9AAE400613A4C /* BottomSheet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BottomSheet.swift; sourceTree = ""; }; + 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF88201F53D600D50D53 /* Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF8B201F53D600D50D53 /* BottomSheetPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BottomSheetPlugin.h; sourceTree = ""; }; + 50ADFF8C201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50ADFF91201F53D600D50D53 /* PluginTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PluginTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF96201F53D600D50D53 /* BottomSheetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomSheetTests.swift; sourceTree = ""; }; + 50ADFF98201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50ADFFA52020D75100D50D53 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFFA72020EE4F00D50D53 /* BottomSheetPlugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BottomSheetPlugin.m; sourceTree = ""; }; + 50E1A94720377CB70090CE1A /* BottomSheetPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BottomSheetPlugin.swift; sourceTree = ""; }; + 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig"; sourceTree = ""; }; + 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.release.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig"; sourceTree = ""; }; + 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig"; sourceTree = ""; }; + F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.release.xcconfig"; sourceTree = ""; }; + F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PluginTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 50ADFF84201F53D600D50D53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */, + 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8E201F53D600D50D53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */, + 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 50ADFF7E201F53D600D50D53 = { + isa = PBXGroup; + children = ( + 50ADFF8A201F53D600D50D53 /* Plugin */, + 50ADFF95201F53D600D50D53 /* PluginTests */, + 50ADFF89201F53D600D50D53 /* Products */, + 8C8E7744173064A9F6D438E3 /* Pods */, + A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */, + ); + sourceTree = ""; + }; + 50ADFF89201F53D600D50D53 /* Products */ = { + isa = PBXGroup; + children = ( + 50ADFF88201F53D600D50D53 /* Plugin.framework */, + 50ADFF91201F53D600D50D53 /* PluginTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 50ADFF8A201F53D600D50D53 /* Plugin */ = { + isa = PBXGroup; + children = ( + 50E1A94720377CB70090CE1A /* BottomSheetPlugin.swift */, + 2F98D68124C9AAE400613A4C /* BottomSheet.swift */, + 50ADFF8B201F53D600D50D53 /* BottomSheetPlugin.h */, + 50ADFFA72020EE4F00D50D53 /* BottomSheetPlugin.m */, + 50ADFF8C201F53D600D50D53 /* Info.plist */, + ); + path = Plugin; + sourceTree = ""; + }; + 50ADFF95201F53D600D50D53 /* PluginTests */ = { + isa = PBXGroup; + children = ( + 50ADFF96201F53D600D50D53 /* BottomSheetTests.swift */, + 50ADFF98201F53D600D50D53 /* Info.plist */, + ); + path = PluginTests; + sourceTree = ""; + }; + 8C8E7744173064A9F6D438E3 /* Pods */ = { + isa = PBXGroup; + children = ( + 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */, + 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */, + 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */, + F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 50ADFFA52020D75100D50D53 /* Capacitor.framework */, + 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */, + F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 50ADFF85201F53D600D50D53 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF99201F53D600D50D53 /* BottomSheetPlugin.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 50ADFF87201F53D600D50D53 /* Plugin */ = { + isa = PBXNativeTarget; + buildConfigurationList = 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */; + buildPhases = ( + AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */, + 50ADFF83201F53D600D50D53 /* Sources */, + 50ADFF84201F53D600D50D53 /* Frameworks */, + 50ADFF85201F53D600D50D53 /* Headers */, + 50ADFF86201F53D600D50D53 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Plugin; + productName = Plugin; + productReference = 50ADFF88201F53D600D50D53 /* Plugin.framework */; + productType = "com.apple.product-type.framework"; + }; + 50ADFF90201F53D600D50D53 /* PluginTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */; + buildPhases = ( + 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */, + 50ADFF8D201F53D600D50D53 /* Sources */, + 50ADFF8E201F53D600D50D53 /* Frameworks */, + 50ADFF8F201F53D600D50D53 /* Resources */, + 8E97F58B69A94C6503FC9C85 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 50ADFF94201F53D600D50D53 /* PBXTargetDependency */, + ); + name = PluginTests; + productName = PluginTests; + productReference = 50ADFF91201F53D600D50D53 /* PluginTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 50ADFF7F201F53D600D50D53 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1160; + ORGANIZATIONNAME = "Max Lynch"; + TargetAttributes = { + 50ADFF87201F53D600D50D53 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + 50ADFF90201F53D600D50D53 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 50ADFF7E201F53D600D50D53; + productRefGroup = 50ADFF89201F53D600D50D53 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 50ADFF87201F53D600D50D53 /* Plugin */, + 50ADFF90201F53D600D50D53 /* PluginTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 50ADFF86201F53D600D50D53 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8F201F53D600D50D53 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PluginTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 8E97F58B69A94C6503FC9C85 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Capacitor/Capacitor.framework", + "${BUILT_PRODUCTS_DIR}/CapacitorCordova/Cordova.framework", + "${BUILT_PRODUCTS_DIR}/FloatingPanel/FloatingPanel.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Capacitor.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cordova.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FloatingPanel.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Plugin-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 50ADFF83201F53D600D50D53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50E1A94820377CB70090CE1A /* BottomSheetPlugin.swift in Sources */, + 2F98D68224C9AAE500613A4C /* BottomSheet.swift in Sources */, + 50ADFFA82020EE4F00D50D53 /* BottomSheetPlugin.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8D201F53D600D50D53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF97201F53D600D50D53 /* BottomSheetTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 50ADFF94201F53D600D50D53 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 50ADFF87201F53D600D50D53 /* Plugin */; + targetProxy = 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 50ADFF9A201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"${BUILT_PRODUCTS_DIR}/Capacitor\"", + "\"${BUILT_PRODUCTS_DIR}/CapacitorCordova\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 50ADFF9B201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"${BUILT_PRODUCTS_DIR}/Capacitor\"", + "\"${BUILT_PRODUCTS_DIR}/CapacitorCordova\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 50ADFF9D201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Plugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)"; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 50ADFF9E201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Plugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)"; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 50ADFFA0201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PluginTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 50ADFFA1201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PluginTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFF9A201F53D600D50D53 /* Debug */, + 50ADFF9B201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFF9D201F53D600D50D53 /* Debug */, + 50ADFF9E201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFFA0201F53D600D50D53 /* Debug */, + 50ADFFA1201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 50ADFF7F201F53D600D50D53 /* Project object */; +} diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme new file mode 100644 index 00000000..303f2621 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme new file mode 100644 index 00000000..3d8c88d2 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin.xcworkspace/contents.xcworkspacedata b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..afad624e --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.h b/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.h new file mode 100644 index 00000000..f2bd9e0b --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.h @@ -0,0 +1,10 @@ +#import + +//! Project version number for Plugin. +FOUNDATION_EXPORT double PluginVersionNumber; + +//! Project version string for Plugin. +FOUNDATION_EXPORT const unsigned char PluginVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.m b/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.m new file mode 100644 index 00000000..3823ad5c --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.m @@ -0,0 +1,16 @@ +#import +#import + +// Define the plugin using the CAP_PLUGIN Macro, and +// each method the plugin supports using the CAP_PLUGIN_METHOD macro. +CAP_PLUGIN(BottomSheetPlugin, "BottomSheet", + CAP_PLUGIN_METHOD(prepare, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(delegate, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(release, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(openSelf, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(closeSelf, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(setSelfSize, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(callActionInMain, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(callActionInNative, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(openInMain, CAPPluginReturnPromise); +) diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.swift b/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.swift new file mode 100644 index 00000000..5cd829ae --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin/BottomSheetPlugin.swift @@ -0,0 +1,394 @@ +import Capacitor +import FloatingPanel +import UIKit +import WebKit + +private let CORNER_RADIUS = 16.0 +private let EASING_1 = CGPoint(x: 0.16, y: 1) +private let EASING_2 = CGPoint(x: 0.3, y: 1) +private let ANIMATION_DURATION = 0.600 +private let HALF_FRACTIONAL_INSET = 0.5 +private let MAX_HALF_INSET = 0.85 +private let FULL_INSET = 8.0 + +@objc(BottomSheetPlugin) +public class BottomSheetPlugin: CAPPlugin, FloatingPanelControllerDelegate { + let timingParameters = UICubicTimingParameters(controlPoint1: EASING_1, controlPoint2: EASING_2) + let fpc = FloatingPanelController() + + var capVc: CAPBridgeViewControllerForBottomSheet? + var isPrepared = false + var currentDelegateCall: CAPPluginCall? + public var currentOpenSelfCall: CAPPluginCall? + var currentHalfY: CGFloat? + var prevStatusBarStyle: UIStatusBarStyle? + + @objc func prepare(_ call: CAPPluginCall) { + ensureLocalOrigin() + + isPrepared = true + + DispatchQueue.main.async { [self] in + fpc.layout = MyPanelLayout() + fpc.delegate = self + fpc.isRemovalInteractionEnabled = false + fpc.backdropView.dismissalTapGestureRecognizer.isEnabled = false + fpc.surfaceView.appearance.cornerRadius = CORNER_RADIUS + fpc.surfaceView.grabberHandle.isHidden = true + + capVc = CAPBridgeViewControllerForBottomSheet() + fpc.set(contentViewController: capVc) + fpc.track(scrollView: capVc!.webView!.scrollView) + // Fix redunant scroll offsets + fpc.contentInsetAdjustmentBehavior = .never + + setupScrollReducers() + + let topVc = bridge!.viewController! + topVc.view.clipsToBounds = true + topVc.present(fpc, animated: false) { + call.resolve() + } + } + } + + @objc func delegate(_ call: CAPPluginCall) { + ensureLocalOrigin() + ensureDelegating() + + resolveOpenCalls() + currentDelegateCall = call + + DispatchQueue.main.async { [self] in + capVc!.bridge?.plugin(withName: "BottomSheet")!.notifyListeners("delegate", data: [ + "key": call.getString("key")!, + "globalJson": call.getString("globalJson")! + ]) + + let screenHeight = bridge!.viewController!.view!.superview!.frame.height + currentHalfY = screenHeight - screenHeight * HALF_FRACTIONAL_INSET + } + } + + @objc func release(_ call: CAPPluginCall) { + ensureLocalOrigin() + ensureDelegating() + + DispatchQueue.main.async { [self] in + let releaseKey = call.getString("key") + if releaseKey == currentDelegateCall?.getString("key") || releaseKey == "*" { + doClose() + } + + call.resolve() + } + } + + @objc func openSelf(_ call: CAPPluginCall) { + ensureLocalOrigin() + ensureDelegated() + + currentOpenSelfCall = call + + DispatchQueue.main.async { [self] in + let parentFpc = bridge!.viewController!.parent! as! FloatingPanelController + parentFpc.surfaceView.backgroundColor = UIColor(hexString: call.getString("backgroundColor")!) + + let topVc = parentFpc.presentingViewController as! CAPBridgeViewController + let topBottomSheetPlugin = topVc.bridge!.plugin(withName: "BottomSheet") as! BottomSheetPlugin + + topBottomSheetPlugin.doOpen( + height: CGFloat(Float(call.getString("height")!)!) + ) + + bridge!.webView!.becomeFirstResponder() + } + } + + @objc func closeSelf(_ call: CAPPluginCall) { + ensureLocalOrigin() + ensureDelegated() + + call.resolve() + + DispatchQueue.main.async { [self] in + if currentOpenSelfCall == nil || currentOpenSelfCall!.getString("key") != call.getString("key") { + return + } + + let topVc = bridge!.viewController!.parent!.presentingViewController as! CAPBridgeViewController + let topBottomSheetPlugin = topVc.bridge!.plugin(withName: "BottomSheet") as! BottomSheetPlugin + topBottomSheetPlugin.doClose() + } + } + + @objc func setSelfSize(_ call: CAPPluginCall) { + ensureLocalOrigin() + ensureDelegated() + + call.resolve() + + DispatchQueue.main.async { [self] in + if currentOpenSelfCall == nil { + return + } + + let topVc = bridge!.viewController!.parent!.presentingViewController as! CAPBridgeViewController + let topBottomSheetPlugin = topVc.bridge!.plugin(withName: "BottomSheet") as! BottomSheetPlugin + let toFull = call.getString("size") == "full" + let layout = topBottomSheetPlugin.fpc.layout as! MyPanelLayout + + if toFull && layout.anchors[.full] == nil { + layout.anchors[.full] = layout.fullAnchor + } + + topBottomSheetPlugin.animateTo(to: toFull ? .full : .half) + } + } + + @objc func callActionInMain(_ call: CAPPluginCall) { + ensureLocalOrigin() + ensureDelegated() + + call.resolve() + + DispatchQueue.main.async { [self] in + let topVc = bridge!.viewController!.parent!.presentingViewController as! CAPBridgeViewController + let topBottomSheetPlugin = topVc.bridge!.plugin(withName: "BottomSheet") as! BottomSheetPlugin + topBottomSheetPlugin.notifyListeners("callActionInMain", data: [ + "name": call.getString("name")!, + "optionsJson": call.getString("optionsJson")! + ]) + } + } + + @objc func openInMain(_ call: CAPPluginCall) { + ensureLocalOrigin() + ensureDelegated() + + call.resolve() + + DispatchQueue.main.async { [self] in + let topVc = bridge!.viewController!.parent!.presentingViewController as! CAPBridgeViewController + let topBottomSheetPlugin = topVc.bridge!.plugin(withName: "BottomSheet") as! BottomSheetPlugin + topBottomSheetPlugin.notifyListeners("openInMain", data: [ + "key": call.getString("key")! + ]) + } + } + + @objc func callActionInNative(_ call: CAPPluginCall) { + ensureLocalOrigin() + ensureDelegating() + + call.resolve() + + DispatchQueue.main.async { [self] in + capVc!.bridge?.plugin(withName: "BottomSheet")!.notifyListeners("callActionInNative", data: [ + "name": call.getString("name")!, + "optionsJson": call.getString("optionsJson")! + ]) + } + } + + // Extra security level, potentially redundant + private func ensureLocalOrigin() { + DispatchQueue.main.sync { [self] in + precondition(bridge!.webView!.url!.absoluteString.hasPrefix(bridge!.config.serverURL.absoluteString)) + } + } + + private func ensureDelegating() { + precondition(isPrepared) + } + + private func ensureDelegated() { + precondition(!isPrepared) + } + + public func doOpen(height: CGFloat) { + let screenHeight = bridge!.viewController!.view!.superview!.frame.height + let newFractionalInset = min(height / screenHeight, MAX_HALF_INSET) + let layout = fpc.layout as! MyPanelLayout + + if newFractionalInset < MAX_HALF_INSET { + layout.anchors[.half] = FloatingPanelLayoutAnchor(fractionalInset: newFractionalInset, edge: .bottom, referenceGuide: .superview) + layout.anchors[.full] = nil + currentHalfY = screenHeight - screenHeight * newFractionalInset + toggleExtraScroll(false) + animateTo(to: .half) + } else { + layout.anchors[.half] = nil + layout.anchors[.full] = layout.fullAnchor + currentHalfY = screenHeight - screenHeight * HALF_FRACTIONAL_INSET + toggleExtraScroll(true) + animateTo(to: .full) + } + } + + public func doClose() { + resolveOpenCalls() + + if fpc.state == .hidden { + return + } + + animateTo(to: .hidden) + } + + private func resolveOpenCalls() { + currentDelegateCall?.resolve() + currentDelegateCall = nil + + let childBottomSheetPlugin = capVc!.bridge!.plugin(withName: "BottomSheet") as! BottomSheetPlugin + childBottomSheetPlugin.currentOpenSelfCall?.resolve() + childBottomSheetPlugin.currentOpenSelfCall = nil + } + + private func animateTo(to: FloatingPanelState) { + if to == .half && fpc.layout.anchors[.half] == nil { + return + } + + let timing = UICubicTimingParameters(controlPoint1: EASING_1, controlPoint2: EASING_2) + let animator = UIViewPropertyAnimator(duration: ANIMATION_DURATION, timingParameters: timing) + + animator.addAnimations { [self] in + fpc.move(to: to, animated: false) + } + + animator.startAnimation() + } + + public func floatingPanelDidMove(_ fpc: FloatingPanelController) { + if currentHalfY == nil { + return + } + + let view = bridge!.viewController!.view! + let y = fpc.surfaceView.frame.origin.y + let offsetTop = view.safeAreaInsets.top + FULL_INSET + let currentOffsetY = y - offsetTop + let currentHalfOffsetY = currentHalfY! - offsetTop + let progress = 1 - currentOffsetY / currentHalfOffsetY + + let maxMainHeight = view.superview!.frame.height + let minMainHeight = maxMainHeight - (view.safeAreaInsets.top * 2) + let maxScaleFactor = 1 - minMainHeight / maxMainHeight + + let scale = 1 - maxScaleFactor * max(progress, 0) + + view.transform = CGAffineTransform(scaleX: scale, y: scale) + + let topVc = bridge!.viewController! + topVc.view.layer.cornerRadius = scale < 1 ? CORNER_RADIUS : 0.0 + + let childBottomSheetPlugin = capVc!.bridge!.plugin(withName: "BottomSheet") as! BottomSheetPlugin + childBottomSheetPlugin.notifyListeners("move", data: nil) + } + + public func floatingPanelDidChangeState(_ fpc: FloatingPanelController) { + let inEnteringFull = fpc.state == .full && prevStatusBarStyle == nil + let isLeavingFull = fpc.state != .full && prevStatusBarStyle != nil + + if inEnteringFull { + prevStatusBarStyle = bridge!.statusBarStyle + bridge!.statusBarStyle = .lightContent + } else if isLeavingFull { + bridge!.statusBarStyle = prevStatusBarStyle! + prevStatusBarStyle = nil + } + + if fpc.state == .hidden { + resolveOpenCalls() + bridge!.webView!.becomeFirstResponder() + } + } + + // This is a multi-purpose hack: + // 1. For some reason, we need an extra pixel on container for the scroll tracking to work. + // 2. However, it breaks the rubber-band effect, so we remove it when not needed. + // 3. Also, there are some weird defaults by Bottom Sheet Plugin which break focusing, so we override them. + private func toggleExtraScroll(_ withExtraScroll: Bool = false) { + capVc!.webView!.scrollView.contentInset = withExtraScroll + ? .init(top: 0, left: 0, bottom: 1, right: 0) + : .zero + } +} + +// To be extracted to separate "Reduce Scroll Angle" plugin +extension BottomSheetPlugin: UIGestureRecognizerDelegate { + private static let REDUCED_ANGLE = 45.0 + + private func setupScrollReducers() { + let mainGestureRecognizer = UIPanGestureRecognizer() + mainGestureRecognizer.delegate = self + bridge!.webView!.scrollView.addGestureRecognizer(mainGestureRecognizer) + } + + @objc public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer { + let velocity = panGestureRecognizer.velocity(in: nil) + let angle = atan2(abs(velocity.y), abs(velocity.x)) * 180 / .pi + + if angle < Self.REDUCED_ANGLE { + return true + } + } + + return false + } + + @objc public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + + otherGestureRecognizer.require(toFail: gestureRecognizer) + + return true + } +} + +class CAPBridgeViewControllerForBottomSheet: CAPBridgeViewController { + override func instanceDescriptor() -> InstanceDescriptor { + let descriptor = super.instanceDescriptor() + descriptor.serverURL = String(format: "%@://%@/?bottom-sheet", descriptor.urlScheme!, descriptor.urlHostname!) + return descriptor + } +} + +class MyPanelLayout: FloatingPanelLayout { + let position: FloatingPanelPosition = .bottom + let initialState: FloatingPanelState = .hidden + var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] = [ + .half: FloatingPanelLayoutAnchor(fractionalInset: HALF_FRACTIONAL_INSET, edge: .bottom, referenceGuide: .superview), + .hidden: FloatingPanelLayoutAnchor(fractionalInset: 0, edge: .bottom, referenceGuide: .superview) + ] + let fullAnchor = FloatingPanelLayoutAnchor(absoluteInset: FULL_INSET, edge: .top, referenceGuide: .safeArea) + + func backdropAlpha(for state: FloatingPanelState) -> CGFloat { + switch state { + case .full: return 0.45 + case .half: return 0.35 + default: return 0.0 + } + } +} + +extension UIColor { + convenience init(hexString: String) { + let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + var int = UInt64() + Scanner(string: hex).scanHexInt64(&int) + let a, r, g, b: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (a, r, g, b) = (255, 0, 0, 0) + } + self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255) + } +} diff --git a/mobile/plugins/native-bottom-sheet/ios/Plugin/Info.plist b/mobile/plugins/native-bottom-sheet/ios/Plugin/Info.plist new file mode 100644 index 00000000..1007fd9d --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Plugin/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/mobile/plugins/native-bottom-sheet/ios/PluginTests/BottomSheetTests.swift b/mobile/plugins/native-bottom-sheet/ios/PluginTests/BottomSheetTests.swift new file mode 100644 index 00000000..1ddcc3ee --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/PluginTests/BottomSheetTests.swift @@ -0,0 +1,16 @@ +import XCTest +@testable import Plugin + +class BottomSheetTests: XCTestCase { + + func testEcho() { + // This is an example of a functional test case for a plugin. + // Use XCTAssert and related functions to verify your tests produce the correct results. + + let implementation = BottomSheet() + let value = "Hello, World!" + let result = implementation.echo(value) + + XCTAssertEqual(value, result) + } +} diff --git a/mobile/plugins/native-bottom-sheet/ios/PluginTests/Info.plist b/mobile/plugins/native-bottom-sheet/ios/PluginTests/Info.plist new file mode 100644 index 00000000..6c40a6cd --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/PluginTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/mobile/plugins/native-bottom-sheet/ios/Podfile b/mobile/plugins/native-bottom-sheet/ios/Podfile new file mode 100644 index 00000000..799af1f4 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/ios/Podfile @@ -0,0 +1,18 @@ +platform :ios, '13.0' + +def capacitor_pods + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + pod 'Capacitor', :path => '../node_modules/@capacitor/ios' + pod 'CapacitorCordova', :path => '../node_modules/@capacitor/ios' +end + +target 'Plugin' do + capacitor_pods + + pod 'FloatingPanel' +end + +target 'PluginTests' do + capacitor_pods +end diff --git a/mobile/plugins/native-bottom-sheet/package-lock.json b/mobile/plugins/native-bottom-sheet/package-lock.json new file mode 100644 index 00000000..e56c6dfc --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/package-lock.json @@ -0,0 +1,3452 @@ +{ + "name": "native-bottom-sheet", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "native-bottom-sheet", + "version": "0.0.1", + "license": "MIT", + "devDependencies": { + "@capacitor/android": "^5.0.0", + "@capacitor/core": "^5.0.0", + "@capacitor/docgen": "^0.0.18", + "@capacitor/ios": "^5.0.0", + "@ionic/eslint-config": "^0.3.0", + "@ionic/prettier-config": "^1.0.1", + "@ionic/swiftlint-config": "^1.1.2", + "eslint": "^7.11.0", + "prettier": "~2.3.0", + "prettier-plugin-java": "~1.0.2", + "rimraf": "^3.0.2", + "rollup": "^2.32.0", + "swiftlint": "^1.0.1", + "typescript": "~4.1.5" + }, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@capacitor/android": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@capacitor/android/-/android-5.5.0.tgz", + "integrity": "sha512-ipJijb3M0FA6DvotS9zrbJ8p/mTEVg9EVtBmvUexogm8g5se1mc7i1gvOr3MQ/iTZ3PnNrRC/P7kHxa2R55iqg==", + "dev": true, + "peerDependencies": { + "@capacitor/core": "^5.5.0" + } + }, + "node_modules/@capacitor/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.5.0.tgz", + "integrity": "sha512-w59io0ctwnb7JRng7yO2H0YLHG8uz7XARUugRfp5aYTNiG55FqdSmSMOOqGCMPRg4sEnKjJTvAa4ImCYh3Kk1w==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@capacitor/docgen": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@capacitor/docgen/-/docgen-0.0.18.tgz", + "integrity": "sha512-BVqzrbSi9u5IaKRLlG0H/ZW8M23FDJpH2018RTGVHRn2Yk3na9jOcItBc3r+rYiwgRgAHylNw9Lt7+lWmJBD3Q==", + "dev": true, + "dependencies": { + "@types/node": "^14.17.3", + "colorette": "^1.2.2", + "github-slugger": "^1.3.0", + "minimist": "^1.2.5", + "typescript": "~4.2.4" + }, + "bin": { + "docgen": "bin/docgen" + }, + "engines": { + "node": ">=14.5.0" + } + }, + "node_modules/@capacitor/docgen/node_modules/typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@capacitor/ios": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@capacitor/ios/-/ios-5.5.0.tgz", + "integrity": "sha512-kApjblUOlLY91+1OrWIx+vaVfEN1bl1kh1jSgK1/IdGfS9kFs1hxUE/okRoLJGT6tYeSOa6GA/19MLOs64wb6A==", + "dev": true, + "peerDependencies": { + "@capacitor/core": "^5.5.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@ionic/eslint-config": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@ionic/eslint-config/-/eslint-config-0.3.0.tgz", + "integrity": "sha512-Uf1hS2YIoHlcvXPF5LnsPM6auMewEdChQhR117Rt3sVEAutbyKMpFP4slNC2a6up3a5Q34zepqlf61Qgkf9XeQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "^4.1.0", + "@typescript-eslint/parser": "^4.1.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-import": "^2.22.0" + }, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/@ionic/prettier-config": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@ionic/prettier-config/-/prettier-config-1.0.1.tgz", + "integrity": "sha512-/v8UOW7rxkw/hvrRe/QfjlQsdjkm3sfAHoE3uqffO5BoNGijQMARrT32JT9Ei0g6KySXPyxxW+7LzPHrQmfzCw==", + "dev": true, + "peerDependencies": { + "prettier": "^2.0.0" + } + }, + "node_modules/@ionic/swiftlint-config": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ionic/swiftlint-config/-/swiftlint-config-1.1.2.tgz", + "integrity": "sha512-UbE1AIlTowt9uR7fMzRtbQX4URcyuok7mcpdJfFDHAIGM6nDjohYMke+6xOr6ZYlLnEyVmBGNEg0+grEYRgcVg==", + "dev": true + }, + "node_modules/@ionic/utils-array": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.5.tgz", + "integrity": "sha512-HD72a71IQVBmQckDwmA8RxNVMTbxnaLbgFOl+dO5tbvW9CkkSFCv41h6fUuNsSEVgngfkn0i98HDuZC8mk+lTA==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@ionic/utils-fs": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.6.tgz", + "integrity": "sha512-eikrNkK89CfGPmexjTfSWl4EYqsPSBh0Ka7by4F0PLc1hJZYtJxUZV3X4r5ecA8ikjicUmcbU7zJmAjmqutG/w==", + "dev": true, + "dependencies": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@ionic/utils-object": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.5.tgz", + "integrity": "sha512-XnYNSwfewUqxq+yjER1hxTKggftpNjFLJH0s37jcrNDwbzmbpFTQTVAp4ikNK4rd9DOebX/jbeZb8jfD86IYxw==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@ionic/utils-process": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.10.tgz", + "integrity": "sha512-mZ7JEowcuGQK+SKsJXi0liYTcXd2bNMR3nE0CyTROpMECUpJeAvvaBaPGZf5ERQUPeWBVuwqAqjUmIdxhz5bxw==", + "dev": true, + "dependencies": { + "@ionic/utils-object": "2.1.5", + "@ionic/utils-terminal": "2.3.3", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@ionic/utils-stream": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.5.tgz", + "integrity": "sha512-hkm46uHvEC05X/8PHgdJi4l4zv9VQDELZTM+Kz69odtO9zZYfnt8DkfXHJqJ+PxmtiE5mk/ehJWLnn/XAczTUw==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@ionic/utils-subprocess": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.11.tgz", + "integrity": "sha512-6zCDixNmZCbMCy5np8klSxOZF85kuDyzZSTTQKQP90ZtYNCcPYmuFSzaqDwApJT4r5L3MY3JrqK1gLkc6xiUPw==", + "dev": true, + "dependencies": { + "@ionic/utils-array": "2.1.5", + "@ionic/utils-fs": "3.1.6", + "@ionic/utils-process": "2.1.10", + "@ionic/utils-stream": "3.1.5", + "@ionic/utils-terminal": "2.3.3", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@ionic/utils-terminal": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.3.tgz", + "integrity": "sha512-RnuSfNZ5fLEyX3R5mtcMY97cGD1A0NVBbarsSQ6yMMfRJ5YHU7hHVyUfvZeClbqkBC/pAqI/rYJuXKCT9YeMCQ==", + "dev": true, + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/fs-extra": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.4.tgz", + "integrity": "sha512-OMcQKnlrkrOI0TaZ/MgVDA8LYFl7CykzFsjMj9l5x3un2nFxCY20ZFlnqrM0lcqlbs0Yro2HbnZlmopyRaoJ5w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.1.tgz", + "integrity": "sha512-3YmXzzPAdOTVljVMkTMBdBEvlOLg2cDQaDhnnhT3nT9uDbnJzjWhKlzb+desT12Y7tGqaN6d+AbozcKzyL36Ng==", + "dev": true + }, + "node_modules/@types/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", + "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "4.33.0", + "@typescript-eslint/scope-manager": "4.33.0", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^4.0.0", + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", + "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", + "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "debug": "^4.3.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", + "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", + "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "dev": true, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", + "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chevrotain": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-6.5.0.tgz", + "integrity": "sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==", + "dev": true, + "dependencies": { + "regexp-to-ast": "0.4.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", + "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", + "dev": true, + "dependencies": { + "get-stdin": "^6.0.0" + }, + "bin": { + "eslint-config-prettier-check": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=3.14.1" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", + "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.findlastindex": "^1.2.2", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.8.0", + "has": "^1.0.3", + "is-core-module": "^2.13.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.6", + "object.groupby": "^1.0.0", + "object.values": "^1.1.6", + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/github-slugger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", + "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.23.0", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ignore": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/java-parser": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-parser/-/java-parser-1.0.2.tgz", + "integrity": "sha512-lBXc+F62ds2W83eH5MwGnzuWdb6kgGBV0x0R7w0B4JKGDrJzolMUEhRMzzzlIX68HvRU7XtfPon22YaB+dVg+A==", + "dev": true, + "dependencies": { + "chevrotain": "6.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/prettier-plugin-java": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-java/-/prettier-plugin-java-1.0.2.tgz", + "integrity": "sha512-YgcN1WGZlrH0E+bHdqtIYtfDp6k2PHBnIaGjzdff/7t/NyDWAA6ypAmnD7YQVG2OuoIaXYkC37HN7cz68lLWLg==", + "dev": true, + "dependencies": { + "java-parser": "1.0.2", + "lodash": "4.17.21", + "prettier": "2.2.1" + } + }, + "node_modules/prettier-plugin-java/node_modules/prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/regexp-to-ast": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.4.0.tgz", + "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swiftlint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/swiftlint/-/swiftlint-1.0.2.tgz", + "integrity": "sha512-YhcS0N3vkwBatnZf/iJPg89LGQk7MbFnz67Cg3EZ6Ppqm2H8y6x7A1t6KMQ0jYVQpea9wQiFiFRFhkoChaQ29Q==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@ionic/utils-fs": "3.1.6", + "@ionic/utils-subprocess": "2.1.11", + "cosmiconfig": "^6.0.0" + }, + "bin": { + "node-swiftlint": "bin.js" + } + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "4.1.6", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + } + } +} diff --git a/mobile/plugins/native-bottom-sheet/package.json b/mobile/plugins/native-bottom-sheet/package.json new file mode 100644 index 00000000..b03625c7 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/package.json @@ -0,0 +1,78 @@ +{ + "name": "native-bottom-sheet", + "version": "0.0.1", + "description": "Allows to open a native BottomSheet/FloatingPanel on iOS", + "main": "dist/plugin.cjs.js", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", + "files": [ + "android/src/main/", + "android/build.gradle", + "dist/", + "ios/Plugin/", + "NativeBottomSheet.podspec" + ], + "author": "MyTonWallet Team", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/mytonwalletorg/native-bottom-sheet.git" + }, + "bugs": { + "url": "https://github.com/mytonwalletorg/native-bottom-sheet/issues" + }, + "keywords": [ + "capacitor", + "plugin", + "native" + ], + "scripts": { + "verify": "npm run verify:ios && npm run verify:android && npm run verify:web", + "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..", + "verify:android": "cd android && ./gradlew clean build test && cd ..", + "verify:web": "npm run build", + "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint", + "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format", + "eslint": "eslint . --ext ts", + "prettier": "prettier \"**/*.{css,html,ts,js,java}\"", + "swiftlint": "node-swiftlint", + "docgen": "docgen --api BottomSheetPlugin --output-readme README.md --output-json dist/docs.json", + "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js", + "clean": "rimraf ./dist", + "watch": "tsc --watch", + "prepublishOnly": "npm run build" + }, + "devDependencies": { + "@capacitor/android": "^5.0.0", + "@capacitor/core": "^5.0.0", + "@capacitor/docgen": "^0.0.18", + "@capacitor/ios": "^5.0.0", + "@ionic/eslint-config": "^0.3.0", + "@ionic/prettier-config": "^1.0.1", + "@ionic/swiftlint-config": "^1.1.2", + "eslint": "^7.11.0", + "prettier": "~2.3.0", + "prettier-plugin-java": "~1.0.2", + "rimraf": "^3.0.2", + "rollup": "^2.32.0", + "swiftlint": "^1.0.1", + "typescript": "~4.1.5" + }, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + }, + "prettier": "@ionic/prettier-config", + "swiftlint": "@ionic/swiftlint-config", + "eslintConfig": { + "extends": "@ionic/eslint-config/recommended" + }, + "capacitor": { + "ios": { + "src": "ios" + }, + "android": { + "src": "android" + } + } +} diff --git a/mobile/plugins/native-bottom-sheet/rollup.config.js b/mobile/plugins/native-bottom-sheet/rollup.config.js new file mode 100644 index 00000000..b187fc9f --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/rollup.config.js @@ -0,0 +1,22 @@ +export default { + input: 'dist/esm/index.js', + output: [ + { + file: 'dist/plugin.js', + format: 'iife', + name: 'capacitorBottomSheet', + globals: { + '@capacitor/core': 'capacitorExports', + }, + sourcemap: true, + inlineDynamicImports: true, + }, + { + file: 'dist/plugin.cjs.js', + format: 'cjs', + sourcemap: true, + inlineDynamicImports: true, + }, + ], + external: ['@capacitor/core'], +}; diff --git a/mobile/plugins/native-bottom-sheet/src/definitions.ts b/mobile/plugins/native-bottom-sheet/src/definitions.ts new file mode 100644 index 00000000..94543f6c --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/src/definitions.ts @@ -0,0 +1,66 @@ +import { PluginListenerHandle } from '@capacitor/core'; + +export type BottomSheetKeys = + 'initial' + | 'receive' + | 'invoice' + | 'transfer' + | 'swap' + | 'stake' + | 'unstake' + | 'staking-info' + | 'transaction-info' + | 'swap-activity' + | 'backup' + | 'add-account' + | 'settings' + | 'qr-scanner' + | 'dapp-connect' + | 'dapp-transaction' + | 'disclaimer' + | 'backup-warning'; + +export interface BottomSheetPlugin { + prepare(): Promise; + + delegate(options: { key: BottomSheetKeys, globalJson: string }): Promise; + + release(options: { key: BottomSheetKeys | '*' }): Promise; + + openSelf(options: { key: BottomSheetKeys, height: string, backgroundColor: string }): Promise; + + closeSelf(options: { key: BottomSheetKeys }): Promise; + + setSelfSize(options: { size: 'half' | 'full' }): Promise; + + callActionInMain(options: { name: string, optionsJson: string }): Promise; + + callActionInNative(options: { name: string, optionsJson: string }): Promise; + + openInMain(options: { key: BottomSheetKeys }): Promise; + + addListener( + eventName: 'delegate', + handler: (options: { key: BottomSheetKeys, globalJson: string }) => void, + ): Promise & PluginListenerHandle; + + addListener( + eventName: 'move', + handler: () => void, + ): Promise & PluginListenerHandle; + + addListener( + eventName: 'callActionInMain', + handler: (options: { name: string, optionsJson: string }) => void, + ): Promise & PluginListenerHandle; + + addListener( + eventName: 'callActionInNative', + handler: (options: { name: string, optionsJson: string }) => void, + ): Promise & PluginListenerHandle; + + addListener( + eventName: 'openInMain', + handler: (options: { key: BottomSheetKeys }) => void, + ): Promise & PluginListenerHandle; +} diff --git a/mobile/plugins/native-bottom-sheet/src/index.ts b/mobile/plugins/native-bottom-sheet/src/index.ts new file mode 100644 index 00000000..bd2f5fa1 --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/src/index.ts @@ -0,0 +1,8 @@ +import { registerPlugin } from '@capacitor/core'; + +import type { BottomSheetPlugin } from './definitions'; + +const BottomSheet = registerPlugin('BottomSheet'); + +export * from './definitions'; +export { BottomSheet }; diff --git a/mobile/plugins/native-bottom-sheet/tsconfig.json b/mobile/plugins/native-bottom-sheet/tsconfig.json new file mode 100644 index 00000000..f2e88e6a --- /dev/null +++ b/mobile/plugins/native-bottom-sheet/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "allowUnreachableCode": false, + "declaration": true, + "esModuleInterop": true, + "inlineSources": true, + "lib": ["dom", "es2017"], + "module": "esnext", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "outDir": "dist/esm", + "pretty": true, + "sourceMap": true, + "strict": true, + "target": "es2017" + }, + "files": ["src/index.ts"] +} diff --git a/package-lock.json b/package-lock.json index 620d785a..d0414122 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,31 @@ { "name": "mytonwallet", - "version": "1.16.4", + "version": "1.17.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mytonwallet", - "version": "1.16.4", + "version": "1.17.0", "license": "GPL-3.0-or-later", "dependencies": { + "@capacitor-mlkit/barcode-scanning": "^5.3.0", + "@capacitor/android": "^5.3.0", + "@capacitor/app": "^5.0.6", + "@capacitor/core": "^5.5.1", + "@capacitor/dialog": "^5.0.6", + "@capacitor/haptics": "^5.0.6", + "@capacitor/ios": "^5.5.1", + "@capacitor/status-bar": "^5.0.6", + "@capgo/capacitor-native-biometric": "^5.1.0", "@ledgerhq/hw-transport-webhid": "^6.27.12", + "@mauricewegner/capacitor-navigation-bar": "^2.0.3", "@ton-community/ton-ledger": "^6.0.0", "buffer": "^6.0.3", + "capacitor-plugin-safe-area": "^2.0.5", + "capacitor-splash-screen": "file:mobile/plugins/capacitor-splash-screen", "idb-keyval": "^6.2.0", + "native-bottom-sheet": "file:mobile/plugins/native-bottom-sheet", "pako": "^2.1.0", "qr-code-styling": "github:troman29/qr-code-styling#c00d0", "qrcode-generator": "^1.4.4", @@ -35,6 +48,7 @@ "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.0", "@babel/register": "^7.21.0", + "@capacitor/cli": "^5.3.0", "@peculiar/webcrypto": "^1.3.2", "@playwright/test": "^1.31.2", "@statoscope/cli": "^5.27.0", @@ -71,6 +85,8 @@ "electron": "^22.1.0", "electron-builder": "^24.4.0", "electron-context-menu": "^3.6.1", + "electron-rebuild": "^3.2.9", + "electron-store": "^8.1.0", "electron-updater": "^5.3.0", "electron-window-state": "^5.0.3", "electronmon": "^2.0.2", @@ -97,7 +113,7 @@ "jest": "^29.5.0", "jest-raw-loader": "^1.0.1", "js-yaml": "^4.1.0", - "lint-staged": "^13.2.0", + "lint-staged": "^15.0.2", "mini-css-extract-plugin": "^2.7.5", "postcss": "^8.4.14", "postcss-loader": "^7.1.0", @@ -116,7 +132,7 @@ "stylelint-group-selectors": "^1.0.9", "stylelint-high-performance-animation": "^1.8.0", "stylelint-order": "^5.0.0", - "typescript": "^5.0.2", + "typescript": "^5.2.2", "webpack": "^5.76.2", "webpack-dev-server": "^4.13.1" }, @@ -125,6 +141,799 @@ "npm": "^9" } }, + "mobile/native-bottom-sheet": { + "version": "0.0.1", + "extraneous": true, + "license": "MIT", + "devDependencies": { + "@capacitor/android": "^5.0.0", + "@capacitor/core": "^5.0.0", + "@capacitor/docgen": "^0.0.18", + "@capacitor/ios": "^5.0.0", + "@ionic/eslint-config": "^0.3.0", + "@ionic/prettier-config": "^1.0.1", + "@ionic/swiftlint-config": "^1.1.2", + "eslint": "^7.11.0", + "prettier": "~2.3.0", + "prettier-plugin-java": "~1.0.2", + "rimraf": "^3.0.2", + "rollup": "^2.32.0", + "swiftlint": "^1.0.1", + "typescript": "~4.1.5" + }, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "mobile/plugins/capacitor-splash-screen": { + "version": "5.0.6.1", + "license": "MIT", + "devDependencies": { + "@capacitor/android": "^5.0.0", + "@capacitor/cli": "^5.0.0", + "@capacitor/core": "^5.0.0", + "@capacitor/docgen": "0.2.0", + "@capacitor/ios": "^5.0.0", + "@ionic/eslint-config": "^0.3.0", + "@ionic/prettier-config": "~1.0.1", + "@ionic/swiftlint-config": "^1.1.2", + "eslint": "^7.11.0", + "prettier": "~2.3.0", + "prettier-plugin-java": "~1.0.2", + "rimraf": "^3.0.2", + "rollup": "^2.32.0", + "swiftlint": "^1.0.1", + "typescript": "~4.1.5" + }, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/@capacitor/docgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@capacitor/docgen/-/docgen-0.2.0.tgz", + "integrity": "sha512-JoKuEx4Ep9LVnTY1I4zaoQPlxy+AWVMwwOSou/U9f/nXsL391aBRH7hC12lKNZGetiComnt369vQ+/r7n9iXfQ==", + "dev": true, + "dependencies": { + "@types/node": "^14.18.0", + "colorette": "^2.0.16", + "github-slugger": "^1.4.0", + "minimist": "^1.2.5", + "typescript": "~4.2.4" + }, + "bin": { + "docgen": "bin/docgen" + }, + "engines": { + "node": ">=14.5.0" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/@capacitor/docgen/node_modules/typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "dev": true + }, + "mobile/plugins/capacitor-splash-screen/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "mobile/plugins/capacitor-splash-screen/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/typescript": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.6.tgz", + "integrity": "sha512-pxnwLxeb/Z5SP80JDRzVjh58KsM6jZHRAOtTpS7sXLS4ogXNKC9ANxHHZqLLeVHZN35jCtI4JdmLLbLiC1kBow==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "mobile/plugins/capacitor-splash-screen/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "mobile/plugins/native-bottom-sheet": { + "version": "0.0.1", + "license": "MIT", + "devDependencies": { + "@capacitor/android": "^5.0.0", + "@capacitor/core": "^5.0.0", + "@capacitor/docgen": "^0.0.18", + "@capacitor/ios": "^5.0.0", + "@ionic/eslint-config": "^0.3.0", + "@ionic/prettier-config": "^1.0.1", + "@ionic/swiftlint-config": "^1.1.2", + "eslint": "^7.11.0", + "prettier": "~2.3.0", + "prettier-plugin-java": "~1.0.2", + "rimraf": "^3.0.2", + "rollup": "^2.32.0", + "swiftlint": "^1.0.1", + "typescript": "~4.1.5" + }, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "mobile/plugins/native-bottom-sheet/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/typescript": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.6.tgz", + "integrity": "sha512-pxnwLxeb/Z5SP80JDRzVjh58KsM6jZHRAOtTpS7sXLS4ogXNKC9ANxHHZqLLeVHZN35jCtI4JdmLLbLiC1kBow==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "mobile/plugins/native-bottom-sheet/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -135,9 +944,10 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.2.0", - "dev": true, - "license": "MIT" + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", + "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==", + "dev": true }, "node_modules/@ampproject/remapping": { "version": "2.2.1", @@ -152,12 +962,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -229,12 +1040,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", - "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -343,22 +1154,22 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -521,9 +1332,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -568,13 +1379,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -582,9 +1393,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.6.tgz", - "integrity": "sha512-EIQu22vNkceq3LbjAq7knDf/UmtI2qbcNI8GRBlijez6TpQLvSodJPYfydQmNA5buwkxxxa/PVI44jjYZ+/cLw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -2202,9 +3013,9 @@ } }, "node_modules/@babel/register/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -2236,58 +3047,336 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.6.tgz", - "integrity": "sha512-53CijMvKlLIDlOTrdWiHileRddlIiwUIyCKqYa7lYnnPldXCG5dUSN38uT0cA6i7rHWNKJLH0VU/Kxdr1GzB3w==", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@capacitor-mlkit/barcode-scanning": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@capacitor-mlkit/barcode-scanning/-/barcode-scanning-5.3.0.tgz", + "integrity": "sha512-ML1nEzcvegeOTu3AxufWYwLmvxkkeKbToyTuMHmpFlIYn69qVdAzwGrN39Kh3+jUwZ4cLy7KTCf27/p6ShT8ag==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/capawesome-team/" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/capawesome" + } + ], + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@capacitor/android": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@capacitor/android/-/android-5.5.1.tgz", + "integrity": "sha512-WTnPnpaEvTtaEtTNRbh06Y1afF7A4plY/4uajAL0WW8tdR1FxieadF357yKGiAT6CudI/B+eOu6rxn6qWuphKg==", + "peerDependencies": { + "@capacitor/core": "^5.5.0" + } + }, + "node_modules/@capacitor/app": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/app/-/app-5.0.6.tgz", + "integrity": "sha512-6ZXVdnNmaYILasC/RjQw+yfTmq2ZO7Q3v5lFcDVfq3PFGnybyYQh+RstBrYri+376OmXOXxBD7E6UxBhrMzXGA==", + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@capacitor/cli": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-5.3.0.tgz", + "integrity": "sha512-ku23HPqUHUnSgo/SyEWxVviEAxb4ieWvAVMI3KfrrBoinAhTOvNSZwT346rIpxZ9Xj3Qp41UjdIz0ME+DYwhfA==", + "dev": true, + "dependencies": { + "@ionic/cli-framework-output": "^2.2.5", + "@ionic/utils-fs": "^3.1.6", + "@ionic/utils-subprocess": "^2.1.11", + "@ionic/utils-terminal": "^2.3.3", + "commander": "^9.3.0", + "debug": "^4.3.4", + "env-paths": "^2.2.0", + "kleur": "^4.1.4", + "native-run": "^1.7.2", + "open": "^8.4.0", + "plist": "^3.0.5", + "prompts": "^2.4.2", + "rimraf": "^4.4.1", + "semver": "^7.3.7", + "tar": "^6.1.11", + "tslib": "^2.4.0", + "xml2js": "^0.5.0" + }, + "bin": { + "cap": "bin/capacitor", + "capacitor": "bin/capacitor" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/@capacitor/cli/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@capacitor/cli/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@capacitor/cli/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@capacitor/cli/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "dev": true, + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@capacitor/cli/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@capacitor/core": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.5.1.tgz", + "integrity": "sha512-VG6Iv8Q7ZAbvjodxpvjcSe0jfxUwZXnvjbi93ehuJ6eYP8U926qLSXyrT/DToZq+F6v/HyGyVgn3mrE/9jW2Tg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@capacitor/dialog": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/dialog/-/dialog-5.0.6.tgz", + "integrity": "sha512-/F9aSADswh+5pBE5810vD/N+Ox3KmahLXn1rMqisao8gNVI/Lk4YanWSPqDJCauHwOfZyeZscmDsETizAlSLFA==", + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@capacitor/docgen": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@capacitor/docgen/-/docgen-0.0.18.tgz", + "integrity": "sha512-BVqzrbSi9u5IaKRLlG0H/ZW8M23FDJpH2018RTGVHRn2Yk3na9jOcItBc3r+rYiwgRgAHylNw9Lt7+lWmJBD3Q==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.6", - "@babel/types": "^7.22.5", - "debug": "^4.1.0", - "globals": "^11.1.0" + "@types/node": "^14.17.3", + "colorette": "^1.2.2", + "github-slugger": "^1.3.0", + "minimist": "^1.2.5", + "typescript": "~4.2.4" + }, + "bin": { + "docgen": "bin/docgen" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.5.0" } }, - "node_modules/@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "node_modules/@capacitor/docgen/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "dev": true + }, + "node_modules/@capacitor/docgen/node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, + "node_modules/@capacitor/docgen/node_modules/typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=6.9.0" + "node": ">=4.2.0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "dev": true, - "license": "MIT" + "node_modules/@capacitor/haptics": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-5.0.6.tgz", + "integrity": "sha512-UrMcR7p2X10ql4VLlowUuH/VckTeu0lj+RQpekxox14uxDmu5AGIFDK/iDTi8W6QZkxTJRZK6sbCjgwYgNJ7Pw==", + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@capacitor/ios": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@capacitor/ios/-/ios-5.5.1.tgz", + "integrity": "sha512-h00qt8u32t8eEbIkuG4IjR0r34YZC0sIXglDH8fRDdA84xDkTybmz3WtdpRWDzh6ukE2RIY7rmD7p410WSJ2yA==", + "peerDependencies": { + "@capacitor/core": "^5.5.0" + } + }, + "node_modules/@capacitor/status-bar": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-5.0.6.tgz", + "integrity": "sha512-7od8CxsBnot1XMK3IeOkproFL4hgoKoWAc3pwUvmDOkQsXoxwQm4SR9mLwQavv1XfxtHbFV9Ukd7FwMxOPSViw==", + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@capgo/capacitor-native-biometric": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@capgo/capacitor-native-biometric/-/capacitor-native-biometric-5.1.0.tgz", + "integrity": "sha512-j7PxjX0mZy7sVPSbulAj3YTP2qyXPvHUUt6C9kgzsBh1VNWaIFlzz9TRd/r9IwdOY0L/ASsU5VOhK144SJI5Dg==", + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } }, "node_modules/@csstools/selector-specificity": { "version": "2.2.0", @@ -2339,12 +3428,11 @@ } }, "node_modules/@electron/asar": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.4.tgz", - "integrity": "sha512-lykfY3TJRRWFeTxccEKdf1I6BLl2Plw81H0bbp4Fc5iEc67foDCa5pjJQULVgo0wF+Dli75f3xVcdb/67FFZ/g==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.7.tgz", + "integrity": "sha512-8FaSCAIiZGYFWyjeevPQt+0e9xCK9YmJ2Rjg5SXgdsXon6cRnU0Yxnbe6CvJbQn26baifur2Y2G5EBayRIsjyg==", "dev": true, "dependencies": { - "chromium-pickle-js": "^0.2.0", "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" @@ -2387,13 +3475,14 @@ } }, "node_modules/@electron/notarize": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-1.2.4.tgz", - "integrity": "sha512-W5GQhJEosFNafewnS28d3bpQ37/s91CDWqxVchHfmv2dQSTWpOzNlUVQwYzC1ay5bChRV/A9BTL68yj0Pa+TSg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.1.0.tgz", + "integrity": "sha512-Q02xem1D0sg4v437xHgmBLxI2iz/fc0D4K7fiVWHa/AnW8o7D751xyKNXgziA6HrTOme9ul1JfWN5ark8WH1xA==", "dev": true, "dependencies": { "debug": "^4.1.1", - "fs-extra": "^9.0.1" + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" }, "engines": { "node": ">= 10.0.0" @@ -2426,328 +3515,548 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/@electron/notarize/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "node_modules/@electron/notarize/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", + "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "dev": true, + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/osx-sign/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/universal": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.4.1.tgz", + "integrity": "sha512-lE/U3UNw1YHuowNbTmKNs9UlS3En3cPgwM5MI+agIgr/B1hSze9NdOP0qn7boZaI9Lph8IDv3/24g9IxnJP7aQ==", + "dev": true, + "dependencies": { + "@electron/asar": "^3.2.1", + "@malept/cross-spawn-promise": "^1.1.0", + "debug": "^4.3.1", + "dir-compare": "^3.0.0", + "fs-extra": "^9.0.1", + "minimatch": "^3.0.4", + "plist": "^3.0.4" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/universal/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/universal/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.20.0", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.41.0", "dev": true, + "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@electron/osx-sign": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.4.tgz", - "integrity": "sha512-xfhdEcIOfAZg7scZ9RQPya1G1lWo8/zMCwUXAulq0SfY7ONIW+b9qGyKdMyuMctNYwllrIS+vmxfijSfjeh97g==", + "node_modules/@gar/promisify": { + "version": "1.1.3", "dev": true, - "dependencies": { - "compare-version": "^0.1.2", - "debug": "^4.3.4", - "fs-extra": "^10.0.0", - "isbinaryfile": "^4.0.8", - "minimist": "^1.2.6", - "plist": "^3.0.5" - }, - "bin": { - "electron-osx-flat": "bin/electron-osx-flat.js", - "electron-osx-sign": "bin/electron-osx-sign.js" - }, - "engines": { - "node": ">=12.0.0" - } + "license": "MIT" }, - "node_modules/@electron/osx-sign/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", "dev": true, + "license": "Apache-2.0", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" }, "engines": { - "node": ">=12" + "node": ">=10.10.0" } }, - "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">= 8.0.0" + "node": ">=12.22" }, "funding": { - "url": "https://github.com/sponsors/gjtorikian/" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@electron/osx-sign/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } + "license": "BSD-3-Clause" }, - "node_modules/@electron/osx-sign/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "node_modules/@ionic/cli-framework-output": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.6.tgz", + "integrity": "sha512-YLPRwnk5Lw0XQ9pKWG+p2KoR5HjMBigZ6yv+/XtL3TGOnCS1+oAz56ABbAORCjTWhSJQisr8APNFiELAecY6QA==", "dev": true, + "dependencies": { + "@ionic/utils-terminal": "2.3.4", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=16.0.0" } }, - "node_modules/@electron/rebuild": { - "version": "3.2.13", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.2.13.tgz", - "integrity": "sha512-DH9Ol4JCnHDYVOD0fKWq+Qqbn/0WU1O6QR0mIpMXEVU4YFM4PlaqNC9K36mGShNBxxGFotZCMDrB1wl/iHM12g==", + "node_modules/@ionic/eslint-config": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@ionic/eslint-config/-/eslint-config-0.3.0.tgz", + "integrity": "sha512-Uf1hS2YIoHlcvXPF5LnsPM6auMewEdChQhR117Rt3sVEAutbyKMpFP4slNC2a6up3a5Q34zepqlf61Qgkf9XeQ==", "dev": true, "dependencies": { - "@malept/cross-spawn-promise": "^2.0.0", - "chalk": "^4.0.0", - "debug": "^4.1.1", - "detect-libc": "^2.0.1", - "fs-extra": "^10.0.0", - "got": "^11.7.0", - "node-abi": "^3.0.0", - "node-api-version": "^0.1.4", - "node-gyp": "^9.0.0", - "ora": "^5.1.0", - "semver": "^7.3.5", - "tar": "^6.0.5", - "yargs": "^17.0.1" - }, - "bin": { - "electron-rebuild": "lib/cli.js" + "@typescript-eslint/eslint-plugin": "^4.1.0", + "@typescript-eslint/parser": "^4.1.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-import": "^2.22.0" }, - "engines": { - "node": ">=12.13.0" + "peerDependencies": { + "eslint": ">=7" } }, - "node_modules/@electron/rebuild/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@ionic/eslint-config/node_modules/@typescript-eslint/eslint-plugin": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", + "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@typescript-eslint/experimental-utils": "4.33.0", + "@typescript-eslint/scope-manager": "4.33.0", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=8" + "node": "^10.12.0 || >=12.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^4.0.0", + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@electron/rebuild/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@ionic/eslint-config/node_modules/@typescript-eslint/experimental-utils": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", + "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" }, "engines": { - "node": ">=10" + "node": "^10.12.0 || >=12.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" } }, - "node_modules/@electron/rebuild/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@ionic/eslint-config/node_modules/@typescript-eslint/parser": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", + "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "debug": "^4.3.1" }, "engines": { - "node": ">=7.0.0" + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@electron/rebuild/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@electron/rebuild/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/@ionic/eslint-config/node_modules/@typescript-eslint/scope-manager": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", + "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0" }, "engines": { - "node": ">=12" + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@electron/rebuild/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@ionic/eslint-config/node_modules/@typescript-eslint/types": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", + "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/@electron/rebuild/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@electron/rebuild/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/@ionic/eslint-config/node_modules/@typescript-eslint/typescript-estree": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=10" + "node": "^10.12.0 || >=12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@electron/rebuild/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@ionic/eslint-config/node_modules/@typescript-eslint/visitor-keys": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", + "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "@typescript-eslint/types": "4.33.0", + "eslint-visitor-keys": "^2.0.0" }, "engines": { - "node": ">=10" + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@electron/rebuild/node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "node_modules/@ionic/eslint-config/node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "eslint-visitor-keys": "^2.0.0" }, "engines": { - "node": ">=10" + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" } }, - "node_modules/@electron/rebuild/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "node_modules/@ionic/eslint-config/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, "engines": { "node": ">=10" } }, - "node_modules/@electron/rebuild/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@ionic/eslint-config/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/@electron/rebuild/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "node_modules/@ionic/eslint-config/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=10" } }, - "node_modules/@electron/rebuild/node_modules/yallist": { + "node_modules/@ionic/eslint-config/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/@electron/universal": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.3.4.tgz", - "integrity": "sha512-BdhBgm2ZBnYyYRLRgOjM5VHkyFItsbggJ0MHycOjKWdFGYwK97ZFXH54dTvUWEfha81vfvwr5On6XBjt99uDcg==", + "node_modules/@ionic/prettier-config": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@ionic/prettier-config/-/prettier-config-1.0.1.tgz", + "integrity": "sha512-/v8UOW7rxkw/hvrRe/QfjlQsdjkm3sfAHoE3uqffO5BoNGijQMARrT32JT9Ei0g6KySXPyxxW+7LzPHrQmfzCw==", + "dev": true, + "peerDependencies": { + "prettier": "^2.0.0" + } + }, + "node_modules/@ionic/swiftlint-config": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ionic/swiftlint-config/-/swiftlint-config-1.1.2.tgz", + "integrity": "sha512-UbE1AIlTowt9uR7fMzRtbQX4URcyuok7mcpdJfFDHAIGM6nDjohYMke+6xOr6ZYlLnEyVmBGNEg0+grEYRgcVg==", + "dev": true + }, + "node_modules/@ionic/utils-array": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz", + "integrity": "sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==", "dev": true, "dependencies": { - "@electron/asar": "^3.2.1", - "@malept/cross-spawn-promise": "^1.1.0", - "debug": "^4.3.1", - "dir-compare": "^3.0.0", - "fs-extra": "^9.0.1", - "minimatch": "^3.0.4", - "plist": "^3.0.4" + "debug": "^4.0.0", + "tslib": "^2.0.1" }, "engines": { - "node": ">=8.6" + "node": ">=16.0.0" } }, - "node_modules/@electron/universal/node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "node_modules/@ionic/utils-fs": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.7.tgz", + "integrity": "sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], "dependencies": { - "cross-spawn": "^7.0.1" + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" }, "engines": { - "node": ">= 10" + "node": ">=16.0.0" } }, - "node_modules/@electron/universal/node_modules/fs-extra": { + "node_modules/@ionic/utils-fs/node_modules/@types/fs-extra": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz", + "integrity": "sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ionic/utils-fs/node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", @@ -2762,7 +4071,7 @@ "node": ">=10" } }, - "node_modules/@electron/universal/node_modules/jsonfile": { + "node_modules/@ionic/utils-fs/node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", @@ -2774,7 +4083,7 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/@electron/universal/node_modules/universalify": { + "node_modules/@ionic/utils-fs/node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", @@ -2783,117 +4092,166 @@ "node": ">= 10.0.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", + "node_modules/@ionic/utils-object": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.6.tgz", + "integrity": "sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==", "dev": true, - "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "debug": "^4.0.0", + "tslib": "^2.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=16.0.0" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", + "node_modules/@ionic/utils-process": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.11.tgz", + "integrity": "sha512-Uavxn+x8j3rDlZEk1X7YnaN6wCgbCwYQOeIjv/m94i1dzslqWhqIHEqxEyeE8HsT5Negboagg7GtQiABy+BLbA==", "dev": true, - "license": "MIT", + "dependencies": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.4", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=16.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.0.3", + "node_modules/@ionic/utils-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.6.tgz", + "integrity": "sha512-4+Kitey1lTA1yGtnigeYNhV/0tggI3lWBMjC7tBs1K9GXa/q7q4CtOISppdh8QgtOhrhAXS2Igp8rbko/Cj+lA==", "dev": true, - "license": "MIT", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.5.2", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "debug": "^4.0.0", + "tslib": "^2.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=16.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", + "node_modules/@ionic/utils-subprocess": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.12.tgz", + "integrity": "sha512-N05Y+dIXBHofKWJTheCMzVqmgY9wFmZcRv/LdNnfXaaA/mxLTyGxQYeig8fvQXTtDafb/siZXcrTkmQ+y6n3Yg==", "dev": true, - "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.11", + "@ionic/utils-stream": "3.1.6", + "@ionic/utils-terminal": "2.3.4", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" }, "engines": { - "node": ">=8" + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-terminal": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.4.tgz", + "integrity": "sha512-cEiMFl3jklE0sW60r8JHH3ijFTwh/jkdEKWbylSyExQwZ8pPuwoXz7gpkWoJRLuoRHHSvg+wzNYyPJazIHfoJA==", + "dev": true, + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", + "node_modules/@ionic/utils-terminal/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "(MIT OR CC0-1.0)", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@eslint/js": { - "version": "8.41.0", + "node_modules/@ionic/utils-terminal/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=7.0.0" } }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "dev": true, - "license": "MIT" + "node_modules/@ionic/utils-terminal/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", + "node_modules/@ionic/utils-terminal/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/@ionic/utils-terminal/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, "engines": { - "node": ">=10.10.0" + "node": ">=8" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", + "node_modules/@ionic/utils-terminal/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, - "license": "Apache-2.0", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, "engines": { - "node": ">=12.22" + "node": ">=10" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", + "node_modules/@ionic/utils-terminal/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "BSD-3-Clause" + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -3805,9 +5163,9 @@ "license": "MIT" }, "node_modules/@malept/cross-spawn-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", - "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", "dev": true, "funding": [ { @@ -3823,7 +5181,7 @@ "cross-spawn": "^7.0.1" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 10" } }, "node_modules/@malept/flatpak-bundler": { @@ -3877,6 +5235,14 @@ "node": ">= 10.0.0" } }, + "node_modules/@mauricewegner/capacitor-navigation-bar": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mauricewegner/capacitor-navigation-bar/-/capacitor-navigation-bar-2.0.3.tgz", + "integrity": "sha512-E8HTcVkZEqm4tLJ7MpkTlqc93mboUSbJL41fFbvEaVwBhpgenEezkK268RxwQDij5hkudZmo4/eRXG3aJQwrzQ==", + "peerDependencies": { + "@capacitor/core": "^4.0.1 || ^5.0.0" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "dev": true, @@ -3980,6 +5346,20 @@ "fsevents": "2.3.2" } }, + "node_modules/@playwright/test/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/@react-dnd/asap": { "version": "4.0.1", "license": "MIT" @@ -4676,9 +6056,9 @@ "license": "MIT" }, "node_modules/@types/debug": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", - "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.10.tgz", + "integrity": "sha512-tOSCru6s732pofZ+sMv9o4o3Zc+Sa8l3bxd/tweTQudFn06vAzb13ZX46Zi6m6EJ+RUbRTHvgQJ1gBtSgkaUYA==", "dev": true, "dependencies": { "@types/ms": "*" @@ -4849,9 +6229,9 @@ "license": "MIT" }, "node_modules/@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.33.tgz", + "integrity": "sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ==", "dev": true }, "node_modules/@types/node": { @@ -4877,9 +6257,9 @@ "license": "MIT" }, "node_modules/@types/plist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", - "integrity": "sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.4.tgz", + "integrity": "sha512-pTa9xUFQFM9WJGSWHajYNljD+DbVylE1q9IweK1LBhUYJdJ28YNU8j3KZ4Q1Qw+cSl4+QLLLOVmqNjhhvVO8fA==", "dev": true, "optional": true, "dependencies": { @@ -4975,6 +6355,12 @@ "@types/node": "*" } }, + "node_modules/@types/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==", + "dev": true + }, "node_modules/@types/sockjs": { "version": "0.3.33", "dev": true, @@ -4997,9 +6383,9 @@ } }, "node_modules/@types/verror": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz", - "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.8.tgz", + "integrity": "sha512-YhUhnxRYs/NiVUbIs3F/EzviDP/NZCEAE2Mx5DUqLdldUmphOhFCVh7Kc+7zlYEExM0P8dzfbJi0yRlNb2Bw5g==", "dev": true, "optional": true }, @@ -5774,6 +7160,15 @@ "node": ">=8" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "dev": true, @@ -5837,16 +7232,15 @@ "dev": true }, "node_modules/app-builder-lib": { - "version": "24.5.2", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.5.2.tgz", - "integrity": "sha512-fZbUrFl3FW7yw92KiDpXV3Nd84EW+D7/WU7MEjX2eHDWM45Qx4hYOZpL9PaT9ZzZbaNfNLmt2EOnoqHQXHLdKw==", + "version": "24.6.4", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.6.4.tgz", + "integrity": "sha512-m9931WXb83teb32N0rKg+ulbn6+Hl8NV5SUpVDOVz9MWOXfhV6AQtTdftf51zJJvCQnQugGtSqoLvgw6mdF/Rg==", "dev": true, "dependencies": { "@develar/schema-utils": "~2.6.5", - "@electron/notarize": "^1.2.3", - "@electron/osx-sign": "^1.0.4", - "@electron/rebuild": "3.2.13", - "@electron/universal": "1.3.4", + "@electron/notarize": "2.1.0", + "@electron/osx-sign": "1.0.5", + "@electron/universal": "1.4.1", "@malept/flatpak-bundler": "^0.4.0", "@types/fs-extra": "9.0.13", "7zip-bin": "~5.1.1", @@ -5949,9 +7343,9 @@ } }, "node_modules/app-builder-lib/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6207,6 +7601,15 @@ "node": ">= 4.0.0" } }, + "node_modules/atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "dev": true, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/autoprefixer": { "version": "10.4.14", "dev": true, @@ -6550,6 +7953,15 @@ "dev": true, "license": "MIT" }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "dev": true, @@ -6568,8 +7980,9 @@ }, "node_modules/bl": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, - "license": "MIT", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -6578,6 +7991,8 @@ }, "node_modules/bl/node_modules/buffer": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "funding": [ { @@ -6593,7 +8008,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -6747,6 +8161,18 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "dev": true, + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "dev": true, @@ -7176,6 +8602,18 @@ } ] }, + "node_modules/capacitor-plugin-safe-area": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/capacitor-plugin-safe-area/-/capacitor-plugin-safe-area-2.0.5.tgz", + "integrity": "sha512-Lg3P+/aW2/+C3gz2/xZFdfjgkOJ/ZSFTmZSxLaSY6lIudOTjwVpjoxhAyKUz1tc13Carjqdlq01yRrr++XOzAg==", + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/capacitor-splash-screen": { + "resolved": "mobile/plugins/capacitor-splash-screen", + "link": true + }, "node_modules/capital-case": { "version": "1.0.4", "dev": true, @@ -7312,6 +8750,15 @@ "node": "*" } }, + "node_modules/chevrotain": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-6.5.0.tgz", + "integrity": "sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==", + "dev": true, + "dependencies": { + "regexp-to-ast": "0.4.0" + } + }, "node_modules/chokidar": { "version": "3.5.3", "dev": true, @@ -7436,20 +8883,25 @@ } }, "node_modules/cli-cursor": { - "version": "3.1.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, - "license": "MIT", "dependencies": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-spinners": { - "version": "2.9.0", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", + "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" }, @@ -7459,8 +8911,9 @@ }, "node_modules/cli-truncate": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", "dev": true, - "license": "MIT", "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^5.0.0" @@ -7524,7 +8977,16 @@ "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" } }, "node_modules/clone-deep": { @@ -7814,6 +9276,85 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/conf": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz", + "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==", + "dev": true, + "dependencies": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conf/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/conf/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/conf/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conf/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conf/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/config-file-ts": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.4.tgz", @@ -8351,6 +9892,30 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debounce-fn/node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/debug": { "version": "4.3.4", "dev": true, @@ -8481,8 +10046,9 @@ }, "node_modules/defaults": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, - "license": "MIT", "dependencies": { "clone": "^1.0.2" }, @@ -8490,14 +10056,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/defaults/node_modules/clone": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -8562,9 +10120,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", "dev": true, "engines": { "node": ">=8" @@ -8613,12 +10171,12 @@ } }, "node_modules/dmg-builder": { - "version": "24.5.2", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.5.2.tgz", - "integrity": "sha512-4qWGO3OM+1ipqvrKvskZRLDEvAPZdZwil6e40Tb8dKogpEhabrzcjpwoRycBy8FAx8R2EBQaFCtIp5rBO/DM8A==", + "version": "24.6.4", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.6.4.tgz", + "integrity": "sha512-BNcHRc9CWEuI9qt0E655bUBU/j/3wUCYBVKGu1kVpbN5lcUdEJJJeiO0NHK3dgKmra6LUUZlo+mWqc+OCbi0zw==", "dev": true, "dependencies": { - "app-builder-lib": "24.5.2", + "app-builder-lib": "24.6.4", "builder-util": "24.5.0", "builder-util-runtime": "9.2.1", "fs-extra": "^10.1.0", @@ -8824,6 +10382,21 @@ "tslib": "^2.0.3" } }, + "node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dotenv": { "version": "16.0.3", "dev": true, @@ -8870,9 +10443,9 @@ } }, "node_modules/electron": { - "version": "22.3.15", - "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.15.tgz", - "integrity": "sha512-KhxJkx2tfB8Q1moUI3sI/x48lehTk3wUEwwaKKkfzSKT3m7nK/g1YSYiYe4c8WuqODAcJKhB1MOvRv3WmhBYBw==", + "version": "22.3.27", + "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.27.tgz", + "integrity": "sha512-7Rht21vHqj4ZFRnKuZdFqZFsvMBCmDqmjetiMqPtF+TmTBiGne1mnstVXOA/SRGhN2Qy5gY5bznJKpiqogjM8A==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -8888,21 +10461,21 @@ } }, "node_modules/electron-builder": { - "version": "24.5.2", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.5.2.tgz", - "integrity": "sha512-rxlUSSqziRMdTSSzti7It4R7wmuttouMhgTiF0HmoTXvaBKlmHPgkQjaI8ZFIZ0Rg+2TFPlPdMu2BwX3+6HJCg==", + "version": "24.6.4", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.6.4.tgz", + "integrity": "sha512-uNWQoU7pE7qOaIQ6CJHpBi44RJFVG8OHRBIadUxrsDJVwLLo8Nma3K/EEtx5/UyWAQYdcK4nVPYKoRqBb20hbA==", "dev": true, "dependencies": { - "app-builder-lib": "24.5.2", + "app-builder-lib": "24.6.4", "builder-util": "24.5.0", "builder-util-runtime": "9.2.1", "chalk": "^4.1.2", - "dmg-builder": "24.5.2", + "dmg-builder": "24.6.4", "fs-extra": "^10.1.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", "read-config-file": "6.3.2", - "simple-update-notifier": "^1.1.0", + "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, "bin": { @@ -9160,22 +10733,191 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/electron-publish": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.5.0.tgz", - "integrity": "sha512-zwo70suH15L15B4ZWNDoEg27HIYoPsGJUF7xevLJLSI7JUPC8l2yLBdLGwqueJ5XkDL7ucYyRZzxJVR8ElV9BA==", + "node_modules/electron-publish": { + "version": "24.5.0", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.5.0.tgz", + "integrity": "sha512-zwo70suH15L15B4ZWNDoEg27HIYoPsGJUF7xevLJLSI7JUPC8l2yLBdLGwqueJ5XkDL7ucYyRZzxJVR8ElV9BA==", + "dev": true, + "dependencies": { + "@types/fs-extra": "^9.0.11", + "builder-util": "24.5.0", + "builder-util-runtime": "9.2.1", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-publish/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/electron-publish/node_modules/builder-util-runtime": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.1.tgz", + "integrity": "sha512-2rLv/uQD2x+dJ0J3xtsmI12AlRyk7p45TEbE/6o/fbb633e/S3pPgm+ct+JHsoY7r39dKHnGEFk/AASRFdnXmA==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/electron-publish/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/electron-publish/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/electron-publish/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/electron-publish/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-publish/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-publish/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-publish/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-publish/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-rebuild": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-3.2.9.tgz", + "integrity": "sha512-FkEZNFViUem3P0RLYbZkUjC8LUFIK+wKq09GHoOITSJjfDAVQv964hwaNseTTWt58sITQX3/5fHNYcTefqaCWw==", + "deprecated": "Please use @electron/rebuild moving forward. There is no API change, just a package name change", + "dev": true, + "dependencies": { + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "lzma-native": "^8.0.5", + "node-abi": "^3.0.0", + "node-api-version": "^0.1.4", + "node-gyp": "^9.0.0", + "ora": "^5.1.0", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/src/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/electron-rebuild/node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], "dependencies": { - "@types/fs-extra": "^9.0.11", - "builder-util": "24.5.0", - "builder-util-runtime": "9.2.1", - "chalk": "^4.1.2", - "fs-extra": "^10.1.0", - "lazy-val": "^1.0.5", - "mime": "^2.5.2" + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 12.13.0" } }, - "node_modules/electron-publish/node_modules/ansi-styles": { + "node_modules/electron-rebuild/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -9190,20 +10932,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/electron-publish/node_modules/builder-util-runtime": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.1.tgz", - "integrity": "sha512-2rLv/uQD2x+dJ0J3xtsmI12AlRyk7p45TEbE/6o/fbb633e/S3pPgm+ct+JHsoY7r39dKHnGEFk/AASRFdnXmA==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "sax": "^1.2.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/electron-publish/node_modules/chalk": { + "node_modules/electron-rebuild/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -9219,7 +10948,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/electron-publish/node_modules/color-convert": { + "node_modules/electron-rebuild/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", @@ -9231,13 +10960,13 @@ "node": ">=7.0.0" } }, - "node_modules/electron-publish/node_modules/color-name": { + "node_modules/electron-rebuild/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/electron-publish/node_modules/fs-extra": { + "node_modules/electron-rebuild/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", @@ -9251,7 +10980,7 @@ "node": ">=12" } }, - "node_modules/electron-publish/node_modules/has-flag": { + "node_modules/electron-rebuild/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -9260,7 +10989,7 @@ "node": ">=8" } }, - "node_modules/electron-publish/node_modules/jsonfile": { + "node_modules/electron-rebuild/node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", @@ -9272,7 +11001,34 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/electron-publish/node_modules/supports-color": { + "node_modules/electron-rebuild/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-rebuild/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-rebuild/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -9284,7 +11040,7 @@ "node": ">=8" } }, - "node_modules/electron-publish/node_modules/universalify": { + "node_modules/electron-rebuild/node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", @@ -9293,6 +11049,37 @@ "node": ">= 10.0.0" } }, + "node_modules/electron-rebuild/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/electron-store": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.1.0.tgz", + "integrity": "sha512-2clHg/juMjOH0GT9cQ6qtmIvK183B39ZXR0bUoPwKwYHJsEF3quqyDzMFUAu+0OP8ijmN2CbPRAelhNbWUbzwA==", + "dev": true, + "dependencies": { + "conf": "^10.2.0", + "type-fest": "^2.17.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron-store/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.449", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.449.tgz", @@ -9488,6 +11275,24 @@ "node": ">=8" } }, + "node_modules/elementtree": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==", + "dev": true, + "dependencies": { + "sax": "1.1.4" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/elementtree/node_modules/sax": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", + "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==", + "dev": true + }, "node_modules/emittery": { "version": "0.13.1", "dev": true, @@ -9551,6 +11356,19 @@ "node": ">=0.6" } }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/entities": { "version": "2.2.0", "dev": true, @@ -9888,6 +11706,21 @@ "eslint-plugin-import": "^2.25.3" } }, + "node_modules/eslint-config-prettier": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", + "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", + "dev": true, + "dependencies": { + "get-stdin": "^6.0.0" + }, + "bin": { + "eslint-config-prettier-check": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=3.14.1" + } + }, "node_modules/eslint-config-react-app": { "version": "7.0.1", "dev": true, @@ -9967,9 +11800,10 @@ } }, "node_modules/eslint-import-resolver-webpack/node_modules/semver": { - "version": "5.7.1", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver" } @@ -10290,6 +12124,30 @@ "node": ">=4.0" } }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/eslint-visitor-keys": { "version": "3.4.1", "dev": true, @@ -11135,6 +12993,20 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "dev": true, @@ -11157,6 +13029,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, "node_modules/functions-have-names": { "version": "1.2.3", "dev": true, @@ -11260,6 +13138,15 @@ "node": ">=8.0.0" } }, + "node_modules/get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "dev": true, @@ -11297,6 +13184,12 @@ "webpack": "^5.0.0" } }, + "node_modules/github-slugger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", + "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", + "dev": true + }, "node_modules/glob": { "version": "7.2.3", "dev": true, @@ -12531,8 +14424,9 @@ }, "node_modules/is-fullwidth-code-point": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -12569,8 +14463,9 @@ }, "node_modules/is-interactive": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -12621,6 +14516,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "dev": true, @@ -12754,8 +14658,9 @@ }, "node_modules/is-unicode-supported": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -13003,6 +14908,16 @@ "node": ">=8" } }, + "node_modules/java-parser": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-parser/-/java-parser-1.0.2.tgz", + "integrity": "sha512-lBXc+F62ds2W83eH5MwGnzuWdb6kgGBV0x0R7w0B4JKGDrJzolMUEhRMzzzlIX68HvRU7XtfPon22YaB+dVg+A==", + "dev": true, + "dependencies": { + "chevrotain": "6.5.0", + "lodash": "4.17.21" + } + }, "node_modules/jest": { "version": "29.5.0", "dev": true, @@ -14598,6 +16513,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==", + "dev": true + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "dev": true, @@ -14748,38 +16669,37 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "13.2.2", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.0.2.tgz", + "integrity": "sha512-vnEy7pFTHyVuDmCAIFKR5QDO8XLVlPFQQyujQ/STOxe40ICWqJ6knS2wSJ/ffX/Lw0rz83luRDh+ET7toN+rOw==", "dev": true, - "license": "MIT", "dependencies": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", + "chalk": "5.3.0", + "commander": "11.1.0", + "debug": "4.3.4", + "execa": "8.0.1", "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.2" + "listr2": "7.0.2", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.3" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=18.12.0" }, "funding": { "url": "https://opencollective.com/lint-staged" } }, "node_modules/lint-staged/node_modules/chalk": { - "version": "5.2.0", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -14788,47 +16708,63 @@ } }, "node_modules/lint-staged/node_modules/commander": { - "version": "10.0.1", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/lint-staged/node_modules/execa": { - "version": "7.1.1", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, - "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", - "signal-exit": "^3.0.7", + "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" }, "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + "node": ">=16.17" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lint-staged/node_modules/human-signals": { - "version": "4.3.1", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, - "license": "Apache-2.0", "engines": { - "node": ">=14.18.0" + "node": ">=16.17.0" } }, "node_modules/lint-staged/node_modules/is-stream": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, - "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -14838,8 +16774,9 @@ }, "node_modules/lint-staged/node_modules/mimic-fn": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -14849,8 +16786,9 @@ }, "node_modules/lint-staged/node_modules/npm-run-path": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^4.0.0" }, @@ -14863,8 +16801,9 @@ }, "node_modules/lint-staged/node_modules/onetime": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, - "license": "MIT", "dependencies": { "mimic-fn": "^4.0.0" }, @@ -14877,19 +16816,9 @@ }, "node_modules/lint-staged/node_modules/path-key": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/strip-final-newline": { - "version": "3.0.0", - "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -14897,130 +16826,116 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/yaml": { - "version": "2.3.1", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 14" - } - }, - "node_modules/listr2": { - "version": "5.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } - } - }, - "node_modules/listr2/node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/listr2/node_modules/cli-truncate": { - "version": "2.1.0", + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, - "license": "MIT", - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/listr2/node_modules/color-convert": { - "version": "2.0.1", + "node_modules/lint-staged/node_modules/yaml": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", + "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">= 14" } }, - "node_modules/listr2/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/listr2/node_modules/emoji-regex": { - "version": "8.0.0", + "node_modules/listr2": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-7.0.2.tgz", + "integrity": "sha512-rJysbR9GKIalhTbVL2tYbF2hVyDnrf7pFUZBwjPaMIdadYHmeT+EVi/Bu3qd7ETQPahTotg2WRCatXwRBW554g==", "dev": true, - "license": "MIT" + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^5.0.1", + "rfdc": "^1.3.0", + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/listr2/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/listr2/node_modules/rxjs": { - "version": "7.8.1", + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/listr2/node_modules/slice-ansi": { - "version": "3.0.0", + "node_modules/listr2/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/listr2/node_modules/string-width": { - "version": "4.2.3", + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, - "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/loader-runner": { @@ -15106,15 +17021,14 @@ "dev": true, "license": "MIT" }, - "node_modules/log-update": { - "version": "4.0.0", + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { "node": ">=10" @@ -15123,10 +17037,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/ansi-styles": { + "node_modules/log-symbols/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -15137,10 +17052,27 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/log-update/node_modules/color-convert": { + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -15148,64 +17080,133 @@ "node": ">=7.0.0" } }, - "node_modules/log-update/node_modules/color-name": { + "node_modules/log-symbols/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "8.0.0", + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT" + "engines": { + "node": ">=8" + } }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", + "node_modules/log-update": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/string-width": { - "version": "4.2.3", + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", "dev": true, - "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "type-fest": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/loose-envify": { @@ -15251,6 +17252,30 @@ "es5-ext": "~0.10.2" } }, + "node_modules/lzma-native": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-8.0.6.tgz", + "integrity": "sha512-09xfg67mkL2Lz20PrrDeNYZxzeW7ADtpYFbwSQh9U8+76RIzx5QsJBMy8qikv3hbUPfpy6hqwxt6FcGK81g9AA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^3.1.0", + "node-gyp-build": "^4.2.1", + "readable-stream": "^3.6.0" + }, + "bin": { + "lzmajs": "bin/lzmajs" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/lzma-native/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true + }, "node_modules/make-dir": { "version": "3.1.0", "dev": true, @@ -15917,6 +17942,44 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/native-bottom-sheet": { + "resolved": "mobile/plugins/native-bottom-sheet", + "link": true + }, + "node_modules/native-run": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/native-run/-/native-run-1.7.2.tgz", + "integrity": "sha512-2aahC8iXIO8BcvEukVMrYwL5sXurkuIGyQgfSGBto832W6ejV+cB5Ww+2/CRxmyozhbxARJ2OMpEGPV8sTqsrQ==", + "dev": true, + "dependencies": { + "@ionic/utils-fs": "^3.1.6", + "@ionic/utils-terminal": "^2.3.3", + "bplist-parser": "^0.3.2", + "debug": "^4.3.4", + "elementtree": "^0.1.7", + "ini": "^3.0.1", + "plist": "^3.0.6", + "split2": "^4.1.0", + "through2": "^4.0.2", + "tslib": "^2.4.0", + "yauzl": "^2.10.0" + }, + "bin": { + "native-run": "bin/native-run" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/native-run/node_modules/ini": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", + "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "dev": true, @@ -15963,9 +18026,9 @@ } }, "node_modules/node-abi": { - "version": "3.43.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.43.0.tgz", - "integrity": "sha512-QB0MMv+tn9Ur2DtJrc8y09n0n6sw88CyDniWSX2cHW10goQXYPK9ZpFJOktDS4ron501edPX6h9i7Pg+RnH5nQ==", + "version": "3.51.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.51.0.tgz", + "integrity": "sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA==", "dev": true, "dependencies": { "semver": "^7.3.5" @@ -15986,10 +18049,10 @@ "node": ">=10" } }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "node_modules/node-abi/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -16036,9 +18099,9 @@ } }, "node_modules/node-api-version/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -16106,6 +18169,17 @@ "node": "^12.13 || ^14.13 || >=16" } }, + "node_modules/node-gyp-build": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz", + "integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-gyp/node_modules/lru-cache": { "version": "6.0.0", "dev": true, @@ -16532,6 +18606,124 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -16712,6 +18904,40 @@ "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/path-to-regexp": { "version": "2.2.1", "dev": true, @@ -16833,6 +19059,79 @@ "node": ">=8" } }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/playwright-core": { "version": "1.34.3", "dev": true, @@ -16858,7 +19157,9 @@ } }, "node_modules/postcss": { - "version": "8.4.24", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "dev": true, "funding": [ { @@ -16874,7 +19175,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -17484,6 +19784,41 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/prettier-plugin-java": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-java/-/prettier-plugin-java-1.0.2.tgz", + "integrity": "sha512-YgcN1WGZlrH0E+bHdqtIYtfDp6k2PHBnIaGjzdff/7t/NyDWAA6ypAmnD7YQVG2OuoIaXYkC37HN7cz68lLWLg==", + "dev": true, + "dependencies": { + "java-parser": "1.0.2", + "lodash": "4.17.21", + "prettier": "2.2.1" + } + }, + "node_modules/prettier-plugin-java/node_modules/prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/pretty-error": { "version": "4.0.0", "dev": true, @@ -17963,9 +20298,10 @@ } }, "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.1", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver" } @@ -18062,6 +20398,12 @@ "@babel/runtime": "^7.8.4" } }, + "node_modules/regexp-to-ast": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.4.0.tgz", + "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==", + "dev": true + }, "node_modules/regexp.prototype.flags": { "version": "1.5.0", "dev": true, @@ -18078,6 +20420,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, "node_modules/regexpu-core": { "version": "5.3.2", "dev": true, @@ -18329,15 +20683,19 @@ } }, "node_modules/restore-cursor": { - "version": "3.1.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "dev": true, - "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/retry": { @@ -18359,8 +20717,9 @@ }, "node_modules/rfdc": { "version": "1.3.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true }, "node_modules/rimraf": { "version": "3.0.2", @@ -18376,6 +20735,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "dev": true, @@ -18607,9 +20981,10 @@ } }, "node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -18947,26 +21322,50 @@ "license": "ISC" }, "node_modules/simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, "dependencies": { - "semver": "~7.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8.10.0" + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, + "node_modules/simple-update-notifier/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/sisteransi": { "version": "1.0.5", "dev": true, @@ -18982,8 +21381,9 @@ }, "node_modules/slice-ansi": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" @@ -18997,8 +21397,9 @@ }, "node_modules/slice-ansi/node_modules/ansi-styles": { "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -19184,6 +21585,15 @@ "wbuf": "^1.7.3" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "dev": true, @@ -19809,6 +22219,271 @@ "url": "https://github.com/fontello/svg2ttf?sponsor=1" } }, + "node_modules/swiftlint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/swiftlint/-/swiftlint-1.0.2.tgz", + "integrity": "sha512-YhcS0N3vkwBatnZf/iJPg89LGQk7MbFnz67Cg3EZ6Ppqm2H8y6x7A1t6KMQ0jYVQpea9wQiFiFRFhkoChaQ29Q==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@ionic/utils-fs": "3.1.6", + "@ionic/utils-subprocess": "2.1.11", + "cosmiconfig": "^6.0.0" + }, + "bin": { + "node-swiftlint": "bin.js" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-array": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.5.tgz", + "integrity": "sha512-HD72a71IQVBmQckDwmA8RxNVMTbxnaLbgFOl+dO5tbvW9CkkSFCv41h6fUuNsSEVgngfkn0i98HDuZC8mk+lTA==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-fs": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.6.tgz", + "integrity": "sha512-eikrNkK89CfGPmexjTfSWl4EYqsPSBh0Ka7by4F0PLc1hJZYtJxUZV3X4r5ecA8ikjicUmcbU7zJmAjmqutG/w==", + "dev": true, + "dependencies": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-object": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.5.tgz", + "integrity": "sha512-XnYNSwfewUqxq+yjER1hxTKggftpNjFLJH0s37jcrNDwbzmbpFTQTVAp4ikNK4rd9DOebX/jbeZb8jfD86IYxw==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-process": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.10.tgz", + "integrity": "sha512-mZ7JEowcuGQK+SKsJXi0liYTcXd2bNMR3nE0CyTROpMECUpJeAvvaBaPGZf5ERQUPeWBVuwqAqjUmIdxhz5bxw==", + "dev": true, + "dependencies": { + "@ionic/utils-object": "2.1.5", + "@ionic/utils-terminal": "2.3.3", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-stream": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.5.tgz", + "integrity": "sha512-hkm46uHvEC05X/8PHgdJi4l4zv9VQDELZTM+Kz69odtO9zZYfnt8DkfXHJqJ+PxmtiE5mk/ehJWLnn/XAczTUw==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-subprocess": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.11.tgz", + "integrity": "sha512-6zCDixNmZCbMCy5np8klSxOZF85kuDyzZSTTQKQP90ZtYNCcPYmuFSzaqDwApJT4r5L3MY3JrqK1gLkc6xiUPw==", + "dev": true, + "dependencies": { + "@ionic/utils-array": "2.1.5", + "@ionic/utils-fs": "3.1.6", + "@ionic/utils-process": "2.1.10", + "@ionic/utils-stream": "3.1.5", + "@ionic/utils-terminal": "2.3.3", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@ionic/utils-terminal": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.3.tgz", + "integrity": "sha512-RnuSfNZ5fLEyX3R5mtcMY97cGD1A0NVBbarsSQ6yMMfRJ5YHU7hHVyUfvZeClbqkBC/pAqI/rYJuXKCT9YeMCQ==", + "dev": true, + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/swiftlint/node_modules/@types/fs-extra": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.3.tgz", + "integrity": "sha512-7IdV01N0u/CaVO0fuY1YmEg14HQN3+EW8mpNgg6NEfxEl/lzCa5OxlBu3iFsCAdamnYOcTQ7oEi43Xc/67Rgzw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/swiftlint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/swiftlint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/swiftlint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/swiftlint/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/swiftlint/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/swiftlint/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/swiftlint/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/swiftlint/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/swiftlint/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/swiftlint/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/swiftlint/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/symbol.inspect": { "version": "1.0.1", "license": "ISC" @@ -20116,10 +22791,14 @@ "dev": true, "license": "MIT" }, - "node_modules/through": { - "version": "2.3.8", + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "dev": true, - "license": "MIT" + "dependencies": { + "readable-stream": "3" + } }, "node_modules/thunky": { "version": "1.1.0", @@ -20399,7 +23078,6 @@ }, "node_modules/tslib": { "version": "2.5.2", - "dev": true, "license": "0BSD" }, "node_modules/tsutils": { @@ -20555,15 +23233,16 @@ } }, "node_modules/typescript": { - "version": "5.0.4", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/uglify-js": { @@ -20668,6 +23347,15 @@ "node": ">= 0.8" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/unused-filename": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-2.1.0.tgz", @@ -20920,8 +23608,9 @@ }, "node_modules/wcwidth": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, - "license": "MIT", "dependencies": { "defaults": "^1.0.3" } @@ -21482,6 +24171,28 @@ } } }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", @@ -21593,8 +24304,9 @@ } }, "node_modules/zod": { - "version": "3.21.4", - "license": "MIT", + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 04b9ba2f..a8a43a5f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mytonwallet", - "version": "1.16.4", + "version": "1.17.0", "description": "The most feature-rich web wallet and browser extension for TON – with support of multi-accounts, tokens (jettons), NFT, TON DNS, TON Sites, TON Proxy, and TON Magic.", "main": "index.js", "scripts": { @@ -18,12 +18,18 @@ "extension-opera:package": "cross-env IS_OPERA_EXTENSION=1 IS_EXTENSION=1 webpack && bash ./deploy/package_extension.sh opera", "extension-opera:package:staging": "cross-env APP_ENV=staging npm run extension-opera:package", "extension-opera:package:production": "npm run extension-opera:package", - "electron:dev": "npm run electron:webpack && IS_ELECTRON=1 concurrently -n main,renderer,electron \"npm run electron:webpack -- --watch\" \"npm run dev\" \"electronmon dist/electron\"", + "electron:dev": "npm run electron:webpack && IS_ELECTRON_BUILD=1 concurrently -n main,renderer,electron \"npm run electron:webpack -- --watch\" \"npm run dev\" \"electronmon dist/electron\"", "electron:webpack": "cross-env APP_ENV=$ENV webpack --config ./webpack-electron.config.ts", - "electron:build": "IS_ELECTRON=1 npm run build:$ENV && electron-builder install-app-deps && electron-rebuild && ENV=$ENV npm run electron:webpack", + "electron:build": "IS_ELECTRON_BUILD=1 npm run build:$ENV && electron-builder install-app-deps && electron-rebuild && ENV=$ENV npm run electron:webpack", "electron:package": "npm run electron:build && npx rimraf dist-electron && electron-builder build --win --mac --linux --config src/electron/config.yml", "electron:package:staging": "ENV=staging npm run electron:package -- -p never", "electron:release:production": "ENV=production npm run electron:package -- -p always", + "mobile:build": "IS_CAPACITOR=1 npm run build && cap sync", + "mobile:build:dev": "cross-env APP_ENV=development npm run mobile:build", + "mobile:build:staging": "cross-env APP_ENV=staging npm run mobile:build", + "mobile:build:production": "npm run mobile:build", + "mobile:run:android": "npm run mobile:build:dev && cap run android", + "mobile:run:ios": "npm run mobile:build:dev && cap run ios", "build:icons": "fantasticon", "check": "tsc && stylelint \"**/*.{css,scss}\" && eslint . --ext .ts,.tsx", "check:fix": "npm run check -- --fix", @@ -31,7 +37,9 @@ "test:playwright": "playwright test", "test:record": "playwright codegen localhost:1235", "prepare": "husky install", - "statoscope:validate-diff": "statoscope validate --input input.json --reference reference.json" + "statoscope:validate-diff": "statoscope validate --input input.json --reference reference.json", + "update_version": "node ./deploy/update_version.js", + "postversion": "rm -rf .patch-version && npm run update_version" }, "engines": { "node": "^18", @@ -72,6 +80,7 @@ "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.0", "@babel/register": "^7.21.0", + "@capacitor/cli": "^5.3.0", "@peculiar/webcrypto": "^1.3.2", "@playwright/test": "^1.31.2", "@statoscope/cli": "^5.27.0", @@ -108,6 +117,8 @@ "electron": "^22.1.0", "electron-builder": "^24.4.0", "electron-context-menu": "^3.6.1", + "electron-rebuild": "^3.2.9", + "electron-store": "^8.1.0", "electron-updater": "^5.3.0", "electron-window-state": "^5.0.3", "electronmon": "^2.0.2", @@ -134,7 +145,7 @@ "jest": "^29.5.0", "jest-raw-loader": "^1.0.1", "js-yaml": "^4.1.0", - "lint-staged": "^13.2.0", + "lint-staged": "^15.0.2", "mini-css-extract-plugin": "^2.7.5", "postcss": "^8.4.14", "postcss-loader": "^7.1.0", @@ -153,15 +164,28 @@ "stylelint-group-selectors": "^1.0.9", "stylelint-high-performance-animation": "^1.8.0", "stylelint-order": "^5.0.0", - "typescript": "^5.0.2", + "typescript": "^5.2.2", "webpack": "^5.76.2", "webpack-dev-server": "^4.13.1" }, "dependencies": { + "@capacitor-mlkit/barcode-scanning": "^5.3.0", + "@capacitor/android": "^5.3.0", + "@capacitor/app": "^5.0.6", + "@capacitor/core": "^5.5.1", + "@capacitor/dialog": "^5.0.6", + "@capacitor/haptics": "^5.0.6", + "@capacitor/ios": "^5.5.1", + "@capacitor/status-bar": "^5.0.6", + "@capgo/capacitor-native-biometric": "^5.1.0", "@ledgerhq/hw-transport-webhid": "^6.27.12", + "@mauricewegner/capacitor-navigation-bar": "^2.0.3", "@ton-community/ton-ledger": "^6.0.0", "buffer": "^6.0.3", + "capacitor-plugin-safe-area": "^2.0.5", + "capacitor-splash-screen": "file:mobile/plugins/capacitor-splash-screen", "idb-keyval": "^6.2.0", + "native-bottom-sheet": "file:mobile/plugins/native-bottom-sheet", "pako": "^2.1.0", "qr-code-styling": "github:troman29/qr-code-styling#c00d0", "qrcode-generator": "^1.4.4", diff --git a/public/electronVersion.txt b/public/electronVersion.txt new file mode 100644 index 00000000..092afa15 --- /dev/null +++ b/public/electronVersion.txt @@ -0,0 +1 @@ +1.17.0 diff --git a/public/get/.well-known/apple-app-site-association b/public/get/.well-known/apple-app-site-association new file mode 100644 index 00000000..5347fbc6 --- /dev/null +++ b/public/get/.well-known/apple-app-site-association @@ -0,0 +1,11 @@ +{ + "applinks": { + "apps": [], + "details": [ + { + "appID": "Y54Z4K69Z9.org.mytonwallet.app", + "paths": ["*"] + } + ] + } +} diff --git a/public/get/.well-known/assetlinks.json b/public/get/.well-known/assetlinks.json new file mode 100644 index 00000000..2b133408 --- /dev/null +++ b/public/get/.well-known/assetlinks.json @@ -0,0 +1,15 @@ +[ + { + "relation": [ + "delegate_permission/common.handle_all_urls" + ], + "target": { + "namespace": "android_app", + "package_name": "org.mytonwallet.app", + "sha256_cert_fingerprints": [ + "93:31:8E:37:31:AE:D8:29:A9:9F:18:FB:9A:1F:0C:7A:D4:B6:02:2C:14:E6:89:5C:DA:D0:BE:37:A9:DE:58:97", + "C2:F6:EC:F2:AA:46:B9:76:7E:B4:11:7E:7E:D0:16:AB:04:44:03:5C:5D:8C:7A:AC:5A:9F:04:AC:07:A0:B6:40" + ] + } + } +] \ No newline at end of file diff --git a/public/logo.svg b/public/logo.svg index 40c5b004..5e4d3a57 100644 --- a/public/logo.svg +++ b/public/logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/version.txt b/public/version.txt new file mode 100644 index 00000000..73d74673 --- /dev/null +++ b/public/version.txt @@ -0,0 +1 @@ +1.17.0 \ No newline at end of file diff --git a/src/api/blockchains/ton/address.ts b/src/api/blockchains/ton/address.ts index 6d684ce1..b3a8c081 100644 --- a/src/api/blockchains/ton/address.ts +++ b/src/api/blockchains/ton/address.ts @@ -40,3 +40,7 @@ export async function resolveAddress(network: ApiNetwork, address: string): Prom return undefined; } } + +export function normalizeAddress(address: string) { + return toBase64Address(address, true); +} diff --git a/src/api/blockchains/ton/constants.ts b/src/api/blockchains/ton/constants.ts index 81acd65c..f41d9449 100644 --- a/src/api/blockchains/ton/constants.ts +++ b/src/api/blockchains/ton/constants.ts @@ -9,10 +9,11 @@ export const UNSTAKE_COMMENT = 'w'; export const ATTEMPTS = 5; export const DEFAULT_DECIMALS = 9; +export const DEFAULT_IS_BOUNCEABLE = false; export const LEDGER_SUPPORTED_PAYLOADS: ApiParsedPayload['type'][] = [ - 'transfer-nft', - 'transfer-tokens', + 'nft:transfer', + 'tokens:transfer', 'comment', ]; @@ -33,3 +34,17 @@ export enum JettonOpCode { export enum NftOpCode { TransferOwnership = 0x5fcc3d14, } + +export enum LiquidStakingOpCode { + // Pool + RequestLoan = 0xe642c965, + LoanRepayment = 0xdfdca27b, + Deposit = 0x47d54391, + Withdraw = 0x319B0CDC, + Withdrawal = 0x0a77535c, + DeployController = 0xb27edcad, + Touch = 0x4bc7c2df, + Donate = 0x73affe21, + // NFT + DistributedAsset = 0xdb3b8abd, +} diff --git a/src/api/blockchains/ton/index.ts b/src/api/blockchains/ton/index.ts index 9cc742ca..2fbcad7d 100644 --- a/src/api/blockchains/ton/index.ts +++ b/src/api/blockchains/ton/index.ts @@ -7,9 +7,11 @@ export { seedToKeyPair, validateMnemonic, verifyPassword, + fetchPrivateKey, } from './auth'; export { getAccountNfts, getNftUpdates } from './nfts'; export { oneCellFromBoc } from './util/tonweb'; +export { buildTokenSlug } from './util'; export { checkTransactionDraft, getAccountNewestTxId, @@ -23,6 +25,7 @@ export { sendSignedMessage, sendSignedMessages, decryptComment, + waitUntilTransactionAppears, } from './transactions'; export { getAccountBalance, @@ -33,7 +36,9 @@ export { getWalletStateInit, getWalletBalance, getWalletSeqno, - isWalletInitialized, + isAddressInitialized, + isActiveSmartContract, + getWalletInfo, } from './wallet'; export { checkStakeDraft, @@ -41,14 +46,14 @@ export { submitStake, submitUnstake, getStakingState, - getBackendStakingState, } from './staking'; export { packPayloadToBoc, + checkApiAvailability, } from './other'; export { getAccountTokenBalances, - importToken, + fetchToken, resolveTokenBySlug, } from './tokens'; export { @@ -58,3 +63,6 @@ export { export { parsePayloadBase64, } from './util/metadata'; +export { + normalizeAddress, +} from './address'; diff --git a/src/api/blockchains/ton/nfts.ts b/src/api/blockchains/ton/nfts.ts index b3b0fc39..83f62537 100644 --- a/src/api/blockchains/ton/nfts.ts +++ b/src/api/blockchains/ton/nfts.ts @@ -13,7 +13,7 @@ export async function getAccountNfts(accountId: string, offset?: number, limit?: const { network } = parseAccountId(accountId); const address = await fetchStoredAddress(accountId); - const rawNfts = await fetchAccountNfts(network, address, offset, limit); + const rawNfts = await fetchAccountNfts(network, address, { offset, limit }); return compact(rawNfts.map(buildNft)); } @@ -30,20 +30,25 @@ export function buildNft(rawNft: NftItem): ApiNft | undefined { metadata: { name, image, + attributes, + description, }, previews, sale, } = rawNft; + const isHidden = attributes?.render_type === 'hidden' || description === 'SCAM'; + return { index, name, - address: toBase64Address(address), + address: toBase64Address(address, true), image, thumbnail: previews!.find((x) => x.resolution === '500x500')!.url, isOnSale: Boolean(sale), + isHidden, ...(collection && { - collectionAddress: toBase64Address(collection.address), + collectionAddress: toBase64Address(collection.address, true), collectionName: collection.name, }), }; @@ -72,12 +77,12 @@ export async function getNftUpdates(accountId: string, fromSec: number) { const { sender, recipient, nft: rawNftAddress } = action.nftItemTransfer; if (!sender || !recipient) continue; to = toBase64Address(recipient.address); - nftAddress = toBase64Address(rawNftAddress); + nftAddress = toBase64Address(rawNftAddress, true); } else if (action.nftPurchase) { const { buyer } = action.nftPurchase; to = toBase64Address(buyer.address); rawNft = action.nftPurchase.nft; - nftAddress = toBase64Address(rawNft.address); + nftAddress = toBase64Address(rawNft.address, true); } else { continue; } @@ -86,12 +91,16 @@ export async function getNftUpdates(accountId: string, fromSec: number) { if (!rawNft) { [rawNft] = await fetchNftItems(network, [nftAddress]); } - updates.push({ - type: 'nftReceived', - accountId, - nftAddress, - nft: buildNft(rawNft)!, - }); + const nft = buildNft(rawNft); + + if (nft) { + updates.push({ + type: 'nftReceived', + accountId, + nftAddress, + nft, + }); + } } else if (!isPurchase && await isActiveSmartContract(network, to)) { updates.push({ type: 'nftPutUpForSale', diff --git a/src/api/blockchains/ton/other.ts b/src/api/blockchains/ton/other.ts index f3dce1cf..68d69d0e 100644 --- a/src/api/blockchains/ton/other.ts +++ b/src/api/blockchains/ton/other.ts @@ -1,6 +1,9 @@ import TonWeb from 'tonweb'; import type { Cell as CellType } from 'tonweb/dist/types/boc/cell'; +import type { ApiNetwork } from '../../types'; + +import { getTonWeb } from './util/tonweb'; import { bytesToBase64 } from '../../common/utils'; const { Cell } = TonWeb.boc; @@ -21,3 +24,12 @@ export async function packPayloadToBoc(payload: string | Uint8Array | CellType) } return bytesToBase64(await payloadCell.toBoc()); } + +export async function checkApiAvailability(network: ApiNetwork) { + try { + await getTonWeb(network).provider.getMasterchainInfo(); + return true; + } catch (err: any) { + return false; + } +} diff --git a/src/api/blockchains/ton/staking.ts b/src/api/blockchains/ton/staking.ts index f0eabf7f..7707ec3b 100644 --- a/src/api/blockchains/ton/staking.ts +++ b/src/api/blockchains/ton/staking.ts @@ -1,158 +1,290 @@ -import TonWeb from 'tonweb'; import BN from 'bn.js'; import type { ApiBackendStakingState, ApiNetwork, + ApiStakingCommonData, ApiStakingState, + ApiStakingType, } from '../../types'; -import { - ApiTransactionDraftError, -} from '../../types'; +import type { CheckTransactionDraftResult, SubmitTransferResult } from './transactions'; +import type { TonTransferParams } from './types'; +import { ApiCommonError, ApiLiquidUnstakeMode, ApiTransactionDraftError } from '../../types'; -import { TON_TOKEN_SLUG } from '../../../config'; +import { + LIQUID_JETTON, LIQUID_POOL, STAKING_MIN_AMOUNT, TON_TOKEN_SLUG, +} from '../../../config'; +import { Big } from '../../../lib/big.js'; import { parseAccountId } from '../../../util/account'; -import memoized from '../../../util/memoized'; -import { getTonWeb, toBase64Address } from './util/tonweb'; +import { + buildLiquidStakingDepositBody, + buildLiquidStakingWithdrawBody, + fromNano, + getTokenBalance, + getTonWeb, + resolveTokenWalletAddress, + toBase64Address, + toNano, +} from './util/tonweb'; import { NominatorPool } from './contracts/NominatorPool'; import { fetchStoredAddress } from '../../common/accounts'; -import { callBackendGet } from '../../common/backend'; -import { isKnownStakingPool } from '../../common/utils'; -import { STAKE_COMMENT, UNSTAKE_COMMENT } from './constants'; +import { apiDb } from '../../db'; +import { DEFAULT_DECIMALS, STAKE_COMMENT, UNSTAKE_COMMENT } from './constants'; import { checkTransactionDraft, submitTransfer } from './transactions'; +import { isAddressInitialized } from './wallet'; -const { toNano } = TonWeb.utils; +const LIQUID_STAKE_AMOUNT = '1'; +const LIQUID_UNSTAKE_AMOUNT = '1'; +const UNSTAKE_AMOUNT = '1'; +const MIN_NOMINATORS_AMOUNT = '10001'; -const ONE_TON = '1000000000'; -const UNSTAKE_AMOUNT = ONE_TON; -const MIN_STAKE_AMOUNT = 10001; -const CACHE_TTL = 60; // 1 m. -const DISABLE_CACHE_PERIOD = 30; // 30 s. +export async function checkStakeDraft( + accountId: string, + amount: string, + commonData: ApiStakingCommonData, + backendState: ApiBackendStakingState, +) { + const staked = await getStakingState(accountId, commonData, backendState); -export const fetchStakingStateMemo = memoized(fetchBackendStakingState, CACHE_TTL); + let type: ApiStakingType; + let result: CheckTransactionDraftResult; + const bigAmount = Big(fromNano(amount)); -export async function checkStakeDraft(accountId: string, amount: string) { - const address = await fetchStoredAddress(accountId); - const { poolAddress } = await fetchStakingStateMemo(address); - - const result: { - error?: ApiTransactionDraftError; - fee?: string; - } = {}; - - const staked = await getStakingState(accountId); - if (!staked) { - if (new BN(amount).lt(toNano(MIN_STAKE_AMOUNT.toString()))) { - result.error = ApiTransactionDraftError.InvalidAmount; - return result; - } + if ( + (staked?.type === 'nominators' && bigAmount.gte(STAKING_MIN_AMOUNT)) + || (staked.type === 'empty' && bigAmount.gte(MIN_NOMINATORS_AMOUNT)) + ) { + type = 'nominators'; + + const poolAddress = backendState.nominatorsPool.address; + amount = new BN(amount).add(toNano(LIQUID_STAKE_AMOUNT)).toString(); + result = await checkTransactionDraft(accountId, TON_TOKEN_SLUG, poolAddress, amount, STAKE_COMMENT); + } else if (bigAmount.lt(STAKING_MIN_AMOUNT)) { + return { error: ApiTransactionDraftError.InvalidAmount }; + } else { + type = 'liquid'; + + const body = buildLiquidStakingDepositBody(); + result = await checkTransactionDraft(accountId, TON_TOKEN_SLUG, LIQUID_POOL, amount, body); } - return checkTransactionDraft( - accountId, - TON_TOKEN_SLUG, - poolAddress, - amount, - STAKE_COMMENT, - ); + return { + ...result, + type, + }; } -export async function checkUnstakeDraft(accountId: string) { +export async function checkUnstakeDraft( + accountId: string, + amount: string, + commonData: ApiStakingCommonData, + backendState: ApiBackendStakingState, +) { + const { network } = parseAccountId(accountId); const address = await fetchStoredAddress(accountId); - const { poolAddress } = await fetchStakingStateMemo(address); - return checkTransactionDraft( - accountId, - TON_TOKEN_SLUG, - poolAddress, - UNSTAKE_AMOUNT, - UNSTAKE_COMMENT, - ); + const staked = await getStakingState(accountId, commonData, backendState); + + let type: ApiStakingType; + let result: CheckTransactionDraftResult; + let tokenAmount: string | undefined; + + if (staked.type === 'nominators') { + type = 'nominators'; + + const poolAddress = backendState.nominatorsPool.address; + result = await checkTransactionDraft( + accountId, TON_TOKEN_SLUG, poolAddress, toNano(UNSTAKE_AMOUNT).toString(), UNSTAKE_COMMENT, + ); + } else if (staked.type === 'liquid') { + type = 'liquid'; + + const bigAmount = Big(fromNano(amount).toString()); + if (bigAmount.gt(staked.amount)) { + return { error: ApiTransactionDraftError.InsufficientBalance }; + } else if (bigAmount.eq(staked.amount)) { + tokenAmount = staked.tokenAmount; + } else { + const { currentRate } = commonData.liquid; + tokenAmount = bigAmount.div(currentRate).toFixed(DEFAULT_DECIMALS); + } + + tokenAmount = toNano(tokenAmount).toString(); + + const params = await buildLiquidStakingWithdraw(network, address, tokenAmount); + result = await checkTransactionDraft( + accountId, TON_TOKEN_SLUG, params.toAddress, params.amount, params.payload, + ); + } else { + return { error: ApiCommonError.Unexpected }; + } + + return { + ...result, + type, + tokenAmount, + }; } -export async function submitStake(accountId: string, password: string, amount: string) { - const address = await fetchStoredAddress(accountId); - const { poolAddress } = await fetchStakingStateMemo(address); - const result = await submitTransfer( - accountId, - password, - TON_TOKEN_SLUG, - toBase64Address(poolAddress), - amount, - STAKE_COMMENT, - ); - onStakingChangeExpected(); +export async function submitStake( + accountId: string, + password: string, + amount: string, + type: ApiStakingType, + backendState: ApiBackendStakingState, +) { + let result: SubmitTransferResult; + + if (type === 'liquid') { + amount = new BN(amount).add(toNano(LIQUID_STAKE_AMOUNT)).toString(); + result = await submitTransfer( + accountId, + password, + TON_TOKEN_SLUG, + LIQUID_POOL, + amount, + buildLiquidStakingDepositBody(), + ); + } else { + const poolAddress = backendState.nominatorsPool.address; + result = await submitTransfer( + accountId, + password, + TON_TOKEN_SLUG, + toBase64Address(poolAddress, true), + amount, + STAKE_COMMENT, + ); + } + return result; } -export async function submitUnstake(accountId: string, password: string) { +export async function submitUnstake( + accountId: string, + password: string, + type: ApiStakingType, + amount: string, + backendState: ApiBackendStakingState, +) { + const { network } = parseAccountId(accountId); const address = await fetchStoredAddress(accountId); - const { poolAddress } = await fetchStakingStateMemo(address); - const result = await submitTransfer( - accountId, - password, - TON_TOKEN_SLUG, - toBase64Address(poolAddress), - UNSTAKE_AMOUNT, - UNSTAKE_COMMENT, - ); - onStakingChangeExpected(); + + let result: SubmitTransferResult; + + if (type === 'liquid') { + const params = await buildLiquidStakingWithdraw(network, address, amount); + result = await submitTransfer( + accountId, + password, + TON_TOKEN_SLUG, + params.toAddress, + params.amount, + params.payload, + ); + } else { + const poolAddress = backendState.nominatorsPool.address; + result = await submitTransfer( + accountId, + password, + TON_TOKEN_SLUG, + toBase64Address(poolAddress, true), + toNano(UNSTAKE_AMOUNT).toString(), + UNSTAKE_COMMENT, + ); + } + return result; } -function onStakingChangeExpected() { - fetchStakingStateMemo.disableCache(DISABLE_CACHE_PERIOD); +export async function buildLiquidStakingWithdraw( + network: ApiNetwork, + address: string, + amount: string, + mode: ApiLiquidUnstakeMode = ApiLiquidUnstakeMode.Default, +): Promise { + const tokenWalletAddress = await resolveTokenWalletAddress(network, address, LIQUID_JETTON); + + const payload = buildLiquidStakingWithdrawBody({ + amount, + responseAddress: address, + fillOrKill: mode === ApiLiquidUnstakeMode.Instant, + waitTillRoundEnd: mode === ApiLiquidUnstakeMode.BestRate, + }); + + return { + amount: toNano(LIQUID_UNSTAKE_AMOUNT).toString(), + toAddress: tokenWalletAddress, + payload, + }; } -export async function getStakingState(accountId: string): Promise { +export async function getStakingState( + accountId: string, + commonData: ApiStakingCommonData, + backendState: ApiBackendStakingState, +): Promise { const { network } = parseAccountId(accountId); - const address = await fetchStoredAddress(accountId); - const contract = await getPoolContract(network, address); - if (network !== 'mainnet' || !contract) { + const address = toBase64Address(await fetchStoredAddress(accountId), true); + + const { currentRate, collection } = commonData.liquid; + const tokenBalance = Big(await getLiquidStakingTokenBalance(accountId)); + let unstakeAmount = Big(0); + + if (collection) { + const nfts = await apiDb.nfts.where({ collectionAddress: collection }).toArray(); + + for (const nft of nfts) { + const billAmount = nft.name?.match(/Bill for (?[\d.]+) Pool Jetton/)?.groups?.amount; + unstakeAmount = unstakeAmount.plus(billAmount ?? 0); + } + } + + if (tokenBalance.gt(0) || unstakeAmount.gt(0)) { + const fullTokenAmount = tokenBalance.plus(unstakeAmount); + const amount = Big(currentRate).times(fullTokenAmount).toFixed(DEFAULT_DECIMALS); + return { - amount: 0, - pendingDepositAmount: 0, - isUnstakeRequested: false, + type: 'liquid', + tokenAmount: tokenBalance.toFixed(DEFAULT_DECIMALS), + amount: parseFloat(amount), + unstakeRequestAmount: unstakeAmount.toNumber(), }; } - const nominators = await contract.getListNominators(); + const poolAddress = backendState.nominatorsPool.address; + const nominatorPool = getPoolContract(network, poolAddress); + const nominators = await nominatorPool.getListNominators(); const currentNominator = nominators.find((n) => n.address === address); - if (!currentNominator) { + + if (currentNominator) { return { - amount: 0, - pendingDepositAmount: 0, - isUnstakeRequested: false, + type: 'nominators', + amount: parseFloat(currentNominator.amount), + pendingDepositAmount: parseFloat(currentNominator.pendingDepositAmount), + isUnstakeRequested: currentNominator.withdrawRequested, }; } - return { - amount: parseFloat(currentNominator.amount), - pendingDepositAmount: parseFloat(currentNominator.pendingDepositAmount), - isUnstakeRequested: currentNominator.withdrawRequested, - }; + return { type: 'empty' }; } -async function getPoolContract(network: ApiNetwork, address: string) { - const { poolAddress } = await fetchStakingStateMemo(address); +function getPoolContract(network: ApiNetwork, poolAddress: string) { return new NominatorPool(getTonWeb(network).provider, { address: poolAddress }); } -export async function getBackendStakingState(accountId: string): Promise { +async function getLiquidStakingTokenBalance(accountId: string) { const { network } = parseAccountId(accountId); if (network !== 'mainnet') { - return undefined; + return '0'; } const address = await fetchStoredAddress(accountId); - return fetchStakingStateMemo(address); -} - -export async function fetchBackendStakingState(address: string) { - const stakingState = await callBackendGet(`/staking-state?account=${address}`) as ApiBackendStakingState; + const walletAddress = await resolveTokenWalletAddress(network, address, LIQUID_JETTON); + const isInitialized = await isAddressInitialized(network, walletAddress); - if (!isKnownStakingPool(stakingState.poolAddress)) { - throw Error('Unexpected pool address, likely a malicious activity'); + if (!isInitialized) { + return '0'; } - return stakingState; + return getTokenBalance(network, walletAddress); } diff --git a/src/api/blockchains/ton/tokens.ts b/src/api/blockchains/ton/tokens.ts index 71cf306f..022d31af 100644 --- a/src/api/blockchains/ton/tokens.ts +++ b/src/api/blockchains/ton/tokens.ts @@ -6,13 +6,14 @@ import type { } from '../../types'; import type { AnyPayload, ApiTransactionExtra, JettonMetadata } from './types'; +import { DEFAULT_DECIMAL_PLACES, TON_SYMBOL, TON_TOKEN_SLUG } from '../../../config'; import { parseAccountId } from '../../../util/account'; import { logDebugError } from '../../../util/logs'; +import { fixIpfsUrl } from '../../../util/metadata'; import { buildTokenSlug } from './util'; import { + fetchJettonMetadata, fixBase64ImageData, - fixIpfsUrl, - getJettonMetadata, parseJettonWalletMsgBody, } from './util/metadata'; import { fetchJettonBalances } from './util/tonapiio'; @@ -41,10 +42,11 @@ export type TokenBalanceParsed = { const KNOWN_TOKENS: ApiBaseToken[] = [ { - slug: 'toncoin', + slug: TON_TOKEN_SLUG, name: 'Toncoin', - symbol: 'TON', - decimals: 9, + cmcSlug: TON_TOKEN_SLUG, + symbol: TON_SYMBOL, + decimals: DEFAULT_DECIMAL_PLACES, }, ]; @@ -66,7 +68,7 @@ function parseTokenBalance(balanceRaw: JettonBalance): TokenBalanceParsed { try { const { balance, jetton } = balanceRaw; - const minterAddress = toBase64Address(jetton.address); + const minterAddress = toBase64Address(jetton.address, true); const token = buildTokenByMetadata(minterAddress, jetton); return { slug: token.slug, balance, token }; @@ -174,8 +176,8 @@ export function addKnownTokens(tokens: ApiBaseToken[]) { } } -export async function importToken(network: ApiNetwork, address: string) { - const metadata = await getJettonMetadata(network, address); +export async function fetchToken(network: ApiNetwork, address: string) { + const metadata = await fetchJettonMetadata(network, address); return buildTokenByMetadata(address, metadata); } diff --git a/src/api/blockchains/ton/transactions.ts b/src/api/blockchains/ton/transactions.ts index 8db1b853..1df000e2 100644 --- a/src/api/blockchains/ton/transactions.ts +++ b/src/api/blockchains/ton/transactions.ts @@ -5,11 +5,17 @@ import type { Cell } from 'tonweb/dist/types/boc/cell'; import type { WalletContract } from 'tonweb/dist/types/contract/wallet/wallet-contract'; import type { - ApiNetwork, ApiSignedTransfer, ApiTransaction, ApiTransactionActivity, ApiTxIdBySlug, + ApiAnyDisplayError, + ApiNetwork, + ApiSignedTransfer, + ApiTransaction, + ApiTransactionActivity, + ApiTransactionType, + ApiTxIdBySlug, } from '../../types'; import type { JettonWalletType } from './tokens'; import type { AnyPayload, ApiTransactionExtra, TonTransferParams } from './types'; -import { ApiTransactionDraftError, ApiTransactionError } from '../../types'; +import { ApiCommonError, ApiTransactionDraftError, ApiTransactionError } from '../../types'; import { TON_TOKEN_SLUG } from '../../../config'; import { parseAccountId } from '../../../util/account'; @@ -33,10 +39,11 @@ import { resolveTokenWalletAddress, toBase64Address, } from './util/tonweb'; -import { fetchStoredAccount, fetchStoredAddress, fetchStoredPublicKey } from '../../common/accounts'; +import { fetchStoredAccount, fetchStoredAddress } from '../../common/accounts'; import { getAddressInfo } from '../../common/addresses'; import { updateTransactionMetadata } from '../../common/helpers'; import { bytesToBase64, isKnownStakingPool } from '../../common/utils'; +import { ApiServerError, handleServerError } from '../../errors'; import { resolveAddress } from './address'; import { fetchKeyPair, fetchPrivateKey } from './auth'; import { ATTEMPTS, STAKE_COMMENT, UNSTAKE_COMMENT } from './constants'; @@ -44,10 +51,19 @@ import { buildTokenTransfer, getTokenWalletBalance, parseTokenTransaction, resolveTokenBySlug, } from './tokens'; import { - getWalletBalance, getWalletInfo, isWalletInitialized, pickAccountWallet, + getWalletBalance, getWalletInfo, isAddressInitialized, pickAccountWallet, } from './wallet'; -type SubmitTransferResult = { +export type CheckTransactionDraftResult = { + fee?: string; + addressName?: string; + isScam?: boolean; + resolvedAddress?: string; + isToAddressNew?: boolean; + error?: ApiAnyDisplayError; +}; + +export type SubmitTransferResult = { normalizedAddress: string; amount: string; seqno: number; @@ -57,9 +73,7 @@ type SubmitTransferResult = { }; type SubmitMultiTransferResult = { - messages: (TonTransferParams & { - resolvedAddress: string; - })[]; + messages: TonTransferParams[]; amount: string; seqno: number; boc: string; @@ -75,6 +89,7 @@ const GET_TRANSACTIONS_LIMIT = 50; const GET_TRANSACTIONS_MAX_LIMIT = 100; const WAIT_SEQNO_TIMEOUT = 40000; // 40 sec. const WAIT_SEQNO_PAUSE = 5000; // 5 sec. +const WAIT_TRANSACTION_PAUSE = 500; // 0.5 sec. const lastTransfers: Record { const { network } = parseAccountId(accountId); - const result: { - fee?: string; - addressName?: string; - isScam?: boolean; - resolvedAddress?: string; - normalizedAddress?: string; - isToAddressNew?: boolean; - } = {}; + const result: CheckTransactionDraftResult = {}; - const resolved = await resolveAddress(network, toAddress); - if (resolved) { - result.addressName = resolved.domain; - toAddress = resolved.address; - } else { - return { ...result, error: ApiTransactionDraftError.DomainNotResolved }; - } + try { + const resolved = await resolveAddress(network, toAddress); + if (resolved) { + result.addressName = resolved.domain; + toAddress = resolved.address; + } else { + return { + ...result, + error: ApiTransactionDraftError.DomainNotResolved, + }; + } - if (!Address.isValid(toAddress)) { - return { ...result, error: ApiTransactionDraftError.InvalidToAddress }; - } + if (!Address.isValid(toAddress)) { + return { + ...result, + error: ApiTransactionDraftError.InvalidToAddress, + }; + } - const { - isUserFriendly, isTestOnly, isBounceable, - } = new Address(toAddress); + const { + isUserFriendly, + isTestOnly, + isBounceable, + } = new Address(toAddress); - const regex = /[+=/]/; // Temp check for `isUrlSafe`. Remove after TonWeb fixes the issue - const isUrlSafe = !regex.test(toAddress); + const regex = /[+=/]/; // Temp check for `isUrlSafe`. Remove after TonWeb fixes the issue + const isUrlSafe = !regex.test(toAddress); - if (!isUserFriendly || !isUrlSafe || (network === 'mainnet' && isTestOnly)) { - return { ...result, error: ApiTransactionDraftError.InvalidAddressFormat }; - } + if (!isUserFriendly || !isUrlSafe || (network === 'mainnet' && isTestOnly)) { + return { + ...result, + error: ApiTransactionDraftError.InvalidAddressFormat, + }; + } - if (isBounceable) { - const isInitialized = await isWalletInitialized(network, toAddress); - if (!isInitialized) { - result.isToAddressNew = !(await checkHasTransaction(network, toAddress)); - if (tokenSlug === TON_TOKEN_SLUG) { - toAddress = toBase64Address(toAddress, false); + const isInitialized = await isAddressInitialized(network, toAddress); + + if (isBounceable) { + if (!isInitialized) { + result.isToAddressNew = !(await checkHasTransaction(network, toAddress)); + if (tokenSlug === TON_TOKEN_SLUG) { + // Force non-bounceable for non-initialized recipients + toAddress = toBase64Address(toAddress, false); + } } + } else if (isInitialized) { + toAddress = toBase64Address(toAddress, true); } - } - result.resolvedAddress = toAddress; - result.normalizedAddress = toBase64Address(toAddress); + result.resolvedAddress = toAddress; - const addressInfo = await getAddressInfo(toAddress); - if (addressInfo?.name) result.addressName = addressInfo.name; - if (addressInfo?.isScam) result.isScam = addressInfo.isScam; + const addressInfo = await getAddressInfo(toAddress); + if (addressInfo?.name) result.addressName = addressInfo.name; + if (addressInfo?.isScam) result.isScam = addressInfo.isScam; - if (BigInt(amount) < BigInt(0)) { - return { ...result, error: ApiTransactionDraftError.InvalidAmount }; - } + if (BigInt(amount) < BigInt(0)) { + return { + ...result, + error: ApiTransactionDraftError.InvalidAmount, + }; + } - const wallet = await pickAccountWallet(accountId); - if (!wallet) { - return { ...result, error: ApiTransactionDraftError.Unexpected }; - } + const wallet = await pickAccountWallet(accountId); + if (!wallet) { + return { + ...result, + error: ApiCommonError.Unexpected, + }; + } - if (typeof data === 'string' && isBase64Data) { - data = parseBase64(data); - } + if (typeof data === 'string' && isBase64Data) { + data = parseBase64(data); + } - if (data && typeof data === 'string' && shouldEncrypt) { - const toPublicKey = await getWalletPublicKey(network, toAddress); - if (!toPublicKey) { - return { ...result, error: ApiTransactionDraftError.WalletNotInitialized }; + if (data && typeof data === 'string' && shouldEncrypt) { + const toPublicKey = await getWalletPublicKey(network, toAddress); + if (!toPublicKey) { + return { + ...result, + error: ApiTransactionDraftError.WalletNotInitialized, + }; + } } - } - const account = await fetchStoredAccount(accountId); - const isLedger = !!account?.ledger; + const account = await fetchStoredAccount(accountId); + const isLedger = !!account.ledger; - if (data && typeof data === 'string' && !isBase64Data && !isLedger) { - data = commentToBytes(data); - } - - if (tokenSlug === TON_TOKEN_SLUG) { - if (data && isLedger && (typeof data !== 'string' || shouldEncrypt || !isValidLedgerComment(data))) { - return { ...result, error: ApiTransactionDraftError.UnsupportedHardwarePayload }; + if (data && typeof data === 'string' && !isBase64Data && !isLedger) { + data = commentToBytes(data); } - if (data instanceof Uint8Array) { - data = packBytesAsSnake(data); - } - } else { - const address = await fetchStoredAddress(accountId); - const tokenAmount: string = amount; - let tokenWallet: JettonWalletType; - ({ - tokenWallet, - amount, - toAddress, - payload: data, - } = await buildTokenTransfer(network, tokenSlug, address, toAddress, amount, data)); - - const tokenBalance = await getTokenWalletBalance(tokenWallet!); - if (BigInt(tokenBalance) < BigInt(tokenAmount!)) { - return { ...result, error: ApiTransactionDraftError.InsufficientBalance }; + if (tokenSlug === TON_TOKEN_SLUG) { + if (data && isLedger && (typeof data !== 'string' || shouldEncrypt || !isValidLedgerComment(data))) { + return { + ...result, + error: ApiTransactionDraftError.UnsupportedHardwarePayload, + }; + } + + if (data instanceof Uint8Array) { + data = packBytesAsSnake(data); + } + } else { + const address = await fetchStoredAddress(accountId); + const tokenAmount: string = amount; + let tokenWallet: JettonWalletType; + ({ + tokenWallet, + amount, + toAddress, + payload: data, + } = await buildTokenTransfer(network, tokenSlug, address, toAddress, amount, data)); + + const tokenBalance = await getTokenWalletBalance(tokenWallet!); + if (BigInt(tokenBalance) < BigInt(tokenAmount!)) { + return { + ...result, + error: ApiTransactionDraftError.InsufficientBalance, + }; + } } - } - const isOurWalletInitialized = await isWalletInitialized(network, wallet); - result.fee = await calculateFee(isOurWalletInitialized, async () => (await signTransaction( - network, wallet, toAddress, amount, data, stateInit, - )).query); + const isOurWalletInitialized = await isAddressInitialized(network, wallet); + result.fee = await calculateFee(isOurWalletInitialized, async () => (await signTransaction( + network, wallet, toAddress, amount, data, stateInit, + )).query); - const balance = await getWalletBalance(network, wallet); - if (BigInt(balance) < BigInt(amount) + BigInt(result.fee)) { - return { ...result, error: ApiTransactionDraftError.InsufficientBalance }; - } + const balance = await getWalletBalance(network, wallet); + if (BigInt(balance) < BigInt(amount) + BigInt(result.fee)) { + return { + ...result, + error: ApiTransactionDraftError.InsufficientBalance, + }; + } - return result as { - fee: string; - resolvedAddress: string; - normalizedAddress: string; - addressName?: string; - isScam?: boolean; - isToAddressNew?: boolean; - }; + return result as { + fee: string; + resolvedAddress: string; + addressName?: string; + isScam?: boolean; + isToAddressNew?: boolean; + }; + } catch (err: any) { + return { + ...handleServerError(err), + ...result, + }; + } } export async function submitTransfer( @@ -244,7 +290,7 @@ export async function submitTransfer( const { publicKey, secretKey } = keyPair!; let encryptedComment: string | undefined; - // Force default bounceable address for `waitTxComplete` to work properly + // Fix address format for `waitTxComplete` to work properly const normalizedAddress = toBase64Address(toAddress); if (data && typeof data === 'string') { @@ -296,10 +342,13 @@ export async function submitTransfer( } } -function resolveTransactionError(error: any): ApiTransactionError { - const message = typeof error === 'string' ? error : error?.message; - if (message?.includes('exitcode=35,')) { - return ApiTransactionError.IncorrectDeviceTime; +export function resolveTransactionError(error: any): ApiAnyDisplayError { + if (error instanceof ApiServerError) { + if (error.message.includes('exitcode=35,')) { + return ApiTransactionError.IncorrectDeviceTime; + } else if (error.displayError) { + return error.displayError; + } } return ApiTransactionError.UnsuccesfulTransfer; } @@ -337,15 +386,15 @@ export async function getAccountNewestTxId(accountId: string) { export async function getAccountTransactionSlice( accountId: string, - fromTxId?: string, toTxId?: string, + fromTxId?: string, limit?: number, ) { const { network } = parseAccountId(accountId); const address = await fetchStoredAddress(accountId); let transactions = await fetchTransactions( - network, address, limit ?? GET_TRANSACTIONS_LIMIT, fromTxId, toTxId, + network, address, limit ?? GET_TRANSACTIONS_LIMIT, toTxId, fromTxId, ); transactions = await Promise.all( @@ -388,12 +437,12 @@ export async function getMergedTransactionSlice(accountId: string, lastTxIds: Ap export async function getTokenTransactionSlice( accountId: string, tokenSlug: string, - fromTxId?: string, toTxId?: string, + fromTxId?: string, limit?: number, ): Promise { if (tokenSlug === TON_TOKEN_SLUG) { - return getAccountTransactionSlice(accountId, fromTxId, toTxId, limit); + return getAccountTransactionSlice(accountId, toTxId, fromTxId, limit); } const { network } = parseAccountId(accountId); @@ -403,7 +452,7 @@ export async function getTokenTransactionSlice( const tokenWalletAddress = await resolveTokenWalletAddress(network, address, minterAddress); const transactions = await fetchTransactions( - network, tokenWalletAddress, limit ?? GET_TRANSACTIONS_LIMIT, fromTxId, toTxId, + network, tokenWalletAddress, limit ?? GET_TRANSACTIONS_LIMIT, toTxId, fromTxId, ); return transactions @@ -420,24 +469,33 @@ function omitExtraData(tx: ApiTransactionExtra): ApiTransaction { function updateTransactionType(transaction: ApiTransactionExtra) { const { - fromAddress, toAddress, comment, amount, + fromAddress, toAddress, comment, amount, extraData, } = transaction; - if (fromAddress && toAddress) { - const amountNumber = Math.abs(Number(fromNano(amount))); + const amountNumber = Math.abs(Number(fromNano(amount))); + let type: ApiTransactionType | undefined; - if (isKnownStakingPool(fromAddress) && amountNumber > 1) { - transaction.type = 'unstake'; - } else if (isKnownStakingPool(toAddress)) { - if (comment === STAKE_COMMENT) { - transaction.type = 'stake'; - } else if (comment === UNSTAKE_COMMENT) { - transaction.type = 'unstakeRequest'; - } + if (isKnownStakingPool(fromAddress) && amountNumber > 1) { + type = 'unstake'; + } else if (isKnownStakingPool(toBase64Address(toAddress, true))) { + if (comment === STAKE_COMMENT) { + type = 'stake'; + } else if (comment === UNSTAKE_COMMENT) { + type = 'unstakeRequest'; + } + } else if (extraData?.parsedPayload) { + const payload = extraData.parsedPayload; + + if (payload.type === 'tokens:burn' && payload.isLiquidUnstakeRequest) { + type = 'unstakeRequest'; + } else if (payload.type === 'liquid-staking:deposit') { + type = 'stake'; + } else if (payload.type === 'liquid-staking:withdrawal' || payload.type === 'liquid-staking:withdrawal-nft') { + type = 'unstake'; } } - return transaction; + return { ...transaction, type }; } function transactionToActivity(transaction: ApiTransaction): ApiTransactionActivity { @@ -458,34 +516,38 @@ export async function checkMultiTransactionDraft(accountId: string, messages: To let totalAmount: bigint = 0n; - for (const { toAddress, amount } of messages) { - if (BigInt(amount) < BigInt(0)) { - return { ...result, error: ApiTransactionDraftError.InvalidAmount }; - } - if (!Address.isValid(toAddress)) { - return { ...result, error: ApiTransactionDraftError.InvalidToAddress }; + try { + for (const { toAddress, amount } of messages) { + if (BigInt(amount) < BigInt(0)) { + return { ...result, error: ApiTransactionDraftError.InvalidAmount }; + } + if (!Address.isValid(toAddress)) { + return { ...result, error: ApiTransactionDraftError.InvalidToAddress }; + } + totalAmount += BigInt(amount); } - totalAmount += BigInt(amount); - } - const wallet = await pickAccountWallet(accountId); + const wallet = await pickAccountWallet(accountId); - if (!wallet) { - return { ...result, error: ApiTransactionDraftError.Unexpected }; - } + if (!wallet) { + return { ...result, error: ApiCommonError.Unexpected }; + } - const { isInitialized, balance } = await getWalletInfo(network, wallet); + const { isInitialized, balance } = await getWalletInfo(network, wallet); - result.fee = await calculateFee(isInitialized, async () => (await signMultiTransaction( - network, wallet, messages, - )).query); - result.totalAmount = totalAmount.toString(); + result.fee = await calculateFee(isInitialized, async () => (await signMultiTransaction( + network, wallet, messages, + )).query); + result.totalAmount = totalAmount.toString(); - if (BigInt(balance) < totalAmount + BigInt(result.fee)) { - return { ...result, error: ApiTransactionDraftError.InsufficientBalance }; - } + if (BigInt(balance) < totalAmount + BigInt(result.fee)) { + return { ...result, error: ApiTransactionDraftError.InsufficientBalance }; + } - return result as { fee: string; totalAmount: string }; + return result as { fee: string; totalAmount: string }; + } catch (err: any) { + return handleServerError(err); + } } export async function submitMultiTransfer( @@ -504,29 +566,15 @@ export async function submitMultiTransfer( ]); let totalAmount = 0n; - const preparedMessages = await Promise.all(messages.map(async (message) => { - let { toAddress } = message; - - // Force default bounceable address for `waitTxComplete` to work properly - const resolvedAddress = toBase64Address(toAddress); - toAddress = await isWalletInitialized(network, toAddress) - ? resolvedAddress - : toBase64Address(toAddress, false); - + messages.forEach((message) => { totalAmount += BigInt(message.amount); - - return { - ...message, - toAddress, - resolvedAddress, - }; - })); + }); await waitLastTransfer(network, fromAddress); const { isInitialized, balance } = await getWalletInfo(network, wallet!); - const { seqno, query } = await signMultiTransaction(network, wallet!, preparedMessages, privateKey, expireAt); + const { seqno, query } = await signMultiTransaction(network, wallet!, messages, privateKey, expireAt); const boc = bytesToBase64(await (await query.getQuery()).toBoc()); @@ -542,7 +590,7 @@ export async function submitMultiTransfer( return { seqno, amount: totalAmount.toString(), - messages: preparedMessages, + messages, boc, }; } catch (err) { @@ -638,12 +686,8 @@ async function calculateFee(isInitialized: boolean, query: Method | (() => Promi export async function sendSignedMessage(accountId: string, message: ApiSignedTransfer) { const { network } = parseAccountId(accountId); - const [fromAddress, publicKey, account] = await Promise.all([ - fetchStoredAddress(accountId), - fetchStoredPublicKey(accountId), - fetchStoredAccount(accountId), - ]); - const wallet = getTonWalletContract(publicKey!, account!.version!); + const { address: fromAddress, publicKey, version } = await fetchStoredAccount(accountId); + const wallet = getTonWalletContract(publicKey, version!); const client = getTonClient(network); const contract = client.open(wallet); @@ -655,12 +699,8 @@ export async function sendSignedMessage(accountId: string, message: ApiSignedTra export async function sendSignedMessages(accountId: string, messages: ApiSignedTransfer[]) { const { network } = parseAccountId(accountId); - const [fromAddress, publicKey, account] = await Promise.all([ - fetchStoredAddress(accountId), - fetchStoredPublicKey(accountId), - fetchStoredAccount(accountId), - ]); - const wallet = getTonWalletContract(publicKey!, account!.version!); + const { address: fromAddress, publicKey, version } = await fetchStoredAccount(accountId); + const wallet = getTonWalletContract(publicKey, version!); const client = getTonClient(network); const contract = client.open(wallet); @@ -701,3 +741,20 @@ export async function decryptComment( return decryptMessageComment(buffer, publicKey, secretKey, fromAddress); } + +export async function waitUntilTransactionAppears(network: ApiNetwork, address: string, txId: string) { + const { lt } = parseTxId(txId); + + if (lt === 0) { + return; + } + + // eslint-disable-next-line no-constant-condition + while (true) { + const transaction = (await fetchTransactions(network, address, 1))[0]; + if (parseTxId(transaction.txId).lt >= lt) { + return; + } + await pause(WAIT_TRANSACTION_PAUSE); + } +} diff --git a/src/api/blockchains/ton/types.ts b/src/api/blockchains/ton/types.ts index ef9a370d..e9858134 100644 --- a/src/api/blockchains/ton/types.ts +++ b/src/api/blockchains/ton/types.ts @@ -4,7 +4,7 @@ import type { Cell } from 'tonweb/dist/types/boc/cell'; import type { HttpProvider } from 'tonweb/dist/types/providers/http-provider'; import type { Address as AddressType } from 'tonweb/dist/types/utils/address'; -import type { ApiTransaction } from '../../types'; +import type { ApiParsedPayload, ApiTransaction } from '../../types'; declare class Dns { readonly provider: HttpProvider; @@ -20,7 +20,11 @@ export declare class MyTonWeb extends TonWeb { export type AnyPayload = string | Uint8Array | Cell; export interface ApiTransactionExtra extends ApiTransaction { - extraData?: { body?: any }; + extraData: { + normalizedAddress: string; + body?: string; + parsedPayload?: ApiParsedPayload; + }; } export interface TokenTransferBodyParams { diff --git a/src/api/blockchains/ton/util/CustomHttpProvider.ts b/src/api/blockchains/ton/util/CustomHttpProvider.ts index fd48def7..8a3d77e4 100644 --- a/src/api/blockchains/ton/util/CustomHttpProvider.ts +++ b/src/api/blockchains/ton/util/CustomHttpProvider.ts @@ -1,8 +1,8 @@ import TonWeb from 'tonweb'; import type { HttpProviderOptions } from 'tonweb/dist/types/providers/http-provider'; -import { logDebugError } from '../../../../util/logs'; import { pause } from '../../../../util/schedulers'; +import { ApiServerError } from '../../../errors'; type Options = HttpProviderOptions & { headers?: AnyLiteral; @@ -27,8 +27,6 @@ class CustomHttpProvider extends TonWeb.HttpProvider { async sendRequest(apiUrl: string, request: any) { const method: string = request.method; - let lastError: any; - let lastStatusCode: number | undefined; const headers: AnyLiteral = { ...this.options.headers, @@ -39,35 +37,40 @@ class CustomHttpProvider extends TonWeb.HttpProvider { } const body = JSON.stringify(request); + let message = 'Unknown error'; + let statusCode: number | undefined; + for (let i = 1; i <= ATTEMPTS; i++) { try { const response = await fetch(apiUrl, { method: 'POST', headers, body, }); - lastStatusCode = response.status; - const { error, result } = await response.json(); - if (error) { + statusCode = response.status; + + if (statusCode >= 400) { + if (response.headers.get('content-type') !== 'application/json') { + throw new Error(`HTTP Error ${statusCode}`); + } + const { error } = await response.json(); throw new Error(error); } + + const { result } = await response.json(); return result; } catch (err: any) { - lastError = err; + message = typeof err === 'string' ? err : err.message ?? message; - const message: string = typeof err === 'string' ? err : err.message; - if (isNotTemporaryError(method, message, lastStatusCode)) { - throw err; + if (isNotTemporaryError(method, message, statusCode)) { + throw new ApiServerError(message); } if (i < ATTEMPTS) { - if (!isFrequentError(message)) { - logDebugError('HttpProvider:sendRequest', 'retry', err); - } await pause(ERROR_PAUSE * i); } } } - throw lastError; + throw new ApiServerError(message); } } @@ -75,8 +78,4 @@ function isNotTemporaryError(method: string, message: string, statusCode?: numbe return statusCode === 422 || (method === 'sendBoc' && message?.includes('exitcode=')); } -function isFrequentError(message: string) { - return message.includes('LITE_SERVER_NOTREADY') || message.includes('LITE_SERVER_UNKNOWN'); -} - export default CustomHttpProvider; diff --git a/src/api/blockchains/ton/util/index.ts b/src/api/blockchains/ton/util/index.ts index aeb71570..e787520c 100644 --- a/src/api/blockchains/ton/util/index.ts +++ b/src/api/blockchains/ton/util/index.ts @@ -2,7 +2,7 @@ export function cloneDeep(value: T): T { return JSON.parse(JSON.stringify(value)); } -export function stringifyTxId({ lt, hash }: { lt: number; hash: string }) { +export function stringifyTxId({ lt, hash }: { lt: number | string; hash: string }) { return `${lt}:${hash}`; } @@ -15,7 +15,3 @@ export function buildTokenSlug(minterAddress: string) { const addressPart = minterAddress.replace(/[^a-z\d]/gi, '').slice(0, 10); return `ton-${addressPart}`.toLowerCase(); } - -export function buildSwapId(backendId: string) { - return `swap:${backendId}`; -} diff --git a/src/api/blockchains/ton/util/metadata.ts b/src/api/blockchains/ton/util/metadata.ts index e9c74746..fcdffe00 100644 --- a/src/api/blockchains/ton/util/metadata.ts +++ b/src/api/blockchains/ton/util/metadata.ts @@ -6,16 +6,18 @@ import { import type { ApiNetwork, ApiParsedPayload } from '../../../types'; import type { ApiTransactionExtra, JettonMetadata } from '../types'; +import { LIQUID_JETTON } from '../../../../config'; import { pick, range } from '../../../../util/iteratees'; import { logDebugError } from '../../../../util/logs'; -import { base64ToString, handleFetchErrors, sha256 } from '../../../common/utils'; -import { JettonOpCode, NftOpCode, OpCode } from '../constants'; +import { fetchJsonMetadata } from '../../../../util/metadata'; +import { base64ToString, sha256 } from '../../../common/utils'; +import { + DEFAULT_IS_BOUNCEABLE, JettonOpCode, LiquidStakingOpCode, NftOpCode, OpCode, +} from '../constants'; import { buildTokenSlug } from './index'; import { fetchNftItems } from './tonapiio'; import { getJettonMinterData, resolveTokenMinterAddress } from './tonweb'; -const IPFS_GATEWAY_BASE_URL: string = 'https://ipfs.io/ipfs/'; - const ONCHAIN_CONTENT_PREFIX = 0x00; const SNAKE_PREFIX = 0x00; @@ -64,7 +66,7 @@ export function parseJettonWalletMsgBody(body?: string) { queryId, jettonAmount, responseAddress, - address: address ? toBounceableAddress(address) : undefined, + address: address ? toBase64Address(address) : undefined, forwardAmount, comment, encryptedComment, @@ -76,14 +78,6 @@ export function parseJettonWalletMsgBody(body?: string) { return undefined; } -export function toBounceableAddress(address: Address) { - return address.toString({ urlSafe: true, bounceable: true }); -} - -export function fixIpfsUrl(url: string) { - return url.replace('ipfs://', IPFS_GATEWAY_BASE_URL); -} - export function fixBase64ImageData(data: string) { const decodedData = base64ToString(data); if (decodedData.includes(' { +export async function fetchJettonOffchainMetadata(uri: string): Promise { const metadata = await fetchJsonMetadata(uri); return pick(metadata, ['name', 'description', 'symbol', 'decimals', 'image', 'image_data']); } -async function fetchJsonMetadata(uri: string) { - uri = fixIpfsUrl(uri); - - const response = await fetch(uri); - handleFetchErrors(response); - return response.json(); -} - export async function parseWalletTransactionBody( network: ApiNetwork, transaction: ApiTransactionExtra, ): Promise { const body = transaction.extraData?.body; - if (!body) return transaction; + if (!body || transaction.comment || transaction.encryptedComment) { + return transaction; + } try { const slice = dataToSlice(body); if (slice.remainingBits > 32) { const parsedPayload = await parsePayloadSlice(network, transaction.toAddress, slice); + transaction.extraData!.parsedPayload = parsedPayload; - if (parsedPayload?.type === 'encrypted-comment') { + if (parsedPayload?.type === 'comment') { + transaction = { + ...transaction, + comment: parsedPayload.comment, + }; + } else if (parsedPayload?.type === 'encrypted-comment') { transaction = { ...transaction, encryptedComment: parsedPayload.encryptedComment, @@ -229,10 +223,12 @@ export async function parsePayloadSlice( const opCode = slice.loadUint(32); if (opCode === OpCode.Comment) { - const comment = slice.loadStringTail(); + const buffer = readSnakeBytes(slice); + const comment = buffer.toString('utf-8'); return { type: 'comment', comment }; } else if (opCode === OpCode.Encrypted) { - const encryptedComment = slice.loadBuffer(slice.remainingBits / 8).toString('base64'); + const buffer = readSnakeBytes(slice); + const encryptedComment = buffer.toString('base64'); return { type: 'encrypted-comment', encryptedComment }; } @@ -249,9 +245,9 @@ export async function parsePayloadSlice( if (!responseDestination) { return { - type: 'transfer-tokens:non-standard', + type: 'tokens:transfer-non-standard', queryId, - destination: toBounceableAddress(destination), + destination: toBase64Address(destination), amount: amount.toString(), slug, }; @@ -269,11 +265,11 @@ export async function parsePayloadSlice( } return { - type: 'transfer-tokens', + type: 'tokens:transfer', queryId, amount: amount.toString(), - destination: toBounceableAddress(destination), - responseDestination: toBounceableAddress(responseDestination), + destination: toBase64Address(destination), + responseDestination: toBase64Address(responseDestination), customPayload: customPayload?.toBoc().toString('base64'), forwardAmount: forwardAmount.toString(), forwardPayload: forwardPayload?.toBoc().toString('base64'), @@ -298,10 +294,10 @@ export async function parsePayloadSlice( const nftAddress = toAddress; const [nft] = await fetchNftItems(network, [nftAddress]); return { - type: 'transfer-nft', + type: 'nft:transfer', queryId, - newOwner: toBounceableAddress(newOwner), - responseDestination: toBounceableAddress(responseDestination), + newOwner: toBase64Address(newOwner), + responseDestination: toBase64Address(responseDestination), customPayload: customPayload?.toBoc().toString('base64'), forwardAmount: forwardAmount.toString(), forwardPayload: forwardPayload?.toBoc().toString('base64'), @@ -309,6 +305,44 @@ export async function parsePayloadSlice( nftName: nft?.metadata?.name, }; } + case JettonOpCode.Burn: { + const minterAddress = await resolveTokenMinterAddress(network, toAddress); + const slug = buildTokenSlug(minterAddress); + + const amount = slice.loadCoins(); + const address = slice.loadAddress(); + const customPayload = slice.loadMaybeRef(); + const isLiquidUnstakeRequest = minterAddress === LIQUID_JETTON; + + return { + type: 'tokens:burn', + queryId, + amount: amount.toString(), + address: toBase64Address(address), + customPayload: customPayload?.toBoc().toString('base64'), + slug, + isLiquidUnstakeRequest, + }; + } + case LiquidStakingOpCode.DistributedAsset: { + return { + type: 'liquid-staking:withdrawal-nft', + queryId, + }; + } + case LiquidStakingOpCode.Withdrawal: { + return { + type: 'liquid-staking:withdrawal', + queryId, + }; + } + case LiquidStakingOpCode.Deposit: { + // const amount = slice.loadCoins(); + return { + type: 'liquid-staking:deposit', + queryId, + }; + } } } catch (err) { logDebugError('parsePayload', err); @@ -317,6 +351,10 @@ export async function parsePayloadSlice( return undefined; } +function toBase64Address(address: Address, isBounceable = DEFAULT_IS_BOUNCEABLE) { + return address.toString({ urlSafe: true, bounceable: isBounceable }); +} + function dataToSlice(data: string | Buffer | Uint8Array): Slice { let buffer: Buffer; if (typeof data === 'string') { diff --git a/src/api/blockchains/ton/util/tonapiio.ts b/src/api/blockchains/ton/util/tonapiio.ts index a032a8b0..0bf219d7 100644 --- a/src/api/blockchains/ton/util/tonapiio.ts +++ b/src/api/blockchains/ton/util/tonapiio.ts @@ -8,11 +8,10 @@ import { import type { ApiNetwork } from '../../../types'; +import { TONAPIIO_MAINNET_URL, TONAPIIO_TESTNET_URL } from '../../../../config'; import { logDebugError } from '../../../../util/logs'; import { API_HEADERS } from '../../../environment'; -const TONAPIIO_MAINNET_URL = process.env.TONAPIIO_MAINNET_URL || 'https://tonapi.io'; -const TONAPIIO_TESTNET_URL = process.env.TONAPIIO_TESTNET_URL || 'https://testnet.tonapi.io'; const MAX_LIMIT = 1000; const configurationMainnet = new Configuration({ @@ -53,13 +52,20 @@ export function fetchNftItems(network: ApiNetwork, addresses: string[]) { })).nftItems, []); } -export function fetchAccountNfts(network: ApiNetwork, address: string, offset?: number, limit?: number) { +export function fetchAccountNfts(network: ApiNetwork, address: string, options?: { + collection?: string; + offset?: number; + limit?: number; +}) { + const { collection, offset, limit } = options ?? {}; const api = tonapiioByNetwork[network].accountsApi; + return tonapiioErrorHandler(async () => (await api.getNftItemsByOwner({ accountId: address, offset: offset ?? 0, limit: limit ?? MAX_LIMIT, indirectOwnership: true, + collection, })).nftItems, []); } diff --git a/src/api/blockchains/ton/util/tonweb.ts b/src/api/blockchains/ton/util/tonweb.ts index cb126755..0f014a9c 100644 --- a/src/api/blockchains/ton/util/tonweb.ts +++ b/src/api/blockchains/ton/util/tonweb.ts @@ -13,12 +13,19 @@ import { TONHTTPAPI_MAINNET_URL, TONHTTPAPI_TESTNET_API_KEY, TONHTTPAPI_TESTNET_URL, + TONINDEXER_MAINNET_URL, + TONINDEXER_TESTNET_URL, } from '../../../../config'; import { logDebugError } from '../../../../util/logs'; import withCacheAsync from '../../../../util/withCacheAsync'; -import { base64ToBytes, hexToBytes } from '../../../common/utils'; +import { base64ToBytes, fetchJson, hexToBytes } from '../../../common/utils'; import { API_HEADERS } from '../../../environment'; -import { JettonOpCode, OpCode } from '../constants'; +import { + DEFAULT_IS_BOUNCEABLE, + JettonOpCode, + LiquidStakingOpCode, + OpCode, +} from '../constants'; import { parseTxId, stringifyTxId } from './index'; import CustomHttpProvider from './CustomHttpProvider'; @@ -26,6 +33,7 @@ import CustomHttpProvider from './CustomHttpProvider'; const { Cell } = TonWeb.boc; const { Address } = TonWeb.utils; const { JettonMinter, JettonWallet } = TonWeb.token.jetton; +export const { toNano, fromNano } = TonWeb.utils; const TON_MAX_COMMENT_BYTES = 127; @@ -43,13 +51,13 @@ const tonwebByNetwork = { export const resolveTokenWalletAddress = withCacheAsync( async (network: ApiNetwork, address: string, minterAddress: string) => { const minter = new JettonMinter(getTonWeb(network).provider, { address: minterAddress } as any); - return toBase64Address(await minter.getJettonWalletAddress(new Address(address))); + return toBase64Address(await minter.getJettonWalletAddress(new Address(address)), true); }, ); export const resolveTokenMinterAddress = withCacheAsync(async (network: ApiNetwork, tokenWalletAddress: string) => { const tokenWallet = new JettonWallet(getTonWeb(network).provider, { address: tokenWalletAddress } as any); - return toBase64Address((await tokenWallet.getData()).jettonMinterAddress); + return toBase64Address((await tokenWallet.getData()).jettonMinterAddress, true); }); export const getWalletPublicKey = withCacheAsync(async (network: ApiNetwork, address: string) => { @@ -74,48 +82,58 @@ export async function getJettonMinterData(network: ApiNetwork, address: string) return { ...data, totalSupply: data.totalSupply.toString(), - adminAddress: data.adminAddress ? toBase64Address(data.adminAddress) : undefined, + adminAddress: data.adminAddress ? toBase64Address(data.adminAddress, false) : undefined, }; } export async function fetchNewestTxId(network: ApiNetwork, address: string) { - const tonWeb = getTonWeb(network); - - const result: any[] = await tonWeb.provider.getTransactions( - address, - 1, - undefined, - undefined, - undefined, - true, - ); - if (!result?.length) { + const transactions = await fetchTransactions(network, address, 1); + + if (!transactions.length) { return undefined; } - return stringifyTxId(result[0].transaction_id); + return transactions[0].txId; } export async function fetchTransactions( - network: ApiNetwork, address: string, limit: number, fromTxId?: string, toTxId?: string, + network: ApiNetwork, + address: string, + limit: number, + toTxId?: string, + fromTxId?: string, ): Promise { - const tonWeb = getTonWeb(network); - - const fromLt = fromTxId ? parseTxId(fromTxId).lt : undefined; - const fromHash = fromTxId ? parseTxId(fromTxId).hash : undefined; - const toLt = toTxId ? parseTxId(toTxId).lt : undefined; - - let rawTransactions = await tonWeb.provider.getTransactions( - address, limit, fromLt, fromHash, toLt, true, - ) as any[]; - - if ( - fromTxId - && rawTransactions.length - && Number(rawTransactions[0].transaction_id.lt) === fromLt - && rawTransactions[0].transaction_id.hash === fromHash - ) { - rawTransactions = rawTransactions.slice(1); + const indexerUrl = network === 'testnet' ? TONINDEXER_TESTNET_URL : TONINDEXER_MAINNET_URL; + const apiKey = network === 'testnet' ? TONHTTPAPI_TESTNET_API_KEY : TONHTTPAPI_MAINNET_API_KEY; + + const fromLt = fromTxId ? parseTxId(fromTxId).lt.toString() : undefined; + const toLt = toTxId ? parseTxId(toTxId).lt.toString() : undefined; + + let rawTransactions: any[] = await fetchJson(`${indexerUrl}/transactions`, { + account: address, + limit, + start_lt: fromLt, + end_lt: toLt, + sort: 'desc', + }, { + headers: { + ...(apiKey && { 'X-Api-Key': apiKey }), + ...API_HEADERS, + }, + }); + + if (!rawTransactions.length) { + return []; + } + + if (limit > 1) { + if (fromLt && rawTransactions[rawTransactions.length - 1].lt === fromLt) { + rawTransactions.pop(); + } + + if (toLt && rawTransactions[0]?.lt === toLt) { + rawTransactions = rawTransactions.slice(1); + } } return rawTransactions.map(parseRawTransaction).flat(); @@ -123,15 +141,14 @@ export async function fetchTransactions( function parseRawTransaction(rawTx: any): ApiTransactionExtra[] { const { - utime, - transaction_id: { - lt, - hash, - }, + now, + lt, + hash, fee, } = rawTx; + const txId = stringifyTxId({ lt, hash }); - const timestamp = utime as number * 1000; + const timestamp = now as number * 1000; const isIncoming = !!rawTx.in_msg.source; const msgs: any[] = isIncoming ? [rawTx.in_msg] : rawTx.out_msgs; @@ -139,41 +156,27 @@ function parseRawTransaction(rawTx: any): ApiTransactionExtra[] { return msgs.map((msg, i) => { const { source, destination, value } = msg; + const normalizedAddress = toBase64Address(isIncoming ? source : destination, true); return { txId: msgs.length > 1 ? `${txId}:${i + 1}` : txId, timestamp, isIncoming, - fromAddress: source, - toAddress: destination, + fromAddress: toBase64Address(source), + toAddress: toBase64Address(destination), amount: isIncoming ? value : `-${value}`, - comment: getComment(msg), - encryptedComment: getEncryptedComment(msg), slug: TON_TOKEN_SLUG, fee, extraData: { + normalizedAddress, body: getRawBody(msg), }, }; }); } -function getComment(msg: any): string | undefined { - if (!msg.msg_data) return undefined; - if (msg.msg_data['@type'] !== 'msg.dataText') return undefined; - const base64 = msg.msg_data.text; - return new TextDecoder().decode(TonWeb.utils.base64ToBytes(base64)); -} - -function getEncryptedComment(msg: any): string | undefined { - if (!msg.msg_data) return undefined; - if (msg.msg_data['@type'] !== 'msg.dataEncryptedText') return undefined; - return msg.msg_data.text; -} - function getRawBody(msg: any) { - if (!msg.msg_data) return undefined; - if (msg.msg_data['@type'] !== 'msg.dataRaw') return undefined; - return msg.msg_data.body; + if (!msg.message_content) return undefined; + return msg.message_content.body; } export function getTonWeb(network: ApiNetwork = 'mainnet') { @@ -184,7 +187,7 @@ export function oneCellFromBoc(boc: Uint8Array) { return TonWeb.boc.Cell.oneFromBoc(boc); } -export function toBase64Address(address: AddressType, isBounceable = true) { +export function toBase64Address(address: AddressType, isBounceable = DEFAULT_IS_BOUNCEABLE) { return new TonWeb.utils.Address(address).toString(true, true, isBounceable); } @@ -275,3 +278,44 @@ export function packBytesAsSnake(bytes: Uint8Array, maxBytes = TON_MAX_COMMENT_B return cell; } + +export function buildLiquidStakingDepositBody(queryId?: number) { + const cell = new Cell(); + cell.bits.writeUint(LiquidStakingOpCode.Deposit, 32); + cell.bits.writeUint(queryId || 0, 64); + return cell; +} + +export function buildLiquidStakingWithdrawBody(options: { + queryId?: number; + amount: string | BN; + responseAddress: AddressType; + waitTillRoundEnd?: boolean; // opposite of request_immediate_withdrawal + fillOrKill?: boolean; +}) { + const { + queryId, amount, responseAddress, waitTillRoundEnd, fillOrKill, + } = options; + + const customPayload = new Cell(); + customPayload.bits.writeUint(Number(waitTillRoundEnd), 1); + customPayload.bits.writeUint(Number(fillOrKill), 1); + + const cell = new Cell(); + cell.bits.writeUint(JettonOpCode.Burn, 32); + cell.bits.writeUint(queryId ?? 0, 64); + cell.bits.writeCoins(new BN(amount)); + cell.bits.writeAddress(new Address(responseAddress)); + cell.bits.writeBit(1); + cell.refs.push(customPayload); + + return cell; +} + +export async function getTokenBalance(network: ApiNetwork, walletAddress: string) { + const jettonWallet = new JettonWallet(getTonWeb(network).provider, { + address: walletAddress, + }); + const wallletData = await jettonWallet.getData(); + return fromNano(wallletData.balance); +} diff --git a/src/api/blockchains/ton/wallet.ts b/src/api/blockchains/ton/wallet.ts index d99e61a2..a9ca64b3 100644 --- a/src/api/blockchains/ton/wallet.ts +++ b/src/api/blockchains/ton/wallet.ts @@ -5,13 +5,14 @@ import type { ApiNetwork, ApiWalletVersion } from '../../types'; import { parseAccountId } from '../../../util/account'; import { compact } from '../../../util/iteratees'; import withCacheAsync from '../../../util/withCacheAsync'; +import { stringifyTxId } from './util'; import { getTonWeb, toBase64Address } from './util/tonweb'; -import { fetchStoredAccount, fetchStoredAddress, fetchStoredPublicKey } from '../../common/accounts'; +import { fetchStoredAccount, fetchStoredAddress } from '../../common/accounts'; import { bytesToBase64, hexToBytes } from '../../common/utils'; const DEFAULT_WALLET_VERSION: ApiWalletVersion = 'v4R2'; -export const isWalletInitialized = withCacheAsync( +export const isAddressInitialized = withCacheAsync( async (network: ApiNetwork, walletOrAddress: WalletContract | string) => { return (await getWalletInfo(network, walletOrAddress)).isInitialized; }, @@ -19,8 +20,8 @@ export const isWalletInitialized = withCacheAsync( export const isActiveSmartContract = withCacheAsync(async (network: ApiNetwork, address: string) => { const { isInitialized, isWallet } = await getWalletInfo(network, address); - return isInitialized && !isWallet; -}); + return isInitialized ? !isWallet : undefined; +}, (value) => value !== undefined); export async function publicKeyToAddress( network: ApiNetwork, @@ -30,7 +31,7 @@ export async function publicKeyToAddress( const wallet = buildWallet(network, publicKey, walletVersion); const address = await wallet.getAddress(); - return address.toString(true, true, true); + return toBase64Address(address, false); } export function buildWallet(network: ApiNetwork, publicKey: Uint8Array, walletVersion: ApiWalletVersion) { @@ -44,24 +45,31 @@ export async function getWalletInfo(network: ApiNetwork, walletOrAddress: Wallet isWallet: boolean; seqno: number; balance: string; + lastTxId?: string; }> { const address = typeof walletOrAddress === 'string' ? walletOrAddress - : (await walletOrAddress.getAddress()).toString(true, true, true); + : toBase64Address(await walletOrAddress.getAddress()); const { account_state: accountState, wallet: isWallet, seqno, - balance = '0', + balance, + last_transaction_id: { + lt, + hash, + }, } = await getTonWeb(network).provider.getWalletInfo(address); - const isInitialized = accountState === 'active'; return { - isInitialized, + isInitialized: accountState === 'active', isWallet, seqno, - balance, + balance: balance || '0', + lastTxId: lt === '0' + ? undefined + : stringifyTxId({ lt, hash }), }; } @@ -115,13 +123,13 @@ export async function getWalletStateInit(accountId: string) { } export async function pickWalletByAddress(network: ApiNetwork, publicKey: Uint8Array, address: string) { - address = toBase64Address(address); + address = toBase64Address(address, false); const tonWeb = getTonWeb(network); const walletClasses = tonWeb.wallet.list; const allWallets = await Promise.all(walletClasses.map(async (WalletClass) => { const wallet = new WalletClass(tonWeb.provider, { publicKey, wc: 0 }); - const walletAddress = (await wallet.getAddress()).toString(true, true, true); + const walletAddress = toBase64Address(await wallet.getAddress(), false); return { wallet, @@ -136,19 +144,15 @@ export async function pickWalletByAddress(network: ApiNetwork, publicKey: Uint8A export async function pickAccountWallet(accountId: string) { const { network } = parseAccountId(accountId); - const [publicKeyHex, address, account] = await Promise.all([ - fetchStoredPublicKey(accountId), - fetchStoredAddress(accountId), - fetchStoredAccount(accountId), - ]); + const { address, publicKey, version } = await fetchStoredAccount(accountId); - const publicKey = hexToBytes(publicKeyHex); + const publicKeyBytes = hexToBytes(publicKey); - if (account?.version) { - return buildWallet(network, publicKey, account.version); + if (version) { + return buildWallet(network, publicKeyBytes, version); } - return pickWalletByAddress(network, publicKey, address); + return pickWalletByAddress(network, publicKeyBytes, address); } export function resolveWalletVersion(wallet: WalletContract) { diff --git a/src/api/common/accounts.ts b/src/api/common/accounts.ts index e0c2e17e..6237a840 100644 --- a/src/api/common/accounts.ts +++ b/src/api/common/accounts.ts @@ -1,5 +1,5 @@ import type { StorageKey } from '../storages/types'; -import type { ApiAccountInfo, ApiNetwork } from '../types'; +import type { ApiAccount, ApiNetwork } from '../types'; import { buildAccountId, parseAccountId } from '../../util/account'; import { buildCollectionByKey } from '../../util/iteratees'; @@ -14,7 +14,7 @@ const loginPromise = new Promise((resolve) => { }); export async function getAccountIds(): Promise { - return Object.keys(await storage.getItem('addresses') || {}); + return Object.keys(await storage.getItem('accounts') || {}); } export async function getMainAccountId() { @@ -62,16 +62,24 @@ export async function getNewAccountId(network: ApiNetwork) { }); } -export function fetchStoredAccount(accountId: string): Promise { - return getAccountValue(accountId, 'accounts'); +export async function fetchStoredPublicKey(accountId: string): Promise { + return (await fetchStoredAccount(accountId)).publicKey; } -export function fetchStoredPublicKey(accountId: string): Promise { - return getAccountValue(accountId, 'publicKeys'); +export async function fetchStoredAddress(accountId: string): Promise { + return (await fetchStoredAccount(accountId)).address; } -export function fetchStoredAddress(accountId: string): Promise { - return getAccountValue(accountId, 'addresses'); +export function fetchStoredAccount(accountId: string): Promise { + return getAccountValue(accountId, 'accounts'); +} + +export async function updateStoredAccount(accountId: string, partial: Partial): Promise { + const account = await fetchStoredAccount(accountId); + return setAccountValue(accountId, 'accounts', { + ...account, + ...partial, + }); } export async function getAccountValue(accountId: string, key: StorageKey) { diff --git a/src/api/common/backend.ts b/src/api/common/backend.ts index ea194d9f..7a16eb26 100644 --- a/src/api/common/backend.ts +++ b/src/api/common/backend.ts @@ -3,20 +3,29 @@ import { handleFetchErrors } from './utils'; const BAD_REQUEST_CODE = 400; -export async function callBackendPost(path: string, data: AnyLiteral, isAllowBadRequest?: boolean) { +export async function callBackendPost(path: string, data: AnyLiteral, options?: { + authToken?: string; + isAllowBadRequest?: boolean; +}): Promise { + const { authToken, isAllowBadRequest } = options ?? {}; + const response = await fetch(`${BRILLIANT_API_BASE_URL}${path}`, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json', + ...(authToken && { 'X-Auth-Token': authToken }), + }, body: JSON.stringify(data), }); handleFetchErrors(response, isAllowBadRequest ? [BAD_REQUEST_CODE] : undefined); return response.json(); } -export async function callBackendGet(path: string, data?: AnyLiteral, headers?: AnyLiteral) { +export async function callBackendGet(path: string, data?: AnyLiteral, headers?: AnyLiteral): Promise { const url = new URL(`${BRILLIANT_API_BASE_URL}${path}`); if (data) { Object.entries(data).forEach(([key, value]) => { + if (value === undefined) return; url.searchParams.set(key, value.toString()); }); } diff --git a/src/api/common/helpers.ts b/src/api/common/helpers.ts index 1106455b..0aab5b8a 100644 --- a/src/api/common/helpers.ts +++ b/src/api/common/helpers.ts @@ -2,6 +2,7 @@ import type { ApiTransactionExtra } from '../blockchains/ton/types'; import type { StorageKey } from '../storages/types'; import type { AccountIdParsed, + ApiAccount, ApiLocalTransactionParams, ApiTransaction, ApiTransactionActivity, @@ -10,15 +11,15 @@ import type { import { IS_EXTENSION, MAIN_ACCOUNT_ID } from '../../config'; import { buildAccountId, parseAccountId } from '../../util/account'; +import { toBase64Address } from '../blockchains/ton/util/tonweb'; import { storage } from '../storages'; import idbStorage from '../storages/idb'; import { getKnownAddresses, getScamMarkers } from './addresses'; -import { whenTxComplete } from './txCallbacks'; let localCounter = 0; const getNextLocalId = () => `${Date.now()}|${localCounter++}`; -const actualStateVersion = 7; +const actualStateVersion = 8; let migrationEnsurePromise: Promise; export function resolveBlockchainKey(accountId: string) { @@ -34,41 +35,10 @@ export function buildInternalAccountId(account: Omit return `${id}-${blockchain}`; } -export function createLocalTransaction( - onUpdate: OnApiUpdate, - accountId: string, +export function buildLocalTransaction( params: ApiLocalTransactionParams, - onTxComplete?: (transaction: ApiTransactionActivity) => void, -) { - const { amount, toAddress } = params; - - const localTransaction = buildLocalTransaction(params); - - onUpdate({ - type: 'newLocalTransaction', - transaction: localTransaction, - accountId, - }); - - whenTxComplete(toAddress, amount) - .then(({ transaction }) => { - if (onTxComplete) { - onTxComplete(transaction); - } - onUpdate({ - type: 'updateTxComplete', - accountId, - toAddress, - amount, - txId: transaction.txId, - localTxId: localTransaction.txId, - }); - }); - - return localTransaction; -} - -function buildLocalTransaction(params: ApiLocalTransactionParams): ApiTransactionActivity { + normalizedAddress: string, +): ApiTransactionActivity { const { amount, ...restParams } = params; const transaction: ApiTransaction = updateTransactionMetadata({ @@ -77,6 +47,9 @@ function buildLocalTransaction(params: ApiLocalTransactionParams): ApiTransactio isIncoming: false, amount: `-${amount}`, ...restParams, + extraData: { + normalizedAddress, + }, }); return { @@ -87,17 +60,14 @@ function buildLocalTransaction(params: ApiLocalTransactionParams): ApiTransactio } export function updateTransactionMetadata(transaction: ApiTransactionExtra): ApiTransactionExtra { - const { - isIncoming, fromAddress, toAddress, comment, - } = transaction; + const { extraData, comment } = transaction; let { metadata = {} } = transaction; const knownAddresses = getKnownAddresses(); const scamMarkers = getScamMarkers(); - const address = isIncoming ? fromAddress : toAddress; - if (address in knownAddresses) { - metadata = { ...metadata, ...knownAddresses[address] }; + if (extraData.normalizedAddress in knownAddresses) { + metadata = { ...metadata, ...knownAddresses[extraData.normalizedAddress] }; } if (comment && scamMarkers.map((sm) => sm.test(comment)).find(Boolean)) { @@ -121,8 +91,8 @@ export function isUpdaterAlive(onUpdate: OnApiUpdate) { return currentOnUpdate === onUpdate; } -export function startStorageMigration() { - migrationEnsurePromise = migrateStorage(); +export function startStorageMigration(onUpdate: OnApiUpdate) { + migrationEnsurePromise = migrateStorage(onUpdate); return migrationEnsurePromise; } @@ -130,14 +100,14 @@ export function waitStorageMigration() { return migrationEnsurePromise; } -export async function migrateStorage() { +export async function migrateStorage(onUpdate: OnApiUpdate) { let version = Number(await storage.getItem('stateVersion')); if (version === actualStateVersion) { return; } - if (!version && !(await storage.getItem('addresses'))) { + if (!version && !(await storage.getItem('addresses' as StorageKey))) { version = await idbStorage.getItem('stateVersion'); if (IS_EXTENSION && version) { @@ -182,7 +152,7 @@ export async function migrateStorage() { } if (version === 1) { - const addresses = await storage.getItem('addresses') as string | undefined; + const addresses = await storage.getItem('addresses' as StorageKey) as string | undefined; if (addresses && addresses.includes('-undefined')) { for (const field of ['mnemonicsEncrypted', 'addresses', 'publicKeys'] as StorageKey[]) { const newValue = (await storage.getItem(field) as string).replace('-undefined', '-ton'); @@ -243,4 +213,39 @@ export async function migrateStorage() { version = 7; await storage.setItem('stateVersion', version); } + + if (version === 7) { + const addresses = (await storage.getItem('addresses' as StorageKey)) as Record | undefined; + + if (addresses) { + const publicKeys = (await storage.getItem('publicKeys' as StorageKey)) as Record; + const accounts = (await storage.getItem('accounts') ?? {}) as Record; + + for (const [accountId, oldAddress] of Object.entries(addresses)) { + const newAddress = toBase64Address(oldAddress, false); + + accounts[accountId] = { + ...accounts[accountId], + address: newAddress, + publicKey: publicKeys[accountId], + }; + + onUpdate({ + type: 'updateAccount', + accountId, + partial: { + address: newAddress, + }, + }); + } + + await storage.setItem('accounts', accounts); + + await storage.removeItem('addresses' as StorageKey); + await storage.removeItem('publicKeys' as StorageKey); + } + + version = 8; + await storage.setItem('stateVersion', version); + } } diff --git a/src/api/common/utils.ts b/src/api/common/utils.ts index 56f4d301..2b6359a2 100644 --- a/src/api/common/utils.ts +++ b/src/api/common/utils.ts @@ -1,6 +1,7 @@ import TonWeb from 'tonweb'; import { STAKING_POOLS } from '../../config'; +import { ApiServerError } from '../errors'; export function bytesToHex(bytes: Uint8Array) { return TonWeb.utils.bytesToHex(bytes); @@ -44,3 +45,19 @@ export function sumBigString(a: string, b: string) { export function isKnownStakingPool(address: string) { return STAKING_POOLS.some((poolPart) => address.endsWith(poolPart)); } + +export async function fetchJson(url: string, data?: AnyLiteral, init?: RequestInit) { + const urlObject = new URL(url); + if (data) { + Object.entries(data).forEach(([key, value]) => { + if (value === undefined) return; + urlObject.searchParams.set(key, value.toString()); + }); + } + + const response = await fetch(urlObject, init); + if (!response.ok) { + throw new ApiServerError(`Http error ${response.status}`); + } + return response.json(); +} diff --git a/src/api/db.ts b/src/api/db.ts new file mode 100644 index 00000000..1a82634d --- /dev/null +++ b/src/api/db.ts @@ -0,0 +1,24 @@ +import type { ApiNft } from './types'; + +import Dexie from '../lib/dexie/dexie'; +import Table = Dexie.Table; + +export type ApiDbNft = ApiNft & { + accountId: string; + collectionAddress: string; +}; + +const DB_NANE = 'tables'; + +export class ApiDb extends Dexie { + nfts!: Table; + + constructor() { + super(DB_NANE); + this.version(1).stores({ + nfts: '[accountId+address], accountId, address, collectionAddress', + }); + } +} + +export const apiDb = new ApiDb(); diff --git a/src/api/environment.ts b/src/api/environment.ts index 2f39b71a..29427764 100644 --- a/src/api/environment.ts +++ b/src/api/environment.ts @@ -2,13 +2,13 @@ * This module is to be used instead of /src/util/environment.ts * when `window` is not available (e.g. in a web worker). */ -import { APP_ENV, IS_ELECTRON, IS_EXTENSION } from '../config'; +import { APP_ENV, IS_ELECTRON_BUILD, IS_EXTENSION } from '../config'; // eslint-disable-next-line no-restricted-globals export const IS_CHROME_EXTENSION = Boolean(self?.chrome?.system); // eslint-disable-next-line no-restricted-globals export const X_APP_ORIGIN = self.origin; -export const API_HEADERS = IS_EXTENSION || (IS_ELECTRON && APP_ENV !== 'development') +export const API_HEADERS = IS_EXTENSION || (IS_ELECTRON_BUILD && APP_ENV !== 'development') ? { 'X-App-Origin': X_APP_ORIGIN } : undefined; diff --git a/src/api/errors.ts b/src/api/errors.ts index bac8008a..e79b198c 100644 --- a/src/api/errors.ts +++ b/src/api/errors.ts @@ -1,10 +1,10 @@ // eslint-disable-next-line max-classes-per-file import type { ApiAnyDisplayError } from './types'; +import { ApiCommonError } from './types'; export class ApiBaseError extends Error { constructor(message?: string, public displayError?: ApiAnyDisplayError) { super(message); - Error.captureStackTrace(this); this.name = this.constructor.name; } } @@ -14,3 +14,26 @@ export class ApiUserRejectsError extends ApiBaseError { super(message); } } + +export class ApiServerError extends ApiBaseError { + constructor(message: string) { + super(message, ApiCommonError.ServerError); + } +} + +export function maybeApiErrors(fn: AnyAsyncFunction) { + return async (...args: any) => { + try { + return await fn(...args); + } catch (err) { + return handleServerError(err); + } + }; +} + +export function handleServerError(err: any) { + if (err instanceof ApiServerError) { + return { error: err.displayError! }; + } + throw err; +} diff --git a/src/api/extensionMethods/init.ts b/src/api/extensionMethods/init.ts index fb8528ef..2051142b 100644 --- a/src/api/extensionMethods/init.ts +++ b/src/api/extensionMethods/init.ts @@ -9,7 +9,7 @@ import * as extensionMethods from '.'; addHooks({ onWindowNeeded: openPopupWindow, onFullLogout: extensionMethods.onFullLogout, - onDappDisconnected: () => { + onDappDisconnected: (_, origin) => { siteMethods.updateSites({ type: 'disconnectSite', origin, diff --git a/src/api/extensionMethods/legacy.ts b/src/api/extensionMethods/legacy.ts index 4718a49d..ba38074d 100644 --- a/src/api/extensionMethods/legacy.ts +++ b/src/api/extensionMethods/legacy.ts @@ -6,12 +6,15 @@ import { parseAccountId } from '../../util/account'; import { logDebugError } from '../../util/logs'; import blockchains from '../blockchains'; import { - fetchStoredAccount, fetchStoredAddress, fetchStoredPublicKey, getCurrentAccountId, getCurrentAccountIdOrFail, + fetchStoredAccount, + fetchStoredAddress, + getCurrentAccountId, + getCurrentAccountIdOrFail, waitLogin, } from '../common/accounts'; import { createDappPromise } from '../common/dappPromises'; -import { createLocalTransaction } from '../common/helpers'; import { base64ToBytes, hexToBytes } from '../common/utils'; +import { createLocalTransaction } from '../methods'; import { openPopupWindow } from './window'; const ton = blockchains.ton; @@ -52,9 +55,8 @@ export async function requestWallets() { return []; } - const [address, publicKey, wallet] = await Promise.all([ - fetchStoredAddress(accountId), - fetchStoredPublicKey(accountId), + const [{ address, publicKey }, wallet] = await Promise.all([ + fetchStoredAccount(accountId), ton.pickAccountWallet(accountId), ]); @@ -116,7 +118,7 @@ export async function sendTransaction(params: { const { promiseId, promise } = createDappPromise(); const account = await fetchStoredAccount(accountId); - if (account?.ledger) { + if (account.ledger) { return sendLedgerTransaction(accountId, promiseId, promise, checkResult.fee!, params); } @@ -142,7 +144,7 @@ export async function sendTransaction(params: { } const fromAddress = await fetchStoredAddress(accountId); - createLocalTransaction(onPopupUpdate, accountId, { + createLocalTransaction(accountId, { amount, fromAddress, toAddress, @@ -221,7 +223,7 @@ async function sendLedgerTransaction( return false; } - createLocalTransaction(onPopupUpdate, accountId, { + createLocalTransaction(accountId, { amount, fromAddress, toAddress, diff --git a/src/api/hooks.ts b/src/api/hooks.ts index d5161a8f..701fe3f8 100644 --- a/src/api/hooks.ts +++ b/src/api/hooks.ts @@ -6,6 +6,7 @@ interface Hooks { onWindowNeeded: AnyFunction; onDappDisconnected: (accountId: string, origin: string) => any; onDappsChanged: AnyFunction; + onSwapCreated: (accountId: string, fromTimestamp: number) => any; } const hooks: Partial<{ diff --git a/src/api/methods/accounts.ts b/src/api/methods/accounts.ts index 43a2fce0..5c6b9888 100644 --- a/src/api/methods/accounts.ts +++ b/src/api/methods/accounts.ts @@ -1,4 +1,4 @@ -import type { ApiAccountInfo, ApiTxIdBySlug } from '../types'; +import type { ApiAccount, ApiTxIdBySlug } from '../types'; import { IS_EXTENSION } from '../../config'; import { parseAccountId } from '../../util/account'; @@ -9,8 +9,9 @@ import { storage } from '../storages'; import { deactivateAccountDapp, deactivateAllDapps, onActiveDappAccountUpdated } from './dapps'; import { sendUpdateTokens, - setupBackendStakingStatePolling, setupBalanceBasedPolling, + setupStakingPolling, + setupSwapPolling, } from './polling'; let activeAccountId: string | undefined; @@ -40,7 +41,8 @@ export async function activateAccount(accountId: string, newestTxIds?: ApiTxIdBy } void setupBalanceBasedPolling(accountId, newestTxIds); - void setupBackendStakingStatePolling(accountId); + void setupStakingPolling(accountId); + void setupSwapPolling(accountId); } export function deactivateAllAccounts() { @@ -63,7 +65,7 @@ export function isAccountActive(accountId: string) { return activeAccountId === accountId; } -export function fetchAccount(accountId: string): Promise { +export function fetchAccount(accountId: string): Promise { return fetchStoredAccount(accountId); } diff --git a/src/api/methods/auth.ts b/src/api/methods/auth.ts index 1f151a16..ae85d34e 100644 --- a/src/api/methods/auth.ts +++ b/src/api/methods/auth.ts @@ -1,12 +1,19 @@ import type { LedgerWalletInfo } from '../../util/ledger/types'; -import type { ApiAccountInfo, ApiNetwork, ApiTxIdBySlug } from '../types'; +import type { ApiAccount, ApiNetwork, ApiTxIdBySlug } from '../types'; import { IS_DAPP_SUPPORTED } from '../../config'; import blockchains from '../blockchains'; +import { toBase64Address } from '../blockchains/ton/util/tonweb'; import { - getNewAccountId, removeAccountValue, removeNetworkAccountsValue, setAccountValue, + getAccountIds, + getNewAccountId, + removeAccountValue, + removeNetworkAccountsValue, + setAccountValue, } from '../common/accounts'; import { bytesToHex } from '../common/utils'; +import { apiDb } from '../db'; +import { handleServerError } from '../errors'; import { storage } from '../storages'; import { activateAccount, deactivateAllAccounts, deactivateCurrentAccount } from './accounts'; import { removeAccountDapps, removeAllDapps, removeNetworkDapps } from './dapps'; @@ -31,7 +38,10 @@ export async function createWallet(network: ApiNetwork, mnemonic: string[], pass const address = await publicKeyToAddress(network, publicKey); const accountId = await getNewAccountId(network); - await storeAccount(accountId, mnemonic, password, publicKey, address); + await storeAccount(accountId, mnemonic, password, { + address, + publicKey: bytesToHex(publicKey), + }); void activateAccount(accountId); return { @@ -57,11 +67,21 @@ export async function importMnemonic(network: ApiNetwork, mnemonic: string[], pa const seedBase64 = await mnemonicToSeed(mnemonic); const { publicKey } = seedToKeyPair(seedBase64); - const wallet = await pickBestWallet(network, publicKey); - const address = (await wallet.getAddress()).toString(true, true, true); + let wallet: Awaited>; + + try { + wallet = await pickBestWallet(network, publicKey); + } catch (err: any) { + return handleServerError(err); + } + + const address = toBase64Address(await wallet.getAddress(), false); const accountId: string = await getNewAccountId(network); - await storeAccount(accountId, mnemonic, password, publicKey, address); + await storeAccount(accountId, mnemonic, password, { + publicKey: bytesToHex(publicKey), + address, + }); void activateAccount(accountId); return { @@ -76,7 +96,9 @@ export async function importLedgerWallet(network: ApiNetwork, walletInfo: Ledger publicKey, address, index, driver, deviceId, deviceName, version, } = walletInfo; - await storeHardwareAccount(accountId, publicKey, address, { + await storeHardwareAccount(accountId, { + address, + publicKey, version, ledger: { index, @@ -90,37 +112,16 @@ export async function importLedgerWallet(network: ApiNetwork, walletInfo: Ledger return { accountId, address, walletInfo }; } -async function storeHardwareAccount( - accountId: string, - publicKey: Uint8Array | string, - address: string, - accountInfo: ApiAccountInfo = {}, -) { - const publicKeyHex = typeof publicKey === 'string' ? publicKey : bytesToHex(publicKey); - - await Promise.all([ - setAccountValue(accountId, 'publicKeys', publicKeyHex), - setAccountValue(accountId, 'addresses', address), - setAccountValue(accountId, 'accounts', accountInfo), - ]); +function storeHardwareAccount(accountId: string, account?: ApiAccount) { + return setAccountValue(accountId, 'accounts', account); } -async function storeAccount( - accountId: string, - mnemonic: string[], - password: string, - publicKey: Uint8Array | string, - address: string, - accountInfo: ApiAccountInfo = {}, -) { +async function storeAccount(accountId: string, mnemonic: string[], password: string, account: ApiAccount) { const mnemonicEncrypted = await blockchains.ton.encryptMnemonic(mnemonic, password); - const publicKeyHex = typeof publicKey === 'string' ? publicKey : bytesToHex(publicKey); await Promise.all([ setAccountValue(accountId, 'mnemonicsEncrypted', mnemonicEncrypted), - setAccountValue(accountId, 'publicKeys', publicKeyHex), - setAccountValue(accountId, 'addresses', address), - setAccountValue(accountId, 'accounts', accountInfo), + setAccountValue(accountId, 'accounts', account), ]); } @@ -128,8 +129,6 @@ export async function removeNetworkAccounts(network: ApiNetwork) { deactivateAllAccounts(); await Promise.all([ - removeNetworkAccountsValue(network, 'addresses'), - removeNetworkAccountsValue(network, 'publicKeys'), removeNetworkAccountsValue(network, 'mnemonicsEncrypted'), removeNetworkAccountsValue(network, 'accounts'), IS_DAPP_SUPPORTED && removeNetworkDapps(network), @@ -140,24 +139,35 @@ export async function resetAccounts() { deactivateAllAccounts(); await Promise.all([ - storage.removeItem('addresses'), - storage.removeItem('publicKeys'), storage.removeItem('mnemonicsEncrypted'), storage.removeItem('accounts'), storage.removeItem('currentAccountId'), IS_DAPP_SUPPORTED && removeAllDapps(), + apiDb.nfts.clear(), ]); } export async function removeAccount(accountId: string, nextAccountId: string, newestTxIds?: ApiTxIdBySlug) { await Promise.all([ - removeAccountValue(accountId, 'addresses'), - removeAccountValue(accountId, 'publicKeys'), removeAccountValue(accountId, 'mnemonicsEncrypted'), removeAccountValue(accountId, 'accounts'), IS_DAPP_SUPPORTED && removeAccountDapps(accountId), + apiDb.nfts.where({ accountId }).delete(), ]); deactivateCurrentAccount(); await activateAccount(nextAccountId, newestTxIds); } + +export async function changePassword(oldPassword: string, password: string) { + for (const accountId of await getAccountIds()) { + const mnemonic = await blockchains.ton.fetchMnemonic(accountId, oldPassword); + + if (!mnemonic) { + throw new Error('Incorrect password'); + } + + const encryptedMnemonic = await blockchains.ton.encryptMnemonic(mnemonic, password); + await setAccountValue(accountId, 'mnemonicsEncrypted', encryptedMnemonic); + } +} diff --git a/src/api/methods/index.ts b/src/api/methods/index.ts index 64d5cadf..bb9de3d1 100644 --- a/src/api/methods/index.ts +++ b/src/api/methods/index.ts @@ -19,3 +19,6 @@ export { export { startSseConnection, } from '../tonConnect/sse'; +export * from './swap'; +export * from './other'; +export * from './prices'; diff --git a/src/api/methods/init.ts b/src/api/methods/init.ts index f21e52fa..99153177 100644 --- a/src/api/methods/init.ts +++ b/src/api/methods/init.ts @@ -10,6 +10,7 @@ import * as methods from '.'; addHooks({ onDappDisconnected: sendSseDisconnect, onDappsChanged: resetupSseConnection, + onSwapCreated: methods.setupSwapPolling, }); // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -18,14 +19,16 @@ export default async function init(onUpdate: OnApiUpdate, args: ApiInitArgs) { methods.initPolling(onUpdate, methods.isAccountActive); methods.initTransactions(onUpdate); - methods.initStaking(onUpdate); + methods.initStaking(); + methods.initSwap(onUpdate); + methods.initNfts(onUpdate); if (IS_DAPP_SUPPORTED) { methods.initDapps(onUpdate); tonConnect.initTonConnect(onUpdate); } - await startStorageMigration(); + await startStorageMigration(onUpdate); if (IS_SSE_SUPPORTED) { void resetupSseConnection(); diff --git a/src/api/methods/nfts.ts b/src/api/methods/nfts.ts index 87482255..a3c8a2b9 100644 --- a/src/api/methods/nfts.ts +++ b/src/api/methods/nfts.ts @@ -1,8 +1,57 @@ +import type { ApiDbNft } from '../db'; +import type { ApiNft, ApiUpdate, OnApiUpdate } from '../types'; + import blockchains from '../blockchains'; import { resolveBlockchainKey } from '../common/helpers'; +import { apiDb } from '../db'; + +let onUpdate: OnApiUpdate; + +export function initNfts(_onUpdate: OnApiUpdate) { + onUpdate = _onUpdate; +} export function fetchNfts(accountId: string) { const blockchain = blockchains[resolveBlockchainKey(accountId)!]; return blockchain.getAccountNfts(accountId); } + +export async function processNftUpdates(accountId: string, updates: ApiUpdate[]) { + updates.filter((update) => !(update.type === 'nftReceived' && update.nft.isHidden)).forEach(onUpdate); + + for (const update of updates) { + if (update.type === 'nftSent') { + const key = [accountId, update.nftAddress]; + await apiDb.nfts.delete(key); + } else if (update.type === 'nftReceived') { + const dbNft = convertToDbEntity(accountId, update.nft); + await apiDb.nfts.put(dbNft); + } else if (update.type === 'nftPutUpForSale') { + const key = [accountId, update.nftAddress]; + await apiDb.nfts.update(key, { isOnSale: true }); + } + } +} + +export async function updateNfts(accountId: string, nfts: ApiNft[]) { + const visibleNfts = nfts.filter((nft) => !nft.isHidden); + onUpdate({ + type: 'updateNfts', + accountId, + nfts: visibleNfts, + }); + + const dbNfts = nfts.map((nft) => convertToDbEntity(accountId, nft)); + + await apiDb.nfts.where({ accountId }).delete(); + await apiDb.nfts.bulkPut(dbNfts); +} + +function convertToDbEntity(accountId: string, nft: ApiNft): ApiDbNft { + return { + ...nft, + collectionAddress: nft.collectionAddress ?? '', + accountId, + }; +} diff --git a/src/api/methods/other.ts b/src/api/methods/other.ts new file mode 100644 index 00000000..313bd780 --- /dev/null +++ b/src/api/methods/other.ts @@ -0,0 +1,47 @@ +import nacl from 'tweetnacl'; + +import type { ApiBlockchainKey, ApiNetwork } from '../types'; + +import { parseAccountId } from '../../util/account'; +import blockchains from '../blockchains'; +import { fetchStoredAccount, updateStoredAccount } from '../common/accounts'; + +const SIGN_MESSAGE = Buffer.from('MyTonWallet_AuthToken_n6i0k4w8pb'); + +export function checkApiAvailability(options: { + accountId: string; +} | { + network: ApiNetwork; + blockchainKey: ApiBlockchainKey; +}) { + let network: ApiNetwork; + let blockchainKey: ApiBlockchainKey; + if ('network' in options) { + ({ network, blockchainKey } = options); + } else { + ({ network, blockchain: blockchainKey } = parseAccountId(options.accountId)); + } + + const blockchain = blockchains[blockchainKey]; + + return blockchain.checkApiAvailability(network); +} + +export async function getBackendAuthToken(accountId: string, password: string) { + const account = await fetchStoredAccount(accountId); + let authToken = account.authToken; + + if (!authToken) { + const privateKey = await blockchains.ton.fetchPrivateKey(accountId, password); + const signature = nacl.sign.detached(SIGN_MESSAGE, privateKey!); + authToken = Buffer.from(signature).toString('base64'); + + await updateStoredAccount(accountId, { authToken }); + } + + if (!account.isInitialized) { + authToken += `:${account.publicKey}`; + } + + return authToken; +} diff --git a/src/api/methods/polling.ts b/src/api/methods/polling.ts index 4e5d0747..0a7f94e3 100644 --- a/src/api/methods/polling.ts +++ b/src/api/methods/polling.ts @@ -2,8 +2,15 @@ import { randomBytes } from 'tweetnacl'; import type { TokenBalanceParsed } from '../blockchains/ton/tokens'; import type { + ApiActivity, + ApiBackendStakingState, + ApiBaseCurrency, ApiBaseToken, ApiNftUpdate, + ApiStakingCommonData, + ApiStakingState, + ApiSwapAsset, + ApiSwapHistoryItem, ApiToken, ApiTokenPrice, ApiTransactionActivity, @@ -11,38 +18,58 @@ import type { OnApiUpdate, } from '../types'; -import { APP_ENV, APP_VERSION, TON_TOKEN_SLUG } from '../../config'; +import { + APP_ENV, APP_VERSION, DEFAULT_PRICE_CURRENCY, TON_TOKEN_SLUG, +} from '../../config'; +import { parseAccountId } from '../../util/account'; +import { areDeepEqual } from '../../util/areDeepEqual'; import { compareActivities } from '../../util/compareActivities'; import { logDebugError } from '../../util/logs'; import { pause } from '../../util/schedulers'; import blockchains from '../blockchains'; import { addKnownTokens, getKnownTokens } from '../blockchains/ton/tokens'; +import { fetchStoredAccount, updateStoredAccount } from '../common/accounts'; import { tryUpdateKnownAddresses } from '../common/addresses'; import { callBackendGet } from '../common/backend'; import { isUpdaterAlive, resolveBlockchainKey } from '../common/helpers'; import { txCallbacks } from '../common/txCallbacks'; import { X_APP_ORIGIN } from '../environment'; import { storage } from '../storages'; -import { getBackendStakingState } from './staking'; +import { processNftUpdates, updateNfts } from './nfts'; +import { getBaseCurrency } from './prices'; +import { getBackendStakingState, getStakingCommonData, tryUpdateStakingCommonData } from './staking'; +import { + swapGetAssets, swapGetHistory, swapItemToActivity, swapReplaceTransactions, +} from './swap'; type IsAccountActiveFn = (accountId: string) => boolean; const POLLING_INTERVAL = 1100; // 1.1 sec const BACKEND_POLLING_INTERVAL = 30000; // 30 sec const LONG_BACKEND_POLLING_INTERVAL = 60000; // 1 min -const PAUSE_AFTER_BALANCE_CHANGE = 1000; // 1 sec -const FIRST_TRANSACTIONS_LIMIT = 20; + +const FIRST_TRANSACTIONS_LIMIT = 50; + const NFT_FULL_POLLING_INTERVAL = 30000; // 30 sec const NFT_FULL_UPDATE_FREQUNCY = Math.round(NFT_FULL_POLLING_INTERVAL / POLLING_INTERVAL); const DOUBLE_CHECK_TOKENS_PAUSE = 30000; // 30 sec +const SWAP_POLLING_INTERVAL = 3000; // 3 sec +const SWAP_FINISHED_STATUSES = new Set(['failed', 'completed', 'expired']); + let onUpdate: OnApiUpdate; let isAccountActive: IsAccountActiveFn; let clientId: string | undefined; let preloadEnsurePromise: Promise; -let pricesBySlug: Record = {}; - +const prices: { + baseCurrency: ApiBaseCurrency; + bySlug: Record; +} = { + baseCurrency: DEFAULT_PRICE_CURRENCY, + bySlug: {}, +}; +let swapPollingAccountId: string | undefined; const lastBalanceCache: Record; @@ -55,6 +82,8 @@ export function initPolling(_onUpdate: OnApiUpdate, _isAccountActive: IsAccountA preloadEnsurePromise = Promise.all([ tryUpdateKnownAddresses(), tryUpdateTokens(_onUpdate), + tryLoadSwapTokens(_onUpdate), + tryUpdateStakingCommonData(), ]); void setupBackendPolling(); @@ -71,7 +100,7 @@ function registerNewTokens(tokenBalances: TokenBalanceParsed[]) { areNewTokensFound = true; tokens[token.slug] = { ...token, - quote: pricesBySlug[token.slug] || { + quote: prices.bySlug[token.slug] || { price: 0.0, percentChange1h: 0.0, percentChange24h: 0.0, @@ -91,6 +120,11 @@ export async function setupBalanceBasedPolling(accountId: string, newestTxIds: A delete lastBalanceCache[accountId]; + const { network } = parseAccountId(accountId); + const account = await fetchStoredAccount(accountId); + const { address } = account; + let { isInitialized } = account; + let nftFromSec = Math.round(Date.now() / 1000); let nftUpdates: ApiNftUpdate[]; let i = 0; @@ -100,31 +134,22 @@ export async function setupBalanceBasedPolling(accountId: string, newestTxIds: A while (isUpdaterAlive(localOnUpdate) && isAccountActive(accountId)) { try { - const [balance, stakingState] = await Promise.all([ - blockchain.getAccountBalance(accountId).catch(logAndRescue), - blockchain.getStakingState(accountId).catch(logAndRescue), - ]); + const walletInfo = await blockchain.getWalletInfo(network, address); if (!isUpdaterAlive(localOnUpdate) || !isAccountActive(accountId)) return; - if (stakingState) { - onUpdate({ - type: 'updateStakingState', - accountId, - stakingState, - }); - } + const { balance, lastTxId } = walletInfo ?? {}; // Full update NFTs every ~30 seconds if (i % NFT_FULL_UPDATE_FREQUNCY === 0) { - nftFromSec = Math.round(Date.now() / 1000); - const nfts = await blockchain.getAccountNfts(accountId); + const nfts = await blockchain.getAccountNfts(accountId).catch(logAndRescue); if (!isUpdaterAlive(localOnUpdate) || !isAccountActive(accountId)) return; - onUpdate({ - type: 'updateNfts', - accountId, - nfts, - }); + if (nfts) { + nftFromSec = Math.round(Date.now() / 1000); + if (!isUpdaterAlive(localOnUpdate) || !isAccountActive(accountId)) return; + + void updateNfts(accountId, nfts); + } } // Process TON balance @@ -132,21 +157,16 @@ export async function setupBalanceBasedPolling(accountId: string, newestTxIds: A const changedTokenSlugs: string[] = []; const isTonBalanceChanged = balance && balance !== cache?.balance; + const balancesToUpdate: Record = {}; + if (isTonBalanceChanged) { changedTokenSlugs.push(TON_TOKEN_SLUG); - onUpdate({ - type: 'updateBalance', - accountId, - slug: TON_TOKEN_SLUG, - balance, - }); + balancesToUpdate[TON_TOKEN_SLUG] = balance; lastBalanceCache[accountId] = { ...lastBalanceCache[accountId], balance, }; - - await pause(PAUSE_AFTER_BALANCE_CHANGE); } // Fetch and process token balances @@ -154,24 +174,19 @@ export async function setupBalanceBasedPolling(accountId: string, newestTxIds: A doubleCheckTokensTime = isTonBalanceChanged ? Date.now() + DOUBLE_CHECK_TOKENS_PAUSE : undefined; const tokenBalances = await blockchain.getAccountTokenBalances(accountId).catch(logAndRescue); + if (!isUpdaterAlive(localOnUpdate) || !isAccountActive(accountId)) return; if (tokenBalances) { registerNewTokens(tokenBalances); - for (const { slug, balance: tokenBalance } of tokenBalances) { + tokenBalances.forEach(({ slug, balance: tokenBalance }) => { const cachedBalance = cache?.tokenBalances && cache.tokenBalances[slug]; - if (cachedBalance === tokenBalance) continue; + if (cachedBalance === tokenBalance) return; changedTokenSlugs.push(slug); - - onUpdate({ - type: 'updateBalance', - accountId, - slug, - balance: tokenBalance, - }); - } + balancesToUpdate[slug] = tokenBalance; + }); lastBalanceCache[accountId] = { ...lastBalanceCache[accountId], @@ -180,19 +195,40 @@ export async function setupBalanceBasedPolling(accountId: string, newestTxIds: A )), }; } + + if (Object.keys(balancesToUpdate).length > 0) { + onUpdate({ + type: 'updateBalances', + accountId, + balancesToUpdate, + }); + } } // Fetch transactions for tokens with a changed balance if (changedTokenSlugs.length) { + if (lastTxId) { + await blockchain.waitUntilTransactionAppears(network, address, lastTxId); + } + const newTxIds = await processNewTokenActivities(accountId, newestTxIds, changedTokenSlugs); newestTxIds = { ...newestTxIds, ...newTxIds }; } // Fetch NFT updates if (isTonBalanceChanged) { - [nftFromSec, nftUpdates] = await blockchain.getNftUpdates(accountId, nftFromSec); + const nftResult = await blockchain.getNftUpdates(accountId, nftFromSec).catch(logAndRescue); if (!isUpdaterAlive(localOnUpdate) || !isAccountActive(accountId)) return; - nftUpdates.forEach(onUpdate); + + if (nftResult) { + [nftFromSec, nftUpdates] = nftResult; + void processNftUpdates(accountId, nftUpdates); + } + } + + if (isTonBalanceChanged && !isInitialized && await blockchain.isAddressInitialized(network, address)) { + isInitialized = true; + await updateStoredAccount(accountId, { isInitialized }); } i++; @@ -204,6 +240,53 @@ export async function setupBalanceBasedPolling(accountId: string, newestTxIds: A } } +export async function setupStakingPolling(accountId: string) { + const { blockchain: blockchainKey, network } = parseAccountId(accountId); + const blockchain = blockchains[blockchainKey]; + + if (network !== 'mainnet') { + return; + } + + const localOnUpdate = onUpdate; + let lastState: { + stakingCommonData: ApiStakingCommonData; + backendStakingState: ApiBackendStakingState; + stakingState: ApiStakingState; + } | undefined; + + while (isUpdaterAlive(localOnUpdate) && isAccountActive(accountId)) { + try { + const stakingCommonData = getStakingCommonData(); + const backendStakingState = await getBackendStakingState(accountId); + const stakingState = await blockchain.getStakingState( + accountId, stakingCommonData, backendStakingState, + ); + + if (!isUpdaterAlive(localOnUpdate) || !isAccountActive(accountId)) return; + + const state = { + stakingCommonData, + backendStakingState, + stakingState, + }; + + if (!areDeepEqual(state, lastState)) { + lastState = state; + onUpdate({ + type: 'updateStaking', + accountId, + ...state, + }); + } + } catch (err) { + logDebugError('setupBalancePolling', err); + } + + await pause(POLLING_INTERVAL); + } +} + async function processNewTokenActivities( accountId: string, newestTxIds: ApiTxIdBySlug, @@ -216,32 +299,34 @@ async function processNewTokenActivities( } let allTransactions: ApiTransactionActivity[] = []; + let allActivities: ApiActivity[] = []; const entries = await Promise.all(tokenSlugs.map(async (slug) => { let newestTxId = newestTxIds[slug]; const transactions = await blockchain.getTokenTransactionSlice( - accountId, slug, undefined, newestTxId, + accountId, slug, undefined, newestTxId, FIRST_TRANSACTIONS_LIMIT, ); + const activities = await swapReplaceTransactions(accountId, transactions, slug); if (transactions.length) { newestTxId = transactions[0]!.txId; - allTransactions = allTransactions.concat(transactions.slice(0, FIRST_TRANSACTIONS_LIMIT)); + + allActivities = allActivities.concat(activities); + allTransactions = allTransactions.concat(transactions); } return [slug, newestTxId]; })); - allTransactions.sort((a, b) => compareActivities(a, b, true)); + allTransactions = allTransactions.sort((a, b) => compareActivities(a, b, true)); allTransactions.forEach((transaction) => { txCallbacks.runCallbacks(transaction); }); - const activities = allTransactions; - onUpdate({ type: 'newActivities', - activities, + activities: allActivities, accountId, }); @@ -267,36 +352,46 @@ export async function setupLongBackendPolling() { while (isUpdaterAlive(onUpdate)) { await pause(LONG_BACKEND_POLLING_INTERVAL); - try { - await tryUpdateKnownAddresses(); - } catch (err) { - logDebugError('setupLongBackendPolling', err); - } + await Promise.all([ + tryUpdateKnownAddresses(), + tryUpdateStakingCommonData(), + ]); } } -export async function tryUpdateTokens(localOnUpdate: OnApiUpdate) { +export async function tryUpdateTokens(localOnUpdate?: OnApiUpdate) { + if (!localOnUpdate) { + localOnUpdate = onUpdate; + } + try { + const baseCurrency = await getBaseCurrency(); + const pricesHeaders: AnyLiteral = { + 'X-App-Origin': X_APP_ORIGIN, + 'X-App-Version': APP_VERSION, + 'X-App-ClientID': clientId ?? await getClientId(), + 'X-App-Env': APP_ENV, + }; + const [pricesData, tokens] = await Promise.all([ - callBackendGet('/prices', undefined, { - 'X-App-Origin': X_APP_ORIGIN, - 'X-App-Version': APP_VERSION, - 'X-App-ClientID': clientId ?? await getClientId(), - 'X-App-Env': APP_ENV, - }) as Promise>, - callBackendGet('/known-tokens') as Promise, + callBackendGet>('/prices', { base: baseCurrency }, pricesHeaders), + callBackendGet('/known-tokens'), ]); if (!isUpdaterAlive(localOnUpdate)) return; addKnownTokens(tokens); - pricesBySlug = Object.values(pricesData).reduce((acc, { slugs, quote }) => { + prices.bySlug = Object.values(pricesData).reduce((acc, { slugs, quote }) => { for (const slug of slugs) { acc[slug] = quote; } return acc; }, {} as Record); + prices.baseCurrency = baseCurrency; sendUpdateTokens(); } catch (err) { @@ -304,6 +399,29 @@ export async function tryUpdateTokens(localOnUpdate: OnApiUpdate) { } } +export async function tryLoadSwapTokens(localOnUpdate: OnApiUpdate) { + try { + const assets = await swapGetAssets(); + + if (!isUpdaterAlive(localOnUpdate)) return; + + const tokens = assets.reduce((acc: Record, asset) => { + acc[asset.slug] = { + ...asset, + contract: asset.contract ?? asset.slug, + }; + return acc; + }, {}); + + onUpdate({ + type: 'updateSwapTokens', + tokens, + }); + } catch (err) { + logDebugError('tryLoadSwapTokens', err); + } +} + async function getClientId() { clientId = await storage.getItem('clientId'); if (!clientId) { @@ -316,37 +434,108 @@ async function getClientId() { export function sendUpdateTokens() { const tokens = getKnownTokens(); Object.values(tokens).forEach((token) => { - if (token.slug in pricesBySlug) { - token.quote = pricesBySlug[token.slug]; + if (token.slug in prices.bySlug) { + token.quote = prices.bySlug[token.slug]; } }); onUpdate({ type: 'updateTokens', tokens, + baseCurrency: prices.baseCurrency, }); } -export async function setupBackendStakingStatePolling(accountId: string) { - while (isUpdaterAlive(onUpdate) && isAccountActive(accountId)) { +export async function setupSwapPolling(accountId: string) { + if (swapPollingAccountId === accountId) return; // Double launch is not allowed + swapPollingAccountId = accountId; + + const { address, lastFinishedSwapTimestamp } = await fetchStoredAccount(accountId); + + let fromTimestamp = lastFinishedSwapTimestamp ?? await getActualLastFinishedSwapTimestamp(accountId, address); + + const localOnUpdate = onUpdate; + const swapById: Record = {}; + + while (isUpdaterAlive(localOnUpdate) && isAccountActive(accountId)) { try { - const backendStakingState = await getBackendStakingState(accountId); - if (!isUpdaterAlive(onUpdate) || !isAccountActive(accountId)) return; + const swaps = await swapGetHistory(address, { + fromTimestamp, + }); + if (!isUpdaterAlive(onUpdate) || !isAccountActive(accountId)) break; + if (!swaps.length) break; - if (backendStakingState) { - onUpdate({ - type: 'updateBackendStakingState', - backendStakingState, + swaps.reverse(); + + let isLastFinishedSwapUpdated = false; + let isPrevFinished = true; + + for (const swap of swaps) { + if (swap.cex) { + if (swap.cex.status === swapById[swap.id]?.cex!.status) { + continue; + } + } else if (swap.status === swapById[swap.id]?.status) { + continue; + } + + swapById[swap.id] = swap; + + const isFinished = SWAP_FINISHED_STATUSES.has(swap.status); + if (isFinished && isPrevFinished) { + fromTimestamp = swap.timestamp; + isLastFinishedSwapUpdated = true; + } + isPrevFinished = isFinished; + + if (!swap.cex && swap.status !== 'completed') { + // Completed onchain swaps are processed in swapReplaceTransactions + onUpdate({ + type: 'newActivities', + accountId, + activities: [swapItemToActivity(swap)], + }); + } + } + + if (isLastFinishedSwapUpdated) { + await updateStoredAccount(accountId, { + lastFinishedSwapTimestamp: fromTimestamp, }); } } catch (err) { - logDebugError('setupBackendStakingStatePolling', err); + logDebugError('setupSwapCexPolling', err); } - await pause(BACKEND_POLLING_INTERVAL); + await pause(SWAP_POLLING_INTERVAL); + } + + if (accountId === swapPollingAccountId) { + swapPollingAccountId = undefined; } } +async function getActualLastFinishedSwapTimestamp(accountId: string, address: string) { + const swaps = await swapGetHistory(address, {}); + + swaps.reverse(); + + let timestamp = Date.now(); + for (const swap of swaps) { + if (SWAP_FINISHED_STATUSES.has(swap.status)) { + timestamp = swap.timestamp; + } else { + break; + } + } + + await updateStoredAccount(accountId, { + lastFinishedSwapTimestamp: timestamp, + }); + + return timestamp; +} + function logAndRescue(err: Error) { logDebugError('Polling error', err); diff --git a/src/api/methods/prices.ts b/src/api/methods/prices.ts new file mode 100644 index 00000000..832dc4c0 --- /dev/null +++ b/src/api/methods/prices.ts @@ -0,0 +1,12 @@ +import type { ApiBaseCurrency } from '../types'; + +import { DEFAULT_PRICE_CURRENCY } from '../../config'; +import { storage } from '../storages'; + +export async function getBaseCurrency() { + return (await storage.getItem('baseCurrency')) ?? DEFAULT_PRICE_CURRENCY; +} + +export function setBaseCurrency(currency: ApiBaseCurrency) { + return storage.setItem('baseCurrency', currency); +} diff --git a/src/api/methods/staking.ts b/src/api/methods/staking.ts index 8a88fcca..0b5f0bc0 100644 --- a/src/api/methods/staking.ts +++ b/src/api/methods/staking.ts @@ -1,39 +1,61 @@ -import type { ApiBackendStakingState, OnApiUpdate } from '../types'; +import type { + ApiBackendStakingState, + ApiStakingCommonData, + ApiStakingHistory, + ApiStakingType, +} from '../types'; import { TON_TOKEN_SLUG } from '../../config'; +import { logDebugError } from '../../util/logs'; import blockchains from '../blockchains'; import { STAKE_COMMENT, UNSTAKE_COMMENT } from '../blockchains/ton/constants'; import { fetchStoredAddress } from '../common/accounts'; -import { createLocalTransaction, resolveBlockchainKey } from '../common/helpers'; +import { callBackendGet } from '../common/backend'; +import { resolveBlockchainKey } from '../common/helpers'; +import { isKnownStakingPool } from '../common/utils'; +import { createLocalTransaction } from './transactions'; -let onUpdate: OnApiUpdate; +const CACHE_TTL = 60000; // 1 m. +let backendStakingStateByAddress: Record = {}; +let stakingCommonData: ApiStakingCommonData; -export function initStaking(_onUpdate: OnApiUpdate) { - onUpdate = _onUpdate; +// let onUpdate: OnApiUpdate; + +export function initStaking() { + // onUpdate = _onUpdate; } -export function checkStakeDraft(accountId: string, amount: string) { +export async function checkStakeDraft(accountId: string, amount: string) { const blockchain = blockchains[resolveBlockchainKey(accountId)!]; - return blockchain.checkStakeDraft(accountId, amount); + const backendState = await getBackendStakingState(accountId); + return blockchain.checkStakeDraft(accountId, amount, stakingCommonData!, backendState!); } -export function checkUnstakeDraft(accountId: string) { +export async function checkUnstakeDraft(accountId: string, amount: string) { const blockchain = blockchains[resolveBlockchainKey(accountId)!]; - return blockchain.checkUnstakeDraft(accountId); + const backendState = await getBackendStakingState(accountId); + return blockchain.checkUnstakeDraft(accountId, amount, stakingCommonData!, backendState!); } -export async function submitStake(accountId: string, password: string, amount: string, fee?: string) { +export async function submitStake( + accountId: string, password: string, amount: string, type: ApiStakingType, fee?: string, +) { const blockchain = blockchains[resolveBlockchainKey(accountId)!]; const fromAddress = await fetchStoredAddress(accountId); - const result = await blockchain.submitStake(accountId, password, amount); + const backendState = await getBackendStakingState(accountId); + const result = await blockchain.submitStake( + accountId, password, amount, type, backendState!, + ); if ('error' in result) { return false; } - const localTransaction = createLocalTransaction(onUpdate, accountId, { + onStakingChangeExpected(); + + const localTransaction = createLocalTransaction(accountId, { amount: result.amount, fromAddress, toAddress: result.normalizedAddress, @@ -49,16 +71,25 @@ export async function submitStake(accountId: string, password: string, amount: s }; } -export async function submitUnstake(accountId: string, password: string, fee?: string) { +export async function submitUnstake( + accountId: string, + password: string, + type: ApiStakingType, + amount: string, + fee?: string, +) { const blockchain = blockchains[resolveBlockchainKey(accountId)!]; const fromAddress = await fetchStoredAddress(accountId); - const result = await blockchain.submitUnstake(accountId, password); + const backendState = await getBackendStakingState(accountId); + const result = await blockchain.submitUnstake(accountId, password, type, amount, backendState!); if ('error' in result) { return false; } - const localTransaction = createLocalTransaction(onUpdate, accountId, { + onStakingChangeExpected(); + + const localTransaction = createLocalTransaction(accountId, { amount: result.amount, fromAddress, toAddress: result.normalizedAddress, @@ -74,25 +105,62 @@ export async function submitUnstake(accountId: string, password: string, fee?: s }; } -export function getStakingState(accountId: string) { - const blockchain = blockchains[resolveBlockchainKey(accountId)!]; +export async function getBackendStakingState(accountId: string): Promise { + const address = await fetchStoredAddress(accountId); + const state = await fetchBackendStakingState(address); + return { + ...state, + nominatorsPool: { + ...state.nominatorsPool, + start: state.nominatorsPool.start * 1000, + end: state.nominatorsPool.end * 1000, + }, + }; +} + +export async function fetchBackendStakingState(address: string): Promise { + const cacheItem = backendStakingStateByAddress[address]; + if (cacheItem && cacheItem[0] > Date.now()) { + return cacheItem[1]; + } + + const stakingState = await callBackendGet(`/staking/state/${address}`); - return blockchain.getStakingState(accountId); + if (!isKnownStakingPool(stakingState.nominatorsPool.address)) { + throw Error('Unexpected pool address, likely a malicious activity'); + } + + backendStakingStateByAddress[address] = [Date.now() + CACHE_TTL, stakingState]; + + return stakingState; +} + +export async function getStakingHistory( + accountId: string, limit?: number, offset?: number, +): Promise { + const address = await fetchStoredAddress(accountId); + return callBackendGet(`/staking/profits/${address}`, { limit, offset }); +} + +export function onStakingChangeExpected() { + backendStakingStateByAddress = {}; } -export async function getBackendStakingState(accountId: string): Promise { - const state = await blockchains.ton.getBackendStakingState(accountId); - if (!state) { - return state; +export async function tryUpdateStakingCommonData() { + try { + const data: ApiStakingCommonData = await callBackendGet('/staking/common'); + data.round.start *= 1000; + data.round.end *= 1000; + data.round.unlock *= 1000; + data.prevRound.start *= 1000; + data.prevRound.end *= 1000; + data.prevRound.unlock *= 1000; + stakingCommonData = data; + } catch (err) { + logDebugError('tryUpdateLiquidStakingState', err); } +} - const poolState = state.poolState; - return { - ...state, - poolState: { - ...poolState, - startOfCycle: poolState!.startOfCycle * 1000, - endOfCycle: poolState!.endOfCycle * 1000, - }, - }; +export function getStakingCommonData() { + return stakingCommonData!; } diff --git a/src/api/methods/swap.ts b/src/api/methods/swap.ts new file mode 100644 index 00000000..9ac980db --- /dev/null +++ b/src/api/methods/swap.ts @@ -0,0 +1,280 @@ +import type { + ApiActivity, + ApiSwapActivity, + ApiSwapAsset, + ApiSwapBuildRequest, + ApiSwapBuildResponse, + ApiSwapCexCreateTransactionRequest, + ApiSwapCexCreateTransactionResponse, + ApiSwapCexEstimateRequest, + ApiSwapCexEstimateResponse, + ApiSwapEstimateRequest, + ApiSwapEstimateResponse, + ApiSwapHistoryItem, + ApiSwapPairAsset, + ApiSwapTonAsset, + ApiSwapTransfer, + ApiTransactionActivity, + OnApiUpdate, +} from '../types'; + +import { TON_SYMBOL, TON_TOKEN_SLUG } from '../../config'; +import { logDebugError } from '../../util/logs'; +import { pause } from '../../util/schedulers'; +import { buildSwapId } from '../../util/swap/buildSwapId'; +import blockchains from '../blockchains'; +import { buildTokenSlug, parseTxId } from '../blockchains/ton/util'; +import { fetchStoredAddress } from '../common/accounts'; +import { callBackendGet, callBackendPost } from '../common/backend'; +import { whenTxComplete } from '../common/txCallbacks'; +import { callHook } from '../hooks'; +import { getBackendAuthToken } from './other'; + +type LtRange = [number, number]; + +const SWAP_MAX_LT = 50; +const SWAP_WAITING_TIME = 5000; // 5 sec +const SWAP_WAITING_PAUSE = 500; // 0.5 sec + +const pendingLtRanges: LtRange[] = []; +const ton = blockchains.ton; + +let onUpdate: OnApiUpdate; + +export function initSwap(_onUpdate: OnApiUpdate) { + onUpdate = _onUpdate; +} + +export async function swapBuildTransfer(accountId: string, password: string, params: ApiSwapBuildRequest) { + const authToken = await getBackendAuthToken(accountId, password); + + const { id, transfers } = await swapBuild(authToken, params); + + const transferList = transfers.map((transfer) => ({ + ...transfer, + isBase64Payload: true, + })); + const result = await ton.checkMultiTransactionDraft(accountId, transferList); + + if ('error' in result) { + return result; + } + + return { ...result, id, transfers }; +} + +export async function swapSubmit( + accountId: string, + password: string, + fee: string, + transfers: ApiSwapTransfer[], + historyItem: ApiSwapHistoryItem, +) { + const transferList = transfers.map((transfer) => ({ + ...transfer, + isBase64Payload: true, + })); + const result = await ton.submitMultiTransfer(accountId, password, transferList); + + if ('error' in result) { + return result; + } + + const { amount, toAddress } = transfers[0]; + + const from = getSwapItemSlug(historyItem, historyItem.from); + const to = getSwapItemSlug(historyItem, historyItem.to); + + const swap: ApiSwapActivity = { + ...historyItem, + id: buildSwapId(historyItem.id), + from, + to, + kind: 'swap', + }; + + function onTxComplete(transaction: ApiTransactionActivity) { + const lt = parseTxId(transaction.txId).lt; + pendingLtRanges.push([lt, lt + SWAP_MAX_LT]); + } + + whenTxComplete(toAddress, amount) + .then(({ transaction }) => onTxComplete(transaction)); + + onUpdate({ + type: 'newActivities', + accountId, + activities: [swap], + }); + + void callHook('onSwapCreated', accountId, swap.timestamp - 1); + + return result; +} + +function getSwapItemSlug(item: ApiSwapHistoryItem, asset: string) { + if (asset === TON_SYMBOL) return TON_TOKEN_SLUG; + if (item.cex) return asset; + return buildTokenSlug(asset); +} + +export async function swapReplaceTransactions( + accountId: string, + transactions: ApiTransactionActivity[], + slug?: string, +): Promise { + if (!transactions.length) { + return transactions; + } + + try { + const address = await fetchStoredAddress(accountId); + + const firstLt = parseTxId(transactions[0].txId).lt; + const lastLt = parseTxId(transactions[transactions.length - 1].txId).lt; + + const firstTimestamp = transactions[0].timestamp; + const lastTimestamp = transactions[transactions.length - 1].timestamp; + + const [fromLt, fromTimestamp] = firstLt > lastLt ? [lastLt, lastTimestamp] : [firstLt, firstTimestamp]; + const [toLt, toTimestamp] = firstLt > lastLt ? [firstLt, firstTimestamp] : [lastLt, lastTimestamp]; + + const waitUntil = Date.now() + SWAP_WAITING_TIME; + while (Date.now() < waitUntil) { + const pendingSwaps = await swapGetHistory(address, { + status: 'pending', + isCex: false, + }); + if (!pendingSwaps.length) { + break; + } + await pause(SWAP_WAITING_PAUSE); + } + + const swaps = await swapGetHistory(address, { + fromLt, toLt, fromTimestamp, toTimestamp, + }); + + if (!swaps.length) { + return transactions; + } + + const skipLtRanges: LtRange[] = [...pendingLtRanges]; + const result: ApiActivity[] = []; + + for (const swap of swaps) { + if (swap.lt) { + skipLtRanges.push([swap.lt, swap.lt + SWAP_MAX_LT]); + } + + const swapActivity = swapItemToActivity(swap); + + if (slug && swapActivity.from !== slug && swapActivity.to !== slug) { + continue; + } + result.push(swapActivity); + } + + for (const transaction of transactions) { + const lt = parseTxId(transaction.txId).lt; + const swapIndex = skipLtRanges.findIndex(([startLt, endLt]) => lt >= startLt && lt <= endLt); + + if (swapIndex < 0) { + result.push(transaction); + } else { + result.push({ + ...transaction, + shouldHide: true, + }); + } + } + + return result; + } catch (err) { + logDebugError('swapReplaceTransactions', err); + return transactions; + } +} + +export function swapItemToActivity(swap: ApiSwapHistoryItem): ApiSwapActivity { + return { + ...swap, + id: buildSwapId(swap.id), + kind: 'swap', + from: getSwapItemSlug(swap, swap.from), + to: getSwapItemSlug(swap, swap.to), + }; +} + +export function swapEstimate(params: ApiSwapEstimateRequest): Promise { + return callBackendPost('/swap/ton/estimate', params, { + isAllowBadRequest: true, + }); +} + +export function swapBuild(authToken: string, params: ApiSwapBuildRequest): Promise { + return callBackendPost('/swap/ton/build', params, { + authToken, + }); +} + +export function swapGetAssets(): Promise { + return callBackendGet('/swap/assets'); +} + +export function swapGetTonCurrencies(): Promise { + return callBackendGet('/swap/ton/tokens'); +} + +export function swapGetPairs(symbolOrMinter: string): Promise { + return callBackendGet('/swap/pairs', { asset: symbolOrMinter }); +} + +export function swapGetHistory(address: string, params: { + fromLt?: number; + toLt?: number; + fromTimestamp?: number; + toTimestamp?: number; + status?: ApiSwapHistoryItem['status']; + isCex?: boolean; +}): Promise { + return callBackendGet(`/swap/history/${address}`, params); +} + +export function swapGetHistoryItem(address: string, id: number): Promise { + return callBackendGet(`/swap/history/${address}/${id}`); +} + +export function swapCexEstimate(params: ApiSwapCexEstimateRequest): Promise { + return callBackendPost('/swap/cex/estimate', params); +} + +export function swapCexValidateAddress(params: { slug: string; address: string }): Promise<{ + result: boolean; + message?: string; +}> { + return callBackendGet('/swap/cex/validate-address', params); +} + +export async function swapCexCreateTransaction( + accountId: string, + password: string, + params: ApiSwapCexCreateTransactionRequest, +): Promise<{ swap: ApiSwapHistoryItem; activity: ApiSwapActivity }> { + const authToken = await getBackendAuthToken(accountId, password); + + const { swap } = await callBackendPost('/swap/cex/createTransaction', params, { + authToken, + }); + const activity = swapItemToActivity(swap); + + onUpdate({ + type: 'newActivities', + accountId, + activities: [activity], + }); + + void callHook('onSwapCreated', accountId, swap.timestamp - 1); + + return { swap, activity }; +} diff --git a/src/api/methods/tokens.ts b/src/api/methods/tokens.ts index 4f1836a3..e939138c 100644 --- a/src/api/methods/tokens.ts +++ b/src/api/methods/tokens.ts @@ -3,12 +3,11 @@ import type { ApiNetwork } from '../types'; import { parseAccountId } from '../../util/account'; import blockchains from '../blockchains'; -export function importToken(accountId: string, address: string) { +export function fetchToken(accountId: string, address: string) { const { network, blockchain: blockchainKey } = parseAccountId(accountId); - const blockchain = blockchains[blockchainKey]; - return blockchain.importToken(network, address); + return blockchain.fetchToken(network, address); } export function resolveTokenBySlug(slug: string) { @@ -28,3 +27,9 @@ export function resolveTokenMinterAddress(network: ApiNetwork, tokenWalletAddres return blockchain.resolveTokenMinterAddress(network, tokenWalletAddress); } + +export function buildTokenSlug(address: string) { + const blockchain = blockchains.ton; + + return blockchain.buildTokenSlug(address); +} diff --git a/src/api/methods/transactions.ts b/src/api/methods/transactions.ts index 16f09424..16e10d87 100644 --- a/src/api/methods/transactions.ts +++ b/src/api/methods/transactions.ts @@ -1,11 +1,20 @@ import type { - ApiSignedTransfer, ApiSubmitTransferOptions, ApiTxIdBySlug, OnApiUpdate, + ApiLocalTransactionParams, + ApiSignedTransfer, + ApiSubmitTransferOptions, + ApiTxIdBySlug, + OnApiUpdate, } from '../types'; import { parseAccountId } from '../../util/account'; +import { logDebugError } from '../../util/logs'; import blockchains from '../blockchains'; import { fetchStoredAddress } from '../common/accounts'; -import { createLocalTransaction, resolveBlockchainKey } from '../common/helpers'; +import { + buildLocalTransaction, resolveBlockchainKey, +} from '../common/helpers'; +import { handleServerError } from '../errors'; +import { swapReplaceTransactions } from './swap'; let onUpdate: OnApiUpdate; @@ -15,18 +24,24 @@ export function initTransactions(_onUpdate: OnApiUpdate) { export async function fetchTokenActivitySlice(accountId: string, slug: string, fromTxId?: string, limit?: number) { const blockchain = blockchains[resolveBlockchainKey(accountId)!]; - - const transactions = await blockchain.getTokenTransactionSlice(accountId, slug, fromTxId, undefined, limit); - - return transactions; + try { + const transactions = await blockchain.getTokenTransactionSlice(accountId, slug, fromTxId, undefined, limit); + return await swapReplaceTransactions(accountId, transactions, slug); + } catch (err) { + logDebugError('fetchTokenActivitySlice', err); + return handleServerError(err); + } } export async function fetchAllActivitySlice(accountId: string, lastTxIds: ApiTxIdBySlug, limit: number) { const blockchain = blockchains[resolveBlockchainKey(accountId)!]; - - const transactions = await blockchain.getMergedTransactionSlice(accountId, lastTxIds, limit); - - return transactions; + try { + const transactions = await blockchain.getMergedTransactionSlice(accountId, lastTxIds, limit); + return await swapReplaceTransactions(accountId, transactions); + } catch (err) { + logDebugError('fetchAllActivitySlice', err); + return handleServerError(err); + } } export function checkTransactionDraft( @@ -39,7 +54,7 @@ export function checkTransactionDraft( ); } -export async function submitTransfer(options: ApiSubmitTransferOptions) { +export async function submitTransfer(options: ApiSubmitTransferOptions, shouldCreateLocalTransaction = true) { const { accountId, password, slug, toAddress, amount, comment, fee, shouldEncrypt, } = options; @@ -55,7 +70,12 @@ export async function submitTransfer(options: ApiSubmitTransferOptions) { } const { encryptedComment } = result; - const localTransaction = createLocalTransaction(onUpdate, accountId, { + + if (!shouldCreateLocalTransaction) { + return result; + } + + const localTransaction = createLocalTransaction(accountId, { amount, fromAddress, toAddress: result.normalizedAddress, @@ -85,7 +105,7 @@ export async function sendSignedTransferMessage(accountId: string, message: ApiS await blockchain.sendSignedMessage(accountId, message); - const localTransaction = createLocalTransaction(onUpdate, accountId, message.params); + const localTransaction = createLocalTransaction(accountId, message.params); return localTransaction.txId; } @@ -96,7 +116,7 @@ export async function sendSignedTransferMessages(accountId: string, messages: Ap const result = await blockchain.sendSignedMessages(accountId, messages); for (let i = 0; i < result.successNumber; i++) { - createLocalTransaction(onUpdate, accountId, messages[i].params); + createLocalTransaction(accountId, messages[i].params); } return result; @@ -107,3 +127,22 @@ export function decryptComment(accountId: string, encryptedComment: string, from return blockchain.decryptComment(accountId, encryptedComment, fromAddress, password); } + +export function createLocalTransaction(accountId: string, params: ApiLocalTransactionParams) { + const blockchainKey = parseAccountId(accountId).blockchain; + const blockchain = blockchains[blockchainKey]; + + const { toAddress } = params; + + const normalizedAddress = blockchain.normalizeAddress(toAddress); + + const localTransaction = buildLocalTransaction(params, normalizedAddress); + + onUpdate({ + type: 'newLocalTransaction', + transaction: localTransaction, + accountId, + }); + + return localTransaction; +} diff --git a/src/api/methods/wallet.ts b/src/api/methods/wallet.ts index fb31bfec..c37f3d23 100644 --- a/src/api/methods/wallet.ts +++ b/src/api/methods/wallet.ts @@ -6,7 +6,6 @@ import { parseAccountId } from '../../util/account'; import blockchains from '../blockchains'; import { fetchStoredAddress, - fetchStoredPublicKey, getMainAccountId, } from '../common/accounts'; import * as dappPromises from '../common/dappPromises'; @@ -61,12 +60,14 @@ export function fetchAddress(accountId: string) { return fetchStoredAddress(accountId); } -export function fetchPublicKey(accountId: string) { - return fetchStoredPublicKey(accountId); +export function isWalletInitialized(network: ApiNetwork, address: string) { + const blockchain = blockchains.ton; + + return blockchain.isAddressInitialized(network, address); } -export function isWalletInitialized(network: ApiNetwork, address: string) { +export function getWalletBalance(network: ApiNetwork, address: string) { const blockchain = blockchains.ton; - return blockchain.isWalletInitialized(network, address); + return blockchain.getWalletBalance(network, address); } diff --git a/src/api/providers/extension/connectorForPopup.ts b/src/api/providers/extension/connectorForPopup.ts index 0078de66..80bfb7b7 100644 --- a/src/api/providers/extension/connectorForPopup.ts +++ b/src/api/providers/extension/connectorForPopup.ts @@ -29,7 +29,6 @@ export async function callApi(methodName: T, ...args: M args, }) as MethodResponse); } catch (err) { - logDebugError('callApi', err); return undefined; } } diff --git a/src/api/providers/worker/connector.ts b/src/api/providers/worker/connector.ts index 11d3d284..c6b2cecd 100644 --- a/src/api/providers/worker/connector.ts +++ b/src/api/providers/worker/connector.ts @@ -30,7 +30,6 @@ export async function callApi(fnName: T, ...args: Al args, }) as AllMethodResponse); } catch (err) { - logDebugError('callApi', err); return undefined; } } diff --git a/src/api/storages/idb.ts b/src/api/storages/idb.ts index 39286a3c..d8f3daae 100644 --- a/src/api/storages/idb.ts +++ b/src/api/storages/idb.ts @@ -1,24 +1,27 @@ import * as idb from 'idb-keyval'; -import type { Storage } from './types'; +import type { Storage, StorageKey } from './types'; +import { INDEXED_DB_NAME, INDEXED_DB_STORE_NAME } from '../../config'; import { fromKeyValueArrays } from '../../util/iteratees'; +const store = idb.createStore(INDEXED_DB_NAME, INDEXED_DB_STORE_NAME); + export default { - getItem: idb.get, - setItem: idb.set, - removeItem: idb.del, - clear: idb.clear, + getItem: (name: StorageKey) => idb.get(name, store), + setItem: (name: StorageKey, value: any) => idb.set(name, value, store), + removeItem: (name: StorageKey) => idb.del(name, store), + clear: () => idb.clear(store), getAll: async () => { - const keys = await idb.keys() as string[]; - const values = await idb.getMany(keys); + const keys = await idb.keys(store) as string[]; + const values = await idb.getMany(keys, store); return fromKeyValueArrays(keys, values); }, getMany: async (keys) => { - const values = await idb.getMany(keys); + const values = await idb.getMany(keys, store); return fromKeyValueArrays(keys, values); }, setMany: (items) => { - return idb.setMany(Object.entries(items)); + return idb.setMany(Object.entries(items), store); }, } as Storage; diff --git a/src/api/storages/types.ts b/src/api/storages/types.ts index cb3c1955..b2082c29 100644 --- a/src/api/storages/types.ts +++ b/src/api/storages/types.ts @@ -20,13 +20,12 @@ export interface Storage { getMany(keys: string[]): Promise; } -export type StorageKey = 'addresses' -| 'mnemonicsEncrypted' -| 'publicKeys' +export type StorageKey = 'mnemonicsEncrypted' | 'accounts' | 'stateVersion' | 'currentAccountId' | 'clientId' +| 'baseCurrency' // For extension | 'dapps' | 'dappMethods:lastAccountId' diff --git a/src/api/tonConnect/index.ts b/src/api/tonConnect/index.ts index e4a0711d..25e6ad91 100644 --- a/src/api/tonConnect/index.ts +++ b/src/api/tonConnect/index.ts @@ -21,19 +21,19 @@ import type { ApiDappRequest, ApiNetwork, ApiSignedTransfer, - ApiTransactionDraftError, OnApiUpdate, } from '../types'; import type { ApiTonConnectProof, LocalConnectEvent, TransactionPayload, TransactionPayloadMessage, } from './types'; -import { ApiTransactionError } from '../types'; +import { ApiCommonError, ApiTransactionError } from '../types'; import { CONNECT_EVENT_ERROR_CODES, SEND_TRANSACTION_ERROR_CODES, SIGN_DATA_ERROR_CODES } from './types'; import { IS_EXTENSION, TON_TOKEN_SLUG } from '../../config'; import { parseAccountId } from '../../util/account'; import { isValidLedgerComment } from '../../util/ledger/utils'; import { logDebugError } from '../../util/logs'; +import { fetchJsonMetadata } from '../../util/metadata'; import blockchains from '../blockchains'; import { parsePayloadBase64 } from '../blockchains/ton'; import { fetchKeyPair } from '../blockchains/ton/auth'; @@ -43,11 +43,12 @@ import { fetchStoredAccount, fetchStoredAddress, fetchStoredPublicKey, getCurrentAccountId, getCurrentAccountIdOrFail, } from '../common/accounts'; import { createDappPromise } from '../common/dappPromises'; -import { createLocalTransaction, isUpdaterAlive } from '../common/helpers'; +import { isUpdaterAlive } from '../common/helpers'; import { - base64ToBytes, bytesToBase64, handleFetchErrors, sha256, + base64ToBytes, bytesToBase64, sha256, } from '../common/utils'; import * as apiErrors from '../errors'; +import { ApiServerError } from '../errors'; import { callHook } from '../hooks'; import { activateDapp, @@ -57,8 +58,10 @@ import { deleteDapp, findLastConnectedAccount, getDappsByOrigin, - isDappConnected, updateDapp, + isDappConnected, + updateDapp, } from '../methods/dapps'; +import { createLocalTransaction } from '../methods/transactions'; import * as errors from './errors'; import { BadRequestError } from './errors'; import { isValidString, isValidUrl } from './utils'; @@ -85,6 +88,11 @@ export async function connect( id: number, ): Promise { try { + onPopupUpdate({ + type: 'dappLoading', + connectionType: 'connect', + }); + const { origin } = await validateRequest(request, true); const dapp = { ...await fetchDappMetadata(message.manifestUrl, origin), @@ -208,6 +216,11 @@ export async function sendTransaction( message: SendTransactionRpcRequest, ): Promise { try { + onPopupUpdate({ + type: 'dappLoading', + connectionType: 'sendTransaction', + }); + const { origin, accountId } = await validateRequest(request); const txPayload = JSON.parse(message.params[0]) as TransactionPayload; @@ -220,7 +233,7 @@ export async function sendTransaction( const { network } = parseAccountId(accountId); const account = await fetchStoredAccount(accountId); - const isLedger = !!account?.ledger; + const isLedger = !!account.ledger; await openExtensionPopup(true); @@ -285,12 +298,12 @@ export async function sendTransaction( const fromAddress = await fetchStoredAddress(accountId); const successTransactions = transactionsForRequest.slice(0, successNumber!); - successTransactions.forEach(({ amount, resolvedAddress, payload }) => { + successTransactions.forEach(({ amount, normalizedAddress, payload }) => { const comment = payload?.type === 'comment' ? payload.comment : undefined; - createLocalTransaction(onPopupUpdate, accountId, { + createLocalTransaction(accountId, { amount, fromAddress, - toAddress: resolvedAddress, + toAddress: normalizedAddress, comment, fee: checkResult.fee!, slug: TON_TOKEN_SLUG, @@ -315,8 +328,10 @@ export async function sendTransaction( code = err.code; errorMessage = err.message; displayError = err.displayError; + } else if (err instanceof ApiServerError) { + displayError = err.displayError; } else { - displayError = ApiTransactionError.Unexpected; + displayError = ApiCommonError.Unexpected; } if (onPopupUpdate && isUpdaterAlive(onPopupUpdate) && displayError) { @@ -365,7 +380,11 @@ async function checkTransactionMessages(accountId: string, messages: Transaction const checkResult = await ton.checkMultiTransactionDraft(accountId, preparedMessages); if ('error' in checkResult) { - handleDraftError(checkResult.error); + onPopupUpdate({ + type: 'showError', + error: checkResult.error, + }); + throw new errors.BadRequestError(checkResult.error); } return { @@ -382,30 +401,30 @@ function prepareTransactionForRequest(network: ApiNetwork, messages: Transaction payload: rawPayload, stateInit, }) => { - const isInitialized = await ton.isWalletInitialized(network, address); - const resolvedAddress = toBase64Address(address); - + const isActiveContract = await ton.isAddressInitialized(network, address); // Force non-bounceable for non-initialized recipients - const toAddress = isInitialized ? resolvedAddress : toBase64Address(address, false); + const toAddress = toBase64Address(address, isActiveContract); + // Fix address format for `waitTxComplete` to work properly + const normalizedAddress = toBase64Address(address); const payload = rawPayload ? await parsePayloadBase64(network, toAddress, rawPayload) : undefined; if (isLedger && payload) { if ( !LEDGER_SUPPORTED_PAYLOADS.includes(payload.type) || (payload.type === 'comment' && !isValidLedgerComment(payload.comment)) - || (payload.type === 'transfer-nft' && !!payload.forwardPayload) + || (payload.type === 'nft:transfer' && !!payload.forwardPayload) ) { throw new BadRequestError('Unsupported payload', ApiTransactionError.UnsupportedHardwarePayload); } } return { - resolvedAddress, toAddress, amount, rawPayload, payload, stateInit, + normalizedAddress, }; }, )); @@ -527,10 +546,9 @@ function buildTonProofReplyItem(proof: ApiTonConnectProof, signature: string): T export async function fetchDappMetadata(manifestUrl: string, origin?: string): Promise { try { - const response = await fetch(manifestUrl); - handleFetchErrors(response); + const data = await fetchJsonMetadata(manifestUrl); - const { url, name, iconUrl } = await response.json(); + const { url, name, iconUrl } = await data; if (!isValidUrl(url) || !isValidString(name) || !isValidUrl(iconUrl)) { throw new Error('Invalid data'); } @@ -569,14 +587,6 @@ async function validateRequest(request: ApiDappRequest, skipConnection = false) return { origin, accountId }; } -function handleDraftError(error: ApiTransactionDraftError) { - onPopupUpdate({ - type: 'showError', - error, - }); - throw new errors.BadRequestError(error); -} - async function openExtensionPopup(force?: boolean) { if (!IS_EXTENSION || (!force && onPopupUpdate && isUpdaterAlive(onPopupUpdate))) { return false; diff --git a/src/api/tonConnect/sse.ts b/src/api/tonConnect/sse.ts index 1f28dae2..5e5164db 100644 --- a/src/api/tonConnect/sse.ts +++ b/src/api/tonConnect/sse.ts @@ -3,7 +3,7 @@ import type { ConnectEvent, ConnectRequest, DeviceInfo, - DisconnectRpcResponse, + DisconnectEvent, RpcRequests, } from '@tonconnect/protocol'; import nacl, { randomBytes } from 'tweetnacl'; @@ -27,6 +27,8 @@ type SseDapp = { origin: string; } & ApiSseOptions; +type ReturnStrategy = 'back' | 'none' | string; + const BRIDGE_URL = 'https://tonconnectbridge.mytonwallet.org/bridge'; const TTL_SEC = 300; const NONCE_SIZE = 24; @@ -34,16 +36,18 @@ const NONCE_SIZE = 24; let sseEventSource: EventSource | undefined; let sseDapps: SseDapp[] = []; -export async function startSseConnection(url: string, deviceInfo: DeviceInfo) { - await waitLogin(); - +export async function startSseConnection(url: string, deviceInfo: DeviceInfo): Promise { const params = new URL(url).searchParams; + const ret = params.get('ret') as ReturnStrategy | null; + + if (!ret || !params.get('r')) { + return ret ?? undefined; + } + const version = Number(params.get('v') as string); const appClientId = params.get('id') as string; const connectRequest = JSON.parse(params.get('r') as string) as ConnectRequest; - const ret = params.get('ret') as 'back' | 'none' | string | null; - const { origin } = await tonConnect.fetchDappMetadata(connectRequest.manifestUrl); logDebug('SSE Start connection:', { @@ -65,6 +69,8 @@ export async function startSseConnection(url: string, deviceInfo: DeviceInfo) { }, }; + await waitLogin(); + const result = await tonConnect.connect(request, connectRequest, lastOutputId) as ConnectEvent; if (result.event === 'connect') { result.payload.device = deviceInfo; @@ -72,11 +78,11 @@ export async function startSseConnection(url: string, deviceInfo: DeviceInfo) { await sendMessage(result, secretKey, clientId, appClientId); - if (result.event === 'connect_error') { - return; + if (result.event !== 'connect_error') { + await resetupSseConnection(); } - void resetupSseConnection(); + return ret; } export async function resetupSseConnection() { @@ -136,9 +142,10 @@ export async function sendSseDisconnect(accountId: string, origin: string) { const { secretKey, clientId, appClientId } = sseDapp; const lastOutputId = sseDapp.lastOutputId + 1; - const response: DisconnectRpcResponse = { - id: lastOutputId.toString(), - result: {}, + const response: DisconnectEvent = { + event: 'disconnect', + id: lastOutputId, + payload: {}, }; await sendMessage(response, secretKey, clientId, appClientId); diff --git a/src/api/types/activity.ts b/src/api/types/activity.ts index b996d606..5b708026 100644 --- a/src/api/types/activity.ts +++ b/src/api/types/activity.ts @@ -1,8 +1,15 @@ +import type { ApiSwapHistoryItem } from './backend'; import type { ApiTransaction } from './misc'; export type ApiTransactionActivity = ApiTransaction & { id: string; kind: 'transaction'; + shouldHide?: boolean; }; -export type ApiActivity = ApiTransactionActivity; +export type ApiSwapActivity = ApiSwapHistoryItem & { + kind: 'swap'; + shouldHide?: boolean; +}; + +export type ApiActivity = ApiTransactionActivity | ApiSwapActivity; diff --git a/src/api/types/backend.ts b/src/api/types/backend.ts new file mode 100644 index 00000000..23422136 --- /dev/null +++ b/src/api/types/backend.ts @@ -0,0 +1,137 @@ +// Decentralized swap of TON and tokens +export type ApiSwapEstimateRequest = { + from: string; + to: string; + slippage: number; + fromAmount?: string; + toAmount?: string; +}; + +export type ApiSwapEstimateResponse = ApiSwapEstimateRequest & { + toAmount: string; + fromAmount: string; + toMinAmount: string; + networkFee: number; + realNetworkFee: number; + swapFee: string; + swapFeePercent: number; + impact: number; + dexLabel: string; +}; + +export type ApiSwapBuildRequest = Omit & { + fromAddress: string; +}; + +export type ApiSwapTransfer = { + toAddress: string; + amount: string; + payload: string; +}; + +export type ApiSwapBuildResponse = { + id: string; + request: ApiSwapBuildRequest; + transfers: ApiSwapTransfer[]; +}; + +// Swap assets and history +export type ApiSwapAsset = { + name: string; + symbol: string; + blockchain: string; + slug: string; + decimals: number; + image?: string; + contract?: string; + keywords?: string[]; +}; + +export type ApiSwapTonAsset = ApiSwapAsset & { + blockchain: 'ton'; +}; + +export type ApiSwapPairAsset = { + symbol: string; + slug: string; + contract?: string; + isReverseProhibited?: boolean; +}; + +export type ApiSwapHistoryItem = { + id: string; + timestamp: number; + lt?: number; + from: string; + fromAmount: string; + to: string; + toAmount: string; + networkFee: number; + swapFee: string; + status: 'pending' | 'completed' | 'failed' | 'expired'; + txId?: string; + cex?: { + payinAddress: string; + payinExtraId?: string; + status: ApiSwapCexTransactionStatus; + transactionId: string; + }; +}; + +// Cross-chain centralized swap +type ApiSwapCexTransactionStatus = 'new' | 'waiting' | 'confirming' | 'exchanging' | 'sending' | 'finished' +| 'failed' | 'refunded' | 'hold' | 'overdue' | 'expired'; + +export type ApiSwapCexEstimateRequest = { + from: string; + fromAmount: string; + to: string; +}; + +export type ApiSwapCexEstimateResponse = { + from: string; + fromAmount: string; + to: string; + toAmount: string; + swapFee: string; + // additional + fromMin: string; + fromMax: string; +}; + +export type ApiSwapCexCreateTransactionRequest = { + from: string; + fromAmount: string; + fromAddress: string; // Always TON address + to: string; + toAddress: string; // TON or other crypto address + payoutExtraId?: string; + swapFee: string; // from estimate request + networkFee?: number; // only for sent TON +}; + +export type ApiSwapCexCreateTransactionResponse = { + request: ApiSwapCexCreateTransactionRequest; + swap: ApiSwapHistoryItem; +}; + +// Staking +export type ApiStakingCommonData = { + liquid: { + currentRate: number; + nextRoundRate: number; + collection?: string; + apy: number; + available: string; + }; + round: { + start: number; + end: number; + unlock: number; + }; + prevRound: { + start: number; + end: number; + unlock: number; + }; +}; diff --git a/src/api/types/errors.ts b/src/api/types/errors.ts index 2affec70..ebe550dc 100644 --- a/src/api/types/errors.ts +++ b/src/api/types/errors.ts @@ -1,8 +1,12 @@ +export enum ApiCommonError { + Unexpected = 'Unexpected', + ServerError = 'ServerError', +} + export enum ApiTransactionDraftError { InvalidAmount = 'InvalidAmount', InvalidToAddress = 'InvalidToAddress', InsufficientBalance = 'InsufficientBalance', - Unexpected = 'Unexpected', DomainNotResolved = 'DomainNotResolved', WalletNotInitialized = 'WalletNotInitialized', UnsupportedHardwarePayload = 'UnsupportedHardwarePayload', @@ -15,7 +19,6 @@ export enum ApiTransactionError { InsufficientBalance = 'InsufficientBalance', UnsuccesfulTransfer = 'UnsuccesfulTransfer', UnsupportedHardwarePayload = 'UnsupportedHardwarePayload', - Unexpected = 'Unexpected', } -export type ApiAnyDisplayError = ApiTransactionDraftError | ApiTransactionError; +export type ApiAnyDisplayError = ApiCommonError | ApiTransactionDraftError | ApiTransactionError; diff --git a/src/api/types/index.ts b/src/api/types/index.ts index e7bf33dc..e2d5a267 100644 --- a/src/api/types/index.ts +++ b/src/api/types/index.ts @@ -2,5 +2,6 @@ export * from './updates'; export * from './misc'; export * from './payload'; export * from './errors'; +export * from './backend'; export * from './storage'; export * from './activity'; diff --git a/src/api/types/misc.ts b/src/api/types/misc.ts index ec135739..f362aece 100644 --- a/src/api/types/misc.ts +++ b/src/api/types/misc.ts @@ -27,6 +27,7 @@ export interface ApiBaseToken { image?: string; isPopular?: boolean; keywords?: string[]; + cmcSlug?: string; } export interface ApiToken extends ApiBaseToken { @@ -52,7 +53,7 @@ export interface ApiAddressInfo { } export type ApiTxIdBySlug = Record; -export type ApiTransactionType = 'stake' | 'unstake' | 'unstakeRequest' | undefined; +export type ApiTransactionType = 'stake' | 'unstake' | 'unstakeRequest' | 'swap' | undefined; export interface ApiTransaction { txId: string; @@ -81,35 +82,46 @@ export interface ApiNft { collectionName?: string; collectionAddress?: string; isOnSale: boolean; + isHidden?: boolean; } export type ApiHistoryList = Array<[number, number]>; export type ApiTokenSimple = Omit; -export interface ApiPoolState { - startOfCycle: number; - endOfCycle: number; - lastApy: number; - minStake: number; -} +export type ApiStakingType = 'nominators' | 'liquid'; -export interface ApiStakingState { +export type ApiStakingState = { + type: 'nominators'; amount: number; pendingDepositAmount: number; isUnstakeRequested: boolean; +} | { + type: 'liquid'; + tokenAmount: string; + amount: number; + unstakeRequestAmount: number; +} | { + type: 'empty'; +}; + +export interface ApiNominatorsPool { + address: string; + apy: number; + start: number; + end: number; } export interface ApiBackendStakingState { - poolAddress: string; balance: number; totalProfit: number; - poolState: ApiPoolState; - profitHistory: { - timestamp: number; - profit: number; - }[]; + nominatorsPool: ApiNominatorsPool; } +export type ApiStakingHistory = { + timestamp: number; + profit: number; +}[]; + export interface ApiDappPermissions { isAddressRequired?: boolean; isPasswordRequired?: boolean; @@ -158,3 +170,11 @@ export interface ApiSignedTransfer { } export type ApiLocalTransactionParams = Omit; + +export type ApiBaseCurrency = 'USD' | 'EUR' | 'RUB' | 'CNY' | 'BTC' | 'TON'; + +export enum ApiLiquidUnstakeMode { + Default, + Instant, + BestRate, +} diff --git a/src/api/types/payload.ts b/src/api/types/payload.ts index 8d87f188..cf2c0780 100644 --- a/src/api/types/payload.ts +++ b/src/api/types/payload.ts @@ -8,8 +8,8 @@ export type ApiEncryptedCommentPayload = { encryptedComment: string; }; -export type ApiTransferNftPayload = { - type: 'transfer-nft'; +export type ApiNftTransferPayload = { + type: 'nft:transfer'; queryId: string; newOwner: string; responseDestination: string; @@ -21,8 +21,8 @@ export type ApiTransferNftPayload = { nftName?: string; }; -export type ApiTransferTokensPayload = { - type: 'transfer-tokens'; +export type ApiTokensTransferPayload = { + type: 'tokens:transfer'; queryId: string; amount: string; destination: string; @@ -34,8 +34,8 @@ export type ApiTransferTokensPayload = { slug: string; }; -export type ApiTransferTokensNonStandardPayload = { - type: 'transfer-tokens:non-standard'; +export type ApiTokensTransferNonStandardPayload = { + type: 'tokens:transfer-non-standard'; queryId: string; amount: string; destination: string; @@ -48,9 +48,39 @@ export type ApiUnknownPayload = { base64: string; }; +export type ApiTokensBurnPayload = { + type: 'tokens:burn'; + queryId: string; + amount: string; + address: string; + customPayload?: string; + // Specific to UI + slug: string; + isLiquidUnstakeRequest: boolean; +}; + +export type ApiLiquidStakingDepositPayload = { + type: 'liquid-staking:deposit'; + queryId: string; +}; + +export type ApiLiquidStakingWithdrawalNftPayload = { + type: 'liquid-staking:withdrawal-nft'; + queryId: string; +}; + +export type ApiLiquidStakingWithdrawalPayload = { + type: 'liquid-staking:withdrawal'; + queryId: string; +}; + export type ApiParsedPayload = ApiCommentPayload | ApiEncryptedCommentPayload -| ApiTransferNftPayload -| ApiTransferTokensPayload -| ApiTransferTokensNonStandardPayload -| ApiUnknownPayload; +| ApiNftTransferPayload +| ApiTokensTransferPayload +| ApiTokensTransferNonStandardPayload +| ApiUnknownPayload +| ApiTokensBurnPayload +| ApiLiquidStakingDepositPayload +| ApiLiquidStakingWithdrawalPayload +| ApiLiquidStakingWithdrawalNftPayload; diff --git a/src/api/types/storage.ts b/src/api/types/storage.ts index bfa1e379..33202bba 100644 --- a/src/api/types/storage.ts +++ b/src/api/types/storage.ts @@ -1,6 +1,8 @@ import type { ApiLedgerDriver, ApiWalletVersion } from './misc'; -export interface ApiAccountInfo { +export interface ApiAccount { + address: string; + publicKey: string; version?: ApiWalletVersion; ledger?: { index: number; @@ -8,6 +10,9 @@ export interface ApiAccountInfo { deviceId?: string; deviceName?: string; }; + lastFinishedSwapTimestamp?: number; + authToken?: string; + isInitialized?: boolean; } export interface ApiDappMetadata { diff --git a/src/api/types/updates.ts b/src/api/types/updates.ts index 80fa8d93..7a5b064a 100644 --- a/src/api/types/updates.ts +++ b/src/api/types/updates.ts @@ -1,15 +1,17 @@ import type { ApiTonConnectProof } from '../tonConnect/types'; import type { ApiActivity, ApiTransactionActivity } from './activity'; +import type { ApiStakingCommonData, ApiSwapAsset } from './backend'; import type { ApiAnyDisplayError } from './errors'; import type { ApiBackendStakingState, + ApiBaseCurrency, ApiDappTransaction, ApiNft, ApiStakingState, ApiToken, } from './misc'; import type { ApiParsedPayload } from './payload'; -import type { ApiDapp } from './storage'; +import type { ApiAccount, ApiDapp } from './storage'; export type ApiUpdateBalance = { type: 'updateBalance'; @@ -18,6 +20,12 @@ export type ApiUpdateBalance = { balance: string; }; +export type ApiUpdateBalances = { + type: 'updateBalances'; + accountId: string; + balancesToUpdate: Record; +}; + export type ApiUpdateNewActivities = { type: 'newActivities'; accountId: string; @@ -33,6 +41,12 @@ export type ApiUpdateNewLocalTransaction = { export type ApiUpdateTokens = { type: 'updateTokens'; tokens: Record; + baseCurrency: ApiBaseCurrency; +}; + +export type ApiUpdateSwapTokens = { + type: 'updateSwapTokens'; + tokens: Record; }; export type ApiUpdateCreateTransaction = { @@ -53,28 +67,16 @@ export type ApiUpdateCreateSignature = { dataHex: string; }; -export type ApiUpdateTxComplete = { - type: 'updateTxComplete'; - accountId: string; - toAddress: string; - amount: string; - txId: string; - localTxId: string; -}; - export type ApiUpdateShowError = { type: 'showError'; error?: ApiAnyDisplayError; }; -export type ApiUpdateStakingState = { - type: 'updateStakingState'; +export type ApiUpdateStaking = { + type: 'updateStaking'; accountId: string; + stakingCommonData: ApiStakingCommonData; stakingState: ApiStakingState; -}; - -export type ApiUpdateBackendStakingState = { - type: 'updateBackendStakingState'; backendStakingState: ApiBackendStakingState; }; @@ -111,6 +113,11 @@ export type ApiUpdateDappDisconnect = { origin: string; }; +export type ApiUpdateDappLoading = { + type: 'dappLoading'; + connectionType: 'connect' | 'sendTransaction'; +}; + export type ApiUpdatePrepareTransaction = { type: 'prepareTransaction'; toAddress: string; @@ -145,23 +152,31 @@ export type ApiUpdateNftPutUpForSale = { export type ApiNftUpdate = ApiUpdateNftReceived | ApiUpdateNftSent | ApiUpdateNftPutUpForSale; +export type ApiUpdateAccount = { + type: 'updateAccount'; + accountId: string; + partial: Partial; +}; + export type ApiUpdate = ApiUpdateBalance + | ApiUpdateBalances | ApiUpdateNewActivities | ApiUpdateNewLocalTransaction | ApiUpdateTokens + | ApiUpdateSwapTokens | ApiUpdateCreateTransaction | ApiUpdateCreateSignature - | ApiUpdateTxComplete - | ApiUpdateStakingState + | ApiUpdateStaking | ApiUpdateActiveDapp | ApiUpdateDappSendTransactions | ApiUpdateDappConnect | ApiUpdateDappDisconnect - | ApiUpdateBackendStakingState + | ApiUpdateDappLoading | ApiUpdatePrepareTransaction | ApiUpdateShowError | ApiUpdateNfts - | ApiNftUpdate; + | ApiNftUpdate + | ApiUpdateAccount; export type OnApiUpdate = (update: ApiUpdate) => void; diff --git a/src/assets/blockchain/chain_avalanche.png b/src/assets/blockchain/chain_avalanche.png new file mode 100644 index 0000000000000000000000000000000000000000..2faa5a9fa9fa4bb86ece078460900c7e7e3f74af GIT binary patch literal 1810 zcmYL~X;hPU7RLX16OuPbHlPAQNRUO;f`~aR>V!8K1qx-;mQ4jJODb3qp`fzl4LF06 zu&97ksSy~BS`ZOoEJ6|oFbXYX4scjx8CeXdY%)ODGHJi`oO|x?x%b@X>vOO8`+AUc z>AC;_l9wkp5CDu;VgQWQBJP*qV_NL;_c?Z0%P|B|A;^0KnL;=!j=Q#|{_G2d*EXi6 zG#br{RQfSI0$=rRM0*V<1^ONZR4Z<@Q2IXjdXN+Ve7+Z#b1ZdWyeuxy(n2r$f(aH~ z3s1@Xc-s3Buc*|*pPEU?iAs5X)n=$}P(;~##q;-vp_}5L&TlO=%vw91=D9tjeG4mA z{V(>)O>TQi*OxofzM8+*-r4n~a)@+Qvv=7^zC&+)+b(5zaQcPat~yV9DhLf7O~r>L z+zKOxN?AeWwkIBY`Cy;Z#g&PCs4r(r+lyVY?V+?z4oufpkaylVHT(p+pFi;e6A z@um>zeidjNV_XdJVZR1U;E?z0UcKn+SV2bj*J$wxZ{Sa%L;X8I*YEvi#OPBv!Nm^h zcoi9Lvp7`nW)jY(RNK^+AXMN1P5YfgigDSq{2U~YV!jjjtJuaSG2ilgbZWwH%E`D;7cyO7M&5Ti z3mxT05m$^15PiT4i)9|wk=?;`j^I=8{csyqh>#32YynM?6ocP#HahUCYN8~+f)3u@ zBl7M$AymO}%v=NBgr>;n;3A`3n3>+dhAD){XN50uj)%ZHkx#UhW7Gz~0M>3}fe*9H zmhZIG6lRwVSMCP|t>%4xdEhPC)&f#P{4MxP5Qv=EpRmmgP2fB+K@;Th@ubDK;1(^X zwPqj;yKyVa$&%`79DjtF=n!fMa_mEdNWtx3A;R<(>H<=Y3Cct2ReWY+JPT5STnJ2e zOR(h_t%+2#6O7JWkp;v}u=r9EpHY1SKW9l5RSC67sQV^hYKDr0*N6TX#Em#%v~}hx z9RHzsi|<();AIyv_d$0u8v^OF6vP2e$!dYq4l0SHQKGo&#hEw0_K8SW$qaG9a_-sF zZ{4vjkScD(ert(@wh{_*ohjFHlL+flFxS|g=J0Dm60Wyr%|os>-JVcJ+5djg9>ZU{ zN3dFmroq#f%nJ(#w^Q|wU{`4HYS_>E-rZ$$&*k*l)RXMv@A=}Uv49XiA+jouOATGu z8dF}dtm@!O#tWO&=*e0bcWSe!)YDLw7QVLrAn|8wpgo+6#3`edEyfxqrm9 z@86h?uhflbetc?a;(NaY*^e*fal>+)eG=jjL-8-zZAq=`w`9E&K{uD>F6uL_%03#L zQLhWs;h7e4X??PT-UxE_dTuCSAw9 ze0%p{nJM~NUvd7`blw+Oe)A8ofVEsU^&|o~U`%6>9r?ZHXC-$Yg3`YoPcPqGz6fq^ zba#8_XADmb;%V4>u#TO}%H6>X(L_ zJ!Z$u!iR;edg$l7*6d&%dJ%B!s>5}=4(fZ(NTY5+Kq&!8{Z2p?>4HB;8C_O~%U)Bb zv*k_P4qdPYF>>+CI^`(Ln$GG^7m1zBs8(1qF9GgOH(?0L!1D+}>%;;9Js1k13&xv8 zeJn8QroeYMGN|46FnnG&;2S#tteJuw%yKstAGXZKEF6$M9*HvKI)ClBmEW231f(7L zE2I8MEVgvhC`Y-1nSsK-leRpVe`Iw#6^+?Sk_CPMB>{1WW&dL=+sK4_FVz5!@k^V_XxrN0is46Gynhq literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_bitcoin.png b/src/assets/blockchain/chain_bitcoin.png new file mode 100644 index 0000000000000000000000000000000000000000..c6dd1be8dcf8429f700c541ba98643cb426909d8 GIT binary patch literal 2186 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 literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_bnb.png b/src/assets/blockchain/chain_bnb.png new file mode 100644 index 0000000000000000000000000000000000000000..ebacc0ac7abae9a05f51b7cea4f9f9cfcaf0bf08 GIT binary patch literal 2557 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 S4A*s82Auc0*jJJn3I78m({)t< literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_cosmos.png b/src/assets/blockchain/chain_cosmos.png new file mode 100644 index 0000000000000000000000000000000000000000..11a863bfa99b8a8954c1e3d2d5cc1d9da8482bc0 GIT binary patch literal 4162 zcmWNUX*AS*6vu!6S&@BT!dOBiLyBxOwoI~Bl1M09@nlPgm_dvZl2FMOrAR7kwn6r6 zp@huXQkLw?U}hfY+;cwnd(XXZzAx@QH{Qm|jEh5@0|3BvnqYDc0BApj02_QC-o^H| z`@q^**qZM1s5)UkRMiP;SOOMnc2e)czI>7Wp|ZODl2eGsjX2-HWOGYTg~NstQYSPU zJUsVj(6`Q6od;tHwRVyWvoK{C06d>hn;6=LKw}wkcjRR_qoe)l^-no#Sp!$rg6^rz zufXW<^F=xcAzL#)$ykB2?+OG1i&M|3@Tqro?I2PGgvuLo%D>gD8D_j0X5}okAzj~Y zvi)oNJ?9iozH7-_a--6Q>~6eqTjhy+*oHsP?T$JJe)C%9@`HkNX^BzE4m##V4#bY$ zyjA9E^XXwHCQ~?PPf|5QEi&?eygW-M(Rk7SH|=SnVIV{_kB)846NdtKx0GnuZt$%6TQbA)t&77xY7m( zj-+3PPo7LIWor8lBV|oXKQj*ujZ7S`Dud?m4m#L1aC5NO+(GWpx-o23gVyJ;BLh7Z^Yc2*n0hCY#}_L+m=rA~{ZPBM zoe`G=Z=G=|I*Ac>t6iSYu?-BoOM0(^Mjzmf`Kn38LQG5oN`Ah_#P_ zvhkWwNR%M8Kz=%?GtVnCSKNUa!YSHcQg=fiCF! zpHc6&1bOCSgbH=#;6wq3JbhinIsR8mcr?s+Twt*}iCxLE!4(Zlmq?xg1zMQSND5J) zW+GHV-Mu|naNpUT%*}kMX*~u*{1#@@q$`kkEEu1fluj}8=<%y|y?n1}U=Tu!(HYdu zo<)zXb$Jh1h(b60ht;=d*1#x*6Le^Rx<(`n&8sEBN`G2?W+1|CTY;D=KxV1@t)g5U zY)*--b~BH(LC(7q*~t%G1N1ERGLM>y*f&Kp9G^9D&pg2EoGt9Uy~`(E7V|fM;<(5* zhwukkuE&|Xa7uk}Cy<|CEVg`etk-~A|wF{Dhs>GCD~-zJrv+z^hBcfu?I zj|h>b1*PvEt<@4iydyUo1gmG9wxrjNmddC5w@8Xoz-Dj}rjvMz+fPn1zu+t1!XYXu ztjbE&nm1MvY@Y$GCI~@_hSSVoCr^K6!M;`_r5{ zSE&8$m5YYc=GJg>Lfe4n^QNLy?sD~L6D6gOjuOQ8Jm7NfEr7P12=HjO`!Z=myN<8n zew(xifdrK-EqngsrMF-EQeTVK82%>?BqRU*bLYg1#|6C9tjb^7`m#p-8~xERRGjEW z*W1Qj6&c=f>_3wU(>+S;fA#%3p`^?8VcTWHlA!4C3q!@W;;CO%sTM3<_k*XR+#%76 z2Ug!s^w#0Mr>CD?VduAa^NFie+ze*vXD$F8T3ej8{)uC2t?wP%ShwX2ZwM%ORLN48 z$XFKCI**=VpY{uHvMfC{7&x}n=8Sk?)Qd@TpAC zzY(OH+l{T`5)c6{=SS3o*|ylsu~Xxet-Bn)jT(~Qob?BURklH;l|zCfYWF0g)GYZ+ z6Bu_9Hx_nP!6`A7w872hy<|#Ony)%6gQr`+=-O`G$1}J4^`Q4UK%t5cmO(}Ll{%~0 zA{-H$vP31`_Hg54Hj3Dk%|QOX1hB>3>}CgfB_DXj>Iwgb0K4yQd)?@lg=&J`UUCoz zCYBY~occKj*$LedPRTHHgx#&_OVSh#v>fkxO<|ZS;M(1Y610&i9nX1(aM2TKY5dJ1 z6akN}2zjWVpET-A&k_{=!UhqPwBYq3gCC?%)7}dGGzZ^Nz#{pi>}6Zyhs2o z45I8oyeFOmkG2T6#-glFC`=fPIk?uJH(b_*6#+apz5*O^cud7uAXXi-2HIUV(R3aP zVH9^_opnLAon$F8>;VzaI{KI!a3T(YfYyk*TP0>}zI|s};0Wf?Bv;PLWj$iV_7w#sJOFZO-R%Ra;X9` z&Y%uOWHJWRN`4*SZ>Lmamj};nt;2xyw3QR+s_wE$lcSNg);^2(@_C%t+Y$h23P3MY zG3#R6qpjYsYtWHUF29!(81QmWRvaenzI2Xk(ftpg^T_= z>lO}AYIj?>qcB-ae!K$bb}8GDD*YQm%&-pvCh@P?X*Jn!nRu!6u75ri>rU zfVi{<@~}D6p5e}3b&_80V6rXfeBsUu^m2h@i%|I2Xp*3ToJXZG*G^uZ6z8}R^mh%^ z_rQpmZy_$6bc!4rV+mW9$VLBS$pBsy;g?lGHW*e-<^#&P*HAxMn#46zT8?<(s!uHB z!$({Uwz|Mg?+#rZKvQ&hFgO3u-g44}=K#nXZ+&oO?jcy8;e2A84!(*~+yJFBa%~Cv zYD;}UI0VGuHYmLPqpLUxisp@3D`@|=VE&o`KLbpd0Ro$iB3XAsqD&0h{y6)jnb+4O zF33$K(3TV-=2qAoU9u)mWSS^tq1c`*)#>rUfW!8)=uh);-3aQ!rCW6waxve$}W%|a2)^2R4r~MZPfyGq6XUNCamv|^I?@S9X6GIGR zAhPuH&ca^!AGP?F7k*{(ZebQKL$ouivcm>0mb}J7v!_72+WpmwQP}N3aP~0={loE! z0Opi0;-*wd(WnW03XTKMl?8LjWbj+@Mrw?1A)n56*E9<>gDu~M#MbU91!L&}aJOD$~9W4)gojbGe zd@lUXQj&4{To@mi#5kCDKshGZ(WSH+O(fz`;hA7boDaC*L z9b>eGw$GEMzol5;d!amh7*2>(1%l|`4bcq<@R1w3EdE(@F+0pqKh3T|KJ&O8@lJKbVd%dyvWahTTGzw-^(T8~D!#2)&VsTa zObLUTq1EOP^=A;E(@I8o%0S{I^3{q03uw8=LN-e%blrKQZ`n;dmX<|S-{g**1>_k;b zdN+;TB}2Tq`dJ;z?CGd%bfRzEZo$TF|2u!D)&ZpEavWtVv}Y0-VVCU}`g=ZD#_n5{=7$33 zRv7+3vFf6?voUm(eD(17Xp2COPpCn>kkU#7uosh6kew<04fUB4-eY3T)8fC6n;R*V zotWkI>N$(tR1;APL9TG-bA#rgq1}4$jwg0)*Z~VZqJBTR#?#l|G~bo|o|j9-i@?{C z*O+%d@-lO@F_&MZj#p!z`$dIef8*r7~jb_!D`yl<7Z;9m1 zdqtHSvdfnbWcBXtD%$y~=x9NUjaA6%p((rS2&y~qKLA^wx(IR9VC2YQSS83GcNmba z31kG0R%{6Tm}>sObz2R>*X=o(GKym!nlX_Z&S1xIk0BNC%Q() z%38*TlR!bQBE|(K*(jx#qWAT*dy+y$U$CITi+jSfV^<0r2VdOr zG!5cfE8t~T=-sp8!K=Y%)7uBqmusPI1u|-xefA<^>FTSd7J#(Gj9$+gP3v(cZHs1n zKB3Q4LUR#69$jB#eqWGF1kB?=3jQO^d+HE;0w|G5^TAv`KOCLsXz-jPsJ&q~Z=FmM zKiCkcl3RsWqc&!OiaQJo57OC^L2)*|IG_%-vZwMEPgt!4s>B`Tj5(+r?YQ4Q{G@=~ zJWp3Sf1?Hk*jq(%U%^`&B47c1lK{Q#Ng>L{NV=R+=2S8ogev5l ziBe!+>Y)1I8Sqw^OUNs9Op&Kf&O>D5h&IoH?1Ro9I2vMdz`#UEY!52dUsA^AvMtOT zHHoqxoZ~QHo_XnaA5n6N?+<Q ztHG8#B4s3&W@8DAlGJbofxEt2iF*bAi*DAiSY}_6ppaH)O=)W)3*_C{crs8OPI;lckS?Tc0=R&FYMUUo11M~11a65ayGWSDI4 z*5MC(?;%X}#8;Hdd|&#&SA`DJ^v(4xbDua#x{zLeD;=Dd-_plZ;|DI{LK?r7lE!bP zr14uRY5Z2Q-ETNu)tkufG}T^|!dN2_&&Y zv<{}>p=@$~{;dY*=igGclNolHw!w5fp3iH>pRou=0n8^Li3YwZRJ@3i=5I!}{`QPC ze>2kj%}Db%BhBB8G=I|*_8;8|2wLO$O*j&-mgBtl55vo77G5vrcAR4YPPPE|jB^1r z2cO8F94 z9ZtY}6|`{za6>~P62J?94318j68L!m4~&c87#0N(`z8Zz)G=6X2zo#Q*n5y}A7!A8 z5rCV0lz}Z0hQT)hybqk$kTe+EXBk+BT!o7b1VwGIxz}j|aT{nO;y^YO612h7u^xyA zg=}#3@&stW1|bz7_GJki_8ysm-EOMBpr*EP_5DCiF` zi|CsQ;@=lN+$A*u&>(}J70F5Dlre)nNnSkH&o11>2WafPc0c>gT=>*Kp zf2q&i2e~hE(?60-y#NS+RsbcN4-X+gEe#H60(b&!ngtMmfT#&6W>|YhdcaUVr^&6qvoT*6#q;BU2|@{q-A1X3ty&){hauSz)3K z8BPF4fr%PyI05+f`H2doh7rJR0pB(O4#bTgy(4oJ`1t_>IN)P-#stxOeB4B<_-M!t zijNw&&U&xL+z@|>^MkYHh(*zli{ID*a~0?@JotE)amwVa3v_D3L;N+a{P~J%qtUbG zZmmUM5TvZxb63q51lix?U;d0yw!x6ZiA!ri57f-*i`QAs1G@)sB8JTMvFj%fU`GrX zDYSlXfKkg-X#GFLphYgS{@VhxZBQ!s|GfzSNI-}7f56zqR>cF^5dw(Y;IQ(C%^z+R znt*`D5630I1>FDcfd~nx!O;ahg#GyRMp(!lCV;pE3=AdY6#lpbsKBp(3jiSjhSxq+ z0C5QjSo>fB#3i5xfw1SIEq~znP7_ce&&CcLzn@(6kEYDWPXX~mA@wj+MMRj%arj@3 z9b`ESgD?OUV2r)X{ZBhfEt5{0z7+i%KrPWWaBk0|ty;LijefOZ~j)W$!osaXJIq6I)9S^zYT1_u6b zr)B}ts@{JBowM2J07#w!!`r;g0TeujfsY8$`6)!_rx2Z=LUeu#(fKJv=O=G)|5d&f zPs54czl_QN3eV{qSku(+24qKVx}{PEX6n{7~*W{jafjI}8=J@yqrbOUUqz7R<+s zTd3i-(TiD(oTq`W5uClDZRQapvx-w!WsVW&RY2yUH@!tf5Wzc@HW@QOZ)vSc-~}?8=Z( z#IzxaK}Atxi@_}4&+pIQIrp5$J@@`|&ppp`?s=YbuXyfqmJnSh3ILFBb#d4O0Q6r9 z0X+O4Ldv}M{f7ikw|$QPF(;Y)|KcJ!JMsT2*J3ut1RErV8{Rl)T@-Itp0=qvdsAcn zrdRi@dP^PJ9=Ly~_8M$G%YH{6@4581KYfmUe{rU9X0&2q=F8ITx0S_-)upLl6N7{O z?^`OO?`E7jbF*+}JQ(zNldU(>5e zI|AmF575lJOt>Vms||6!yiBA4^X8AU$%>9&zT`gj<92yIdikxnnCNq~)faKSf)m8% z$LxoM_B}TCS<{9o;VQI$M!dn@&gjV!#-qQYc2+Cs6 z<{QbtW%YUu^y!WX+GzY@oJ}5tl+Cud4Pi38(5UEIR~Y(`xGS)8eB0j_v*dAA&>?~w3+5YvYacmH z;a>o@hZZ+#lIFX*^MxpQ4pA*S?##CYOAsgy*YKh}sS@nDO$(NKdMk1>79ix`Sq%AJ za&g2JlseFv^vgAN|0)V6!~d@D&A{aeFB2a|NVUfg!s_4GmNuk^vDgn!{q`J{Z3X}IyVy%Pf4nFDRwTcwXiF) zcCe&%^Qn9V#jcq{j_1$socQuAdP0I3lIFQ_kfSHTBh~mHS3;J%7GifTJL##;7I_Vb6bhz3aF=~ zo7=!j>`mZQ%VoKmwR*|k+6r@%_{&%XQwF77yfq%zP*3j4fkp<;aNju2-W-9>{so0n zPw&@uP65qPCwYdpobFl%-5D#W^@eB%+*RM4pEGjO6vh;qy;G4YDbH>lcX0lfj9&BUEWj(KE{EVP(H2Y#@- zm$6=%NUl6J3^$A>3V%+251kzdrDc{omQP`q(bnmF$gC=^|4c zQr{lWgbJi2-7qiV{$bG7^kw$kc?5XWP@X|dVSi#oB@m?^9i_L}cTY`6oG|u*2-_2F zxUpi2+m+B=&zAoX2g3lGlF(FhKT<_V;^gyGdcd6+U(6;kCic3GMw*ijT5n#S*p-X9 z?~m{jnnWZh`0GZdV31m#oceRSOB@LvTm!QKfnv{Eh!_#i&^l%$%m7PuUtgS%W+>9K z&ke&ZwRvHg;n!c~Zh&9{11{b=8MxOjY1Q{phw9qO8z7!~hn1dZ@fR?N&NBYj`3qpHg zw3Vr}Bybi+j^G4Szp+>lPlK1Q2=>SiW*STW>Q9hM{!H{bEGO_bK{rYgR1nz|8eMtY z7LX^qfobBWB159@XCJ$ZAx3VOJqVEjI;EfP=X5UfE8LbD@*m*LRm{a%-Rr`Zi<#gBxNGi2P z=?|sXm~cZ1WCh-z;hp=yS;Pv1hN6t^W0HhkVAjgk=`&HAIfT#HhgQB+5G~PM_M5&u z2<&KzC`0*7|R=48$^_KxhjXWD_Z zRL4=nH3b6Q2~#%KtM`@cQjd@H_b;@k^n873JQuGm`@CGY-w5n;W}VyW_2M z-$kz%>k7A~=D^2mYGq&g%X#@hOhtNms3o4NZM{wS^{yR;<^Bp{$emzXVG3H8QT30! z80v9W1=ub7DaSA@U+Bb4k_AT3$xq(~a=_iV?tsXR8c>^s8i>~+SSgqqg8BaCn*x%j zLUjSJhQMIs-b(`YAi{ucKAc4sMexi<^5`D;?go-Q`fAp9{Svepz?c#l{ZIFIy@3+6nqm8-&7*-i6PNXYS?p^9b zM%5u4{byB*2^yVAm4a#qn2$vV_wr)}9~N5v7*9U7um@UpG83dx>;tPsUO9NxyYABb z=d_C0*gH#Vi?CGwazYt6;~BGW8qJjtPi;P`nqh?@#O?!Raaf=19otMB<}Pu}JDV7C z?gL8Q`Ny@qMQ<`iD=YR|&Ax_yHts-FnqE!cB|Sv$%v1~!=*w&0h^;@)C`As0T-=(T z`GQjDQ4%ia^OXcpM%QiawDHJo6IQ^=l2g46suU&X6WllI>iO4@Kkr*TAjO0o)H(Us zpS40XGarqhPB|Pl#*^T73n)PY-R}ep7)qNeAzEJS1_z0yG+V*}lM&z(i(MESUy<5^ zURJx1dn@To+xgo-X*+c%dd3IJ5NZIE^{bTfNl^;ir;bGclnr4c>P5ZX6aD~tAzO?Y(e|yI2x$R4e4T8`HE>n)8 zDfDIsh!C61j{l3`-@<;2RgBHIO3j7-gScU-+ZVCRA`=T*QaRIWKV8|*!RTAZk;aRV zN(RKRy#Sb7u(~+2NhO*hLaN7*FgC0H63yW`=!98uH-o}NQAFdpoe@)ugqp%@=-oM~ z$mek3+s?-!mfs#`%6`;$7rXm(S6=r+49`Bk=@sfMw9gwJR5+u?YP47ki_sMXbO^nq zkBTAI+8HLd%s9&1+3Hzz*S(bJ5GX4#ji2xnyq3eS9qol5+Ahb_A{X|);WmMsBcSKs zQPCWJmYlaGR#kcSp=_Yc4rD4b+}%q_=~%DAxJ~oDirDMH4$77Yzunh5>|x9(S(Kj) z*%;8}2uI-1(r#6!iM$Q*lA(&RjG#NeqpbFs zU%#yDd0&L8@wo@$YYK}ff8U*_qSzFCSs47r9bf}BSKC``Igw2ZHMVMD2SlVD87+Eh zmjnhBn{isZE6Zfg9A=LANrvntyqX<-6Eb$JJC<>dw4r=$PTH;J00>Qeu$7J#QDlAkJgekis&d-fv#Lz@;g9mDcC~eEJ7jSfHBZO1 zXZb5OX}G#G(9y#VAI+lpDzDxk(dMmcvom8z^q(StguOu-%uZ@lmP@@f#Cz6fPOwBH zocRMyecQ0>{>?3&cEEs7Ch@&09x-oFze5xvE*uK`on$^kNbeeEJZ9 zU2GQBsYc!Gd3H6={9}R7GbDkK_-vGg^SjxBJkuLVEyTt)askg^H5-tE2#yk-qpC7Hr1O%KO?CK)Iz@x}*o8BiJ4q4vKuYzwq~pf2K> z`g7q3WR{66w^AJ3@|1o|6&!g+8OBaol@G0x{F+$t=qyX4nb+a>n1d!i{CMwIB0A1= zhG|EBO0uHu5hG4L-&cl>pdjaq*tWk=XxLN#)3vD;qSgbNJ zhv8i4D2{Tu0cO)*9e^qoy{YN0^*UtS;wyWxynB%N?xh)dD+&UQ%Xq0^)2m=c+r(}c z)pMzi*h;cZ3L1BkrX6nbkzji1yDMknSi;tv>Wvy=QR7Sg@ynW zbfl&c?ZxO(apodU(#S`#j7bk~F}WYN=#F+AKkhp7EpDKbmzXrfP`+T;agZ|wt1`Db zL|pmC+(f!`Kul8aSq-Y#@bYqTGC|@o8~M+Rzn0B_oGu9iHk}+<>}+{>VX)VdIZQa^ zrqWp^VxeMd^Y%8XSvNF=x^BLr)z_jGChO58iO?|PuhRKYi(DrzI-?(%X6#RD>Oc0uBh^F7kURjy=KGiGxE-fXGf12 zJ1MTy-aIyfUl0g!*?Y#*ew%V{AGPefc4h1769&^6m8>Gv&z@_*0+-egns=vMZHy-u zeg&dz4!Y+KdnMM9ZEL-qA>M)zNe=KEC}+V%jk5aH6m*kRDNB)>%-gNJ9znGGtz}In ze_w-cVD{^c@x-JJS#4B0{XEkEQ2~Fsh~-AA3bmk|Tq!};9F(iq@E#JYlZ?uK?PskR z^zXvK7-S=Q_1}=K`36W^WXF2B-FO{vQb1K1m0DLMxmIF1JDMw@z!ak1+4>xK{_fG= z)|6>T)()q$^hvTLj?DA-`2@i!L$R^A=#>RaqL{mt`m+q1?kFIkjdD1v7OZAjHQZCg_7mrE@|V<>79Jr zj0^&TwwHop9@LPnm&7|asemvcfD3yj3EN) zf&GJMf)uMVctdfI3;~V+oufwLiNf8YT{l=*ihQVX8T{0y^r!Ql0U3A({(iDV{IT<~ z6!=qXbkZO^QL3zQgukU&3m%jsXRjgJQ@=0v4qHr)8cCC7os2`9?8cAj=(F1@7u(w6 zUIizHu7MhC>0Iq7#tZfo(=a~7sy6M~!oI97+AB~wr zalMu(!#;%9DczmL$fJf(qItvU7QR-k+cuicKPxc>ZiKCx z!~w+LtC1F|3IyCODGkiX#QemT{G~f!9kpMQZHtda&NKI-gSWU#S1^F}R3aUM3CF%feQARN!ll_+N(YH58R+LAbw7Ixd3TLtQlh;~k9MAjM~--U-Z3!Vt)bo|L@J{%X1NPdK8+5BcVoSZ18 zO^2=-B6<;j8fl1>H0kKi?2l!W^x|}bQq>4g)F$H&aCTqL+^;EcgB`aNe(&F6V)9cu zTT0CETg!$u+kXDX2#TDT9NT7nuF@H9gKiXqdF15M0C7;GFXZbj71s8|rJ2#Khi;?W zj*C~08}KT=@$+MqhiVl%OvFxjDritBQ%RXV^0k@BV$8;2suMZ|CSYaI_vL0S{dByl zLOSpy1oO16*2mU5MXXXy{O7Q(-I2bB(oVeWkZ1q$vDA?g`Jc`b=36l-^raaO$PM*W zaZ2t#15G%0Pmd(owaKy4R;u{PS+fxE0W&pMr-SHZFo;Lcd5*~&@w@)Yc{ZK*3;^C# z$c1nKD$ICoqfh94%hT3H1$utaSGAR_VIPrOy(CG|H{V|jIL{O}QdKD!;@@>&U8XG^ z@$URdT*3Ez`S|#|Go=%4+2X0_c{)%WyY$4PZqhD)Hm~$dpXHQ{JhQ4* zjBPmMG|1Irs={&%7b?cH$9;a%{KJQ3`mcl$`El1L<7f%(hk1YuAma)(R zbKfCy?epr7Q0N_WZpHeGd`OX^ZT_YvL&5B}F>$GrQYT8lYJmcA#MZQb|2ukU3?)oP ze{l?{s%LCigjm@-C(^+9K~5ZH~Xvx-S5 z4f(AYm@Th?)&kIKGs;P{Er6b^euO2M71lps-X$DdVEp;;={|Y3XyMPQzbvEa(g{EC zVc?i=5cq3Il`s)>j$VC9p43LNm&`GyC^vELVfqO>@#Os%!*_stlVXMs*1^Sc?}$pT z^A}%Zs^ovBB&5T;mKeE1W>JSk+b*3`X^wO$Z%EhpVw9_Ds()HuQE(YuWvta#l2}aB zW{$32dxm|gwS~w&0t#CgP2DsUEn;%36;lxKkvhITaOY}7djXV#dgAfLA6vge9GL9a zV?$ScZW1)3y)}TQt*y1(6tQ= zg%3P@bQFRz_c%O%gt1AqXAqusq*i1;7oPrMBJo&eQQ;b)QJ${h8PB>oU9984TpjDS zZP^9guP<43Qid9dixz^5))XjMf^8AHF|L-WlF1I4D#42c(yU{%nrBx?Z(ZwURi&?j zYtxlAF=A%iA8%A2VkxeQdxk?DOceBTJKb#|v*mB1 zqKK;!wNYUe@TDNp-x6#C^dFQbcr+yRBg9$U-1a56$am*%4Oaf(Kg@?Z6jy>>2hM_d z5)FSO2-4iNXu|uxizVr;h@sh4elG_Ju=z$78AaA&|H|3Nzy@LL{hkJ|R!<=9yV#L? zI_d)TDg3IY8H^y|6xh-}EaIte*ZY0f+AAk-ip~^R$BwL9yau6h)gT0UTU6(&rpo}^ zJEZZytv@QBc{Vze5B&fZiM*0n7H{L7ct{YTh!D-a73#^TEuKI!px61+8%sxl3HLRB z)qPVx>MHFkX6L9msJvYEWnnoaeRQid(ASKD(p@ge8eVvJ*ObhC<7>p*NZ)}Dni#!! zJ-5#onc&8c2mEZ!-V7>Be*c1#m?^_S=XD^TB&k-u>;!Sc)R@k?O2AER`5(VnWz7(3%PHJ)XXW{cky-I^ymHhG8kTv{{soqhVZGP#0Oj-2Daz3} zGfnxT3%w$b#A!o_CGFqM0_j{(o6SxPkfwHEa>{GRj&V7nmc^UEYk5*Ik1mF+G=K{cOnwF!LR<=K z1*~8JOTkuwD$vhSRov2i7ne__Nab^N?$ZQNiU*{kSh~`HpA`#IBqRhN^`CjM7Qn+2 zk#iXabCTLy8R(YD6;4~BcavP#rnXmI)cFR!2yc28a&NhMefJs+;nIDzKxPzhaO5L%=Nn3C9Ja@ z_uC%O&NOld>v~2SWO)85owPBoLYf`t;q>|iR98uyQiA&JmPe^&wmxv_UBjGK98am( z1srdyQ?#w4&FN4i)9y0A@$0%!-lwwFXzz8VEWIc@8NJ2_?YTcXrE1QHw#G|4q^nIh zFTPE;?Z0vci0Sgie9_zxT0%#9cT45aTL&T)$FL(@^;ro|_NWu;5eIkYc@nYOM~xHu zxWE1w--l}%ovsujyY&uVL{Q~7G4W1FCNd4A19wx|u( zeNCRfkKv50UY{!Xox}Bv4LhJlsVG`VA8(Sr_I7bDq_1@VsKxEhdH*fp(6VGmy2R0# z8Nt}+J<1s(mHzW%KV{ir?L(CKaEV7Yg4u1NHU5Xr_5o`-sjC>`0gMnXej^y zK=Sl(4FCX)%*6l%NM@qzSWKCrF?^VAGW&lQnM}5|wPmqbWo2bjsZ=Z$qbSPZa0CK@ zNF<_Csl`=p9kNcjgaF@NfCfFPa1xznd}#mx`Ny8FPD~Ew!<*Nks5v?P9ppK*YGCSc zKB#duMr<&?cr@rWu(4<^qXf&ZJ+MEBls$*>su>Yf&aG9ucOoZiJjt+5qT)~>?Z2m^ z8&a{~iD}AmZ1t+_dhK|9U&PyZ3BkW>b!FmB;z%F2;7RRxv?S;A$dVLJSxuH2h$k*O zAiBbgwW&8Onq~;{1*PBDsC5S2e;BH%=;u=jTB2$t*&xs9mDNBp1bL; zb_jDn=}7k3d>dLV%T4G0xM;Lf%Udz`>;$PQk}9K)P}>bAo9#y^J1P@R4uuyxbmZm$VLzwp_&%fT(x+FL?HU z=COeBTL5_*dwjN+`$N2m_k-bO2&{7*i`pjOrL48he&k+*c?-Ux>IXfkM<6G96e*7h zEEHps?pkEG5l1@RCTsv6>t}v8e^-8aCoJ=#~`837WW$^XDeuz3A^73?v-QQ}P ze%Z$w)b2|vKSzIXljqJul=p#h*ZhhQEiRiDm z)ho1Eom}02Bjkvpb{Y0?vtsbI{_wa1=jkrL_FJ(Dg>iazTK$L$USWmlpGz~JZ!=iT zDJqC(XCmpTHIwNy^LF~uUrOOl%}VpcmMK*I!-mPYAu=3Nc#{c#$mun(+z9)D%!P;P<>+CCEwa*s(DL zn6H4coHRK*P#{>)aH<#S;1eI{vjGzVj!vnOxuRMj0(Vh{ADEG~WH`(wb<3@k@k<@q_C~ds(ulM*%uY15G^e?Bbaz z4f8`+qz5ERK;73-O*|1(S{||d4cKEDO2co*z_`0W6y~z*RJx4HOQ#D{84=WD=j3a# z7`apb%i|_A#aKXn&N|el<%)kEeI%NLzY>X_3Vs^#?!JSA1rw3N09%`1(my0%g z+sGgSY$t2rGrbQspw}PEg!)A?4LG2=qIF=tUB8t`9yMX@-Zn-5L>|0@WvJKHDo@iR zL|ZT&*(SNCj~4d#O=@zyfM#0%(=C=TM3NDUrp4v@bZ^N@#Pd|BcP$z*0A=zg9P5zC z=HZKv{iDbkM0%PUY{xGH}bUpfyb? zb7sOGD?ZINMDTh?B;oH=k!#4px!=o&)n8lu0y$)~;G*pvzZ-a*uCIS1D_-7%o^MkYz?xV}w)=`_vKzIn@M z8CY}m5=brhE%FRD0B-Rq)&4G{mn1T2>57BHb4;1)vin327IbTGiaZjfo|Df5#?o$T zZ|z7!*A_WvD@w^32YXc(1IyhwZ*GYDX2G-DO!h%C^@ z=$M;415cHc%>tL;&I1uZ9N$tj<6mbPf2-bjKghah%cMD%y_0CjvuAnb5B`+WCm0VVb!xXXkdZ4|M_SU7EOO zZ)3_qnv(m^rUS!ARjC38hhU`=j33JRA-E5&u-D?{*B=JxS_chjY_p9sfMkUW!Fs0JW~TBMTNKmCsXga3&gW&Xv9$m{i=a4OGgr zlzf-uESGO#FX@UYPk<4J5OCHWIiwolu`maU$u)R_QbN=~282eVPZ(W$CTDbN@I{Xs zjgk{FVl6Rc@kZ4;KnK&;=F=KMQ4573$8U3>$gO7QrM^_j@!4R= z?834plR>7-jSlO^5aLRA{V;DjE=4U P`8)J<^L4FvW~coJ-H@1v literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_ethereum.png b/src/assets/blockchain/chain_ethereum.png new file mode 100644 index 0000000000000000000000000000000000000000..7b0550b545d81b3844a3b8575fa0cd1982b0f3cc GIT binary patch literal 2256 zcmWkw3pAAb7XN?WJiXr)*Ywz3UT>A06-23V0s6i7#J8igk|}?|kMD%I?jI>`o#iB!-R8n=$%D zx<_;x6M30IjI4Dqcj26`;jp)^)+M@^7tEcLAC*>dz2JRDZTJ2~o|ZG}$fIwZb7z#I zQ2ggN8xPog=W@vpRU*fnqlwn}Q_CvD$13F}PP=Fi5UOppa!1;k$RLAZ{B~KndQ_!c zJ*!gU6-v3*l!*7qc!;O=DAl;;-^CsCqlOf%Se6_)5EHGmD#t|J9iZ58t6e60Q=_cMl4>ASaq}E7{ z%Np^1+YXsw#OEYsG)e$--uH3yyjQ3r+_y2fgP(i&_;Jh~o76^S z1XkA>Ky1JpzkaU#YSyq?>s5-G zhkS$Mw^-@QLyoH%G)Ku6mf#OTO5Ia&W)CcvT zc3Z?SM~V1AV;>#)o!9INK07Csc}z{Ov&7g98%D_tX0ZKajefwBsn9sdTM`y8SbkB}ay zD|9iQX@uWk;M^M}QBw=;`9q`G(M^gBkuU9XIE$hm!aXEW3w|y^=Z`cCj-$uC2;E7p zlqdo!3W$O_Y$sfjr0vkxSu<=0Zc$9a+(k`rH-%-CI~PHVvYMOY61*b~kgM9_z66-% ziWZ}x`W(tGF<%v*+4XmI0deU z_S}uHvJo7Hbe*!Su)DcU8?f+Qnz}^%B3wEDtR8w3M@uIN3upC!Jg%D!r=&>#m^G7T z(n-{CviQ(~7wFS{dKRSiOqGL5iz%`=VIdL(Yeh4__TKL}F?k^!Oum@d9L>PR`-a$KS87OFUC>Cz7&mWM^Q5 zJ-6J<0j_q)8zeZDE~!o*asII8;?`GBL$CBV6Q(78VU2q?otP3{s^`C*d_6YZ_+8r| zV%4SI=4*;_cmIj>U(XRwEcUEC71WVz&Y z*&^X)-h1P_NB;cc-aM3+Saq4m-|JQ|`Tf1q9g;(1qV$*i^aabepNFnJq*5H0dIw%P zQqxrL8qapxOQJ^}m{CR|{czo_y-;f0r=pATY)Lvw)k)b#KM0<6Y`(wjA77t`t*xlrdPU{Xn&Hu9|J&IgxPa<;l}^=Ut3*BJMVRE-r(ggl{Y1!NMb51gZg@)P!BwKzT;P&@ zuh-S{{PerXC)ylVWO%^|Bi1T&&IMFLL~?Y{K#+t>ftUj|PmZlZM;aSemVdld26u02 z0=0OlJI3SuB~Ubev^M`HUyLQ9txdIU+;sgus18g?y|$9wFiIS-n`qBa95nwb@Hdir zh##O;2J;eJTQDh@`ky%;F&K5THadutmBcA++QwW#;d-E#J?GOB)}Cop%fM0akj$4u z0VpW=o3()lgck77Ah?YiFrqaY_Ck6PElIQ5N_UwjwtImzY>)$Q*~;=pbYVhi7rQmH$?}uD-%qBzS$g(Zke)iLiTGyO$Tfxj zAi(X<@nrOi(o+459X;Mx`c&>V&tAlDttLpsp8a^w;y(MPjLPy{Vbrekd4ngy#&>D{ O4G`eFn_1<<;r|bUz2g@E literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_ethereumclassic.png b/src/assets/blockchain/chain_ethereumclassic.png new file mode 100644 index 0000000000000000000000000000000000000000..9d9e077d29974765b2cd58773cdc0a90d221a942 GIT binary patch literal 2684 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 L)zjs{W)A;f@CCeP literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_internetcomputer.png b/src/assets/blockchain/chain_internetcomputer.png new file mode 100644 index 0000000000000000000000000000000000000000..8cc186a8828f411e43367984ec12983ce47c3921 GIT binary patch literal 3323 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 literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_iota.png b/src/assets/blockchain/chain_iota.png new file mode 100644 index 0000000000000000000000000000000000000000..ebc8f14fa53e678c6494cee63bef00e3fe29a7b0 GIT binary patch literal 3974 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 literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_litecoin.png b/src/assets/blockchain/chain_litecoin.png new file mode 100644 index 0000000000000000000000000000000000000000..91f73a0665675f37a707d210bf1129fac02904bc GIT binary patch literal 1791 zcmY+FYgAL$636#R4k1T~K&ZkJFi}r}v>;JHkRl`kC18|CvBXkhM0>$fUkh3yf}A8+ z5fCCE#s_ayR2sx$K$H^XX^RM#YY?##Kne(Ivg8q7$xZLKyVjolo7pq-X=eTR`H;N< zq*d!y0RTvWY`-u7AcG45BHnAzrY#E1wP7MG+NDB00g$tmuYsO`1 zo6V)EKj#pBzB-wUpba>m<)FGj+r19$Rk0OPI0!+9`lHX;0$ zsrh#(e-;}R-=<$l_VRsIP+VW@!m4*AZ`g*N;bKeEx{pH0Sd=u(y ze98KKJ@Oo%lgE~{mPZXXy1u0eTg@%s{Ax~X-~B9GPukkUPHN}-og%oO*OSE9&haw_3K5PFw@-`6>tl;AJD8=9+2^!OoxiEGi#ZP|S` z1;e*!3aB|){J=_L+jiwmlny}bjYt0({=u zQ8w1`A2P=@6{yK+wnB@%-Hfe@NT7}+f_4ga6=8q2Vh5BEF~m~jLad$1|alr^*kaEuhc}cqaB192v;8G#SIe; z-si>BrS(N{-RMsP(XB6J#xb*}=sm>18?fWl8BH`=u@9}cn<8ai%vDNMnueIIVe4q9 zU@+RXGYD15+d4bd=){51 z&ntO2UdP&g)&ugr>Oa?B-t`k&KVDvZ3mVicwwC7{&Luc@@D_UQrT(#z-#pf+uTkez z_NO^7urWb1Z|PkV>2LeS;2KUJhSw?airCiuDxYLvU7?+SlYxNbH9QT)8*F~sXjryJ zq5&Sqj)OW5Q1uVnIkRBDwh@Mt1p`+jTx$G3)kVt*+xHqDc3~}zkjKoZ?OQ!Gd~8iv0>egMinhx+rbtu z4XgF0%`~ERDt2XQSmo0bX@IqTO~8TaL1Yudq8^5-Vr;S2 zo;WJD68VvZZBi%g!l&u*_#6PLzqXYUxgwAqznU4%MQ|^>cYyO=PSDbRV`|Mg8h_^$ zKt=gfY^)Y9u?`V|-{S!Y;GbsV(%v-xU7#wNS3RV4&TR4gw61c-d-@Yyam5?!`hrzz zcJMcu@GFWqB@Ee?VqWM11tD~}dW4*?=;2ikU-1@p+p}&2ADr!17?H8Glr4y7pZ&#$ zXywe2+Irai#7gTo`$bEd3xL|Lm}|&Cy;9r_XP0Okfu?40$uWzB>8PpT zmyZltJj8ho-LUNWf9X0JnOb&qDmbBR(I$aO!~|Zjl}O>;tQ#u9cf;a<{20cD-o;T$ zR#J!N90k^D6RqJt@)NRzU(lQm@i`G?v$uS#=L}NXyEtjw@@7tBKQfz0arAmsPkD_~ zOdgluE$;{gb!w|vR>4rM5k1_j1~=x;5Sf)WFgy_%r|n{>epr04%a!qclBuIozL@rp z){ou|*zm(panh5S!&;j?_Z)Sa^wmq#isvKaLjQ9Mk1pxMJo{=HnU$+u=6xcLZvW*? a=t53k7=u(Wu#;%`m_VTaUO(9`zUaTFbSNeO literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_monero.png b/src/assets/blockchain/chain_monero.png new file mode 100644 index 0000000000000000000000000000000000000000..c25b1dfbe86a6fbfe322aed8ed9d8855216b4d14 GIT binary patch literal 1887 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 U2?*0J-2eap07*qoM6N<$g8460g#Z8m literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_ripple.png b/src/assets/blockchain/chain_ripple.png new file mode 100644 index 0000000000000000000000000000000000000000..f46adba2aede110dd1989b9b09d853aaf03f8f49 GIT binary patch literal 2077 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 literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_ton.png b/src/assets/blockchain/chain_ton.png new file mode 100644 index 0000000000000000000000000000000000000000..a78be99141244381cf817d1dd2665f6b255574e5 GIT binary patch literal 1840 zcmV-02haG4P)V!Y=Jfl)V!Z26R$R zQvl(#bQnL5heg`}00yi{L_t(|0mRldy6Z3$1mIwAuEVt)s>5}-HhiA_FX46b=#okE zZ$QqOkxn{Qogb^o*aZOBo2)j^j!lEd(#1dA>8oSOc^TnD>D0y?jxoOAxBUs{7~k;P z{Dc9A|CQ;kwdpVLBiXv8pAS+DVK_z!*|qWUM<^w2IEFh)o2$;JX{RwUD)Ck4ldn3T zd{zGBtInrc?fVZ%zn~wXrqpz57%Wg*y6aT8MFa9qWqULts9XRv#MCQ*tOhX}<6C|K zh}IPXh(MAMVl>KE-2gJ88$j8v`V%C0j)oCP(!(2C5ULtT#_kd%%2fj(BN_nZssWG@ z4S;gh0LX|2KzWG)s7CNcs|vgSLx|F=-8D!Q2nJAQDfZC1k?0OmuDXL{L=~h!RzcQK zlmHn~1u0i0K%ZsMg(aiU6Od*wfHhZ90_2Dipj?#z?L-OCNR$BOvRCNyeb57G_Q3^K z6`g-AU{xmC#DuHp64L0CODMaD5iCnp6(A$306C%xP%f$frK$?hRksNE63`n0g=i5_ z)}a9;cA^fF5j{hch&o7xsDqS=JBV~wwu8TjH)EJ4;~`uz~B^A|ak;HF7JX1NBL1j8l^xy_q#B*B*u zfL!JpL=vPGK&H6{i3Ect4M|k&$1lNg3_#}bYp^FA2yz~Km`udit=;b)WYPJrEx=hi z8E@q^xa#D3-CZz~)WhxI=f(}cDFCSV&3)F1hhZBSHTQWH0njv3fQ=Yn9s%IYZMY!r z|4)E^(gvu3*}n%kN*`nDIs&+hYoM1t##gz9{B-mnM|}vfE;E2(^cvBLD#=TlTL5rO z#U!0*yONwTWds22f7eANc@YAP+g|)O0T{Qi%WyKM5hg`2O8`zABuD_5hmwI{k^tOM zFF|&rPE0RsRsp~QV}d-*0C4;AaeyHcSq8vU5RJuWj{|&7rUBBAKN}bz;v)dp$v{Sq z05Bo~AS48Ux5$Xt0dNvYNO}M$5jy~GnBH;#DW`B2NDd zzz30G^c<5dg6e z1XFP!A^=iC0HlZjn1uiURx>3c0I`IAHzbG%fLaIuU_BQ*A^<`{*7J=jDIx%N7U2^T zDIx$$LI5_aq=*2BS%mD0`w>J0KuXBxgkD4d@`WJ$q%;t54uFynfUPPiA^>6*A>xn@ z5drWL)`2>qVFw3bC?%{zbIXW`0B8xTC$5-iyHfzD3U$l0UtYu(z)M(tcSweaEdYN{ zahI)6%8S?nXbJh-u@>0qJ3lvcsvsLiHds0gy>K z38aUJNX|>7$4RFakt|C~5NHsHxRw%l4Y+S(A&?RTU$hHEoR^irYcR?r3?$$+@FGsj zG2PrECUV~}Fr{&(67Wo5QJp+>3EZgJz#H5dG`poEdD(w0D z)U1!~o+erXxnUn%_Q~lNZo(wr_-1zec6i#C&yzKup8v^=FU9xkjlW+`cUaA3@Ho}@Oj$YrT{$5c{bQXBad zQ>k1kvs-PuTg*!qMIkZL#mKmnLd3Xbe$zR>zkbj8p7Z&f&-3{{&-b~V^JMw^ZPuZ) z=>Py7A8)Tf03Z#801eT=z8hh|8nE~G4c@3hbztECA|OER@BfwW+NIvNPkro|TGJ*c z)dK_S=4N$SSyaZ(zcmSz#6Z6w(03OPFg^6%;R66#20mWy!70$IF8L+Io#7FH@#x)uWmco+MkNZ&mF8xHLxE-3-3L5B56Fp&ZZ^vuf=r&hmM8m zbaHxnHSD&qK<`H4C#XHrjBWnx3nz-dIJyXij~+EYT);Lv95LOSS(d+lVJ=tn6rWz_ zzAr`LWTV3jrx@!C*eSX#PaFj23R_wN4Q5(;uqT%P#;K)aGrM~y9@Y+K9&KT27n?73 z<9<6oBU8Jz-rg~xiVT~W*&gf0ZF-^>NSn=Z=@=EgF|C7NrHqfpp$V3~E+Fh<1RyM1 zFPxgu0gI=!8iQ_Y<9kw|v08iZX34#QORF~1kljD8N`YkRm)G*M(!m$NgV5?~G1;)%z{DDLQ9N?~ zmWras@pjRw-v`X$Nyg`PN{4m4<0a(J_Tfk8!zSXO4@ceyHS`x9rj=T^rhj)M5#cj( z+KMh@T|5Aapflypvz~3zYdy%hsrN)E+1a02Qbc_Jc)jbqmQ(No)0xiPl_8A+J)?YiT;|AQF_7FrBo ztTirs!_9z@A!4r;i?yD-fO(V*4$ty#13DJf_e|fB<_v0ic3n`;F%mTi#|j}mHd3VZ zLnH784^cko5`Q4#agvlXc4%rZ(^ap@s~TD7he_VLZ3poFhu%p%vc32xiF&?Sp1c0U zGRJQ^gvc^Hnk7QT{RzB2E!y)4xBR;&k-|F*rQjz% z=$!qQnsgRKkfT^*ldAXXr>DhGYX)Z67%y>FHMEO#O8P+uH6<3Q~!5Qc3S+Os(6vi>QVrYt^X z+&~W&nyVM&w^c-qabpFZFLT_~*HTf=o;HY}?m;VI8maqD_?EKREBD&>5-Y{E7+#~O z?aGy<^hadpLbX@~8PIA!g?4P3KGEC&oblr02YyW6`8r!R{P^SCDbz7^3-_lVy`_UuLtC z4uD3?I`eRKeEW`DE0TJ2F*8OCAy!NcWsJib z9&~z@#3X@&1Asu$O`p>sAcWw5D=?ev1O`N_V}RIE?(0~rnT7WUXcBnyCrd!H3m%zM zzulRFv7C6sDNbw6_Wco6l;@@xcoY>^Tu zw>X-MzDiHO*5*Y1I$=ellH=HwGys$;XvSC(Bu4nvkf~@t9R@Js>|-c1y~)$?yq&#e zMx-DXU>Ql99x!lQ8fc6>b}fqK>kcp;VS&BNzw*^LF7G{Wec5cQI~_yk&6kb$_y|_e z0pI)Fxo`wfW`5(l0j@#vU`&qEpK;`exm$DQ$q#0>Y+s7M_&KY`6P#aWcTtbM4YKWm zTVWgcBuAm?BcNbDvyCNNWyCRykE^rE`(At zKmfn^^K$Y|5QWl|A^5t8)u01qg#|+3oy(f|tXLyw=t6y*f)9mlgS7qOR=~R0lz%1A5on&!<>>9?5=k@)x|mq? z)^fu{@Ey3K=TA0ea-bNn$qI-99Z3?XARFiKp!q9X@V`jUmOqp%Vt^rA#dW{ms0&+b zbn&FRvO^9)^tllXnIx>z9R~-27hy})%LUS0;tgp%2G&@j2lKsY;5w}2@b`m% zju?@MDrjiYuK=hhlIS2ZkbXzFX}MW|5VTN!X)_I@NH$O+R)Q}JRa!ij##9=r=!9EG zd!hjak_sr@(TbI1MYT>T3<|2rFS(1-?}1mMv4uAX{{dVqAD)PunLNk}U?2g(g_wHz z89jm5Xm^|+RsQ1T6mx^=mXJ0pVwTGIX#Rh({ucLFQm}UE)<_`rjz!H!hfyb=cle(;(q@?Zoqv3BUw4pGa@Se z$I5D8q$h!0 z(O0v^qJ`q=qS^N2(+L~;UC(bUsb}had6?uo9}-q4?`w5 literal 0 HcmV?d00001 diff --git a/src/assets/blockchain/chain_zcash.png b/src/assets/blockchain/chain_zcash.png new file mode 100644 index 0000000000000000000000000000000000000000..a6bf58a9b8e1f6108d89423a3d3de5051714ad2a GIT binary patch literal 1570 zcmV+-2Hp9IP)$phkx=8A|Nb0#r|NsB<)|314+W6(V?!;t3g=KL7000McQchCwsmIfjZf(SO1)z5*vmIiIF-9`{KMm5Io}2Ni2w!zLL2%dd;hh@{>6OfqtFu@ z|9U!j=j-?Sc>MSF6n2)akmKf>}S@q|Goe-bAH3#oTp9!mt zxtK&AhEL4P6A-)=I+9oM#IE0MQ@}EGjd}3LsWO)*25J zh-(&|w`4OA^H4Mc!Avy+LB(%^9AniB0F~$kfU)WYfJ*cNz*zMHKqYzsV61uppwfh( zK$!)&^bE*sR0_a5`?OR~zUW5;q|KjQh{Brp(Zrls8 zv1jqek%Lq3BuFf>fOit4{R<4eIqVbgrg781z}%ApegS6^6bgZ%XCv?n_$EQC5Lo!S zfM3AT2@+EXSOtMpBQS)(KqD}NK&ug0K)^Htn1zW*0JE@A2@H{g8Ua+}S|oro>L|Xz zv8S777GL1jb4bqP3!JzPXk~nXGuPoDjW2L+&&TW733vg|8UQoTmkFGV0QeaK&Xd{# ztB_*v=wICV&dA1c-zfyJ4s&Dqxl;(BnkTVG;N;Ht<-RbIi*6x+AV`hl@aJwJfcyP;0D)E%!`Kd6 zQ3ya_BwzLxhN z0=PJAHC@bbVNr`>7*{p}O&2p8Qr{-o}#(Hjb5Ah^cb*qhzz%L&41!)ZeoFke3h2q>g~WL@CxqrcM5 zZ1{!7BrBcxQY*gJTbnZhmi%frz1&W(_Yd%b16=XgS)P)ME{Fgtyy%pv>ZOPB0hT@j zCpN&sFP_H+82GK<{|(@DuOCVWSUMb}@Y?Ufp$~)?p9k5h-+Cw;V9VG4VF1D-kAPqy z7>kS;klGvup>QY|0ERPgm|)=nEGH02jZy&y4wfuDfWcU>MP4wP=CJ{MLg7IXI3A4$ zY-I){s<{iFOB|9(V?uppL+lugie2NdTpAaQ1Cx1VG!M<@vEe*8okz##hv(~}B^geI6sXo8`HBnU9x_ObW>hrjWamF@1jtZsOE2n&~uS5AZ0=_^y* z)Sq41^a(0npLG>K_5LTno=#Q*>R07*qoM6N<$f{6RL%K!iX literal 0 HcmV?d00001 diff --git a/src/assets/coins/btc.png b/src/assets/coins/btc.png deleted file mode 100644 index efb40ec1b9bc06f0263e23d652ad8677758bd3fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13736 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

rB5V7h}XTXdG^&EO~I0ACZbdmO6Bz$91 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 diff --git a/src/assets/coins/coin_btc.png b/src/assets/coins/coin_btc.png new file mode 100644 index 0000000000000000000000000000000000000000..8f6a33f958899c699ea1cae063fbaef718649b06 GIT binary patch literal 2672 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 F`M=1vlluSw literal 0 HcmV?d00001 diff --git a/src/assets/coins/ton.png b/src/assets/coins/ton.png deleted file mode 100644 index ab89ee4c1a2320080e8090c30db5cf5b2ab0b9e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4902 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 Js5kRU`adnH^8f$< diff --git a/src/assets/font-icons/accept.svg b/src/assets/font-icons/accept.svg index 1d551a69..bbd5175a 100644 --- a/src/assets/font-icons/accept.svg +++ b/src/assets/font-icons/accept.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/arrow-down.svg b/src/assets/font-icons/arrow-down.svg index 0862a707..cf5ba322 100644 --- a/src/assets/font-icons/arrow-down.svg +++ b/src/assets/font-icons/arrow-down.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/arrow-right-swap.svg b/src/assets/font-icons/arrow-right-swap.svg new file mode 100644 index 00000000..1e7904f6 --- /dev/null +++ b/src/assets/font-icons/arrow-right-swap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/arrow-up-swap.svg b/src/assets/font-icons/arrow-up-swap.svg new file mode 100644 index 00000000..de7da1af --- /dev/null +++ b/src/assets/font-icons/arrow-up-swap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/backspace.svg b/src/assets/font-icons/backspace.svg new file mode 100644 index 00000000..9d7c3313 --- /dev/null +++ b/src/assets/font-icons/backspace.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/font-icons/caret-down.svg b/src/assets/font-icons/caret-down.svg index 0d66f0c2..d0aa3b96 100644 --- a/src/assets/font-icons/caret-down.svg +++ b/src/assets/font-icons/caret-down.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/changelly.svg b/src/assets/font-icons/changelly.svg new file mode 100644 index 00000000..cfcdb2a9 --- /dev/null +++ b/src/assets/font-icons/changelly.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/chevron-down.svg b/src/assets/font-icons/chevron-down.svg index b5359e68..671daf51 100644 --- a/src/assets/font-icons/chevron-down.svg +++ b/src/assets/font-icons/chevron-down.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/chevron-left.svg b/src/assets/font-icons/chevron-left.svg index 419a52dd..e44da171 100644 --- a/src/assets/font-icons/chevron-left.svg +++ b/src/assets/font-icons/chevron-left.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/clock.svg b/src/assets/font-icons/clock.svg index a56d1796..a0dc9ad4 100644 --- a/src/assets/font-icons/clock.svg +++ b/src/assets/font-icons/clock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/cog.svg b/src/assets/font-icons/cog.svg index e5fffea9..5268b3e3 100644 --- a/src/assets/font-icons/cog.svg +++ b/src/assets/font-icons/cog.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/coinmarket.svg b/src/assets/font-icons/coinmarket.svg index 2046bc2d..bcd18a1f 100644 --- a/src/assets/font-icons/coinmarket.svg +++ b/src/assets/font-icons/coinmarket.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/copy.svg b/src/assets/font-icons/copy.svg index 7802118f..7a847806 100644 --- a/src/assets/font-icons/copy.svg +++ b/src/assets/font-icons/copy.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/dot.svg b/src/assets/font-icons/dot.svg index 974982d4..44ab8611 100644 --- a/src/assets/font-icons/dot.svg +++ b/src/assets/font-icons/dot.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/earn.svg b/src/assets/font-icons/earn.svg index ebc62083..6501700c 100644 --- a/src/assets/font-icons/earn.svg +++ b/src/assets/font-icons/earn.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/eye-closed.svg b/src/assets/font-icons/eye-closed.svg index 4ee30b79..a1899bfb 100644 --- a/src/assets/font-icons/eye-closed.svg +++ b/src/assets/font-icons/eye-closed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/eye.svg b/src/assets/font-icons/eye.svg index 712f0e10..d48f9ca2 100644 --- a/src/assets/font-icons/eye.svg +++ b/src/assets/font-icons/eye.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/face-id.svg b/src/assets/font-icons/face-id.svg new file mode 100644 index 00000000..934f8c7c --- /dev/null +++ b/src/assets/font-icons/face-id.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/flashlight.svg b/src/assets/font-icons/flashlight.svg new file mode 100644 index 00000000..302a4446 --- /dev/null +++ b/src/assets/font-icons/flashlight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/github.svg b/src/assets/font-icons/github.svg index 0914b11b..d3c13e21 100644 --- a/src/assets/font-icons/github.svg +++ b/src/assets/font-icons/github.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/laptop.svg b/src/assets/font-icons/laptop.svg index 574375f1..d816a10b 100644 --- a/src/assets/font-icons/laptop.svg +++ b/src/assets/font-icons/laptop.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/ledger.svg b/src/assets/font-icons/ledger.svg index f24dbe12..49cf6e33 100644 --- a/src/assets/font-icons/ledger.svg +++ b/src/assets/font-icons/ledger.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/params.svg b/src/assets/font-icons/params.svg index 3bf98138..e96b9c8e 100644 --- a/src/assets/font-icons/params.svg +++ b/src/assets/font-icons/params.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/paste.svg b/src/assets/font-icons/paste.svg index f130eb9e..8e51f0c5 100644 --- a/src/assets/font-icons/paste.svg +++ b/src/assets/font-icons/paste.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/pen.svg b/src/assets/font-icons/pen.svg index c3ffe107..a38165c9 100644 --- a/src/assets/font-icons/pen.svg +++ b/src/assets/font-icons/pen.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/percent.svg b/src/assets/font-icons/percent.svg index 906bd23b..0ea6f023 100644 --- a/src/assets/font-icons/percent.svg +++ b/src/assets/font-icons/percent.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/plus.svg b/src/assets/font-icons/plus.svg index ac7c6ec6..717ee6bc 100644 --- a/src/assets/font-icons/plus.svg +++ b/src/assets/font-icons/plus.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/qr-scanner-alt.svg b/src/assets/font-icons/qr-scanner-alt.svg new file mode 100644 index 00000000..475037ab --- /dev/null +++ b/src/assets/font-icons/qr-scanner-alt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/qr-scanner.svg b/src/assets/font-icons/qr-scanner.svg new file mode 100644 index 00000000..a4883ee9 --- /dev/null +++ b/src/assets/font-icons/qr-scanner.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/qrcode.svg b/src/assets/font-icons/qrcode.svg deleted file mode 100644 index de4e34b4..00000000 --- a/src/assets/font-icons/qrcode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/assets/font-icons/question.svg b/src/assets/font-icons/question.svg index 61e8f3a3..0868e523 100644 --- a/src/assets/font-icons/question.svg +++ b/src/assets/font-icons/question.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/receive.svg b/src/assets/font-icons/receive.svg index 018eb62a..6184e71d 100644 --- a/src/assets/font-icons/receive.svg +++ b/src/assets/font-icons/receive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/search.svg b/src/assets/font-icons/search.svg index 66e33b2c..807325f2 100644 --- a/src/assets/font-icons/search.svg +++ b/src/assets/font-icons/search.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/share.svg b/src/assets/font-icons/share.svg index f85a8c0b..ef979655 100644 --- a/src/assets/font-icons/share.svg +++ b/src/assets/font-icons/share.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/star-filled.svg b/src/assets/font-icons/star-filled.svg index c6da8cd6..90091e00 100644 --- a/src/assets/font-icons/star-filled.svg +++ b/src/assets/font-icons/star-filled.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/star.svg b/src/assets/font-icons/star.svg index a209b618..d1fd7f8f 100644 --- a/src/assets/font-icons/star.svg +++ b/src/assets/font-icons/star.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/telegram.svg b/src/assets/font-icons/telegram.svg index 6f1be8db..7b9667a6 100644 --- a/src/assets/font-icons/telegram.svg +++ b/src/assets/font-icons/telegram.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/tonscan.svg b/src/assets/font-icons/tonscan.svg index 2e6b8f9a..fb2dd471 100644 --- a/src/assets/font-icons/tonscan.svg +++ b/src/assets/font-icons/tonscan.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/touch-id.svg b/src/assets/font-icons/touch-id.svg new file mode 100644 index 00000000..16e1de60 --- /dev/null +++ b/src/assets/font-icons/touch-id.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/font-icons/trash.svg b/src/assets/font-icons/trash.svg index 9fcab435..281ef4cf 100644 --- a/src/assets/font-icons/trash.svg +++ b/src/assets/font-icons/trash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/windows-maximize.svg b/src/assets/font-icons/windows-maximize.svg index 39614d39..27b0d7dc 100644 --- a/src/assets/font-icons/windows-maximize.svg +++ b/src/assets/font-icons/windows-maximize.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/font-icons/windows-minimize.svg b/src/assets/font-icons/windows-minimize.svg index 29f6debc..3d8b6014 100644 --- a/src/assets/font-icons/windows-minimize.svg +++ b/src/assets/font-icons/windows-minimize.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/lottie/duck_guard.tgs b/src/assets/lottie/duck_guard.tgs new file mode 100644 index 0000000000000000000000000000000000000000..1e6c844892ff53d9ea0712bcac85919ed494d43f GIT binary patch literal 39162 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 literal 0 HcmV?d00001 diff --git a/src/assets/lottiePreview/duck_guard.png b/src/assets/lottiePreview/duck_guard.png new file mode 100644 index 0000000000000000000000000000000000000000..d1d78c2ceb9fa3ee7343cd74488dc0d41b269235 GIT binary patch literal 8127 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 \ No newline at end of file diff --git a/src/assets/settings/settings_face-id.svg b/src/assets/settings/settings_face-id.svg new file mode 100644 index 00000000..fd7721d2 --- /dev/null +++ b/src/assets/settings/settings_face-id.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/App.module.scss b/src/components/App.module.scss index cdf1e4f6..20b9caa8 100644 --- a/src/components/App.module.scss +++ b/src/components/App.module.scss @@ -26,6 +26,17 @@ } } +.appSlide.appSlideTransparent { + background: transparent !important; + + transition: background-color 150ms; + transition-delay: 350ms; + + :global(html.is-ios) & { + transition-delay: 650ms; + } +} + .appSlideContent { /* These styles need to be applied via regular CSS and not as conditional class, since Transition does not work well when `slideClassName` updates */ :global(html.is-extension) & { @@ -35,6 +46,10 @@ @include respond-below(xs) { overflow-y: scroll; } + + @supports (padding-top: env(safe-area-inset-top)) { + padding-top: env(safe-area-inset-top); + } } :global(html.is-electron) .transitionContainer { diff --git a/src/components/App.tsx b/src/components/App.tsx index 3b14feca..7a323fbc 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,16 +1,22 @@ -import React, { memo, useEffect } from '../lib/teact/teact'; +import { BarcodeScanner } from '@capacitor-mlkit/barcode-scanning'; +import React, { memo, useEffect, useState } from '../lib/teact/teact'; import { getActions, withGlobal } from '../global'; import { AppState } from '../global/types'; -import { INACTIVE_MARKER, IS_ELECTRON } from '../config'; +import { INACTIVE_MARKER, IS_CAPACITOR } from '../config'; import { setActiveTabChangeListener } from '../util/activeTabMonitor'; import buildClassName from '../util/buildClassName'; -import { IS_ANDROID, IS_LINUX } from '../util/windowEnvironment'; +import { + CAN_DELEGATE_BOTTOM_SHEET, IS_ANDROID, IS_DELEGATED_BOTTOM_SHEET, IS_ELECTRON, IS_IOS, IS_LINUX, +} from '../util/windowEnvironment'; import { updateSizes } from '../util/windowSize'; import { useDeviceScreen } from '../hooks/useDeviceScreen'; +import useEffectOnce from '../hooks/useEffectOnce'; import useFlag from '../hooks/useFlag'; +import useLang from '../hooks/useLang'; +import useLastCallback from '../hooks/useLastCallback'; import useSyncEffect from '../hooks/useSyncEffect'; import useTimeout from '../hooks/useTimeout'; @@ -22,13 +28,18 @@ import Dialogs from './Dialogs'; import ElectronHeader from './electron/ElectronHeader'; import LedgerModal from './ledger/LedgerModal'; import Main from './main/Main'; +import AddAccountModal from './main/modals/AddAccountModal'; import BackupModal from './main/modals/BackupModal'; +import QrScannerModal from './main/modals/QrScannerModal'; import SignatureModal from './main/modals/SignatureModal'; +import SwapActivityModal from './main/modals/SwapActivityModal'; import TransactionModal from './main/modals/TransactionModal'; import Notifications from './main/Notifications'; import Settings from './settings/Settings'; import SettingsModal from './settings/SettingsModal'; +import SwapModal from './swap/SwapModal'; import TransferModal from './transfer/TransferModal'; +import ConfettiContainer from './ui/ConfettiContainer'; import Transition from './ui/Transition'; // import Test from './components/test/TestNoRedundancy'; @@ -38,6 +49,7 @@ interface StateProps { appState: AppState; accountId?: string; isBackupWalletModalOpen?: boolean; + isQrScannerOpen?: boolean; isHardwareModalOpen?: boolean; areSettingsOpen?: boolean; } @@ -50,6 +62,7 @@ function App({ accountId, isBackupWalletModalOpen, isHardwareModalOpen, + isQrScannerOpen, areSettingsOpen, }: StateProps) { // return ; @@ -58,22 +71,41 @@ function App({ closeHardwareWalletModal, closeSettings, cancelCaching, + openQrScanner, + closeQrScanner, + showNotification, + openDeeplink, } = getActions(); + + const lang = useLang(); const { isPortrait } = useDeviceScreen(); - const areSettingsInModal = !isPortrait || IS_ELECTRON; + const areSettingsInModal = !isPortrait || IS_ELECTRON || CAN_DELEGATE_BOTTOM_SHEET || IS_DELEGATED_BOTTOM_SHEET; + const [isBarcodeSupported, setIsBarcodeSupported] = useState(false); const [isInactive, markInactive] = useFlag(false); const [canPrerenderMain, prerenderMain] = useFlag(); const renderingKey = isInactive ? AppState.Inactive - : ((areSettingsOpen && !areSettingsInModal) ? AppState.Settings : appState); + : ((areSettingsOpen && !areSettingsInModal) + ? AppState.Settings : appState + ); useTimeout( prerenderMain, renderingKey === AppState.Auth && !canPrerenderMain ? PRERENDER_MAIN_DELAY : undefined, ); + useEffectOnce(() => { + if (!IS_CAPACITOR) return; + + BarcodeScanner + .isSupported() + .then((result) => { + setIsBarcodeSupported(result.supported); + }); + }); + useEffect(() => { updateSizes(); setActiveTabChangeListener(() => { @@ -91,6 +123,23 @@ function App({ } }, [accountId]); + const handleOpenQrScanner = useLastCallback(async () => { + const granted = await requestCameraPermissions(); + + if (!granted) { + showNotification({ + message: lang('Permission denied. Please grant camera permission to use the QR code scanner.'), + }); + return; + } + + openQrScanner(); + }); + + const handleQrScan = useLastCallback((scanResult) => { + openDeeplink({ url: scanResult }); + }); + // eslint-disable-next-line consistent-return function renderContent(isActive: boolean, isFrom: boolean, currentKey: number) { switch (currentKey) { @@ -111,7 +160,11 @@ function App({ nextKey={renderingKey === AppState.Auth && canPrerenderMain ? mainKey + 1 : undefined} slideClassName={slideFullClassName} > -

+
); } @@ -129,9 +182,9 @@ function App({ {IS_ELECTRON && !IS_LINUX && } @@ -153,13 +206,24 @@ function App({ isOpen={isBackupWalletModalOpen} onClose={closeBackupWalletModal} /> - + + + - + + {!IS_DELEGATED_BOTTOM_SHEET && } + {IS_CAPACITOR && ( + + )} + {!IS_DELEGATED_BOTTOM_SHEET && } )} @@ -173,6 +237,7 @@ export default memo(withGlobal((global): StateProps => { isBackupWalletModalOpen: global.isBackupWalletModalOpen, isHardwareModalOpen: global.isHardwareModalOpen, areSettingsOpen: global.areSettingsOpen, + isQrScannerOpen: global.isQrScannerOpen, }; })(App)); @@ -180,3 +245,9 @@ async function handleCloseBrowserTab() { const tab = await chrome.tabs.getCurrent(); await chrome.tabs.remove(tab.id!); } + +async function requestCameraPermissions(): Promise { + const { camera } = await BarcodeScanner.requestPermissions(); + + return camera === 'granted' || camera === 'limited'; +} diff --git a/src/components/Dialogs.tsx b/src/components/Dialogs.tsx index c5c830b1..27ce496f 100644 --- a/src/components/Dialogs.tsx +++ b/src/components/Dialogs.tsx @@ -1,9 +1,11 @@ +import { Dialog } from '@capacitor/dialog'; import type { FC } from '../lib/teact/teact'; import React, { memo, useEffect } from '../lib/teact/teact'; import { getActions, withGlobal } from '../global'; import renderText from '../global/helpers/renderText'; import { pick } from '../util/iteratees'; +import { CAN_DELEGATE_BOTTOM_SHEET, IS_DELEGATED_BOTTOM_SHEET } from '../util/windowEnvironment'; import useFlag from '../hooks/useFlag'; import useLang from '../hooks/useLang'; @@ -24,27 +26,37 @@ const Dialogs: FC = ({ dialogs }) => { const lang = useLang(); const [isModalOpen, openModal, closeModal] = useFlag(); + const message = dialogs[dialogs.length - 1]; + const title = lang('Something went wrong'); + useEffect(() => { - if (dialogs.length > 0) { + if (CAN_DELEGATE_BOTTOM_SHEET || IS_DELEGATED_BOTTOM_SHEET) { + if (message) { + Dialog.alert({ + title, + message: lang(message), + }).then(() => { + dismissDialog(); + }); + } + } else if (message) { openModal(); } else { closeModal(); } - }, [dialogs, openModal]); + }, [dialogs, lang, message, openModal, title]); - if (!dialogs.length) { + if (!message || CAN_DELEGATE_BOTTOM_SHEET || IS_DELEGATED_BOTTOM_SHEET) { return undefined; } - const message = dialogs[dialogs.length - 1]; - return (
{renderText(lang(message))} diff --git a/src/components/auth/Auth.module.scss b/src/components/auth/Auth.module.scss index d006fddd..8ee66e58 100644 --- a/src/components/auth/Auth.module.scss +++ b/src/components/auth/Auth.module.scss @@ -17,6 +17,10 @@ .transitionSlide { background: var(--color-background-second); + + @supports (padding-top: env(safe-area-inset-top)) { + padding-top: env(safe-area-inset-top); + } } .container { @@ -49,7 +53,7 @@ } @supports (padding-bottom: env(safe-area-inset-bottom)) { - padding-bottom: calc(1rem + env(safe-area-inset-bottom)); + padding-bottom: max(1rem, env(safe-area-inset-bottom)); } @include respond-above(sm) { @@ -57,6 +61,12 @@ } } +.containerFullSize { + overflow: hidden; + + padding: 0 !important; +} + .logo { display: block !important; @@ -296,6 +306,10 @@ column-gap: 1rem; margin: 1.5rem 1rem 1rem; + + @supports (margin-bottom: max(env(safe-area-inset-bottom), 1rem)) { + margin-bottom: max(env(safe-area-inset-bottom), 1rem); + } } .modalSticker { @@ -303,19 +317,27 @@ } .form { + display: flex; + flex-direction: column; + flex-grow: 1; + + width: 100%; +} + +.formWidgets { width: 100%; margin-top: 1.75rem; } .checkMnemonicInput { - margin-bottom: 1rem; + margin-bottom: 1.25rem; } .errors { width: 100%; padding: 0 0.5rem; - font-size: 0.9375rem; + font-size: 0.8125rem; font-weight: 600; color: var(--color-gray-1); } @@ -324,7 +346,7 @@ width: 100%; padding: 0 0.5rem; - font-size: 0.9375rem; + font-size: 0.8125rem; color: var(--color-gray-1); } @@ -343,8 +365,6 @@ } .error { - margin-top: 0.5rem; - font-size: 0.9375rem; font-weight: 600; color: var(--color-red); @@ -500,6 +520,121 @@ margin: 0.75rem -1rem 0; } +.biometricsStep { + align-self: center; + + margin-bottom: 1rem; + padding: 0.75rem; + + font-size: 0.9375rem; + font-weight: 600; + color: var(--light-gray-1); + white-space: nowrap; + + background-color: var(--color-gray-button-background-light); + border-radius: var(--border-radius-buttons); +} + +.biometricsError { + align-self: center; + + margin-top: 0.25rem; + padding: 0.375rem 0.5rem; + + font-size: 1.0625rem; + font-weight: 700; + line-height: 1; + color: var(--color-transaction-red-text); + + background-color: var(--color-transaction-red-background); + border-radius: var(--border-radius-tiny); +} + +.stepTransition { + width: auto !important; + height: auto !important; + + white-space: nowrap; +} + +.passwordFormContainer { + overflow-y: scroll; + display: flex; + flex-direction: column; + align-items: center; + + max-width: 27rem; + height: 32.5rem; + max-height: 100%; + margin: 0 auto; + padding: 0 1rem 1rem; + + @include adapt-padding-to-scrollbar(1rem); + + @include respond-above(xs) { + max-width: 31.4375rem; + } + + @supports (padding-bottom: env(safe-area-inset-bottom)) { + padding-bottom: calc(1rem + env(safe-area-inset-bottom)); + } +} + +.pinPadHeader { + display: flex; + flex-direction: column; + align-items: center; + + margin-top: auto; +} + +.headerBack { + cursor: var(--custom-cursor, pointer); + + position: absolute; + top: 1.5rem; + left: 0.5rem; + + display: flex; + align-items: center; + + padding: 0; + + font-size: 1.0625rem; + color: var(--color-blue); + + @supports (top: max(calc(env(safe-area-inset-top) + 0.375rem), 1.5rem)) { + top: max(calc(env(safe-area-inset-top) + 0.375rem), 1.5rem); + } +} + +.iconChevron { + font-size: 1.5rem; +} + +.biometricsIcon { + width: 7rem; + height: 7rem; + margin: 5rem auto 2rem; +} + +.biometricsTitle { + margin-bottom: 2rem; + + font-size: 1.6875rem; + font-weight: 800; + text-align: center; +} + +.biometricsSubtitle { + margin: 0 2rem; + padding-bottom: 2rem; + + font-size: 1.0625rem; + color: var(--color-gray-1); + text-align: center; +} + @supports (padding-bottom: env(safe-area-inset-bottom)) { @include respond-below(xs) { .disclaimerBackupDialog { diff --git a/src/components/auth/Auth.tsx b/src/components/auth/Auth.tsx index 5c38c6e9..46546367 100644 --- a/src/components/auth/Auth.tsx +++ b/src/components/auth/Auth.tsx @@ -14,8 +14,13 @@ import useLastCallback from '../../hooks/useLastCallback'; import SettingsAbout from '../settings/SettingsAbout'; import Transition from '../ui/Transition'; +import AuthBackupWalletModal from './AuthBackupWalletModal'; +import AuthConfirmPin from './AuthConfirmPin'; import AuthCreateBackup from './AuthCreateBackup'; +import AuthCreateBiometrics from './AuthCreateBiometrics'; +import AuthCreateNativeBiometrics from './AuthCreateNativeBiometrics'; import AuthCreatePassword from './AuthCreatePassword'; +import AuthCreatePin from './AuthCreatePin'; import AuthCreatingWallet from './AuthCreatingWallet'; import AuthDisclaimer from './AuthDisclaimer'; import AuthImportMnemonic from './AuthImportMnemonic'; @@ -24,16 +29,20 @@ import AuthStart from './AuthStart'; import styles from './Auth.module.scss'; type StateProps = Pick; const RENDER_COUNT = Object.keys(AuthState).length / 2; const Auth = ({ state, + biometricsStep, + error, isLoading, mnemonic, mnemonicCheckIndexes, + isBackupModalOpen, method, }: StateProps) => { const { @@ -48,9 +57,11 @@ const Auth = ({ true, ) ?? -1; + const [prevKey, setPrevKey] = useState(undefined); const [nextKey, setNextKey] = useState(renderingAuthState + 1); - const updateNextKey = useLastCallback(() => { + const updateRenderingKeys = useLastCallback(() => { setNextKey(renderingAuthState + 1); + setPrevKey(renderingAuthState === AuthState.confirmPin ? AuthState.createPin : undefined); }); // eslint-disable-next-line consistent-return @@ -60,57 +71,95 @@ const Auth = ({ return ; case AuthState.creatingWallet: return ; + case AuthState.createPin: + return ; + case AuthState.confirmPin: + return ; + case AuthState.createBiometrics: + return ( + + ); + case AuthState.createNativeBiometrics: + return ( + + ); case AuthState.createPassword: return ; case AuthState.createBackup: - return ; + return ; case AuthState.disclaimerAndBackup: return ( - + ); case AuthState.importWallet: return ; + case AuthState.importWalletCreatePin: + return ; + case AuthState.importWalletConfirmPin: + return ; case AuthState.disclaimer: return ( ); + case AuthState.importWalletCreateNativeBiometrics: + return ( + + ); case AuthState.importWalletCreatePassword: return ; + case AuthState.importWalletCreateBiometrics: + return ( + + ); case AuthState.about: return ; } } return ( - - {renderAuthScreen} - + <> + + {renderAuthScreen} + + + ); }; export default memo(withGlobal((global): StateProps => { return pick(global.auth, [ - 'state', 'mnemonic', 'mnemonicCheckIndexes', 'isLoading', 'method', + 'state', 'biometricsStep', 'error', 'mnemonic', 'mnemonicCheckIndexes', 'isLoading', 'method', + 'isBackupModalOpen', ]); })(Auth)); diff --git a/src/components/auth/AuthBackupWalletModal.tsx b/src/components/auth/AuthBackupWalletModal.tsx new file mode 100644 index 00000000..dd599d22 --- /dev/null +++ b/src/components/auth/AuthBackupWalletModal.tsx @@ -0,0 +1,127 @@ +import React, { memo, useState } from '../../lib/teact/teact'; +import { getActions } from '../../global'; + +import resolveModalTransitionName from '../../util/resolveModalTransitionName'; + +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; + +import Modal from '../ui/Modal'; +import Transition from '../ui/Transition'; +import MnemonicCheck from './MnemonicCheck'; +import MnemonicList from './MnemonicList'; +import SafetyRules from './SafetyRules'; + +import modalStyles from '../ui/Modal.module.scss'; +import styles from './Auth.module.scss'; + +interface OwnProps { + isOpen?: boolean; + mnemonic?: string[]; + checkIndexes?: number[]; +} + +const SLIDE_ANIMATION_DURATION_MS = 250; + +enum BackupState { + Accept, + View, + Confirm, +} +function AuthBackupWalletModal({ + isOpen, mnemonic, checkIndexes, +}: OwnProps) { + const { + restartCheckMnemonicIndexes, + closeAuthBackupWalletModal, + } = getActions(); + + const lang = useLang(); + const [renderingKey, setRenderingKey] = useState(BackupState.Accept); + const [nextKey, setNextKey] = useState(BackupState.View); + + const handleModalClose = useLastCallback(() => { + setRenderingKey(BackupState.Accept); + setNextKey(BackupState.View); + }); + + const handleMnemonicView = useLastCallback(() => { + setRenderingKey(BackupState.View); + setNextKey(BackupState.Confirm); + }); + const handleRestartCheckMnemonic = useLastCallback(() => { + handleMnemonicView(); + + setTimeout(() => { + restartCheckMnemonicIndexes(); + }, SLIDE_ANIMATION_DURATION_MS); + }); + + const handleShowMnemonicCheck = useLastCallback(() => { + setRenderingKey(BackupState.Confirm); + setNextKey(undefined); + }); + + const handleMnemonicCheckSubmit = useLastCallback(() => { + closeAuthBackupWalletModal({ isBackupCreated: true }); + }); + + // eslint-disable-next-line consistent-return + function renderModalContent(isScreenActive: boolean, isFrom: boolean, currentScreenKey: number) { + switch (currentScreenKey) { + case BackupState.Accept: + return ( + + ); + + case BackupState.View: + return ( + + ); + + case BackupState.Confirm: + return ( + + ); + } + } + return ( + + + {renderModalContent} + + + ); +} + +export default memo(AuthBackupWalletModal); diff --git a/src/components/auth/AuthBackupWarning.tsx b/src/components/auth/AuthBackupWarning.tsx new file mode 100644 index 00000000..46007d50 --- /dev/null +++ b/src/components/auth/AuthBackupWarning.tsx @@ -0,0 +1,50 @@ +import React, { memo } from '../../lib/teact/teact'; +import { getActions } from '../../global'; + +import renderText from '../../global/helpers/renderText'; +import buildClassName from '../../util/buildClassName'; + +import useLang from '../../hooks/useLang'; + +import Button from '../ui/Button'; +import Modal from '../ui/Modal'; + +import styles from './Auth.module.scss'; + +interface OwnProps { + isOpen: boolean; + onSkip: NoneToVoidFunction; + onClose: NoneToVoidFunction; +} + +function AuthBackupWarning({ isOpen, onSkip, onClose }: OwnProps) { + const { openAuthBackupWalletModal } = getActions(); + + const lang = useLang(); + + return ( + +

{renderText(lang('$auth_backup_warning_notice'))}

+
+ + +
+
+ ); +} + +export default memo(AuthBackupWarning); diff --git a/src/components/auth/AuthConfirmPin.tsx b/src/components/auth/AuthConfirmPin.tsx new file mode 100644 index 00000000..bcc0c9fb --- /dev/null +++ b/src/components/auth/AuthConfirmPin.tsx @@ -0,0 +1,111 @@ +import React, { memo, useEffect, useState } from '../../lib/teact/teact'; +import { getActions, withGlobal } from '../../global'; + +import type { AuthMethod } from '../../global/types'; + +import { PIN_LENGTH } from '../../config'; +import buildClassName from '../../util/buildClassName'; +import { pause } from '../../util/schedulers'; +import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; + +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; + +import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; +import Button from '../ui/Button'; +import PinPad from '../ui/PinPad'; + +import styles from './Auth.module.scss'; + +interface OwnProps { + isActive?: boolean; + method?: AuthMethod; +} + +interface StateProps { + pin: string; +} + +const SUBMIT_PAUSE_MS = 1500; + +const AuthConfirmPin = ({ + isActive, + method, + pin, +}: OwnProps & StateProps) => { + const { confirmPin, cancelConfirmPin } = getActions(); + + const lang = useLang(); + const [pinConfirm, setPinConfirm] = useState(''); + const [error, setError] = useState(''); + const [isConfirmed, setIsConfirmed] = useState(false); + const isImporting = method !== 'createAccount'; + + const handleBackClick = useLastCallback(() => { + cancelConfirmPin({ isImporting }); + }); + + useHistoryBack({ + isActive, + onBack: handleBackClick, + }); + + useEffect(() => { + if (isActive) { + setPinConfirm(''); + setError(''); + setIsConfirmed(false); + } + }, [isActive]); + + const handleChange = useLastCallback((value: string) => { + setPinConfirm(value); + setError(''); + }); + + const handleSubmit = useLastCallback(async (value: string) => { + if (value === pin) { + setIsConfirmed(true); + await pause(SUBMIT_PAUSE_MS); + confirmPin({ isImporting }); + } else { + setError(lang('Codes don\'t match')); + } + }); + + return ( +
+ + +
+ +
{lang(isImporting ? 'Wallet is imported!' : 'Wallet is ready!')}
+
+ + +
+ ); +}; + +export default memo(withGlobal((global) => { + return { + pin: global.auth.password, + }; +})(AuthConfirmPin)); diff --git a/src/components/auth/AuthCreateBackup.tsx b/src/components/auth/AuthCreateBackup.tsx index 53892093..a3b0d097 100644 --- a/src/components/auth/AuthCreateBackup.tsx +++ b/src/components/auth/AuthCreateBackup.tsx @@ -1,111 +1,31 @@ -import React, { memo, useState } from '../../lib/teact/teact'; +import React, { memo } from '../../lib/teact/teact'; import { getActions } from '../../global'; import renderText from '../../global/helpers/renderText'; import buildClassName from '../../util/buildClassName'; import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; -import useFlag from '../../hooks/useFlag'; import useLang from '../../hooks/useLang'; -import useLastCallback from '../../hooks/useLastCallback'; import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; import Button from '../ui/Button'; -import Modal from '../ui/Modal'; -import Transition from '../ui/Transition'; -import MnemonicCheck from './MnemonicCheck'; -import MnemonicList from './MnemonicList'; -import SafetyRules from './SafetyRules'; -import modalStyles from '../ui/Modal.module.scss'; import styles from './Auth.module.scss'; interface OwnProps { isActive?: boolean; - mnemonic?: string[]; - checkIndexes?: number[]; } -enum BackupState { - Accept, - View, - Confirm, -} - -const SLIDE_ANIMATION_DURATION_MS = 250; - -const AuthCreateBackup = ({ isActive, mnemonic, checkIndexes }: OwnProps) => { - const { afterCheckMnemonic, skipCheckMnemonic, restartCheckMnemonicIndexes } = getActions(); +const AuthCreateBackup = ({ isActive }: OwnProps) => { + const { skipCheckMnemonic, openAuthBackupWalletModal } = getActions(); const lang = useLang(); - const [isModalOpen, openModal, closeModal] = useFlag(); - - const [renderingKey, setRenderingKey] = useState(BackupState.Accept); - const [nextKey, setNextKey] = useState(BackupState.View); - - const handleModalClose = useLastCallback(() => { - setRenderingKey(BackupState.Accept); - setNextKey(BackupState.View); - }); - - const handleMnemonicView = useLastCallback(() => { - setRenderingKey(BackupState.View); - setNextKey(BackupState.Confirm); - }); - - const handleRestartCheckMnemonic = useLastCallback(() => { - handleMnemonicView(); - - setTimeout(() => { - restartCheckMnemonicIndexes(); - }, SLIDE_ANIMATION_DURATION_MS); - }); - - const handleShowMnemonicCheck = useLastCallback(() => { - setRenderingKey(BackupState.Confirm); - setNextKey(undefined); - }); - - const handleMnemonicCheckSubmit = useLastCallback(() => { - closeModal(); - afterCheckMnemonic(); - }); - - // eslint-disable-next-line consistent-return - function renderModalContent(isScreenActive: boolean, isFrom: boolean, currentScreenKey: number) { - switch (currentScreenKey) { - case BackupState.Accept: - return ; - - case BackupState.View: - return ( - - ); - - case BackupState.Confirm: - return ( - - ); - } - } return (
{

{renderText(lang('$auth_backup_description3'))}

-
- - - - {renderModalContent} - -
); }; diff --git a/src/components/auth/AuthCreateBiometrics.tsx b/src/components/auth/AuthCreateBiometrics.tsx new file mode 100644 index 00000000..22c16c7e --- /dev/null +++ b/src/components/auth/AuthCreateBiometrics.tsx @@ -0,0 +1,143 @@ +import React, { memo } from '../../lib/teact/teact'; +import { getActions } from '../../global'; + +import type { AuthMethod } from '../../global/types'; + +import { ANIMATED_STICKER_HUGE_SIZE_PX } from '../../config'; +import buildClassName from '../../util/buildClassName'; +import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; +import { getFormId } from './helpers/getFormId'; + +import useCurrentOrPrev from '../../hooks/useCurrentOrPrev'; +import useFlag from '../../hooks/useFlag'; +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; +import useShowTransition from '../../hooks/useShowTransition'; + +import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; +import Button from '../ui/Button'; +import CreatePasswordForm from '../ui/CreatePasswordForm'; +import Modal from '../ui/Modal'; + +import styles from './Auth.module.scss'; + +interface OwnProps { + isActive?: boolean; + method?: AuthMethod; + isLoading?: boolean; + error?: string; + biometricsStep?: 1 | 2; +} + +const AuthCreateBiometrics = ({ + isActive, + method, + biometricsStep, + error, + isLoading, +}: OwnProps) => { + const { afterCreatePassword, afterCreateBiometrics, restartAuth } = getActions(); + + const lang = useLang(); + const [isPasswordModalOpen, openPasswordModal, closePasswordModal] = useFlag(false); + const isImporting = method !== 'createAccount'; + const formId = getFormId(method!); + const { + shouldRender: shouldRenderSteps, + transitionClassNames: stepsClassNames, + } = useShowTransition(Boolean(biometricsStep)); + const { + shouldRender: shouldRenderError, + transitionClassNames: errorClassNames, + } = useShowTransition(Boolean(error && !shouldRenderSteps)); + const renderingError = useCurrentOrPrev(error, true); + + useHistoryBack({ + isActive, + onBack: restartAuth, + }); + + const handleSubmit = useLastCallback((password: string, isPasswordNumeric: boolean) => { + closePasswordModal(); + afterCreatePassword({ password, isPasswordNumeric }); + }); + + return ( + <> +
+ +
{lang('Congratulations!')}
+

+ {lang(isImporting ? 'The wallet is imported' : 'The wallet is ready')}. +

+

+ {lang('Create a password or use biometric authentication to protect it.')} +

+ + {shouldRenderSteps && ( +
+ {lang(biometricsStep === 1 ? 'Step 1 of 2. Registration' : 'Step 2 of 2. Verification')} +
+ )} + {shouldRenderError && !shouldRenderSteps && ( +
+ {lang(renderingError || 'Unknown error')} +
+ )} + +
+ + +
+
+ + + + + + + ); +}; + +export default memo(AuthCreateBiometrics); diff --git a/src/components/auth/AuthCreateNativeBiometrics.tsx b/src/components/auth/AuthCreateNativeBiometrics.tsx new file mode 100644 index 00000000..2297e3f6 --- /dev/null +++ b/src/components/auth/AuthCreateNativeBiometrics.tsx @@ -0,0 +1,77 @@ +import React, { memo } from '../../lib/teact/teact'; +import { getActions } from '../../global'; + +import buildClassName from '../../util/buildClassName'; +import { getIsFaceIdAvailable, getIsTouchIdAvailable } from '../../util/capacitor'; + +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; + +import Button from '../ui/Button'; + +import styles from './Auth.module.scss'; + +import touchIdSvg from '../../assets/settings/settings_biometrics.svg'; +import faceIdSvg from '../../assets/settings/settings_face-id.svg'; + +interface OwnProps { + isActive?: boolean; + isLoading?: boolean; +} + +const AuthCreateNativeBiometrics = ({ isActive, isLoading }: OwnProps) => { + const { afterCreateNativeBiometrics, skipCreateNativeBiometrics, restartAuth } = getActions(); + + const lang = useLang(); + + const isFaceId = getIsFaceIdAvailable(); + const isTouchId = getIsTouchIdAvailable(); + + useHistoryBack({ + isActive, + onBack: restartAuth, + }); + + return ( +
+ + + + +
+ {isFaceId ? lang('Use Face ID') : (isTouchId ? lang('Use Touch ID') : lang('Use Biometrics'))} +
+
+ {lang('You can connect your biometric data for more convenience')} +
+ +
+ + +
+
+ ); +}; + +export default memo(AuthCreateNativeBiometrics); diff --git a/src/components/auth/AuthCreatePassword.tsx b/src/components/auth/AuthCreatePassword.tsx index 4ec925de..534a2bd7 100644 --- a/src/components/auth/AuthCreatePassword.tsx +++ b/src/components/auth/AuthCreatePassword.tsx @@ -1,6 +1,4 @@ -import React, { - memo, useEffect, useRef, useState, -} from '../../lib/teact/teact'; +import React, { memo } from '../../lib/teact/teact'; import { getActions } from '../../global'; import type { AuthMethod } from '../../global/types'; @@ -8,18 +6,13 @@ import type { AuthMethod } from '../../global/types'; import buildClassName from '../../util/buildClassName'; import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; -import useFlag from '../../hooks/useFlag'; -import useFocusAfterAnimation from '../../hooks/useFocusAfterAnimation'; +import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; -import { usePasswordValidation } from '../../hooks/usePasswordValidation'; import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; -import Button from '../ui/Button'; -import Input from '../ui/Input'; -import Modal from '../ui/Modal'; +import CreatePasswordForm from '../ui/CreatePasswordForm'; -import modalStyles from '../ui/Modal.module.scss'; import styles from './Auth.module.scss'; interface OwnProps { @@ -36,140 +29,20 @@ const AuthCreatePassword = ({ const { afterCreatePassword, restartAuth } = getActions(); const lang = useLang(); - - // eslint-disable-next-line no-null/no-null - const firstInputRef = useRef(null); - const [firstPassword, setFirstPassword] = useState(''); - const [secondPassword, setSecondPassword] = useState(''); - const [hasError, setHasError] = useState(false); - const [isJustSubmitted, setIsJustSubmitted] = useState(false); - const [isPasswordFocused, markPasswordFocused, unmarkPasswordFocused] = useFlag(false); - const [isSecondPasswordFocused, markSecondPasswordFocused, unmarkSecondPasswordFocused] = useFlag(false); - const [isPasswordsNotEqual, setIsPasswordsNotEqual] = useState(false); - const [isWeakPasswordModalOpen, openWeakPasswordModal, closeWeakPasswordModal] = useFlag(false); - const canSubmit = firstPassword.length > 0 && secondPassword.length > 0 && !hasError; const isImporting = method !== 'createAccount'; const formId = getFormId(method!); - const validation = usePasswordValidation({ - firstPassword, - secondPassword, - }); - - useFocusAfterAnimation(firstInputRef, !isActive); - - useEffect(() => { - setIsPasswordsNotEqual(false); - if (firstPassword === '' || !isActive || isPasswordFocused) { - setHasError(false); - return; - } - - const { noEqual } = validation; - - if ((!isSecondPasswordFocused || isJustSubmitted) && noEqual && secondPassword !== '') { - setHasError(true); - setIsPasswordsNotEqual(true); - } else if (!noEqual || secondPassword === '' || (isSecondPasswordFocused && !isJustSubmitted)) { - setHasError(false); - } - }, [ - isActive, firstPassword, secondPassword, validation, isSecondPasswordFocused, isPasswordFocused, isJustSubmitted, - ]); - - const handleFirstPasswordChange = useLastCallback((value: string) => { - setFirstPassword(value); - if (isJustSubmitted) { - setIsJustSubmitted(false); - } + useHistoryBack({ + isActive, + onBack: restartAuth, }); - const handleSecondPasswordChange = useLastCallback((value: string) => { - setSecondPassword(value); - if (isJustSubmitted) { - setIsJustSubmitted(false); - } - }); - - const handleCancel = useLastCallback(() => { - restartAuth(); - }); - - const handleSubmit = useLastCallback((e: React.FormEvent) => { - e.preventDefault(); - e.stopPropagation(); - - if (!canSubmit) { - return; - } - - if (firstPassword !== secondPassword) { - setIsJustSubmitted(true); - setHasError(true); - setIsPasswordsNotEqual(true); - return; - } - - const isWeakPassword = Object.values(validation).find((rule) => rule); - - if (isWeakPassword && !isWeakPasswordModalOpen) { - openWeakPasswordModal(); - return; - } - - if (isWeakPasswordModalOpen) { - closeWeakPasswordModal(); - } - afterCreatePassword({ password: firstPassword }); + const handleSubmit = useLastCallback((password: string, isPasswordNumeric: boolean) => { + afterCreatePassword({ password, isPasswordNumeric }); }); - const shouldRenderError = hasError && !isPasswordFocused; - - function renderErrors() { - if (isPasswordsNotEqual) { - return ( -
- {lang('Passwords must be equal.')} -
- ); - } - - const { - invalidLength, - noUpperCase, - noLowerCase, - noNumber, - noSpecialChar, - } = validation; - - return ( -
- {lang('To protect your wallet as much as possible, use a password with')} - - {' '}{lang('$auth_password_rule_8chars')}, - - - {' '}{lang('$auth_password_rule_one_small_char')}, - - - {' '}{lang('$auth_password_rule_one_capital_char')}, - - - {' '}{lang('$auth_password_rule_one_digit')}, - - - {' '}{lang('$auth_password_rule_one_special_char')} - . -
- ); - } - return ( -
+
-
- - -
- - {renderErrors()} - -
- - -
- - -

- {lang('Your have entered an insecure password, which can be easily guessed by scammers.')} -

-

- {lang('Continue or change password to something more secure?')} -

-
- - -
-
- + +
); }; -function getValidationRuleClass(shouldRenderError: boolean, ruleHasError: boolean) { - return buildClassName( - styles.passwordRule, - !ruleHasError ? styles.valid : shouldRenderError ? styles.invalid : undefined, - ); -} - // eslint-disable-next-line consistent-return function getFormId(method: AuthMethod) { switch (method) { diff --git a/src/components/auth/AuthCreatePin.tsx b/src/components/auth/AuthCreatePin.tsx new file mode 100644 index 00000000..2cebf4bf --- /dev/null +++ b/src/components/auth/AuthCreatePin.tsx @@ -0,0 +1,82 @@ +import React, { memo, useEffect, useState } from '../../lib/teact/teact'; +import { getActions } from '../../global'; + +import type { AuthMethod } from '../../global/types'; + +import { PIN_LENGTH } from '../../config'; +import buildClassName from '../../util/buildClassName'; +import { pause } from '../../util/schedulers'; +import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; + +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; + +import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; +import Button from '../ui/Button'; +import PinPad from '../ui/PinPad'; + +import styles from './Auth.module.scss'; + +interface OwnProps { + isActive?: boolean; + method?: AuthMethod; +} + +const SUBMIT_PAUSE_MS = 750; + +const AuthCreatePin = ({ + isActive, + method, +}: OwnProps) => { + const { createPin, restartAuth } = getActions(); + + const lang = useLang(); + const [pin, setPin] = useState(''); + const isImporting = method !== 'createAccount'; + + useEffect(() => { + if (isActive) { + setPin(''); + } + }, [isActive]); + + useHistoryBack({ + isActive, + onBack: restartAuth, + }); + + const handleSubmit = useLastCallback(async (value: string) => { + await pause(SUBMIT_PAUSE_MS); + createPin({ pin: value, isImporting }); + }); + + return ( +
+ + +
+ +
{lang(isImporting ? 'Wallet is imported!' : 'Wallet is ready!')}
+
+ +
+ ); +}; + +export default memo(AuthCreatePin); diff --git a/src/components/auth/AuthDisclaimer.tsx b/src/components/auth/AuthDisclaimer.tsx index e8cf8a1f..7630c498 100644 --- a/src/components/auth/AuthDisclaimer.tsx +++ b/src/components/auth/AuthDisclaimer.tsx @@ -1,4 +1,4 @@ -import React, { memo, useState } from '../../lib/teact/teact'; +import React, { memo } from '../../lib/teact/teact'; import { getActions } from '../../global'; import { ANIMATED_STICKER_MIDDLE_SIZE_PX } from '../../config'; @@ -6,7 +6,9 @@ import renderText from '../../global/helpers/renderText'; import buildClassName from '../../util/buildClassName'; import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; +import { useOpenFromMainBottomSheet } from '../../hooks/useDelegatedBottomSheet'; import useFlag from '../../hooks/useFlag'; +import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; import useShowTransition from '../../hooks/useShowTransition'; @@ -14,50 +16,44 @@ import useShowTransition from '../../hooks/useShowTransition'; import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; import Button from '../ui/Button'; import Checkbox from '../ui/Checkbox'; -import Modal from '../ui/Modal'; -import Transition from '../ui/Transition'; -import MnemonicCheck from './MnemonicCheck'; -import MnemonicList from './MnemonicList'; -import SafetyRules from './SafetyRules'; +import AuthBackupWarning from './AuthBackupWarning'; -import modalStyles from '../ui/Modal.module.scss'; import styles from './Auth.module.scss'; interface OwnProps { isActive?: boolean; isImport?: boolean; - mnemonic?: string[]; - checkIndexes?: number[]; } -enum BackupState { - Accept, - View, - Confirm, -} - -const SLIDE_ANIMATION_DURATION_MS = 250; - const AuthDisclaimer = ({ - isActive, isImport, mnemonic, checkIndexes, + isActive, isImport, }: OwnProps) => { const { - afterCheckMnemonic, skipCheckMnemonic, - restartCheckMnemonicIndexes, confirmDisclaimer, + cancelDisclaimer, } = getActions(); const lang = useLang(); - const [isModalOpen, openModal, closeModal] = useFlag(); - const [isInformationConfirmed, setIsInformationConfirmed] = useState(false); + const [isInformationConfirmed, markInformationConfirmed, unmarkInformationConfirmed] = useFlag(false); const { shouldRender: shouldRenderStartButton, transitionClassNames: startButtonTransitionClassNames, } = useShowTransition(isInformationConfirmed && isImport); - const [renderingKey, setRenderingKey] = useState(BackupState.Accept); - const [nextKey, setNextKey] = useState(BackupState.View); + useHistoryBack({ + isActive, + onBack: cancelDisclaimer, + }); + const setIsInformationConfirmed = useLastCallback((isConfirmed: boolean) => { + if (isConfirmed) { + markInformationConfirmed(); + } else { + unmarkInformationConfirmed(); + } + }); + + useOpenFromMainBottomSheet('backup-warning', markInformationConfirmed); const handleCloseBackupWarningModal = useLastCallback(() => { setIsInformationConfirmed(false); @@ -68,72 +64,12 @@ const AuthDisclaimer = ({ handleCloseBackupWarningModal(); }); - const handleModalClose = useLastCallback(() => { - setRenderingKey(BackupState.Accept); - setNextKey(BackupState.View); - }); - - const handleMnemonicView = useLastCallback(() => { - setRenderingKey(BackupState.View); - setNextKey(BackupState.Confirm); - }); - - const handleRestartCheckMnemonic = useLastCallback(() => { - handleMnemonicView(); - - setTimeout(() => { - restartCheckMnemonicIndexes(); - }, SLIDE_ANIMATION_DURATION_MS); - }); - - const handleShowMnemonicCheck = useLastCallback(() => { - setRenderingKey(BackupState.Confirm); - setNextKey(undefined); - }); - - const handleMnemonicCheckSubmit = useLastCallback(() => { - closeModal(); - // Don't flicker the backup notice modal after submitting a mnemonic - setIsInformationConfirmed(false); - afterCheckMnemonic(); - }); - - // eslint-disable-next-line consistent-return - function renderModalContent(isScreenActive: boolean, isFrom: boolean, currentScreenKey: number) { - switch (currentScreenKey) { - case BackupState.Accept: - return ; - - case BackupState.View: - return ( - - ); - - case BackupState.Confirm: - return ( - - ); - } - } - return (
- -

{renderText(lang('$auth_backup_warning_notice'))}

-
- - -
-
- - - - {renderModalContent} - - + {!isImport && ( + + )}
); }; diff --git a/src/components/auth/AuthImportMnemonic.tsx b/src/components/auth/AuthImportMnemonic.tsx index 3dd8c964..2d904f8e 100644 --- a/src/components/auth/AuthImportMnemonic.tsx +++ b/src/components/auth/AuthImportMnemonic.tsx @@ -11,6 +11,7 @@ import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; import useClipboardPaste from '../../hooks/useClipboardPaste'; import { useDeviceScreen } from '../../hooks/useDeviceScreen'; +import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; @@ -99,6 +100,11 @@ const AuthImportMnemonic = ({ isActive, isLoading, error }: OwnProps & StateProp } }); + useHistoryBack({ + isActive, + onBack: handleCancel, + }); + useEffect(() => { return isSubmitDisabled ? undefined diff --git a/src/components/auth/MnemonicCheck.tsx b/src/components/auth/MnemonicCheck.tsx index bd8f8b91..a0a8bf0a 100644 --- a/src/components/auth/MnemonicCheck.tsx +++ b/src/components/auth/MnemonicCheck.tsx @@ -8,7 +8,7 @@ import renderText from '../../global/helpers/renderText'; import buildClassName from '../../util/buildClassName'; import { areSortedArraysEqual } from '../../util/iteratees'; -import { useDeviceScreen } from '../../hooks/useDeviceScreen'; +import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; @@ -36,7 +36,6 @@ function MnemonicCheck({ const lang = useLang(); const [words, setWords] = useState>({}); const [hasMnemonicError, setHasMnemonicError] = useState(false); - const { isPortrait } = useDeviceScreen(); useEffect(() => { if (isActive) { @@ -45,6 +44,11 @@ function MnemonicCheck({ } }, [isActive]); + useHistoryBack({ + isActive, + onBack: onCancel, + }); + const handleSetWord = useLastCallback((value: string, index: number) => { setWords({ ...words, @@ -83,7 +87,7 @@ function MnemonicCheck({ labelText={`${key + 1}`} value={words[key]} isInModal={isInModal} - suggestionsPosition={i > 1 || isPortrait ? 'top' : undefined} + suggestionsPosition={i > 1 ? 'top' : undefined} inputArg={key} className={styles.checkMnemonicInput} onInput={handleSetWord} @@ -98,8 +102,8 @@ function MnemonicCheck({ )}
- - + +
diff --git a/src/components/auth/MnemonicList.tsx b/src/components/auth/MnemonicList.tsx index d254daff..010fd3e0 100644 --- a/src/components/auth/MnemonicList.tsx +++ b/src/components/auth/MnemonicList.tsx @@ -4,6 +4,7 @@ import { MNEMONIC_COUNT } from '../../config'; import renderText from '../../global/helpers/renderText'; import buildClassName from '../../util/buildClassName'; +import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; import Button from '../ui/Button'; @@ -13,16 +14,22 @@ import modalStyles from '../ui/Modal.module.scss'; import styles from './Auth.module.scss'; type OwnProps = { + isActive?: boolean; mnemonic?: string[]; onClose: NoneToVoidFunction; onNext: NoneToVoidFunction; }; function MnemonicList({ - mnemonic, onNext, onClose, + isActive, mnemonic, onNext, onClose, }: OwnProps) { const lang = useLang(); + useHistoryBack({ + isActive, + onBack: onClose, + }); + return (
diff --git a/src/components/auth/helpers/getFormId.ts b/src/components/auth/helpers/getFormId.ts new file mode 100644 index 00000000..10ac865a --- /dev/null +++ b/src/components/auth/helpers/getFormId.ts @@ -0,0 +1,13 @@ +import type { AuthMethod } from '../../../global/types'; + +// eslint-disable-next-line consistent-return +export function getFormId(method: AuthMethod) { + switch (method) { + case 'createAccount': + return 'auth_create_password'; + case 'importMnemonic': + return 'auth_import_mnemonic_password'; + case 'importHardwareWallet': + return 'auth_import_hardware_password'; + } +} diff --git a/src/components/common/Countdown.module.scss b/src/components/common/Countdown.module.scss new file mode 100644 index 00000000..478f7cf3 --- /dev/null +++ b/src/components/common/Countdown.module.scss @@ -0,0 +1,12 @@ +.time { + font-size: 0.8125rem; + font-weight: 700; + line-height: 0.8125rem; + color: var(--color-gray-2); + + transition: color 150ms; +} + +.timeWarning { + color: var(--color-red); +} diff --git a/src/components/common/Countdown.tsx b/src/components/common/Countdown.tsx new file mode 100644 index 00000000..1092c697 --- /dev/null +++ b/src/components/common/Countdown.tsx @@ -0,0 +1,69 @@ +import React, { memo, useEffect, useState } from '../../lib/teact/teact'; + +import type { LangFn } from '../../hooks/useLang'; + +import buildClassName from '../../util/buildClassName'; + +import useLang from '../../hooks/useLang'; + +import styles from './Countdown.module.scss'; + +interface OwnProps { + timestamp: number; + deadline: number; + onCompleted?: NoneToVoidFunction; +} + +const WARNING_TIME = 5 * 60; // 5 minutes in seconds; +const SECOND = 1000; + +function Countdown({ + timestamp, + deadline, + onCompleted, +}: OwnProps) { + const lang = useLang(); + const initialSeconds = Math.floor((timestamp + deadline - Date.now()) / 1000); + const [secondsLeft, setSecondsLeft] = useState(Math.max(initialSeconds, 0)); + const shouldShowWarning = secondsLeft <= WARNING_TIME; + + useEffect(() => { + const timerId = setTimeout(() => { + if (secondsLeft <= 0) return; + + setSecondsLeft(Math.floor((timestamp + deadline - Date.now()) / 1000)); + }, SECOND); + + if (secondsLeft <= 0) { + onCompleted?.(); + } + + return () => clearTimeout(timerId); + }, [secondsLeft, onCompleted, timestamp, deadline]); + + return ( + + {formatTime(lang, secondsLeft)} + + ); +} + +function formatTime(lang: LangFn, seconds: number): string { + const pad = (num: number) => num.toString().padStart(2, '0'); + + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const remainingSeconds = seconds % 60; + + let formattedTime = hours > 0 ? `${hours}:` : ''; + formattedTime += `${hours > 0 ? pad(minutes) : minutes}:`; + formattedTime += pad(remainingSeconds); + + return formattedTime; +} + +export default memo(Countdown); diff --git a/src/components/common/SwapResult.module.scss b/src/components/common/SwapResult.module.scss new file mode 100644 index 00000000..21167db6 --- /dev/null +++ b/src/components/common/SwapResult.module.scss @@ -0,0 +1,60 @@ +.sticker { + margin: 0 auto 1.25rem; +} + +.buttons { + display: flex; + justify-content: center; + + margin-top: 2rem; +} + +.button { + min-width: auto !important; + height: auto !important; + padding: 0.75rem 1.25rem !important; + + line-height: 1rem !important; + + border-radius: 0 !important; + + &:first-child { + border-top-left-radius: var(--border-radius-buttons) !important; + border-bottom-left-radius: var(--border-radius-buttons) !important; + } + + &:last-child { + border-top-right-radius: var(--border-radius-buttons) !important; + border-bottom-right-radius: var(--border-radius-buttons) !important; + } + + & + & { + /* stylelint-disable-next-line plugin/whole-pixel */ + box-shadow: inset 0.025rem 0 0 0 var(--color-separator) + } +} + + +.changellyInfoBlock { + display: flex; + flex-direction: column; + gap: 1rem; + align-items: center; + + margin-top: 2rem; +} + +.changellyDescription { + font-size: 0.8125rem; + font-weight: 500; + color: var(--color-gray-2); + text-align: center; +} + +.changellyDescriptionBold { + font-weight: 700; +} + +.changellyTextField { + width: 100%; +} diff --git a/src/components/common/SwapResult.tsx b/src/components/common/SwapResult.tsx new file mode 100644 index 00000000..258defb1 --- /dev/null +++ b/src/components/common/SwapResult.tsx @@ -0,0 +1,124 @@ +import React, { memo } from '../../lib/teact/teact'; + +import type { UserSwapToken } from '../../global/types'; +import { SwapType } from '../../global/types'; + +import getBlockchainNetworkName from '../../util/swap/getBlockchainNetworkName'; +import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; + +import useLang from '../../hooks/useLang'; + +import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; +import Button from '../ui/Button'; +import InteractiveTextField from '../ui/InteractiveTextField'; +import SwapTokensInfo from './SwapTokensInfo'; + +import styles from './SwapResult.module.scss'; + +interface OwnProps { + tokenIn?: UserSwapToken; + tokenOut?: UserSwapToken; + amountIn?: number; + amountOut?: number; + playAnimation?: boolean; + firstButtonText?: string; + secondButtonText?: string; + swapType?: SwapType; + toAddress?: string; + onFirstButtonClick?: NoneToVoidFunction; + onSecondButtonClick?: NoneToVoidFunction; +} + +function SwapResult({ + tokenIn, + tokenOut, + amountIn, + amountOut, + playAnimation, + firstButtonText, + secondButtonText, + swapType, + toAddress = '', + onFirstButtonClick, + onSecondButtonClick, +}: OwnProps) { + const lang = useLang(); + + function renderButtons() { + if (!firstButtonText && !secondButtonText) { + return undefined; + } + + return ( +
+ {firstButtonText && ( + + )} + {secondButtonText && ( + + )} +
+ ); + } + + function renderSticker() { + if (swapType === SwapType.CrosschainFromTon) return undefined; + + return ( + + ); + } + + function renderChangellyInfo() { + if (swapType !== SwapType.CrosschainFromTon) return undefined; + + return ( +
+ + { + lang('$swap_changelly_from_ton_description', { + blockchain: ( + + {getBlockchainNetworkName(tokenOut?.blockchain)} + + ), + }) + } + + +
+ ); + } + + return ( + <> + {renderSticker()} + + + + {renderChangellyInfo()} + + {renderButtons()} + + ); +} + +export default memo(SwapResult); diff --git a/src/components/common/SwapTokensInfo.module.scss b/src/components/common/SwapTokensInfo.module.scss new file mode 100644 index 00000000..d9087358 --- /dev/null +++ b/src/components/common/SwapTokensInfo.module.scss @@ -0,0 +1,94 @@ + +.infoBlock { + position: relative; + + display: flex; + flex-direction: column; + gap: 1.25rem; + + width: 100%; +} + +.infoRow { + display: flex; + justify-content: space-between; + + padding: 0.875rem 1rem; + + background-color: var(--color-background-first); + border-radius: var(--border-radius-default); +} + +.infoRowIcon { + width: 2.25rem; + height: 2.25rem; + + border-radius: 50%; +} + +.infoRowToken { + display: flex; + gap: 0.625rem; +} + +.infoRowText { + display: flex; + flex-direction: column; +} + +.infoRowTextCenter { + justify-content: center; +} + +.infoRowTitle { + font-size: 0.9375rem; + font-weight: 600; + color: var(--color-black); +} + +.infoRowDescription { + font-size: 0.75rem; + font-weight: 600; + color: var(--color-gray-2); +} + +.infoRowAmount { + font-size: 0.9375rem; + font-weight: 600; + color: var(--color-gray-1); +} + +.infoRowAmountGreen { + color: var(--color-green); +} + +.infoRowAmountError { + color: var(--color-gray-3) +} + +.infoSeparator { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + display: flex; + align-items: center; + justify-content: center; + + width: 2.5rem; + height: 2.5rem; + + background-color: var(--color-background-second); + border-radius: 50%; +} + +.infoSeparatorIcon { + font-size: 1.5rem; + color: var(--color-gray-1); +} + +.infoSeparatorIconError { + font-size: 2.5rem; + color: var(--color-red); +} diff --git a/src/components/common/SwapTokensInfo.tsx b/src/components/common/SwapTokensInfo.tsx new file mode 100644 index 00000000..56ff90c4 --- /dev/null +++ b/src/components/common/SwapTokensInfo.tsx @@ -0,0 +1,71 @@ +import React, { memo } from '../../lib/teact/teact'; + +import type { ApiSwapAsset } from '../../api/types'; +import type { UserSwapToken } from '../../global/types'; + +import buildClassName from '../../util/buildClassName'; +import { formatCurrencyExtended } from '../../util/formatNumber'; +import getBlockchainNetworkName from '../../util/swap/getBlockchainNetworkName'; +import { ASSET_LOGO_PATHS } from '../ui/helpers/assetLogos'; + +import styles from './SwapTokensInfo.module.scss'; + +interface OwnProps { + tokenIn?: UserSwapToken | ApiSwapAsset; + amountIn?: number; + tokenOut?: UserSwapToken | ApiSwapAsset; + amountOut?: number; + isError?: boolean; +} + +function SwapTokensInfo({ + tokenIn, amountIn, tokenOut, amountOut, isError = false, +}: OwnProps) { + function renderTokenInfo(token?: UserSwapToken | ApiSwapAsset, amount = 0, isReceived = false) { + const image = token?.image ?? ASSET_LOGO_PATHS[token?.symbol.toLowerCase() as keyof typeof ASSET_LOGO_PATHS]; + const amountWithSign = isReceived ? amount : -amount; + return ( +
+
+ {token?.symbol} +
+ {token?.name} + {getBlockchainNetworkName(token?.blockchain)} +
+
+
+ {formatCurrencyExtended(amountWithSign, token?.symbol ?? '')} + +
+
+ ); + } + + return ( +
+ {renderTokenInfo(tokenIn, amountIn)} +
+ +
+ {renderTokenInfo(tokenOut, amountOut, true)} +
+ ); +} + +export default memo(SwapTokensInfo); diff --git a/src/components/common/TokenPriceChart.tsx b/src/components/common/TokenPriceChart.tsx index ce863ef3..ccc4c547 100644 --- a/src/components/common/TokenPriceChart.tsx +++ b/src/components/common/TokenPriceChart.tsx @@ -16,6 +16,7 @@ interface OwnProps { const IS_SMOOTH = true; const PATH_SMOOTHING = 0.0001; +const MIN_CHART_HEIGHT = 0.000000001; function TokenPriceChart({ width, @@ -35,7 +36,7 @@ function TokenPriceChart({ return { width: prices.length - 1, - height: max - min, + height: Math.max(MIN_CHART_HEIGHT, max - min), min, }; }, [prices]); diff --git a/src/components/common/TokenSelector.module.scss b/src/components/common/TokenSelector.module.scss new file mode 100644 index 00000000..a1d0264c --- /dev/null +++ b/src/components/common/TokenSelector.module.scss @@ -0,0 +1,288 @@ +@import "../../styles/mixins"; + +.tokenSelectInputWrapper { + position: relative; + + display: flex; + align-items: center; + + height: 2.25rem; + min-height: 2.25rem; + margin: 0 0.75rem 0.5rem; + + font-size: 1.25rem; + line-height: 1; + color: var(--color-gray-2); + + background-color: var(--color-close-button-background); + border-radius: var(--border-radius-buttons); +} + +.tokenSelectSearchIcon { + margin-left: 0.5rem; +} + +.tokenSelectSearchResetWrapper { + position: absolute; + right: 0.5rem; + + display: flex; + + width: 1rem; + height: 1rem; +} + +.tokenSelectSearchReset { + cursor: var(--custom-cursor, pointer); + + font-size: 1rem; + color: var(--color-close-button-background); + + background-color: var(--color-gray-2); + border-radius: 50%; +} + +.tokenSelectInput { + display: flex; + + width: 100%; + padding: 0 2.125rem 0 0.25rem; + + font-size: 1rem; + font-weight: 600; + color: var(--color-black); + + background: transparent; + border: none; + outline: none; + + appearance: none; + + &::placeholder { + font-weight: 600; + color: var(--color-gray-2); + } + + &:hover, + &:focus { + &::placeholder { + color: var(--color-interactive-input-text-hover-active); + } + } +} + +.tokenSelectContent { + overflow-y: scroll; + + height: 100%; + padding: 0 0.25rem 0 0; + + background-color: var(--color-background-first); + + @include adapt-padding-to-scrollbar(0.25rem); +} + +.tokenGroupContainer { + display: flex; + flex-direction: column; +} + +.tokenGroupHeader { + display: flex; + justify-content: space-between; + + padding: 0.5rem 1rem; + + background: var(--color-background-first-disabled); +} + +.tokenGroupTitle, +.tokenGroupAdditionalTitle { + font-size: 0.8125rem; + font-weight: 600; + color: var(--color-gray-2); +} + +.tokenGroupAdditionalTitle { + cursor: var(--custom-cursor, pointer); +} + +.tokenContainer { + cursor: var(--custom-cursor, pointer); + + position: relative; + + display: flex; + align-items: center; + justify-content: space-between; + + height: 4rem; + padding: 0 1rem; + + @media (hover: hover) { + &:hover { + background-color: var(--color-interactive-item-hover); + } + } + + @media (pointer: coarse) { + &:active { + background-color: var(--color-interactive-item-hover); + } + } +} + +.tokenContainerDisabled { + cursor: auto; +} + +.tokenLogoContainer { + display: flex; + gap: 0.625rem; + align-items: center; +} + +.logoContainer { + position: relative; + + width: 2.25rem; + height: 2.25rem; +} + +.tokenLogo, +.tokenLogoSkeleton, +.tokenLogoSymbol { + width: 2.25rem; + height: 2.25rem; + + font-size: 0; + + border-radius: 100%; +} + +.tokenLogoDisabled { + opacity: 0.5; +} + +.tokenLogoSymbol { + position: absolute; + top: 0; + + display: flex; + align-items: center; + justify-content: center; + + font-size: 1.0625rem; + font-weight: 800; + color: var(--color-gray-3); + + background-color: var(--color-background-first); + box-shadow: inset 0 0 0 0.0625rem var(--color-gray-4); +} + +.tokenLogoSkeleton { + background-color: var(--color-separator-input-stroke); +} + +.tokenNetworkLogo, +.tokenNetworkLogoSkeleton { + position: absolute; + z-index: 1; + top: 1.5rem; + right: -0.25rem; + + width: 1.125rem; + height: 1.125rem; + + border-radius: 100%; + + /* stylelint-disable-next-line plugin/whole-pixel */ + box-shadow: 0 0 0 0.0938rem var(--color-background-drop-down), + inset 0 0 0 0.125rem var(--color-background-drop-down); +} + +.tokenNetworkLogoSkeleton { + background-color: var(--color-separator-input-stroke); + + /* stylelint-disable-next-line plugin/whole-pixel */ + box-shadow: 0 0 0 0.0938rem var(--color-background-drop-down); +} + +.tokenNetworkLogoDisabled { + position: absolute; + z-index: 1; + top: 1.5rem; + right: -0.25rem; + + width: 1.125rem; + height: 1.125rem; + + opacity: 0.5; + background-color: var(--color-background-first); + border-radius: 100%; +} + +.nameContainer, +.tokenPriceContainer { + display: flex; + flex-direction: column; + gap: 0.3125rem; +} + +.tokenPriceContainer { + align-items: flex-end; +} + +.tokenName, +.tokenAmount { + font-size: 0.9375rem; + font-weight: 600; +} + +.tokenNetwork, +.tokenValue { + font-size: 0.75rem; + font-weight: 500; + color: var(--color-gray-2); +} + +.tokenNameSkeleton { + width: 5rem; + height: 0.875rem; + + background-color: var(--color-separator-input-stroke); + border-radius: var(--border-radius-tiny); +} + +.tokenValueSkeleton { + width: 3rem; + height: 0.6875rem; + + background-color: var(--color-separator-input-stroke); + border-radius: var(--border-radius-tiny); +} + +.tokenNotFound { + display: flex; + flex-direction: column; + gap: 1.25rem; + align-items: center; + justify-content: center; + + padding-top: 2rem; +} + +.tokenNotFoundTitle { + font-size: 1.0625rem; + font-weight: 700; +} + +.tokenNotFoundDesc { + font-size: 0.9375rem; + font-weight: 400; + color: var(--color-gray-2); +} + +.tokenTextDisabled { + color: var(--color-gray-2); +} diff --git a/src/components/common/TokenSelector.tsx b/src/components/common/TokenSelector.tsx new file mode 100644 index 00000000..87f1a8fa --- /dev/null +++ b/src/components/common/TokenSelector.tsx @@ -0,0 +1,519 @@ +import React, { + memo, useEffect, useLayoutEffect, useMemo, useRef, useState, +} from '../../lib/teact/teact'; +import { getActions, withGlobal } from '../../global'; + +import type { ApiBaseCurrency } from '../../api/types'; +import { + type AssetPairs, + SettingsState, + type UserSwapToken, + type UserToken, +} from '../../global/types'; + +import { ANIMATED_STICKER_MIDDLE_SIZE_PX, TON_BLOCKCHAIN } from '../../config'; +import { Big } from '../../lib/big.js/index.js'; +import { + selectCurrentAccountTokens, + selectPopularTokensWithoutAccountTokens, + selectSwapTokens, +} from '../../global/selectors'; +import buildClassName from '../../util/buildClassName'; +import { + formatCurrency, getShortCurrencySymbol, +} from '../../util/formatNumber'; +import { getIsAddressValid } from '../../util/getIsAddressValid'; +import getBlockchainNetworkIcon from '../../util/swap/getBlockchainNetworkIcon'; +import getBlockchainNetworkName from '../../util/swap/getBlockchainNetworkName'; +import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; +import { ASSET_LOGO_PATHS } from '../ui/helpers/assetLogos'; + +import { useDeviceScreen } from '../../hooks/useDeviceScreen'; +import useFocusAfterAnimation from '../../hooks/useFocusAfterAnimation'; +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; +import usePrevious from '../../hooks/usePrevious'; +import useScrolledState from '../../hooks/useScrolledState'; +import useSyncEffect from '../../hooks/useSyncEffect'; + +import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; +import ModalHeader from '../ui/ModalHeader'; +import Transition from '../ui/Transition'; + +import styles from './TokenSelector.module.scss'; + +type Token = UserToken | UserSwapToken; + +interface StateProps { + token?: Token; + userTokens?: Token[]; + popularTokens?: Token[]; + swapTokens?: UserSwapToken[]; + tokenInSlug?: string; + pairsBySlug?: Record; + baseCurrency?: ApiBaseCurrency; + isLoading?: boolean; +} + +interface OwnProps { + isActive?: boolean; + shouldFilter?: boolean; + onlyPopular?: boolean; + onClose: NoneToVoidFunction; + onBack: NoneToVoidFunction; +} + +enum SearchState { + Initial, + Search, + Loading, + Token, + Empty, +} + +const EMPTY_ARRAY: Token[] = []; + +function TokenSelector({ + token, + userTokens, + swapTokens, + popularTokens, + shouldFilter, + onlyPopular, + baseCurrency, + tokenInSlug, + pairsBySlug, + isActive, + isLoading, + onBack, + onClose, +}: OwnProps & StateProps) { + const { + importToken, + resetImportToken, + openSettingsWithState, + setSwapTokenIn, + setSwapTokenOut, + addToken, + } = getActions(); + const lang = useLang(); + + const shortBaseSymbol = getShortCurrencySymbol(baseCurrency); + + // eslint-disable-next-line no-null/no-null + const scrollContainerRef = useRef(null); + + // eslint-disable-next-line no-null/no-null + const searchInputRef = useRef(null); + + useHistoryBack({ + isActive, + onBack, + }); + + useFocusAfterAnimation(searchInputRef, !isActive); + + const { + handleScroll: handleContentScroll, + } = useScrolledState(); + const { isPortrait } = useDeviceScreen(); + + const [searchValue, setSearchValue] = useState(''); + const [isResetButtonVisible, setIsResetButtonVisible] = useState(false); + const [renderingKey, setRenderingKey] = useState(SearchState.Initial); + const [searchTokenList, setSearchTokenList] = useState([]); + + const popularTokensPrev = usePrevious(popularTokens); + + const filterTokens = useLastCallback((tokens: Token[]) => filterAndSortTokens(tokens, tokenInSlug, pairsBySlug)); + + const { userTokensWithFilter, popularTokensWithFilter, swapTokensWithFilter } = useMemo(() => { + const currentUserTokens = userTokens ?? EMPTY_ARRAY; + const currentSwapTokens = swapTokens ?? EMPTY_ARRAY; + const currentPopularTokens = popularTokensPrev ?? popularTokens ?? EMPTY_ARRAY; + + if (!shouldFilter) { + return { + userTokensWithFilter: currentUserTokens, + popularTokensWithFilter: currentPopularTokens, + swapTokensWithFilter: currentSwapTokens, + }; + } + + const filteredPopularTokens = filterTokens(currentPopularTokens); + let filteredUserTokens: Token[]; + let filteredSwapTokens: Token[]; + + if (onlyPopular) { + filteredUserTokens = EMPTY_ARRAY; + filteredSwapTokens = EMPTY_ARRAY; + } else { + filteredUserTokens = filterTokens(currentUserTokens); + filteredSwapTokens = filterTokens(currentSwapTokens); + } + + return { + userTokensWithFilter: filteredUserTokens, + popularTokensWithFilter: filteredPopularTokens, + swapTokensWithFilter: filteredSwapTokens, + }; + }, [filterTokens, onlyPopular, popularTokens, popularTokensPrev, shouldFilter, swapTokens, userTokens]); + + const filteredTokenList = useMemo(() => { + const tokensToFilter = onlyPopular ? popularTokensWithFilter : swapTokensWithFilter; + const lowerCaseSearchValue = searchValue.toLowerCase().trim(); + + return tokensToFilter.filter(({ + name, symbol, keywords, isDisabled, + }) => { + if (isDisabled) { + return false; + } + + const isName = name.toLowerCase().includes(lowerCaseSearchValue); + const isSymbol = symbol.toLowerCase().includes(lowerCaseSearchValue); + const isKeyword = keywords?.some((key) => key.toLowerCase().includes(lowerCaseSearchValue)); + + return isName || isSymbol || isKeyword; + }).sort((a, b) => b.amount - a.amount) ?? []; + }, [onlyPopular, popularTokensWithFilter, searchValue, swapTokensWithFilter]); + + const resetSearch = () => { + setSearchValue(''); + }; + + useSyncEffect(() => { + setIsResetButtonVisible(Boolean(searchValue.length)); + + const isValidAddress = getIsAddressValid(searchValue); + let newRenderingKey = SearchState.Initial; + + if (isLoading && isValidAddress) { + newRenderingKey = SearchState.Loading; + } else if (token && isValidAddress) { + newRenderingKey = SearchState.Token; + } else if (searchValue.length && filteredTokenList.length !== 0) { + newRenderingKey = SearchState.Search; + } else if (filteredTokenList.length === 0) { + newRenderingKey = SearchState.Empty; + } + + setRenderingKey(newRenderingKey); + + if (newRenderingKey !== SearchState.Initial) { + setSearchTokenList(filteredTokenList); + } + }, [searchTokenList.length, isLoading, searchValue, token, filteredTokenList]); + + useEffect(() => { + if (getIsAddressValid(searchValue)) { + importToken({ address: searchValue, isSwap: true }); + setRenderingKey(SearchState.Loading); + } else { + resetImportToken(); + } + }, [searchValue]); + + useLayoutEffect(() => { + if (!isActive || !scrollContainerRef.current) return; + + scrollContainerRef.current.scrollTop = 0; + }, [isActive]); + + const handleTokenClick = useLastCallback((selectedToken: Token) => { + if (isPortrait) { + onBack(); + } else { + onClose(); + } + + if (onlyPopular) { + addToken({ token: selectedToken as UserToken }); + } else { + const setToken = shouldFilter ? setSwapTokenOut : setSwapTokenIn; + setToken({ tokenSlug: selectedToken.slug }); + } + + resetSearch(); + }); + + const handleOpenSettings = useLastCallback(() => { + onClose(); + openSettingsWithState({ state: SettingsState.Assets }); + }); + + function renderSearch() { + return ( +
+ + setSearchValue(e.target.value)} + placeholder={lang('Name or Address...')} + value={searchValue} + /> + + {isResetButtonVisible && ( + + )} + +
+ ); + } + + function renderToken(currentToken: Token) { + const image = ASSET_LOGO_PATHS[ + currentToken?.symbol.toLowerCase() as keyof typeof ASSET_LOGO_PATHS + ] ?? currentToken?.image; + const blockchain = 'blockchain' in currentToken ? currentToken.blockchain : TON_BLOCKCHAIN; + const price = 'price' in currentToken ? currentToken.price : 1; + + const isAvailable = !shouldFilter || currentToken.canSwap; + const descriptionText = isAvailable + ? getBlockchainNetworkName(blockchain) + : lang('Unavailable'); + const currencyHoldings = Big(price).mul(currentToken.amount); + const handleClick = isAvailable ? () => handleTokenClick(currentToken) : undefined; + + return ( +
+
+
+ {currentToken.symbol} + {blockchain} + {!isAvailable && } +
+
+ + {currentToken.name} + + + {descriptionText} + +
+
+
+ + {formatCurrency(currentToken.amount, currentToken.symbol)} + + + {formatCurrency(currencyHoldings.toNumber(), shortBaseSymbol)} + +
+
+ ); + } + + function renderTokenGroup(tokens: Token[], title: string, shouldShowSettings?: boolean) { + return ( +
+
+ {title} + {shouldShowSettings && ( + + {lang('Settings')} + + )} +
+ {tokens.map(renderToken)} +
+ ); + } + + function renderAllTokens(tokens: Token[]) { + return ( +
+ {tokens.map(renderToken)} +
+ ); + } + + function renderTokenSkeleton() { + return ( +
+
+
+
+
+
+
+ + +
+
+
+ + +
+
+ ); + } + + function renderNotFound(shouldPlay: boolean) { + return ( +
+ + {lang('Not Found')} + {lang('Try another keyword or address.')} +
+ ); + } + + function renderSearchResults(tokenToImport?: Token) { + if (tokenToImport) { + return ( +
+ {renderToken(tokenToImport)} +
+ ); + } + + return ( + <> + {renderTokenSkeleton()} + {renderTokenSkeleton()} + {renderTokenSkeleton()} + {renderTokenSkeleton()} + {renderTokenSkeleton()} + + ); + } + + function renderTokenGroups() { + if (onlyPopular) { + return renderTokenGroup(popularTokensWithFilter, lang('POPULAR')); + } + + return ( + <> + {renderTokenGroup(userTokensWithFilter, lang('MY'), true)} + {renderTokenGroup(popularTokensWithFilter, lang('POPULAR'))} + {renderTokenGroup(swapTokensWithFilter, lang('A-Z'))} + + ); + } + + // eslint-disable-next-line consistent-return + function renderContent(isContentActive: boolean, isFrom: boolean, currentKey: number) { + switch (currentKey) { + case SearchState.Initial: + return renderTokenGroups(); + case SearchState.Loading: + return renderSearchResults(); + case SearchState.Search: + return renderAllTokens(searchTokenList); + case SearchState.Token: + return renderSearchResults(token); + case SearchState.Empty: + return renderNotFound(isContentActive); + } + } + + return ( + <> + + {renderSearch()} + +
+ + {renderContent} + +
+ + ); +} + +export default memo(withGlobal((global): StateProps => { + const { isLoading, token } = global.settings.importToken ?? {}; + const { pairs, tokenInSlug } = global.currentSwap ?? {}; + + const userTokens = selectCurrentAccountTokens(global); + const popularTokens = selectPopularTokensWithoutAccountTokens(global); + const swapTokens = selectSwapTokens(global); + const { baseCurrency } = global.settings; + + return { + isLoading, + token, + userTokens, + popularTokens, + swapTokens, + tokenInSlug, + baseCurrency, + pairsBySlug: pairs?.bySlug, + }; +})(TokenSelector)); + +function filterAndSortTokens(tokens: Token[], tokenInSlug?: string, pairsBySlug?: Record) { + if (!tokens.length || !tokenInSlug) return []; + + return tokens.map((token) => { + const canSwap = Boolean(pairsBySlug?.[tokenInSlug]?.[token.slug]); + return { ...token, canSwap }; + }).sort((a, b) => Number(b.canSwap) - Number(a.canSwap)); +} diff --git a/src/components/common/TransferResult.module.scss b/src/components/common/TransferResult.module.scss index ba4fc78a..9fe32dbd 100644 --- a/src/components/common/TransferResult.module.scss +++ b/src/components/common/TransferResult.module.scss @@ -55,6 +55,9 @@ } .buttons { + --color-gray-button-background: var(--color-gray-button-background-light); + --color-gray-button-background-hover: var(--color-gray-button-background-light-hover); + display: flex; justify-content: center; diff --git a/src/components/common/TransferResult.tsx b/src/components/common/TransferResult.tsx index 9c6ca9c0..a1706fe2 100644 --- a/src/components/common/TransferResult.tsx +++ b/src/components/common/TransferResult.tsx @@ -41,8 +41,11 @@ function TransferResult({ onFirstButtonClick, onSecondButtonClick, }: OwnProps) { - const withBalanceChange = balance && operationAmount; - const finalBalance = withBalanceChange ? balance + operationAmount - (fee ?? 0) : 0; + const withBalanceChange = Boolean(balance !== undefined && operationAmount); + let finalBalance = withBalanceChange ? balance! + operationAmount! : 0; + if (finalBalance && fee && tokenSymbol === TON_SYMBOL) { + finalBalance -= fee; + } const [wholePart, fractionPart] = formatCurrencyExtended(amount, '', noSign).split('.'); function renderButtons() { diff --git a/src/components/dapps/Dapp.module.scss b/src/components/dapps/Dapp.module.scss index 69a0b44a..54f9c020 100644 --- a/src/components/dapps/Dapp.module.scss +++ b/src/components/dapps/Dapp.module.scss @@ -100,7 +100,7 @@ display: block; - margin-bottom: 0.25rem; + margin-bottom: 0.5rem; padding: 0 0.5rem; font-size: 0.8125rem; @@ -182,6 +182,10 @@ opacity: 1; } + &_disabled { + cursor: auto; + } + .accounts_single & { grid-column-start: 2; } @@ -293,7 +297,7 @@ overflow: hidden; box-sizing: border-box; - margin-bottom: 1rem; + margin-bottom: 1.25rem; padding: 0.875rem 0.75rem; font-size: 1rem; @@ -508,3 +512,176 @@ background: var(--color-background-first); border-radius: 1rem; } + +@keyframes shimmer { + 0% { + background-position: 200%; + } + 100% { + background-position: -200%; + } +} + +@mixin skeleton($color: var(--color-background-first), $edgeColor: var(--color-separator-input-stroke)) { + background: linear-gradient( + 90deg, + $edgeColor, + $edgeColor, + $color, + $edgeColor, + $edgeColor + ); + background-repeat: no-repeat; + background-size: 400% 100%; + + animation: shimmer 2s infinite linear; +} + +.dappInfoSkeleton { + display: flex; + gap: 0.625rem; + align-items: center; + + height: 4rem; + padding: 0.875rem 1rem; + + background-color: var(--color-background-first); + border-radius: var(--border-radius-default); +} + +.accountWrapperSkeleton { + margin-top: 1.25rem; +} + +.dappInfoIconSkeleton { + @include skeleton(); + + width: 2.25rem; + height: 2.25rem; + + background-color: var(--color-separator-input-stroke); + border-radius: var(--border-radius-tiny); +} + +.transactionDappIconSkeleton { + @include skeleton( + var(--color-background-first), + #00000000 + ); + + background-color: var(--color-card-button); +} + +.dappInfoTextSkeleton { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.nameSkeleton { + @include skeleton(); + + width: 4rem; + height: 0.75rem; + + background-color: var(--color-separator-input-stroke); + border-radius: var(--border-radius-tiny); +} + +.descSkeleton { + @include skeleton(); + + width: 5rem; + height: 0.75rem; + + background-color: var(--color-separator-input-stroke); + border-radius: var(--border-radius-tiny); +} + +.nameDappSkeleton { + @include skeleton( + var(--color-background-first), + #00000000 + ); + + height: 0.875rem; + + background-color: var(--color-card-button); +} +.descDappSkeleton { + @include skeleton( + var(--color-background-first), + #00000000 + ); + + height: 0.6875rem; + + background-color: var(--color-card-button); +} + +.transactionDirectionLeftSkeleton { + display: flex; + flex-direction: column; + gap: 0.25rem; + + margin-left: 1rem; +} + +.transactionDirectionRightSkeleton { + display: flex; + gap: 0.5rem; + align-items: center; + justify-self: center; +} + +.dappInfoDataSkeleton { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.rowContainerSkeleton { + display: flex; + flex-direction: column; + gap: 0.5rem; + + margin-bottom: 1.25rem; +} + +.rowSkeleton { + @include skeleton( + var(--color-background-second) + ); + + width: 100%; + height: 3rem; + + background-color: var(--color-separator-input-stroke); + border-radius: var(--border-radius-default); +} + +.rowLargeSkeleton { + height: 4.25rem; +} + +.rowTextSkeleton { + @include skeleton( + var(--color-background-second) + ); + + width: 3.0625rem; + height: 0.8125rem; + margin-left: 0.5rem; + + background-color: var(--color-separator-input-stroke); + border-radius: var(--border-radius-default); +} + +.rowTextLargeSkeleton { + width: 6.8125rem; +} + +.skeletonTransitionWrapper { + display: flex; + flex-direction: column; +} diff --git a/src/components/dapps/DappConnectModal.tsx b/src/components/dapps/DappConnectModal.tsx index efc75abe..2b8927cf 100644 --- a/src/components/dapps/DappConnectModal.tsx +++ b/src/components/dapps/DappConnectModal.tsx @@ -15,6 +15,7 @@ import { bigStrToHuman } from '../../global/helpers'; import { selectCurrentAccountTokens, selectNetworkAccounts } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import { formatCurrency } from '../../util/formatNumber'; +import resolveModalTransitionName from '../../util/resolveModalTransitionName'; import { shortenAddress } from '../../util/shortenAddress'; import useFlag from '../../hooks/useFlag'; @@ -27,9 +28,9 @@ import LedgerConnect from '../ledger/LedgerConnect'; import Button from '../ui/Button'; import Modal from '../ui/Modal'; import ModalHeader from '../ui/ModalHeader'; -import PasswordForm from '../ui/PasswordForm'; import Transition from '../ui/Transition'; import DappInfo from './DappInfo'; +import DappPassword from './DappPassword'; import modalStyles from '../ui/Modal.module.scss'; import styles from './Dapp.module.scss'; @@ -72,7 +73,6 @@ function DappConnectModal({ const { submitDappConnectRequestConfirm, submitDappConnectRequestConfirmHardware, - clearDappConnectRequestError, cancelDappConnectRequestConfirm, setDappConnectRequestState, } = getActions(); @@ -84,6 +84,8 @@ function DappConnectModal({ const { renderingKey, nextKey } = useModalTransitionKeys(state ?? 0, isModalOpen); + const isLoading = dapp === undefined; + useEffect(() => { if (hasConnectRequest) { openModal(); @@ -145,10 +147,11 @@ function DappConnectModal({ function renderAccount(accountId: string, address: string, title?: string) { const balance = accountsData?.[accountId].balances?.bySlug[tonToken.slug] || '0'; const isActive = accountId === selectedAccount; - const onClick = isActive ? undefined : () => setSelectedAccount(accountId); + const onClick = isActive || isLoading ? undefined : () => setSelectedAccount(accountId); const fullClassName = buildClassName( styles.account, isActive && styles.account_current, + isLoading && styles.account_disabled, ); return ( @@ -188,24 +191,6 @@ function DappConnectModal({ ); } - function renderPasswordForm(isActive: boolean) { - return ( - <> - - - - ); - } - function renderDappInfo() { return ( <> @@ -228,16 +213,53 @@ function DappConnectModal({ ); } + function renderWaitForConnection() { + return ( + <> + +
+
+
+
+
+
+
+
+
+ {shouldRenderAccounts && renderAccounts()} +
+
+ + ); + } + + function renderDappInfoWithSkeleton() { + return ( + + {isLoading ? renderWaitForConnection() : renderDappInfo()} + + ); + } + // eslint-disable-next-line consistent-return function renderContent(isActive: boolean, isFrom: boolean, currentKey: number) { switch (currentKey) { case DappConnectState.Info: - return renderDappInfo(); + return renderDappInfoWithSkeleton(); case DappConnectState.Password: - return renderPasswordForm(isActive); + return ( + + ); case DappConnectState.ConnectHardware: return ( { const accounts = selectNetworkAccounts(global); - const hasConnectRequest = Boolean(global.dappConnectRequest?.dapp); + const hasConnectRequest = global.dappConnectRequest?.state !== undefined; const { state, dapp, error, accountId, permissions, proof, diff --git a/src/components/dapps/DappLedgerWarning.tsx b/src/components/dapps/DappLedgerWarning.tsx index c3fe529a..99fc731a 100644 --- a/src/components/dapps/DappLedgerWarning.tsx +++ b/src/components/dapps/DappLedgerWarning.tsx @@ -69,10 +69,11 @@ function DappLedgerWarning({
- + + @@ -201,14 +231,23 @@ function LedgerConnect({ } function renderContent() { - if (state === HardwareConnectState.WaitingForBrowser) { + if (isWaitingForBrowser) { return renderWaitingForBrowser(); } return renderConnect(); } - return renderContent(); + return ( + + {renderContent} + + ); } export default memo(LedgerConnect); diff --git a/src/components/ledger/LedgerModal.module.scss b/src/components/ledger/LedgerModal.module.scss index dfeb30ba..31d68e7d 100644 --- a/src/components/ledger/LedgerModal.module.scss +++ b/src/components/ledger/LedgerModal.module.scss @@ -155,10 +155,12 @@ padding: 0.5rem; - @include adapt-padding-to-scrollbar(0.5rem); - background-color: var(--color-background-first); border-radius: var(--border-radius-default); + + &_two { + grid-template-columns: repeat(6, 1fr); + } } .account { @@ -196,6 +198,22 @@ opacity: 1; } + .accounts_single & { + grid-column-start: 2; + } + + .accounts_two & { + grid-column: span 2; + + &:first-child { + grid-column-end: 4; + } + + &:nth-child(2) { + grid-column-end: 6; + } + } + @media (min-resolution: 1.5dppx) { background-image: url('../../assets/account_button_bg@2x.jpg'), linear-gradient(125deg, #71A9ED 0, #436CB6 100%); } diff --git a/src/components/ledger/LedgerModal.tsx b/src/components/ledger/LedgerModal.tsx index 0877478c..573f9812 100644 --- a/src/components/ledger/LedgerModal.tsx +++ b/src/components/ledger/LedgerModal.tsx @@ -8,6 +8,7 @@ import type { LedgerWalletInfo } from '../../util/ledger/types'; import { selectNetworkAccounts } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; +import resolveModalTransitionName from '../../util/resolveModalTransitionName'; import useLastCallback from '../../hooks/useLastCallback'; @@ -50,6 +51,7 @@ function LedgerModal({ isRemoteTab, }: OwnProps & StateProps) { const { + afterSelectHardwareWallets, resetHardwareWalletConnect, } = getActions(); @@ -60,7 +62,16 @@ function LedgerModal({ LedgerModalState.SelectWallets, ); - const handleConnected = useLastCallback(() => { + const handleAddLedgerWallet = useLastCallback(() => { + afterSelectHardwareWallets({ hardwareSelectedIndices: [hardwareWallets![0].index] }); + onClose(); + }); + + const handleConnected = useLastCallback((isSingleWallet: boolean) => { + if (isSingleWallet) { + handleAddLedgerWallet(); + return; + } setCurrentSlide(LedgerModalState.SelectWallets); }); @@ -75,6 +86,7 @@ function LedgerModal({ case LedgerModalState.Connect: return ( ; - onCancel?: NoneToVoidFunction ; - onClose: NoneToVoidFunction ; + onCancel?: NoneToVoidFunction; + onClose: NoneToVoidFunction; }; -const ACCOUNT_ADDRESS_SHIFT = 6; -const ACCOUNT_ADDRESS_SHIFT_END = 6; +const ACCOUNT_ADDRESS_SHIFT = 4; const ACCOUNT_BALANCE_DECIMALS = 3; function LedgerSelectWallets({ + isActive, hardwareWallets, accounts, onCancel, @@ -44,6 +46,11 @@ function LedgerSelectWallets({ const [selectedAccountIndices, setSelectedAccountIndices] = useState([]); const shouldCloseOnCancel = !onCancel; + useHistoryBack({ + isActive, + onBack: onCancel ?? onClose, + }); + const handleAccountToggle = useLastCallback((index: number) => { if (selectedAccountIndices.includes(index)) { setSelectedAccountIndices(selectedAccountIndices.filter((id) => id !== index)); @@ -63,12 +70,12 @@ function LedgerSelectWallets({ ); function renderAccount(address: string, balance: string, index: number, isConnected: boolean) { - const isActive = isConnected || selectedAccountIndices.includes(index); + const isActiveAccount = isConnected || selectedAccountIndices.includes(index); return (
handleAccountToggle(index)} > @@ -78,20 +85,25 @@ function LedgerSelectWallets({
- {shortenAddress(address, ACCOUNT_ADDRESS_SHIFT, ACCOUNT_ADDRESS_SHIFT_END)} + {shortenAddress(address, ACCOUNT_ADDRESS_SHIFT, ACCOUNT_ADDRESS_SHIFT)}
-
+
); } function renderAccounts() { const list = hardwareWallets ?? []; + const fullClassName = buildClassName( + styles.accounts, + list.length === 1 && styles.accounts_single, + list.length === 2 && styles.accounts_two, + ); return ( -
+
{list.map( ({ address, balance, index }) => renderAccount( address, diff --git a/src/components/main/Main.module.scss b/src/components/main/Main.module.scss index f1428c7f..15619ca3 100644 --- a/src/components/main/Main.module.scss +++ b/src/components/main/Main.module.scss @@ -28,6 +28,10 @@ $scrollOffset: 0.1875rem; } } + @supports (padding-top: env(safe-area-inset-top)) { + min-height: calc(var(--vh, 1vh) * 100 - env(safe-area-inset-top)); + } + .head { width: 100%; max-width: 27rem; diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index f7532611..cc0cc4bc 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -1,3 +1,4 @@ +import { BottomSheet } from 'native-bottom-sheet'; import React, { memo, useEffect, useRef, useState, } from '../../lib/teact/teact'; @@ -5,10 +6,18 @@ import { getActions, withGlobal } from '../../global'; import { ContentTab } from '../../global/types'; +import { IS_CAPACITOR } from '../../config'; import { selectCurrentAccount, selectCurrentAccountState } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; -import { REM } from '../../util/windowEnvironment'; +import { getStatusBarHeight } from '../../util/capacitor'; +import { captureEvents, SwipeDirection } from '../../util/captureEvents'; +import { setStatusBarStyle } from '../../util/switchTheme'; +import { + getSafeAreaTop, IS_DELEGATED_BOTTOM_SHEET, IS_TOUCH_ENV, REM, +} from '../../util/windowEnvironment'; +import { useOpenFromMainBottomSheet } from '../../hooks/useDelegatedBottomSheet'; +import { useOpenFromNativeBottomSheet } from '../../hooks/useDelegatingBottomSheet'; import { useDeviceScreen } from '../../hooks/useDeviceScreen'; import useFlag from '../../hooks/useFlag'; import useLastCallback from '../../hooks/useLastCallback'; @@ -26,33 +35,65 @@ import Warnings from './sections/Warnings'; import styles from './Main.module.scss'; +interface OwnProps { + isActive?: boolean; + onQrScanPress?: NoneToVoidFunction; +} + type StateProps = { currentTokenSlug?: string; - currentAccountId?: string; isStakingActive: boolean; isUnstakeRequested?: boolean; isTestnet?: boolean; isLedger?: boolean; + isStakingInfoModalOpen?: boolean; }; const STICKY_CARD_INTERSECTION_THRESHOLD = -3.75 * REM; +const STICKY_CARD_WITH_SAFE_AREA_INTERSECTION_THRESHOLD = -5.5 * REM; function Main({ - currentTokenSlug, currentAccountId, isStakingActive, isUnstakeRequested, isTestnet, isLedger, -}: StateProps) { + isActive, + currentTokenSlug, + isStakingActive, + isUnstakeRequested, + isTestnet, + isLedger, + onQrScanPress, + isStakingInfoModalOpen, +}: OwnProps & StateProps) { const { selectToken, startStaking, - fetchBackendStakingState, openBackupWalletModal, - setActiveContentTabIndex, + setActiveContentTab, + setSwapTokenOut, + changeTransferToken, + openStakingInfo, + closeStakingInfo, } = getActions(); // eslint-disable-next-line no-null/no-null const cardRef = useRef(null); + // eslint-disable-next-line no-null/no-null + const portraitContainerRef = useRef(null); const [canRenderStickyCard, setCanRenderStickyCard] = useState(false); - const [isStakingInfoOpened, openStakingInfo, closeStakingInfo] = useFlag(false); - const [isReceiveModalOpened, openReceiveModal, closeReceiveModal] = useFlag(false); + const [shouldRenderDarkStatusBar, setShouldRenderDarkStatusBar] = useState(false); + const [isReceiveModalOpened, openReceiveModal, closeReceiveModal] = useFlag(); + + useOpenFromMainBottomSheet('staking-info', openStakingInfo); + useOpenFromNativeBottomSheet('staking-info', openStakingInfo); + + useOpenFromMainBottomSheet('receive', openReceiveModal); + + const handleOpenStakingInfo = useLastCallback(() => { + if (IS_DELEGATED_BOTTOM_SHEET) { + BottomSheet.openInMain({ key: 'staking-info' }); + } else { + openStakingInfo(); + } + }); + const { isPortrait } = useDeviceScreen(); const { shouldRender: shouldRenderStickyCard, @@ -60,45 +101,74 @@ function Main({ } = useShowTransition(canRenderStickyCard); useEffect(() => { - if (currentAccountId && (isStakingActive || isUnstakeRequested)) { - fetchBackendStakingState(); - } - }, [fetchBackendStakingState, currentAccountId, isStakingActive, isUnstakeRequested]); + setStatusBarStyle(shouldRenderDarkStatusBar); + }, [shouldRenderDarkStatusBar]); useEffect(() => { if (currentTokenSlug) { - setActiveContentTabIndex({ index: ContentTab.Activity }); + setActiveContentTab({ tab: ContentTab.Activity }); + setSwapTokenOut({ tokenSlug: currentTokenSlug }); + changeTransferToken({ tokenSlug: currentTokenSlug }); } }, [currentTokenSlug]); useEffect(() => { - if (!isPortrait) { + if (!isPortrait || !isActive) { setCanRenderStickyCard(false); return undefined; } + const safeAreaTop = IS_CAPACITOR ? getStatusBarHeight() : getSafeAreaTop(); + const rootMarginTop = safeAreaTop > 0 + ? STICKY_CARD_WITH_SAFE_AREA_INTERSECTION_THRESHOLD + : STICKY_CARD_INTERSECTION_THRESHOLD; const observer = new IntersectionObserver((entries) => { const { isIntersecting, boundingClientRect: { left, width } } = entries[0]; setCanRenderStickyCard(entries.length > 0 && !isIntersecting && left >= 0 && left < width); - }, { rootMargin: `${STICKY_CARD_INTERSECTION_THRESHOLD}px 0px 0px` }); + }, { rootMargin: `${rootMarginTop}px 0px 0px` }); + const cardTopSideObserver = new IntersectionObserver((entries) => { + const { isIntersecting } = entries[0]; + + setShouldRenderDarkStatusBar(!isIntersecting); + }, { rootMargin: `${rootMarginTop / 2}px 0px 0px`, threshold: [1] }); const cardElement = cardRef.current; if (cardElement) { observer.observe(cardElement); + cardTopSideObserver.observe(cardElement); } return () => { if (cardElement) { observer.unobserve(cardElement); + cardTopSideObserver.unobserve(cardElement); } }; - }, [isPortrait]); + }, [isActive, isPortrait]); const handleTokenCardClose = useLastCallback(() => { selectToken({ slug: undefined }); - setActiveContentTabIndex({ index: ContentTab.Assets }); + setActiveContentTab({ tab: ContentTab.Assets }); }); + useEffect(() => { + if (!IS_TOUCH_ENV || !isPortrait || !portraitContainerRef.current || !currentTokenSlug) { + return undefined; + } + + return captureEvents(portraitContainerRef.current!, { + excludedClosestSelector: '.token-card', + onSwipe: (e, direction) => { + if (direction === SwipeDirection.Right) { + handleTokenCardClose(); + return true; + } + + return false; + }, + }); + }, [currentTokenSlug, handleTokenCardClose, isPortrait]); + const handleEarnClick = useLastCallback(() => { if (isStakingActive || isUnstakeRequested) { openStakingInfo(); @@ -109,11 +179,22 @@ function Main({ function renderPortraitLayout() { return ( -
+
- - {shouldRenderStickyCard && } + + {shouldRenderStickyCard && ( + + )}
- + - {isPortrait ? renderPortraitLayout() : renderLandscapeLayout()} + {!IS_DELEGATED_BOTTOM_SHEET && (isPortrait ? renderPortraitLayout() : renderLandscapeLayout())} - - + + @@ -161,18 +242,20 @@ function Main({ } export default memo( - withGlobal((global, ownProps, detachWhenChanged): StateProps => { - detachWhenChanged(global.currentAccountId); - const accountState = selectCurrentAccountState(global); - const account = selectCurrentAccount(global); - - return { - isStakingActive: Boolean(accountState?.stakingBalance) && !accountState?.isUnstakeRequested, - isUnstakeRequested: accountState?.isUnstakeRequested, - currentTokenSlug: accountState?.currentTokenSlug, - currentAccountId: global.currentAccountId, - isTestnet: global.settings.isTestnet, - isLedger: !!account?.ledger, - }; - })(Main), + withGlobal( + (global): StateProps => { + const accountState = selectCurrentAccountState(global); + const account = selectCurrentAccount(global); + + return { + isStakingActive: Boolean(accountState?.staking?.balance), + isUnstakeRequested: accountState?.staking?.isUnstakeRequested, + currentTokenSlug: accountState?.currentTokenSlug, + isTestnet: global.settings.isTestnet, + isLedger: !!account?.ledger, + isStakingInfoModalOpen: global.isStakingInfoModalOpen, + }; + }, + (global, _, stickToFirst) => stickToFirst(global.currentAccountId), + )(Main), ); diff --git a/src/components/main/modals/AddAccountModal.module.scss b/src/components/main/modals/AddAccountModal.module.scss index 777ca2f3..c673d06f 100644 --- a/src/components/main/modals/AddAccountModal.module.scss +++ b/src/components/main/modals/AddAccountModal.module.scss @@ -24,12 +24,17 @@ .button { width: 100%; min-width: 0 !important; - max-width: 100% !important; + max-width: 68vw !important; margin: 0 auto; &_single { width: 13.4375rem; } + + @media (max-width: 350px) { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } } .modalButtons { @@ -50,8 +55,11 @@ } .importButtons { - display: flex; + display: grid; + grid-auto-columns: minmax(max-content, 1fr); + grid-auto-flow: column; gap: 1rem; + justify-items: center; width: 100%; margin: 0.875rem 0 1rem; @@ -67,3 +75,12 @@ .sticker { margin: 0 auto 1.375rem; } + +.ledgerButton { + width: auto; + min-width: 8rem !important; + + @supports (width: stretch) { + width: stretch; + } +} diff --git a/src/components/main/modals/AddAccountModal.tsx b/src/components/main/modals/AddAccountModal.tsx index c789c1c2..6a2cd589 100644 --- a/src/components/main/modals/AddAccountModal.tsx +++ b/src/components/main/modals/AddAccountModal.tsx @@ -1,14 +1,17 @@ import React, { memo, useState } from '../../../lib/teact/teact'; -import { getActions, withGlobal } from '../../../global'; +import { getActions, getGlobal, withGlobal } from '../../../global'; import type { Account, HardwareConnectState } from '../../../global/types'; import type { LedgerWalletInfo } from '../../../util/ledger/types'; +import { ApiCommonError } from '../../../api/types'; import { ANIMATED_STICKER_BIG_SIZE_PX, MNEMONIC_COUNT } from '../../../config'; import renderText from '../../../global/helpers/renderText'; import { selectFirstNonHardwareAccount, selectNetworkAccounts } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; +import resolveModalTransitionName from '../../../util/resolveModalTransitionName'; import { IS_LEDGER_SUPPORTED } from '../../../util/windowEnvironment'; +import { callApi } from '../../../api'; import { ANIMATED_STICKERS_PATHS } from '../../ui/helpers/animatedAssets'; import useLang from '../../../hooks/useLang'; @@ -20,8 +23,8 @@ import AnimatedIconWithPreview from '../../ui/AnimatedIconWithPreview'; import Button from '../../ui/Button'; import Modal from '../../ui/Modal'; import ModalHeader from '../../ui/ModalHeader'; -import PasswordForm from '../../ui/PasswordForm'; import Transition from '../../ui/Transition'; +import AddAccountPasswordModal from './AddAccountPasswordModal'; import modalStyles from '../../ui/Modal.module.scss'; import styles from './AddAccountModal.module.scss'; @@ -58,7 +61,13 @@ function AddAccountModal({ isLedgerConnected, isTonAppConnected, }: StateProps) { - const { addAccount, clearAccountError, closeAddAccountModal } = getActions(); + const { + addAccount, + clearAccountError, + closeAddAccountModal, + afterSelectHardwareWallets, + showError, + } = getActions(); const lang = useLang(); const [renderingKey, setRenderingKey] = useState(RenderingState.Initial); @@ -75,7 +84,7 @@ function AddAccountModal({ setIsNewAccountImporting(false); }); - const handleNewAccountClick = useLastCallback(() => { + const handleNewAccountClick = useLastCallback(async () => { if (!firstNonHardwareAccount) { addAccount({ method: 'createAccount', @@ -84,8 +93,16 @@ function AddAccountModal({ return; } - setRenderingKey(RenderingState.Password); - setIsNewAccountImporting(false); + const isApiAvailable = await callApi('checkApiAvailability', { + accountId: getGlobal().currentAccountId!, + }); + + if (isApiAvailable) { + setRenderingKey(RenderingState.Password); + setIsNewAccountImporting(false); + } else { + showError({ error: ApiCommonError.ServerError }); + } }); const handleImportAccountClick = useLastCallback(() => { @@ -105,7 +122,16 @@ function AddAccountModal({ setRenderingKey(RenderingState.ConnectHardware); }); - const handleHardwareWalletConnected = useLastCallback(() => { + const handleAddLedgerWallet = useLastCallback(() => { + afterSelectHardwareWallets({ hardwareSelectedIndices: [hardwareWallets![0].index] }); + closeAddAccountModal(); + }); + + const handleHardwareWalletConnected = useLastCallback((isSingleWallet: boolean) => { + if (isSingleWallet) { + handleAddLedgerWallet(); + return; + } setRenderingKey(RenderingState.SelectAccountsHardware); }); @@ -151,7 +177,7 @@ function AddAccountModal({ {IS_LEDGER_SUPPORTED && ( - + +
@@ -162,12 +163,12 @@ function SignatureModal({ ; +}; + +enum EmailType { + Support, + Security, +} + +const CHANGELLY_PENDING_STATUSES = new Set(['new', 'waiting', 'confirming', 'exchanging', 'sending']); +const CHANGELLY_ERROR_STATUSES = new Set(['failed', 'expired', 'refunded', 'overdue']); +const ONCHAIN_ERROR_STATUSES = new Set(['failed', 'expired']); + +function SwapActivityModal({ activity, tokensBySlug }: StateProps) { + const { + startSwap, + closeActivityInfo, + } = getActions(); + + const lang = useLang(); + const { isPortrait } = useDeviceScreen(); + const animationLevel = getGlobal().settings.animationLevel; + const animationDuration = animationLevel === ANIMATION_LEVEL_MIN + ? 0 + : (isPortrait ? CLOSE_DURATION_PORTRAIT : CLOSE_DURATION) + ANIMATION_END_DELAY; + const renderedActivity = usePrevDuringAnimation(activity, animationDuration); + + const { txId, timestamp, networkFee = 0 } = renderedActivity ?? {}; + + let fromToken: ApiSwapAsset | undefined; + let toToken: ApiSwapAsset | undefined; + let fromAmount = 0; + let toAmount = 0; + let isPending = true; + let isError = false; + let isCexError = false; + let isCexHold = false; + let isCexWaiting = false; + let title = ''; + let errorMessage = ''; + let emailType: EmailType | undefined; + + if (renderedActivity) { + const { + status, from, to, cex, + } = renderedActivity; + fromToken = tokensBySlug?.[from]; + toToken = tokensBySlug?.[to]; + fromAmount = Number(renderedActivity.fromAmount); + toAmount = Number(renderedActivity.toAmount); + + const isFromTon = from === TON_TOKEN_SLUG; + + if (cex) { + isPending = CHANGELLY_PENDING_STATUSES.has(cex.status); + isCexError = CHANGELLY_ERROR_STATUSES.has(cex.status); + isCexHold = cex.status === 'hold'; + // Skip the 'waiting' status for transactions from TON to account for delayed status updates from changelly. + isCexWaiting = cex.status === 'waiting' && !isFromTon; + } else { + isPending = status === 'pending'; + isError = ONCHAIN_ERROR_STATUSES.has(status!); + } + + if (isPending) { + title = lang('Swapping'); + } else if (isCexHold) { + title = lang('Swap Hold'); + errorMessage = lang('Please contact security team to pass the KYC procedure.'); + emailType = EmailType.Security; + } else if (isCexError) { + const { status: cexStatus } = renderedActivity?.cex ?? {}; + if (cexStatus === 'expired' || cexStatus === 'overdue') { + title = lang('Swap Expired'); + errorMessage = lang('You have not sent the coins to the specified address.'); + } else if (cexStatus === 'refunded') { + title = lang('Swap Refunded'); + errorMessage = lang('Exchange failed and coins were refunded to your wallet.'); + } else { + title = lang('Swap Failed'); + errorMessage = lang('Please contact support and provide a transaction ID.'); + emailType = EmailType.Support; + } + } else if (isError) { + title = lang('Swap Failed'); + } else { + title = lang('Swapped'); + } + } + + const [, transactionHash] = (txId || '').split(':'); + const tonscanBaseUrl = TONSCAN_BASE_MAINNET_URL; + const tonscanTransactionUrl = transactionHash ? `${tonscanBaseUrl}tx/${transactionHash}` : undefined; + + const handleClose = useLastCallback(() => { + closeActivityInfo({ id: renderedActivity!.id }); + }); + + const handleSwapClick = useLastCallback(() => { + closeActivityInfo({ id: activity!.id }); + startSwap({ + tokenInSlug: activity!.from, + tokenOutSlug: activity!.to, + amountIn: fromAmount, + isPortrait, + }); + }); + + function renderHeader() { + return ( +
+
+ {title} + {isPending && ( + + )} +
+ {!!timestamp && ( +
+ {formatFullDay(lang.code!, timestamp)}, {formatTime(timestamp)} +
+ )} +
+ ); + } + + function renderFooterButton() { + let isButtonVisible = true; + let buttonText = 'Swap Again'; + + if (isCexWaiting) { + return ( + + ); + } + + if (isCexHold) { + isButtonVisible = false; + } else if (isCexError) { + const { status: cexStatus } = renderedActivity?.cex ?? {}; + if (cexStatus === 'expired' || cexStatus === 'refunded') { + buttonText = 'Try Again'; + } + } + + if (!isButtonVisible) { + return undefined; + } + + return ( + + ); + } + + function renderErrorMessage() { + const email = emailType === EmailType.Support + ? CHANGELLY_SUPPORT_EMAIL + : CHANGELLY_SECURITY_EMAIL; + + return ( +
+ {errorMessage} + {emailType !== undefined && {email}} +
+ ); + } + + function renderSwapInfo() { + const payinAddress = renderedActivity?.cex?.payinAddress; + + if (isCexWaiting) { + return ( +
+ {lang('$swap_changelly_to_ton_description1', { + value: ( + + {formatCurrencyExtended(Number(fromAmount), fromToken?.symbol ?? '', true)} + + ), + blockchain: ( + + {getBlockchainNetworkName(fromToken?.blockchain)} + + ), + time: , + })} + + +
+ ); + } + + if (isCexError || isCexHold) { + return ( +
+ {lang('Changelly Payment Address')} + +
+ ); + } + + return ( + <> + {payinAddress && ( +
+ {lang('Changelly Payment Address')} + +
+ )} +
+ {lang('Exchange rate')} + {renderCurrency(renderedActivity, fromToken, toToken)} +
+
+ + {lang('Blockchain fee')} + +
+ {formatCurrency(networkFee, TON_SYMBOL)} +
+
+ + ); + } + + function renderContent() { + return ( + <> + +
+ {renderSwapInfo()} + {renderErrorMessage()} +
+
+ {renderFooterButton()} +
+ + ); + } + + return ( + +
+ {tonscanTransactionUrl && ( + + + + )} + {renderContent()} +
+ + ); +} + +export default memo( + withGlobal((global): StateProps => { + const accountState = selectCurrentAccountState(global); + + const id = accountState?.currentActivityId; + const activity = id ? accountState?.activities?.byId[id] : undefined; + + return { + activity: activity?.kind === 'swap' ? activity : undefined, + tokensBySlug: global.swapTokenInfo?.bySlug, + }; + })(SwapActivityModal), +); + +function renderCurrency(activity?: ApiSwapActivity, fromToken?: ApiSwapAsset, toToken?: ApiSwapAsset) { + const rate = getSwapRate(activity?.fromAmount, activity?.toAmount, fromToken, toToken); + + if (!rate) return undefined; + + return ( +
+ 1 {rate.firstCurrencySymbol}{' ≈ '} + {rate.price}{' '}{rate.secondCurrencySymbol} +
+ ); +} diff --git a/src/components/main/modals/TransactionModal.module.scss b/src/components/main/modals/TransactionModal.module.scss index 61c85827..fc9dbbdf 100644 --- a/src/components/main/modals/TransactionModal.module.scss +++ b/src/components/main/modals/TransactionModal.module.scss @@ -19,7 +19,7 @@ } .copyButtonWrapper { - margin-bottom: 1rem; + margin-bottom: 1.25rem; } .comment { @@ -98,7 +98,10 @@ .unstakeTime { position: relative; - max-width: 18rem; + display: flex; + flex-direction: column; + + max-width: 19rem; margin: 0.25rem auto 0; padding: 0.75rem; @@ -130,7 +133,6 @@ .transactionHeader { display: flex; flex-direction: column; - gap: 0.3125rem; } .headerTitle { @@ -139,6 +141,8 @@ align-items: center; justify-content: center; + margin-bottom: 0.25rem; + font-size: 1.0625rem; font-weight: 700; color: var(--color-black); @@ -168,7 +172,7 @@ display: flex; align-items: center; - padding-left: 1rem; + padding-left: 0.5rem; font-size: 0.8125rem; font-weight: 700; @@ -184,13 +188,14 @@ font-size: 1rem; font-weight: 600; color: var(--color-black); + word-break: break-all; background-color: var(--color-background-first); border-radius: 1rem; } .advancedTooltip { - cursor: pointer; + cursor: var(--custom-cursor, pointer); margin-left: 0.25rem; @@ -204,3 +209,51 @@ width: 18.9375rem; } + +.errorCexBlock { + display: flex; + flex-direction: column; + gap: 1rem; + align-items: center; + justify-content: center; + + margin-top: 0.75rem; +} + +.errorCexMessage { + font-size: 0.8125rem; + font-weight: 500; + line-height: 0.8125rem; + color: var(--color-gray-2); + text-align: center; +} + +.errorCexEmail { + font-size: 1.0625rem; + font-weight: 700; + line-height: 1.0625rem; + color: var(--color-blue); + text-align: center; +} + +.changellyInfoBlock { + display: flex; + flex-direction: column; + gap: 1rem; + align-items: center; +} + +.changellyDescription { + font-size: 0.8125rem; + font-weight: 500; + color: var(--color-gray-2); + text-align: center; +} + +.changellyTextField { + width: 100%; +} + +.changellyDescriptionBold { + font-weight: 700; +} diff --git a/src/components/main/modals/TransactionModal.tsx b/src/components/main/modals/TransactionModal.tsx index 5eeee63c..b50edf92 100644 --- a/src/components/main/modals/TransactionModal.tsx +++ b/src/components/main/modals/TransactionModal.tsx @@ -1,10 +1,12 @@ import React, { memo, useState } from '../../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../../global'; -import type { ApiToken, ApiTransactionActivity } from '../../../api/types'; +import type { ApiStakingType, ApiToken, ApiTransactionActivity } from '../../../api/types'; import { - ANIMATION_END_DELAY, ANIMATION_LEVEL_MIN, + ANIMATION_END_DELAY, + ANIMATION_LEVEL_MIN, + IS_CAPACITOR, STAKING_CYCLE_DURATION_MS, TON_SYMBOL, TON_TOKEN_SLUG, @@ -14,11 +16,12 @@ import { import { bigStrToHuman, getIsTxIdLocal } from '../../../global/helpers'; import { selectCurrentAccountState } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; +import { vibrateOnSuccess } from '../../../util/capacitor'; import { formatFullDay, formatRelativeHumanDateTime, formatTime } from '../../../util/dateFormat'; +import resolveModalTransitionName from '../../../util/resolveModalTransitionName'; import { callApi } from '../../../api'; import { useDeviceScreen } from '../../../hooks/useDeviceScreen'; -import useFlag from '../../../hooks/useFlag'; import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; import usePrevDuringAnimation from '../../../hooks/usePrevDuringAnimation'; @@ -29,8 +32,10 @@ import TransactionAmount from '../../common/TransactionAmount'; import AmountWithFeeTextField from '../../ui/AmountWithFeeTextField'; import Button from '../../ui/Button'; import InteractiveTextField from '../../ui/InteractiveTextField'; -import Modal, { ANIMATION_DURATION, ANIMATION_DURATION_PORTRAIT } from '../../ui/Modal'; +import Modal, { CLOSE_DURATION, CLOSE_DURATION_PORTRAIT } from '../../ui/Modal'; +import ModalHeader from '../../ui/ModalHeader'; import PasswordForm from '../../ui/PasswordForm'; +import Transition from '../../ui/Transition'; import transferStyles from '../../transfer/Transfer.module.scss'; import modalStyles from '../../ui/Modal.module.scss'; @@ -45,7 +50,14 @@ type StateProps = { isTestnet?: boolean; startOfStakingCycle?: number; endOfStakingCycle?: number; + stakingType?: ApiStakingType; + isUnstakeRequested?: boolean; + isInstantUnstakeRequested?: boolean; }; +const enum SLIDES { + initial, + password, +} const EMPTY_HASH_VALUE = 'NOHASH'; @@ -56,19 +68,26 @@ function TransactionModal({ isTestnet, startOfStakingCycle, endOfStakingCycle, + stakingType, + isUnstakeRequested, + isInstantUnstakeRequested, }: StateProps) { const { startTransfer, startStaking, closeActivityInfo, + setIsPinPadPasswordAccepted, + clearIsPinPadPasswordAccepted, } = getActions(); const lang = useLang(); const { isPortrait } = useDeviceScreen(); + const [currentSlide, setCurrentSlide] = useState(SLIDES.initial); + const [nextKey, setNextKey] = useState(SLIDES.password); const animationLevel = getGlobal().settings.animationLevel; const animationDuration = animationLevel === ANIMATION_LEVEL_MIN ? 0 - : (isPortrait ? ANIMATION_DURATION_PORTRAIT : ANIMATION_DURATION) + ANIMATION_END_DELAY; + : (isPortrait ? CLOSE_DURATION_PORTRAIT : CLOSE_DURATION) + ANIMATION_END_DELAY; const renderedTransaction = usePrevDuringAnimation(transaction, animationDuration); const [unstakeDate, setUnstakeDate] = useState(Date.now() + STAKING_CYCLE_DURATION_MS); @@ -79,12 +98,12 @@ function TransactionModal({ comment, encryptedComment, fee, - txId, + id, isIncoming, slug, timestamp, } = renderedTransaction || {}; - const [, transactionHash] = (txId || '').split(':'); + const [, transactionHash] = (id || '').split(':'); const isStaking = Boolean(transaction?.type); const token = slug ? tokensBySlug?.[slug] : undefined; @@ -93,8 +112,8 @@ function TransactionModal({ const addressName = (address && savedAddresses?.[address]) || transaction?.metadata?.name; const isScam = Boolean(transaction?.metadata?.isScam); + const [isLoading, setIsLoading] = useState(false); const [decryptedComment, setDecryptedComment] = useState(); - const [isPasswordModalOpen, openPasswordModal, closePasswordModal] = useFlag(); const [passwordError, setPasswordError] = useState(); const tonscanBaseUrl = isTestnet ? TONSCAN_BASE_TESTNET_URL : TONSCAN_BASE_MAINNET_URL; @@ -103,7 +122,12 @@ function TransactionModal({ : undefined; const withUnstakeTimer = Boolean( - transaction?.type === 'unstakeRequest' && startOfStakingCycle && transaction.timestamp >= startOfStakingCycle, + (stakingType === 'liquid' ? isInstantUnstakeRequested || isUnstakeRequested : true) + && transaction?.type === 'unstakeRequest' + && startOfStakingCycle + && endOfStakingCycle + && transaction.timestamp >= startOfStakingCycle + && transaction.timestamp <= endOfStakingCycle, ); const { @@ -114,8 +138,6 @@ function TransactionModal({ useSyncEffect(() => { if (renderedTransaction) { setDecryptedComment(undefined); - } else { - closeActivityInfo(); } }, [renderedTransaction]); @@ -125,9 +147,20 @@ function TransactionModal({ } }, [endOfStakingCycle]); + const openPasswordSlide = useLastCallback(() => { + setCurrentSlide(SLIDES.password); + setNextKey(undefined); + }); + + const closePasswordSlide = useLastCallback(() => { + setCurrentSlide(SLIDES.initial); + setNextKey(SLIDES.password); + }); + const handleSendClick = useLastCallback(() => { - closeActivityInfo(); + closeActivityInfo({ id: id! }); startTransfer({ + isPortrait, tokenSlug: slug || TON_TOKEN_SLUG, toAddress: address, amount: Math.abs(amountHuman), @@ -136,11 +169,12 @@ function TransactionModal({ }); const handleStartStakingClick = useLastCallback(() => { - closeActivityInfo(); + closeActivityInfo({ id: id! }); startStaking(); }); const handlePasswordSubmit = useLastCallback(async (password: string) => { + setIsLoading(true); const result = await callApi( 'decryptComment', getGlobal().currentAccountId!, @@ -148,22 +182,36 @@ function TransactionModal({ fromAddress!, password, ); + setIsLoading(false); if (!result) { setPasswordError('Wrong password, please try again'); return; } - closePasswordModal(); + if (IS_CAPACITOR) { + setIsPinPadPasswordAccepted(); + await vibrateOnSuccess(true); + } + + closePasswordSlide(); setDecryptedComment(result); }); + const handleClose = useLastCallback(() => { + closeActivityInfo({ id: id! }); + if (IS_CAPACITOR) { + clearIsPinPadPasswordAccepted(); + } + }); + const clearPasswordError = useLastCallback(() => { setPasswordError(undefined); }); function renderHeader() { - const isLocal = txId && getIsTxIdLocal(txId); + const isLocal = id && getIsTxIdLocal(id); + const title = isIncoming ? lang('Received') : isLocal @@ -171,23 +219,38 @@ function TransactionModal({ : lang('Sent'); return ( -
-
- {title} - {isLocal && ( - +
+
+
+ {title} + {isLocal && ( + + )} + {isScam && {lang('Scam')}} +
+ {!!timestamp && ( +
+ {formatFullDay(lang.code!, timestamp)}, {formatTime(timestamp)} +
)} - {isScam && {lang('Scam')}}
- {!!timestamp && ( -
- {formatFullDay(lang.code!, timestamp)}, {formatTime(timestamp)} -
- )} +
); } @@ -224,31 +287,11 @@ function TransactionModal({ text={encryptedComment ? decryptedComment : comment} spoiler={spoiler} spoilerRevealText={encryptedComment ? lang('Decrypt') : lang('Display')} - spoilerCallback={openPasswordModal} + spoilerCallback={openPasswordSlide} copyNotification={lang('Comment was copied!')} className={styles.copyButtonWrapper} textClassName={styles.comment} /> - {encryptedComment && ( - - - - )} ); } @@ -306,27 +349,70 @@ function TransactionModal({ ); } + // eslint-disable-next-line consistent-return + function renderContent(isActive: boolean, isFrom: boolean, currentKey: number) { + switch (currentKey) { + case SLIDES.initial: + return ( + <> + {renderHeader()} +
+ {tonscanTransactionUrl && ( + + + + )} + {renderTransactionContent()} +
+ + ); + case SLIDES.password: + if (!encryptedComment) return undefined; + + return ( + <> + {!IS_CAPACITOR && } + + + ); + } + } + return ( -
- {tonscanTransactionUrl && ( - - - - )} - {renderTransactionContent()} -
+ + {renderContent} +
); } @@ -337,7 +423,10 @@ export default memo( const txId = accountState?.currentActivityId; const activity = txId ? accountState?.activities?.byId[txId] : undefined; - const { startOfCycle: startOfStakingCycle, endOfCycle: endOfStakingCycle } = accountState?.poolState || {}; + const { + start: startOfStakingCycle, + end: endOfStakingCycle, + } = accountState?.staking || {}; const savedAddresses = accountState?.savedAddresses; return { @@ -347,6 +436,9 @@ export default memo( isTestnet: global.settings.isTestnet, startOfStakingCycle, endOfStakingCycle, + stakingType: accountState?.staking?.type, + isUnstakeRequested: accountState?.staking?.isUnstakeRequested, + isInstantUnstakeRequested: accountState?.staking?.isInstantUnstakeRequested, }; })(TransactionModal), ); diff --git a/src/components/main/sections/Actions/LandscapeActions.module.scss b/src/components/main/sections/Actions/LandscapeActions.module.scss index a1752cf8..22c9990a 100644 --- a/src/components/main/sections/Actions/LandscapeActions.module.scss +++ b/src/components/main/sections/Actions/LandscapeActions.module.scss @@ -14,7 +14,7 @@ $tab-1-height: 348; .tabs { display: grid; - grid-template-columns: 1fr 1fr 1fr; + grid-template-columns: 1fr 1fr 1fr 1fr; width: 100%; @@ -50,10 +50,12 @@ $tab-1-height: 348; transition: color 150ms; - &:not(.active) { - &:hover, - &:focus { - color: var(--color-blue); + @media (hover: hover) { + &:not(.active) { + &:hover, + &:focus { + color: var(--color-blue); + } } } diff --git a/src/components/main/sections/Actions/LandscapeActions.tsx b/src/components/main/sections/Actions/LandscapeActions.tsx index 550ba024..92e61936 100644 --- a/src/components/main/sections/Actions/LandscapeActions.tsx +++ b/src/components/main/sections/Actions/LandscapeActions.tsx @@ -1,9 +1,7 @@ import React, { memo, useEffect, useMemo, useRef, } from '../../../../lib/teact/teact'; -import { - getActions, withGlobal, -} from '../../../../global'; +import { getActions, withGlobal } from '../../../../global'; import { ElectronEvent } from '../../../../electron/types'; import { ActiveTab } from '../../../../global/types'; @@ -19,12 +17,13 @@ import useLastCallback from '../../../../hooks/useLastCallback'; import StakingInfoContent from '../../../staking/StakingInfoContent'; import StakingInitial from '../../../staking/StakingInitial'; +import SwapInitial from '../../../swap/SwapInitial'; import TransferInitial from '../../../transfer/TransferInitial'; import Transition, { ACTIVE_SLIDE_CLASS_NAME, TO_SLIDE_CLASS_NAME } from '../../../ui/Transition'; import styles from './LandscapeActions.module.scss'; -const TABS = [ActiveTab.Receive, ActiveTab.Transfer, ActiveTab.Stake]; +const TABS = [ActiveTab.Receive, ActiveTab.Transfer, ActiveTab.Swap, ActiveTab.Stake]; interface OwnProps { hasStaking?: boolean; @@ -58,11 +57,18 @@ function LandscapeActions({ isStaking, ); + const isSwapAllowed = !(isTestnet || isLedger); + const isStakingAllowed = !(isTestnet || isLedger); + const areNotAllTabs = !isSwapAllowed || !isStakingAllowed; + useEffect(() => { - if ((isTestnet || isLedger) && [ActiveTab.Stake].includes(activeTabIndex)) { + if ( + (!isSwapAllowed && activeTabIndex === ActiveTab.Swap) + || (!isStakingAllowed && activeTabIndex === ActiveTab.Stake) + ) { setActiveTabIndex({ index: ActiveTab.Transfer }); } - }, [activeTabIndex, isTestnet, isLedger]); + }, [activeTabIndex, isTestnet, isLedger, isSwapAllowed, isStakingAllowed]); function renderCurrentTab(isActive: boolean) { switch (activeTabIndex) { @@ -76,6 +82,13 @@ function LandscapeActions({
); + case ActiveTab.Swap: + return ( +
+ +
+ ); + case ActiveTab.Stake: if (hasStaking || isUnstakeRequested) { return ( @@ -109,7 +122,7 @@ function LandscapeActions({ return (
- {!isTestnet && !isLedger && ( + {isSwapAllowed && ( +
setActiveTabIndex({ index: ActiveTab.Swap })} + > + + {lang('Swap')} + + +
+ )} + {isStakingAllowed && (
((global, ownProps, detachWhenChanged): StateProps => { - detachWhenChanged(global.currentAccountId); - - const accountState = selectAccountState(global, global.currentAccountId!) ?? {}; - - return { - activeTabIndex: accountState?.landscapeActionsActiveTabIndex, - isTestnet: global.settings.isTestnet, - }; - })(LandscapeActions), + withGlobal( + (global): StateProps => { + const accountState = selectAccountState(global, global.currentAccountId!) ?? {}; + + return { + activeTabIndex: accountState?.landscapeActionsActiveTabIndex, + isTestnet: global.settings.isTestnet, + }; + }, + (global, _, stickToFirst) => stickToFirst(global.currentAccountId), + )(LandscapeActions), ); diff --git a/src/components/main/sections/Actions/PortraitActions.module.scss b/src/components/main/sections/Actions/PortraitActions.module.scss index aea1d245..5703a0f6 100644 --- a/src/components/main/sections/Actions/PortraitActions.module.scss +++ b/src/components/main/sections/Actions/PortraitActions.module.scss @@ -38,9 +38,11 @@ transition: background-color 150ms, color 150ms; - &:hover, - &:focus { - color: var(--color-blue); + @media (hover: hover) { + &:hover, + &:focus { + color: var(--color-blue); + } } &:active { diff --git a/src/components/main/sections/Actions/PortraitActions.tsx b/src/components/main/sections/Actions/PortraitActions.tsx index 2f25c081..d3e7de6a 100644 --- a/src/components/main/sections/Actions/PortraitActions.tsx +++ b/src/components/main/sections/Actions/PortraitActions.tsx @@ -1,13 +1,16 @@ +import type { URLOpenListenerEvent } from '@capacitor/app'; +import { App as CapacitorApp } from '@capacitor/app'; import React, { memo, useEffect } from '../../../../lib/teact/teact'; import { getActions } from '../../../../global'; import { ElectronEvent } from '../../../../electron/types'; -import { TON_TOKEN_SLUG } from '../../../../config'; -import { bigStrToHuman } from '../../../../global/helpers'; import buildClassName from '../../../../util/buildClassName'; +import { clearLaunchUrl, getLaunchUrl } from '../../../../util/capacitor'; +import { processDeeplink } from '../../../../util/processDeeplink'; import useLang from '../../../../hooks/useLang'; +import useLastCallback from '../../../../hooks/useLastCallback'; import Button from '../../../ui/Button'; @@ -25,21 +28,39 @@ interface OwnProps { function PortraitActions({ hasStaking, isTestnet, isUnstakeRequested, onEarnClick, onReceiveClick, isLedger, }: OwnProps) { - const { startTransfer } = getActions(); + const { startTransfer, startSwap } = getActions(); const lang = useLang(); + const isSwapAllowed = !(isTestnet || isLedger); + const isStakingAllowed = !(isTestnet || isLedger); + useEffect(() => { - return window.electron?.on(ElectronEvent.DEEPLINK, (params: any) => { - startTransfer({ - tokenSlug: TON_TOKEN_SLUG, - toAddress: params.to, - amount: bigStrToHuman(params.amount), - comment: params.text, - }); + return window.electron?.on(ElectronEvent.DEEPLINK, ({ url }: { url: string }) => { + processDeeplink(url); }); }, [startTransfer]); + useEffect(() => { + const launchUrl = getLaunchUrl(); + if (launchUrl) { + processDeeplink(launchUrl); + clearLaunchUrl(); + } + + return CapacitorApp.addListener('appUrlOpen', (event: URLOpenListenerEvent) => { + processDeeplink(event.url); + }).remove; + }, [startTransfer]); + + const handleStartSwap = useLastCallback(() => { + startSwap({ isPortrait: true }); + }); + + const handleStartTransfer = useLastCallback(() => { + startTransfer({ isPortrait: true }); + }); + return (
@@ -47,11 +68,17 @@ function PortraitActions({ {lang('Receive')} - - {!isTestnet && !isLedger && ( + {isSwapAllowed && ( + + )} + {isStakingAllowed && ( + {shouldRenderSettingsButton && ( + + )} + {shouldRenderQrScannerButton && ( + + )} ); } @@ -212,17 +252,19 @@ function AccountSelector({ {isEdit && renderInput()} {shouldRender && renderAccountsChooser()} - ); } -export default memo(withGlobal((global): StateProps => { - const accounts = selectNetworkAccounts(global); - - return { - currentAccountId: global.currentAccountId!, - currentAccount: accounts?.[global.currentAccountId!], - accounts, - }; -})(AccountSelector)); +export default memo(withGlobal( + (global): StateProps => { + const accounts = selectNetworkAccounts(global); + + return { + currentAccountId: global.currentAccountId!, + currentAccount: accounts?.[global.currentAccountId!], + accounts, + }; + }, + (global, _, stickToFirst) => stickToFirst(global.currentAccountId), +)(AccountSelector)); diff --git a/src/components/main/sections/Card/Card.tsx b/src/components/main/sections/Card/Card.tsx index 2dbce5e2..d1961c28 100644 --- a/src/components/main/sections/Card/Card.tsx +++ b/src/components/main/sections/Card/Card.tsx @@ -1,31 +1,33 @@ import type { Ref } from 'react'; -import React, { - memo, useEffect, useMemo, -} from '../../../../lib/teact/teact'; +import React, { memo, useEffect, useMemo } from '../../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../../global'; +import type { ApiBaseCurrency } from '../../../../api/types'; import type { UserToken } from '../../../../global/types'; import { - DEFAULT_PRICE_CURRENCY, + IS_CAPACITOR, TON_TOKEN_SLUG, TONSCAN_BASE_MAINNET_URL, TONSCAN_BASE_TESTNET_URL, } from '../../../../config'; import { selectAccount, selectCurrentAccountState, selectCurrentAccountTokens } from '../../../../global/selectors'; import buildClassName from '../../../../util/buildClassName'; +import { vibrateOnSuccess } from '../../../../util/capacitor'; import captureEscKeyListener from '../../../../util/captureEscKeyListener'; import { copyTextToClipboard } from '../../../../util/clipboard'; -import { formatCurrency } from '../../../../util/formatNumber'; +import { formatCurrency, getShortCurrencySymbol } from '../../../../util/formatNumber'; import { shortenAddress } from '../../../../util/shortenAddress'; import { getTokenCardColor } from '../../helpers/card_colors'; import { buildTokenValues } from './helpers/buildTokenValues'; import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev'; +import useHistoryBack from '../../../../hooks/useHistoryBack'; import useLang from '../../../../hooks/useLang'; import useLastCallback from '../../../../hooks/useLastCallback'; import useShowTransition from '../../../../hooks/useShowTransition'; +import AnimatedCounter from '../../../ui/AnimatedCounter'; import Loading from '../../../ui/Loading'; import AccountSelector from './AccountSelector'; import TokenCard from './TokenCard'; @@ -34,8 +36,10 @@ import styles from './Card.module.scss'; interface OwnProps { ref?: Ref; + forceCloseAccountSelector?: boolean; onTokenCardClose: NoneToVoidFunction; onApyClick: NoneToVoidFunction; + onQrScanPress?: NoneToVoidFunction; } interface StateProps { @@ -44,6 +48,7 @@ interface StateProps { activeDappOrigin?: string; currentTokenSlug?: string; isTestnet?: boolean; + baseCurrency?: ApiBaseCurrency; } function Card({ @@ -52,15 +57,19 @@ function Card({ tokens, activeDappOrigin, currentTokenSlug, + forceCloseAccountSelector, onTokenCardClose, onApyClick, + onQrScanPress, isTestnet, + baseCurrency, }: OwnProps & StateProps) { const { showNotification } = getActions(); const lang = useLang(); const tonscanBaseUrl = isTestnet ? TONSCAN_BASE_TESTNET_URL : TONSCAN_BASE_MAINNET_URL; const tonscanAddressUrl = `${tonscanBaseUrl}address/${address}`; + const shortBaseSymbol = getShortCurrencySymbol(baseCurrency); const currentToken = useMemo(() => { return tokens ? tokens.find((token) => token.slug === currentTokenSlug) : undefined; @@ -102,6 +111,11 @@ function Card({ transitionClassNames: dappClassNames, } = useShowTransition(Boolean(dappDomain)); + useHistoryBack({ + isActive: Boolean(currentTokenSlug), + onBack: onTokenCardClose, + }); + useEffect( () => (shouldRenderTokenCard ? captureEscKeyListener(onTokenCardClose) : undefined), [shouldRenderTokenCard, onTokenCardClose], @@ -111,7 +125,10 @@ function Card({ if (!address) return; showNotification({ message: lang('Address was copied!') as string, icon: 'icon-copy' }); - copyTextToClipboard(address); + void copyTextToClipboard(address); + if (IS_CAPACITOR) { + void vibrateOnSuccess(); + } }); const { @@ -130,23 +147,45 @@ function Card({ return ( <>
- + {shouldRenderDapp && (
{renderingDappDomain}
)} -
- {DEFAULT_PRICE_CURRENCY} - {primaryWholePart} - {primaryFractionPart && .{primaryFractionPart}} -
+ { + shortBaseSymbol.length === 1 ? ( +
+ {shortBaseSymbol} + + {primaryFractionPart && ( + + + + )} +
+ ) : ( +
+ + {primaryFractionPart && ( + + + + )} + +  {shortBaseSymbol} + +
+ ) + } {primaryValue !== 0 && (
{changePrefix}   - {Math.abs(changePercent!)}% · {formatCurrency(Math.abs(changeValue!), DEFAULT_PRICE_CURRENCY)} + + {' · '} +
)}
@@ -190,16 +229,21 @@ function Card({ ); } -export default memo(withGlobal((global, ownProps, detachWhenChanged): StateProps => { - detachWhenChanged(global.currentAccountId); - const { address } = selectAccount(global, global.currentAccountId!) || {}; - const accountState = selectCurrentAccountState(global); - - return { - address, - tokens: selectCurrentAccountTokens(global), - activeDappOrigin: accountState?.activeDappOrigin, - currentTokenSlug: accountState?.currentTokenSlug, - isTestnet: global.settings.isTestnet, - }; -})(Card)); +export default memo( + withGlobal( + (global): StateProps => { + const { address } = selectAccount(global, global.currentAccountId!) || {}; + const accountState = selectCurrentAccountState(global); + + return { + address, + tokens: selectCurrentAccountTokens(global), + activeDappOrigin: accountState?.activeDappOrigin, + currentTokenSlug: accountState?.currentTokenSlug, + isTestnet: global.settings.isTestnet, + baseCurrency: global.settings.baseCurrency, + }; + }, + (global, _, stickToFirst) => stickToFirst(global.currentAccountId), + )(Card), +); diff --git a/src/components/main/sections/Card/StickyCard.module.scss b/src/components/main/sections/Card/StickyCard.module.scss index 0a24a770..63c52f63 100644 --- a/src/components/main/sections/Card/StickyCard.module.scss +++ b/src/components/main/sections/Card/StickyCard.module.scss @@ -1,3 +1,5 @@ +@import "../../../../styles/mixins"; + .root { position: fixed; top: 0; @@ -42,6 +44,17 @@ opacity: 1; } + + @supports (padding-top: env(safe-area-inset-top)) { + padding-top: env(safe-area-inset-top); + } + + @include respond-below(xs) { + // Fix for opera, dead zone of 37 pixels in extension window on windows + :global(html.is-windows.is-opera.is-extension) & { + padding-top: 1rem !important; + } + } } .content { @@ -55,6 +68,12 @@ max-width: 27rem; height: 3.75rem; margin: 0 auto; + + :global(html.with-safe-area-top) & { + box-sizing: content-box; + height: 1.5rem; + padding-bottom: 1.125rem; + } } .account { diff --git a/src/components/main/sections/Card/StickyCard.tsx b/src/components/main/sections/Card/StickyCard.tsx index b7d2a451..0ebfeb9e 100644 --- a/src/components/main/sections/Card/StickyCard.tsx +++ b/src/components/main/sections/Card/StickyCard.tsx @@ -1,11 +1,12 @@ import React, { memo, useMemo } from '../../../../lib/teact/teact'; import { withGlobal } from '../../../../global'; +import type { ApiBaseCurrency } from '../../../../api/types'; import type { UserToken } from '../../../../global/types'; -import { DEFAULT_PRICE_CURRENCY } from '../../../../config'; import { selectCurrentAccountTokens } from '../../../../global/selectors'; import buildClassName from '../../../../util/buildClassName'; +import { getShortCurrencySymbol } from '../../../../util/formatNumber'; import { buildTokenValues } from './helpers/buildTokenValues'; import AccountSelector from './AccountSelector'; @@ -14,17 +15,25 @@ import styles from './StickyCard.module.scss'; interface OwnProps { classNames?: string; + onQrScanPress?: NoneToVoidFunction; } interface StateProps { tokens?: UserToken[]; + baseCurrency?: ApiBaseCurrency; } -function StickyCard({ classNames, tokens }: OwnProps & StateProps) { +function StickyCard({ + classNames, + tokens, + onQrScanPress, + baseCurrency, +}: OwnProps & StateProps) { const values = useMemo(() => { return tokens ? buildTokenValues(tokens) : undefined; }, [tokens]); + const shortBaseSymbol = getShortCurrencySymbol(baseCurrency); const { primaryWholePart, primaryFractionPart } = values || {}; return ( @@ -33,10 +42,13 @@ function StickyCard({ classNames, tokens }: OwnProps & StateProps) {
- {DEFAULT_PRICE_CURRENCY} + {shortBaseSymbol} {primaryWholePart} {primaryFractionPart && .{primaryFractionPart}}
@@ -46,10 +58,13 @@ function StickyCard({ classNames, tokens }: OwnProps & StateProps) { ); } -export default memo(withGlobal((global, ownProps, detachWhenChanged): StateProps => { - detachWhenChanged(global.currentAccountId); - - return { - tokens: selectCurrentAccountTokens(global), - }; -})(StickyCard)); +export default memo( + withGlobal( + (global): StateProps => { + return { + tokens: selectCurrentAccountTokens(global), + }; + }, + (global, _, stickToFirst) => stickToFirst(global.currentAccountId), + )(StickyCard), +); diff --git a/src/components/main/sections/Card/TokenCard.tsx b/src/components/main/sections/Card/TokenCard.tsx index c8ef6367..d9f14cf3 100644 --- a/src/components/main/sections/Card/TokenCard.tsx +++ b/src/components/main/sections/Card/TokenCard.tsx @@ -1,19 +1,22 @@ import React, { memo, useState } from '../../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../../global'; +import type { ApiBaseCurrency } from '../../../../api/types'; import type { UserToken } from '../../../../global/types'; -import { DEFAULT_PRICE_CURRENCY, TON_TOKEN_SLUG } from '../../../../config'; +import { TON_TOKEN_SLUG } from '../../../../config'; import { selectCurrentAccountState } from '../../../../global/selectors'; import buildClassName from '../../../../util/buildClassName'; import { calcChangeValue } from '../../../../util/calcChangeValue'; import { formatShortDay } from '../../../../util/dateFormat'; -import { formatCurrency } from '../../../../util/formatNumber'; +import { formatCurrency, getShortCurrencySymbol } from '../../../../util/formatNumber'; import { round } from '../../../../util/round'; import { ASSET_LOGO_PATHS } from '../../../ui/helpers/assetLogos'; import { useDeviceScreen } from '../../../../hooks/useDeviceScreen'; +import useForceUpdate from '../../../../hooks/useForceUpdate'; import useLang from '../../../../hooks/useLang'; +import useTimeout from '../../../../hooks/useTimeout'; import TokenPriceChart from '../../../common/TokenPriceChart'; import Button from '../../../ui/Button'; @@ -34,6 +37,7 @@ interface OwnProps { interface StateProps { period?: keyof typeof HISTORY_PERIODS; apyValue: number; + baseCurrency?: ApiBaseCurrency; } const HISTORY_PERIODS = { @@ -43,11 +47,7 @@ const HISTORY_PERIODS = { } as const; const DEFAULT_PERIOD = '24h'; - -const COIN_MARKET_CAP_TOKENS: Record = { - toncoin: 'toncoin', - 'ton-tgr': 'tgr', -}; +const OFFLINE_TIMEOUT = 120000; // 2 minutes const CHART_DIMENSIONS = { width: 300, height: 64 }; @@ -59,11 +59,14 @@ function TokenCard({ apyValue, onApyClick, onClose, + baseCurrency, }: OwnProps & StateProps) { const { setCurrentTokenPeriod } = getActions(); const { isPortrait } = useDeviceScreen(); - const lang = useLang(); + const forceUpdate = useForceUpdate(); + + const shortBaseSymbol = getShortCurrencySymbol(baseCurrency); const currentHistoryPeriod = HISTORY_PERIODS[period].historyKey; const currentChangePeriod = HISTORY_PERIODS[period].changeKey; @@ -79,11 +82,15 @@ function TokenCard({ const history = currentHistoryPeriod in token ? token[currentHistoryPeriod] : undefined; + const tokenLastUpdatedAt = history?.length ? history[history.length - 1][0] * 1000 : undefined; const selectedHistoryPoint = history?.[selectedHistoryIndex]; const price = selectedHistoryPoint?.[1] || lastPrice; + const isLoading = !tokenLastUpdatedAt || (Date.now() - tokenLastUpdatedAt > OFFLINE_TIMEOUT); const dateStr = selectedHistoryPoint ? formatShortDay(lang.code!, selectedHistoryPoint[0] * 1000, true, true) - : lang('Now'); + : (isLoading && tokenLastUpdatedAt ? formatShortDay(lang.code!, tokenLastUpdatedAt, true, false) : lang('Now')); + + useTimeout(forceUpdate, isLoading ? undefined : OFFLINE_TIMEOUT, [tokenLastUpdatedAt]); const initialPrice = history?.[0]?.[1]; const change = initialPrice @@ -100,7 +107,7 @@ function TokenCard({ const withChange = Boolean(change !== undefined); const withHistory = Boolean(history?.length); const historyStartDay = withHistory ? new Date(history![0][0] * 1000) : undefined; - const withCmcButton = slug in COIN_MARKET_CAP_TOKENS; + const withCmcButton = Boolean(token.cmcSlug); function renderChart() { return ( @@ -117,7 +124,7 @@ function TokenCard({ } return ( -
+
+ ); +} + +export default memo(Swap); + +function renderCurrency(activity: ApiSwapActivity, fromToken?: ApiSwapAsset, toToken?: ApiSwapAsset) { + const rate = getSwapRate(activity.fromAmount, activity.toAmount, fromToken, toToken); + + if (!rate) return undefined; + + return ( +
+ {rate.firstCurrencySymbol}{' ≈ '} + + {rate.price}{' '}{rate.secondCurrencySymbol} + +
+ ); +} diff --git a/src/components/main/sections/Content/Token.module.scss b/src/components/main/sections/Content/Token.module.scss index 49c7bf4b..fc89a24f 100644 --- a/src/components/main/sections/Content/Token.module.scss +++ b/src/components/main/sections/Content/Token.module.scss @@ -38,8 +38,14 @@ } } - &:focus, - &:hover { + @media (hover: hover) { + &:focus, + &:hover { + background-color: var(--color-interactive-item-hover); + } + } + + &.active { background-color: var(--color-interactive-item-hover); } diff --git a/src/components/main/sections/Content/Token.tsx b/src/components/main/sections/Content/Token.tsx index 6645bf2d..76fb266e 100644 --- a/src/components/main/sections/Content/Token.tsx +++ b/src/components/main/sections/Content/Token.tsx @@ -1,11 +1,12 @@ import React, { memo } from '../../../../lib/teact/teact'; +import type { ApiBaseCurrency } from '../../../../api/types'; import type { UserToken } from '../../../../global/types'; -import { DEFAULT_PRICE_CURRENCY, TON_TOKEN_SLUG } from '../../../../config'; +import { TON_TOKEN_SLUG } from '../../../../config'; import buildClassName from '../../../../util/buildClassName'; import { calcChangeValue } from '../../../../util/calcChangeValue'; -import { formatCurrency } from '../../../../util/formatNumber'; +import { formatCurrency, getShortCurrencySymbol } from '../../../../util/formatNumber'; import { round } from '../../../../util/round'; import { ASSET_LOGO_PATHS } from '../../../ui/helpers/assetLogos'; @@ -24,7 +25,9 @@ interface OwnProps { isInvestorView?: boolean; classNames?: string; apyValue?: number; + isActive?: boolean; onClick: (slug: string) => void; + baseCurrency?: ApiBaseCurrency; } function Token({ @@ -34,7 +37,9 @@ function Token({ apyValue, isInvestorView, classNames, + isActive, onClick, + baseCurrency, }: OwnProps) { const { name, @@ -53,6 +58,8 @@ function Token({ const changeValue = Math.abs(round(calcChangeValue(value, change), 4)); const changePercent = Math.abs(round(change * 100, 2)); const withApy = Boolean(apyValue) && slug === TON_TOKEN_SLUG; + const fullClassName = buildClassName(styles.container, isActive && styles.active, classNames); + const shortBaseSymbol = getShortCurrencySymbol(baseCurrency); const { shouldRender: shouldRenderApy, @@ -86,7 +93,7 @@ function Token({ function renderInvestorView() { return ( -
- +
{renderChangeIcon()}% - +
@@ -128,7 +135,7 @@ function Token({ const canRenderApy = Boolean(apyValue) && slug === TON_TOKEN_SLUG; return ( -
diff --git a/src/components/main/sections/Content/Transaction.module.scss b/src/components/main/sections/Content/Transaction.module.scss index 34170bb6..5a58ae37 100644 --- a/src/components/main/sections/Content/Transaction.module.scss +++ b/src/components/main/sections/Content/Transaction.module.scss @@ -18,23 +18,27 @@ color: var(--color-black); text-align: left; - &:not(:last-of-type) { - &::after { - content: ''; - - position: absolute; - right: 1.5rem; - bottom: 0; - left: 3.625rem; - - height: 0.0625rem; - /* stylelint-disable-next-line plugin/whole-pixel */ - box-shadow: inset 0 -0.025rem 0 0 var(--color-separator); + &::after { + content: ''; + + position: absolute; + right: 1.5rem; + bottom: 0; + left: 3.625rem; + + height: 0.0625rem; + /* stylelint-disable-next-line plugin/whole-pixel */ + box-shadow: inset 0 -0.025rem 0 0 var(--color-separator); + } + + @media (hover: hover) { + &:focus, + &:hover { + background-color: var(--color-interactive-item-hover); } } - &:focus, - &:hover { + &.active { background-color: var(--color-interactive-item-hover); } @@ -44,6 +48,12 @@ } } +.itemLast { + &::after { + content: none; + } +} + .iconArrow { position: absolute; top: 0.8125rem; @@ -87,12 +97,40 @@ background-color: var(--color-activity-purple-background); } + + &_error { + color: var(--color-red); + + background: var(--color-activity-red-background) !important; + } + + &_swap { + font-size: 1.875rem; + + background-image: linear-gradient(135deg, #EDF1F6 13.89%, #EAFBF1 86.11%); + + :global(.theme-dark) & { + background-image: linear-gradient(135deg, #2E3947 13.89%, #1F3838 86.11%); + } + + &:not(.icon_error)::before { + color: transparent; + + background: linear-gradient(74deg, #53657B 29.67%, #1EC160 45.27%); + background-clip: text; + background-size: 100%; + + :global(.theme-dark) & { + background-image: linear-gradient(74deg, #A3B8CA 29.67%, #2CD36F 45.27%); + } + } + } } .iconWaiting { position: absolute; top: 2.25rem; - left: 2.375rem; + left: 2.125rem; font-size: 1rem; line-height: 1; @@ -119,6 +157,10 @@ color: var(--color-activity-green-text); } +.iconWaitingStake { + color: var(--color-activity-purple-text); +} + .leftBlock { grid-area: left; @@ -222,3 +264,44 @@ width: 2.375rem; margin-left: 0.25rem; } + +.swapAmount { + display: flex; + align-items: center; +} + +.swapArrowRight { + padding: 0 0.125rem; + + color: transparent; + + background: linear-gradient(90deg, #8399AE 0.98%, #2CD36F 99.02%); + background-clip: text; + background-size: 100%; + + :global(.theme-dark) & { + background-image: linear-gradient(90deg, #8799B3 0.98%, #2CD36F 99.02%); + } +} + +.swapSell { + color: var(--color-gray-2); +} + +.swapBuy { + color: var(--color-green); +} + +.swapError { + color: var(--color-red); +} + +.swapHold { + color: var(--color-gray-2); +} + +.isSwapErrorMessage { + font-size: 0.75rem; + font-weight: 400; + color: var(--color-red); +} diff --git a/src/components/main/sections/Content/Transaction.tsx b/src/components/main/sections/Content/Transaction.tsx index 8defd972..c2bd0103 100644 --- a/src/components/main/sections/Content/Transaction.tsx +++ b/src/components/main/sections/Content/Transaction.tsx @@ -23,6 +23,8 @@ type OwnProps = { ref?: Ref; tokensBySlug?: Record; transaction: ApiTransactionActivity; + isLast: boolean; + isActive: boolean; apyValue: number; savedAddresses?: Record; onClick: (id: string) => void; @@ -32,8 +34,10 @@ function Transaction({ ref, tokensBySlug, transaction, + isActive, apyValue, savedAddresses, + isLast, onClick, }: OwnProps) { const lang = useLang(); @@ -143,18 +147,28 @@ function Transaction({ ); } + const waitingIconClassName = buildClassName( + styles.iconWaiting, + isStaking && styles.iconWaitingStake, + 'icon-clock', + ); + return ( +
@@ -85,3 +116,33 @@ export default memo( }; })(Content), ); + +function initializeQrCode(cb: NoneToVoidFunction) { + import('qr-code-styling') + .then(({ default: QrCodeStyling }) => { + qrCode = new QrCodeStyling({ + width: QR_SIZE, + height: QR_SIZE, + image: './logo.svg', + margin: 0, + type: 'canvas', + dotsOptions: { + type: 'rounded', + }, + cornersSquareOptions: { + type: 'extra-rounded', + }, + imageOptions: { + imageSize: 0.4, + margin: 8, + crossOrigin: 'anonymous', + }, + qrOptions: { + errorCorrectionLevel: 'M', + }, + data: formatTransferUrl(''), + }); + + cb(); + }); +} diff --git a/src/components/receive/InvoiceModal.tsx b/src/components/receive/InvoiceModal.tsx index 16516f3b..85d6dfbc 100644 --- a/src/components/receive/InvoiceModal.tsx +++ b/src/components/receive/InvoiceModal.tsx @@ -1,6 +1,4 @@ -import React, { - memo, useMemo, useState, -} from '../../lib/teact/teact'; +import React, { memo, useMemo, useState } from '../../lib/teact/teact'; import { withGlobal } from '../../global'; import type { UserToken } from '../../global/types'; @@ -17,7 +15,6 @@ import { ASSET_LOGO_PATHS } from '../ui/helpers/assetLogos'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; -import Button from '../ui/Button'; import Dropdown from '../ui/Dropdown'; import Input from '../ui/Input'; import InteractiveTextField from '../ui/InteractiveTextField'; @@ -32,18 +29,14 @@ interface StateProps { } type OwnProps = { - backButtonText?: string; isOpen: boolean; - onBackButtonClick: () => void; onClose: () => void; }; function InvoiceModal({ address, - backButtonText = 'Back', isOpen, tokens, - onBackButtonClick, onClose, }: StateProps & OwnProps) { const lang = useLang(); @@ -95,13 +88,14 @@ function InvoiceModal({ return ( -
+
{renderText(lang('$receive_invoice_description'))}
-

+

{lang('Share this URL to receive TON')}

- - -
- -
+ ); } diff --git a/src/components/receive/QrModal.tsx b/src/components/receive/QrModal.tsx deleted file mode 100644 index 826ba286..00000000 --- a/src/components/receive/QrModal.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import QrCodeStyling from 'qr-code-styling'; -import React, { memo, useEffect, useRef } from '../../lib/teact/teact'; -import { withGlobal } from '../../global'; - -import { selectAccount } from '../../global/selectors'; -import buildClassName from '../../util/buildClassName'; -import { shortenAddress } from '../../util/shortenAddress'; -import formatTransferUrl from '../../util/ton/formatTransferUrl'; - -import useLang from '../../hooks/useLang'; - -import Button from '../ui/Button'; -import Modal from '../ui/Modal'; - -import styles from './ReceiveModal.module.scss'; - -const QR_SIZE = 600; -let qrCode: QrCodeStyling; - -type StateProps = { - address?: string; -}; - -type OwnProps = { - backButtonText?: string; - isOpen: boolean; - onBackButtonClick: () => void; - onClose: () => void; -}; - -function QrModal({ - address, backButtonText, isOpen, onBackButtonClick, onClose, -}: StateProps & OwnProps) { - const lang = useLang(); - // eslint-disable-next-line no-null/no-null - const qrCodeRef = useRef(null); - - useEffect(() => { - if (isOpen) { - if (!qrCode) { - qrCode = initializeQrCode(); - } - qrCode.append(qrCodeRef.current || undefined); - } - }, [isOpen]); - - useEffect(() => { - if (!address || !qrCode || !isOpen) { - return; - } - qrCode.update({ data: formatTransferUrl(address) }); - }, [address, isOpen]); - - return ( - -
-
-

{address && shortenAddress(address)}

- -
- -
-
- - ); -} - -export default memo( - withGlobal((global): StateProps => { - const address = selectAccount(global, global.currentAccountId!)?.address; - - return { - address, - }; - })(QrModal), -); - -function initializeQrCode() { - return new QrCodeStyling({ - width: QR_SIZE, - height: QR_SIZE, - image: './logo.svg', - margin: 0, - type: 'canvas', - dotsOptions: { - type: 'rounded', - }, - cornersSquareOptions: { - type: 'extra-rounded', - }, - imageOptions: { - imageSize: 0.4, - margin: 8, - crossOrigin: 'anonymous', - }, - qrOptions: { - errorCorrectionLevel: 'M', - }, - data: formatTransferUrl(''), - }); -} diff --git a/src/components/receive/ReceiveModal.module.scss b/src/components/receive/ReceiveModal.module.scss index 4503452d..77ed85a6 100644 --- a/src/components/receive/ReceiveModal.module.scss +++ b/src/components/receive/ReceiveModal.module.scss @@ -1,6 +1,10 @@ @import '../../styles/mixins'; .content { + display: flex; + flex-direction: column; + flex-grow: 1; + padding: 0 1rem 1rem; @supports (padding-bottom: env(safe-area-inset-bottom)) { @@ -8,29 +12,13 @@ } } -.contentQr { - margin-top: -0.25rem; -} +.contentTitle { + margin-bottom: 1.5rem; -.info { font-size: 0.9375rem; - font-weight: 600; - line-height: 1.0625rem; - color: var(--color-gray-1); + line-height: 1.1875rem; + color: var(--color-gray-2); text-align: center; - - &_push { - margin-bottom: 1.25rem; - } - - &_small { - margin-top: 0.75rem; - margin-bottom: 0; - } - - &.infoStatic { - padding-top: 0.5rem; - } } .qrCode { @@ -40,12 +28,13 @@ aspect-ratio: 1; width: 100%; - max-width: 20.5rem; - max-height: 20.5rem; - margin: 0 auto; + max-width: 11rem; + margin: 1rem auto 0; background-color: var(--color-white); - border-radius: var(--border-radius-default); + border-radius: var(--border-radius-card); + + transition: opacity 350ms ease-in-out; canvas { position: absolute; @@ -59,17 +48,23 @@ } } -.description { - margin: 1.5rem 0 0.375rem 0.5rem; +.qrCodeHidden { + opacity: 0; + + transition: none; +} + +.label { + margin: 0 0 0.5rem 0.5rem; font-size: 0.8125rem; font-weight: 700; line-height: 1; color: var(--color-gray-2); +} - &_forInvoice { - margin-top: 0.3125rem; - } +.labelForInvoice { + margin-top: 0.3125rem; } .copyButtonStatic { @@ -77,31 +72,9 @@ border: 1px solid var(--color-separator-input-stroke) !important; } -.buttons { - display: flex; - gap: 0.5rem; - justify-content: center; - - margin-top: 2rem; -} - -.qrButton { - width: auto !important; - min-width: 1.75rem !important; - padding: 0.5rem !important; - - font-size: 1.875rem !important; - - @include respond-above(xs) { - background-color: var(--color-gray-button-background-desktop) !important; - - &:hover { - background-color: var(--color-gray-button-background-desktop-hover) !important; - } - } -} - .invoiceButton { + max-width: 100% !important; + @include respond-above(xs) { background-color: var(--color-gray-button-background-desktop) !important; @@ -111,10 +84,6 @@ } } -.qrIcon { - max-width: 1.75rem; -} - .tokenDropdown { --offset-y-value: calc(100% - 3.25rem); --offset-x-value: 0; diff --git a/src/components/receive/ReceiveModal.tsx b/src/components/receive/ReceiveModal.tsx index aa2cfabb..3a1e6753 100644 --- a/src/components/receive/ReceiveModal.tsx +++ b/src/components/receive/ReceiveModal.tsx @@ -1,5 +1,11 @@ -import React, { memo } from '../../lib/teact/teact'; +import { BottomSheet } from 'native-bottom-sheet'; +import React, { memo, useEffect } from '../../lib/teact/teact'; +import { IS_DELEGATED_BOTTOM_SHEET } from '../../util/windowEnvironment'; + +import { useOpenFromMainBottomSheet } from '../../hooks/useDelegatedBottomSheet'; +import { useOpenFromNativeBottomSheet } from '../../hooks/useDelegatingBottomSheet'; +import { useDeviceScreen } from '../../hooks/useDeviceScreen'; import useFlag from '../../hooks/useFlag'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; @@ -7,7 +13,6 @@ import useLastCallback from '../../hooks/useLastCallback'; import Modal from '../ui/Modal'; import Content from './Content'; import InvoiceModal from './InvoiceModal'; -import QrModal from './QrModal'; import styles from './ReceiveModal.module.scss'; @@ -18,24 +23,49 @@ type Props = { function ReceiveModal({ isOpen, onClose }: Props) { const lang = useLang(); - const [isQrModalOpen, openQrModal, closeQrModal] = useFlag(false); + const { isPortrait } = useDeviceScreen(); const [isInvoiceModalOpen, openInvoiceModal, closeInvoiceModal] = useFlag(false); + useOpenFromNativeBottomSheet('invoice', openInvoiceModal); + useOpenFromMainBottomSheet('invoice', openInvoiceModal); + + useEffect(() => { + if (isOpen && !isPortrait) { + onClose(); + } + }, [isOpen, isPortrait, onClose]); + + const handleOpenInvoiceModal = useLastCallback(() => { + onClose(); + + if (IS_DELEGATED_BOTTOM_SHEET) { + BottomSheet.openInMain({ key: 'invoice' }); + } else { + openInvoiceModal(); + } + }); + const handleClose = useLastCallback(() => { onClose(); closeInvoiceModal(); - closeQrModal(); }); return ( <> - -
- -
+ + - - + ); } diff --git a/src/components/receive/ReceiveStatic.tsx b/src/components/receive/ReceiveStatic.tsx index 370cfa9d..a3eb7066 100644 --- a/src/components/receive/ReceiveStatic.tsx +++ b/src/components/receive/ReceiveStatic.tsx @@ -1,41 +1,23 @@ import React, { memo } from '../../lib/teact/teact'; import useFlag from '../../hooks/useFlag'; -import useLang from '../../hooks/useLang'; -import useLastCallback from '../../hooks/useLastCallback'; import Content from './Content'; import InvoiceModal from './InvoiceModal'; -import QrModal from './QrModal'; type Props = { className?: string; }; function ReceiveStatic({ className }: Props) { - const lang = useLang(); - const [isQrModalOpen, openQrModal, closeQrModal] = useFlag(false); const [isInvoiceModalOpen, openInvoiceModal, closeInvoiceModal] = useFlag(false); - const handleClose = useLastCallback(() => { - closeInvoiceModal(); - closeQrModal(); - }); - return (
- - +
); diff --git a/src/components/settings/SelectTokens.tsx b/src/components/settings/SelectTokens.tsx deleted file mode 100644 index e8fd5a18..00000000 --- a/src/components/settings/SelectTokens.tsx +++ /dev/null @@ -1,348 +0,0 @@ -import React, { - memo, useEffect, useMemo, useRef, useState, -} from '../../lib/teact/teact'; -import { getActions, withGlobal } from '../../global'; - -import type { UserToken } from '../../global/types'; - -import buildClassName from '../../util/buildClassName'; -import captureEscKeyListener from '../../util/captureEscKeyListener'; -import { formatCurrencyForBigValue, formatInteger } from '../../util/formatNumber'; -import { getIsAddressValid } from '../../util/getIsAddressValid'; -import { REM } from '../../util/windowEnvironment'; -import { ASSET_LOGO_PATHS } from '../ui/helpers/assetLogos'; - -import useLang from '../../hooks/useLang'; -import useLastCallback from '../../hooks/useLastCallback'; -import useScrolledState from '../../hooks/useScrolledState'; -import useShowTransition from '../../hooks/useShowTransition'; - -import Portal from '../ui/Portal'; -import Transition from '../ui/Transition'; - -import styles from './Settings.module.scss'; - -interface OwnProps { - isOpen: boolean; - type?: 'price' | 'balance'; - shouldFilter?: boolean; - tokens?: UserToken[]; - position?: { - top: number; - right: number; - left: number; - width: number; - }; - offset?: { - top: number; - left: number; - right: number; - bottom: number; - }; - onClose: NoneToVoidFunction; - onSelect: (token: UserToken) => void; -} - -interface StateProps { - token?: UserToken; - isLoading?: boolean; -} - -enum SearchState { - Popular, - Loading, - Token, - Empty, -} - -const SLIDE_ANIMATION_DURATION_MS = 250; - -const DEFAULT_OFFSET = { - top: 0.25 * REM, - right: 0.5 * REM, - bottom: REM, - left: 0, -}; - -function SelectTokens({ - token, - type = 'price', - shouldFilter = false, - tokens, - isOpen, - isLoading, - position, - offset = DEFAULT_OFFSET, - onClose, - onSelect, -}: OwnProps & StateProps) { - const { - importToken, - resetImportToken, - } = getActions(); - const lang = useLang(); - - const { shouldRender, transitionClassNames } = useShowTransition(isOpen); - const [searchValue, setSearchValue] = useState(''); - const [style, setStyle] = useState(); - const [renderingKey, setRenderingKey] = useState(SearchState.Popular); - - // eslint-disable-next-line no-null/no-null - const elementRef = useRef(null); - const isImportingRef = useRef(false); - - const { - handleScroll: handleContentScroll, - isAtBeginning: isContentNotScrolled, - } = useScrolledState(); - - useEffect(() => { - if (!elementRef.current) return; - - if (position) { - const { - top, right, - } = position; - - const { width: componentWidth, height: componentHeight } = elementRef.current.getBoundingClientRect(); - const isTop = window.innerHeight > top + componentHeight; - const verticalStyle = isTop - ? `top: ${top + offset.top}px;` - : `top: calc(100% - ${componentHeight + offset.bottom}px);`; - - setStyle(`${verticalStyle} left: ${(right - componentWidth) - offset.right}px;`); - } - }, [position, offset]); - - useEffect( - () => (shouldRender ? captureEscKeyListener(onClose) : undefined), - [onClose, shouldRender], - ); - - useEffect(() => { - setSearchValue(''); - resetImportToken(); - }, [shouldRender]); - - useEffect(() => { - if (getIsAddressValid(searchValue)) { - isImportingRef.current = true; - importToken({ address: searchValue }); - } else { - resetImportToken(); - } - }, [importToken, searchValue]); - - const filteredTokenList = useMemo(() => tokens?.filter((t) => !t.isDisabled).filter( - ({ symbol, keywords }) => { - const isSymbol = symbol.toLowerCase().includes(searchValue.toLowerCase()); - const isKeyword = keywords?.find((key) => key.toLowerCase().includes(searchValue.toLowerCase())); - return isSymbol || isKeyword; - }, - ) ?? [], [tokens, searchValue]); - - useEffect(() => { - const isImporting = isLoading || isImportingRef.current; - - if (isImporting && getIsAddressValid(searchValue)) { - isImportingRef.current = false; - setRenderingKey(SearchState.Loading); - } else if (token && getIsAddressValid(searchValue)) { - setRenderingKey(SearchState.Token); - } else if (filteredTokenList.length === 0) { - setRenderingKey(SearchState.Empty); - } else { - setRenderingKey(SearchState.Popular); - } - }, [token, isLoading, filteredTokenList, searchValue]); - - const handleTokenClick = useLastCallback((selectedToken: UserToken) => { - onClose(); - setTimeout(() => { - onSelect(selectedToken); - }, SLIDE_ANIMATION_DURATION_MS); - }); - - const popularTokenList = useMemo(() => filteredTokenList.map((t) => { - const image = t?.image ?? ASSET_LOGO_PATHS[t?.symbol.toLowerCase() as keyof typeof ASSET_LOGO_PATHS]; - const isAvailable = !shouldFilter; - const value = isAvailable - ? type === 'price' - ? formatCurrencyForBigValue(t.price) - : formatInteger(t.amount) - : lang('Unavailable'); - const handleClick = isAvailable ? () => handleTokenClick(t) : undefined; - return ( -
- -
- {t.symbol} - - {value} - -
-
- ); - }), [filteredTokenList, handleTokenClick, type, lang, shouldFilter]); - - // eslint-disable-next-line consistent-return - function renderContent(isActive: boolean, isFrom: boolean, currentKey: number) { - const { image, symbol } = token ?? {}; - - let content; - - switch (currentKey) { - case SearchState.Popular: - content = popularTokenList; - break; - case SearchState.Loading: - content = ( -
-
-
- - -
-
- ); - break; - case SearchState.Token: - content = ( -
handleTokenClick(token!)}> - {symbol} -
- {symbol} - {formatCurrencyForBigValue(0)} -
-
- ); - break; - case SearchState.Empty: - content = ( -
-
- -
-
- - {lang('Token Not Found')} - - - {lang('Try another keyword or address.')} - -
-
- ); - break; - } - - const isSingle = currentKey !== SearchState.Popular || popularTokenList.length === 1; - - return ( -
-
- {content} -
-
- ); - } - - const fullClassName = buildClassName( - styles.addTokenDialog, - transitionClassNames, - ); - - return shouldRender && ( - -
-
-
-
-
- - setSearchValue(e.target.value)} - value={searchValue} - /> -
-
- - {renderContent} - -
-
- - ); -} - -function LazyImage({ symbol, image, isAvailable }: { symbol: string; image: string; isAvailable?: boolean }) { - const [isLoading, setIsLoading] = useState(true); - const [isImageVisible, setIsImageVisible] = useState(true); - const [firstLetter] = symbol; - - return ( - <> - {isImageVisible && ( - {symbol} setIsLoading(false)} - onError={() => setIsImageVisible(false)} - /> - )} - {isLoading && ( -
- {firstLetter} -
- )} - - ); -} - -export default memo(withGlobal((global): StateProps => { - const { isLoading, token } = global.settings.importToken ?? {}; - - return { - isLoading, - token, - }; -})(SelectTokens)); diff --git a/src/components/settings/Settings.module.scss b/src/components/settings/Settings.module.scss index 229be886..d260b6b3 100644 --- a/src/components/settings/Settings.module.scss +++ b/src/components/settings/Settings.module.scss @@ -23,6 +23,14 @@ background-color: var(--color-background-second); } +@supports (padding-top: env(safe-area-inset-top)) { + html:global(:not(.is-native-bottom-sheet)) { + .transitionSlide { + padding-top: env(safe-area-inset-top); + } + } +} + .developerTitle { margin: 1rem auto 1.5rem; @@ -41,8 +49,7 @@ grid-template-columns: 1fr max-content 1fr; align-items: center; - margin-bottom: 2.25rem; - padding: 1.5rem 0.125rem 0; + padding: 1.5rem 0.125rem 1.375rem; font-size: 1.0625rem; font-weight: 700; @@ -54,6 +61,10 @@ padding-top: 2.3125rem; } } + + :global(html.with-safe-area-top) & { + padding-top: 0.75rem; + } } .languageHeader { @@ -76,6 +87,12 @@ font-size: 1.5rem; } +.headerBackInContent { + position: absolute; + top: 1.125rem; + left: 0.125rem; +} + .headerTitle { display: flex; justify-content: center; @@ -97,17 +114,24 @@ height: 100%; min-height: 0; - padding: 0 1rem 1rem; + padding: 0.75rem 1rem 1rem; @include adapt-padding-to-scrollbar(1rem); &_noScroll { overflow: visible; } + + @supports (padding-bottom: max(env(safe-area-inset-bottom), 1rem)) { + padding-bottom: max(env(safe-area-inset-bottom), 1rem); + } } -.contentInModal { - padding-top: 0.75rem; +.contentFullSize { + overflow: visible; + align-items: center; + + padding: 0 !important; } .blockTitle { @@ -162,14 +186,14 @@ color: var(--color-black); &_active { + pointer-events: none; + font-weight: 700; color: var(--color-blue); } } .themeIcon { - position: relative; - &::after { content: ''; @@ -311,9 +335,17 @@ padding: 0.875rem 1rem; } - &:focus, - &:hover { - background-color: var(--color-interactive-item-hover); + @media (hover: hover) { + &:focus, + &:hover { + background-color: var(--color-interactive-item-hover); + } + } + + @media (pointer: coarse) { + &:active { + background-color: var(--color-interactive-item-hover); + } } &:active { @@ -361,12 +393,6 @@ } } -.languageInfo { - display: flex; - flex-direction: column; - gap: 0.25rem; -} - .languageMain { font-size: 0.9375rem; font-weight: 600; @@ -451,21 +477,17 @@ color: var(--color-black); } -.dapp { - position: relative; - - &:not(:last-of-type)::after { - content: ''; +.dapp:not(:last-of-type)::after { + content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 3.875rem; + position: absolute; + right: 0; + bottom: 0; + left: 3.875rem; - height: 0.0625rem; - /* stylelint-disable-next-line plugin/whole-pixel */ - box-shadow: inset 0 -0.025rem 0 0 var(--color-separator); - } + height: 0.0625rem; + /* stylelint-disable-next-line plugin/whole-pixel */ + box-shadow: inset 0 -0.025rem 0 0 var(--color-separator); } .backButton { @@ -480,6 +502,10 @@ margin: 0 auto 0.875rem; } +.stickerNativeBiometric { + margin-top: auto; +} + .title { margin-bottom: 1.25rem; @@ -566,7 +592,7 @@ } .tokenSortIcon { - cursor: pointer; + cursor: var(--custom-cursor, pointer); padding: 0 0.375rem; @@ -605,6 +631,8 @@ height: 0.1875rem; } +.dapp, +.themeIcon, .contentRelative, .sortableContainer { position: relative; @@ -617,7 +645,9 @@ height: 46rem; @supports (height: env(safe-area-inset-bottom)) { - height: calc(46rem + env(safe-area-inset-bottom)); + html:global(:not(.is-native-bottom-sheet)) { + height: calc(46rem + env(safe-area-inset-bottom)); + } } } @@ -625,7 +655,9 @@ height: 38rem; @supports (height: env(safe-area-inset-bottom)) { - height: calc(38rem + env(safe-area-inset-bottom)); + html:global(:not(.is-native-bottom-sheet)) { + height: calc(38rem + env(safe-area-inset-bottom)); + } } } @@ -646,270 +678,13 @@ } } -.addTokenTransition { - min-height: 4.5rem; -} - -.addTokenDialog { - position: absolute; - z-index: var(--z-menu-bubble); - transform: translateY(-0.375rem) !important; - - transition: var(--dropdown-transition) !important; - - :global(html.animation-level-0) &, - &:global(.open) { - transform: translateY(0) !important; - } - - &:global(.closing) { - transition: var(--dropdown-transition-backwards) !important; - - :global(html.animation-level-0) & { - transition: var(--no-animation-transition) !important; - } - } - - :global(html.animation-level-0) & { - transition: var(--no-animation-transition) !important; - } -} - -.addTokenBlock { - position: relative; - z-index: var(--z-menu-bubble); - - width: 19rem; - max-height: 15rem; - - background-color: var(--color-background-drop-down); - border-radius: var(--border-radius-default); - box-shadow: var(--default-shadow); -} - -.addTokenSearch { - position: relative; - z-index: 1; - - display: flex; - - padding: 0.5rem; - - background-color: var(--color-background-drop-down); - border-radius: var(--border-radius-default) var(--border-radius-default) 0 0; - - &_bordered { - /* stylelint-disable-next-line plugin/whole-pixel */ - box-shadow: 0 0.035rem 0 0 var(--color-separator); - } -} - -.addTokenInputWrapper { - display: flex; - align-items: center; - - width: 100%; - padding: 0.3125rem 0.375rem; - - font-size: 1.25rem; - line-height: 1; - color: var(--color-gray-3); - - background-color: var(--color-background-drop-down-search); - border-radius: var(--border-radius-default); -} - -.addTokenInput { - width: 100%; - - font-size: 0.9375rem; - color: var(--color-black); - - background: transparent; - border: none; - outline: none; - - appearance: none; - - &::placeholder { - font-weight: 600; - color: var(--color-gray-3); - } - - &:hover, - &:focus { - &::placeholder { - color: var(--color-interactive-input-text-hover-active); - } - } -} - -.addTokenContentWrapper { - overflow: hidden; - - max-height: 12rem; - - background-color: var(--color-background-drop-down); - border-radius: 0 0 var(--border-radius-default) var(--border-radius-default); -} - -.addTokenContent { - overflow-y: auto; - display: grid; - grid-gap: 0.5rem; - grid-template-columns: repeat(auto-fill, minmax(48%, 1fr)); - - max-height: 12rem; - padding: 0 0.5rem 0.5rem; - - &:global(.custom-scroll) { - overflow-x: hidden; - overflow-y: scroll; - - @include adapt-padding-to-scrollbar(0.5rem); - } - - &_single { - grid-template-columns: 1fr; - } -} - -.addTokenItem { - cursor: pointer; - - position: relative; - - overflow: hidden; - display: flex; - gap: 0.625rem; - align-items: center; - - height: 4rem; - padding-left: 0.75rem; - - border-radius: 0.625rem; - box-shadow: inset 0 0 0 0.0625rem var(--color-separator-input-stroke); - - &:hover { - background-color: var(--color-interactive-item-hover); - } -} - -.tokenInfo, -.addTokenText { +.languageInfo, +.tokenInfo { display: flex; flex-direction: column; gap: 0.25rem; } -.addTokenText_center { - align-items: center; - justify-content: center; - - width: 100%; -} - -.addTokenSymbol { - font-size: 0.9375rem; - font-weight: 600; - line-height: 1; - color: var(--color-black); - - &_loading { - width: 6.25rem; - height: 0.8125rem; - - background-color: var(--color-separator-input-stroke); - border-radius: var(--border-radius-default); - } - - &_gray, - &_disabled { - color: var(--color-gray-2); - } -} - -.addTokenPrice { - font-size: 0.75rem; - font-weight: 600; - line-height: 1; - color: var(--color-gray-2); - - &_loading { - width: 5rem; - height: 0.625rem; - - background-color: var(--color-separator-input-stroke); - border-radius: var(--border-radius-default); - } - - &_gray, - &_disabled { - color: var(--color-gray-3); - } -} - -.addTokenIconLetter { - font-size: 1.0625rem; - font-weight: 800; - color: var(--color-gray-3); - text-transform: capitalize; -} - -.addTokenIcon { - z-index: 2; - - flex-shrink: 0; - - width: 2.25rem; - height: 2.25rem; - - border-radius: 100%; - - &_loading { - background-color: var(--color-separator-input-stroke); - } - - &_empty { - display: flex; - align-items: center; - justify-content: center; - - box-shadow: inset 0 0 0 0.0625rem var(--color-gray-4); - } - - &_disabled { - opacity: 0.5; - } - - &_symbol { - position: absolute; - z-index: 1; - - display: flex; - align-items: center; - justify-content: center; - - box-shadow: inset 0 0 0 0.0625rem var(--color-gray-4); - } -} - -.emptyIcon { - margin-top: 0.3125rem; - - font-size: 0.9375rem; - color: var(--color-gray-3); -} - -.backdrop { - position: fixed; - z-index: var(--z-menu-backdrop); - top: -100vh; - right: -100vw; - bottom: -100vh; - left: -100vw; -} - .stickerAndTitle { display: flex; column-gap: 1rem; diff --git a/src/components/settings/Settings.tsx b/src/components/settings/Settings.tsx index 51cc071d..91e040f2 100644 --- a/src/components/settings/Settings.tsx +++ b/src/components/settings/Settings.tsx @@ -3,40 +3,53 @@ import React, { } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; -import type { ApiDapp } from '../../api/types'; -import type { - AnimationLevel, LangCode, Theme, UserToken, -} from '../../global/types'; +import { type GlobalState, SettingsState, type UserToken } from '../../global/types'; import { APP_NAME, APP_VERSION, + IS_CAPACITOR, IS_DAPP_SUPPORTED, - IS_ELECTRON, IS_EXTENSION, LANG_LIST, PROXY_HOSTS, TELEGRAM_WEB_URL, } from '../../config'; import { - selectAccountSettings, selectCurrentAccountTokens, selectPopularTokensWithoutAccountTokens, + selectAccountSettings, + selectCurrentAccountTokens, } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; +import { getIsNativeBiometricAuthSupported } from '../../util/capacitor'; import captureEscKeyListener from '../../util/captureEscKeyListener'; -import { captureSwipe, SwipeDirection } from '../../util/captureSwipe'; -import { IS_ANDROID, IS_LEDGER_SUPPORTED, IS_TOUCH_ENV } from '../../util/windowEnvironment'; +import resolveModalTransitionName from '../../util/resolveModalTransitionName'; +import { captureControlledSwipe } from '../../util/swipeController'; +import { + IS_BIOMETRIC_AUTH_SUPPORTED, + IS_DELEGATED_BOTTOM_SHEET, + IS_ELECTRON, + IS_LEDGER_SUPPORTED, + IS_TOUCH_ENV, +} from '../../util/windowEnvironment'; import useFlag from '../../hooks/useFlag'; +import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; +import useModalTransitionKeys from '../../hooks/useModalTransitionKeys'; +import usePrevious2 from '../../hooks/usePrevious2'; import useScrolledState from '../../hooks/useScrolledState'; import useShowTransition from '../../hooks/useShowTransition'; +import { useStateRef } from '../../hooks/useStateRef'; import LogOutModal from '../main/modals/LogOutModal'; import Button from '../ui/Button'; import ModalHeader from '../ui/ModalHeader'; import Switcher from '../ui/Switcher'; import Transition from '../ui/Transition'; +import Biometrics from './biometrics/Biometrics'; +import NativeBiometricsToggle from './biometrics/NativeBiometricsToggle'; +import SettingsNativeBiometricsTurnOn from './biometrics/NativeBiometricsTurnOn'; import SettingsAbout from './SettingsAbout'; import SettingsAppearance from './SettingsAppearance'; import SettingsAssets from './SettingsAssets'; @@ -44,6 +57,7 @@ import SettingsDapps from './SettingsDapps'; import SettingsDeveloperOptions from './SettingsDeveloperOptions'; import SettingsDisclaimer from './SettingsDisclaimer'; import SettingsLanguage from './SettingsLanguage'; +import SettingsTokenList from './SettingsTokenList'; import modalStyles from '../ui/Modal.module.scss'; import styles from './Settings.module.scss'; @@ -52,6 +66,7 @@ import aboutImg from '../../assets/settings/settings_about.svg'; import appearanceImg from '../../assets/settings/settings_appearance.svg'; import assetsActivityImg from '../../assets/settings/settings_assets-activity.svg'; import backupSecretImg from '../../assets/settings/settings_backup-secret.svg'; +import biometricsImg from '../../assets/settings/settings_biometrics.svg'; import connectedDappsImg from '../../assets/settings/settings_connected-dapps.svg'; import disclaimerImg from '../../assets/settings/settings_disclaimer.svg'; import exitImg from '../../assets/settings/settings_exit.svg'; @@ -62,63 +77,47 @@ import tonLinksImg from '../../assets/settings/settings_ton-links.svg'; import tonMagicImg from '../../assets/settings/settings_ton-magic.svg'; import tonProxyImg from '../../assets/settings/settings_ton-proxy.svg'; -const enum RenderingState { - Initial, - Appearance, - Assets, - Dapps, - Language, - About, - Disclaimer, -} - type OwnProps = { isInsideModal?: boolean; }; type StateProps = { - theme: Theme; - animationLevel: AnimationLevel; - areTinyTransfersHidden?: boolean; - isInvestorViewEnabled?: boolean; - isTestnet?: boolean; - canPlaySounds?: boolean; - langCode: LangCode; - isTonProxyEnabled?: boolean; - isTonMagicEnabled?: boolean; - isDeeplinkHookEnabled?: boolean; - areTokensWithNoBalanceHidden?: boolean; - areTokensWithNoPriceHidden?: boolean; - isSortByValueEnabled?: boolean; - dapps: ApiDapp[]; + settings: GlobalState['settings']; + isOpen?: boolean; tokens?: UserToken[]; - popularTokens?: UserToken[]; orderedSlugs?: string[]; + isBiometricAuthEnabled: boolean; }; const AMOUNT_OF_CLICKS_FOR_DEVELOPERS_MODE = 5; function Settings({ - theme, - animationLevel, - areTinyTransfersHidden, - isTestnet, - isInvestorViewEnabled, - canPlaySounds, - langCode, - isTonProxyEnabled, - isTonMagicEnabled, - isDeeplinkHookEnabled, - areTokensWithNoBalanceHidden, - areTokensWithNoPriceHidden, - isSortByValueEnabled, - dapps, + settings: { + state, + theme, + animationLevel, + areTinyTransfersHidden, + isTestnet, + isInvestorViewEnabled, + canPlaySounds, + langCode, + isTonProxyEnabled, + isTonMagicEnabled, + isDeeplinkHookEnabled, + areTokensWithNoBalanceHidden, + areTokensWithNoPriceHidden, + isSortByValueEnabled, + dapps, + baseCurrency, + }, + isOpen = false, tokens, - popularTokens, orderedSlugs, isInsideModal, + isBiometricAuthEnabled, }: OwnProps & StateProps) { const { + setSettingsState, openBackupWalletModal, openHardwareWalletModal, closeSettings, @@ -127,13 +126,17 @@ function Settings({ toggleTonMagic, getDapps, initTokensOrder, + openBiometricsTurnOn, + openBiometricsTurnOffWarning, + clearIsPinPadPasswordAccepted, } = getActions(); const lang = useLang(); // eslint-disable-next-line no-null/no-null const transitionRef = useRef(null); + const { renderingKey, nextKey } = useModalTransitionKeys(state, isOpen); const [clicksAmount, setClicksAmount] = useState(isTestnet ? AMOUNT_OF_CLICKS_FOR_DEVELOPERS_MODE : 0); - const [renderingKey, setRenderingKey] = useState(RenderingState.Initial); + const prevRenderingKeyRef = useStateRef(usePrevious2(renderingKey)); const [isDeveloperModalOpen, openDeveloperModal, closeDeveloperModal] = useFlag(); const [isLogOutModalOpened, openLogOutModal, closeLogOutModal] = useFlag(); @@ -151,36 +154,59 @@ function Settings({ const { handleScroll: handleContentScroll, - isAtBeginning: isContentNotScrolled, + isScrolled, } = useScrolledState(); + const handleSlideAnimationStop = useLastCallback(() => { + if (prevRenderingKeyRef.current === SettingsState.NativeBiometricsTurnOn) { + clearIsPinPadPasswordAccepted(); + } + }); + + const handleCloseSettings = useLastCallback(() => { + closeSettings(undefined, { forceOnHeavyAnimation: true }); + }); + + useHistoryBack({ + isActive: !isInsideModal && renderingKey === SettingsState.Initial, + onBack: handleCloseSettings, + }); + const handleConnectedDappsOpen = useLastCallback(() => { getDapps(); - setRenderingKey(RenderingState.Dapps); + setSettingsState({ state: SettingsState.Dapps }); }); function handleAppearanceOpen() { - setRenderingKey(RenderingState.Appearance); + setSettingsState({ state: SettingsState.Appearance }); } function handleAssetsOpen() { - setRenderingKey(RenderingState.Assets); + setSettingsState({ state: SettingsState.Assets }); } function handleLanguageOpen() { - setRenderingKey(RenderingState.Language); + setSettingsState({ state: SettingsState.Language }); } function handleAboutOpen() { - setRenderingKey(RenderingState.About); + setSettingsState({ state: SettingsState.About }); } function handleDisclaimerOpen() { - setRenderingKey(RenderingState.Disclaimer); + setSettingsState({ state: SettingsState.Disclaimer }); } + const handleNativeBiometricsTurnOnOpen = useLastCallback(() => { + setSettingsState({ state: SettingsState.NativeBiometricsTurnOn }); + }); + const handleBackClick = useLastCallback(() => { - setRenderingKey(RenderingState.Initial); + setSettingsState({ state: SettingsState.Initial }); + }); + + const handleBackClickToAssets = useLastCallback(() => { + setSettingsState({ state: SettingsState.Assets }); }); const handleDeeplinkHookToggle = useLastCallback(() => { @@ -195,13 +221,45 @@ function Settings({ toggleTonMagic({ isEnabled: !isTonMagicEnabled }); }); + const handleBiometricAuthToggle = useLastCallback(() => { + if (isBiometricAuthEnabled) { + openBiometricsTurnOffWarning(); + } else { + openBiometricsTurnOn(); + } + }); + function handleOpenBackupWallet() { + if (IS_DELEGATED_BOTTOM_SHEET) { + handleCloseSettings(); + } + openBackupWalletModal(); } + const [isTrayIconEnabled, setIsTrayIconEnabled] = useState(false); + useEffect(() => { + window.electron?.getIsTrayIconEnabled().then(setIsTrayIconEnabled); + }, []); + + const handleTrayIconEnabledToggle = useLastCallback(() => { + setIsTrayIconEnabled(!isTrayIconEnabled); + window.electron?.setIsTrayIconEnabled(!isTrayIconEnabled); + }); + + const [isAutoUpdateEnabled, setIsAutoUpdateEnabled] = useState(false); + useEffect(() => { + window.electron?.getIsAutoUpdateEnabled().then(setIsAutoUpdateEnabled); + }, []); + + const handleAutoUpdateEnabledToggle = useLastCallback(() => { + setIsAutoUpdateEnabled(!isAutoUpdateEnabled); + window.electron?.setIsAutoUpdateEnabled(!isAutoUpdateEnabled); + }); + const handleBackOrCloseAction = useLastCallback(() => { - if (renderingKey === RenderingState.Initial) { - closeSettings(); + if (renderingKey === SettingsState.Initial) { + handleCloseSettings(); } else { handleBackClick(); } @@ -210,7 +268,7 @@ function Settings({ const handleCloseLogOutModal = useLastCallback((shouldCloseSettings: boolean) => { closeLogOutModal(); if (shouldCloseSettings) { - closeSettings(); + handleCloseSettings(); } }); @@ -236,15 +294,13 @@ function Settings({ return undefined; } - return captureSwipe(transitionRef.current!, (e, direction) => { - if (direction === SwipeDirection.Right) { - handleBackOrCloseAction(); - return true; - } - - return false; + return captureControlledSwipe(transitionRef.current!, { + onSwipeRightStart: IS_DELEGATED_BOTTOM_SHEET ? handleBackClick : handleBackOrCloseAction, + onCancel: () => { + setSettingsState({ state: prevRenderingKeyRef.current! }); + }, }); - }, [handleBackOrCloseAction]); + }, [handleBackClick, handleBackOrCloseAction, prevRenderingKeyRef]); function renderHandleDeeplinkButton() { return ( @@ -267,13 +323,13 @@ function Settings({ {isInsideModal ? ( ) : ( -
- @@ -282,9 +338,28 @@ function Settings({ )}
+ {getIsNativeBiometricAuthSupported() && ( + + )} + {IS_BIOMETRIC_AUTH_SUPPORTED && ( +
+
+ {lang('Biometric + {lang('Biometric Authentication')} + + +
+
+ )} {IS_EXTENSION && (
{PROXY_HOSTS && ( @@ -339,7 +414,7 @@ function Settings({
- {(IS_DAPP_SUPPORTED) && ( + {IS_DAPP_SUPPORTED && (
{lang('Connected {lang('Connected Dapps')} @@ -410,23 +485,28 @@ function Settings({ // eslint-disable-next-line consistent-return function renderContent(isActive: boolean, isFrom: boolean, currentKey: number) { switch (currentKey) { - case RenderingState.Initial: + case SettingsState.Initial: return renderSettings(); - case RenderingState.Appearance: + case SettingsState.Appearance: return ( ); - case RenderingState.Assets: + case SettingsState.Assets: return ( ); - case RenderingState.Dapps: + case SettingsState.Dapps: return ( ); - case RenderingState.Language: - return ; - case RenderingState.About: - return ; - case RenderingState.Disclaimer: + case SettingsState.Language: + return ( + + ); + case SettingsState.About: + return ; + case SettingsState.Disclaimer: return ( ); + case SettingsState.NativeBiometricsTurnOn: + return ( + + ); + case SettingsState.SelectTokenList: + return ( + + ); } } @@ -465,56 +567,33 @@ function Settings({
{renderContent} + {IS_BIOMETRIC_AUTH_SUPPORTED && }
); } -export default memo(withGlobal((global): StateProps => { - const { - theme, - animationLevel, - areTinyTransfersHidden, - isTestnet, - isInvestorViewEnabled, - canPlaySounds, - langCode, - isTonMagicEnabled, - isTonProxyEnabled, - isDeeplinkHookEnabled, - areTokensWithNoBalanceHidden, - areTokensWithNoPriceHidden, - isSortByValueEnabled, - dapps, - } = global.settings; +export default memo(withGlobal((global): StateProps => { + const { authConfig } = global.settings; const { orderedSlugs } = selectAccountSettings(global, global.currentAccountId!) ?? {}; return { - theme, - animationLevel, - areTinyTransfersHidden, - isTestnet, - isInvestorViewEnabled, - canPlaySounds, - langCode, - isTonMagicEnabled, - isTonProxyEnabled, - isDeeplinkHookEnabled, - areTokensWithNoBalanceHidden, - areTokensWithNoPriceHidden, - isSortByValueEnabled, - dapps, + settings: global.settings, + isOpen: global.areSettingsOpen, tokens: selectCurrentAccountTokens(global), - popularTokens: selectPopularTokensWithoutAccountTokens(global), orderedSlugs, + isBiometricAuthEnabled: !!authConfig && authConfig.kind !== 'password', }; })(Settings)); diff --git a/src/components/settings/SettingsAbout.tsx b/src/components/settings/SettingsAbout.tsx index e940e074..6ec73202 100644 --- a/src/components/settings/SettingsAbout.tsx +++ b/src/components/settings/SettingsAbout.tsx @@ -4,6 +4,7 @@ import { APP_NAME, APP_VERSION, IS_EXTENSION } from '../../config'; import renderText from '../../global/helpers/renderText'; import buildClassName from '../../util/buildClassName'; +import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; import useScrolledState from '../../hooks/useScrolledState'; @@ -16,16 +17,22 @@ import styles from './Settings.module.scss'; import logoSrc from '../../assets/logo.svg'; interface OwnProps { + isActive?: boolean; handleBackClick: () => void; isInsideModal?: boolean; } -function SettingsAbout({ handleBackClick, isInsideModal }: OwnProps) { +function SettingsAbout({ isActive, handleBackClick, isInsideModal }: OwnProps) { const lang = useLang(); + useHistoryBack({ + isActive, + onBack: handleBackClick, + }); + const { handleScroll: handleContentScroll, - isAtBeginning: isContentNotScrolled, + isScrolled, } = useScrolledState(); return ( @@ -33,12 +40,12 @@ function SettingsAbout({ handleBackClick, isInsideModal }: OwnProps) { {isInsideModal ? ( ) : ( -
+
)} -
+

{lang('Theme')}

@@ -139,6 +166,28 @@ function SettingsAppearance({ checked={canPlaySounds} />
+ {IS_ELECTRON && IS_WINDOWS && ( +
+ {lang('Display Tray Icon')} + + +
+ )} + {IS_ELECTRON && ( +
+ {lang('Enable Auto-Updates')} + + +
+ )}
diff --git a/src/components/settings/SettingsAssets.tsx b/src/components/settings/SettingsAssets.tsx index b923230b..440c2e0a 100644 --- a/src/components/settings/SettingsAssets.tsx +++ b/src/components/settings/SettingsAssets.tsx @@ -1,13 +1,16 @@ -import React, { memo } from '../../lib/teact/teact'; +import React, { memo, useRef, useState } from '../../lib/teact/teact'; import { getActions } from '../../global'; +import type { ApiBaseCurrency } from '../../api/types'; import type { UserToken } from '../../global/types'; import { + DEFAULT_PRICE_CURRENCY, TINY_TRANSFER_MAX_COST, TON_SYMBOL, } from '../../config'; import buildClassName from '../../util/buildClassName'; +import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; import useScrolledState from '../../hooks/useScrolledState'; @@ -22,8 +25,8 @@ import SettingsTokens from './SettingsTokens'; import styles from './Settings.module.scss'; interface OwnProps { + isActive?: boolean; tokens?: UserToken[]; - popularTokens?: UserToken[]; orderedSlugs?: string[]; areTinyTransfersHidden?: boolean; isInvestorViewEnabled?: boolean; @@ -31,17 +34,40 @@ interface OwnProps { areTokensWithNoPriceHidden?: boolean; isSortByValueEnabled?: boolean; isInsideModal?: boolean; - handleBackClick: () => void; + handleBackClick: NoneToVoidFunction; + baseCurrency?: ApiBaseCurrency; } -const CURRENCY_OPTIONS = [{ - value: 'usd', - name: 'US Dollar', -}]; +const CURRENCY_OPTIONS = [ + { + value: 'USD', + name: 'US Dollar', + }, + { + value: 'EUR', + name: 'Euro', + }, + { + value: 'RUB', + name: 'Ruble', + }, + { + value: 'CNY', + name: 'Yuan', + }, + { + value: 'BTC', + name: 'Bitcoin', + }, + { + value: 'TON', + name: 'Toncoin', + }, +]; function SettingsAssets({ + isActive, tokens, - popularTokens, orderedSlugs, areTinyTransfersHidden, isInvestorViewEnabled, @@ -50,6 +76,7 @@ function SettingsAssets({ isSortByValueEnabled, handleBackClick, isInsideModal, + baseCurrency, }: OwnProps) { const { toggleTinyTransfersHidden, @@ -57,12 +84,21 @@ function SettingsAssets({ toggleTokensWithNoBalance, toggleTokensWithNoPrice, toggleSortByValue, + changeBaseCurrency, } = getActions(); const lang = useLang(); + // eslint-disable-next-line no-null/no-null + const scrollContainerRef = useRef(null); + + useHistoryBack({ + isActive, + onBack: handleBackClick, + }); + const { handleScroll: handleContentScroll, - isAtBeginning: isContentNotScrolled, + isScrolled, } = useScrolledState(); const handleTinyTransfersHiddenToggle = useLastCallback(() => { @@ -85,17 +121,26 @@ function SettingsAssets({ toggleSortByValue({ isEnabled: !isSortByValueEnabled }); }); + const [localBaseCurrency, setLocalBaseCurrency] = useState(baseCurrency); + + const handleBaseCurrencyChange = useLastCallback((currency: string) => { + if (currency !== baseCurrency) { + setLocalBaseCurrency(currency as ApiBaseCurrency); + changeBaseCurrency({ currency: currency as ApiBaseCurrency }); + } + }); + return (
{isInsideModal ? ( ) : ( -
+
)}
@@ -190,10 +237,11 @@ function SettingsAssets({
diff --git a/src/components/settings/SettingsDapps.tsx b/src/components/settings/SettingsDapps.tsx index e7a39e42..ca4abc55 100644 --- a/src/components/settings/SettingsDapps.tsx +++ b/src/components/settings/SettingsDapps.tsx @@ -7,6 +7,7 @@ import buildClassName from '../../util/buildClassName'; import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; import useFlag from '../../hooks/useFlag'; +import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; import useScrolledState from '../../hooks/useScrolledState'; @@ -16,6 +17,7 @@ import DisconnectDappModal from '../main/modals/DisconnectDappModal'; import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; import Button from '../ui/Button'; import ModalHeader from '../ui/ModalHeader'; +import Transition from '../ui/Transition'; import styles from './Settings.module.scss'; @@ -37,9 +39,14 @@ function SettingsDapps({ const [isDisconnectModalOpen, openDisconnectModal, closeDisconnectModal] = useFlag(); const [dappToDelete, setDappToDelete] = useState(); + useHistoryBack({ + isActive, + onBack: handleBackClick, + }); + const { handleScroll: handleContentScroll, - isAtBeginning: isContentNotScrolled, + isScrolled, } = useScrolledState(); const handleDisconnectDapp = useLastCallback((origin: string) => { @@ -104,7 +111,6 @@ function SettingsDapps({ tgsUrl={ANIMATED_STICKERS_PATHS.noData} previewUrl={ANIMATED_STICKERS_PATHS.noDataPreview} size={ANIMATED_STICKER_BIG_SIZE_PX} - className={styles.sticker} noLoop={false} nonInteractive /> @@ -122,12 +128,12 @@ function SettingsDapps({ {isInsideModal ? ( ) : ( -
+
)}
- {content} + + {content} +
diff --git a/src/components/settings/SettingsDisclaimer.tsx b/src/components/settings/SettingsDisclaimer.tsx index ae614185..7e0b6cda 100644 --- a/src/components/settings/SettingsDisclaimer.tsx +++ b/src/components/settings/SettingsDisclaimer.tsx @@ -5,6 +5,7 @@ import renderText from '../../global/helpers/renderText'; import buildClassName from '../../util/buildClassName'; import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; +import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; import useScrolledState from '../../hooks/useScrolledState'; @@ -23,9 +24,14 @@ interface OwnProps { function SettingsDisclaimer({ isActive, handleBackClick, isInsideModal }: OwnProps) { const lang = useLang(); + useHistoryBack({ + isActive, + onBack: handleBackClick, + }); + const { handleScroll: handleContentScroll, - isAtBeginning: isContentNotScrolled, + isScrolled, } = useScrolledState(); return ( @@ -33,11 +39,11 @@ function SettingsDisclaimer({ isActive, handleBackClick, isInsideModal }: OwnPro {isInsideModal ? ( ) : ( -
+
)} -
+
{renderLanguages()}
diff --git a/src/components/settings/SettingsModal.tsx b/src/components/settings/SettingsModal.tsx index e8197774..65367ae8 100644 --- a/src/components/settings/SettingsModal.tsx +++ b/src/components/settings/SettingsModal.tsx @@ -1,7 +1,8 @@ import React, { memo } from '../../lib/teact/teact'; -import { IS_ELECTRON, IS_EXTENSION } from '../../config'; +import { IS_EXTENSION } from '../../config'; import buildClassName from '../../util/buildClassName'; +import { IS_ELECTRON } from '../../util/windowEnvironment'; import Modal from '../ui/Modal'; @@ -23,9 +24,11 @@ function SettingsModal({ children, isOpen, onClose }: OwnProps) { {children} diff --git a/src/components/settings/SettingsTokenList.tsx b/src/components/settings/SettingsTokenList.tsx new file mode 100644 index 00000000..25ec48bd --- /dev/null +++ b/src/components/settings/SettingsTokenList.tsx @@ -0,0 +1,35 @@ +import React, { memo } from '../../lib/teact/teact'; + +import useHistoryBack from '../../hooks/useHistoryBack'; + +import TokenSelector from '../common/TokenSelector'; + +import styles from './Settings.module.scss'; + +interface OwnProps { + isActive?: boolean; + handleBackClick: NoneToVoidFunction; +} + +function SettingsTokenList({ + isActive, + handleBackClick, +}: OwnProps) { + useHistoryBack({ + isActive, + onBack: handleBackClick, + }); + + return ( +
+ +
+ ); +} + +export default memo(SettingsTokenList); diff --git a/src/components/settings/SettingsTokens.tsx b/src/components/settings/SettingsTokens.tsx index 1b28c1e4..8509144d 100644 --- a/src/components/settings/SettingsTokens.tsx +++ b/src/components/settings/SettingsTokens.tsx @@ -1,17 +1,18 @@ +import type { RefObject } from 'react'; import React, { memo, useEffect, useRef, useState, } from '../../lib/teact/teact'; import { getActions } from '../../global'; -import type { UserToken } from '../../global/types'; +import type { ApiBaseCurrency } from '../../api/types'; +import { SettingsState, type UserToken } from '../../global/types'; -import { DEFAULT_PRICE_CURRENCY, TON_TOKEN_SLUG } from '../../config'; +import { TON_TOKEN_SLUG } from '../../config'; import buildClassName from '../../util/buildClassName'; -import { formatCurrency } from '../../util/formatNumber'; +import { formatCurrency, getShortCurrencySymbol } from '../../util/formatNumber'; import { isBetween } from '../../util/math'; import { ASSET_LOGO_PATHS } from '../ui/helpers/assetLogos'; -import useFlag from '../../hooks/useFlag'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; @@ -19,7 +20,6 @@ import DeleteTokenModal from '../main/modals/DeleteTokenModal'; import AnimatedCounter from '../ui/AnimatedCounter'; import Draggable from '../ui/Draggable'; import Switcher from '../ui/Switcher'; -import SelectTokens from './SelectTokens'; import styles from './Settings.module.scss'; @@ -29,44 +29,43 @@ interface SortState { draggedIndex?: number; } -interface Position { - top: number; - right: number; -} - interface OwnProps { + parentContainer: RefObject; tokens?: UserToken[]; - popularTokens?: UserToken[]; orderedSlugs?: string[]; isSortByValueEnabled?: boolean; + baseCurrency?: ApiBaseCurrency; } const TOKEN_HEIGHT_PX = 64; const TOP_OFFSET = 48; function SettingsTokens({ - tokens, popularTokens, orderedSlugs, isSortByValueEnabled, + parentContainer, + tokens, + orderedSlugs, + isSortByValueEnabled, + baseCurrency, }: OwnProps) { const { + openSettingsWithState, sortTokens, - toggleDisabledToken, - addToken, + toggleExceptionToken, } = getActions(); const lang = useLang(); + const shortBaseSymbol = getShortCurrencySymbol(baseCurrency); // eslint-disable-next-line no-null/no-null const tokensRef = useRef(null); // eslint-disable-next-line no-null/no-null const sortableContainerRef = useRef(null); - const [isAddTokenModalOpen, openAddTokenModal, closeAddTokenModal] = useFlag(); const [tokenToDelete, setTokenToDelete] = useState(); const [state, setState] = useState({ orderedTokenSlugs: orderedSlugs, dragOrderTokenSlugs: orderedSlugs, draggedIndex: undefined, }); - const [sortableContainerPosition, setSortableContainerPosition] = useState(); useEffect(() => { if (!arraysAreEqual(orderedSlugs, state.orderedTokenSlugs)) { @@ -78,15 +77,8 @@ function SettingsTokens({ } }, [orderedSlugs, state.orderedTokenSlugs]); - const handleOpenAddTokenModal = useLastCallback(() => { - if (sortableContainerRef.current) { - const { top, right } = sortableContainerRef.current.getBoundingClientRect(); - setSortableContainerPosition({ - top, right, - }); - } - - openAddTokenModal(); + const handleOpenAddTokenPage = useLastCallback(() => { + openSettingsWithState({ state: SettingsState.SelectTokenList }); }); const handleDrag = useLastCallback((translation: { x: number; y: number }, id: string | number) => { @@ -120,10 +112,12 @@ function SettingsTokens({ }); }); - const handleDisabledToken = useLastCallback((slug: string) => { + const handleExceptionToken = useLastCallback((slug: string, e: React.MouseEvent | React.TouchEvent) => { if (slug === TON_TOKEN_SLUG) return; - toggleDisabledToken({ slug }); + e.preventDefault(); + e.stopPropagation(); + toggleExceptionToken({ slug }); }); const handleDeleteToken = useLastCallback((token: UserToken, e: React.MouseEvent) => { @@ -131,10 +125,6 @@ function SettingsTokens({ setTokenToDelete(token); }); - const handleTokenSelect = useLastCallback((token: UserToken) => { - addToken({ token }); - }); - function renderToken(token: UserToken, index: number) { const { symbol, image, name, amount, price, slug, isDisabled, @@ -145,8 +135,8 @@ function SettingsTokens({ const totalAmount = amount * price; const isDragged = state.draggedIndex === index; - const draggedTop = getOrderIndex(slug, state.orderedTokenSlugs); - const top = getOrderIndex(slug, state.dragOrderTokenSlugs); + const draggedTop = isSortByValueEnabled ? getOffsetByIndex(index) : getOffsetBySlug(slug, state.orderedTokenSlugs); + const top = isSortByValueEnabled ? getOffsetByIndex(index) : getOffsetBySlug(slug, state.dragOrderTokenSlugs); const style = `top: ${isDragged ? draggedTop : top}px;`; const knobStyle = 'left: 1rem;'; @@ -167,8 +157,9 @@ function SettingsTokens({ className={buildClassName(styles.item, styles.item_token)} offset={{ top: TOP_OFFSET }} parentRef={tokensRef} + scrollRef={parentContainer} // eslint-disable-next-line react/jsx-no-bind - onClick={() => handleDisabledToken(slug)} + onClick={(e) => handleExceptionToken(slug, e)} >
- + {isDeleteButtonVisible && ( @@ -196,8 +187,6 @@ function SettingsTokens({ className={styles.menuSwitcher} label={lang('Investor View')} checked={!isDisabled} - // eslint-disable-next-line react/jsx-no-bind - onCheck={() => handleDisabledToken(slug)} /> )} @@ -213,21 +202,13 @@ function SettingsTokens({ style={`height: ${(tokens?.length ?? 0) * TOKEN_HEIGHT_PX + TOP_OFFSET}px`} ref={tokensRef} > -
+
{lang('Add Token')}
{tokens?.map(renderToken)}
- -
@@ -239,9 +220,13 @@ function arraysAreEqual(arr1: T[] = [], arr2: T[] = []) { return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]); } -function getOrderIndex(slug: string, list: string[] = []) { +function getOffsetBySlug(slug: string, list: string[] = []) { const realIndex = list.indexOf(slug); const index = realIndex === -1 ? list.length : realIndex; + return getOffsetByIndex(index); +} + +function getOffsetByIndex(index: number) { return index * TOKEN_HEIGHT_PX + TOP_OFFSET; } diff --git a/src/components/settings/biometrics/Biometrics.module.scss b/src/components/settings/biometrics/Biometrics.module.scss new file mode 100644 index 00000000..e552357f --- /dev/null +++ b/src/components/settings/biometrics/Biometrics.module.scss @@ -0,0 +1,47 @@ +.modalDialog { + height: 37rem; + + @supports (height: env(safe-area-inset-bottom)) { + height: calc(37rem + env(safe-area-inset-bottom)); + } + + @media (min-width: 416.01px) { + max-width: 24rem; + } +} + +.sticker { + margin: 0 auto 1.25rem; +} + +.stickerHuge { + margin-top: 2rem; +} + +.step { + align-self: center; + + margin-bottom: 1rem; + padding: 0.75rem; + + font-size: 0.9375rem; + font-weight: 600; + color: var(--light-gray-1); + + background-color: var(--color-gray-button-background-light); + border-radius: var(--border-radius-buttons); +} + +.error { + align-self: center; + + padding: 0.375rem 0.5rem; + + font-size: 1.0625rem; + font-weight: 700; + line-height: 1; + color: var(--color-transaction-red-text); + + background-color: var(--color-transaction-red-background); + border-radius: var(--border-radius-tiny); +} diff --git a/src/components/settings/biometrics/Biometrics.tsx b/src/components/settings/biometrics/Biometrics.tsx new file mode 100644 index 00000000..30b21190 --- /dev/null +++ b/src/components/settings/biometrics/Biometrics.tsx @@ -0,0 +1,53 @@ +import React, { memo } from '../../../lib/teact/teact'; +import { getActions, withGlobal } from '../../../global'; + +import { BiometricsState } from '../../../global/types'; + +import BiometricsTurnOff from './TurnOff'; +import BiometricsTurnOffWarning from './TurnOffWarning'; +import BiometricsTurnOn from './TurnOn'; + +interface StateProps { + state: BiometricsState; + error?: string; +} + +function Biometrics({ state, error }: StateProps) { + const { closeBiometricSettings } = getActions(); + + const isTurnOnBiometricsOpened = state === BiometricsState.TurnOnPasswordConfirmation + || state === BiometricsState.TurnOnRegistration + || state === BiometricsState.TurnOnVerification + || state === BiometricsState.TurnOnComplete; + const isTurnOffBiometricsOpened = state === BiometricsState.TurnOffBiometricConfirmation + || state === BiometricsState.TurnOffCreatePassword + || state === BiometricsState.TurnOffComplete; + const isTurnOffBiometricsWarningOpened = state === BiometricsState.TurnOffWarning; + + return ( + <> + + + + + ); +} + +export default memo(withGlobal((global) => { + const { biometrics: { state, error } } = global; + + return { + state, + error, + }; +})(Biometrics)); diff --git a/src/components/settings/biometrics/NativeBiometricsToggle.tsx b/src/components/settings/biometrics/NativeBiometricsToggle.tsx new file mode 100644 index 00000000..97653245 --- /dev/null +++ b/src/components/settings/biometrics/NativeBiometricsToggle.tsx @@ -0,0 +1,123 @@ +import { Dialog } from '@capacitor/dialog'; +import React, { memo } from '../../../lib/teact/teact'; +import { getActions, withGlobal } from '../../../global'; + +import renderText from '../../../global/helpers/renderText'; +import { getIsFaceIdAvailable, getIsTouchIdAvailable } from '../../../util/capacitor'; +import { IS_DELEGATED_BOTTOM_SHEET } from '../../../util/windowEnvironment'; + +import useFlag from '../../../hooks/useFlag'; +import useLang from '../../../hooks/useLang'; +import useLastCallback from '../../../hooks/useLastCallback'; +import useSyncEffect from '../../../hooks/useSyncEffect'; + +import Button from '../../ui/Button'; +import Modal from '../../ui/Modal'; +import Switcher from '../../ui/Switcher'; + +import modalStyles from '../../ui/Modal.module.scss'; +import styles from '../Settings.module.scss'; + +import biometricsImg from '../../../assets/settings/settings_biometrics.svg'; + +interface OwnProps { + onEnable: NoneToVoidFunction; +} + +interface StateProps { + isBiometricAuthEnabled: boolean; +} + +function NativeBiometricsToggle({ isBiometricAuthEnabled, onEnable }: OwnProps & StateProps) { + const { disableNativeBiometrics } = getActions(); + const isFaceId = getIsFaceIdAvailable(); + const isTouchId = getIsTouchIdAvailable(); + + const lang = useLang(); + const [isWarningModalOpen, openWarningModal, closeWarningModal] = useFlag(); + const switcherTitle = isFaceId ? 'Face ID' : (isTouchId ? 'Touch ID' : lang('Biometric Authentication')); + const warningTitle = isFaceId + ? 'Turn Off Face ID?' + : (isTouchId ? 'Turn Off Touch ID?' : 'Turn Off Biometrics?'); + const warningDescription = isFaceId + ? 'Are you sure you want to disable Face ID?' + : (isTouchId ? 'Are you sure you want to disable Touch ID?' : 'Are you sure you want to disable biometrics?'); + + const handleConfirmDisableBiometrics = useLastCallback(() => { + closeWarningModal(); + disableNativeBiometrics(); + }); + + useSyncEffect(() => { + if (!IS_DELEGATED_BOTTOM_SHEET) return; + + if (isWarningModalOpen) { + Dialog.confirm({ + title: lang(warningTitle), + message: lang(warningDescription), + okButtonTitle: lang('Yes'), + }) + .then(({ value }) => { + if (value) { + handleConfirmDisableBiometrics(); + } + }) + .finally(closeWarningModal); + } + }, [handleConfirmDisableBiometrics, isWarningModalOpen, lang, warningDescription, warningTitle]); + + const handleBiometricAuthToggle = useLastCallback(() => { + if (isBiometricAuthEnabled) { + openWarningModal(); + } else { + onEnable(); + } + }); + + function renderDisableNativeBiometricsWarning() { + if (IS_DELEGATED_BOTTOM_SHEET) return undefined; + + return ( + +

+ {renderText(lang(warningDescription))} +

+ +
+ + +
+
+ ); + } + + return ( +
+
+ + {switcherTitle} + + +
+ {renderDisableNativeBiometricsWarning()} +
+ ); +} + +export default memo(withGlobal((global): StateProps => { + const { authConfig } = global.settings; + + return { + isBiometricAuthEnabled: !!authConfig && authConfig.kind === 'native-biometrics', + }; +})(NativeBiometricsToggle)); diff --git a/src/components/settings/biometrics/NativeBiometricsTurnOn.tsx b/src/components/settings/biometrics/NativeBiometricsTurnOn.tsx new file mode 100644 index 00000000..e17605af --- /dev/null +++ b/src/components/settings/biometrics/NativeBiometricsTurnOn.tsx @@ -0,0 +1,116 @@ +import React, { memo, useEffect, useState } from '../../../lib/teact/teact'; +import { getActions, withGlobal } from '../../../global'; + +import { PIN_LENGTH } from '../../../config'; +import buildClassName from '../../../util/buildClassName'; +import { ANIMATED_STICKERS_PATHS } from '../../ui/helpers/animatedAssets'; + +import useEffectWithPrevDeps from '../../../hooks/useEffectWithPrevDeps'; +import useHistoryBack from '../../../hooks/useHistoryBack'; +import useLang from '../../../hooks/useLang'; +import useLastCallback from '../../../hooks/useLastCallback'; + +import AnimatedIconWithPreview from '../../ui/AnimatedIconWithPreview'; +import Button from '../../ui/Button'; +import PinPad from '../../ui/PinPad'; + +import styles from '../Settings.module.scss'; + +interface OwnProps { + isActive?: boolean; + handleBackClick: NoneToVoidFunction; +} + +interface StateProps { + isPinPadPasswordAccepted?: boolean; + error?: string; + isNativeBiometricsEnabled?: boolean; +} + +function NativeBiometricsTurnOn({ + isActive, + isPinPadPasswordAccepted, + error, + isNativeBiometricsEnabled, + handleBackClick, +}: OwnProps & StateProps) { + const { enableNativeBiometrics, clearNativeBiometricsError } = getActions(); + + const lang = useLang(); + const [pin, setPin] = useState(''); + const pinPadType = pin.length !== PIN_LENGTH + ? undefined + : (isPinPadPasswordAccepted ? 'success' : (error ? 'error' : undefined)); + const pinTitle = isPinPadPasswordAccepted + ? 'Correct' + : (error && pin.length === PIN_LENGTH ? error : 'Enter code'); + + useEffect(() => { + if (!isActive) return; + + setPin(''); + }, [isActive]); + + useEffectWithPrevDeps(([prevIsEnabled]) => { + if (isNativeBiometricsEnabled && !prevIsEnabled) { + handleBackClick(); + } + }, [isNativeBiometricsEnabled, handleBackClick]); + + useHistoryBack({ + isActive, + onBack: handleBackClick, + }); + + const handleSubmit = useLastCallback((password: string) => { + enableNativeBiometrics({ password }); + }); + + return ( +
+
+ + + + +
+
+ ); +} + +export default memo(withGlobal((global): StateProps => { + const { + nativeBiometricsError, + isPinPadPasswordAccepted, + settings: { authConfig }, + } = global; + + return { + isPinPadPasswordAccepted, + error: nativeBiometricsError, + isNativeBiometricsEnabled: !!authConfig && authConfig.kind === 'native-biometrics', + }; +})(NativeBiometricsTurnOn)); diff --git a/src/components/settings/biometrics/TurnOff.tsx b/src/components/settings/biometrics/TurnOff.tsx new file mode 100644 index 00000000..f89e64e4 --- /dev/null +++ b/src/components/settings/biometrics/TurnOff.tsx @@ -0,0 +1,146 @@ +import React, { memo } from '../../../lib/teact/teact'; +import { getActions } from '../../../global'; + +import { BiometricsState } from '../../../global/types'; + +import { ANIMATED_STICKER_HUGE_SIZE_PX } from '../../../config'; +import buildClassName from '../../../util/buildClassName'; +import resolveModalTransitionName from '../../../util/resolveModalTransitionName'; +import { ANIMATED_STICKERS_PATHS } from '../../ui/helpers/animatedAssets'; + +import useLang from '../../../hooks/useLang'; +import useLastCallback from '../../../hooks/useLastCallback'; +import useModalTransitionKeys from '../../../hooks/useModalTransitionKeys'; + +import AnimatedIconWithPreview from '../../ui/AnimatedIconWithPreview'; +import Button from '../../ui/Button'; +import CreatePasswordForm from '../../ui/CreatePasswordForm'; +import Modal from '../../ui/Modal'; +import ModalHeader from '../../ui/ModalHeader'; +import Transition from '../../ui/Transition'; + +import modalStyles from '../../ui/Modal.module.scss'; +import styles from './Biometrics.module.scss'; + +interface OwnProps { + isOpen: boolean; + state: BiometricsState; + error?: string; + onClose: NoneToVoidFunction; +} + +const STICKER_SIZE = 180; + +function TurnOff({ + isOpen, state, error, onClose, +}: OwnProps) { + const { disableBiometrics } = getActions(); + + const lang = useLang(); + const { renderingKey, nextKey, updateNextKey } = useModalTransitionKeys(state, isOpen); + + const handleSubmit = useLastCallback((password: string, isPasswordNumeric?: boolean) => { + disableBiometrics({ password, isPasswordNumeric }); + }); + + // eslint-disable-next-line consistent-return + function renderContent(isActive: boolean, isFrom: boolean, currentKey: number) { + switch (currentKey) { + case BiometricsState.TurnOffBiometricConfirmation: + return ( + <> + +
+ +
{lang('Please verify your identity.')}
+ {error &&
{lang(error)}
} + +
+ +
+
+ + ); + + case BiometricsState.TurnOffCreatePassword: + return ( + <> + +
+ + +
+ + ); + + case BiometricsState.TurnOffComplete: + return ( + <> + +
+ + +
+ +
+
+ + ); + } + } + + return ( + + + {renderContent} + + + ); +} + +export default memo(TurnOff); diff --git a/src/components/settings/biometrics/TurnOffWarning.tsx b/src/components/settings/biometrics/TurnOffWarning.tsx new file mode 100644 index 00000000..a4011013 --- /dev/null +++ b/src/components/settings/biometrics/TurnOffWarning.tsx @@ -0,0 +1,46 @@ +import React, { memo } from '../../../lib/teact/teact'; +import { getActions } from '../../../global'; + +import buildClassName from '../../../util/buildClassName'; + +import useLang from '../../../hooks/useLang'; + +import Button from '../../ui/Button'; +import Modal from '../../ui/Modal'; + +import modalStyles from '../../ui/Modal.module.scss'; + +interface OwnProps { + isOpen: boolean; + onClose: NoneToVoidFunction; +} + +function TurnOffWaning({ isOpen, onClose }: OwnProps) { + const { openBiometricsTurnOff } = getActions(); + + const lang = useLang(); + + return ( + +

+ {lang('If you turn off biometric protection, you will need to create a password.')} +

+ +
+ + +
+
+ ); +} + +export default memo(TurnOffWaning); diff --git a/src/components/settings/biometrics/TurnOn.tsx b/src/components/settings/biometrics/TurnOn.tsx new file mode 100644 index 00000000..b699448b --- /dev/null +++ b/src/components/settings/biometrics/TurnOn.tsx @@ -0,0 +1,189 @@ +import React, { memo, useEffect, useState } from '../../../lib/teact/teact'; +import { getActions } from '../../../global'; + +import { BiometricsState } from '../../../global/types'; + +import { ANIMATED_STICKER_HUGE_SIZE_PX } from '../../../config'; +import buildClassName from '../../../util/buildClassName'; +import resolveModalTransitionName from '../../../util/resolveModalTransitionName'; +import { IS_ELECTRON } from '../../../util/windowEnvironment'; +import { ANIMATED_STICKERS_PATHS } from '../../ui/helpers/animatedAssets'; + +import useLang from '../../../hooks/useLang'; +import useLastCallback from '../../../hooks/useLastCallback'; +import useModalTransitionKeys from '../../../hooks/useModalTransitionKeys'; + +import AnimatedIconWithPreview from '../../ui/AnimatedIconWithPreview'; +import Button from '../../ui/Button'; +import Modal from '../../ui/Modal'; +import ModalHeader from '../../ui/ModalHeader'; +import PasswordForm from '../../ui/PasswordForm'; +import Transition from '../../ui/Transition'; + +import modalStyles from '../../ui/Modal.module.scss'; +import styles from './Biometrics.module.scss'; + +interface OwnProps { + isOpen: boolean; + state: BiometricsState; + error?: string; + onClose: NoneToVoidFunction; +} + +const STICKER_SIZE = 180; + +function TurnOn({ + isOpen, state, error, onClose, +}: OwnProps) { + const { enableBiometrics } = getActions(); + + const lang = useLang(); + const [localError, setLocalError] = useState(); + const { renderingKey, nextKey, updateNextKey } = useModalTransitionKeys(state, isOpen); + const shouldDisablePasswordForm = Boolean(state !== BiometricsState.TurnOnPasswordConfirmation); + + useEffect(() => { + if (isOpen) { + setLocalError(''); + } + }, [isOpen]); + + const handleClearError = useLastCallback(() => { + setLocalError(undefined); + }); + + const handleSubmit = useLastCallback((password: string) => { + if (shouldDisablePasswordForm) { + return; + } + + try { + enableBiometrics({ password }); + } catch (err: any) { + setLocalError(err.message || 'Unknown error'); + } + }); + + // eslint-disable-next-line consistent-return + function renderContent(isActive: boolean, isFrom: boolean, currentKey: number) { + switch (currentKey) { + case BiometricsState.TurnOnPasswordConfirmation: + return ( + <> + + + + ); + + case BiometricsState.TurnOnRegistration: + return ( + <> + +
+ + +
{lang('Step 1 of 2. Registration')}
+ +
+ +
+
+ + ); + + case BiometricsState.TurnOnVerification: + return ( + <> + +
+ + +
+ {lang(IS_ELECTRON ? 'Verification' : 'Step 2 of 2. Verification')} +
+ +
+ +
+
+ + ); + + case BiometricsState.TurnOnComplete: + return ( + <> + +
+ + +
+ +
+
+ + ); + } + } + + return ( + + + {renderContent} + + + ); +} + +export default memo(TurnOn); diff --git a/src/components/staking/StakeModal.tsx b/src/components/staking/StakeModal.tsx index 586922e2..5f1ad252 100644 --- a/src/components/staking/StakeModal.tsx +++ b/src/components/staking/StakeModal.tsx @@ -1,17 +1,19 @@ -import React, { memo, useMemo } from '../../lib/teact/teact'; +import React, { memo, useMemo, useState } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; import type { GlobalState, UserToken } from '../../global/types'; import { StakingState } from '../../global/types'; -import { TON_TOKEN_SLUG } from '../../config'; +import { IS_CAPACITOR, TON_TOKEN_SLUG } from '../../config'; import { selectCurrentAccountTokens } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; +import { formatCurrency } from '../../util/formatNumber'; +import resolveModalTransitionName from '../../util/resolveModalTransitionName'; +import { ASSET_LOGO_PATHS } from '../ui/helpers/assetLogos'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; import useModalTransitionKeys from '../../hooks/useModalTransitionKeys'; -import usePrevious from '../../hooks/usePrevious'; import TransferResult from '../common/TransferResult'; import Button from '../ui/Button'; @@ -57,18 +59,19 @@ function StakeModal({ const lang = useLang(); const isOpen = IS_OPEN_STATES.has(state); const tonToken = useMemo(() => tokens?.find(({ slug }) => slug === TON_TOKEN_SLUG), [tokens]); - const renderedTokenBalance = usePrevious(tonToken?.amount, true); - const renderedStakingAmount = usePrevious(amount, true); + const [renderedStakingAmount, setRenderedStakingAmount] = useState(amount); const { renderingKey, nextKey, updateNextKey } = useModalTransitionKeys(state, isOpen); const handleBackClick = useLastCallback(() => { if (state === StakingState.StakePassword) { + clearStakingError(); setStakingScreen({ state: StakingState.StakeInitial }); } }); const handleTransferSubmit = useLastCallback((password: string) => { + setRenderedStakingAmount(amount); submitStakingPassword({ password }); }); @@ -77,21 +80,38 @@ function StakeModal({ cancelStaking(); }); + function renderStakingShortInfo() { + if (!tonToken || !amount) return undefined; + + const logoPath = tonToken.image || ASSET_LOGO_PATHS[tonToken.symbol.toLowerCase() as keyof typeof ASSET_LOGO_PATHS]; + + return ( +
+ {tonToken.symbol} + {formatCurrency(amount, tonToken.symbol)} +
+ ); + } + function renderPassword(isActive: boolean) { return ( <> - + {!IS_CAPACITOR && } + > + {IS_CAPACITOR && renderStakingShortInfo()} + ); } @@ -107,7 +127,7 @@ function StakeModal({ playAnimation={isActive} amount={renderedStakingAmount} noSign - balance={renderedTokenBalance} + balance={tonToken?.amount ?? 0} operationAmount={amount ? -amount : undefined} firstButtonText={lang('View')} secondButtonText={lang('Stake More')} @@ -144,15 +164,17 @@ function StakeModal({ return ( { if (isActive) { - fetchBackendStakingState(); + fetchStakingHistory(); } - }, [fetchBackendStakingState, isActive]); + }, [fetchStakingHistory, isActive]); const handleStakeClick = useLastCallback(() => { onClose?.(); @@ -110,7 +112,7 @@ function StakingInfoContent({ {lang('$total', { value: ( - {formatCurrency(stakingHistory!.totalProfit, TON_SYMBOL)} + {formatCurrency(totalProfit, TON_SYMBOL)} ), })} @@ -123,7 +125,7 @@ function StakingInfoContent({ !isStatic && height >= HISTORY_SCROLL_APPEARANCE_HEIGHT_PX && 'custom-scroll', )} > - {stakingHistory?.profitHistory.map((record) => ( + {stakingHistory?.map((record) => ( ((global): StateProps => { const accountState = selectCurrentAccountState(global); return { - amount: accountState?.stakingBalance || 0, - apyValue: accountState?.poolState?.lastApy || 0, + amount: accountState?.staking?.balance || 0, + apyValue: accountState?.staking?.apy || 0, + totalProfit: accountState?.staking?.totalProfit ?? 0, stakingHistory: accountState?.stakingHistory, tokens: selectCurrentAccountTokens(global), - isUnstakeRequested: accountState?.isUnstakeRequested, - endOfStakingCycle: accountState?.poolState?.endOfCycle, + isUnstakeRequested: accountState?.staking?.isUnstakeRequested, + endOfStakingCycle: accountState?.staking?.end, }; })(StakingInfoContent)); diff --git a/src/components/staking/StakingInfoModal.tsx b/src/components/staking/StakingInfoModal.tsx index 789411e3..7910cd00 100644 --- a/src/components/staking/StakingInfoModal.tsx +++ b/src/components/staking/StakingInfoModal.tsx @@ -28,7 +28,7 @@ function StakingInfoModal({ isUnstakeRequested, onClose, }: OwnProps & StateProps) { - const { fetchBackendStakingState } = getActions(); + const { fetchStakingHistory } = getActions(); const forceUpdate = useForceUpdate(); @@ -36,15 +36,16 @@ function StakingInfoModal({ useEffect(() => { if (isOpen) { - fetchBackendStakingState(); + fetchStakingHistory(); } - }, [fetchBackendStakingState, isOpen]); + }, [fetchStakingHistory, isOpen]); return ( @@ -55,6 +56,6 @@ export default memo(withGlobal((global): StateProps => { const accountState = selectCurrentAccountState(global); return { - isUnstakeRequested: accountState?.isUnstakeRequested, + isUnstakeRequested: accountState?.staking?.isUnstakeRequested, }; })(StakingInfoModal)); diff --git a/src/components/staking/StakingInitial.tsx b/src/components/staking/StakingInitial.tsx index 78ca8fb4..5e187945 100644 --- a/src/components/staking/StakingInitial.tsx +++ b/src/components/staking/StakingInitial.tsx @@ -1,3 +1,4 @@ +import { Dialog } from '@capacitor/dialog'; import React, { memo, useEffect, useMemo, useState, } from '../../lib/teact/teact'; @@ -8,22 +9,28 @@ import type { UserToken } from '../../global/types'; import { ANIMATED_STICKER_MIDDLE_SIZE_PX, ANIMATED_STICKER_SMALL_SIZE_PX, + DEFAULT_DECIMAL_PLACES, + DEFAULT_FEE, MIN_BALANCE_FOR_UNSTAKE, + STAKING_FORWARD_AMOUNT, + STAKING_MIN_AMOUNT, TON_TOKEN_SLUG, } from '../../config'; import { bigStrToHuman } from '../../global/helpers'; import renderText from '../../global/helpers/renderText'; import { selectCurrentAccountState, selectCurrentAccountTokens } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; -import { formatCurrency, formatCurrencyExtended } from '../../util/formatNumber'; +import { formatCurrency, formatCurrencySimple } from '../../util/formatNumber'; import { floor } from '../../util/round'; import { throttle } from '../../util/schedulers'; +import { IS_DELEGATED_BOTTOM_SHEET } from '../../util/windowEnvironment'; import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; import { ASSET_LOGO_PATHS } from '../ui/helpers/assetLogos'; import useFlag from '../../hooks/useFlag'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; +import useSyncEffect from '../../hooks/useSyncEffect'; import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; import Button from '../ui/Button'; @@ -46,7 +53,6 @@ interface StateProps { tokens?: UserToken[]; fee?: string; stakingBalance: number; - stakingMinAmount: number; apyValue: number; } @@ -64,7 +70,6 @@ function StakingInitial({ tokens, fee, stakingBalance, - stakingMinAmount, apyValue, }: OwnProps & StateProps) { const { submitStakingInitial, fetchStakingFee } = getActions(); @@ -78,9 +83,11 @@ function StakingInitial({ const [shouldUseAllBalance, setShouldUseAllBalance] = useState(false); const { - amount: balance, decimals, symbol, + amount: balance, symbol, } = useMemo(() => tokens?.find(({ slug }) => slug === TON_TOKEN_SLUG), [tokens]) || {}; const hasAmountError = Boolean(isInsufficientBalance || apiError); + const calculatedFee = fee ? bigStrToHuman(fee) * RESERVED_FEE_FACTOR : DEFAULT_FEE; + const decimals = DEFAULT_DECIMAL_PLACES; const validateAndSetAmount = useLastCallback((newAmount: number | undefined, noReset = false) => { if (!noReset) { @@ -99,9 +106,9 @@ function StakingInitial({ return; } - if (!balance || newAmount > balance) { + if (!balance || newAmount + STAKING_MIN_AMOUNT + calculatedFee > balance) { setIsInsufficientBalance(true); - } else if (balance + stakingBalance < stakingMinAmount) { + } else if (balance + stakingBalance < STAKING_MIN_AMOUNT) { setIsNotEnough(true); } @@ -110,14 +117,13 @@ function StakingInitial({ useEffect(() => { if (shouldUseAllBalance && balance) { - const calculatedFee = fee && shouldUseAllBalance ? bigStrToHuman(fee, decimals) : 0; - const newAmount = balance - calculatedFee * RESERVED_FEE_FACTOR; + const newAmount = balance - STAKING_FORWARD_AMOUNT - calculatedFee; validateAndSetAmount(newAmount, true); } else { validateAndSetAmount(amount, true); } - }, [amount, balance, decimals, fee, shouldUseAllBalance, validateAndSetAmount]); + }, [amount, balance, fee, shouldUseAllBalance, validateAndSetAmount, calculatedFee]); useEffect(() => { if (!amount) { @@ -131,8 +137,24 @@ function StakingInitial({ }); }, [amount, fetchStakingFee]); + useSyncEffect(() => { + if (!IS_DELEGATED_BOTTOM_SHEET) return; + + if (isStakingInfoModalOpen) { + Dialog.alert({ + title: lang('Why staking is safe?'), + message: [ + `1. ${lang('$safe_staking_description1')}`, + `2. ${lang('$safe_staking_description2')}`, + `3. ${lang('$safe_staking_description3')}`, + ].join('\n\n').replace(/\*\*/g, ''), + }) + .then(closeStakingInfoModal); + } + }, [isStakingInfoModalOpen, lang]); + const handleAmountBlur = useLastCallback(() => { - if (amount && amount + stakingBalance < stakingMinAmount) { + if (amount && amount + stakingBalance < STAKING_MIN_AMOUNT) { setIsNotEnough(true); } }); @@ -157,11 +179,12 @@ function StakingInitial({ } validateAndSetAmount(amount - MIN_BALANCE_FOR_UNSTAKE); + setShouldUseAllBalance(false); }); const canSubmit = amount && balance && !isNotEnough && amount <= balance - && (amount + stakingBalance >= stakingMinAmount); + && (amount + stakingBalance >= STAKING_MIN_AMOUNT); const handleSubmit = useLastCallback((e) => { e.preventDefault(); @@ -187,16 +210,18 @@ function StakingInitial({ } const isFullBalanceSelected = balance && amount - && (balance >= amount && balance - amount <= MIN_BALANCE_FOR_UNSTAKE); - const balanceLink = lang('$balance_is', { + && (balance >= amount && Number((balance - amount).toFixed(2)) < MIN_BALANCE_FOR_UNSTAKE); // TODO $decimals + + const balanceLink = lang('$max_balance', { balance: ( {balance !== undefined - ? formatCurrencyExtended(floor(balance, STAKING_DECIMAL), symbol, true) + ? formatCurrencySimple(balance, symbol, decimals) : lang('Loading...')} ), }); + const minusOneLink = (
{formatCurrency(-Math.round(MIN_BALANCE_FOR_UNSTAKE), symbol)} @@ -231,7 +256,7 @@ function StakingInitial({ lang('$min_value', { value: ( - {formatCurrency(stakingMinAmount, 'TON')} + {formatCurrency(STAKING_MIN_AMOUNT, 'TON')} ), }) @@ -241,6 +266,8 @@ function StakingInitial({ } function renderStakingSafeModal() { + if (IS_DELEGATED_BOTTOM_SHEET) return undefined; + return ( +
@@ -157,12 +190,13 @@ function UnstakeModal({ )}
- + @@ -175,18 +209,21 @@ function UnstakeModal({ function renderPassword(isActive: boolean) { return ( <> - + {!IS_CAPACITOR && } + > + {IS_CAPACITOR && renderUnstakingShortInfo()} + ); } @@ -194,7 +231,10 @@ function UnstakeModal({ function renderComplete(isActive: boolean) { return ( <> - +
-
- - {lang('$unstaking_when_receive', { - time: {formatRelativeHumanDateTime(lang.code, unstakeDate)}, - })} -
+ {renderedIsInstantUnstake && ( +
+ + {lang('$unstaking_when_receive', { + time: {formatRelativeHumanDateTime(lang.code, unstakeDate)}, + })} +
+ )}
@@ -237,15 +279,17 @@ function UnstakeModal({ return ( { return { ...global.staking, tokens, - stakingBalance: currentAccountState?.stakingBalance, - endOfStakingCycle: currentAccountState?.poolState?.endOfCycle, + stakingType: currentAccountState?.staking?.type, + stakingBalance: currentAccountState?.staking?.balance, + endOfStakingCycle: currentAccountState?.staking?.end, + stakingInfo: global.stakingInfo, }; })(UnstakeModal)); diff --git a/src/components/swap/Swap.module.scss b/src/components/swap/Swap.module.scss new file mode 100644 index 00000000..b4a3d936 --- /dev/null +++ b/src/components/swap/Swap.module.scss @@ -0,0 +1,795 @@ +@import "../../styles/mixins"; + +.modalDialog { + overflow: hidden; + + height: 35rem; + + @supports (height: env(safe-area-inset-bottom)) { + height: calc(35rem + env(safe-area-inset-bottom)); + } +} + +.scrollContent { + overflow-x: hidden; + overflow-y: scroll; + display: flex; + flex-direction: column; + + height: 100%; + min-height: 0; + padding: 0 1rem 1rem; + + @include adapt-padding-to-scrollbar(1rem); + + @supports (padding-bottom: env(safe-area-inset-bottom)) { + padding-bottom: max(env(safe-area-inset-bottom), 1rem); + } +} + +.amountInput { + margin-bottom: 0.8125rem; +} + +.inputLabel { + margin-bottom: 0.3125rem; +} + +.amountInputInner { + padding-right: 0.5rem; +} + +.balanceContainer, +.priceContainer, +.advancedSlippageContainer { + position: relative; + z-index: 1; + + width: 100%; +} + +.tokenPrice, +.balance { + position: absolute; + top: -0.125rem; + right: 0.5rem; + + display: flex; + gap: 0.25rem; + + font-size: 0.8125rem; + color: var(--color-gray-1); +} + +.tokenPrice { + font-weight: 400; +} + +.tokenPriceBold { + font-weight: 600; +} + +.balanceLink { + cursor: var(--custom-cursor, pointer); + + font-weight: 600; + color: var(--color-blue); + text-decoration: underline; + text-decoration-style: dotted; + + @media (hover: hover) { + &:hover, + &:focus { + text-decoration: none; + } + } +} + +.content { + position: relative; + + display: flex; + flex-direction: column; +} + +.inputContainer { + display: flex; + flex-direction: column; +} + +.slippageLabel { + display: flex; + align-items: center; +} + +.tokenSelectorWrapper { + align-self: center; + + width: auto; + height: 3rem; +} + +.tokenSelectorSlide { + display: flex; + justify-content: flex-end; +} + +.tokenSelector { + cursor: var(--custom-cursor, pointer); + + display: flex; + gap: 0.375rem; + align-items: center; + align-self: center; + justify-content: center; + + height: 3rem; + padding: 0.75rem !important; + + color: var(--color-input-button-text); + + background-color: var(--color-input-button-background); + border: none; + border-radius: var(--border-radius-small); + outline: none; + + @media (hover: hover) { + &:hover { + background-color: var(--color-input-button-background-hover); + } + } + + @media (pointer: coarse) { + &:active { + background-color: var(--color-input-button-background-hover); + } + } +} + +.tokenSelectorIcon { + font-size: 1rem; + color: var(--color-gray-3); +} + +.tokenIcon { + width: 1.5rem; + height: 1.5rem; + + border-radius: 50%; +} + +.tokenIconWrapper { + position: relative; +} + +.tokenBlockchainIcon { + position: absolute; + z-index: 1; + top: 0.875rem; + right: -0.25rem; + + width: 0.75rem; + height: 0.75rem; + + border-radius: 50%; + + /* stylelint-disable-next-line plugin/whole-pixel */ + box-shadow: 0 0 0 0.0938rem var(--color-input-button-background), + inset 0 0 0 0.125rem var(--color-input-button-background); +} + +.tokenContent { + display: flex; + align-items: center; + + font-size: 0.9375rem; + font-weight: 700; +} + +.swapButtonWrapper { + cursor: var(--custom-cursor, pointer); + + position: absolute; + z-index: 1; + top: 50%; + left: 50%; + transform: translate(-50%, -45%); + + display: flex; + align-items: center; + justify-content: center; + + width: 2.75rem; + height: 2.75rem; + + background-color: var(--color-background-second); + border-radius: 50%; +} + +.swapButtonWrapperStatic { + background-color: var(--color-background-first); +} + +.swapButton { + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + + width: 2.25rem; + height: 2.25rem; + + font-size: 0.875rem; + color: var(--color-blue-button-text); + + background-color: var(--color-blue-button-background); + border-radius: 50%; + + transition: background-color 150ms; + + @media (hover: hover) { + &:hover { + background-color: var(--color-blue-button-background-hover); + } + } +} + +.feeWrapper { + display: flex; + justify-content: center; + + height: 1rem; +} + +.feeContent { + display: flex; + gap: 0.25rem; + align-items: center; + justify-content: center; +} + +.feeContentClickable { + cursor: var(--custom-cursor, pointer); +} + +.feeText { + font-size: 0.8125rem; + font-weight: 600; + color: var(--color-gray-2); +} + +.feeIcon { + font-size: 0.75rem; + color: var(--color-gray-4); +} + +.footerBlock { + display: flex; + flex-direction: column; + gap: 1rem; + align-items: center; + + margin-top: auto; +} + +.footerBlockStatic { + margin-top: 2rem; +} + +.dot { + width: 0.125rem; + height: 0.125rem; + margin: 0 0.25rem; + + font-style: normal; + line-height: 1rem; + + background-color: var(--color-blue); + border-radius: 50%; +} + +.advancedTitle { + display: flex; + justify-content: center; + + margin: 1rem 0 1.5rem; + + font-size: 1.0625rem; + font-weight: 700; + color: var(--color-black); +} + +.advancedBlock { + display: flex; + flex-direction: column; + gap: 1rem; + + margin-bottom: 1.25rem; + padding: 1rem; + + background-color: var(--color-background-first); + border-radius: var(--border-radius-default); +} + +.advancedRow { + display: flex; + justify-content: space-between; +} + +.advancedDescription { + display: flex; + align-items: center; + + font-size: 0.8125rem; + font-weight: 600; + color: var(--color-gray-1); +} + +.advancedTooltip { + cursor: var(--custom-cursor, pointer); + + margin-left: 0.25rem; + + color: var(--color-gray-4); + + transition: color 150ms; +} + +.advancedTooltipContainer { + z-index: 1; + + width: 18.9375rem; +} + +.advancedTooltipMessage { + display: flex; + flex-direction: column; + gap: 1rem; + + font-size: 0.75rem; +} + +.advancedValue { + font-size: 0.8125rem; + font-weight: 700; + color: var(--color-black); +} + +.advancedSlippageError { + position: absolute; + bottom: -0.25rem; + + display: flex; + justify-content: space-between; + + width: 100%; + min-height: 1rem; + padding: 0 0.5rem; + + font-size: 0.75rem; + line-height: 1rem; + color: var(--color-red); +} + +.advancedSlippage { + position: absolute; + top: 0; + right: 0.5rem; + + display: flex; + align-items: center; + + font-size: 0.8125rem; + font-weight: 600; + line-height: 0.8125rem; + color: var(--color-blue); +} + +.advancedInput { + position: relative; + + margin-bottom: 0.5rem; +} + +.advancedInputValue { + font-size: 1rem; + + &.isEmpty::before { + content: "0"; + + font-size: 1rem; + } +} + +.advancedError { + color: var(--color-red); +} + +.footerButtonWrapper { + height: 2.75rem; +} + +.footerButton { + width: 100%; + max-width: 100% !important; +} + +.priceImpact { + cursor: var(--custom-cursor, pointer); + + display: flex; + gap: 1rem; + align-items: center; + + padding: 1rem; + + background-color: var(--color-background-first); + border-radius: var(--border-radius-default); +} + +.priceImpactStatic { + padding: 0; +} + +.priceImpactSticker { + width: 4.3125rem; + height: 4.3125rem; +} + +.priceImpactContent { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.priceImpactTitle { + position: relative; + + font-size: 0.9375rem; + font-weight: 700; + color: var(--color-red); + + @media (hover: hover) { + &:hover { + color: var(--color-red-button-background-hover) + } + } +} + +.priceImpactArrow { + position: absolute; + bottom: 0.0625rem; + + font-size: 0.75rem; +} + +.priceImpactDescription { + font-size: 0.9375rem; + font-weight: 500; + color: var(--color-gray-1); +} + +.swapCornerTop, +.swapCornerBottom { + position: absolute; + bottom: 0; + left: 50%; + transform: translate(-50%, 0); + + overflow: hidden; + + width: 3rem; + height: 0.6875rem; + + &::after, + &::before { + content: ""; + + position: absolute; + top: 1.8125rem; + left: 50%; + transform: translate(-50%, -50%); + + width: 3rem; + height: 3rem; + + background: var(--color-background-first); + border-radius: 50% 50% 0 0; + } + + &::before { + transition: box-shadow 150ms; + + :global(html.animation-level-0) & { + transition: none !important; + } + } +} + +.swapCornerStaticTop, +.swapCornerStaticBottom { + &::before { + box-shadow: 0 0 0 0.0625rem var(--color-separator-input-stroke); + } +} + +.swapCornerBottom, +.swapCornerStaticBottom { + top: 1.125rem; + transform: rotate(180deg) translate(50%, 0); +} + +.swapCornerTop { + &::before { + top: 1.75rem + } +} + +.swapCornerStaticTop { + bottom: 0; + + height: 0.75rem; + + &::before { + top: 1.8125rem + } +} + +.selectBlockchainBlock { + align-items: center; +} + +.inputAddressWrapper { + margin: 0; +} + +.inputAddress { + width: 100%; + margin-top: 1.5rem; +} + +.blockchainHintWrapper { + display: flex; + justify-content: center; + + margin-top: 1rem; + padding: 0 2rem; +} + +.blockchainHintText { + font-size: 0.8125rem; + font-weight: 500; + color: var(--color-gray-2); + text-align: center; +} + +.blockchainHintTextError { + font-size: 0.9375rem; + color: var(--color-red); +} + +.inputButton { + position: absolute; + top: 0.5rem; + right: 0.5rem; + + display: flex; + align-items: center; + justify-content: center; + + width: 2rem; + height: 2rem; + + font-size: 1.25rem; + color: var(--color-gray-3); + + background-color: var(--color-background-first); + border-radius: var(--border-radius-small) !important; + + @media (hover: hover) { + &:hover, + &:focus { + color: var(--color-input-button-text); + + background-color: var(--color-input-button-background); + } + } + + + @media (pointer: coarse) { + &:active { + color: var(--color-input-button-text); + + background-color: var(--color-input-button-background); + // Optimization + transition: none; + } + } +} + +.changellyInfo { + display: flex; + gap: 1rem; + align-items: center; + + padding: 1rem; + + background-color: var(--color-background-first); + border-radius: var(--border-radius-default); +} + +.changellyInfoStatic { + padding: 0; +} + +.changellyInfoContent { + display: flex; + flex-direction: column; + gap: 0.875rem; +} + +.changellyInfoTitle { + display: flex; + gap: 0.25rem; + align-items: center; + justify-content: center; + + font-size: 0.8125rem; + font-weight: 700; + line-height: 0.8125rem; + text-align: center; +} +.changellyInfoDescription { + font-size: 0.75rem; + font-weight: 500; + line-height: 0.9375rem; + text-align: center; +} + +.changellyIcon { + font-size: 1rem; + color: var(--color-green); +} + +.blockchainButtons { + padding: 0; +} + +.changellyInfoBlock { + display: flex; + flex-direction: column; + gap: 1rem; + align-items: center; + + margin-top: 2rem; +} + +.changellyDescription { + font-size: 0.8125rem; + font-weight: 500; + color: var(--color-gray-2); + text-align: center; +} + +.changellyTextField { + width: 100%; +} + +.changellyImportantRed { + font-size: 1.0625rem; + font-weight: 700; + color: var(--color-red); + text-align: center; +} + +.changellyDescriptionBold { + font-weight: 700; +} + +.arrowContainer, +.arrowContainerInverted { + position: relative; +} + +.arrowContainerInverted { + transform: rotate(180deg); +} + +$translate: 1.75em; +$animation-time: 350ms; + +@keyframes arrow-disappear { + from { + transform: none; + + opacity: 1; + } + + to { + transform: translateY(-$translate); + + opacity: 0; + } +} + +@keyframes arrow-appear { + from { + transform: translateY($translate); + + opacity: 0; + } + + to { + transform: none; + + opacity: 1; + } +} + +.arrow { + visibility: hidden; +} + +.arrowNew, +.arrowOld { + position: absolute; + top: 0.0625rem; + left: 0; +} + +.animateDisappear { + animation: $animation-time ease-in-out arrow-disappear forwards; +} + +.animateAppear { + animation: $animation-time ease-in-out arrow-appear forwards; +} + +.swapShortInfo { + display: flex; + gap: 0.25rem; + align-items: center; + + max-width: calc(100% - 2rem); + height: 2rem; + padding: 0 0.375rem; + + font-size: 0.9375rem; + font-weight: 500; + color: var(--color-activity-blue); + + background-color: var(--color-activity-blue-background); + border-radius: 1rem; +} + +.swapShortAmount { + font-weight: 700; +} + +.swapShortValue { + overflow: hidden; + + text-overflow: ellipsis; + white-space: nowrap; +} + +.swapShortInfoTokenIcon { + width: 1.25rem; + height: 1.25rem; + + border-radius: 50%; +} + +.swapShortInfoBlockchainIcon { + position: absolute; + z-index: 1; + top: 0.75rem; + left: 0.75rem; + + width: 0.5625rem; + height: 0.5625rem; + + border-radius: 50%; + box-shadow: 0 0 0 0.0625rem var(--color-activity-blue-background), + 0 0 0 0.0625rem var(--color-background-second); +} diff --git a/src/components/swap/SwapBlockchain.tsx b/src/components/swap/SwapBlockchain.tsx new file mode 100644 index 00000000..44eb8e5b --- /dev/null +++ b/src/components/swap/SwapBlockchain.tsx @@ -0,0 +1,235 @@ +import type { TeactNode } from '../../lib/teact/teact'; +import React, { + memo, useRef, useState, +} from '../../lib/teact/teact'; +import { getActions } from '../../global'; + +import { SwapState, SwapType, type UserSwapToken } from '../../global/types'; + +import { ANIMATED_STICKER_BIG_SIZE_PX, IS_FIREFOX_EXTENSION } from '../../config'; +import buildClassName from '../../util/buildClassName'; +import { shortenAddress } from '../../util/shortenAddress'; +import getBlockchainNetworkName from '../../util/swap/getBlockchainNetworkName'; +import { IS_FIREFOX } from '../../util/windowEnvironment'; +import { callApi } from '../../api'; +import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; + +import { useDeviceScreen } from '../../hooks/useDeviceScreen'; +import useFlag from '../../hooks/useFlag'; +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; + +import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; +import Button from '../ui/Button'; +import Input from '../ui/Input'; +import ModalHeader from '../ui/ModalHeader'; +import Transition from '../ui/Transition'; + +import modalStyles from '../ui/Modal.module.scss'; +import styles from './Swap.module.scss'; + +interface OwnProps { + isActive: boolean; + swapType?: SwapType; + toAddress?: string; + tokenIn?: UserSwapToken; + tokenOut?: UserSwapToken; +} + +const SHORT_ADDRESS_SHIFT = 14; +const MIN_ADDRESS_LENGTH_TO_SHORTEN = SHORT_ADDRESS_SHIFT * 2; + +function SwapBlockchain({ + isActive, + swapType, + toAddress = '', + tokenIn, + tokenOut, +}: OwnProps) { + const { + cancelSwap, setSwapCexAddress, showNotification, setSwapScreen, + } = getActions(); + const lang = useLang(); + const { isPortrait } = useDeviceScreen(); + + // eslint-disable-next-line no-null/no-null + const toAddressRef = useRef(null); + + // Note: As of 27-11-2023, Firefox does not support readText() + const [shouldRenderPasteButton, setShouldRenderPasteButton] = useState(!(IS_FIREFOX || IS_FIREFOX_EXTENSION)); + const [isAddressFocused, markAddressFocused, unmarkAddressFocused] = useFlag(); + const [hasToAddressError, setHasToAddressError] = useState(false); + const [canContinue, setCanContinue] = useState(swapType !== SwapType.CrosschainFromTon); + + const toAddressShort = toAddress.length > MIN_ADDRESS_LENGTH_TO_SHORTEN + ? shortenAddress(toAddress, SHORT_ADDRESS_SHIFT) || '' + : toAddress; + + const addressPlaceholder = (lang('Receiving address in blockchain', { + blockchain: getBlockchainNetworkName(tokenOut?.blockchain), + }) as TeactNode[]).join(''); + + const handleCancelClick = useLastCallback(() => { + setHasToAddressError(false); + setCanContinue(false); + setSwapScreen({ state: isPortrait ? SwapState.Initial : SwapState.None }); + }); + + useHistoryBack({ + isActive, + onBack: handleCancelClick, + }); + + const handleAddressFocus = useLastCallback(() => { + const el = toAddressRef.current!; + + // `selectionStart` is only updated in the next frame after `focus` event + requestAnimationFrame(() => { + const caretPosition = el.selectionStart!; + markAddressFocused(); + + // Restore caret position after input field value has been focused and expanded + requestAnimationFrame(() => { + const newCaretPosition = caretPosition <= SHORT_ADDRESS_SHIFT + 3 + ? caretPosition + : Math.max(0, el.value.length - (toAddressShort.length - caretPosition)); + + el.setSelectionRange(newCaretPosition, newCaretPosition); + if (newCaretPosition > SHORT_ADDRESS_SHIFT * 2) { + el.scrollLeft = el.scrollWidth - el.clientWidth; + } + }); + }); + }); + + const validateToAddress = useLastCallback(async (address: string) => { + const response = await callApi('swapCexValidateAddress', { + slug: tokenOut?.slug!, + address, + }); + + if (!response) { + setHasToAddressError(false); + setCanContinue(false); + return; + } + + setHasToAddressError(!response.result); + setCanContinue(response.result); + }); + + const handleAddressBlur = useLastCallback(() => { + unmarkAddressFocused(); + }); + + const handleAddressInput = useLastCallback((newToAddress: string) => { + validateToAddress(newToAddress.trim()); + setSwapCexAddress({ toAddress: newToAddress.trim() }); + }); + + const handlePasteClick = useLastCallback(() => { + navigator.clipboard + .readText() + .then((clipboardText) => { + setSwapCexAddress({ toAddress: clipboardText.trim() }); + validateToAddress(clipboardText.trim()); + }) + .catch(() => { + showNotification({ + message: lang('Error reading clipboard') as string, + }); + setShouldRenderPasteButton(false); + }); + }); + + const submitPassword = useLastCallback(() => { + setSwapScreen({ state: SwapState.Password }); + }); + + function renderInfo() { + const text = hasToAddressError + ? lang('Incorrect address.') + : lang('Please provide an address of your wallet in another blockchain to receive bought tokens.'); + + return ( + + + {text} + + + ); + } + + function renderInputAddress() { + if (swapType !== SwapType.CrosschainFromTon) return undefined; + + return ( + <> +
+ + {shouldRenderPasteButton && toAddress === '' && ( + + )} + +
+ {renderInfo()} + + ); + } + + const title = (lang('$swap_from_to', { + from: tokenIn?.symbol, + to: tokenOut?.symbol, + }) as TeactNode[]).join(''); + + return ( + <> + +
+ + + {renderInputAddress()} + +
+ + +
+
+ + ); +} + +export default memo(SwapBlockchain); diff --git a/src/components/swap/SwapComplete.tsx b/src/components/swap/SwapComplete.tsx new file mode 100644 index 00000000..96c773e3 --- /dev/null +++ b/src/components/swap/SwapComplete.tsx @@ -0,0 +1,76 @@ +import React, { memo } from '../../lib/teact/teact'; + +import type { SwapType, UserSwapToken } from '../../global/types'; + +import buildClassName from '../../util/buildClassName'; + +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; + +import SwapResult from '../common/SwapResult'; +import Button from '../ui/Button'; +import ModalHeader from '../ui/ModalHeader'; + +import modalStyles from '../ui/Modal.module.scss'; +import styles from './Swap.module.scss'; + +interface OwnProps { + isActive: boolean; + tokenIn?: UserSwapToken; + tokenOut?: UserSwapToken; + amountIn?: number; + amountOut?: number; + swapType?: SwapType; + toAddress?: string; + onInfoClick: NoneToVoidFunction; + onStartSwap: NoneToVoidFunction; + onClose: NoneToVoidFunction; +} + +function SwapComplete({ + isActive, + tokenIn, + tokenOut, + amountIn, + amountOut, + swapType, + toAddress, + onInfoClick, + onStartSwap, + onClose, +}: OwnProps) { + const lang = useLang(); + + useHistoryBack({ + isActive, + onBack: onClose, + }); + + return ( + <> + + +
+ + +
+ +
+
+ + ); +} + +export default memo(SwapComplete); diff --git a/src/components/swap/SwapInitial.tsx b/src/components/swap/SwapInitial.tsx new file mode 100644 index 00000000..2f8786d5 --- /dev/null +++ b/src/components/swap/SwapInitial.tsx @@ -0,0 +1,626 @@ +import { BottomSheet } from 'native-bottom-sheet'; +import React, { + memo, useEffect, useMemo, useRef, useState, +} from '../../lib/teact/teact'; +import { + getActions, getGlobal, withGlobal, +} from '../../global'; + +import type { GlobalState, UserSwapToken } from '../../global/types'; +import { + SwapInputSource, SwapState, SwapType, +} from '../../global/types'; + +import { + ANIMATED_STICKER_TINY_SIZE_PX, + ANIMATION_LEVEL_MAX, + CHANGELLY_AML_KYC, + CHANGELLY_PRIVACY_POLICY, + CHANGELLY_TERMS_OF_USE, + JUSDT_TOKEN_SLUG, + JWBTC_TOKEN_SLUG, + TON_SYMBOL, + TON_TOKEN_SLUG, +} from '../../config'; +import { selectCurrentAccountState, selectSwapTokens } from '../../global/selectors'; +import buildClassName from '../../util/buildClassName'; +import { formatCurrency, formatCurrencySimple } from '../../util/formatNumber'; +import getSwapRate from '../../util/swap/getSwapRate'; +import { IS_DELEGATED_BOTTOM_SHEET } from '../../util/windowEnvironment'; +import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; + +import useDebouncedCallback from '../../hooks/useDebouncedCallback'; +import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps'; +import useFlag from '../../hooks/useFlag'; +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; +import usePrevious from '../../hooks/usePrevious'; +import usePrevious2 from '../../hooks/usePrevious2'; +import useSyncEffect from '../../hooks/useSyncEffect'; + +import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; +import RichNumberInput from '../ui/RichNumberInput'; +import Transition from '../ui/Transition'; +import SwapSelectToken from './components/SwapSelectToken'; +import SwapSubmitButton from './components/SwapSubmitButton'; +import SwapSettingsModal, { MAX_PRICE_IMPACT_VALUE } from './SwapSettingsModal'; + +import modalStyles from '../ui/Modal.module.scss'; +import styles from './Swap.module.scss'; + +interface OwnProps { + isStatic?: boolean; + isActive?: boolean; +} + +interface StateProps { + currentSwap: GlobalState['currentSwap']; + accountId?: string; + tokens?: UserSwapToken[]; + cardTokenSlug?: string; +} + +const ESTIMATE_REQUEST_INTERVAL = 15_000; +const ESTIMATE_REQUEST_DEBOUNCE_TIME = 500; +const DEFAULT_SWAP_FEE = 0.5; + +function SwapInitial({ + currentSwap: { + tokenInSlug = TON_TOKEN_SLUG, + tokenOutSlug = JWBTC_TOKEN_SLUG, + amountIn, + amountOut, + errorType, + isEstimating, + shouldEstimate, + networkFee = 0, + realNetworkFee = 0, + priceImpact = 0, + inputSource, + swapType, + limits, + isLoading, + pairs, + }, + accountId, + cardTokenSlug, + tokens, + isActive, + isStatic, +}: OwnProps & StateProps) { + const { + setDefaultSwapParams, + setSwapAmountIn, + setSwapAmountOut, + switchSwapTokens, + estimateSwap, + estimateSwapCex, + setSwapScreen, + loadSwapPairs, + setSwapType, + setSwapCexAddress, + } = getActions(); + const lang = useLang(); + + // eslint-disable-next-line no-null/no-null + const inputInRef = useRef(null); + // eslint-disable-next-line no-null/no-null + const inputOutRef = useRef(null); + + // eslint-disable-next-line no-null/no-null + const estimateIntervalId = useRef(null); + + const [isSettingsModalOpen, openSettingsModal, closeSettingsModal] = useFlag(false); + + const [hasAmountInError, setHasAmountInError] = useState(false); + + const tokenInTransitionKey = useTokenTransitionKey(tokenInSlug); + + const accountIdPrev = usePrevious(accountId, true); + + const tokenIn = useMemo( + () => tokens?.find((token) => token.slug === tokenInSlug), + [tokenInSlug, tokens], + ); + + const tokenOut = useMemo( + () => tokens?.find((token) => token.slug === tokenOutSlug), + [tokenOutSlug, tokens], + ); + + const TON = useMemo( + () => tokens?.find((token) => token.slug === TON_TOKEN_SLUG) ?? { amount: 0 }, + [tokens], + ); + + const isTokenInTON = tokenInSlug === TON_TOKEN_SLUG; + const totalTonAmount = useMemo( + () => { + if (!tokenIn || !amountIn) { + return 0; + } + if (isTokenInTON) { + return amountIn + networkFee; + } + return networkFee; + }, + [tokenIn, amountIn, isTokenInTON, networkFee], + ); + + const isErrorExist = errorType !== undefined; + const isEnoughTON = TON.amount > totalTonAmount; + // eslint-disable-next-line max-len + const isCorrectAmountIn = (amountIn && tokenIn?.amount && amountIn > 0 && amountIn <= tokenIn?.amount && isEnoughTON) || swapType === SwapType.CrosschainToTon; + const isCorrectAmountOut = amountOut && amountOut > 0; + const canSubmit = Boolean(isCorrectAmountIn && isCorrectAmountOut && !isEstimating && !isErrorExist); + const isPriceImpactError = priceImpact >= MAX_PRICE_IMPACT_VALUE; + + const isCrosschain = swapType === SwapType.CrosschainFromTon || swapType === SwapType.CrosschainToTon; + + const isReverseProhibited = useMemo(() => { + return isCrosschain || pairs?.bySlug?.[tokenInSlug]?.[tokenOutSlug]?.isReverseProhibited; + }, [isCrosschain, pairs, tokenInSlug, tokenOutSlug]); + + const handleEstimateSwap = useLastCallback((shouldBlock: boolean) => { + if (isCrosschain) { + estimateSwapCex({ shouldBlock }); + return; + } + estimateSwap({ shouldBlock }); + }); + + const debounceEstimateSwap = useDebouncedCallback( + handleEstimateSwap, [handleEstimateSwap], ESTIMATE_REQUEST_DEBOUNCE_TIME, true, + ); + + const createEstimateTimer = useLastCallback(() => { + estimateIntervalId.current = window.setInterval(() => { + debounceEstimateSwap(false); + }, ESTIMATE_REQUEST_INTERVAL); + }); + + useEffect(() => { + if (cardTokenSlug === TON_TOKEN_SLUG) { + setDefaultSwapParams({ tokenInSlug: JUSDT_TOKEN_SLUG, tokenOutSlug: cardTokenSlug }); + } else { + setDefaultSwapParams({ tokenOutSlug: cardTokenSlug }); + } + }, [cardTokenSlug]); + + useEffect(() => { + const clearEstimateTimer = () => estimateIntervalId.current && window.clearInterval(estimateIntervalId.current); + + if (shouldEstimate) { + clearEstimateTimer(); + debounceEstimateSwap(true); + } + + createEstimateTimer(); + + return clearEstimateTimer; + }, [shouldEstimate, debounceEstimateSwap, createEstimateTimer]); + + useEffect(() => { + const shouldForceUpdate = accountId !== accountIdPrev; + + if (tokenInSlug) { + loadSwapPairs({ tokenSlug: tokenInSlug, shouldForceUpdate }); + } + if (tokenOutSlug) { + loadSwapPairs({ tokenSlug: tokenOutSlug, shouldForceUpdate }); + } + }, [tokenInSlug, tokenOutSlug, accountId, accountIdPrev]); + + useEffect(() => { + if (tokenIn?.blockchain === 'ton' && tokenOut?.blockchain !== 'ton') { + setSwapType({ type: SwapType.CrosschainFromTon }); + return; + } else if (tokenOut?.blockchain === 'ton' && tokenIn?.blockchain !== 'ton') { + setSwapType({ type: SwapType.CrosschainToTon }); + return; + } + setSwapType({ type: SwapType.OnChain }); + }, [tokenIn, tokenOut]); + + const validateAmountIn = useLastCallback((amount: number | undefined) => { + if (swapType === SwapType.CrosschainToTon) { + setHasAmountInError(false); + return; + } + + const hasError = amount !== undefined && ( + Number.isNaN(amount) || amount < 0 + || (tokenIn?.amount !== undefined && amount > tokenIn.amount) + ); + + setHasAmountInError(hasError); + }); + + useEffect(() => { + validateAmountIn(amountIn); + }, [amountIn, tokenIn, validateAmountIn, swapType]); + + useEffectWithPrevDeps(([prevIsOpen]) => { + if (!IS_DELEGATED_BOTTOM_SHEET) return; + + if (isSettingsModalOpen || prevIsOpen) { + BottomSheet.setSelfSize({ size: isSettingsModalOpen ? 'full' : 'half' }); + } + }, [isSettingsModalOpen]); + + const handleAmountInChange = useLastCallback( + (amount: number | undefined, noReset = false) => { + if (!noReset) { + setHasAmountInError(false); + } + + if (amount === undefined) { + setSwapAmountIn({ amount: undefined }); + return; + } + + validateAmountIn(amount); + setSwapAmountIn({ amount }); + }, + ); + + const handleAmountOutChange = useLastCallback( + (amount: number | undefined) => { + setSwapAmountOut({ amount }); + }, + ); + + const handleMaxAmountClick = useLastCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + + if (!tokenIn?.amount) { + return; + } + + const amountWithFee = tokenIn.amount > DEFAULT_SWAP_FEE + ? tokenIn.amount - DEFAULT_SWAP_FEE + : tokenIn.amount; + const newAmount = isTokenInTON ? amountWithFee : tokenIn.amount; + + handleAmountInChange(newAmount); + }, + ); + + const handleSubmit = useLastCallback((e) => { + e.preventDefault(); + + if (!canSubmit) { + return; + } + + if (isCrosschain) { + setSwapCexAddress({ toAddress: '' }); + if (swapType === SwapType.CrosschainToTon) { + setSwapScreen({ state: SwapState.Password }); + } else { + setSwapScreen({ state: SwapState.Blockchain }); + } + return; + } + + setSwapScreen({ state: SwapState.Password }); + }); + + const handleSwitchTokens = useLastCallback(() => { + switchSwapTokens(); + }); + + function renderBalance() { + const isBalanceVisible = tokenIn && swapType !== SwapType.CrosschainToTon; + + return ( + + {isBalanceVisible && ( +
+ + {lang('$max_balance', { + balance: ( +
+ {formatCurrencySimple(tokenIn.amount, tokenIn?.symbol, tokenIn?.decimals)} +
+ ), + })} +
+
+ )} +
+ ); + } + + function renderPrice() { + const isPriceVisible = Boolean(amountIn && amountOut); + const shouldBeRendered = isPriceVisible && !isEstimating; + const rate = getSwapRate( + amountIn ? String(amountIn) : undefined, + amountOut ? String(amountOut) : undefined, + tokenIn, + tokenOut, + true, + ); + + if (!rate) return undefined; + + return ( + +
+ {shouldBeRendered && ( + + {rate.firstCurrencySymbol}{' ≈ '} + + {rate.price}{' '}{rate.secondCurrencySymbol} + + + )} +
+
+ ); + } + + function renderPriceImpactWarning() { + if (!priceImpact || !isPriceImpactError || isCrosschain) { + return undefined; + } + + return ( +
+ +
+ + {lang('The exchange rate is below market value!', { value: `${priceImpact}%` })} + + + + {lang('We do not recommend to perform an exchange, try to specify a lower amount.')} + +
+
+ ); + } + + function renderChangellyInfo() { + if (!isCrosschain) { + return undefined; + } + + return ( +
+ ); + } + + function renderFee() { + if (swapType === SwapType.CrosschainToTon) return undefined; + + const isFeeEqualZero = realNetworkFee === 0; + const text = lang(isFeeEqualZero ? '$fee_value' : '$fee_value_almost_equal', { + fee: formatCurrency(realNetworkFee, TON_SYMBOL), + }); + + return ( + +
+ {text} + { + isCrosschain + ? undefined + : + } +
+
+ ); + } + + return ( + <> +
+
+
+ {renderBalance()} + + + +
+ +
+
+ +
+
+ +
+ {renderPrice()} + + + +
+
+ +
+ {renderFee()} + + {renderPriceImpactWarning()} + {renderChangellyInfo()} + + +
+
+ + + ); +} + +export default memo( + withGlobal( + (global): StateProps => { + const accountState = selectCurrentAccountState(global); + + return { + accountId: global.currentAccountId, + currentSwap: global.currentSwap, + tokens: selectSwapTokens(global), + cardTokenSlug: accountState?.currentTokenSlug, + }; + }, + (global, _, stickToFirst) => stickToFirst(global.currentAccountId), + )(SwapInitial), +); + +function useTokenTransitionKey(tokenSlug: string) { + const transitionKeyRef = useRef(0); + + useSyncEffect(() => { + transitionKeyRef.current++; + }, [tokenSlug]); + + return transitionKeyRef.current; +} + +function AnimatedArrows({ value }: { value?: string }) { + const animationLevel = getGlobal().settings.animationLevel; + const prevValue = usePrevious2(value); + + const shouldAnimate = (animationLevel === ANIMATION_LEVEL_MAX) && prevValue && prevValue !== value; + const [hasAnimation, setHasAnimation] = useState(false); + + useEffect(() => { + if (!shouldAnimate) return undefined; + + setHasAnimation(true); + + const timeoutId = window.setTimeout(() => { + setHasAnimation(false); + }, 350); + + return () => window.clearTimeout(timeoutId); + }, [animationLevel, prevValue, shouldAnimate, value]); + + function renderArrow(isInverted?: boolean) { + return ( +
+
+ +
+
+ +
+
+ +
+
+ ); + } + + return ( + <> + {renderArrow()} + {renderArrow(true)} + + ); +} diff --git a/src/components/swap/SwapModal.tsx b/src/components/swap/SwapModal.tsx new file mode 100644 index 00000000..fd12333f --- /dev/null +++ b/src/components/swap/SwapModal.tsx @@ -0,0 +1,306 @@ +import React, { + memo, useEffect, useMemo, useState, +} from '../../lib/teact/teact'; +import { getActions, withGlobal } from '../../global'; + +import type { ApiActivity } from '../../api/types'; +import type { GlobalState, UserSwapToken } from '../../global/types'; +import { SwapState, SwapType } from '../../global/types'; + +import { IS_CAPACITOR } from '../../config'; +import { selectCurrentAccountState, selectSwapTokens } from '../../global/selectors'; +import buildClassName from '../../util/buildClassName'; +import { formatCurrencyExtended } from '../../util/formatNumber'; +import resolveModalTransitionName from '../../util/resolveModalTransitionName'; +import getBlockchainNetworkIcon from '../../util/swap/getBlockchainNetworkIcon'; +import { ASSET_LOGO_PATHS } from '../ui/helpers/assetLogos'; + +import { useDeviceScreen } from '../../hooks/useDeviceScreen'; +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; +import useModalTransitionKeys from '../../hooks/useModalTransitionKeys'; + +import TokenSelector from '../common/TokenSelector'; +import Modal from '../ui/Modal'; +import ModalHeader from '../ui/ModalHeader'; +import Transition from '../ui/Transition'; +import SwapBlockchain from './SwapBlockchain'; +import SwapComplete from './SwapComplete'; +import SwapInitial from './SwapInitial'; +import SwapPassword from './SwapPassword'; +import SwapWaitTokens from './SwapWaitTokens'; + +import modalStyles from '../ui/Modal.module.scss'; +import styles from './Swap.module.scss'; + +interface StateProps { + currentSwap: GlobalState['currentSwap']; + swapTokens?: UserSwapToken[]; + activityById?: Record; +} + +function SwapModal({ + currentSwap: { + state, + tokenInSlug, + tokenOutSlug, + amountIn = 0, + amountOut = 0, + isLoading, + error, + activityId, + swapType, + toAddress, + payinAddress, + }, + swapTokens, + activityById, +}: StateProps) { + const { + startSwap, + cancelSwap, + setSwapScreen, + submitSwap, + showActivityInfo, + submitSwapCexFromTon, + submitSwapCexToTon, + } = getActions(); + const lang = useLang(); + const { isPortrait } = useDeviceScreen(); + + const isOpen = state !== SwapState.None; + const { renderingKey, nextKey, updateNextKey } = useModalTransitionKeys(state, isOpen); + + const tokenIn = useMemo( + () => swapTokens?.find((token) => token.slug === tokenInSlug), + [tokenInSlug, swapTokens], + ); + + const tokenOut = useMemo( + () => swapTokens?.find((token) => token.slug === tokenOutSlug), + [swapTokens, tokenOutSlug], + ); + + const [renderedTransactionAmountIn, setRenderedTransactionAmountIn] = useState(amountIn); + const [renderedTransactionAmountOut, setRenderedTransactionAmountOut] = useState(amountOut); + + useEffect(() => { + if (!isOpen || !activityById || !activityId || swapType !== SwapType.CrosschainToTon) return; + + const activity = activityById[activityId]; + + if (activity.kind === 'swap') { + const status = activity.cex?.status; + if (status === 'exchanging' || status === 'confirming') { + setSwapScreen({ state: SwapState.Complete }); + } + } + }, [activityById, activityId, isOpen, swapType]); + + const handleTransferSubmit = useLastCallback((password: string) => { + setRenderedTransactionAmountIn(amountIn); + setRenderedTransactionAmountOut(amountOut); + + if (swapType === SwapType.OnChain) { + submitSwap({ password }); + return; + } + + if (swapType === SwapType.CrosschainToTon) { + submitSwapCexToTon({ password }); + } else { + submitSwapCexFromTon({ password }); + } + }); + + const handleBackClick = useLastCallback(() => { + if (state === SwapState.Password) { + if (swapType === SwapType.CrosschainFromTon) { + setSwapScreen({ state: SwapState.Blockchain }); + } else { + setSwapScreen({ state: isPortrait ? SwapState.Initial : SwapState.None }); + } + return; + } + + if (state === SwapState.SelectTokenTo || state === SwapState.SelectTokenFrom) { + setSwapScreen({ state: isPortrait ? SwapState.Initial : SwapState.None }); + } + + if (state === SwapState.Blockchain) { + setSwapScreen({ state: isPortrait ? SwapState.Initial : SwapState.None }); + } + }); + + const handleTransactionInfoClick = useLastCallback(() => { + cancelSwap({ shouldReset: true }); + showActivityInfo({ id: activityId! }); + }); + + const handleModalClose = useLastCallback(() => { + cancelSwap({ shouldReset: isPortrait }); + updateNextKey(); + }); + + const handleModalCloseWithReset = useLastCallback(() => { + cancelSwap({ shouldReset: true }); + }); + + const handleStartSwap = useLastCallback(() => { + startSwap({ isPortrait }); + }); + + function renderSwapShortInfo() { + if (!tokenIn || !tokenOut || !amountIn || !amountOut) return undefined; + + const logoIn = tokenIn.image ?? ASSET_LOGO_PATHS[tokenIn.symbol.toLowerCase() as keyof typeof ASSET_LOGO_PATHS]; + const logoOut = tokenOut.image ?? ASSET_LOGO_PATHS[tokenOut.symbol.toLowerCase() as keyof typeof ASSET_LOGO_PATHS]; + + return ( +
+
+ {tokenIn.symbol} + {tokenIn.blockchain && ( + {tokenIn.blockchain} + )} +
+ + {lang('%amount_from% to %amount_to%', { + amount_from: ( + + {formatCurrencyExtended(amountIn, tokenIn.symbol ?? '', true)} + ), + amount_to: ( + + {formatCurrencyExtended(amountOut, tokenOut.symbol ?? '', true)} + ), + })} + +
+ {tokenOut.symbol} + {tokenOut.blockchain && ( + {tokenOut.blockchain} + )} +
+
+ ); + } + + // eslint-disable-next-line consistent-return + function renderContent(isActive: boolean, isFrom: boolean, currentKey: number) { + switch (currentKey) { + case SwapState.Initial: + return ( + <> + + + + ); + case SwapState.Blockchain: + return ( + + ); + case SwapState.WaitTokens: + return ( + + ); + case SwapState.Password: + return ( + + {IS_CAPACITOR && renderSwapShortInfo()} + + ); + case SwapState.Complete: + return ( + + ); + case SwapState.SelectTokenFrom: + case SwapState.SelectTokenTo: + return ( + + ); + } + } + + return ( + + + {renderContent} + + + ); +} + +export default memo(withGlobal((global): StateProps => { + const accountState = selectCurrentAccountState(global); + const activityById = accountState?.activities?.byId; + + return { + currentSwap: global.currentSwap, + swapTokens: selectSwapTokens(global), + activityById, + }; +})(SwapModal)); diff --git a/src/components/swap/SwapPassword.tsx b/src/components/swap/SwapPassword.tsx new file mode 100644 index 00000000..f72fc4f2 --- /dev/null +++ b/src/components/swap/SwapPassword.tsx @@ -0,0 +1,60 @@ +import React, { memo, type TeactNode } from '../../lib/teact/teact'; +import { getActions } from '../../global'; + +import { IS_CAPACITOR } from '../../config'; + +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; + +import ModalHeader from '../ui/ModalHeader'; +import PasswordForm from '../ui/PasswordForm'; + +interface OwnProps { + isActive: boolean; + isLoading?: boolean; + error?: string; + children?: TeactNode; + onSubmit: (password: string) => void; + onBack: NoneToVoidFunction; +} + +function SwapPassword({ + isActive, + isLoading, + error, + children, + onSubmit, + onBack, +}: OwnProps) { + const { cancelSwap, clearSwapError } = getActions(); + + const lang = useLang(); + + useHistoryBack({ + isActive, + onBack, + }); + + return ( + <> + {!IS_CAPACITOR && } + + {children} + + + ); +} + +export default memo(SwapPassword); diff --git a/src/components/swap/SwapSettingsModal.tsx b/src/components/swap/SwapSettingsModal.tsx new file mode 100644 index 00000000..85d36b72 --- /dev/null +++ b/src/components/swap/SwapSettingsModal.tsx @@ -0,0 +1,239 @@ +import React, { memo, useState } from '../../lib/teact/teact'; +import { getActions, withGlobal } from '../../global'; + +import type { UserSwapToken, UserToken } from '../../global/types'; + +import { TON_SYMBOL } from '../../config'; +import buildClassName from '../../util/buildClassName'; +import { formatCurrency } from '../../util/formatNumber'; + +import useFlag from '../../hooks/useFlag'; +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; + +import Button from '../ui/Button'; +import IconWithTooltip from '../ui/IconWithTooltip'; +import Modal from '../ui/Modal'; +import RichNumberInput from '../ui/RichNumberInput'; + +import modalStyles from '../ui/Modal.module.scss'; +import styles from './Swap.module.scss'; + +interface OwnProps { + isOpen: boolean; + tokenOut?: UserToken | UserSwapToken; + fee?: number; + onClose: () => void; +} + +interface StateProps { + slippage: number; + priceImpact?: number; + amountOutMin?: string; +} + +const SLIPPAGE_VALUES = [0.1, 0.5, 1, 5]; +const MAX_SLIPPAGE_VALUE = 50; + +export const MAX_PRICE_IMPACT_VALUE = 5; + +function SwapSettingsModal({ + isOpen, + onClose, + tokenOut, + slippage, + priceImpact = 0, + fee = 0, + amountOutMin = '0', +}: OwnProps & StateProps) { + const { setSlippage } = getActions(); + const lang = useLang(); + + const [isSlippageFocused, markSlippageFocused, unmarkSlippageFocused] = useFlag(); + const [hasError, setHasError] = useState(false); + + const [currentSlippage, setCurrentSlippage] = useState(slippage); + + const priceImpactError = priceImpact >= MAX_PRICE_IMPACT_VALUE; + const slippageError = currentSlippage === undefined || currentSlippage > MAX_SLIPPAGE_VALUE; + + const handleSave = useLastCallback(() => { + setSlippage({ slippage: currentSlippage! }); + onClose(); + }); + + const resetModal = useLastCallback(() => { + setCurrentSlippage(slippage); + }); + + function renderSlippageValues() { + const slippageList = SLIPPAGE_VALUES.map((value, index) => { + return ( + <> +
setCurrentSlippage(value)} + className={styles.balanceLink} + >{value}% +
+ {index + 1 !== SLIPPAGE_VALUES.length && } + + ); + }); + + return ( +
+ {slippageList} +
+ ); + } + + function renderSlippageError() { + const error = currentSlippage === undefined + ? lang('Slippage not specified') + : currentSlippage > MAX_SLIPPAGE_VALUE + ? lang('Slippage too high') + : ''; + + setHasError(!!error); + + return ( +
+ {lang(error)} +
+ ); + } + + function renderSlippageLabel() { + return ( + <> + {lang('Slippage')} + + {lang('$swap_slippage_tooltip1')} + {lang('$swap_slippage_tooltip2')} +
+ )} + tooltipClassName={styles.advancedTooltipContainer} + iconClassName={buildClassName( + styles.advancedTooltip, slippageError && styles.advancedError, + )} + /> + + ); + } + + return ( + +
+ {lang('Swap Details')} +
+
+ {renderSlippageValues()} + + {renderSlippageError()} +
+
+
+ + {lang('Blockchain fee')} + + + ≈ {formatCurrency(fee, TON_SYMBOL)} + +
+
+ + {lang('Price Impact')} + + {lang('$swap_price_impact_tooltip1')} + {lang('$swap_price_impact_tooltip2')} +
+ )} + tooltipClassName={styles.advancedTooltipContainer} + iconClassName={buildClassName( + styles.advancedTooltip, priceImpactError && styles.advancedError, + )} + /> + + {priceImpact}% + +
+
+ + {lang('Minimum Received')} + + {lang('$swap_minimum_received_tooltip1')} + {lang('$swap_minimum_received_tooltip2')} +
+ )} + tooltipClassName={styles.advancedTooltipContainer} + iconClassName={styles.advancedTooltip} + /> + + {tokenOut && ( + { + formatCurrency(Number(amountOutMin), tokenOut.symbol) + } + + )} +
+
+
+ + +
+ + ); +} + +export default memo( + withGlobal((global): StateProps => { + const { + slippage, priceImpact, amountOutMin, + } = global.currentSwap; + + return { + slippage, + priceImpact, + amountOutMin, + }; + })(SwapSettingsModal), +); diff --git a/src/components/swap/SwapWaitTokens.tsx b/src/components/swap/SwapWaitTokens.tsx new file mode 100644 index 00000000..6d3666cd --- /dev/null +++ b/src/components/swap/SwapWaitTokens.tsx @@ -0,0 +1,133 @@ +import React, { memo, useMemo, useState } from '../../lib/teact/teact'; + +import type { UserSwapToken } from '../../global/types'; + +import { CHANGELLY_WAITING_DEADLINE } from '../../config'; +import buildClassName from '../../util/buildClassName'; +import { formatCurrencyExtended } from '../../util/formatNumber'; +import getBlockchainNetworkName from '../../util/swap/getBlockchainNetworkName'; + +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; + +import Countdown from '../common/Countdown'; +import SwapTokensInfo from '../common/SwapTokensInfo'; +import Button from '../ui/Button'; +import InteractiveTextField from '../ui/InteractiveTextField'; +import ModalHeader from '../ui/ModalHeader'; +import Transition from '../ui/Transition'; + +import modalStyles from '../ui/Modal.module.scss'; +import styles from './Swap.module.scss'; + +interface OwnProps { + isActive: boolean; + tokenIn?: UserSwapToken; + tokenOut?: UserSwapToken; + amountIn?: number; + amountOut?: number; + payinAddress?: string; + onClose: NoneToVoidFunction; +} + +function SwapWaitTokens({ + isActive, + tokenIn, + tokenOut, + amountIn, + amountOut, + payinAddress, + onClose, +}: OwnProps) { + const lang = useLang(); + + const [isExpired, setIsExpired] = useState(false); + + const timestamp = useMemo(() => Date.now(), []); + + useHistoryBack({ + isActive, + onBack: onClose, + }); + + const handleTimeout = useLastCallback(() => { + setIsExpired(true); + }); + + function renderInfo() { + if (isExpired) { + return ( +
+ + {lang('The time for sending coins is over')} + + + {lang('Please wait a few moments...')} + +
+ ); + } + + return ( +
+ {lang('$swap_changelly_to_ton_description1', { + value: ( + + {formatCurrencyExtended(Number(amountIn), tokenIn?.symbol ?? '', true)} + + ), + blockchain: ( + + {getBlockchainNetworkName(tokenIn?.blockchain)} + + ), + time: , + })} + + +
+ ); + } + + return ( + <> + + +
+ + + + {renderInfo()} + + +
+ +
+
+ + ); +} + +export default memo(SwapWaitTokens); diff --git a/src/components/swap/components/SwapSelectToken.tsx b/src/components/swap/components/SwapSelectToken.tsx new file mode 100644 index 00000000..cb6f87a1 --- /dev/null +++ b/src/components/swap/components/SwapSelectToken.tsx @@ -0,0 +1,109 @@ +import React, { + memo, + useLayoutEffect, + useRef, +} from '../../../lib/teact/teact'; +import { setExtraStyles } from '../../../lib/teact/teact-dom'; +import { getActions } from '../../../global'; + +import { SwapState, type UserSwapToken } from '../../../global/types'; + +import { TON_BLOCKCHAIN } from '../../../config'; +import buildClassName from '../../../util/buildClassName'; +import getBlockchainNetworkIcon from '../../../util/swap/getBlockchainNetworkIcon'; +import { REM } from '../../../util/windowEnvironment'; +import { ASSET_LOGO_PATHS } from '../../ui/helpers/assetLogos'; + +import useLastCallback from '../../../hooks/useLastCallback'; +import useSyncEffect from '../../../hooks/useSyncEffect'; + +import Transition from '../../ui/Transition'; + +import styles from '../Swap.module.scss'; + +interface OwnProps { + token?: UserSwapToken; + shouldFilter?: boolean; +} + +function SwapSelectToken({ token, shouldFilter }: OwnProps) { + const { setSwapScreen } = getActions(); + + const { transitionRef } = useSelectorWidth(token); + + const handleOpenSelectTokenModal = useLastCallback(() => { + setSwapScreen({ + state: shouldFilter ? SwapState.SelectTokenTo : SwapState.SelectTokenFrom, + }); + }); + + const buttonTransitionKeyRef = useRef(0); + const buttonStateStr = `${token?.symbol}_${token?.blockchain}`; + + useSyncEffect(() => { + buttonTransitionKeyRef.current++; + }, [buttonStateStr]); + + function renderToken(tokenToRender: UserSwapToken | undefined) { + const image = ASSET_LOGO_PATHS[ + tokenToRender?.symbol.toLowerCase() as keyof typeof ASSET_LOGO_PATHS + ] ?? tokenToRender?.image; + const blockchain = tokenToRender?.blockchain ?? TON_BLOCKCHAIN; + + return ( + + + + ); + } + + return renderToken(token); +} + +export default memo(SwapSelectToken); + +const BUTTON_WIDTH = 70 / REM; +const CHARACTER_WIDTH = 10; + +function useSelectorWidth(token?: UserSwapToken) { + // eslint-disable-next-line no-null/no-null + const transitionRef = useRef(null); + + useLayoutEffect(() => { + if (!transitionRef.current || !token) return; + + const symbolWidth = (token.symbol.length * CHARACTER_WIDTH) / REM; + const minWidth = `${symbolWidth + BUTTON_WIDTH}rem`; + + setExtraStyles(transitionRef.current, { + minWidth, + }); + }, [token]); + + return { + transitionRef, + }; +} diff --git a/src/components/swap/components/SwapSubmitButton.tsx b/src/components/swap/components/SwapSubmitButton.tsx new file mode 100644 index 00000000..8d2d8a7a --- /dev/null +++ b/src/components/swap/components/SwapSubmitButton.tsx @@ -0,0 +1,114 @@ +import React, { + memo, useRef, +} from '../../../lib/teact/teact'; + +import type { UserSwapToken } from '../../../global/types'; +import { SwapErrorType, SwapType } from '../../../global/types'; + +import { formatCurrencySimple } from '../../../util/formatNumber'; + +import useLang from '../../../hooks/useLang'; +import useSyncEffect from '../../../hooks/useSyncEffect'; + +import Button from '../../ui/Button'; +import Transition from '../../ui/Transition'; + +import styles from '../Swap.module.scss'; + +interface OwnProps { + tokenIn?: UserSwapToken; + tokenOut?: UserSwapToken; + amountIn?: number; + amountOut?: number; + swapType?: SwapType; + isEstimating?: boolean; + isSending?: boolean; + isEnoughTON?: boolean; + isPriceImpactError?: boolean; + canSubmit?: boolean; + errorType?: SwapErrorType; + limits?: { + fromMin?: string; + fromMax?: string; + }; +} + +function SwapSubmitButton({ + tokenIn, + tokenOut, + amountIn, + amountOut, + swapType, + isEstimating, + isSending, + isEnoughTON, + isPriceImpactError, + canSubmit, + errorType, + limits, +}: OwnProps) { + const lang = useLang(); + + const isErrorExist = errorType !== undefined; + const shouldSendingBeVisible = isSending && swapType === SwapType.CrosschainToTon; + const isDisabled = !canSubmit || shouldSendingBeVisible; + const isLoading = isEstimating || shouldSendingBeVisible; + + const errorMsgByType = { + [SwapErrorType.InvalidPair]: lang('Invalid Pair'), + [SwapErrorType.NotEnoughLiquidity]: lang('Insufficient liquidity'), + [SwapErrorType.ChangellyMinSwap]: lang('Minimum amount', { + value: formatCurrencySimple(Number(limits?.fromMin ?? 0), tokenIn?.symbol ?? '', tokenIn?.decimals), + }), + [SwapErrorType.ChangellyMaxSwap]: lang('Maximum amount', { + value: formatCurrencySimple(Number(limits?.fromMax ?? 0), tokenIn?.symbol ?? '', tokenIn?.decimals), + }), + }; + + const isTouched = Boolean(amountIn || amountOut); + + let text = lang('$swap_from_to', { + from: tokenIn?.symbol, + to: tokenOut?.symbol, + }); + + if (isTouched && isErrorExist) { + text = errorMsgByType[errorType]; + } else if (isTouched && !isEnoughTON && swapType !== SwapType.CrosschainToTon) { + text = lang('Not enough TON'); + } + + let shouldShowError = !isEstimating && ((isPriceImpactError || isErrorExist || !isEnoughTON)); + + if (swapType === SwapType.CrosschainToTon) { + shouldShowError = !isEstimating && ((isPriceImpactError || isErrorExist)); + } + + const buttonTransitionKeyRef = useRef(0); + const buttonStateStr = `${text}_${!canSubmit}_${isTouched && shouldShowError}`; + + useSyncEffect(() => { + buttonTransitionKeyRef.current++; + }, [buttonStateStr]); + + return ( + + + + ); +} + +export default memo(SwapSubmitButton); diff --git a/src/components/transfer/Transfer.module.scss b/src/components/transfer/Transfer.module.scss index ed1394b2..712dac29 100644 --- a/src/components/transfer/Transfer.module.scss +++ b/src/components/transfer/Transfer.module.scss @@ -1,10 +1,10 @@ @import "../../styles/mixins"; .modalDialog { - height: 35rem; + height: 35.5rem; @supports (height: env(safe-area-inset-bottom)) { - height: calc(35rem + env(safe-area-inset-bottom)); + height: calc(35.5rem + env(safe-area-inset-bottom)); } } @@ -25,18 +25,27 @@ border-radius: var(--border-radius-small) !important; - &:hover, - &:focus, &:active { color: var(--color-input-button-text); background-color: var(--color-input-button-background); - } - &:active { // Optimization transition: none; } + + @media (hover: hover) { + &:hover, + &:focus { + color: var(--color-input-button-text); + + background-color: var(--color-input-button-background); + } + } +} + +.inputButtonShifted { + right: 2.5rem; } .amountInput { @@ -150,7 +159,7 @@ gap: 0.25rem; align-items: center; - margin-bottom: 0.3125rem; + margin-bottom: 0.5rem; font-weight: 700; color: var(--color-gray-2); @@ -175,7 +184,7 @@ } .addressWidget { - margin-bottom: 1rem; + margin-bottom: 1.25rem; } .inputReadOnly { @@ -184,7 +193,7 @@ display: flex; box-sizing: border-box; - margin-bottom: 1rem; + margin-bottom: 1.25rem; padding: 0.875rem 0.75rem; font-size: 1rem; @@ -204,6 +213,10 @@ } } +.commentInputWrapper { + margin-bottom: 0 !important; +} + .sticker { margin: 0 auto 1.25rem; @@ -237,10 +250,6 @@ animation: button-loading-spinner 1s linear infinite; } -.buttonSubmit { - margin-top: 2rem; -} - .savedAddressItem { cursor: var(--custom-cursor, pointer); @@ -392,6 +401,10 @@ padding: 0.5rem 0.6875rem 0.375rem; } +.inputWithIcon { + padding-right: 2.375rem; +} + textarea.inputStatic { /* default input padding minus 1px accounted for border width */ padding: 0.875rem 0.75rem 0.8125rem; @@ -432,4 +445,31 @@ textarea.inputStatic { z-index: 1; width: 18.9375rem; -} \ No newline at end of file +} + +.transferShortInfo { + display: flex; + gap: 0.25rem; + align-items: center; + + height: 2rem; + padding: 0 0.5rem 0 0.375rem; + + font-size: 0.9375rem; + font-weight: 500; + color: var(--color-activity-blue); + + background-color: var(--color-activity-blue-background); + border-radius: 1rem; +} + +.bold { + font-weight: 700; +} + +.tokenIcon { + width: 1.25rem; + height: 1.25rem; + + border-radius: 50%; +} diff --git a/src/components/transfer/TransferComplete.tsx b/src/components/transfer/TransferComplete.tsx new file mode 100644 index 00000000..45b90c4c --- /dev/null +++ b/src/components/transfer/TransferComplete.tsx @@ -0,0 +1,96 @@ +import React, { memo } from '../../lib/teact/teact'; +import { getActions } from '../../global'; + +import { TON_TOKEN_SLUG } from '../../config'; +import { bigStrToHuman } from '../../global/helpers'; + +import { useDeviceScreen } from '../../hooks/useDeviceScreen'; +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; + +import TransferResult from '../common/TransferResult'; +import Button from '../ui/Button'; +import ModalHeader from '../ui/ModalHeader'; + +import modalStyles from '../ui/Modal.module.scss'; + +interface OwnProps { + isActive?: boolean; + amount?: number; + symbol: string; + balance?: number; + fee?: string; + operationAmount?: number; + txId?: string; + tokenSlug?: string; + toAddress?: string; + comment?: string; + onInfoClick: NoneToVoidFunction; + onClose: NoneToVoidFunction; +} + +const AMOUNT_PRECISION = 4; + +function TransferComplete({ + isActive, + amount, + symbol, + balance, + fee, + operationAmount, + txId, + tokenSlug, + toAddress, + comment, + onInfoClick, + onClose, +}: OwnProps) { + const { startTransfer } = getActions(); + + const lang = useLang(); + const { isPortrait } = useDeviceScreen(); + + useHistoryBack({ + isActive, + onBack: onClose, + }); + + const handleTransactionRepeatClick = useLastCallback(() => { + startTransfer({ + isPortrait, + tokenSlug: tokenSlug || TON_TOKEN_SLUG, + toAddress, + amount, + comment, + }); + }); + + return ( + <> + + +
+ + +
+ +
+
+ + ); +} + +export default memo(TransferComplete); diff --git a/src/components/transfer/TransferConfirm.tsx b/src/components/transfer/TransferConfirm.tsx new file mode 100644 index 00000000..358d3801 --- /dev/null +++ b/src/components/transfer/TransferConfirm.tsx @@ -0,0 +1,140 @@ +import React, { memo } from '../../lib/teact/teact'; +import { getActions, withGlobal } from '../../global'; + +import type { GlobalState } from '../../global/types'; + +import { ANIMATED_STICKER_SMALL_SIZE_PX } from '../../config'; +import { bigStrToHuman } from '../../global/helpers'; +import buildClassName from '../../util/buildClassName'; +import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; + +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; + +import AmountWithFeeTextField from '../ui/AmountWithFeeTextField'; +import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; +import Button from '../ui/Button'; +import IconWithTooltip from '../ui/IconWithTooltip'; +import InteractiveTextField from '../ui/InteractiveTextField'; +import ModalHeader from '../ui/ModalHeader'; + +import modalStyles from '../ui/Modal.module.scss'; +import styles from './Transfer.module.scss'; + +interface OwnProps { + isActive: boolean; + savedAddresses?: Record; + symbol: string; + onBack: NoneToVoidFunction; + onClose: NoneToVoidFunction; +} + +interface StateProps { + currentTransfer: GlobalState['currentTransfer']; +} + +function TransferConfirm({ + currentTransfer: { + amount, + toAddress, + resolvedAddress, + fee, + comment, + shouldEncrypt, + promiseId, + isLoading, + toAddressName, + isToNewAddress, + }, symbol, isActive, savedAddresses, onBack, onClose, +}: OwnProps & StateProps) { + const { submitTransferConfirm } = getActions(); + + const lang = useLang(); + + const addressName = savedAddresses?.[toAddress!] || toAddressName; + + useHistoryBack({ + isActive, + onBack, + }); + + function renderComment() { + if (!comment) { + return undefined; + } + + return ( + <> +
{shouldEncrypt ? lang('Encrypted Message') : lang('Comment')}
+
+ {comment} +
+ + ); + } + + return ( + <> + +
+ +
+ {lang('Receiving Address')} + + {isToNewAddress && ( + + )} +
+ + + + + {renderComment()} + +
+ {promiseId ? ( + + ) : ( + + )} + +
+
+ + ); +} + +export default memo(withGlobal((global): StateProps => { + return { + currentTransfer: global.currentTransfer, + }; +})(TransferConfirm)); diff --git a/src/components/transfer/TransferInitial.tsx b/src/components/transfer/TransferInitial.tsx index 6cad1536..19aa4d9a 100644 --- a/src/components/transfer/TransferInitial.tsx +++ b/src/components/transfer/TransferInitial.tsx @@ -1,15 +1,16 @@ +import type { URLOpenListenerEvent } from '@capacitor/app'; +import { App as CapacitorApp } from '@capacitor/app'; import React, { memo, useEffect, useMemo, useRef, useState, } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; +import type { ApiBaseCurrency } from '../../api/types'; import type { UserToken } from '../../global/types'; import type { DropdownItem } from '../ui/Dropdown'; import { ElectronEvent } from '../../electron/types'; -import { - DEFAULT_PRICE_CURRENCY, TON_SYMBOL, TON_TOKEN_SLUG, -} from '../../config'; +import { IS_FIREFOX_EXTENSION, TON_SYMBOL, TON_TOKEN_SLUG } from '../../config'; import { bigStrToHuman } from '../../global/helpers'; import { selectCurrentAccountState, @@ -17,13 +18,20 @@ import { selectIsHardwareAccount, } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; +import { clearLaunchUrl, getLaunchUrl } from '../../util/capacitor'; import dns from '../../util/dns'; -import { formatCurrency, formatCurrencyExtended } from '../../util/formatNumber'; +import { + formatCurrency, + formatCurrencyExtended, + formatCurrencySimple, + getShortCurrencySymbol, +} from '../../util/formatNumber'; import { getIsAddressValid } from '../../util/getIsAddressValid'; import { throttle } from '../../util/schedulers'; import { shortenAddress } from '../../util/shortenAddress'; import stopEvent from '../../util/stopEvent'; -import { IS_TOUCH_ENV } from '../../util/windowEnvironment'; +import { parseTonDeeplink } from '../../util/ton/deeplinks'; +import { IS_FIREFOX, IS_TOUCH_ENV } from '../../util/windowEnvironment'; import { ASSET_LOGO_PATHS } from '../ui/helpers/assetLogos'; import useCurrentOrPrev from '../../hooks/useCurrentOrPrev'; @@ -45,6 +53,8 @@ import styles from './Transfer.module.scss'; interface OwnProps { isStatic?: boolean; + onQrScanPress?: NoneToVoidFunction; + onCommentChange?: NoneToVoidFunction; } interface StateProps { @@ -52,14 +62,14 @@ interface StateProps { amount?: number; comment?: string; shouldEncrypt?: boolean; + isLoading?: boolean; fee?: string; tokenSlug?: string; tokens?: UserToken[]; savedAddresses?: Record; - isLoading?: boolean; - onCommentChange?: NoneToVoidFunction; isEncryptedCommentSupported: boolean; isCommentSupported: boolean; + baseCurrency?: ApiBaseCurrency; } const COMMENT_MAX_SIZE_BYTES = 5000; @@ -82,10 +92,12 @@ function TransferInitial({ tokens, fee, savedAddresses, - isLoading, - onCommentChange, isEncryptedCommentSupported, isCommentSupported, + isLoading, + onCommentChange, + onQrScanPress, + baseCurrency, }: OwnProps & StateProps) { const { submitTransferInitial, @@ -96,6 +108,7 @@ function TransferInitial({ setTransferToAddress, setTransferComment, setTransferShouldEncrypt, + cancelTransfer, } = getActions(); // eslint-disable-next-line no-null/no-null @@ -103,7 +116,8 @@ function TransferInitial({ const lang = useLang(); - const [shouldRenderPasteButton, setShouldRenderPasteButton] = useState(true); + // Note: As of 27-11-2023, Firefox does not support readText() + const [shouldRenderPasteButton, setShouldRenderPasteButton] = useState(!(IS_FIREFOX || IS_FIREFOX_EXTENSION)); const [isAddressFocused, markAddressFocused, unmarkAddressFocused] = useFlag(); const [isSavedAddressesOpen, openSavedAddresses, closeSavedAddresses] = useFlag(); const [savedAddressForDeletion, setSavedAddressForDeletion] = useState(); @@ -126,6 +140,9 @@ function TransferInitial({ const amountInCurrency = price && amount && !Number.isNaN(amount) ? amount * price : undefined; const renderingAmountInCurrency = useCurrentOrPrev(amountInCurrency, true); const renderingFee = useCurrentOrPrev(fee, true); + const withPasteButton = shouldRenderPasteButton && toAddress === ''; + const withQrScanButton = Boolean(onQrScanPress); + const shortBaseSymbol = getShortCurrencySymbol(baseCurrency); const { shouldRender: shouldRenderCurrency, transitionClassNames: currencyClassNames } = useShowTransition( Boolean(amountInCurrency), @@ -201,13 +218,32 @@ function TransferInitial({ }); }, [amount, comment, fetchFee, hasToAddressError, isAddressValid, toAddress, tokenSlug]); + const processDeeplink = useLastCallback((url: string) => { + const params = parseTonDeeplink(url); + if (!params) return; + + setTransferToAddress({ toAddress: params.to }); + setTransferAmount({ amount: params.amount ? bigStrToHuman(params.amount) : undefined }); + setTransferComment({ comment: params.comment }); + }); + useEffect(() => { - return window.electron?.on(ElectronEvent.DEEPLINK, (params: any) => { - setTransferToAddress({ toAddress: params.to }); - setTransferAmount({ amount: bigStrToHuman(params.amount) }); - setTransferComment({ comment: params.text }); + return window.electron?.on(ElectronEvent.DEEPLINK, ({ url }: { url: string }) => { + processDeeplink(url); }); - }, []); + }, [processDeeplink]); + + useEffect(() => { + const launchUrl = getLaunchUrl(); + if (launchUrl) { + processDeeplink(launchUrl); + clearLaunchUrl(); + } + + return CapacitorApp.addListener('appUrlOpen', (event: URLOpenListenerEvent) => { + processDeeplink(event.url); + }).remove; + }, [processDeeplink]); const handleTokenChange = useLastCallback( (slug: string) => { @@ -262,6 +298,11 @@ function TransferInitial({ setTransferToAddress({ toAddress: newToAddress }); }); + const handleQrScanClick = useLastCallback(() => { + cancelTransfer(); + onQrScanPress!(); + }); + const handlePasteClick = useLastCallback(() => { navigator.clipboard .readText() @@ -396,10 +437,10 @@ function TransferInitial({ return (
- {lang('$balance_is', { + {lang('$max_balance', { balance: ( - {balance !== undefined ? formatCurrencyExtended(balance, symbol, true) : lang('Loading...')} + {balance !== undefined ? formatCurrencySimple(balance, symbol, decimals) : lang('Loading...')} ), })} @@ -422,7 +463,7 @@ function TransferInitial({ function renderCurrencyValue() { return ( - ≈ {formatCurrency(renderingAmountInCurrency || 0, DEFAULT_PRICE_CURRENCY)} + ≈ {formatCurrency(renderingAmountInCurrency || 0, shortBaseSymbol)} ); } @@ -446,7 +487,7 @@ function TransferInitial({
- {shouldRenderPasteButton && toAddress === '' && ( + {withQrScanButton && ( + + )} + {withPasteButton && ( @@ -490,17 +541,18 @@ function TransferInitial({ {isCommentSupported && ( )} -
+
@@ -516,35 +568,37 @@ function TransferInitial({ } export default memo( - withGlobal((global, ownProps, detachWhenChanged): StateProps => { - detachWhenChanged(global.currentAccountId); - - const { - toAddress, - amount, - comment, - shouldEncrypt, - fee, - tokenSlug, - isLoading, - } = global.currentTransfer; + withGlobal( + (global): StateProps => { + const { + toAddress, + amount, + comment, + shouldEncrypt, + fee, + tokenSlug, + isLoading, + } = global.currentTransfer; - const isLedger = selectIsHardwareAccount(global); + const isLedger = selectIsHardwareAccount(global); + const accountState = selectCurrentAccountState(global); - return { - toAddress, - amount, - comment, - shouldEncrypt, - fee, - tokenSlug, - tokens: selectCurrentAccountTokens(global), - isLoading, - savedAddresses: selectCurrentAccountState(global)?.savedAddresses, - isEncryptedCommentSupported: !isLedger, - isCommentSupported: !tokenSlug || tokenSlug === TON_TOKEN_SLUG || !isLedger, - }; - })(TransferInitial), + return { + toAddress, + amount, + comment, + shouldEncrypt, + fee, + tokenSlug, + tokens: selectCurrentAccountTokens(global), + savedAddresses: accountState?.savedAddresses, + isEncryptedCommentSupported: !isLedger, + isCommentSupported: !tokenSlug || tokenSlug === TON_TOKEN_SLUG || !isLedger, + isLoading, + }; + }, + (global, _, stickToFirst) => stickToFirst(global.currentAccountId), + )(TransferInitial), ); function trimStringByMaxBytes(str: string, maxBytes: number) { diff --git a/src/components/transfer/TransferModal.tsx b/src/components/transfer/TransferModal.tsx index 5759dff4..c4bec414 100644 --- a/src/components/transfer/TransferModal.tsx +++ b/src/components/transfer/TransferModal.tsx @@ -1,38 +1,44 @@ -import React, { memo, useEffect, useMemo } from '../../lib/teact/teact'; +import React, { + memo, useEffect, useMemo, useState, +} from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; import type { GlobalState, HardwareConnectState, UserToken } from '../../global/types'; import { TransferState } from '../../global/types'; -import { ANIMATED_STICKER_SMALL_SIZE_PX, TON_TOKEN_SLUG } from '../../config'; -import { bigStrToHuman } from '../../global/helpers'; +import { IS_CAPACITOR } from '../../config'; import { selectCurrentAccountState, selectCurrentAccountTokens } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import captureKeyboardListeners from '../../util/captureKeyboardListeners'; -import { ANIMATED_STICKERS_PATHS } from '../ui/helpers/animatedAssets'; +import { formatCurrency } from '../../util/formatNumber'; +import resolveModalTransitionName from '../../util/resolveModalTransitionName'; +import { shortenAddress } from '../../util/shortenAddress'; +import { ASSET_LOGO_PATHS } from '../ui/helpers/assetLogos'; +import { useDeviceScreen } from '../../hooks/useDeviceScreen'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; import useModalTransitionKeys from '../../hooks/useModalTransitionKeys'; import usePrevious from '../../hooks/usePrevious'; +import useWindowSize from '../../hooks/useWindowSize'; -import TransferResult from '../common/TransferResult'; import LedgerConfirmOperation from '../ledger/LedgerConfirmOperation'; import LedgerConnect from '../ledger/LedgerConnect'; -import AmountWithFeeTextField from '../ui/AmountWithFeeTextField'; -import AnimatedIconWithPreview from '../ui/AnimatedIconWithPreview'; -import Button from '../ui/Button'; -import IconWithTooltip from '../ui/IconWithTooltip'; -import InteractiveTextField from '../ui/InteractiveTextField'; import Modal from '../ui/Modal'; import ModalHeader from '../ui/ModalHeader'; -import PasswordForm from '../ui/PasswordForm'; import Transition from '../ui/Transition'; +import TransferComplete from './TransferComplete'; +import TransferConfirm from './TransferConfirm'; import TransferInitial from './TransferInitial'; +import TransferPassword from './TransferPassword'; import modalStyles from '../ui/Modal.module.scss'; import styles from './Transfer.module.scss'; +interface OwnProps { + onQrScanPress?: NoneToVoidFunction; +} + interface StateProps { currentTransfer: GlobalState['currentTransfer']; tokens?: UserToken[]; @@ -42,42 +48,37 @@ interface StateProps { isTonAppConnected?: boolean; } -const AMOUNT_PRECISION = 4; +const SCREEN_HEIGHT_FOR_FORCE_FULLSIZE_NBS = 762; // Computed empirically function TransferModal({ currentTransfer: { state, amount, toAddress, - resolvedAddress, fee, comment, - shouldEncrypt, - promiseId, error, isLoading, txId, tokenSlug, - toAddressName, - isToNewAddress, - }, tokens, savedAddresses, hardwareState, isLedgerConnected, isTonAppConnected, -}: StateProps) { + }, tokens, savedAddresses, hardwareState, isLedgerConnected, isTonAppConnected, onQrScanPress, +}: OwnProps & StateProps) { const { submitTransferConfirm, submitTransferPassword, submitTransferHardware, setTransferScreen, - clearTransferError, cancelTransfer, showActivityInfo, - startTransfer, } = getActions(); const lang = useLang(); + const { isPortrait } = useDeviceScreen(); const isOpen = state !== TransferState.None; + const { screenHeight } = useWindowSize(); const selectedToken = useMemo(() => tokens?.find((token) => token.slug === tokenSlug), [tokenSlug, tokens]); - const renderedTokenBalance = usePrevious(selectedToken?.amount, true); + const [renderedTokenBalance, setRenderedTokenBalance] = useState(selectedToken?.amount); const renderedTransactionAmount = usePrevious(amount, true); const symbol = selectedToken?.symbol || ''; @@ -94,6 +95,8 @@ function TransferModal({ ), [state, submitTransferConfirm]); const handleTransferSubmit = useLastCallback((password: string) => { + setRenderedTokenBalance(selectedToken?.amount); + submitTransferPassword({ password }); }); @@ -107,143 +110,36 @@ function TransferModal({ }); const handleTransactionInfoClick = useLastCallback(() => { - cancelTransfer(); - showActivityInfo({ id: txId }); - }); - - const handleTransactionRepeatClick = useLastCallback(() => { - startTransfer({ - tokenSlug: tokenSlug || TON_TOKEN_SLUG, - toAddress, - amount, - comment, - }); + cancelTransfer({ shouldReset: true }); + showActivityInfo({ id: txId! }); }); const handleModalClose = useLastCallback(() => { - cancelTransfer(); + cancelTransfer({ shouldReset: isPortrait }); updateNextKey(); }); - function renderComment() { - if (!comment) { - return undefined; - } - - return ( - <> -
{shouldEncrypt ? lang('Encrypted Message') : lang('Comment')}
-
{comment}
- - ); - } - - function renderConfirm(isActive: boolean) { - const addressName = savedAddresses?.[toAddress!] || toAddressName; - - return ( - <> - -
- -
- {lang('Receiving Address')} - - {isToNewAddress && ( - - )} -
- - - - - {renderComment()} + const handleModalCloseWithReset = useLastCallback(() => { + cancelTransfer({ shouldReset: true }); + }); -
- {promiseId ? ( - - ) : ( - - )} - -
-
- - ); - } + const handleLedgerConnect = useLastCallback(() => { + submitTransferHardware(); + }); - function renderPassword(isActive: boolean) { - return ( - <> - - - - ); - } + function renderTransferShortInfo() { + const logoPath = selectedToken?.image || ASSET_LOGO_PATHS[symbol.toLowerCase() as keyof typeof ASSET_LOGO_PATHS]; - function renderComplete(isActive: boolean) { return ( - <> - - -
- - -
- -
-
- +
+ {symbol} + + {lang('%amount% to %address%', { + amount: {formatCurrency(amount!, symbol)}, + address: {shortenAddress(toAddress!)}, + })} + +
); } @@ -253,22 +149,41 @@ function TransferModal({ case TransferState.Initial: return ( <> - - + + ); case TransferState.Confirm: - return renderConfirm(isActive); + return ( + + ); case TransferState.Password: - return renderPassword(isActive); + return ( + + {IS_CAPACITOR ? renderTransferShortInfo() : undefined} + + ); case TransferState.ConnectHardware: return ( ); case TransferState.ConfirmHardware: @@ -276,26 +191,42 @@ function TransferModal({ ); case TransferState.Complete: - return renderComplete(isActive); + return ( + + ); } } return ( { +export default memo(withGlobal((global): StateProps => { const accountState = selectCurrentAccountState(global); const { diff --git a/src/components/transfer/TransferPassword.tsx b/src/components/transfer/TransferPassword.tsx new file mode 100644 index 00000000..eb8020a1 --- /dev/null +++ b/src/components/transfer/TransferPassword.tsx @@ -0,0 +1,56 @@ +import React, { memo, type TeactNode } from '../../lib/teact/teact'; +import { getActions } from '../../global'; + +import useHistoryBack from '../../hooks/useHistoryBack'; +import useLang from '../../hooks/useLang'; + +import ModalHeader from '../ui/ModalHeader'; +import PasswordForm from '../ui/PasswordForm'; + +interface OwnProps { + isActive: boolean; + isLoading?: boolean; + error?: string; + children?: TeactNode; + onSubmit: (password: string) => void; + onCancel: NoneToVoidFunction; +} + +function TransferPassword({ + isActive, isLoading, error, children, onSubmit, onCancel, +}: OwnProps) { + const { + cancelTransfer, + clearTransferError, + } = getActions(); + + const lang = useLang(); + + useHistoryBack({ + isActive, + onBack: onCancel, + }); + + return ( + <> + {!children && } + + {children} + + + ); +} + +export default memo(TransferPassword); diff --git a/src/components/ui/AmountWithFeeTextField.module.scss b/src/components/ui/AmountWithFeeTextField.module.scss index a32e8108..f8b2be3c 100644 --- a/src/components/ui/AmountWithFeeTextField.module.scss +++ b/src/components/ui/AmountWithFeeTextField.module.scss @@ -4,7 +4,7 @@ display: flex; box-sizing: border-box; - margin-bottom: 1rem; + margin-bottom: 1.25rem; padding: 0.875rem 0.75rem; font-size: 1rem; @@ -28,7 +28,7 @@ .label, .feeLabel { - margin-bottom: 0.3125rem; + margin-bottom: 0.5rem; padding: 0 0.5rem; font-size: 0.8125rem; @@ -40,7 +40,7 @@ .feeLabel { position: absolute; - top: -1.1875rem; + top: -1.375rem; right: 0.3125rem; } diff --git a/src/components/ui/AnimatedSticker.tsx b/src/components/ui/AnimatedSticker.tsx index c4865120..ed8ee16e 100644 --- a/src/components/ui/AnimatedSticker.tsx +++ b/src/components/ui/AnimatedSticker.tsx @@ -6,12 +6,12 @@ import React, { import type RLottieInstance from '../../lib/rlottie/RLottie'; -import { IS_ELECTRON } from '../../config'; import { requestMeasure } from '../../lib/fasterdom/fasterdom'; import { ensureRLottie, getRLottie } from '../../lib/rlottie/RLottie.async'; import buildClassName from '../../util/buildClassName'; import buildStyle from '../../util/buildStyle'; import generateUniqueId from '../../util/generateUniqueId'; +import { IS_ELECTRON } from '../../util/windowEnvironment'; import useBackgroundMode, { isBackgroundModeActive } from '../../hooks/useBackgroundMode'; import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps'; diff --git a/src/components/ui/Button.module.scss b/src/components/ui/Button.module.scss index c39d6be9..4a97cb12 100644 --- a/src/components/ui/Button.module.scss +++ b/src/components/ui/Button.module.scss @@ -10,6 +10,10 @@ text-decoration: none; white-space: nowrap; + &.loadingInit::after { + border-top-color: var(--color-gray-button-text); + } + background: none; border: 0; border-radius: 0; @@ -49,11 +53,13 @@ background-color: var(--color-gray-button-background); border-radius: var(--border-radius-buttons); - &:hover, - &:focus { - color: var(--color-gray-button-text-hover); + @media (hover: hover) { + &:hover, + &:focus { + color: var(--color-gray-button-text-hover); - background-color: var(--color-gray-button-background-hover); + background-color: var(--color-gray-button-background-hover); + } } &[disabled] { @@ -72,12 +78,17 @@ transition: color 150ms; - &:hover, - &:focus, &:active { color: var(--color-blue-button-background-hover); } + @media (hover: hover) { + &:hover, + &:focus{ + color: var(--color-blue-button-background-hover); + } + } + &[disabled] { opacity: 0.4; } @@ -92,11 +103,17 @@ background-color: var(--color-blue-button-background); - &:hover, - &:focus { - color: var(--color-blue-button-text-hover); + &.loadingInit::after { + border-top-color: var(--color-blue-button-text); + } + + @media (hover: hover) { + &:hover, + &:focus { + color: var(--color-blue-button-text-hover); - background-color: var(--color-blue-button-background-hover); + background-color: var(--color-blue-button-background-hover); + } } } @@ -120,11 +137,17 @@ background-color: var(--color-red-button-background); - &:hover, - &:focus { - color: var(--color-red-button-text-hover); + &.loadingInit::after { + border-top-color: var(--color-red-button-text); + } - background-color: var(--color-red-button-background-hover); + @media (hover: hover) { + &:hover, + &:focus { + color: var(--color-red-button-text-hover); + + background-color: var(--color-red-button-background-hover); + } } &[disabled] { @@ -140,9 +163,11 @@ transition: color 150ms; - &:hover, - &:focus { - color: var(--color-red-button-background-hover); + @media (hover: hover) { + &:hover, + &:focus { + color: var(--color-red-button-background-hover); + } } :global(html.animation-level-0) & { @@ -173,7 +198,7 @@ } } -.loading { +.loadingInit { position: relative; &::after { @@ -189,17 +214,28 @@ height: 1rem; margin: auto; + opacity: 0; border: 0.25rem solid transparent; - border-top-color: var(--color-white); border-radius: 50%; - animation: button-loading-spinner 1s linear infinite; + transition: opacity 150ms; } } -.buttonText { - visibility: hidden; - opacity: 0; +.loadingStart { + color: rgba(0,0,0,0) !important; + + &::after { + content: ""; + + opacity: 1; + } +} + +.loadingAnimation { + &::after { + animation: button-loading-spinner 1s linear infinite; + } } @keyframes button-loading-spinner { diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx index df987475..79517d76 100644 --- a/src/components/ui/Button.tsx +++ b/src/components/ui/Button.tsx @@ -4,6 +4,7 @@ import React, { memo, useState } from '../../lib/teact/teact'; import buildClassName from '../../util/buildClassName'; import useLastCallback from '../../hooks/useLastCallback'; +import useShowTransition from '../../hooks/useShowTransition'; import styles from './Button.module.scss'; @@ -29,6 +30,8 @@ type OwnProps = { // Longest animation duration const CLICKED_TIMEOUT = 400; +const LOADING_CLOSE_DURATION = 200; + function Button({ ref, children, @@ -49,6 +52,10 @@ function Button({ }: OwnProps) { const [isClicked, setIsClicked] = useState(false); + const { + shouldRender: shouldRenderLoading, + } = useShowTransition(isLoading, undefined, undefined, undefined, undefined, LOADING_CLOSE_DURATION); + const handleClick = useLastCallback(() => { if (!isDisabled && onClick) { onClick(); @@ -60,6 +67,12 @@ function Button({ }, CLICKED_TIMEOUT); }); + const loadingClassName = buildClassName( + isLoading !== undefined && styles.loadingInit, + isLoading && styles.loadingStart, + shouldRenderLoading && styles.loadingAnimation, + ); + return ( ); } diff --git a/src/components/ui/ConfettiContainer.module.scss b/src/components/ui/ConfettiContainer.module.scss new file mode 100644 index 00000000..93984cb9 --- /dev/null +++ b/src/components/ui/ConfettiContainer.module.scss @@ -0,0 +1,13 @@ +.root { + pointer-events: none; + touch-action: none; + + position: fixed; + z-index: var(--z-confetti); + top: 0; + right: 0; + bottom: 0; + left: 0; + + overflow: hidden; +} diff --git a/src/components/ui/ConfettiContainer.tsx b/src/components/ui/ConfettiContainer.tsx new file mode 100644 index 00000000..ff21e6ca --- /dev/null +++ b/src/components/ui/ConfettiContainer.tsx @@ -0,0 +1,199 @@ +import React, { memo, useRef } from '../../lib/teact/teact'; +import { withGlobal } from '../../global'; + +import { requestMeasure } from '../../lib/fasterdom/fasterdom'; + +import { useDeviceScreen } from '../../hooks/useDeviceScreen'; +import useForceUpdate from '../../hooks/useForceUpdate'; +import useLastCallback from '../../hooks/useLastCallback'; +import useSyncEffect from '../../hooks/useSyncEffect'; +import useWindowSize from '../../hooks/useWindowSize'; + +import styles from './ConfettiContainer.module.scss'; + +type StateProps = { + lastRequestedAt?: number; +}; + +interface Confetti { + pos: { + x: number; + y: number; + }; + velocity: { + x: number; + y: number; + }; + size: number; + color: string; + flicker: number; + flickerFrequency: number; + rotation: number; + lastDrawnAt: number; + frameCount: number; +} + +const CONFETTI_FADEOUT_TIMEOUT = 10000; +const DEFAULT_CONFETTI_SIZE = 10; +const CONFETTI_COLORS = ['#E8BC2C', '#D0049E', '#02CBFE', '#5723FD', '#FE8C27', '#6CB859']; + +function ConfettiContainer({ lastRequestedAt }: StateProps) { + // eslint-disable-next-line no-null/no-null + const canvasRef = useRef(null); + const confettiRef = useRef([]); + const isRafStartedRef = useRef(false); + const windowSize = useWindowSize(); + const forceUpdate = useForceUpdate(); + const { isPortrait } = useDeviceScreen(); + + const defaultConfettiAmount = isPortrait ? 50 : 100; + + const invokeGenerateConfetti = useLastCallback((w: number, h: number) => { + generateConfetti(confettiRef, w, h, defaultConfettiAmount); + }); + + const updateCanvas = useLastCallback(() => { + if (!canvasRef.current || !isRafStartedRef.current) { + return; + } + const canvas = canvasRef.current; + + const ctx = canvas.getContext('2d'); + if (!ctx) { + return; + } + + const { width: canvasWidth, height: canvasHeight } = canvas; + ctx.clearRect(0, 0, canvasWidth, canvasHeight); + + const confettiToRemove: Set = new Set([]); + confettiRef.current.forEach((c, i) => { + const { + pos, + velocity, + size, + color, + flicker, + flickerFrequency, + rotation, + lastDrawnAt, + frameCount, + } = c; + const diff = (Date.now() - lastDrawnAt) / 1000; + + const newPos = { + x: pos.x + velocity.x * diff, + y: pos.y + velocity.y * diff, + }; + + const newVelocity = { + x: velocity.x * 0.98, // Air Resistance + y: velocity.y += diff * 1000, // Gravity + }; + + const newFlicker = size * Math.abs(Math.sin(frameCount * flickerFrequency)); + const newRotation = 5 * frameCount * flickerFrequency * (Math.PI / 180); + + const newFrameCount = frameCount + 1; + const newLastDrawnAt = Date.now(); + + const shouldRemove = newPos.y > canvasHeight + c.size; + if (shouldRemove) { + confettiToRemove.add(c); + return; + } + + confettiRef.current[i] = { + ...c, + pos: newPos, + velocity: newVelocity, + flicker: newFlicker, + rotation: newRotation, + lastDrawnAt: newLastDrawnAt, + frameCount: newFrameCount, + }; + ctx.fillStyle = color; + ctx.beginPath(); + ctx.ellipse( + pos.x, + pos.y, + size, + flicker, + rotation, + 0, + 2 * Math.PI, + ); + ctx.fill(); + }); + + confettiRef.current = confettiRef.current.filter((c) => !confettiToRemove.has(c)); + if (confettiRef.current.length) { + requestMeasure(updateCanvas); + } else { + isRafStartedRef.current = false; + } + }); + + useSyncEffect(([prevConfettiTime]) => { + let hideTimeout: number; + if (lastRequestedAt && prevConfettiTime !== lastRequestedAt) { + invokeGenerateConfetti(windowSize.width, windowSize.height); + hideTimeout = window.setTimeout(forceUpdate, CONFETTI_FADEOUT_TIMEOUT); + + if (!isRafStartedRef.current) { + isRafStartedRef.current = true; + requestMeasure(updateCanvas); + } + } + + return () => { + window.clearTimeout(hideTimeout); + }; + // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- Old timeout should be cleared only if new confetti is generated + }, [lastRequestedAt, forceUpdate, updateCanvas]); + + if (!lastRequestedAt || Date.now() - lastRequestedAt > CONFETTI_FADEOUT_TIMEOUT) { + return undefined; + } + return ( +
+ +
+ ); +} + +export default memo(withGlobal((global): StateProps => { + return { + lastRequestedAt: global.confettiRequestedAt, + }; +})(ConfettiContainer)); + +function generateConfetti(confettiRef: { current: Confetti[] }, width : number, height: number, amount: number) { + for (let i = 0; i < amount; i++) { + const leftSide = i % 2; + const pos = { + x: width * (leftSide ? -0.1 : 1.1), + y: height * 0.75, + }; + const randomX = Math.random() * width * 1.5; + const randomY = -height / 2 - Math.random() * height; + const velocity = { + x: leftSide ? randomX : randomX * -1, + y: randomY, + }; + + const randomColor = CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)]; + const size = DEFAULT_CONFETTI_SIZE; + confettiRef.current.push({ + pos, + size, + color: randomColor, + velocity, + flicker: size, + flickerFrequency: Math.random() * 0.2, + rotation: 0, + lastDrawnAt: Date.now(), + frameCount: 0, + }); + } +} diff --git a/src/components/ui/CreatePasswordForm.tsx b/src/components/ui/CreatePasswordForm.tsx new file mode 100644 index 00000000..ff045f5f --- /dev/null +++ b/src/components/ui/CreatePasswordForm.tsx @@ -0,0 +1,258 @@ +import React, { + memo, useEffect, useRef, useState, +} from '../../lib/teact/teact'; + +import { PIN_LENGTH } from '../../config'; +import buildClassName from '../../util/buildClassName'; +import { IS_ANDROID, IS_IOS } from '../../util/windowEnvironment'; + +import useFlag from '../../hooks/useFlag'; +import useFocusAfterAnimation from '../../hooks/useFocusAfterAnimation'; +import useLang from '../../hooks/useLang'; +import useLastCallback from '../../hooks/useLastCallback'; +import { usePasswordValidation } from '../../hooks/usePasswordValidation'; + +import Button from './Button'; +import Input from './Input'; +import Modal from './Modal'; + +import styles from '../auth/Auth.module.scss'; +import modalStyles from './Modal.module.scss'; + +interface OwnProps { + isActive?: boolean; + isLoading?: boolean; + formId: string; + onCancel: NoneToVoidFunction; + onSubmit: (password: string, isPasswordNumeric: boolean) => void; +} + +function CreatePasswordForm({ + isActive, isLoading, formId, onCancel, onSubmit, +}: OwnProps) { + const lang = useLang(); + const isMobile = IS_IOS || IS_ANDROID; + + // eslint-disable-next-line no-null/no-null + const firstInputRef = useRef(null); + + const [isJustSubmitted, setIsJustSubmitted] = useState(false); + const [firstPassword, setFirstPassword] = useState(''); + const [secondPassword, setSecondPassword] = useState(''); + const [isPasswordFocused, markPasswordFocused, unmarkPasswordFocused] = useFlag(false); + const [isWeakPasswordModalOpen, openWeakPasswordModal, closeWeakPasswordModal] = useFlag(false); + + const [hasError, setHasError] = useState(false); + const [isPasswordsNotEqual, setIsPasswordsNotEqual] = useState(false); + const [isSecondPasswordFocused, markSecondPasswordFocused, unmarkSecondPasswordFocused] = useFlag(false); + const canSubmit = isActive && firstPassword.length > 0 && secondPassword.length > 0 && !hasError; + + const shouldRenderError = hasError && !isPasswordFocused; + + const validation = usePasswordValidation({ + firstPassword, + secondPassword, + isOnlyNumbers: isMobile, + requiredLength: isMobile ? PIN_LENGTH : undefined, + }); + + useFocusAfterAnimation(firstInputRef, !isActive); + + useEffect(() => { + setIsPasswordsNotEqual(false); + if (firstPassword === '' || !isActive || isPasswordFocused) { + setHasError(false); + return; + } + + const { noEqual, invalidLength } = validation; + + if ((!isSecondPasswordFocused || isJustSubmitted) && noEqual && secondPassword !== '') { + setHasError(true); + setIsPasswordsNotEqual(true); + } else if (!noEqual || secondPassword === '' || (isSecondPasswordFocused && !isJustSubmitted)) { + setHasError(false); + } + if (isMobile && invalidLength && !isJustSubmitted) { + setHasError(true); + } + }, [ + isActive, firstPassword, secondPassword, validation, isSecondPasswordFocused, isPasswordFocused, isJustSubmitted, + isMobile, + ]); + + const handleFirstPasswordChange = useLastCallback((value: string) => { + setFirstPassword(value); + if (isJustSubmitted) { + setIsJustSubmitted(false); + } + }); + + const handleSecondPasswordChange = useLastCallback((value: string) => { + setSecondPassword(value); + if (isJustSubmitted) { + setIsJustSubmitted(false); + } + }); + + const handleSubmit = useLastCallback((e: React.FormEvent) => { + e.preventDefault(); + e.stopPropagation(); + + if (!canSubmit) { + return; + } + + if (firstPassword !== secondPassword) { + setIsJustSubmitted(true); + setHasError(true); + setIsPasswordsNotEqual(true); + return; + } + + const isWeakPassword = Object.values(validation).find((rule) => rule); + + if (!isMobile && isWeakPassword && !isWeakPasswordModalOpen) { + openWeakPasswordModal(); + return; + } + + if (isWeakPasswordModalOpen) { + closeWeakPasswordModal(); + } + onSubmit(firstPassword, isMobile); + }); + + function renderErrors() { + if (isPasswordsNotEqual) { + return ( +
+ {lang('Passwords must be equal.')} +
+ ); + } + + const { + invalidLength, + noUpperCase, + noLowerCase, + noNumber, + noSpecialChar, + } = validation; + + if (isMobile) { + return ( +
+ + {lang('Password must contain %length% digits.', { length: PIN_LENGTH })} + +
+ ); + } + + return ( +
+ {lang('To protect your wallet as much as possible, use a password with')} + + {' '}{lang('$auth_password_rule_8chars')}, + + + {' '}{lang('$auth_password_rule_one_small_char')}, + + + {' '}{lang('$auth_password_rule_one_capital_char')}, + + + {' '}{lang('$auth_password_rule_one_digit')}, + + + {' '}{lang('$auth_password_rule_one_special_char')} + . +
+ ); + } + + return ( + <> + +
+ + +
+ + {renderErrors()} + +
+ + +
+ + + +

+ {lang('Your have entered an insecure password, which can be easily guessed by scammers.')} +

+

+ {lang('Continue or change password to something more secure?')} +

+
+ + +
+
+ + ); +} + +export default memo(CreatePasswordForm); + +function getValidationRuleClass(shouldRenderError: boolean, ruleHasError: boolean) { + return buildClassName( + styles.passwordRule, + !ruleHasError ? styles.valid : shouldRenderError ? styles.invalid : undefined, + ); +} diff --git a/src/components/ui/Draggable.tsx b/src/components/ui/Draggable.tsx index 2922f9ec..a6b02105 100644 --- a/src/components/ui/Draggable.tsx +++ b/src/components/ui/Draggable.tsx @@ -5,6 +5,7 @@ import React, { import buildClassName from '../../util/buildClassName'; import buildStyle from '../../util/buildStyle'; +import { clamp } from '../../util/math'; import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; @@ -25,6 +26,7 @@ type Offset = { type DraggableState = { isDragging: boolean; + scrollTop: number; origin: TPoint; translation: TPoint; width?: number; @@ -41,8 +43,9 @@ type OwnProps = { isDisabled?: boolean; offset?: Offset; parentRef?: RefObject; + scrollRef?: RefObject; className?: string; - onClick: NoneToVoidFunction; + onClick: (e: React.MouseEvent | React.TouchEvent) => void; }; const ZERO_POINT: TPoint = { x: 0, y: 0 }; @@ -54,6 +57,8 @@ const DEFAULT_OFFSET: Offset = { right: 0, }; +const EDGE_THRESHOLD = 150; + function Draggable({ children, id, @@ -64,6 +69,7 @@ function Draggable({ isDisabled, offset = DEFAULT_OFFSET, parentRef, + scrollRef, className, onClick, }: OwnProps) { @@ -73,61 +79,110 @@ function Draggable({ // eslint-disable-next-line no-null/no-null const buttonRef = useRef(null); + const scrollIntervalId = useRef(); + const [state, setState] = useState({ isDragging: false, + scrollTop: 0, origin: ZERO_POINT, translation: ZERO_POINT, }); - const handleMouseDown = (e: React.MouseEvent) => { - const { x, y } = getClientCoordinate(e); + const lastMousePosition = useRef({ x: 0, y: 0 }); + + const updateDraggablePosition = () => { + if (!state.isDragging || !ref.current || !parentRef?.current || !scrollRef?.current) return; + + const translation = calculateConstrainedTranslation( + state, + lastMousePosition.current, + scrollRef.current.scrollTop, + ref.current, + parentRef.current, + offset, + ); + + setState((current) => ({ + ...current, + translation, + })); + + onDrag(translation, id); + }; + + const stopContinuousScroll = () => { + if (scrollIntervalId.current !== undefined) { + cancelAnimationFrame(scrollIntervalId.current); + scrollIntervalId.current = undefined; + } + }; + + const startContinuousScroll = (scrollContainer: HTMLElement, speed: number) => { + const animateScroll = () => { + scrollContainer.scrollBy(0, speed); + updateDraggablePosition(); + scrollIntervalId.current = requestAnimationFrame(animateScroll); + }; + + stopContinuousScroll(); + animateScroll(); + }; + + const setInitialState = (e: React.MouseEvent | TouchEvent) => { + const origin = getClientCoordinate(e); + const scrollTop = scrollRef?.current?.scrollTop ?? 0; setState({ ...state, isDragging: true, - origin: { x, y }, + origin, + scrollTop, width: ref.current?.offsetWidth, height: ref.current?.offsetHeight, }); }; + const handleMouseDown = (e: React.MouseEvent) => { + setInitialState(e); + }; + const handleTouchStart = useLastCallback((e: TouchEvent) => { e.stopPropagation(); e.preventDefault(); - const { x, y } = getClientCoordinate(e); - - setState({ - ...state, - isDragging: true, - origin: { x, y }, - width: ref.current?.offsetWidth, - height: ref.current?.offsetHeight, - }); + setInitialState(e); }); const handleMouseMove = useLastCallback((e: MouseEvent | TouchEvent) => { - const { x, y } = getClientCoordinate(e); - - const translation = { - x: x - state.origin.x, - y: y - state.origin.y, - }; + if (!ref.current || !parentRef?.current || !scrollRef?.current) return; - if (parentRef && parentRef?.current && ref.current) { - const { - top = 0, right = 0, bottom = 0, left = 0, - } = offset; - const parentRect = parentRef.current.getBoundingClientRect(); - const draggableRect = ref.current.getBoundingClientRect(); - - const minX = -ref.current.offsetLeft + left; - const maxX = parentRect.width - draggableRect.width - ref.current.offsetLeft + right; - - const minY = -ref.current.offsetTop + top; - const maxY = parentRect.height - draggableRect.height - ref.current.offsetTop + bottom; + const { x, y } = getClientCoordinate(e); + lastMousePosition.current = { x, y }; + + const translation = calculateConstrainedTranslation( + state, + lastMousePosition.current, + scrollRef.current.scrollTop, + ref.current, + parentRef.current, + offset, + ); - translation.x = Math.max(minX, Math.min(translation.x, maxX)); - translation.y = Math.max(minY, Math.min(translation.y, maxY)); + const scrollRect = scrollRef.current.getBoundingClientRect(); + const distanceFromTop = y - scrollRect.top; + const distanceFromBottom = scrollRect.bottom - y; + + if (distanceFromTop < EDGE_THRESHOLD) { + startContinuousScroll( + scrollRef.current, + -scaledEase((EDGE_THRESHOLD - distanceFromTop) / EDGE_THRESHOLD), + ); + } else if (distanceFromBottom < EDGE_THRESHOLD) { + startContinuousScroll( + scrollRef.current, + scaledEase((EDGE_THRESHOLD - distanceFromBottom) / EDGE_THRESHOLD), + ); + } else { + stopContinuousScroll(); } setState((current) => ({ @@ -149,23 +204,12 @@ function Draggable({ onDragEnd(); }); - useEffect(() => { - if (state.isDragging && isDisabled) { - setState((current) => ({ - ...current, - isDragging: false, - width: undefined, - height: undefined, - })); - } - }, [isDisabled, state.isDragging]); - - const handleClick = useLastCallback(() => { + const handleClick = useLastCallback((e: React.MouseEvent | React.TouchEvent) => { if (state.isDragging) { return; } - onClick(); + onClick(e); }); useEffect(() => { @@ -182,6 +226,7 @@ function Draggable({ window.addEventListener('touchcancel', handleMouseUp); window.addEventListener('mouseup', handleMouseUp); } else { + stopContinuousScroll(); window.removeEventListener('touchmove', handleMouseMove); window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('touchend', handleMouseUp); @@ -196,6 +241,7 @@ function Draggable({ return () => { if (state.isDragging) { + stopContinuousScroll(); window.removeEventListener('touchmove', handleMouseMove); window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('touchend', handleMouseUp); @@ -207,7 +253,7 @@ function Draggable({ } } }; - }, [handleMouseMove, handleMouseUp, handleTouchStart, state.isDragging, buttonRef]); + }, [handleMouseMove, handleMouseUp, handleTouchStart, state.isDragging, buttonRef, isDisabled]); const fullClassName = buildClassName(styles.container, className, state.isDragging && styles.isDragging); @@ -257,3 +303,38 @@ function getClientCoordinate(e: MouseEvent | TouchEvent | React.MouseEvent | Rea return { x, y }; } + +function scaledEase(n: number) { + return 5 * (n ** 3) + 1; +} + +function calculateConstrainedTranslation( + state: DraggableState, + lastMousePosition: TPoint, + lastScrollTop: number, + draggableElement: HTMLDivElement, + parentElement: HTMLDivElement, + offset: Offset, +) { + const translation = { + x: lastMousePosition.x - state.origin.x, + y: lastMousePosition.y - state.origin.y + lastScrollTop - state.scrollTop, + }; + + const { + top = 0, right = 0, bottom = 0, left = 0, + } = offset; + + const parentRect = parentElement.getBoundingClientRect(); + const draggableRect = draggableElement.getBoundingClientRect(); + + const minX = -draggableElement.offsetLeft + left; + const maxX = parentRect.width - draggableRect.width - draggableElement.offsetLeft + right; + const minY = -draggableElement.offsetTop + top; + const maxY = parentRect.height - draggableRect.height - draggableElement.offsetTop + bottom; + + return { + x: clamp(translation.x, minX, maxX), + y: clamp(translation.y, minY, maxY), + }; +} diff --git a/src/components/ui/Dropdown.module.scss b/src/components/ui/Dropdown.module.scss index 88cd097b..ddce8fdd 100644 --- a/src/components/ui/Dropdown.module.scss +++ b/src/components/ui/Dropdown.module.scss @@ -56,9 +56,17 @@ transition: none !important; } - &:hover, - &:focus { - background-color: var(--color-input-button-background-hover); + @media (hover: hover) { + &:hover, + &:focus { + background-color: var(--color-input-button-background-hover); + } + } + + @media (pointer: coarse) { + &:active { + background-color: var(--color-input-button-background-hover); + } } } @@ -162,3 +170,14 @@ line-height: 1rem; } } + +.dropdownButtonWrapper { + display: flex; + + width: 5rem; + height: 1rem; +} + +.spinner { + --spinner-size: 1rem; +} diff --git a/src/components/ui/Dropdown.tsx b/src/components/ui/Dropdown.tsx index 2a7502cc..c9c293b5 100644 --- a/src/components/ui/Dropdown.tsx +++ b/src/components/ui/Dropdown.tsx @@ -5,6 +5,7 @@ import buildClassName from '../../util/buildClassName'; import useFlag from '../../hooks/useFlag'; import useLang from '../../hooks/useLang'; +import Loading from './Loading'; import Menu from './Menu'; import styles from './Dropdown.module.scss'; @@ -27,6 +28,7 @@ interface OwnProps { disabled?: boolean; shouldTranslateOptions?: boolean; onChange?: (value: string) => void; + isLoading?: boolean; } const DEFAULT_ARROW = 'caret'; @@ -44,6 +46,7 @@ function Dropdown({ disabled, shouldTranslateOptions, onChange, + isLoading = false, }: OwnProps): TeactJsx { const lang = useLang(); const [isMenuOpen, openMenu, closeMenu] = useFlag(); @@ -88,23 +91,29 @@ function Dropdown({ onClick={isFullyInteractive && !disabled && withMenu ? openMenu : undefined} > {label && {label}} - + + {isLoading ? ( + + ) : ( + + )} + {withMenu && ( diff --git a/src/components/ui/IconWithTooltip.module.scss b/src/components/ui/IconWithTooltip.module.scss index 0be2131d..446f8018 100644 --- a/src/components/ui/IconWithTooltip.module.scss +++ b/src/components/ui/IconWithTooltip.module.scss @@ -46,5 +46,5 @@ } .icon { - cursor: pointer; + cursor: var(--custom-cursor, pointer); } \ No newline at end of file diff --git a/src/components/ui/InfiniteScroll.tsx b/src/components/ui/InfiniteScroll.tsx new file mode 100644 index 00000000..7a3c9e76 --- /dev/null +++ b/src/components/ui/InfiniteScroll.tsx @@ -0,0 +1,279 @@ +import type { RefObject, UIEvent } from 'react'; +import type { FC } from '../../lib/teact/teact'; +import React, { + useEffect, useLayoutEffect, useMemo, useRef, +} from '../../lib/teact/teact'; + +import { LoadMoreDirection } from '../../global/types'; + +import { requestForcedReflow } from '../../lib/fasterdom/fasterdom'; +import buildStyle from '../../util/buildStyle'; +import resetScroll from '../../util/resetScroll'; +import { debounce } from '../../util/schedulers'; +import { IS_ANDROID } from '../../util/windowEnvironment'; + +import useLastCallback from '../../hooks/useLastCallback'; + +type OwnProps = { + ref?: RefObject; + scrollRef?: RefObject; + style?: string; + className?: string; + items?: any[]; + itemSelector?: string; + preloadBackwards?: number; + sensitiveArea?: number; + withAbsolutePositioning?: boolean; + maxHeight?: number; + noScrollRestore?: boolean; + noScrollRestoreOnTop?: boolean; + noFastList?: boolean; + cacheBuster?: any; + beforeChildren?: React.ReactNode; + children: React.ReactNode; + onLoadMore?: ({ direction }: { direction: LoadMoreDirection }) => void; + onScroll?: (e: UIEvent) => void; + onWheel?: (e: React.WheelEvent) => void; + onClick?: (e: React.MouseEvent) => void; + onKeyDown?: (e: React.KeyboardEvent) => void; + onDragOver?: (e: React.DragEvent) => void; + onDragLeave?: (e: React.DragEvent) => void; +}; + +const DEFAULT_LIST_SELECTOR = '.ListItem'; +const DEFAULT_PRELOAD_BACKWARDS = 20; +const DEFAULT_SENSITIVE_AREA = 800; + +const InfiniteScroll: FC = ({ + ref, + scrollRef, + style, + className, + items, + itemSelector = DEFAULT_LIST_SELECTOR, + preloadBackwards = DEFAULT_PRELOAD_BACKWARDS, + sensitiveArea = DEFAULT_SENSITIVE_AREA, + withAbsolutePositioning, + maxHeight, + // Used to turn off restoring scroll position (e.g. for frequently re-ordered chat or user lists) + noScrollRestore = false, + noScrollRestoreOnTop = false, + noFastList, + // Used to re-query `listItemElements` if rendering is delayed by transition + cacheBuster, + beforeChildren, + children, + onLoadMore, + onScroll, + onWheel, + onClick, + onKeyDown, + onDragOver, + onDragLeave, +}: OwnProps) => { + // eslint-disable-next-line no-null/no-null + let containerRef = useRef(null); + if (ref) { + containerRef = ref; + } + + const stateRef = useRef<{ + listItemElements?: NodeListOf; + isScrollTopJustUpdated?: boolean; + currentAnchor?: HTMLDivElement | undefined; + currentAnchorTop?: number; + }>({}); + + const [loadMoreBackwards, loadMoreForwards] = useMemo(() => { + if (!onLoadMore) { + return []; + } + + return [ + debounce(() => { + onLoadMore({ direction: LoadMoreDirection.Backwards }); + }, 1000, true, false), + debounce(() => { + onLoadMore({ direction: LoadMoreDirection.Forwards }); + }, 1000, true, false), + ]; + // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps + }, [onLoadMore, items]); + + // Initial preload + useEffect(() => { + if (!loadMoreBackwards) { + return; + } + + if (preloadBackwards > 0 && (!items || items.length < preloadBackwards)) { + loadMoreBackwards(); + return; + } + + const { scrollHeight, clientHeight } = scrollRef?.current ?? containerRef.current!; + if (clientHeight && scrollHeight <= clientHeight) { + loadMoreBackwards(); + } + }, [items, loadMoreBackwards, preloadBackwards, scrollRef]); + + // Restore `scrollTop` after adding items + useLayoutEffect(() => { + requestForcedReflow(() => { + const container = scrollRef?.current ?? containerRef.current!; + const state = stateRef.current; + + state.listItemElements = container.querySelectorAll(itemSelector); + + let newScrollTop: number; + + if (state.currentAnchor && Array.from(state.listItemElements).includes(state.currentAnchor)) { + const { scrollTop } = container; + const newAnchorTop = state.currentAnchor!.getBoundingClientRect().top; + newScrollTop = scrollTop + (newAnchorTop - state.currentAnchorTop!); + } else { + const nextAnchor = state.listItemElements[0]; + if (nextAnchor) { + state.currentAnchor = nextAnchor; + state.currentAnchorTop = nextAnchor.getBoundingClientRect().top; + } + } + + if (withAbsolutePositioning || noScrollRestore) { + return undefined; + } + + const { scrollTop } = container; + if (noScrollRestoreOnTop && scrollTop === 0) { + return undefined; + } + + return () => { + resetScroll(container, newScrollTop); + + state.isScrollTopJustUpdated = true; + }; + }); + }, [items, itemSelector, noScrollRestore, noScrollRestoreOnTop, cacheBuster, withAbsolutePositioning, scrollRef]); + + const handleScroll = useLastCallback((e: UIEvent) => { + if (loadMoreForwards && loadMoreBackwards) { + const { + isScrollTopJustUpdated, currentAnchor, currentAnchorTop, + } = stateRef.current; + const listItemElements = stateRef.current.listItemElements!; + + if (isScrollTopJustUpdated) { + stateRef.current.isScrollTopJustUpdated = false; + return; + } + + const listLength = listItemElements.length; + const container = scrollRef?.current ?? containerRef.current!; + const { scrollTop, scrollHeight, offsetHeight } = container; + const top = listLength ? listItemElements[0].offsetTop : 0; + const isNearTop = scrollTop <= top + sensitiveArea; + const bottom = listLength + ? listItemElements[listLength - 1].offsetTop + listItemElements[listLength - 1].offsetHeight + : scrollHeight; + const isNearBottom = bottom - (scrollTop + offsetHeight) <= sensitiveArea; + let isUpdated = false; + + if (isNearTop) { + const nextAnchor = listItemElements[0]; + if (nextAnchor) { + const nextAnchorTop = nextAnchor.getBoundingClientRect().top; + const newAnchorTop = currentAnchor?.offsetParent && currentAnchor !== nextAnchor + ? currentAnchor.getBoundingClientRect().top + : nextAnchorTop; + const isMovingUp = ( + currentAnchor && currentAnchorTop !== undefined && newAnchorTop > currentAnchorTop + ); + + if (isMovingUp) { + stateRef.current.currentAnchor = nextAnchor; + stateRef.current.currentAnchorTop = nextAnchorTop; + isUpdated = true; + loadMoreForwards(); + } + } + } + + if (isNearBottom) { + const nextAnchor = listItemElements[listLength - 1]; + if (nextAnchor) { + const nextAnchorTop = nextAnchor.getBoundingClientRect().top; + const newAnchorTop = currentAnchor?.offsetParent && currentAnchor !== nextAnchor + ? currentAnchor.getBoundingClientRect().top + : nextAnchorTop; + const isMovingDown = ( + currentAnchor && currentAnchorTop !== undefined && newAnchorTop < currentAnchorTop + ); + + if (isMovingDown) { + stateRef.current.currentAnchor = nextAnchor; + stateRef.current.currentAnchorTop = nextAnchorTop; + isUpdated = true; + loadMoreBackwards(); + } + } + } + + if (!isUpdated) { + if (currentAnchor?.offsetParent) { + stateRef.current.currentAnchorTop = currentAnchor.getBoundingClientRect().top; + } else { + const nextAnchor = listItemElements[0]; + + if (nextAnchor) { + stateRef.current.currentAnchor = nextAnchor; + stateRef.current.currentAnchorTop = nextAnchor.getBoundingClientRect().top; + } + } + } + } + + if (onScroll) { + onScroll(e); + } + }); + + useLayoutEffect(() => { + const scrollContainerRef = scrollRef?.current; + const handleNativeScroll = (e: Event) => handleScroll(e as unknown as UIEvent); + if (scrollContainerRef) { + scrollContainerRef.addEventListener('scroll', handleNativeScroll); + } + + return () => { + scrollContainerRef?.removeEventListener('scroll', handleNativeScroll); + }; + }, [handleScroll, scrollRef]); + + return ( +
+ {beforeChildren} + {withAbsolutePositioning && items?.length ? ( +
+ {children} +
+ ) : children} +
+ ); +}; + +export default InfiniteScroll; diff --git a/src/components/ui/Input.module.scss b/src/components/ui/Input.module.scss index 17d26366..2e5f69e5 100644 --- a/src/components/ui/Input.module.scss +++ b/src/components/ui/Input.module.scss @@ -1,7 +1,7 @@ .wrapper { position: relative; - margin-bottom: 1rem; + margin-bottom: 1.25rem; } .input { @@ -117,6 +117,8 @@ line-height: 1; white-space: nowrap; + transition: color 150ms; + /* Fix for ContentEditable multiline "issues" */ br { display: none; @@ -162,6 +164,11 @@ } } +.isLoading { + font-size: 3rem; + color: var(--color-gray-4); +} + textarea.input { resize: none; @@ -172,6 +179,10 @@ textarea.input { line-height: 1.3125rem; } +.inputWrapperStatic { + box-shadow: inset 0 0 0 0.0625rem var(--color-separator-input-stroke); +} + .input__wrapper { display: flex; @@ -242,7 +253,7 @@ textarea.input { display: block; - margin-bottom: 0.3125rem; + margin-bottom: 0.5rem; padding: 0 0.5rem; font-size: 0.8125rem; @@ -286,3 +297,15 @@ textarea.input { white-space: normal; } } + +.swapCorner { + &::before { + box-shadow: 0 0 0 0.125rem var(--color-blue) !important; + } + + &_error { + &::before { + box-shadow: 0 0 0 0.125rem var(--color-red) !important; + } + } +} diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx index 8f640375..df5c5a4c 100644 --- a/src/components/ui/Input.tsx +++ b/src/components/ui/Input.tsx @@ -18,6 +18,7 @@ type OwnProps = { label?: TeactNode; placeholder?: string; value?: string | number; + inputMode?: 'numeric' | 'text' | 'search'; maxLength?: number; isRequired?: boolean; isControlled?: boolean; @@ -31,8 +32,8 @@ type OwnProps = { children?: TeactNode; onInput: (value: string, inputArg?: any) => void; onKeyDown?: (e: KeyboardEvent) => void; - onFocus?: NoneToVoidFunction; - onBlur?: NoneToVoidFunction; + onFocus?: (e: React.FocusEvent) => void; + onBlur?: (e: React.FocusEvent) => void; }; function Input({ @@ -40,6 +41,7 @@ function Input({ id, label, placeholder, + inputMode, isRequired, isControlled, isMultiline, @@ -127,6 +129,7 @@ function Input({ className={inputFullClass} type={finalType} value={value} + inputMode={inputMode} maxLength={maxLength} autoComplete={autoComplete} onInput={handleInput} diff --git a/src/components/ui/InteractiveTextField.tsx b/src/components/ui/InteractiveTextField.tsx index 0c69dcda..915e6344 100644 --- a/src/components/ui/InteractiveTextField.tsx +++ b/src/components/ui/InteractiveTextField.tsx @@ -3,9 +3,10 @@ import React, { } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; -import { TONSCAN_BASE_MAINNET_URL, TONSCAN_BASE_TESTNET_URL } from '../../config'; +import { IS_CAPACITOR, TONSCAN_BASE_MAINNET_URL, TONSCAN_BASE_TESTNET_URL } from '../../config'; import { selectCurrentAccountState } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; +import { vibrateOnSuccess } from '../../util/capacitor'; import captureKeyboardListeners from '../../util/captureKeyboardListeners'; import { copyTextToClipboard } from '../../util/clipboard'; import { shortenAddress } from '../../util/shortenAddress'; @@ -35,6 +36,7 @@ interface OwnProps { className?: string; textClassName?: string; noSavedAddress?: boolean; + noExplorer?: boolean; } interface StateProps { @@ -53,6 +55,7 @@ function InteractiveTextField({ spoilerCallback, copyNotification, noSavedAddress, + noExplorer, className, textClassName, isAddressAlreadySaved, @@ -83,7 +86,7 @@ function InteractiveTextField({ } addSavedAddress({ address, name: savedAddressName }); - showNotification({ message: 'Address was saved!', icon: 'icon-star' }); + showNotification({ message: lang('Address was saved!'), icon: 'icon-star' }); closeSaveAddressModal(); }); @@ -99,7 +102,10 @@ function InteractiveTextField({ const handleCopy = useLastCallback(() => { showNotification({ message: copyNotification, icon: 'icon-copy' }); - copyTextToClipboard(address || text || ''); + void copyTextToClipboard(address || text || ''); + if (IS_CAPACITOR) { + void vibrateOnSuccess(); + } }); const handleRevealSpoiler = useLastCallback(() => { @@ -209,7 +215,7 @@ function InteractiveTextField({ )} - {address && ( + {!noExplorer && address && ( = ({ // eslint-disable-next-line no-null/no-null const menuRef = useRef(null); + useHistoryBack({ + isActive: Boolean(isOpen && onClose), + onBack: onClose!, + shouldBeReplaced: true, + }); + const { transitionClassNames, } = useShowTransition( diff --git a/src/components/ui/MenuItem.module.scss b/src/components/ui/MenuItem.module.scss index e7b59957..9ea67b30 100644 --- a/src/components/ui/MenuItem.module.scss +++ b/src/components/ui/MenuItem.module.scss @@ -44,12 +44,23 @@ } } - &:hover, - &:focus { - color: var(--color-blue); - text-decoration: none; + @media (hover: hover) { + &:hover, + &:focus { + color: var(--color-blue); + text-decoration: none; - background-color: var(--color-interactive-popup-menu-hover); + background-color: var(--color-interactive-popup-menu-hover); + } + } + + @media (pointer: coarse) { + &:active { + color: var(--color-blue); + text-decoration: none; + + background-color: var(--color-interactive-popup-menu-hover); + } } &:active { diff --git a/src/components/ui/Modal.module.scss b/src/components/ui/Modal.module.scss index 122d3981..669bc13c 100644 --- a/src/components/ui/Modal.module.scss +++ b/src/components/ui/Modal.module.scss @@ -6,44 +6,178 @@ position: relative; z-index: var(--z-modal); - &.slideUpAnimation { - --transition: 500ms cubic-bezier(0.3, 0.8, 0.2, 1); - } - &.error { .dialog { max-width: 23rem; } } - &:global(.open) .backdrop, + .dialog { + transform: translateY(-1rem); + + opacity: 0; + + transition: transform var(--transition), opacity var(--transition); + + :global(html.animation-level-0) & { + transform: translateY(0); + + transition: var(--no-animation-transition); + } + } + &:global(.open) .dialog { + transform: translateY(0); + opacity: 1; } - &:not(.slideUpAnimation):global(.open) .dialog { - transform: translate3d(0, 0, 0); + &:global(.closing) .dialog { + transform: translateY(1rem); } - :global(html:not(.animation-level-0)) &:not(.slideUpAnimation):global(.closing) .dialog { - transform: translate3d(0, 1rem, 0); + .backdrop { + opacity: 0; + + transition: opacity var(--transition); + + :global(html.animation-level-0) & { + transition: var(--no-animation-transition); + } } - &.slideUpAnimation:global(.shown) { - .container { - overflow: hidden; + &:global(.open) .backdrop { + opacity: 1; + } +} + +.delegatingToNative { + display: none; +} + +.slideUpAnimation { + --transition: 500ms cubic-bezier(0.3, 0.8, 0.2, 1); + + .container { + overflow: hidden; + align-items: flex-end; + + padding: 0; + } + + // Disable catching events on closing modals + &:global(.closing) .container { + pointer-events: none; + } + + .dialog { + transform: translateY(100%); + + max-width: 28rem; + max-height: calc(95 * var(--vh, 1vh)); + margin: 0; + + background: var(--color-background-second); + border-radius: var(--border-radius-default) var(--border-radius-default) 0 0; + + transition: transform var(--transition); + + :global(html:not(.animation-level-0)) & { + opacity: 1; + } + + :global(html.animation-level-0) & { + transition: var(--no-animation-transition); } + > :global(.Transition-slideLayers), + > :global(.Transition-slideLayersBackwards) { + border-radius: var(--border-radius-default) var(--border-radius-default) 0 0; + } + } + + &:global(.open) .dialog { + transform: translateY(0); + } + + &:global(.closing) .dialog { + transform: translateY(100%); + } + + :global(html.animation-level-0) &:global(.closing) { + .dialog { + display: none; + + // The transition for the `display` property is needed to prevent blocking events + /* stylelint-disable-next-line plugin/no-low-performance-animation-properties */ + transition: var(--no-animation-transition), display 0ms 200ms !important; + } + + .backdrop { + display: none; + } + } + + // Mimic the animation from the system Settings section on Android + :global(html.is-android) &:not(.forceBottomSheet) { + --transition: 500ms cubic-bezier(0.16, 1, 0.3, 1); + .dialog { - transform: translateY(100%); + transform: translateY(15%); + + height: 100%; + max-height: 100%; + + opacity: 0; + border-radius: 0; + + > :global(.Transition-slideLayers), + > :global(.Transition-slideLayersBackwards) { + border-radius: 0; + } + } + + &:global(.open) .dialog { + transform: translateY(0); + + opacity: 1; + + transition: transform var(--transition), opacity var(--transition); } - &:global(.shown.open) { + &:global(.closing) { .dialog { - transform: translateY(0); + transform: translateY(3%); + + transition: transform 200ms ease-in, opacity 100ms 100ms ease-out !important; } } } + + :global(html.is-native-bottom-sheet) & { + .container { + align-items: flex-start; + + background: var(--color-background-second); + } + + .dialog { + max-height: none; + + border-radius: 0; + box-shadow: none; + + transition: none; + } + + &:global(.closing) .dialog { + transform: translateY(0); + } + + .backdrop { + display: none; + } + } } .container { @@ -58,12 +192,6 @@ justify-content: center; padding: 1rem; - - .slideUpAnimation & { - align-items: flex-end; - - padding: 0; - } } .backdrop { @@ -74,14 +202,7 @@ bottom: 0; left: 0; - opacity: 0; background-color: var(--color-tint); - - transition: opacity var(--transition); - - :global(html.animation-level-0) & { - transition: var(--no-animation-transition) !important; - } } .noBackdrop { @@ -90,7 +211,6 @@ .dialog { position: relative; - transform: translate3d(0, -1rem, 0); display: inline-flex; flex-direction: column; @@ -101,47 +221,10 @@ max-height: 100%; margin: 2rem auto; - opacity: 0; background-color: var(--color-background-window); border-radius: var(--border-radius-default); box-shadow: var(--default-shadow); - transition: transform var(--transition), opacity var(--transition); - - .slideUpAnimation & { - transform: none; - - max-width: 28rem; - max-height: calc(95 * var(--vh, 1vh)); - margin: 0; - - background: var(--color-background-second); - border-radius: var(--border-radius-default) var(--border-radius-default) 0 0; - - transition: transform var(--transition); - - :global(html:not(.animation-level-0)) & { - opacity: 1; - } - - :global(html.animation-level-0) & { - transition: var(--no-animation-transition) !important; - } - - > :global(.Transition-slideLayers), - > :global(.Transition-slideLayersBackwards) { - border-radius: var(--border-radius-default) var(--border-radius-default) 0 0; - } - } - - :global(html.animation-level-0) & { - transform: translate3d(0, 0, 0) !important; - - opacity: 0; - - transition: var(--no-animation-transition) !important; - } - .content > :global(.Transition-slideLayers), .content > :global(.Transition-slideLayersBackwards) { overflow: hidden; @@ -169,8 +252,6 @@ color: var(--color-black); - transition: box-shadow 200ms; - &_noClose { grid-template-areas: "title title title"; @@ -183,13 +264,7 @@ } &_wideContent { - grid-template-columns: 0.25fr 1fr 0.25fr; - } - - &_bordered { - z-index: 1; - /* stylelint-disable-next-line plugin/whole-pixel */ - box-shadow: 0 0.035rem 0 0 var(--color-separator); + grid-template-columns: 0.25fr 1.5fr 0.25fr; } &_back { @@ -200,6 +275,7 @@ align-items: center; margin-left: -0.875rem; + padding: 0 0.5rem; font-size: 1.0625rem; color: var(--color-blue); @@ -306,23 +382,38 @@ } .buttons { - display: flex; + display: grid; + grid-auto-columns: minmax(max-content, 1fr); + grid-auto-flow: column; gap: 1rem; - justify-content: center; + justify-items: center; - margin-top: auto; + margin-top: auto; // Used to pull down when modal has fixed height + padding-top: 2rem; } -.button { - min-width: 9rem !important; +.buttonsInsideContentWithScroll { + margin-bottom: -1rem; + padding-bottom: 1rem; + + @supports (padding-bottom: env(safe-area-inset-bottom)) { + margin-bottom: calc(-1 * max(env(safe-area-inset-bottom), 1rem)); + padding-bottom: max(env(safe-area-inset-bottom), 1rem); + } } -.customCancelButton { - min-width: 7rem !important; +.buttonsNoExtraSpace { + padding-top: 0; } -.customSubmitButton { - max-width: 100%; +.button { + width: 100%; + min-width: 9rem !important; + max-width: 68vw !important; +} + +.shortButton { + min-width: 6rem !important; } .transition { diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx index 0a7350ef..658f5e8f 100644 --- a/src/components/ui/Modal.tsx +++ b/src/components/ui/Modal.tsx @@ -1,22 +1,24 @@ import type { RefObject } from 'react'; -import type { - TeactNode, -} from '../../lib/teact/teact'; -import React, { useEffect, useRef } from '../../lib/teact/teact'; +import type { BottomSheetKeys } from 'native-bottom-sheet'; +import type { TeactNode } from '../../lib/teact/teact'; +import React, { useEffect, useLayoutEffect, useRef } from '../../lib/teact/teact'; import { ANIMATION_END_DELAY, IS_EXTENSION } from '../../config'; import buildClassName from '../../util/buildClassName'; +import { captureEvents, SwipeDirection } from '../../util/captureEvents'; import captureKeyboardListeners from '../../util/captureKeyboardListeners'; -import { captureSwipe, SwipeDirection } from '../../util/captureSwipe'; +import { getIsSwipeToCloseDisabled } from '../../util/modalSwipeManager'; import trapFocus from '../../util/trapFocus'; -import { IS_TOUCH_ENV } from '../../util/windowEnvironment'; +import { IS_ANDROID, IS_DELEGATED_BOTTOM_SHEET, IS_TOUCH_ENV } from '../../util/windowEnvironment'; import windowSize from '../../util/windowSize'; +import freezeWhenClosed from '../../hooks/freezeWhenClosed'; +import { useDelegatedBottomSheet } from '../../hooks/useDelegatedBottomSheet'; +import { useDelegatingBottomSheet } from '../../hooks/useDelegatingBottomSheet'; import { useDeviceScreen } from '../../hooks/useDeviceScreen'; -import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps'; import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; +import useHistoryBack from '../../hooks/useHistoryBack'; import useLang from '../../hooks/useLang'; -import useLastCallback from '../../hooks/useLastCallback'; import useShowTransition from '../../hooks/useShowTransition'; import Button from './Button'; @@ -31,6 +33,10 @@ type OwnProps = { contentClassName?: string; isOpen?: boolean; isCompact?: boolean; + nativeBottomSheetKey?: BottomSheetKeys; + forceFullNative?: boolean; // Always open in "full" size + noResetFullNativeOnBlur?: boolean; // Don't reset "full" size on blur + forceBottomSheet?: boolean; noBackdrop?: boolean; noBackdropClose?: boolean; header?: any; @@ -42,12 +48,8 @@ type OwnProps = { dialogRef?: RefObject; }; -type StateProps = { - shouldSkipHistoryAnimations?: boolean; -}; - -export const ANIMATION_DURATION = 350; -export const ANIMATION_DURATION_PORTRAIT = 500; +export const CLOSE_DURATION = 350; +export const CLOSE_DURATION_PORTRAIT = IS_ANDROID ? 200 : 500; function Modal({ dialogRef, @@ -57,6 +59,10 @@ function Modal({ contentClassName, isOpen, isCompact, + nativeBottomSheetKey, + forceFullNative, + forceBottomSheet, + noResetFullNativeOnBlur, noBackdrop, noBackdropClose, header, @@ -65,63 +71,75 @@ function Modal({ onClose, onCloseAnimationEnd, onEnter, - shouldSkipHistoryAnimations, -}: OwnProps & StateProps): TeactJsx { +}: OwnProps): TeactJsx { + const lang = useLang(); + // eslint-disable-next-line no-null/no-null + const modalRef = useRef(null); + + // eslint-disable-next-line no-null/no-null + const localDialogRef = useRef(null); + dialogRef ||= localDialogRef; + const { isPortrait } = useDeviceScreen(); - const animationDuration = isPortrait ? ANIMATION_DURATION_PORTRAIT : ANIMATION_DURATION; + const animationDuration = isPortrait ? CLOSE_DURATION_PORTRAIT : CLOSE_DURATION; const { shouldRender, transitionClassNames } = useShowTransition( isOpen, onCloseAnimationEnd, - shouldSkipHistoryAnimations, + undefined, false, - shouldSkipHistoryAnimations, + undefined, animationDuration + ANIMATION_END_DELAY, ); - const lang = useLang(); - // eslint-disable-next-line no-null/no-null - const modalRef = useRef(null); const isSlideUp = !isCompact && isPortrait; - const handleClose = useLastCallback((e: KeyboardEvent) => { - if (IS_EXTENSION) { - e.preventDefault(); - } - - onClose(); + useHistoryBack({ + isActive: isOpen, + onBack: onClose, }); useEffect( - () => (isOpen ? captureKeyboardListeners({ onEsc: handleClose, onEnter }) : undefined), - [handleClose, isOpen, onEnter], + () => (isOpen ? captureKeyboardListeners({ + onEnter, + onEsc: (e: KeyboardEvent) => { + if (IS_EXTENSION) { + e.preventDefault(); + } + + onClose(); + }, + }) : undefined), + [isOpen, onClose, onEnter], ); useEffect(() => (isOpen && modalRef.current ? trapFocus(modalRef.current) : undefined), [isOpen]); - useEffectWithPrevDeps( - ([prevIsOpen]) => { - if (isOpen || (!isOpen && prevIsOpen !== undefined)) { - dispatchHeavyAnimationEvent(animationDuration + ANIMATION_END_DELAY); - } - }, - [animationDuration, isOpen], - ); + useLayoutEffect(() => ( + isOpen ? dispatchHeavyAnimationEvent(animationDuration + ANIMATION_END_DELAY) : undefined + ), [animationDuration, isOpen]); useEffect(() => { - if (!IS_TOUCH_ENV || !isOpen || !isPortrait || !isSlideUp) { + if (!IS_TOUCH_ENV || !isOpen || !isPortrait || !isSlideUp || IS_DELEGATED_BOTTOM_SHEET) { return undefined; } - return captureSwipe(modalRef.current!, (e, direction) => { - if (direction === SwipeDirection.Down && !windowSize.getIsKeyboardVisible()) { - onClose(); - return true; - } + return captureEvents(modalRef.current!, { + onSwipe: (e, direction) => { + if (direction === SwipeDirection.Down && !windowSize.getIsKeyboardVisible() && !getIsSwipeToCloseDisabled()) { + onClose(); + return true; + } - return false; + return false; + }, }); }, [isOpen, isPortrait, isSlideUp, onClose]); + const isDelegatingToNative = useDelegatingBottomSheet(nativeBottomSheetKey, isPortrait, isOpen, onClose); + useDelegatedBottomSheet( + nativeBottomSheetKey, isOpen, onClose, dialogRef, forceFullNative, noResetFullNativeOnBlur, + ); + if (!shouldRender) { return undefined; } @@ -136,7 +154,9 @@ function Modal({ } return ( -
+
{title}
{hasCloseButton && ( + )} +
+ + {!isSmallHeight &&
{lang(title)}
} + {children} +
+ + + + ); + } + return (
+ {isBiometricAuthEnabled ? ( +
+ {lang(operationType === 'transfer' + ? 'Please confirm transaction using biometrics' : 'Please confirm operation using biometrics')} +
+ ) : ( + + )} + {localError && ( +
{lang(localError)}
+ )} + {help && !error && ( +
{help}
+ )}
{onCancel && ( - )} - + {!isBiometricAuthEnabled && ( + + )}
); } -export default memo(PasswordForm); +export default memo(withGlobal((global): StateProps => { + const { isPasswordNumeric, authConfig } = global.settings; + const isBiometricAuthEnabled = !!authConfig && authConfig.kind !== 'password'; + const isNativeBiometricAuthEnabled = !!authConfig && authConfig.kind === 'native-biometrics'; + + return { + isPasswordNumeric, + isBiometricAuthEnabled, + isNativeBiometricAuthEnabled, + authConfig, + }; +})(PasswordForm)); diff --git a/src/components/ui/PinPad.module.scss b/src/components/ui/PinPad.module.scss new file mode 100644 index 00000000..d5264a22 --- /dev/null +++ b/src/components/ui/PinPad.module.scss @@ -0,0 +1,280 @@ +.root { + width: 100%; + margin-top: auto; + padding: 1.5rem 1rem 1rem; + + background-color: var(--color-background-first); + + @supports (padding-bottom: max(1rem, env(safe-area-inset-bottom))) { + padding-bottom: max(1rem, env(safe-area-inset-bottom)); + } +} + +.title { + margin-bottom: 2rem; + + font-size: 1.0625rem; + font-weight: 700; + color: var(--color-gray-1); + text-align: center; + + transition: color 200ms; +} + +.dots { + --fill-color: var(--color-gray-4); + + display: flex; + align-items: center; + justify-content: center; + + margin-bottom: 1.5rem; +} + +.dotsError { + animation-name: shakeAnimation; + animation-duration: 200ms; + animation-timing-function: ease; + animation-iteration-count: 2; +} + +.dotsLoading { + position: relative; + + &::after { + content: ''; + + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + + width: 1.5rem; + height: 1.5rem; + + opacity: 0; + background-image: var(--spinner-green-data); + background-repeat: no-repeat; + background-size: 100%; + + animation: spin 1000ms linear 750ms infinite, + appear 1000ms linear 750ms forwards; + } +} + +.dot { + width: 0.75rem; + height: 0.75rem; + + background-color: var(--fill-color); + border-radius: 50%; + + transition: background-color 200ms, transform 300ms; + + .dotsLoading > & { + &:nth-child(1) { + animation: dotLoadingAnimation 1000ms linear forwards, firstDotLoadingAnimation 1000ms linear forwards; + } + &:nth-child(2) { + animation: dotLoadingAnimation 1000ms linear forwards, secondDotLoadingAnimation 1000ms linear forwards; + } + &:nth-child(3) { + animation: dotLoadingAnimation 1000ms linear forwards, thirdDotLoadingAnimation 1000ms linear forwards; + } + &:nth-child(4) { + animation: dotLoadingAnimation 1000ms linear forwards, fourthDotLoadingAnimation 1000ms linear forwards; + } + } +} + +.dotFilled { + --fill-color: var(--color-blue); + + animation: scaleAnimation 300ms linear forwards; +} + +.dot + .dot { + margin-left: 0.625rem; +} + +.grid { + display: grid; + grid-template-columns: repeat(3, 1fr); +} + +.button { + cursor: var(--custom-cursor, pointer); + user-select: none; + + position: relative; + + display: flex; + align-items: center; + justify-content: center; + + height: 5rem; + + font-size: 2rem; + font-weight: 700; + line-height: 2rem; + text-align: center; + + transition: opacity 200ms; + + @media (max-height: 43.5rem) { + height: 4rem; + } + + &::after { + content: ''; + + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + + aspect-ratio: 1; + height: 100%; + + opacity: 0; + background: var(--color-tint); + border-radius: 50%; + + transition: opacity 200ms; + } +} + +.buttonActive { + &::after { + opacity: 0.2; + } +} + +.buttonHidden { + pointer-events: none; + + opacity: 0; +} + +.error { + --fill-color: var(--color-red); + + color: var(--color-red); +} + +.success { + --fill-color: var(--color-green); + + color: var(--color-green); +} + +@keyframes scaleAnimation { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.4); + } + 100% { + transform: scale(1); + } +} + +@keyframes shakeAnimation { + 0% { + transform: translateX(0); + } + 25% { + transform: translateX(0.75rem); + } + 75% { + transform: translateX(-0.75rem); + } + 100% { + transform: translateX(0); + } +} + +@keyframes dotLoadingAnimation { + 0% { + transform: translateX(0) scale(1); + + opacity: 1; + } + 25% { + transform: translateX(0) scale(1.4); + } + 50% { + transform: translateX(0) scale(1); + } + 100% { + opacity: 0; + } +} + +@keyframes firstDotLoadingAnimation { + 50% { + transform: translateX(0); + } + 75% { + transform: translateX(2.0625rem); + } + 100% { + transform: translateX(2.0625rem); + } +} + +@keyframes secondDotLoadingAnimation { + 50% { + transform: translateX(0); + } + 75% { + transform: translateX(0.6875rem); + } + 100% { + transform: translateX(0.6875rem); + } +} + +@keyframes thirdDotLoadingAnimation { + 50% { + transform: translateX(0); + } + 75% { + transform: translateX(-0.6875rem); + } + 100% { + transform: translateX(-0.6875rem); + } +} + +@keyframes fourthDotLoadingAnimation { + 50% { + transform: translateX(0); + } + 75% { + transform: translateX(-2.0625rem); + } + 100% { + transform: translateX(-2.0625rem); + } +} + +@keyframes spin { + from { + transform: translate(-50%, -0.375rem) rotate(0deg); + } + + to { + transform: translate(-50%, -0.375rem) rotate(360deg); + } +} + +@keyframes appear { + from { + opacity: 0; + } + to { + opacity: 1; + } +} diff --git a/src/components/ui/PinPad.tsx b/src/components/ui/PinPad.tsx new file mode 100644 index 00000000..51aeadbc --- /dev/null +++ b/src/components/ui/PinPad.tsx @@ -0,0 +1,152 @@ +import React, { memo, useEffect } from '../../lib/teact/teact'; +import { withGlobal } from '../../global'; + +import type { GlobalState } from '../../global/types'; + +import buildClassName from '../../util/buildClassName'; +import { getIsFaceIdAvailable, vibrateOnError } from '../../util/capacitor'; + +import useLastCallback from '../../hooks/useLastCallback'; + +import PinPadButton from './PinPadButton'; + +import styles from './PinPad.module.scss'; + +interface OwnProps { + title: string; + type?: 'error' | 'success'; + value: string; + length?: number; + className?: string; + onBiometricsClick?: NoneToVoidFunction; + onChange: (value: string) => void; + onClearError?: NoneToVoidFunction; + onSubmit: (pin: string) => void; +} + +type StateProps = Pick & { + isPinPadPasswordAccepted?: boolean; +}; + +const DEFAULT_PIN_LENGTH = 4; +const RESET_STATE_DELAY_MS = 1500; + +function PinPad({ + title, + type, + value, + length = DEFAULT_PIN_LENGTH, + onBiometricsClick, + isPinPadPasswordAccepted, + className, + onChange, + onClearError, + onSubmit, +}: OwnProps & StateProps) { + const isFaceId = getIsFaceIdAvailable(); + const canRenderBackspace = value.length > 0; + const isDisablePinButtons = value.length === length || Boolean(type); + const isSuccess = type === 'success' || isPinPadPasswordAccepted; + const titleClassName = buildClassName( + styles.title, + type === 'error' && styles.error, + isSuccess && styles.success, + ); + + useEffect(() => { + if (type !== 'error') return undefined; + + const timeoutId = window.setTimeout(() => { + if (value.length === length) { + onChange(''); + } + onClearError?.(); + }, RESET_STATE_DELAY_MS); + void vibrateOnError(); + + return () => { + window.clearTimeout(timeoutId); + }; + }, [length, onChange, onClearError, type, value.length]); + + const handleClick = useLastCallback((char: string) => { + const newValue = `${value}${char}`; + onChange(newValue); + + if (newValue.length === length) { + onSubmit(newValue); + } + }); + + const handleBackspaceClick = useLastCallback(() => { + onClearError?.(); + if (!value.length) return; + + onChange(value.slice(0, -1)); + }); + + function renderDots() { + const dotsClassName = buildClassName( + styles.dots, + type === 'error' && styles.dotsError, + isSuccess && styles.dotsLoading, + ); + + return ( +
+ {Array.from({ length }, (_, i) => ( +
+ ))} +
+ ); + } + + return ( +
+
{title}
+ {renderDots()} + +
+ + + + + + + + + + {!onBiometricsClick ? : ( + + + + )} + + + + +
+
+ ); +} + +export default memo(withGlobal( + (global) => { + const { isPinPadPasswordAccepted } = global; + return { + isPinPadPasswordAccepted, + }; + }, +)(PinPad)); diff --git a/src/components/ui/PinPadButton.tsx b/src/components/ui/PinPadButton.tsx new file mode 100644 index 00000000..214a7229 --- /dev/null +++ b/src/components/ui/PinPadButton.tsx @@ -0,0 +1,43 @@ +import React, { memo, useState } from '../../lib/teact/teact'; + +import buildClassName from '../../util/buildClassName'; +import { vibrate } from '../../util/capacitor'; + +import styles from './PinPad.module.scss'; + +interface OwnProps { + value?: any; + children?: React.ReactNode; + className?: string; + isDisabled?: boolean; + onClick?: (value?: any) => void; +} + +const CLICKED_TIMEOUT_MS = 200; + +function PinPadButton({ + value, children, className, isDisabled, onClick, +}: OwnProps) { + const [isClicked, setIsClicked] = useState(false); + + const handleClick = () => { + void vibrate(); + onClick?.(value); + setIsClicked(true); + setTimeout(() => { + setIsClicked(false); + }, CLICKED_TIMEOUT_MS); + }; + return ( +
+ {value || children} +
+ ); +} + +export default memo(PinPadButton); diff --git a/src/components/ui/RichNumberInput.tsx b/src/components/ui/RichNumberInput.tsx index 1ddad20d..a4368094 100644 --- a/src/components/ui/RichNumberInput.tsx +++ b/src/components/ui/RichNumberInput.tsx @@ -1,11 +1,10 @@ import type { TeactNode } from '../../lib/teact/teact'; import React, { - memo, useEffect, useRef, + memo, useLayoutEffect, useRef, useState, } from '../../lib/teact/teact'; -import { FRACTION_DIGITS } from '../../config'; +import { DEFAULT_DECIMAL_PLACES, FRACTION_DIGITS } from '../../config'; import { Big } from '../../lib/big.js'; -import { requestMutation } from '../../lib/fasterdom/fasterdom'; import buildClassName from '../../util/buildClassName'; import { round } from '../../util/round'; import { saveCaretPosition } from '../../util/saveCaretPosition'; @@ -21,6 +20,7 @@ type OwnProps = { labelText?: React.ReactNode; value?: number; hasError?: boolean; + isLoading?: boolean; suffix?: string; className?: string; inputClassName?: string; @@ -34,12 +34,14 @@ type OwnProps = { onPressEnter?: (e: React.KeyboardEvent) => void; decimals?: number; disabled?: boolean; + isStatic?: boolean; }; function RichNumberInput({ id, labelText, hasError, + isLoading = false, suffix, value, children, @@ -53,39 +55,53 @@ function RichNumberInput({ onFocus, onPressEnter, decimals = FRACTION_DIGITS, - disabled, + disabled = false, + isStatic = false, }: OwnProps) { // eslint-disable-next-line no-null/no-null const inputRef = useRef(null); const lang = useLang(); const [hasFocus, markHasFocus, unmarkHasFocus] = useFlag(false); + const [isContentEditable, setContentEditable] = useState(!disabled); - const updateHtml = useLastCallback((parts?: RegExpMatchArray) => { - const input = inputRef.current!; + const handleLoadingHtml = useLastCallback((input: HTMLInputElement, parts?: RegExpMatchArray) => { const newHtml = parts ? buildContentHtml(parts, suffix, decimals) : ''; + input.innerHTML = newHtml; + setContentEditable(false); + return newHtml; + }); + + const handleNumberHtml = useLastCallback((input: HTMLInputElement, parts?: RegExpMatchArray) => { + const newHtml = parts ? buildContentHtml(parts, suffix, decimals) : ''; const restoreCaretPosition = document.activeElement === inputRef.current ? saveCaretPosition(input, decimals) : undefined; + input.innerHTML = newHtml; + setContentEditable(!disabled); restoreCaretPosition?.(); - // Trick to remove pseudo-element with placeholder in this tick - requestMutation(() => { - input.classList.toggle(styles.isEmpty, !newHtml.length); - }); + return newHtml; + }); + + const updateHtml = useLastCallback((parts?: RegExpMatchArray) => { + const input = inputRef.current!; + const content = isLoading ? handleLoadingHtml(input, parts) : handleNumberHtml(input, parts); + + input.classList.toggle(styles.isEmpty, !content.length); }); - useEffect(() => { - const newValue = castValue(value, decimals); + useLayoutEffect(() => { + const newValue = castValue(value); - const parts = getParts(String(newValue), decimals); + const parts = getParts(String(newValue)); updateHtml(parts); if (value !== newValue) { onChange?.(newValue); } - }, [decimals, onChange, updateHtml, value, suffix]); + }, [decimals, onChange, updateHtml, value, suffix, isLoading, disabled]); function handleChange(e: React.FormEvent) { const inputValue = e.currentTarget.innerText.trim(); @@ -126,6 +142,7 @@ function RichNumberInput({ const inputWrapperFullClass = buildClassName( styles.input__wrapper, + isStatic && styles.inputWrapperStatic, hasError && styles.error, hasFocus && styles.input__wrapper_hasFocus, inputClassName, @@ -135,6 +152,7 @@ function RichNumberInput({ styles.input_rich, !value && styles.isEmpty, valueClassName, + isLoading && styles.isLoading, ); const labelTextClassName = buildClassName( styles.label, @@ -143,6 +161,8 @@ function RichNumberInput({ ); const cornerFullClass = buildClassName( cornerClassName, + hasFocus && styles.swapCorner, + hasError && styles.swapCorner_error, ); return ( @@ -160,13 +180,14 @@ function RichNumberInput({ {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
{children} - {cornerClassName &&
}
+ {cornerClassName &&
}
); } -function getParts(value: string, decimals: number) { +function getParts(value: string, decimals = DEFAULT_DECIMAL_PLACES) { const regex = getInputRegex(decimals); // Correct problem with numbers like 1e-8 if (value.includes('e-')) { @@ -195,7 +216,7 @@ export function getInputRegex(decimals: number) { return new RegExp(`^(\\d+)([.,])?(\\d{1,${decimals}})?`); } -function castValue(value?: number | string, decimals?: number) { +function castValue(value?: number | string, decimals = DEFAULT_DECIMAL_PLACES) { return value && Number.isFinite(Number(value)) ? round(value, decimals, Big.roundDown) : undefined; } diff --git a/src/components/ui/Switcher.tsx b/src/components/ui/Switcher.tsx index d13211e7..a3aa5d82 100644 --- a/src/components/ui/Switcher.tsx +++ b/src/components/ui/Switcher.tsx @@ -46,6 +46,7 @@ function Switcher({ checked={checked} className={styles.input} onChange={handleChange} + teactExperimentControlled={!onChange && !onCheck} /> diff --git a/src/components/ui/TabList.tsx b/src/components/ui/TabList.tsx index d637a001..22a0e76f 100644 --- a/src/components/ui/TabList.tsx +++ b/src/components/ui/TabList.tsx @@ -12,6 +12,7 @@ import Tab from './Tab'; import styles from './TabList.module.scss'; export type TabWithProperties = { + id: number; title: string; className?: string; }; @@ -74,7 +75,7 @@ function TabList({ previousActiveTab={previousActiveTab} className={tab?.className} onClick={onSwitchTab} - clickArg={i} + clickArg={tab.id} /> ))}
diff --git a/src/components/ui/Transition.scss b/src/components/ui/Transition.scss index a84969e2..0e04dff1 100644 --- a/src/components/ui/Transition.scss +++ b/src/components/ui/Transition.scss @@ -17,6 +17,11 @@ left: 0; } + &-from, + &-from .custom-scroll { + pointer-events: none !important; + } + &-inactive { display: none !important; // Best performance when animating container //transform: scale(0); // Shortest initial delay @@ -181,6 +186,44 @@ } } + &-slideFadeAndroid { + --background-color: var(--color-background-second); + + > .Transition_slide { + z-index: 0; + + background: var(--background-color); + } + + > .Transition_slide-to { + transform: translateX(1.5rem); + transform-origin: left; + + opacity: 0; + + animation: fade-in-opacity var(--slide-transition), slide-fade-in-move-android var(--slide-transition); + } + } + + &-slideFadeAndroidBackwards { + --background-color: var(--color-background-second); + + > .Transition_slide { + z-index: 0; + + background: var(--background-color); + } + + > .Transition_slide-from { + transform: translateX(0); + + opacity: 1; + + animation: fade-in-backwards-opacity var(--slide-transition), + slide-fade-in-backwards-move-android var(--slide-transition); + } + } + &-zoomFade { > .Transition_slide-from { transform: scale(1); @@ -198,7 +241,7 @@ // We can omit `transform: scale(1.1);` here because `opacity` is 0. // We need to for proper position calculation in `InfiniteScroll`. - animation: fade-in-opacity 0.15s ease, zoomFade-in-move 0.15s ease; + animation: fade-in-opacity 0.15s ease, zoom-fade-in-move 0.15s ease; } } @@ -206,13 +249,13 @@ > .Transition_slide-from { transform: scale(1); - animation: fade-in-backwards-opacity 0.1s ease, zoomFade-in-backwards-move 0.15s ease; + animation: fade-in-backwards-opacity 0.1s ease, zoom-fade-in-backwards-move 0.15s ease; } > .Transition_slide-to { transform: scale(0.95); - animation: fade-out-backwards-opacity 0.15s ease, zoomFade-out-backwards-move 0.15s ease; + animation: fade-out-backwards-opacity 0.15s ease, zoom-fade-out-backwards-move 0.15s ease; } } @@ -277,7 +320,7 @@ } > .Transition_slide-from { - animation: slide-layers-out var(--layer-transition); + animation: slide-layers-out var(--layer-transition-behind); } } @@ -293,9 +336,10 @@ > .Transition_slide-to { transform: translateX(-20%); - opacity: 0.75; + opacity: calc(1 - var(--layer-blackout-opacity)); - animation: slide-layers-out-backwards var(--layer-transition); + animation: slide-layers-out-backwards var(--layer-transition-behind); + animation-duration: 450ms; } > .Transition_slide-from { @@ -565,7 +609,25 @@ } } -@keyframes zoomFade-in-move { +@keyframes slide-fade-in-move-android { + 0% { + transform: translateX(20%); + } + 100% { + transform: translateX(0); + } +} + +@keyframes slide-fade-in-backwards-move-android { + 0% { + transform: translateX(0); + } + 100% { + transform: translateX(15%); + } +} + +@keyframes zoom-fade-in-move { 0% { transform: scale(1.1); } @@ -574,7 +636,7 @@ } } -@keyframes zoomFade-in-backwards-move { +@keyframes zoom-fade-in-backwards-move { 0% { transform: scale(1); } @@ -583,7 +645,7 @@ } } -@keyframes zoomFade-out-backwards-move { +@keyframes zoom-fade-out-backwards-move { 0% { transform: scale(0.95); } diff --git a/src/components/ui/Transition.tsx b/src/components/ui/Transition.tsx index de70af17..7ddda35f 100644 --- a/src/components/ui/Transition.tsx +++ b/src/components/ui/Transition.tsx @@ -1,15 +1,16 @@ import type { RefObject } from 'react'; import React, { useEffect, useLayoutEffect, useRef } from '../../lib/teact/teact'; -import { addExtraClass, removeExtraClass, toggleExtraClass } from '../../lib/teact/teact-dom'; +import { + addExtraClass, removeExtraClass, setExtraStyles, toggleExtraClass, +} from '../../lib/teact/teact-dom'; import { getGlobal } from '../../global'; -import type { GlobalState } from '../../global/types'; - import { ANIMATION_LEVEL_MIN } from '../../config'; import { requestForcedReflow, requestMutation } from '../../lib/fasterdom/fasterdom'; import buildClassName from '../../util/buildClassName'; import { waitForAnimationEnd, waitForTransitionEnd } from '../../util/cssAnimationEndListeners'; import forceReflow from '../../util/forceReflow'; +import { allowSwipeControlForTransition } from '../../util/swipeController'; import useForceUpdate from '../../hooks/useForceUpdate'; import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; @@ -20,12 +21,13 @@ import './Transition.scss'; type AnimationName = ( 'none' | 'slide' | 'slideRtl' | 'slideFade' | 'zoomFade' | 'slideLayers' | 'fade' | 'pushSlide' | 'reveal' | 'slideOptimized' | 'slideOptimizedRtl' | 'semiFade' - | 'slideVertical' | 'slideVerticalFade' + | 'slideVertical' | 'slideVerticalFade' | 'slideFadeAndroid' ); -export type ChildrenFn = (isActive: boolean, isFrom: boolean, currentKey: number) => React.ReactNode; +export type ChildrenFn = (isActive: boolean, isFrom: boolean, currentKey: number, activeKey: number) => React.ReactNode; export type TransitionProps = { ref?: RefObject; activeKey: number; + prevKey?: number; nextKey?: number; name: AnimationName; direction?: 'auto' | 'inverse' | 1 | -1; @@ -39,6 +41,7 @@ export type TransitionProps = { id?: string; className?: string; slideClassName?: string; + withSwipeControl?: boolean; onStart?: NoneToVoidFunction; onStop?: NoneToVoidFunction; children: React.ReactNode | ChildrenFn; @@ -60,6 +63,7 @@ function Transition({ ref, activeKey, nextKey, + prevKey, name, direction = 'auto', renderCount, @@ -71,6 +75,7 @@ function Transition({ id, className, slideClassName, + withSwipeControl, onStart, onStop, children, @@ -78,6 +83,7 @@ function Transition({ const currentKeyRef = useRef(); // No need for a container to update on change const { animationLevel } = getGlobal().settings; + const shouldDisableAnimation = animationLevel === ANIMATION_LEVEL_MIN; // eslint-disable-next-line no-null/no-null let containerRef = useRef(null); @@ -88,14 +94,19 @@ function Transition({ const rendersRef = useRef>({}); const prevActiveKey = usePrevious(activeKey); const forceUpdate = useForceUpdate(); + const isAnimatingRef = useRef(false); + const isSwipeJustCancelledRef = useRef(false); - const activeKeyChanged = prevActiveKey !== undefined && activeKey !== prevActiveKey; + const hasActiveKeyChanged = prevActiveKey !== undefined && activeKey !== prevActiveKey; - if (!renderCount && activeKeyChanged) { + if (!renderCount && hasActiveKeyChanged) { rendersRef.current = { [prevActiveKey]: rendersRef.current[prevActiveKey] }; } rendersRef.current[activeKey] = children; + if (prevKey) { + rendersRef.current[prevKey] = children; + } if (nextKey) { rendersRef.current[nextKey] = children; } @@ -124,7 +135,6 @@ function Transition({ const keys = Object.keys(rendersRef.current).map(Number); const prevActiveIndex = renderCount ? prevActiveKey : keys.indexOf(prevActiveKey); const activeIndex = renderCount ? activeKey : keys.indexOf(activeKey); - const nextIndex = nextKey ? (renderCount ? nextKey : keys.indexOf(nextKey)) : -1; const childNodes = Array.from(container.childNodes); if (!childNodes.length) { @@ -140,21 +150,25 @@ function Transition({ } }); - if (!activeKeyChanged) { - const activeChild = childNodes[activeIndex]; - if (activeChild instanceof HTMLElement) { - addExtraClass(activeChild, CLASSES.active); - - if (isSlideOptimized) { - activeChild.style.transition = 'none'; - activeChild.style.transform = 'translate3d(0, 0, 0)'; - } + if (!hasActiveKeyChanged) { + if (isAnimatingRef.current) { + return; } - const nextChild = nextIndex !== -1 && nextIndex !== activeIndex && childNodes[nextIndex] as HTMLElement; - if (nextChild instanceof HTMLElement) { - addExtraClass(nextChild, CLASSES.inactive); - } + childElements.forEach((childElement) => { + if (childElement === childNodes[activeIndex]) { + addExtraClass(childElement, CLASSES.active); + + if (isSlideOptimized) { + setExtraStyles(childElement, { + transition: 'none', + transform: 'translate3d(0, 0, 0)', + }); + } + } else if (!isSlideOptimized) { + addExtraClass(childElement, CLASSES.inactive); + } + }); return; } @@ -162,13 +176,18 @@ function Transition({ currentKeyRef.current = activeKey; if (isSlideOptimized) { + if (!childNodes[activeIndex]) { + return; + } + performSlideOptimized( - animationLevel, + shouldDisableAnimation, name, isBackwards, cleanup, activeKey, currentKeyRef, + isAnimatingRef, container, childNodes[activeIndex], childNodes[prevActiveIndex], @@ -180,7 +199,11 @@ function Transition({ return; } - if (name === 'none' || animationLevel === ANIMATION_LEVEL_MIN) { + if (name === 'none' || shouldDisableAnimation || isSwipeJustCancelledRef.current) { + if (isSwipeJustCancelledRef.current) { + isSwipeJustCancelledRef.current = false; + } + childNodes.forEach((node, i) => { if (node instanceof HTMLElement) { removeExtraClass(node, CLASSES.from); @@ -204,6 +227,7 @@ function Transition({ } }); + isAnimatingRef.current = true; const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent(); onStart?.(); @@ -233,24 +257,41 @@ function Transition({ if (shouldRestoreHeight) { if (activeElement) { - activeElement.style.height = 'auto'; - container.style.height = `${clientHeight}px`; + setExtraStyles(activeElement, { height: 'auto' }); + setExtraStyles(container, { height: `${clientHeight}px` }); } } onStop?.(); dispatchHeavyAnimationStop(); + isAnimatingRef.current = false; cleanup(); }); } - const watchedNode = name === 'reveal' && isBackwards + const watchedNode = (name === 'reveal' || name === 'slideFadeAndroid') && isBackwards ? childNodes[prevActiveIndex] : childNodes[activeIndex]; if (watchedNode) { - waitForAnimationEnd(watchedNode, onAnimationEnd, undefined, FALLBACK_ANIMATION_END); + if (withSwipeControl && childNodes[prevActiveIndex]) { + const giveUpAnimationEnd = waitForAnimationEnd(watchedNode, onAnimationEnd); + + allowSwipeControlForTransition( + childNodes[prevActiveIndex] as HTMLElement, + childNodes[activeIndex] as HTMLElement, + () => { + giveUpAnimationEnd(); + isSwipeJustCancelledRef.current = true; + onStop?.(); + dispatchHeavyAnimationStop(); + isAnimatingRef.current = false; + }, + ); + } else { + waitForAnimationEnd(watchedNode, onAnimationEnd, undefined, FALLBACK_ANIMATION_END); + } } else { onAnimationEnd(); } @@ -258,7 +299,7 @@ function Transition({ activeKey, nextKey, prevActiveKey, - activeKeyChanged, + hasActiveKeyChanged, isBackwards, name, onStart, @@ -268,8 +309,9 @@ function Transition({ shouldCleanup, slideClassName, cleanupExceptionKey, - animationLevel, + shouldDisableAnimation, forceUpdate, + withSwipeControl, ]); useEffect(() => { @@ -290,9 +332,11 @@ function Transition({ } requestMutation(() => { - activeElement.style.height = 'auto'; - container.style.height = `${clientHeight}px`; - container.style.flexBasis = `${clientHeight}px`; + setExtraStyles(activeElement, { height: 'auto' }); + setExtraStyles(container, { + height: `${clientHeight}px`, + flexBasis: `${clientHeight}px`, + }); }); }, [shouldRestoreHeight, children]); @@ -305,15 +349,13 @@ function Transition({ return undefined; } - const rendered = typeof render === 'function' ? render(key === activeKey, key === prevActiveKey, key) : render; + const rendered = typeof render === 'function' + ? render(key === activeKey, key === prevActiveKey, key, activeKey) + : render; - return (shouldWrap && key !== wrapExceptionKey) || asFastList ? ( -
- {rendered} -
- ) : ( - rendered - ); + return (shouldWrap && key !== wrapExceptionKey) || asFastList + ?
{rendered}
+ : rendered; }); return ( @@ -331,12 +373,13 @@ function Transition({ export default Transition; function performSlideOptimized( - animationLevel: GlobalState['settings']['animationLevel'], + shouldDisableAnimation: boolean, name: 'slideOptimized' | 'slideOptimizedRtl', isBackwards: boolean, cleanup: NoneToVoidFunction, activeKey: number, currentKeyRef: { current: number | undefined }, + isAnimatingRef: { current: boolean | undefined }, container: HTMLElement, toSlide: ChildNode, fromSlide?: ChildNode, @@ -344,20 +387,24 @@ function performSlideOptimized( onStart?: NoneToVoidFunction, onStop?: NoneToVoidFunction, ) { - if (animationLevel === ANIMATION_LEVEL_MIN) { + if (shouldDisableAnimation) { toggleExtraClass(container, `Transition-${name}`, !isBackwards); toggleExtraClass(container, `Transition-${name}Backwards`, isBackwards); if (fromSlide instanceof HTMLElement) { - fromSlide.style.transition = 'none'; - fromSlide.style.transform = ''; removeExtraClass(fromSlide, CLASSES.active); + setExtraStyles(fromSlide, { + transition: 'none', + transform: '', + }); } if (toSlide instanceof HTMLElement) { - toSlide.style.transition = 'none'; - toSlide.style.transform = 'translate3d(0, 0, 0)'; addExtraClass(toSlide, CLASSES.active); + setExtraStyles(toSlide, { + transition: 'none', + transform: 'translate3d(0, 0, 0)', + }); } cleanup(); @@ -369,21 +416,25 @@ function performSlideOptimized( isBackwards = !isBackwards; } + isAnimatingRef.current = true; const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent(); - onStart?.(); toggleExtraClass(container, `Transition-${name}`, !isBackwards); toggleExtraClass(container, `Transition-${name}Backwards`, isBackwards); if (fromSlide instanceof HTMLElement) { - fromSlide.style.transition = 'none'; - fromSlide.style.transform = 'translate3d(0, 0, 0)'; + setExtraStyles(fromSlide, { + transition: 'none', + transform: 'translate3d(0, 0, 0)', + }); } if (toSlide instanceof HTMLElement) { - toSlide.style.transition = 'none'; - toSlide.style.transform = `translate3d(${isBackwards ? '-' : ''}100%, 0, 0)`; + setExtraStyles(toSlide, { + transition: 'none', + transform: `translate3d(${isBackwards ? '-' : ''}100%, 0, 0)`, + }); } requestForcedReflow(() => { @@ -393,15 +444,19 @@ function performSlideOptimized( return () => { if (fromSlide instanceof HTMLElement) { - fromSlide.style.transition = ''; - fromSlide.style.transform = `translate3d(${isBackwards ? '' : '-'}100%, 0, 0)`; removeExtraClass(fromSlide, CLASSES.active); + setExtraStyles(fromSlide, { + transition: '', + transform: `translate3d(${isBackwards ? '' : '-'}100%, 0, 0)`, + }); } if (toSlide instanceof HTMLElement) { - toSlide.style.transition = ''; - toSlide.style.transform = 'translate3d(0, 0, 0)'; addExtraClass(toSlide, CLASSES.active); + setExtraStyles(toSlide, { + transition: '', + transform: 'translate3d(0, 0, 0)', + }); } }; }); @@ -415,17 +470,21 @@ function performSlideOptimized( } if (fromSlide instanceof HTMLElement) { - fromSlide.style.transition = 'none'; - fromSlide.style.transform = ''; + setExtraStyles(fromSlide, { + transition: 'none', + transform: '', + }); } if (shouldRestoreHeight && clientHeight && toSlide instanceof HTMLElement) { - toSlide.style.height = 'auto'; - container.style.height = `${clientHeight}px`; + setExtraStyles(toSlide, { height: 'auto' }); + setExtraStyles(container, { height: `${clientHeight}px` }); } onStop?.(); dispatchHeavyAnimationStop(); + isAnimatingRef.current = false; + cleanup(); }); }); diff --git a/src/components/ui/helpers/animatedAssets.ts b/src/components/ui/helpers/animatedAssets.ts index b1125900..c281a779 100644 --- a/src/components/ui/helpers/animatedAssets.ts +++ b/src/components/ui/helpers/animatedAssets.ts @@ -1,5 +1,6 @@ import bill from '../../../assets/lottie/duck_bill.tgs'; import forge from '../../../assets/lottie/duck_forges.tgs'; +import guard from '../../../assets/lottie/duck_guard.tgs'; import happy from '../../../assets/lottie/duck_happy.tgs'; import hello from '../../../assets/lottie/duck_hello.tgs'; import noData from '../../../assets/lottie/duck_no-data.tgs'; @@ -8,8 +9,10 @@ import snitch from '../../../assets/lottie/duck_snitch.tgs'; import thumbUp from '../../../assets/lottie/duck_thumb.tgs'; import holdTon from '../../../assets/lottie/duck_ton.tgs'; import wait from '../../../assets/lottie/duck_wait.tgs'; +import yeee from '../../../assets/lottie/duck_yeee.tgs'; import billPreview from '../../../assets/lottiePreview/duck_bill.png'; import forgePreview from '../../../assets/lottiePreview/duck_forges.png'; +import guardPreview from '../../../assets/lottiePreview/duck_guard.png'; import happyPreview from '../../../assets/lottiePreview/duck_happy.png'; import helloPreview from '../../../assets/lottiePreview/duck_hello.png'; import noDataPreview from '../../../assets/lottiePreview/duck_no-data.png'; @@ -18,6 +21,7 @@ import snitchPreview from '../../../assets/lottiePreview/duck_snitch.png'; import thumbUpPreview from '../../../assets/lottiePreview/duck_thumb.png'; import holdTonPreview from '../../../assets/lottiePreview/duck_ton.png'; import waitPreview from '../../../assets/lottiePreview/duck_wait.png'; +import yeeePreview from '../../../assets/lottiePreview/duck_yeee.png'; export const ANIMATED_STICKERS_PATHS = { hello, @@ -30,6 +34,8 @@ export const ANIMATED_STICKERS_PATHS = { forge, wait, run, + yeee, + guard, helloPreview, snitchPreview, billPreview, @@ -40,4 +46,6 @@ export const ANIMATED_STICKERS_PATHS = { forgePreview, waitPreview, runPreview, + yeeePreview, + guardPreview, }; diff --git a/src/components/ui/helpers/assetLogos.ts b/src/components/ui/helpers/assetLogos.ts index 0198f964..17213020 100644 --- a/src/components/ui/helpers/assetLogos.ts +++ b/src/components/ui/helpers/assetLogos.ts @@ -1,5 +1,5 @@ -import btc from '../../../assets/coins/btc.png'; -import ton from '../../../assets/coins/ton.png'; +import btc from '../../../assets/coins/coin_btc.png'; +import ton from '../../../assets/coins/coin_ton.png'; export const ASSET_LOGO_PATHS = { ton, diff --git a/src/config.ts b/src/config.ts index 28b751a2..bf498965 100644 --- a/src/config.ts +++ b/src/config.ts @@ -12,17 +12,24 @@ export const IS_TEST = APP_ENV === 'test'; export const IS_PERF = APP_ENV === 'perf'; export const IS_EXTENSION = process.env.IS_EXTENSION === '1'; export const IS_FIREFOX_EXTENSION = process.env.IS_FIREFOX_EXTENSION === '1'; -export const IS_ELECTRON = process.env.IS_ELECTRON === '1'; -export const IS_DAPP_SUPPORTED = IS_EXTENSION || IS_ELECTRON; -export const IS_SSE_SUPPORTED = IS_ELECTRON; +export const IS_ELECTRON_BUILD = process.env.IS_ELECTRON_BUILD === '1'; +export const IS_CAPACITOR = process.env.IS_CAPACITOR === '1'; +export const IS_DAPP_SUPPORTED = IS_EXTENSION || IS_ELECTRON_BUILD || IS_CAPACITOR; +export const IS_SSE_SUPPORTED = IS_ELECTRON_BUILD || IS_CAPACITOR; export const ELECTRON_HOST_URL = 'https://dumb-host'; export const INACTIVE_MARKER = '[Inactive]'; +export const PRODUCTION_URL = 'https://mytonwallet.app'; +export const BASE_URL = process.env.BASE_URL; -export const STRICTERDOM_ENABLED = DEBUG && !IS_ELECTRON; +export const STRICTERDOM_ENABLED = DEBUG && !IS_ELECTRON_BUILD; export const DEBUG_ALERT_MSG = 'Shoot!\nSomething went wrong, please see the error details in Dev Tools Console.'; +export const PIN_LENGTH = 4; +export const NATIVE_BIOMETRICS_USERNAME = 'MyTonWallet'; +export const NATIVE_BIOMETRICS_SERVER = 'https://mytonwallet.app'; + export const MNEMONIC_COUNT = 24; export const MNEMONIC_CHECK_COUNT = 3; @@ -35,8 +42,8 @@ export const ANIMATED_STICKER_SMALL_SIZE_PX = 110; export const ANIMATED_STICKER_MIDDLE_SIZE_PX = 120; export const ANIMATED_STICKER_DEFAULT_PX = 150; export const ANIMATED_STICKER_BIG_SIZE_PX = 156; +export const ANIMATED_STICKER_HUGE_SIZE_PX = 192; -export const DEFAULT_PRICE_CURRENCY = '$'; export const TON_SYMBOL = 'TON'; export const DEFAULT_LANDSCAPE_ACTION_TAB_ID = 1; @@ -45,22 +52,6 @@ export const DEFAULT_DECIMAL_PLACES = 9; export const DEFAULT_SLIPPAGE_VALUE = 0.5; -export const TOKEN_INFO = { - toncoin: { - name: 'Toncoin', - symbol: TON_SYMBOL, - slug: 'toncoin', - quote: { - price: 1.95, - percentChange1h: 0, - percentChange24h: 0, - percentChange7d: 0, - percentChange30d: 0, - }, - decimals: DEFAULT_DECIMAL_PLACES, - }, -}; - export const GLOBAL_STATE_CACHE_DISABLED = false; export const GLOBAL_STATE_CACHE_KEY = 'mytonwallet-global-state'; @@ -72,13 +63,22 @@ export const THEME_DEFAULT = 'system'; export const MAIN_ACCOUNT_ID = '0-ton-mainnet'; -export const TONHTTPAPI_MAINNET_URL = process.env.TONHTTPAPI_MAINNET_URL || 'https://toncenter.com/api/v2/jsonRPC'; -export const TONHTTPAPI_MAINNET_API_KEY = (IS_ELECTRON && process.env.ELECTRON_TONHTTPAPI_MAINNET_API_KEY) +export const TONHTTPAPI_MAINNET_URL = process.env.TONHTTPAPI_MAINNET_URL + || 'https://tonhttpapi.mytonwallet.org/api/v2/jsonRPC'; +export const TONHTTPAPI_MAINNET_API_KEY = (IS_ELECTRON_BUILD && process.env.ELECTRON_TONHTTPAPI_MAINNET_API_KEY) || process.env.TONHTTPAPI_MAINNET_API_KEY; +export const TONINDEXER_MAINNET_URL = process.env.TONINDEXER_MAINNET_URL + || 'https://tonhttpapi.mytonwallet.org/api/v3'; +export const TONAPIIO_MAINNET_URL = process.env.TONAPIIO_MAINNET_URL || 'https://tonapiio.mytonwallet.org'; + export const TONHTTPAPI_TESTNET_URL = process.env.TONHTTPAPI_TESTNET_URL - || 'https://testnet.toncenter.com/api/v2/jsonRPC'; -export const TONHTTPAPI_TESTNET_API_KEY = (IS_ELECTRON && process.env.ELECTRON_TONHTTPAPI_TESTNET_API_KEY) + || 'https://tonhttpapi-testnet.mytonwallet.org/api/v2/jsonRPC'; +export const TONHTTPAPI_TESTNET_API_KEY = (IS_ELECTRON_BUILD && process.env.ELECTRON_TONHTTPAPI_TESTNET_API_KEY) || process.env.TONHTTPAPI_TESTNET_API_KEY; +export const TONINDEXER_TESTNET_URL = process.env.TONINDEXER_TESTNET_URL + || 'https://tonhttpapi-testnet.mytonwallet.org/api/v3'; +export const TONAPIIO_TESTNET_URL = process.env.TONAPIIO_TESTNET_URL || 'https://tonapiio-testnet.mytonwallet.org'; + export const BRILLIANT_API_BASE_URL = process.env.BRILLIANT_API_BASE_URL || 'https://mytonwallet-api.herokuapp.com'; export const FRACTION_DIGITS = 9; @@ -91,14 +91,22 @@ export const TONSCAN_BASE_TESTNET_URL = 'https://testnet.tonscan.org/'; export const GETGEMS_BASE_MAINNET_URL = 'https://getgems.io/'; export const GETGEMS_BASE_TESTNET_URL = 'https://testnet.getgems.io/'; +export const CHANGELLY_SUPPORT_EMAIL = 'support@changelly.com'; +export const CHANGELLY_SECURITY_EMAIL = 'security@changelly.com'; +export const CHANGELLY_TERMS_OF_USE = 'https://changelly.com/terms-of-use'; +export const CHANGELLY_PRIVACY_POLICY = 'https://changelly.com/privacy-policy'; +export const CHANGELLY_AML_KYC = 'https://changelly.com/aml-kyc'; +export const CHANGELLY_WAITING_DEADLINE = 3 * 60 * 60 * 1000; // 3 hour + export const TON_TOKEN_SLUG = 'toncoin'; export const JWBTC_TOKEN_SLUG = 'ton-eqdcbkghmc'; +export const JUSDT_TOKEN_SLUG = 'ton-eqbynbo23y'; export const PROXY_HOSTS = process.env.PROXY_HOSTS; export const TINY_TRANSFER_MAX_COST = 0.01; -export const LANG_CACHE_NAME = 'mtw-lang-31'; +export const LANG_CACHE_NAME = 'mtw-lang-53'; export const LANG_LIST: LangItem[] = [{ langCode: 'en', @@ -127,7 +135,72 @@ export const LANG_LIST: LangItem[] = [{ rtl: false, }]; -export const STAKING_CYCLE_DURATION_MS = 129600000; // 36 hours +export const STAKING_CYCLE_DURATION_MS = 131072000; // 36.4 hours export const MIN_BALANCE_FOR_UNSTAKE = 1.02; +export const STAKING_FORWARD_AMOUNT = 1; +export const DEFAULT_FEE = 0.01; export const STAKING_POOLS = process.env.STAKING_POOLS ? process.env.STAKING_POOLS.split(' ') : []; +export const LIQUID_POOL = process.env.LIQUID_POOL || 'EQD2_4d91M4TVbEBVyBF8J1UwpMJc361LKVCz6bBlffMW05o'; +export const LIQUID_JETTON = process.env.LIQUID_JETTON || 'EQCqC6EhRJ_tpWngKxL6dV0k6DSnRUrs9GSVkLbfdCqsj6TE'; +export const STAKING_MIN_AMOUNT = 1; + +export const TON_PROTOCOL = 'ton://'; +export const TONCONNECT_PROTOCOL = 'tc://'; +export const TONCONNECT_UNIVERSAL_URL = 'https://connect.mytonwallet.org'; + +export const TOKEN_INFO = { + toncoin: { + name: 'Toncoin', + symbol: TON_SYMBOL, + slug: TON_TOKEN_SLUG, + cmcSlug: TON_TOKEN_SLUG, + quote: { + price: 1.95, + percentChange1h: 0, + percentChange24h: 0, + percentChange7d: 0, + percentChange30d: 0, + }, + decimals: DEFAULT_DECIMAL_PLACES, + }, +}; + +export const TON_BLOCKCHAIN = 'ton'; + +export const INIT_SWAP_ASSETS = { + toncoin: { + name: 'Toncoin', + symbol: TON_SYMBOL, + blockchain: TON_BLOCKCHAIN, + slug: TON_TOKEN_SLUG, + decimals: DEFAULT_DECIMAL_PLACES, + }, + 'ton-eqdcbkghmc': { + name: 'jWBTC', + symbol: 'jWBTC', + blockchain: TON_BLOCKCHAIN, + slug: 'ton-eqdcbkghmc', + decimals: 8, + // eslint-disable-next-line max-len + image: 'https://cache.tonapi.io/imgproxy/LaFKdzahVX9epWT067gyVLd8aCa1lFrZd7Rp9siViEE/rs:fill:200:200:1/g:no/aHR0cHM6Ly9icmlkZ2UudG9uLm9yZy90b2tlbi8xLzB4MjI2MGZhYzVlNTU0MmE3NzNhYTQ0ZmJjZmVkZjdjMTkzYmMyYzU5OS5wbmc.webp', + contract: 'EQDcBkGHmC4pTf34x3Gm05XvepO5w60DNxZ-XT4I6-UGG5L5', + keywords: ['bitcoin'], + }, +}; + +export const MULTITAB_DATA_CHANNEL_NAME = 'mtw-multitab'; +export const ACTIVE_TAB_STORAGE_KEY = 'mtw-active-tab'; + +export const INDEXED_DB_NAME = 'keyval-store'; +export const INDEXED_DB_STORE_NAME = 'keyval'; + +export const MIN_ASSETS_TAB_VIEW = 5; + +export const DEFAULT_PRICE_CURRENCY = 'USD'; +export const SHORT_CURRENCY_SYMBOL_MAP = { + USD: '$', + EUR: '€', + RUB: '₽', + CNY: '¥', +}; diff --git a/src/electron/autoUpdates.ts b/src/electron/autoUpdates.ts index e8162edd..7dd5cbc6 100644 --- a/src/electron/autoUpdates.ts +++ b/src/electron/autoUpdates.ts @@ -1,43 +1,55 @@ -import { ipcMain } from 'electron'; +import { app, ipcMain, net } from 'electron'; import type { ProgressInfo, UpdateInfo } from 'electron-updater'; import { autoUpdater, CancellationToken } from 'electron-updater'; import { ElectronAction, ElectronEvent } from './types'; -import { forceQuit, IS_MAC_OS, mainWindow } from './utils'; +import { PRODUCTION_URL } from '../config'; +import getIsAppUpdateNeeded from '../util/getIsAppUpdateNeeded'; +import { pause } from '../util/schedulers'; +import { + forceQuit, IS_MAC_OS, IS_PREVIEW, IS_WINDOWS, mainWindow, store, +} from './utils'; +export const AUTO_UPDATE_SETTING_KEY = 'autoUpdate'; + +const ELECTRON_APP_VERSION_URL = 'electronVersion.txt'; const CHECK_UPDATE_INTERVAL = 5 * 60 * 1000; let cancellationToken: CancellationToken = new CancellationToken(); -let interval: NodeJS.Timer; +let isUpdateCheckStarted = false; export function setupAutoUpdates() { - if (!interval) { - autoUpdater.autoDownload = true; - autoUpdater.autoInstallOnAppQuit = true; - autoUpdater.checkForUpdates(); + if (isUpdateCheckStarted) { + return; + } - interval = setInterval(() => autoUpdater.checkForUpdates(), CHECK_UPDATE_INTERVAL); + isUpdateCheckStarted = true; + autoUpdater.autoDownload = true; + autoUpdater.autoInstallOnAppQuit = true; - ipcMain.handle(ElectronAction.DOWNLOAD_UPDATE, () => { - autoUpdater.downloadUpdate(cancellationToken).catch((error) => { - mainWindow.webContents.send(ElectronEvent.UPDATE_ERROR, error); - }); - }); - ipcMain.handle(ElectronAction.CANCEL_UPDATE, () => { - cancellationToken.cancel(); - cancellationToken = new CancellationToken(); - }); - ipcMain.handle(ElectronAction.INSTALL_UPDATE, () => { - if (IS_MAC_OS) { - forceQuit.enable(); - } + checkForUpdates(); - return autoUpdater.quitAndInstall(); + ipcMain.handle(ElectronAction.DOWNLOAD_UPDATE, () => { + autoUpdater.downloadUpdate(cancellationToken).catch((error) => { + mainWindow.webContents.send(ElectronEvent.UPDATE_ERROR, error); }); - } + }); + ipcMain.handle(ElectronAction.INSTALL_UPDATE, () => { + if (IS_MAC_OS || IS_WINDOWS) { + forceQuit.enable(); + } + + return autoUpdater.quitAndInstall(); + }); + ipcMain.handle(ElectronAction.CANCEL_UPDATE, () => { + cancellationToken.cancel(); + cancellationToken = new CancellationToken(); + }); - autoUpdater.on('error', (error: Error) => mainWindow.webContents.send(ElectronEvent.UPDATE_ERROR, error)); + autoUpdater.on('error', (error: Error) => { + mainWindow.webContents.send(ElectronEvent.UPDATE_ERROR, error); + }); autoUpdater.on('update-available', (info: UpdateInfo) => { mainWindow.webContents.send(ElectronEvent.UPDATE_AVAILABLE, info); }); @@ -48,3 +60,51 @@ export function setupAutoUpdates() { mainWindow.webContents.send(ElectronEvent.UPDATE_DOWNLOADED, info); }); } + +export function getIsAutoUpdateEnabled() { + return !IS_PREVIEW && store.get(AUTO_UPDATE_SETTING_KEY); +} + +async function checkForUpdates(): Promise { + while (true) { // eslint-disable-line no-constant-condition + if (await shouldPerformAutoUpdate()) { + if (getIsAutoUpdateEnabled()) { + autoUpdater.checkForUpdates(); + + return; + } + + mainWindow.webContents.send(ElectronEvent.UPDATE_DOWNLOADED); + } + + await pause(CHECK_UPDATE_INTERVAL); + } +} + +function shouldPerformAutoUpdate(): Promise { + return new Promise((resolve) => { + const request = net.request(`${PRODUCTION_URL}/${ELECTRON_APP_VERSION_URL}?${Date.now()}`); + + request.on('response', (response) => { + let contents = ''; + + response.on('end', () => { + resolve(getIsAppUpdateNeeded(contents, app.getVersion())); + }); + + response.on('data', (data: Buffer) => { + contents = `${contents}${String(data)}`; + }); + + response.on('error', () => { + resolve(false); + }); + }); + + request.on('error', () => { + resolve(false); + }); + + request.end(); + }); +} diff --git a/src/electron/config.yml b/src/electron/config.yml index 58c68bdd..97d36277 100644 --- a/src/electron/config.yml +++ b/src/electron/config.yml @@ -7,6 +7,7 @@ extraMetadata: files: - "dist" - "package.json" + - "public/icon-electron-windows.ico" - "!dist/get" - "!dist/**/statoscope-build-statistics.json" - "!dist/**/statoscope-report.html" diff --git a/src/electron/deeplink.ts b/src/electron/deeplink.ts index 95bde6f3..7ce36d79 100644 --- a/src/electron/deeplink.ts +++ b/src/electron/deeplink.ts @@ -67,6 +67,10 @@ export function initDeeplink() { processDeeplink(); if (mainWindow) { + if (!mainWindow.isVisible()) { + mainWindow.show(); + } + if (mainWindow.isMinimized()) { mainWindow.restore(); } @@ -82,11 +86,8 @@ export function processDeeplink() { } if (isTonTransferDeeplink(deeplinkUrl)) { - const parsed = new URL(deeplinkUrl); mainWindow.webContents.send(ElectronEvent.DEEPLINK, { - to: parsed.pathname.replace(/^.*\//g, ''), - amount: Number(parsed.searchParams.get('amount')), - text: parsed.searchParams.get('text'), + url: deeplinkUrl, }); } else if (isTonConnectDeeplink(deeplinkUrl)) { mainWindow.webContents.send(ElectronEvent.DEEPLINK_TONCONNECT, { diff --git a/src/electron/main.ts b/src/electron/main.ts index bde4f2ed..689bf4df 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -5,8 +5,9 @@ import contextMenu from 'electron-context-menu'; import path from 'path'; import { initDeeplink } from './deeplink'; +import { setupSecrets } from './secrets'; import { IS_MAC_OS } from './utils'; -import { createWindow, setupCloseHandlers } from './window'; +import { createWindow, setupCloseHandlers, setupElectronActionHandlers } from './window'; initDeeplink(); @@ -24,5 +25,7 @@ app.on('ready', () => { } createWindow(); + setupElectronActionHandlers(); setupCloseHandlers(); + setupSecrets(); }); diff --git a/src/electron/preload.ts b/src/electron/preload.ts index 4cab3a56..6c3cd5f1 100644 --- a/src/electron/preload.ts +++ b/src/electron/preload.ts @@ -18,6 +18,17 @@ const electronApi: ElectronApi = { toggleDeeplinkHandler: (isEnabled: boolean) => ipcRenderer.invoke(ElectronAction.TOGGLE_DEEPLINK_HANDLER, isEnabled), + getIsTouchIdSupported: () => ipcRenderer.invoke(ElectronAction.GET_IS_TOUCH_ID_SUPPORTED), + encryptPassword: (password: string) => ipcRenderer.invoke(ElectronAction.ENCRYPT_PASSWORD, password), + decryptPassword: (encrypted: string) => ipcRenderer.invoke(ElectronAction.DECRYPT_PASSWORD, encrypted), + + setIsTrayIconEnabled: (value: boolean) => ipcRenderer.invoke(ElectronAction.SET_IS_TRAY_ICON_ENABLED, value), + getIsTrayIconEnabled: () => ipcRenderer.invoke(ElectronAction.GET_IS_TRAY_ICON_ENABLED), + setIsAutoUpdateEnabled: (value: boolean) => ipcRenderer.invoke(ElectronAction.SET_IS_AUTO_UPDATE_ENABLED, value), + getIsAutoUpdateEnabled: () => ipcRenderer.invoke(ElectronAction.GET_IS_AUTO_UPDATE_ENABLED), + + restoreStorage: () => ipcRenderer.invoke(ElectronAction.RESTORE_STORAGE), + on: (eventName: ElectronEvent, callback) => { const subscription = (event: IpcRendererEvent, ...args: any) => callback(...args); diff --git a/src/electron/secrets.ts b/src/electron/secrets.ts new file mode 100644 index 00000000..38147859 --- /dev/null +++ b/src/electron/secrets.ts @@ -0,0 +1,20 @@ +import { ipcMain, safeStorage, systemPreferences } from 'electron'; + +import { ElectronAction } from './types'; + +export function setupSecrets() { + ipcMain.handle(ElectronAction.GET_IS_TOUCH_ID_SUPPORTED, () => { + return safeStorage.isEncryptionAvailable() && systemPreferences.canPromptTouchID(); + }); + ipcMain.handle(ElectronAction.ENCRYPT_PASSWORD, (e, password: string) => { + return safeStorage.encryptString(password).toString('base64'); + }); + ipcMain.handle(ElectronAction.DECRYPT_PASSWORD, async (e, encrypted: string) => { + try { + await systemPreferences.promptTouchID('confirm an operation in MyTonWallet'); + return safeStorage.decryptString(Buffer.from(encrypted, 'base64')); + } catch (err) { + return undefined; + } + }); +} diff --git a/src/electron/storageUtils.ts b/src/electron/storageUtils.ts new file mode 100644 index 00000000..879c6622 --- /dev/null +++ b/src/electron/storageUtils.ts @@ -0,0 +1,152 @@ +import type { StorageKey } from '../api/storages/types'; + +import { ACTIVE_TAB_STORAGE_KEY, INDEXED_DB_NAME, INDEXED_DB_STORE_NAME } from '../config'; +import { checkIsWebContentsUrlAllowed, mainWindow } from './utils'; + +let localStorage: Record | undefined; +let idb: { key: StorageKey; value: any }[] | undefined; + +export function captureStorage(): Promise<[void, void]> { + return Promise.all([captureLocalStorage(), captureIdb()]); +} + +export function restoreStorage(): Promise<[void, void]> { + return Promise.all([restoreLocalStorage(), restoreIdb()]); +} + +async function captureLocalStorage(): Promise { + const contents = mainWindow.webContents; + const contentsUrl = contents.getURL(); + + if (!checkIsWebContentsUrlAllowed(contentsUrl)) { + return; + } + + localStorage = await contents.executeJavaScript('({ ...localStorage });'); +} + +async function captureIdb(): Promise { + const contents = mainWindow.webContents; + const contentsUrl = contents.getURL(); + + if (!checkIsWebContentsUrlAllowed(contentsUrl)) { + return; + } + + idb = await contents.executeJavaScript(` + new Promise((resolve) => { + const request = window.indexedDB.open('${INDEXED_DB_NAME}'); + + request.onupgradeneeded = (event) => { + event.target.transaction.abort(); + resolve(); + } + + request.onsuccess = (event) => { + const result = []; + + const db = event.target.result; + const transaction = db.transaction(['${INDEXED_DB_STORE_NAME}'], 'readonly'); + const store = transaction.objectStore('${INDEXED_DB_STORE_NAME}'); + + store.openCursor().onsuccess = (e) => { + const cursor = e.target.result; + if (cursor) { + result.push({ key: cursor.key, value: cursor.value }); + cursor.continue(); + } else { + resolve(result); + } + }; + + transaction.oncomplete = () => { + db.close(); + }; + + transaction.onerror = () => { + resolve(); + }; + } + + request.onerror = () => { + resolve(); + }; + }); + `); +} + +export async function restoreLocalStorage(): Promise { + if (!localStorage) { + return; + } + + const contents = mainWindow.webContents; + const contentsUrl = contents.getURL(); + + if (!checkIsWebContentsUrlAllowed(contentsUrl)) { + return; + } + + await contents.executeJavaScript( + Object.keys(localStorage) + .filter((key: string) => key !== ACTIVE_TAB_STORAGE_KEY) + .map((key: string) => `localStorage.setItem('${key}', JSON.stringify(${localStorage![key]}))`) + .join(';'), + ); + + localStorage = undefined; +} + +export async function restoreIdb(): Promise { + if (!idb) { + return; + } + + const contents = mainWindow.webContents; + const contentsUrl = contents.getURL(); + + if (!checkIsWebContentsUrlAllowed(contentsUrl)) { + return; + } + + await contents.executeJavaScript(` + new Promise((resolve) => { + const request = window.indexedDB.open('${INDEXED_DB_NAME}'); + + request.onupgradeneeded = (event) => { + const db = event.target.result; + + if (!db.objectStoreNames.contains('${INDEXED_DB_STORE_NAME}')) { + db.createObjectStore('${INDEXED_DB_STORE_NAME}'); + } + } + + request.onsuccess = (event) => { + const result = {}; + + const db = event.target.result; + const transaction = db.transaction(['${INDEXED_DB_STORE_NAME}'], 'readwrite'); + const store = transaction.objectStore('${INDEXED_DB_STORE_NAME}'); + + ${JSON.stringify(idb)}.forEach(item => { + store.put(item.value, item.key); + }); + + transaction.oncomplete = () => { + db.close(); + resolve(); + }; + + transaction.onerror = () => { + resolve(); + }; + } + + request.onerror = () => { + resolve(); + }; + }); + `); + + idb = undefined; +} diff --git a/src/electron/tray.ts b/src/electron/tray.ts new file mode 100644 index 00000000..1d385ec8 --- /dev/null +++ b/src/electron/tray.ts @@ -0,0 +1,123 @@ +import type { BrowserWindow } from 'electron'; +import { + app, Menu, nativeImage, Tray, +} from 'electron'; +import path from 'path'; + +import { forceQuit, mainWindow, store } from './utils'; + +const TRAY_ICON_SETTINGS_KEY = 'trayIcon'; +const WINDOW_BLUR_TIMEOUT = 800; + +interface TrayHelper { + instance?: Tray; + lastFocusedWindow?: BrowserWindow; + lastFocusedWindowTimer?: NodeJS.Timeout; + handleWindowFocus: () => void; + handleWindowBlur: () => void; + handleWindowClose: () => void; + setupListeners: () => void; + removeListeners: () => void; + create: () => void; + enable: () => void; + disable: () => void; + isEnabled: boolean; +} + +const tray: TrayHelper = { + handleWindowFocus() { + clearTimeout(this.lastFocusedWindowTimer as unknown as NodeJS.Timeout); + this.lastFocusedWindow = mainWindow; + }, + + handleWindowBlur() { + this.lastFocusedWindowTimer = setTimeout(() => { + if (this.lastFocusedWindow === mainWindow) { + this.lastFocusedWindow = undefined; + } + }, WINDOW_BLUR_TIMEOUT); + }, + + handleWindowClose() { + this.lastFocusedWindow = undefined; + }, + + setupListeners() { + this.handleWindowFocus = this.handleWindowFocus.bind(this); + this.handleWindowBlur = this.handleWindowBlur.bind(this); + this.handleWindowClose = this.handleWindowClose.bind(this); + + mainWindow.on('focus', this.handleWindowFocus); + mainWindow.on('blur', this.handleWindowBlur); + mainWindow.on('close', this.handleWindowClose); + }, + + removeListeners() { + mainWindow.removeListener('focus', this.handleWindowFocus); + mainWindow.removeListener('blur', this.handleWindowBlur); + mainWindow.removeListener('close', this.handleWindowClose); + }, + + create() { + if (this.instance) { + return; + } + + this.setupListeners(); + + const icon = nativeImage.createFromPath(path.resolve(__dirname, '../public/icon-electron-windows.ico')); + const title = app.getName(); + + this.instance = new Tray(icon); + + const handleOpenFromTray = () => { + if (!mainWindow.isVisible()) { + mainWindow.show(); + } else { + mainWindow.focus(); + } + }; + + const handleCloseFromTray = () => { + forceQuit.enable(); + app.quit(); + }; + + const handleTrayClick = () => { + if (this.lastFocusedWindow) { + mainWindow.hide(); + this.lastFocusedWindow = undefined; + } else { + handleOpenFromTray(); + } + }; + + const contextMenu = Menu.buildFromTemplate([ + { label: `Open ${title}`, click: handleOpenFromTray }, + { label: `Quit ${title}`, click: handleCloseFromTray }, + ]); + + this.instance.on('click', handleTrayClick); + this.instance.setContextMenu(contextMenu); + this.instance.setToolTip(title); + this.instance.setTitle(title); + }, + + enable() { + store.set(TRAY_ICON_SETTINGS_KEY, true); + this.create(); + }, + + disable() { + store.set(TRAY_ICON_SETTINGS_KEY, false); + this.instance?.destroy(); + this.instance = undefined; + this.removeListeners(); + }, + + get isEnabled(): boolean { + return store.get(TRAY_ICON_SETTINGS_KEY, true) as boolean; + }, +}; + +export default tray; diff --git a/src/electron/types.ts b/src/electron/types.ts index 2b9e32c5..86627327 100644 --- a/src/electron/types.ts +++ b/src/electron/types.ts @@ -20,6 +20,17 @@ export enum ElectronAction { HANDLE_DOUBLE_CLICK = 'handle-double-click', TOGGLE_DEEPLINK_HANDLER = 'toggle-deeplink-handler', + + GET_IS_TOUCH_ID_SUPPORTED = 'get-is-touch-id-supported', + ENCRYPT_PASSWORD = 'encrypt-password', + DECRYPT_PASSWORD = 'decrypt-password', + + SET_IS_TRAY_ICON_ENABLED = 'set-is-tray-icon-enabled', + GET_IS_TRAY_ICON_ENABLED = 'get-is-tray-icon-enabled', + SET_IS_AUTO_UPDATE_ENABLED = 'set-is-auto-update-enabled', + GET_IS_AUTO_UPDATE_ENABLED = 'get-is-auto-update-enabled', + + RESTORE_STORAGE = 'restore-storage', } export interface ElectronApi { @@ -36,6 +47,17 @@ export interface ElectronApi { toggleDeeplinkHandler: (isEnabled: boolean) => Promise; + getIsTouchIdSupported: () => Promise; + encryptPassword: (password: string) => Promise; + decryptPassword: (encrypted: string) => Promise; + + setIsTrayIconEnabled: (value: boolean) => Promise; + getIsTrayIconEnabled: () => Promise; + setIsAutoUpdateEnabled: (value: boolean) => Promise; + getIsAutoUpdateEnabled: () => Promise; + + restoreStorage: () => Promise; + on: (eventName: ElectronEvent, callback: any) => VoidFunction; } diff --git a/src/electron/utils.ts b/src/electron/utils.ts index 28f0e8df..835cff7e 100644 --- a/src/electron/utils.ts +++ b/src/electron/utils.ts @@ -1,10 +1,37 @@ import type { BrowserWindow } from 'electron'; +import { app } from 'electron'; +import Store from 'electron-store'; +import fs from 'fs'; + +import { BASE_URL, PRODUCTION_URL } from '../config'; + +const ALLOWED_URL_ORIGINS = [BASE_URL!, PRODUCTION_URL].map((url) => (new URL(url).origin)); + +export function checkIsWebContentsUrlAllowed(url: string): boolean { + if (!app.isPackaged) { + return true; + } + + const parsedUrl = new URL(url); + + if (parsedUrl.pathname === encodeURI(`${__dirname}/index.html`)) { + return true; + } + + return ALLOWED_URL_ORIGINS.includes(parsedUrl.origin); +} + +export const WINDOW_STATE_FILE = 'window-state.json'; export const IS_MAC_OS = process.platform === 'darwin'; export const IS_WINDOWS = process.platform === 'win32'; export const IS_LINUX = process.platform === 'linux'; +export const IS_PREVIEW = process.env.IS_PREVIEW === 'true'; +export const IS_FIRST_RUN = !fs.existsSync(`${app.getPath('userData')}/${WINDOW_STATE_FILE}`); export let mainWindow: BrowserWindow; // eslint-disable-line import/no-mutable-exports +export const store: Store = new Store(); + export function setMainWindow(window: BrowserWindow) { mainWindow = window; } diff --git a/src/electron/window.ts b/src/electron/window.ts index 253a8502..09c0148c 100644 --- a/src/electron/window.ts +++ b/src/electron/window.ts @@ -6,16 +6,21 @@ import path from 'path'; import { ElectronAction } from './types'; -import { setupAutoUpdates } from './autoUpdates'; +import { APP_ENV, BASE_URL } from '../config'; +import { AUTO_UPDATE_SETTING_KEY, getIsAutoUpdateEnabled, setupAutoUpdates } from './autoUpdates'; import { processDeeplink } from './deeplink'; +import { captureStorage, restoreStorage } from './storageUtils'; +import tray from './tray'; import { - forceQuit, IS_MAC_OS, mainWindow, setMainWindow, + checkIsWebContentsUrlAllowed, forceQuit, IS_FIRST_RUN, IS_MAC_OS, IS_PREVIEW, IS_WINDOWS, + mainWindow, setMainWindow, store, WINDOW_STATE_FILE, } from './utils'; const ALLOWED_DEVICE_ORIGINS = ['http://localhost:4321', 'file://']; export function createWindow() { const windowState = windowStateKeeper({ + file: WINDOW_STATE_FILE, defaultWidth: 980, defaultHeight: 788, }); @@ -31,7 +36,7 @@ export function createWindow() { title: 'MyTonWallet', webPreferences: { preload: path.join(__dirname, 'preload.js'), - devTools: process.env.APP_ENV !== 'production', + devTools: APP_ENV !== 'production', }, titleBarStyle: 'hidden', ...(IS_MAC_OS && { @@ -56,17 +61,54 @@ export function createWindow() { return deviceType === 'hid' && ALLOWED_DEVICE_ORIGINS.includes(origin); }); - if (app.isPackaged) { - mainWindow.loadURL(`file://${__dirname}/index.html`); - } else { - mainWindow.loadURL('http://localhost:4321'); - mainWindow.webContents.openDevTools(); - } + window.webContents.on('will-navigate', (event, newUrl) => { + if (!checkIsWebContentsUrlAllowed(newUrl)) { + event.preventDefault(); + } + }); if (!IS_MAC_OS) { setupWindowsTitleBar(); } + if (IS_WINDOWS && tray.isEnabled) { + tray.create(); + } + + mainWindow.webContents.once('dom-ready', async () => { + processDeeplink(); + + if (APP_ENV === 'production') { + setupAutoUpdates(); + } + + if (!IS_FIRST_RUN && getIsAutoUpdateEnabled() === undefined) { + store.set(AUTO_UPDATE_SETTING_KEY, true); + await captureStorage(); + loadWindowUrl(); + } + + mainWindow.show(); + }); + + loadWindowUrl(); +} + +function loadWindowUrl(): void { + if (!app.isPackaged) { + mainWindow.loadURL('http://localhost:4321'); + mainWindow.webContents.openDevTools(); + } else if (getIsAutoUpdateEnabled()) { + mainWindow.loadURL(BASE_URL!); + } else if (getIsAutoUpdateEnabled() === undefined && IS_FIRST_RUN) { + store.set(AUTO_UPDATE_SETTING_KEY, true); + mainWindow.loadURL(BASE_URL!); + } else { + mainWindow.loadURL(`file://${__dirname}/index.html`); + } +} + +export function setupElectronActionHandlers() { ipcMain.handle(ElectronAction.HANDLE_DOUBLE_CLICK, () => { const doubleClickAction = systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string'); @@ -81,19 +123,36 @@ export function createWindow() { } }); - mainWindow.webContents.once('dom-ready', () => { - mainWindow.show(); - processDeeplink(); + ipcMain.handle(ElectronAction.SET_IS_TRAY_ICON_ENABLED, (_, isTrayIconEnabled: boolean) => { + if (isTrayIconEnabled) { + tray.enable(); + } else { + tray.disable(); + } + }); - if (process.env.APP_ENV === 'production') { - setupAutoUpdates(); + ipcMain.handle(ElectronAction.GET_IS_TRAY_ICON_ENABLED, () => tray.isEnabled); + + ipcMain.handle(ElectronAction.SET_IS_AUTO_UPDATE_ENABLED, async (_, isAutoUpdateEnabled: boolean) => { + if (IS_PREVIEW) { + return; } + + store.set(AUTO_UPDATE_SETTING_KEY, isAutoUpdateEnabled); + await captureStorage(); + loadWindowUrl(); + }); + + ipcMain.handle(ElectronAction.GET_IS_AUTO_UPDATE_ENABLED, () => { + return store.get(AUTO_UPDATE_SETTING_KEY, true); }); + + ipcMain.handle(ElectronAction.RESTORE_STORAGE, () => restoreStorage()); } export function setupCloseHandlers() { mainWindow.on('close', (event: Event) => { - if (IS_MAC_OS) { + if (IS_MAC_OS || (IS_WINDOWS && tray.isEnabled)) { if (forceQuit.isEnabled) { app.exit(0); forceQuit.disable(); @@ -135,7 +194,7 @@ export function setupCloseHandlers() { function setupWindowsTitleBar() { mainWindow.removeMenu(); - ipcMain.handle(ElectronAction.CLOSE, () => mainWindow.destroy()); + ipcMain.handle(ElectronAction.CLOSE, () => mainWindow.close()); ipcMain.handle(ElectronAction.MINIMIZE, () => mainWindow.minimize()); ipcMain.handle(ElectronAction.MAXIMIZE, () => mainWindow.maximize()); ipcMain.handle(ElectronAction.UNMAXIMIZE, () => mainWindow.unmaximize()); diff --git a/src/extension/pageScript/deeplinkHook.ts b/src/extension/pageScript/deeplinkHook.ts index 086180ff..37ecbc02 100644 --- a/src/extension/pageScript/deeplinkHook.ts +++ b/src/extension/pageScript/deeplinkHook.ts @@ -1,3 +1,4 @@ +import { parseTonDeeplink } from '../../util/ton/deeplinks'; import { callApi } from '../../api/providers/extension/connectorForPageScript'; const originalOpenFn = window.open; @@ -36,24 +37,9 @@ function patchedOpenFn(url?: string | URL, ...args: any[]) { } function tryHandleDeeplink(value: any) { - if (typeof value !== 'string' || !value.startsWith('ton://transfer/')) { - return false; - } + const params = parseTonDeeplink(value); + if (!params) return false; - try { - const url = new URL(value); - const params = { - to: url.pathname.replace('//transfer/', ''), - amount: url.searchParams.get('amount'), - text: url.searchParams.get('text'), - }; - void callApi('prepareTransaction', { - to: params.to, - amount: params.amount, - comment: params.text, - }); - return true; - } catch (err) { - return false; - } + void callApi('prepareTransaction', params); + return true; } diff --git a/src/global/actions/api/auth.ts b/src/global/actions/api/auth.ts index eccf5a51..22f8b710 100644 --- a/src/global/actions/api/auth.ts +++ b/src/global/actions/api/auth.ts @@ -1,18 +1,38 @@ -import { AppState, AuthState, HardwareConnectState } from '../../types'; +import { NativeBiometric } from '@capgo/capacitor-native-biometric'; -import { MNEMONIC_CHECK_COUNT, MNEMONIC_COUNT } from '../../../config'; +import { ApiCommonError } from '../../../api/types'; +import { + AppState, AuthState, BiometricsState, HardwareConnectState, +} from '../../types'; + +import { + APP_NAME, IS_CAPACITOR, MNEMONIC_CHECK_COUNT, MNEMONIC_COUNT, +} from '../../../config'; import { parseAccountId } from '../../../util/account'; +import authApi from '../../../util/authApi'; +import webAuthn from '../../../util/authApi/webAuthn'; +import { + getIsBiometricAuthSupported, + getIsNativeBiometricAuthSupported, + vibrateOnError, + vibrateOnSuccess, +} from '../../../util/capacitor'; import { cloneDeep } from '../../../util/iteratees'; import { pause } from '../../../util/schedulers'; +import { IS_DELEGATED_BOTTOM_SHEET, IS_ELECTRON } from '../../../util/windowEnvironment'; import { callApi } from '../../../api'; import { addActionHandler, getGlobal, setGlobal } from '../..'; import { INITIAL_STATE } from '../../initialState'; import { + clearCurrentSwap, clearCurrentTransfer, + clearIsPinPadPasswordAccepted, createAccount, updateAuth, + updateBiometrics, updateCurrentAccountState, updateHardware, + updateIsPinPadPasswordAccepted, updateSettings, } from '../../reducers'; import { @@ -20,11 +40,15 @@ import { selectCurrentNetwork, selectFirstNonHardwareAccount, selectIsOneAccount, + selectLastLedgerAccountIndex, selectNetworkAccountsMemoized, selectNewestTxIds, } from '../../selectors'; +import { callActionInMain } from '../../../hooks/useDelegatedBottomSheet'; + const CREATING_DURATION = 3300; +const NATIVE_BIOMETRICS_PAUSE_MS = 750; addActionHandler('restartAuth', (global) => { if (global.currentAccountId) { @@ -46,6 +70,19 @@ addActionHandler('startCreatingWallet', async (global, actions) => { const isFirstAccount = !Object.values(accounts).length; const methodState = isFirstAccount ? AuthState.creatingWallet : AuthState.createBackup; + const network = selectCurrentNetwork(global); + const checkResult = await callApi('checkApiAvailability', { + blockchainKey: 'ton', + network, + }); + + if (!checkResult) { + actions.showError({ error: ApiCommonError.ServerError }); + return; + } + + global = getGlobal(); + const promiseCalls = [ callApi('generateMnemonic'), ...(isFirstAccount ? [pause(CREATING_DURATION)] : []), @@ -75,10 +112,51 @@ addActionHandler('startCreatingWallet', async (global, actions) => { return; } - setGlobal(updateAuth(global, { state: AuthState.createPassword })); + setGlobal(updateAuth(global, { + state: getIsBiometricAuthSupported() + ? (getIsNativeBiometricAuthSupported() ? AuthState.createPin : AuthState.createBiometrics) + : AuthState.createPassword, + })); + + if (isFirstAccount) { + actions.requestConfetti(); + if (IS_CAPACITOR) { + void vibrateOnSuccess(); + } + } }); -addActionHandler('afterCreatePassword', (global, actions, { password }) => { +addActionHandler('createPin', (global, actions, { pin, isImporting }) => { + global = updateAuth(global, { + state: isImporting ? AuthState.importWalletConfirmPin : AuthState.confirmPin, + password: pin, + }); + setGlobal(global); +}); + +addActionHandler('confirmPin', (global, actions, { isImporting }) => { + global = updateAuth(global, { + state: isImporting ? AuthState.importWalletCreateNativeBiometrics : AuthState.createNativeBiometrics, + }); + setGlobal(global); +}); + +addActionHandler('cancelConfirmPin', (global, actions, { isImporting }) => { + global = updateAuth(global, { + state: isImporting ? AuthState.importWalletCreatePin : AuthState.createPin, + }); + setGlobal(global); +}); + +addActionHandler('cancelDisclaimer', (global) => { + setGlobal(updateAuth(global, { + state: getIsBiometricAuthSupported() + ? (getIsNativeBiometricAuthSupported() ? AuthState.createPin : AuthState.createBiometrics) + : AuthState.createPassword, + })); +}); + +addActionHandler('afterCreatePassword', (global, actions, { password, isPasswordNumeric }) => { setGlobal(updateAuth(global, { isLoading: true })); const { method } = getGlobal().auth; @@ -91,10 +169,102 @@ addActionHandler('afterCreatePassword', (global, actions, { password }) => { return; } - actions.createAccount({ password, isImporting }); + actions.createAccount({ password, isImporting, isPasswordNumeric }); +}); + +addActionHandler('afterCreateBiometrics', async (global, actions) => { + const withCredential = !IS_ELECTRON; + global = updateAuth(global, { + isLoading: true, + error: undefined, + biometricsStep: withCredential ? 1 : undefined, + }); + setGlobal(global); + + try { + const credential = withCredential + ? await webAuthn.createCredential() + : undefined; + global = getGlobal(); + global = updateAuth(global, { biometricsStep: withCredential ? 2 : undefined }); + setGlobal(global); + const result = await authApi.setupBiometrics({ credential }); + + global = getGlobal(); + global = updateAuth(global, { + isLoading: false, + biometricsStep: undefined, + }); + + if (!result) { + global = updateAuth(global, { error: 'Biometric setup failed' }); + setGlobal(global); + + return; + } + + global = updateSettings(global, { authConfig: result.config }); + setGlobal(global); + + actions.afterCreatePassword({ password: result.password }); + } catch (err: any) { + const error = err?.message.includes('privacy-considerations-client') + ? 'Biometric setup failed' + : (err?.message || 'Biometric setup failed'); + global = getGlobal(); + global = updateAuth(global, { + isLoading: false, + error, + biometricsStep: undefined, + }); + setGlobal(global); + } +}); + +addActionHandler('afterCreateNativeBiometrics', async (global, actions) => { + global = updateAuth(global, { + isLoading: true, + error: undefined, + }); + setGlobal(global); + + try { + const { password } = global.auth; + const result = await authApi.setupNativeBiometrics(password!); + + global = getGlobal(); + global = updateAuth(global, { isLoading: false }); + global = updateSettings(global, { authConfig: result.config }); + setGlobal(global); + + actions.afterCreatePassword({ password: password!, isPasswordNumeric: true }); + } catch (err: any) { + const error = err?.message.includes('privacy-considerations-client') + ? 'Biometric setup failed' + : (err?.message || 'Biometric setup failed'); + global = getGlobal(); + global = updateAuth(global, { + isLoading: false, + error, + }); + setGlobal(global); + } +}); + +addActionHandler('skipCreateNativeBiometrics', (global, actions) => { + const { password } = global.auth; + + global = updateAuth(global, { isLoading: false, error: undefined }); + global = updateSettings(global, { + authConfig: { kind: 'password' }, + isPasswordNumeric: true, + }); + setGlobal(global); + + actions.afterCreatePassword({ password: password!, isPasswordNumeric: true }); }); -addActionHandler('createAccount', async (global, actions, { password, isImporting }) => { +addActionHandler('createAccount', async (global, actions, { password, isImporting, isPasswordNumeric }) => { setGlobal(updateAuth(global, { isLoading: true })); const network = selectCurrentNetwork(getGlobal()); @@ -108,40 +278,40 @@ addActionHandler('createAccount', async (global, actions, { password, isImportin global = getGlobal(); - if (!result) { + if (!result || 'error' in result) { setGlobal(updateAuth(global, { isLoading: undefined })); + actions.showError({ error: result?.error }); return; } + const { accountId, address } = result; global = updateAuth(global, { + address, + accountId, isLoading: undefined, password: undefined, + ...(isPasswordNumeric && { isPasswordNumeric: true }), }); - const { accountId, address } = result; if (isImporting) { - global = { ...global, currentAccountId: accountId }; - global = updateAuth(global, { - state: AuthState.ready, - }); - global = createAccount(global, accountId, address); - setGlobal(global); - - actions.afterSignIn(); - if (selectIsOneAccount(global)) { - actions.resetApiSettings(); + const hasAccounts = Object.keys(selectAccounts(global) || {}).length > 0; + if (hasAccounts) { + setGlobal(global); + actions.afterConfirmDisclaimer(); + + return; + } else { + global = updateAuth(global, { state: AuthState.disclaimer }); } } else { const accounts = selectAccounts(global) ?? {}; const isFirstAccount = !Object.values(accounts).length; global = updateAuth(global, { state: isFirstAccount ? AuthState.disclaimerAndBackup : AuthState.createBackup, - accountId, - address, }); - - setGlobal(global); } + + setGlobal(global); }); addActionHandler('createHardwareAccounts', async (global, actions) => { @@ -187,6 +357,11 @@ addActionHandler('createHardwareAccounts', async (global, actions) => { if (isFirstAccount) { actions.afterSignIn(); actions.resetApiSettings(); + actions.requestConfetti(); + + if (IS_CAPACITOR) { + void vibrateOnSuccess(); + } } }); @@ -259,11 +434,24 @@ addActionHandler('afterImportMnemonic', async (global, actions, { mnemonic }) => global = updateAuth(global, { mnemonic, error: undefined, - ...(!hasAccounts && { state: AuthState.disclaimer }), + ...(!hasAccounts && { + state: getIsBiometricAuthSupported() + ? (getIsNativeBiometricAuthSupported() + ? AuthState.importWalletCreatePin + : AuthState.importWalletCreateBiometrics + ) + : AuthState.importWalletCreatePassword, + }), }); setGlobal(global); - if (hasAccounts) { + if (!hasAccounts) { + actions.requestConfetti(); + + if (IS_CAPACITOR) { + void vibrateOnSuccess(); + } + } else { actions.confirmDisclaimer(); } }); @@ -278,7 +466,21 @@ addActionHandler('confirmDisclaimer', (global, actions) => { return; } - setGlobal(updateAuth(global, { state: AuthState.importWalletCreatePassword })); + actions.afterConfirmDisclaimer(); +}); + +addActionHandler('afterConfirmDisclaimer', (global, actions) => { + const { accountId, address } = global.auth; + + global = { ...global, currentAccountId: accountId }; + global = updateAuth(global, { state: AuthState.ready }); + global = createAccount(global, accountId!, address!); + setGlobal(global); + + actions.afterSignIn(); + if (selectIsOneAccount(global)) { + actions.resetApiSettings(); + } }); addActionHandler('cleanAuthError', (global) => { @@ -296,6 +498,10 @@ export function selectMnemonicForCheck() { } addActionHandler('startChangingNetwork', (global, actions, { network }) => { + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('startChangingNetwork', { network }); + } + const accountIds = Object.keys(selectNetworkAccountsMemoized(network, global.accounts!.byId)!); if (accountIds.length) { @@ -311,7 +517,12 @@ addActionHandler('startChangingNetwork', (global, actions, { network }) => { } }); -addActionHandler('switchAccount', async (global, actions, { accountId, newNetwork }) => { +addActionHandler('switchAccount', async (global, actions, payload) => { + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('switchAccount', payload); + } + + const { accountId, newNetwork } = payload; const newestTxIds = selectNewestTxIds(global, accountId); await callApi('activateAccount', accountId, newestTxIds); @@ -321,6 +532,7 @@ addActionHandler('switchAccount', async (global, actions, { accountId, newNetwor }; global = clearCurrentTransfer(global); + global = clearCurrentSwap(global); setGlobal(global); if (newNetwork) { @@ -328,75 +540,86 @@ addActionHandler('switchAccount', async (global, actions, { accountId, newNetwor } }); -addActionHandler('connectHardwareWallet', async (global) => { - setGlobal( - updateHardware(global, { - hardwareState: HardwareConnectState.Connecting, - hardwareWallets: undefined, - hardwareSelectedIndices: undefined, - isLedgerConnected: undefined, - isTonAppConnected: undefined, - }), - ); +addActionHandler('connectHardwareWallet', async (global, actions) => { + global = updateHardware(global, { + hardwareState: HardwareConnectState.Connecting, + hardwareWallets: undefined, + hardwareSelectedIndices: undefined, + isLedgerConnected: undefined, + isTonAppConnected: undefined, + }); + setGlobal(global); const ledgerApi = await import('../../../util/ledger'); const isLedgerConnected = await ledgerApi.connectLedger(); + global = getGlobal(); + if (!isLedgerConnected) { - setGlobal( - updateHardware(getGlobal(), { - isLedgerConnected: false, - hardwareState: HardwareConnectState.Failed, - }), - ); + global = updateHardware(global, { + isLedgerConnected: false, + hardwareState: HardwareConnectState.Failed, + }); + setGlobal(global); return; } - setGlobal( - updateHardware(getGlobal(), { - isLedgerConnected: true, - }), - ); + global = updateHardware(global, { + isLedgerConnected: true, + }); + setGlobal(global); const isTonAppConnected = await ledgerApi.waitLedgerTonApp(); + global = getGlobal(); if (!isTonAppConnected) { - setGlobal( - updateHardware(getGlobal(), { - isTonAppConnected: false, - hardwareState: HardwareConnectState.Failed, - }), - ); + global = updateHardware(global, { + isTonAppConnected: false, + hardwareState: HardwareConnectState.Failed, + }); + setGlobal(global); return; } - setGlobal( - updateHardware(getGlobal(), { - isTonAppConnected: true, - }), - ); + global = updateHardware(global, { + isTonAppConnected: true, + }); + setGlobal(global); try { - const network = selectCurrentNetwork(getGlobal()); - const hardwareWallets = await ledgerApi.getFirstLedgerWallets(network); + global = getGlobal(); + const { isRemoteTab } = global.hardware; + const network = selectCurrentNetwork(global); + const lastIndex = selectLastLedgerAccountIndex(global, network); + const hardwareWallets = isRemoteTab ? [] : await ledgerApi.getNextLedgerWallets(network, lastIndex); - setGlobal( - updateHardware(getGlobal(), { - hardwareWallets, - hardwareState: HardwareConnectState.Connected, - }), - ); + global = getGlobal(); + + if ('error' in hardwareWallets) { + actions.showError({ error: hardwareWallets.error }); + throw Error(hardwareWallets.error); + } + + const nextHardwareState = isRemoteTab || hardwareWallets.length === 1 + ? HardwareConnectState.ConnectedWithSingleWallet + : HardwareConnectState.ConnectedWithSeveralWallets; + + global = updateHardware(global, { + hardwareWallets, + hardwareState: nextHardwareState, + }); + setGlobal(global); } catch (err) { - setGlobal( - updateHardware(getGlobal(), { - hardwareState: HardwareConnectState.Failed, - }), - ); + global = getGlobal(); + global = updateHardware(global, { + hardwareState: HardwareConnectState.Failed, + }); + setGlobal(global); } }); addActionHandler('afterSelectHardwareWallets', (global, actions, { hardwareSelectedIndices }) => { - global = updateAuth(getGlobal(), { + global = updateAuth(global, { method: 'importHardwareWallet', error: undefined, }); @@ -410,11 +633,250 @@ addActionHandler('afterSelectHardwareWallets', (global, actions, { hardwareSelec }); addActionHandler('resetHardwareWalletConnect', (global) => { - setGlobal( - updateHardware(global, { - hardwareState: HardwareConnectState.Connect, - isLedgerConnected: undefined, - isTonAppConnected: undefined, - }), - ); + global = updateHardware(global, { + hardwareState: HardwareConnectState.Connect, + isLedgerConnected: undefined, + isTonAppConnected: undefined, + }); + setGlobal(global); +}); + +addActionHandler('enableBiometrics', async (global, actions, { password }) => { + if (!(await callApi('verifyPassword', password))) { + global = getGlobal(); + global = updateBiometrics(global, { error: 'Wrong password, please try again' }); + setGlobal(global); + + return; + } + + global = getGlobal(); + global = updateBiometrics(global, { + error: undefined, + state: BiometricsState.TurnOnRegistration, + }); + setGlobal(global); + + try { + const credential = IS_ELECTRON + ? undefined + : await webAuthn.createCredential(); + + global = getGlobal(); + global = updateBiometrics(global, { state: BiometricsState.TurnOnVerification }); + setGlobal(global); + + const result = await authApi.setupBiometrics({ credential }); + + global = getGlobal(); + if (!result) { + global = updateBiometrics(global, { + error: 'Biometric setup failed', + state: BiometricsState.TurnOnPasswordConfirmation, + }); + setGlobal(global); + + return; + } + global = updateBiometrics(global, { state: BiometricsState.TurnOnComplete }); + setGlobal(global); + + await callApi('changePassword', password, result.password); + + global = getGlobal(); + global = updateSettings(global, { authConfig: result.config }); + + setGlobal(global); + } catch (err: any) { + const error = err?.message.includes('privacy-considerations-client') + ? 'Biometric setup failed' + : (err?.message || 'Biometric setup failed'); + global = getGlobal(); + global = updateBiometrics(global, { + error, + state: BiometricsState.TurnOnPasswordConfirmation, + }); + setGlobal(global); + } +}); + +addActionHandler('disableBiometrics', async (global, actions, { password, isPasswordNumeric }) => { + const { password: oldPassword } = global.biometrics; + + if (!password || !oldPassword) { + global = updateBiometrics(global, { error: 'Biometric confirmation failed' }); + setGlobal(global); + + return; + } + + try { + await callApi('changePassword', oldPassword, password); + } catch (err: any) { + global = getGlobal(); + global = updateBiometrics(global, { error: err?.message || 'Failed to disable biometrics' }); + setGlobal(global); + + return; + } + + global = getGlobal(); + global = updateBiometrics(global, { + state: BiometricsState.TurnOffComplete, + error: undefined, + }); + global = updateSettings(global, { + authConfig: { kind: 'password' }, + isPasswordNumeric, + }); + setGlobal(global); +}); + +addActionHandler('closeBiometricSettings', (global) => { + global = { ...global, biometrics: cloneDeep(INITIAL_STATE.biometrics) }; + + setGlobal(global); +}); + +addActionHandler('openBiometricsTurnOn', (global) => { + global = updateBiometrics(global, { state: BiometricsState.TurnOnPasswordConfirmation }); + + setGlobal(global); +}); + +addActionHandler('openBiometricsTurnOffWarning', (global) => { + global = updateBiometrics(global, { state: BiometricsState.TurnOffWarning }); + + setGlobal(global); +}); + +addActionHandler('openBiometricsTurnOff', async (global) => { + global = updateBiometrics(global, { state: BiometricsState.TurnOffBiometricConfirmation }); + setGlobal(global); + + const password = await authApi.getPassword(global.settings.authConfig!); + global = getGlobal(); + + if (!password) { + global = updateBiometrics(global, { error: 'Biometric confirmation failed' }); + } else { + global = updateBiometrics(global, { + state: BiometricsState.TurnOffCreatePassword, + password, + }); + } + + setGlobal(global); +}); + +addActionHandler('disableNativeBiometrics', (global) => { + global = updateSettings(global, { + authConfig: { kind: 'password' }, + isPasswordNumeric: true, + }); + setGlobal(global); +}); + +addActionHandler('enableNativeBiometrics', async (global, actions, { password }) => { + if (!(await callApi('verifyPassword', password))) { + global = getGlobal(); + global = { + ...global, + nativeBiometricsError: 'Incorrect code, please try again', + }; + global = clearIsPinPadPasswordAccepted(global); + setGlobal(global); + + return; + } + + global = getGlobal(); + + global = updateIsPinPadPasswordAccepted(global); + global = { + ...global, + nativeBiometricsError: undefined, + }; + setGlobal(global); + + try { + const isVerified = await NativeBiometric.verifyIdentity({ + title: APP_NAME, + subtitle: '', + }) + .then(() => true) + .catch(() => false); + + if (!isVerified) { + global = getGlobal(); + global = { + ...global, + nativeBiometricsError: 'Failed to enable biometrics', + }; + global = clearIsPinPadPasswordAccepted(global); + setGlobal(global); + void vibrateOnError(); + + return; + } + + const result = await authApi.setupNativeBiometrics(password); + await pause(NATIVE_BIOMETRICS_PAUSE_MS); + + global = getGlobal(); + global = updateSettings(global, { + authConfig: result.config, + }); + global = { + ...global, + nativeBiometricsError: undefined, + }; + setGlobal(global); + + void vibrateOnSuccess(); + } catch (err: any) { + global = getGlobal(); + global = { + ...global, + nativeBiometricsError: err?.message || 'Failed to enable biometrics', + }; + global = clearIsPinPadPasswordAccepted(global); + setGlobal(global); + + void vibrateOnError(); + } +}); + +addActionHandler('clearNativeBiometricsError', (global) => { + return { + ...global, + nativeBiometricsError: undefined, + }; +}); + +addActionHandler('openAuthBackupWalletModal', (global) => { + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('openAuthBackupWalletModal'); + return; + } + + global = updateAuth(global, { isBackupModalOpen: true }); + setGlobal(global); +}); + +addActionHandler('closeAuthBackupWalletModal', (global, actions, props) => { + const { isBackupCreated } = props || {}; + + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('closeAuthBackupWalletModal', props); + } + + global = updateAuth(global, { + isBackupModalOpen: undefined, + }); + setGlobal(global); + + if (!IS_DELEGATED_BOTTOM_SHEET && isBackupCreated) { + actions.afterCheckMnemonic(); + } }); diff --git a/src/global/actions/api/dapps.ts b/src/global/actions/api/dapps.ts index e91c329f..ddc70a25 100644 --- a/src/global/actions/api/dapps.ts +++ b/src/global/actions/api/dapps.ts @@ -1,5 +1,9 @@ import { DappConnectState, TransferState } from '../../types'; +import { IS_CAPACITOR } from '../../../config'; +import { vibrateOnSuccess } from '../../../util/capacitor'; +import { pause } from '../../../util/schedulers'; +import { IS_DELEGATED_BOTTOM_SHEET } from '../../../util/windowEnvironment'; import { callApi } from '../../../api'; import { ApiUserRejectsError } from '../../../api/errors'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; @@ -7,11 +11,18 @@ import { clearConnectedDapps, clearCurrentDappTransfer, clearDappConnectRequest, + clearIsPinPadPasswordAccepted, removeConnectedDapp, updateConnectedDapps, updateCurrentDappTransfer, updateDappConnectRequest, + updateIsPinPadPasswordAccepted, } from '../../reducers'; +import { selectAccount, selectIsHardwareAccount, selectNewestTxIds } from '../../selectors'; + +import { callActionInMain } from '../../../hooks/useDelegatedBottomSheet'; + +const GET_DAPPS_PAUSE = 250; addActionHandler('submitDappConnectRequestConfirm', async (global, actions, { password, accountId }) => { const { @@ -20,7 +31,22 @@ addActionHandler('submitDappConnectRequestConfirm', async (global, actions, { pa if (permissions?.isPasswordRequired && (!password || !(await callApi('verifyPassword', password)))) { global = getGlobal(); - setGlobal(updateDappConnectRequest(global, { error: 'Wrong password, please try again' })); + global = updateDappConnectRequest(global, { error: 'Wrong password, please try again' }); + setGlobal(global); + + return; + } + + if (IS_CAPACITOR) { + global = getGlobal(); + global = updateIsPinPadPasswordAccepted(global); + setGlobal(global); + + await vibrateOnSuccess(true); + } + + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('submitDappConnectRequestConfirm', { password, accountId }); return; } @@ -37,6 +63,7 @@ addActionHandler('submitDappConnectRequestConfirm', async (global, actions, { pa const { currentAccountId } = global; + await pause(GET_DAPPS_PAUSE); const result = await callApi('getDapps', currentAccountId!); if (!result) { @@ -84,6 +111,7 @@ addActionHandler( const { currentAccountId } = global; + await pause(GET_DAPPS_PAUSE); const result = await callApi('getDapps', currentAccountId!); if (!result) { @@ -99,6 +127,11 @@ addActionHandler( addActionHandler('cancelDappConnectRequestConfirm', (global) => { const { promiseId } = global.dappConnectRequest || {}; + if (IS_CAPACITOR) { + global = clearIsPinPadPasswordAccepted(global); + setGlobal(global); + } + if (!promiseId) { return; } @@ -117,11 +150,17 @@ addActionHandler('setDappConnectRequestState', (global, actions, { state }) => { addActionHandler('cancelDappTransfer', (global) => { const { promiseId } = global.currentDappTransfer; + if (IS_CAPACITOR) { + global = clearIsPinPadPasswordAccepted(global); + setGlobal(global); + } + if (promiseId) { void callApi('cancelDappRequest', promiseId, 'Canceled by the user'); } - setGlobal(clearCurrentDappTransfer(global)); + global = clearCurrentDappTransfer(getGlobal()); + setGlobal(global); }); addActionHandler('submitDappTransferPassword', async (global, actions, { password }) => { @@ -141,13 +180,26 @@ addActionHandler('submitDappTransferPassword', async (global, actions, { passwor return; } + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('submitDappTransferPassword', { password }); + + return; + } + global = getGlobal(); + if (IS_CAPACITOR) { + global = updateIsPinPadPasswordAccepted(global); + } global = updateCurrentDappTransfer(global, { isLoading: true, error: undefined, }); setGlobal(global); + if (IS_CAPACITOR) { + await vibrateOnSuccess(true); + } + void callApi('confirmDappRequest', promiseId, password); global = getGlobal(); @@ -217,9 +269,8 @@ addActionHandler('deleteAllDapps', (global) => { setGlobal(global); }); -addActionHandler('deleteDapp', (global, actions, payload) => { +addActionHandler('deleteDapp', (global, actions, { origin }) => { const { currentAccountId } = global; - const { origin } = payload; void callApi('deleteDapp', currentAccountId!, origin); @@ -227,3 +278,56 @@ addActionHandler('deleteDapp', (global, actions, payload) => { global = removeConnectedDapp(global, origin); setGlobal(global); }); + +addActionHandler('apiUpdateDappConnect', (global, actions, { + accountId, dapp, permissions, promiseId, proof, +}) => { + const { isHardware } = selectAccount(global, accountId)!; + + global = updateDappConnectRequest(global, { + state: DappConnectState.Info, + promiseId, + accountId, + dapp, + permissions: { + isAddressRequired: permissions.address, + isPasswordRequired: permissions.proof && !isHardware, + }, + proof, + }); + setGlobal(global); +}); + +addActionHandler('apiUpdateDappSendTransaction', async (global, actions, { + promiseId, + transactions, + fee, + accountId, + dapp, +}) => { + const { currentAccountId } = global; + if (currentAccountId !== accountId) { + const newestTxIds = selectNewestTxIds(global, accountId); + await callApi('activateAccount', accountId, newestTxIds); + global = getGlobal(); + setGlobal({ + ...global, + currentAccountId: accountId, + }); + } + + const state = selectIsHardwareAccount(global) && transactions.length > 1 + ? TransferState.WarningHardware + : TransferState.Initial; + + global = getGlobal(); + global = clearCurrentDappTransfer(global); + global = updateCurrentDappTransfer(global, { + state, + promiseId, + transactions, + fee, + dapp, + }); + setGlobal(global); +}); diff --git a/src/global/actions/api/initial.ts b/src/global/actions/api/initial.ts index 82a4d2ea..f779c03d 100644 --- a/src/global/actions/api/initial.ts +++ b/src/global/actions/api/initial.ts @@ -1,7 +1,8 @@ import { ElectronEvent } from '../../../electron/types'; -import { IS_ELECTRON, IS_EXTENSION } from '../../../config'; +import { DEFAULT_PRICE_CURRENCY, IS_EXTENSION } from '../../../config'; import { tonConnectGetDeviceInfo } from '../../../util/tonConnectEnvironment'; +import { IS_ELECTRON } from '../../../util/windowEnvironment'; import { callApi, initApi } from '../../../api'; import { addActionHandler, getGlobal } from '../../index'; import { selectNewestTxIds } from '../../selectors'; @@ -39,4 +40,5 @@ addActionHandler('resetApiSettings', (global, actions, params) => { if (IS_EXTENSION || IS_ELECTRON) { actions.toggleDeeplinkHook({ isEnabled: isDefaultEnabled }); } + actions.changeBaseCurrency({ currency: DEFAULT_PRICE_CURRENCY }); }); diff --git a/src/global/actions/api/staking.ts b/src/global/actions/api/staking.ts index 6f937eaa..ea807d19 100644 --- a/src/global/actions/api/staking.ts +++ b/src/global/actions/api/staking.ts @@ -1,31 +1,31 @@ import { StakingState } from '../../types'; -import { DEFAULT_DECIMAL_PLACES } from '../../../config'; +import { DEFAULT_DECIMAL_PLACES, IS_CAPACITOR } from '../../../config'; +import { Big } from '../../../lib/big.js'; +import { vibrateOnSuccess } from '../../../util/capacitor'; +import { IS_DELEGATED_BOTTOM_SHEET } from '../../../util/windowEnvironment'; import { callApi } from '../../../api'; import { humanToBigStr } from '../../helpers'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { + clearIsPinPadPasswordAccepted, clearStaking, + updateAccountStakingStatePartial, updateAccountState, - updatePoolState, + updateIsPinPadPasswordAccepted, updateStaking, } from '../../reducers'; +import { selectAccountState } from '../../selectors'; -addActionHandler('fetchStakingState', async (global) => { - const { currentAccountId } = global; +import { callActionInMain } from '../../../hooks/useDelegatedBottomSheet'; - const stakingState = await callApi('getStakingState', currentAccountId!); - if (!stakingState) { +addActionHandler('startStaking', (global, actions, payload) => { + const isOpen = global.staking.state !== StakingState.None; + if (IS_DELEGATED_BOTTOM_SHEET && !isOpen) { + callActionInMain('startStaking', payload); return; } - setGlobal(updateAccountState(getGlobal(), currentAccountId!, { - stakingBalance: stakingState.amount + stakingState.pendingDepositAmount, - isUnstakeRequested: stakingState.isUnstakeRequested, - }, true)); -}); - -addActionHandler('startStaking', (global, actions, payload) => { const { isUnstaking } = payload || {}; setGlobal(updateStaking(global, { @@ -47,7 +47,7 @@ addActionHandler('fetchStakingFee', async (global, actions, payload) => { currentAccountId, humanToBigStr(amount!, DEFAULT_DECIMAL_PLACES), ); - if (!result) { + if (!result || 'error' in result) { return; } @@ -59,7 +59,8 @@ addActionHandler('fetchStakingFee', async (global, actions, payload) => { }); addActionHandler('submitStakingInitial', async (global, actions, payload) => { - const { amount, isUnstaking } = payload || {}; + const { isUnstaking } = payload || {}; + let { amount } = payload ?? {}; const { currentAccountId } = global; if (!currentAccountId) { @@ -69,7 +70,8 @@ addActionHandler('submitStakingInitial', async (global, actions, payload) => { setGlobal(updateStaking(global, { isLoading: true, error: undefined })); if (isUnstaking) { - const result = await callApi('checkUnstakeDraft', currentAccountId); + amount = selectAccountState(global, currentAccountId)!.staking!.balance; + const result = await callApi('checkUnstakeDraft', currentAccountId, humanToBigStr(amount)); global = getGlobal(); global = updateStaking(global, { isLoading: false }); @@ -82,6 +84,8 @@ addActionHandler('submitStakingInitial', async (global, actions, payload) => { fee: result.fee, amount, error: undefined, + type: result.type, + tokenAmount: result.tokenAmount, }); } } @@ -103,6 +107,7 @@ addActionHandler('submitStakingInitial', async (global, actions, payload) => { fee: result.fee, amount, error: undefined, + type: result.type, }); } } @@ -113,7 +118,13 @@ addActionHandler('submitStakingInitial', async (global, actions, payload) => { addActionHandler('submitStakingPassword', async (global, actions, payload) => { const { password, isUnstaking } = payload; - const { amount, fee } = global.staking; + const { + fee, + type, + amount, + tokenAmount, + } = global.staking; + const { currentAccountId } = global; if (!(await callApi('verifyPassword', password))) { setGlobal(updateStaking(getGlobal(), { error: 'Wrong password, please try again' })); @@ -121,26 +132,56 @@ addActionHandler('submitStakingPassword', async (global, actions, payload) => { return; } - setGlobal(updateStaking(getGlobal(), { + global = getGlobal(); + + if (IS_CAPACITOR) { + global = updateIsPinPadPasswordAccepted(global); + } + + global = updateStaking(global, { isLoading: true, error: undefined, - })); + }); + setGlobal(global); + + if (IS_CAPACITOR) { + await vibrateOnSuccess(true); + } + + global = getGlobal(); if (isUnstaking) { + const { instantAvailable } = global.stakingInfo.liquid ?? {}; + const stakingBalance = selectAccountState(global, currentAccountId!)!.staking!.balance; + + const unstakeAmount = type === 'nominators' ? humanToBigStr(stakingBalance) : tokenAmount!; const result = await callApi( 'submitUnstake', global.currentAccountId!, password, + type!, + unstakeAmount, fee, ); + const isInstantUnstakeRequested = Boolean( + type === 'liquid' && instantAvailable && Big(instantAvailable).gt(stakingBalance), + ); + global = getGlobal(); + global = updateAccountStakingStatePartial(global, currentAccountId!, { isInstantUnstakeRequested }); global = updateStaking(global, { isLoading: false }); + setGlobal(global); + global = getGlobal(); + if (!result) { actions.showDialog({ message: 'Unstaking was unsuccessful. Try again later', }); - global = getGlobal(); + + if (IS_CAPACITOR) { + global = clearIsPinPadPasswordAccepted(global); + } } else { global = updateStaking(global, { state: StakingState.UnstakeComplete }); } @@ -150,16 +191,23 @@ addActionHandler('submitStakingPassword', async (global, actions, payload) => { global.currentAccountId!, password, humanToBigStr(amount!, DEFAULT_DECIMAL_PLACES), + type!, fee, ); global = getGlobal(); global = updateStaking(global, { isLoading: false }); + setGlobal(global); + global = getGlobal(); + if (!result) { actions.showDialog({ message: 'Staking was unsuccessful. Try again later', }); - global = getGlobal(); + + if (IS_CAPACITOR) { + global = clearIsPinPadPasswordAccepted(global); + } } else { global = updateStaking(global, { state: StakingState.StakeComplete }); } @@ -173,7 +221,12 @@ addActionHandler('clearStakingError', (global) => { }); addActionHandler('cancelStaking', (global) => { - setGlobal(clearStaking(global)); + if (IS_CAPACITOR) { + global = clearIsPinPadPasswordAccepted(global); + } + + global = clearStaking(global); + setGlobal(global); }); addActionHandler('setStakingScreen', (global, actions, payload) => { @@ -182,15 +235,25 @@ addActionHandler('setStakingScreen', (global, actions, payload) => { setGlobal(updateStaking(global, { state })); }); -addActionHandler('fetchBackendStakingState', async (global) => { - const result = await callApi('getBackendStakingState', global.currentAccountId!); +addActionHandler('fetchStakingHistory', async (global, actions, payload) => { + const { limit, offset } = payload ?? {}; + const stakingHistory = await callApi('getStakingHistory', global.currentAccountId!, limit, offset); - if (!result) { + if (!stakingHistory) { return; } global = getGlobal(); - global = updateAccountState(global, global.currentAccountId!, { stakingHistory: result }, true); - global = updatePoolState(global, result.poolState, true); + global = updateAccountState(global, global.currentAccountId!, { stakingHistory }, true); + setGlobal(global); +}); + +addActionHandler('openStakingInfo', (global) => { + global = { ...global, isStakingInfoModalOpen: true }; + setGlobal(global); +}); + +addActionHandler('closeStakingInfo', (global) => { + global = { ...global, isStakingInfoModalOpen: undefined }; setGlobal(global); }); diff --git a/src/global/actions/api/swap.ts b/src/global/actions/api/swap.ts new file mode 100644 index 00000000..2a80f506 --- /dev/null +++ b/src/global/actions/api/swap.ts @@ -0,0 +1,814 @@ +import type { AssetPairs, GlobalState } from '../../types'; +import { + ApiCommonError, + type ApiSwapBuildRequest, + type ApiSwapHistoryItem, + type ApiSwapPairAsset, +} from '../../../api/types'; +import { + ActiveTab, + SwapErrorType, + SwapFeeSource, + SwapInputSource, + SwapState, + SwapType, +} from '../../types'; + +import { IS_CAPACITOR, JWBTC_TOKEN_SLUG, TON_TOKEN_SLUG } from '../../../config'; +import { Big } from '../../../lib/big.js'; +import { vibrateOnSuccess } from '../../../util/capacitor'; +import safeNumberToString from '../../../util/safeNumberToString'; +import { pause } from '../../../util/schedulers'; +import shiftDecimals from '../../../util/shiftDecimals'; +import { buildSwapId } from '../../../util/swap/buildSwapId'; +import { IS_DELEGATED_BOTTOM_SHEET } from '../../../util/windowEnvironment'; +import { callApi } from '../../../api'; +import { addActionHandler, getGlobal, setGlobal } from '../..'; +import { bigStrToHuman, humanToBigStr } from '../../helpers'; +import { + clearCurrentSwap, + clearIsPinPadPasswordAccepted, + updateCurrentSwap, + updateIsPinPadPasswordAccepted, +} from '../../reducers'; +import { selectAccount } from '../../selectors'; + +import { callActionInMain } from '../../../hooks/useDelegatedBottomSheet'; + +const PAIRS_CACHE: Record = {}; + +const CACHE_DURATION = 15 * 60 * 1000; // 15 minutes +const WAIT_FOR_CHANGELLY = 5 * 1000; + +function getSwapBuildOptions(global: GlobalState): ApiSwapBuildRequest { + const { + dexLabel, + amountIn, + amountOut, + amountOutMin, + tokenInSlug, + tokenOutSlug, + slippage, + networkFee, + swapFee, + } = global.currentSwap; + + const tokenIn = global.swapTokenInfo!.bySlug[tokenInSlug!]; + const tokenOut = global.swapTokenInfo!.bySlug[tokenOutSlug!]; + const from = tokenIn.slug === TON_TOKEN_SLUG ? tokenIn.symbol : tokenIn.contract!; + const to = tokenOut.slug === TON_TOKEN_SLUG ? tokenOut.symbol : tokenOut.contract!; + const fromAmount = safeNumberToString(amountIn!, tokenIn.decimals); + const toAmount = safeNumberToString(amountOut!, tokenOut.decimals); + const account = selectAccount(global, global.currentAccountId!); + + return { + from, + to, + fromAmount, + toAmount, + toMinAmount: amountOutMin!, + slippage, + fromAddress: account?.address!, + dexLabel: dexLabel!, + networkFee: networkFee!, + swapFee: swapFee!, + }; +} + +addActionHandler('startSwap', (global, actions, payload) => { + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('startSwap', payload); + return; + } + + const { isPortrait, ...rest } = payload ?? {}; + const { tokenInSlug, tokenOutSlug, amountIn } = rest; + + if (tokenInSlug || tokenOutSlug || amountIn) { + const tokenIn = global.swapTokenInfo?.bySlug[tokenInSlug!]; + const tokenOut = global.swapTokenInfo?.bySlug[tokenOutSlug!]; + + const isCrosschain = tokenIn?.blockchain !== 'ton' || tokenOut?.blockchain !== 'ton'; + const isToTon = tokenOut?.blockchain === 'ton'; + + const swapType = isCrosschain + ? (isToTon ? SwapType.CrosschainToTon : SwapType.CrosschainFromTon) + : SwapType.OnChain; + + global = updateCurrentSwap(global, { + ...rest, + isEstimating: true, + shouldEstimate: true, + inputSource: SwapInputSource.In, + swapType, + }); + } + + global = updateCurrentSwap(global, { + state: isPortrait ? SwapState.Initial : SwapState.None, + }); + setGlobal(global); + + if (!isPortrait) { + actions.setLandscapeActionsActiveTabIndex({ index: ActiveTab.Swap }); + } +}); + +addActionHandler('setDefaultSwapParams', (global, actions, payload) => { + const { tokenInSlug: requiredTokenInSlug, tokenOutSlug: requiredTokenOutSlug } = payload ?? {}; + + global = updateCurrentSwap(global, { + tokenInSlug: requiredTokenInSlug ?? TON_TOKEN_SLUG, + tokenOutSlug: requiredTokenOutSlug ?? JWBTC_TOKEN_SLUG, + priceImpact: 0, + transactionFee: '0', + swapFee: '0', + networkFee: 0, + amountOutMin: '0', + inputSource: SwapInputSource.In, + }); + setGlobal(global); +}); + +addActionHandler('cancelSwap', (global, actions, { shouldReset } = {}) => { + if (shouldReset) { + const { + tokenInSlug, tokenOutSlug, pairs, swapType, + } = global.currentSwap; + + global = clearCurrentSwap(global); + global = updateCurrentSwap(global, { + tokenInSlug, + tokenOutSlug, + priceImpact: 0, + transactionFee: '0', + swapFee: '0', + networkFee: 0, + realNetworkFee: 0, + amountOutMin: '0', + inputSource: SwapInputSource.In, + swapType, + pairs, + }); + + setGlobal(global); + return; + } + + if (IS_CAPACITOR) { + global = clearIsPinPadPasswordAccepted(global); + } + global = updateCurrentSwap(global, { + state: SwapState.None, + }); + setGlobal(global); +}); + +addActionHandler('submitSwap', async (global, actions, { password }) => { + if (!(await callApi('verifyPassword', password))) { + setGlobal(updateCurrentSwap( + getGlobal(), + { error: 'Wrong password, please try again' }, + )); + + return; + } + + global = getGlobal(); + if (IS_CAPACITOR) { + global = updateIsPinPadPasswordAccepted(global); + } + global = updateCurrentSwap(global, { + isLoading: true, + error: undefined, + }); + setGlobal(global); + + if (IS_CAPACITOR) { + await vibrateOnSuccess(true); + } + + const options = getSwapBuildOptions(global); + const buildResult = await callApi('swapBuildTransfer', global.currentAccountId!, password, options); + + if (!buildResult || 'error' in buildResult) { + actions.showError({ error: buildResult?.error }); + global = getGlobal(); + if (IS_CAPACITOR) { + global = clearIsPinPadPasswordAccepted(global); + } + global = updateCurrentSwap(global, { + isLoading: false, + }); + setGlobal(global); + return; + } + + const swapHistoryItem: ApiSwapHistoryItem = { + id: buildResult.id, + timestamp: Date.now(), + status: 'pending', + from: options.from, + fromAmount: options.fromAmount, + to: options.to, + toAmount: options.toAmount, + networkFee: global.currentSwap.networkFee!, + swapFee: global.currentSwap.swapFee!, + }; + + const result = await callApi( + 'swapSubmit', + global.currentAccountId!, + password, + buildResult.fee, + buildResult.transfers, + swapHistoryItem, + ); + + global = getGlobal(); + + if (!result || 'error' in result) { + global = updateCurrentSwap(global, { + isLoading: false, + }); + setGlobal(global); + actions.showError({ error: result?.error }); + return; + } + + global = updateCurrentSwap(global, { + isLoading: false, + state: SwapState.Complete, + activityId: buildSwapId(buildResult.id), + }); + setGlobal(global); +}); + +addActionHandler('submitSwapCexFromTon', async (global, actions, { password }) => { + if (!(await callApi('verifyPassword', password))) { + setGlobal(updateCurrentSwap( + getGlobal(), + { error: 'Wrong password, please try again' }, + )); + + return; + } + + global = getGlobal(); + if (IS_CAPACITOR) { + global = updateIsPinPadPasswordAccepted(global); + } + global = updateCurrentSwap(global, { + isLoading: true, + error: undefined, + }); + setGlobal(global); + + if (IS_CAPACITOR) { + await vibrateOnSuccess(true); + } + + const swapOptions = getSwapBuildOptions(global); + const swapItem = await callApi( + 'swapCexCreateTransaction', + global.currentAccountId!, + password, + { + from: swapOptions.from, + fromAmount: swapOptions.fromAmount, + fromAddress: swapOptions.fromAddress, + to: swapOptions.to, + toAddress: global.currentSwap.toAddress!, + swapFee: swapOptions.swapFee, + networkFee: swapOptions.networkFee, + }, + ); + + if (!swapItem) { + global = updateCurrentSwap(global, { + isLoading: false, + }); + setGlobal(global); + actions.showError({ error: ApiCommonError.Unexpected }); + return; + } + + global = getGlobal(); + + const asset = global.swapTokenInfo.bySlug[global.currentSwap.tokenInSlug!]; + + const transferOptions = { + password, + accountId: global.currentAccountId!, + slug: global.currentSwap.tokenInSlug!, + toAddress: swapItem.swap.cex!.payinAddress, + amount: humanToBigStr(Number(swapItem.swap.fromAmount), asset.decimals), + fee: swapItem.swap.swapFee, + }; + + await pause(WAIT_FOR_CHANGELLY); + + const result = await callApi('submitTransfer', transferOptions, false); + + global = getGlobal(); + + if (!result || 'error' in result) { + global = updateCurrentSwap(global, { + isLoading: false, + }); + setGlobal(global); + actions.showError({ error: result?.error }); + return; + } + + global = getGlobal(); + global = updateCurrentSwap(global, { + isLoading: false, + state: SwapState.Complete, + activityId: swapItem.activity.id, + }); + setGlobal(global); +}); + +addActionHandler('submitSwapCexToTon', async (global, actions, { password }) => { + if (!(await callApi('verifyPassword', password))) { + setGlobal(updateCurrentSwap( + getGlobal(), + { error: 'Wrong password, please try again' }, + )); + + return; + } + + global = getGlobal(); + global = updateCurrentSwap(global, { + isLoading: true, + error: undefined, + }); + setGlobal(global); + + const swapOptions = getSwapBuildOptions(global); + const swapItem = await callApi( + 'swapCexCreateTransaction', + global.currentAccountId!, + password, + { + from: swapOptions.from, + fromAmount: swapOptions.fromAmount, + fromAddress: swapOptions.fromAddress, + to: swapOptions.to, + toAddress: swapOptions.fromAddress, + swapFee: swapOptions.swapFee, + networkFee: swapOptions.networkFee, + }, + ); + + global = getGlobal(); + + if (!swapItem) { + global = updateCurrentSwap(global, { + isLoading: false, + }); + setGlobal(global); + actions.showError({ error: ApiCommonError.Unexpected }); + return; + } + + global = getGlobal(); + global = updateCurrentSwap(global, { + isLoading: false, + state: SwapState.WaitTokens, + payinAddress: swapItem.swap.cex!.payinAddress, + activityId: swapItem.activity.id, + }); + setGlobal(global); +}); + +addActionHandler('switchSwapTokens', (global) => { + const { + tokenInSlug, tokenOutSlug, amountIn, amountOut, swapType, + } = global.currentSwap; + + const newSwapType = swapType === SwapType.OnChain + ? SwapType.OnChain + : swapType === SwapType.CrosschainFromTon + ? SwapType.CrosschainToTon + : SwapType.CrosschainFromTon; + + global = updateCurrentSwap(global, { + amountIn: amountOut, + amountOut: amountIn, + tokenInSlug: tokenOutSlug, + tokenOutSlug: tokenInSlug, + inputSource: SwapInputSource.In, + swapType: newSwapType, + isEstimating: true, + shouldEstimate: true, + }); + setGlobal(global); +}); + +addActionHandler('setSwapTokenIn', (global, actions, { tokenSlug }) => { + const { tokenInSlug, amountIn, amountOut } = global.currentSwap; + const isFilled = Boolean(amountIn || amountOut); + const oldTokenIn = global.swapTokenInfo!.bySlug[tokenInSlug!]; + const newTokenIn = global.swapTokenInfo!.bySlug[tokenSlug!]; + const amount = amountIn + ? shiftDecimals(amountIn ?? 0, oldTokenIn.decimals, newTokenIn.decimals) + : amountIn; + + global = updateCurrentSwap(global, { + amountIn: amount === 0 ? undefined : amount, + tokenInSlug: tokenSlug, + isEstimating: isFilled, + shouldEstimate: true, + }); + setGlobal(global); +}); + +addActionHandler('setSwapTokenOut', (global, actions, { tokenSlug }) => { + const { tokenOutSlug, amountIn, amountOut } = global.currentSwap; + const isFilled = Boolean(amountIn || amountOut); + const oldTokenOut = global.swapTokenInfo!.bySlug[tokenOutSlug!]; + const newTokenOut = global.swapTokenInfo!.bySlug[tokenSlug!]; + const amount = amountOut + ? shiftDecimals(amountOut ?? 0, oldTokenOut.decimals, newTokenOut.decimals) + : amountOut; + + global = updateCurrentSwap(global, { + amountOut: amount === 0 ? undefined : amount, + tokenOutSlug: tokenSlug, + isEstimating: isFilled, + shouldEstimate: true, + }); + setGlobal(global); +}); + +addActionHandler('setSwapAmountIn', (global, actions, { amount }) => { + const isEstimating = Boolean(amount && amount > 0); + + global = updateCurrentSwap(global, { + amountIn: amount, + inputSource: SwapInputSource.In, + isEstimating, + shouldEstimate: true, + }); + setGlobal(global); +}); + +addActionHandler('setSwapAmountOut', (global, actions, { amount }) => { + const isEstimating = Boolean(amount && amount > 0); + + global = updateCurrentSwap(global, { + amountOut: amount, + inputSource: SwapInputSource.Out, + isEstimating, + shouldEstimate: true, + }); + setGlobal(global); +}); + +addActionHandler('setSlippage', (global, actions, { slippage }) => { + global = updateCurrentSwap(global, { + slippage, + isEstimating: true, + shouldEstimate: true, + }); + setGlobal(global); +}); + +addActionHandler('estimateSwap', async (global, actions, { shouldBlock }) => { + const resetParams = { + amountOutMin: '0', + transactionFee: '0', + swapFee: '0', + networkFee: 0, + realNetworkFee: 0, + priceImpact: 0, + errorType: undefined, + isEstimating: false, + }; + + global = updateCurrentSwap(global, { + shouldEstimate: false, + }); + + // Check for empty string + if ((global.currentSwap.amountIn === undefined && global.currentSwap.inputSource === SwapInputSource.In) + || (global.currentSwap.amountOut === undefined && global.currentSwap.inputSource === SwapInputSource.Out)) { + global = updateCurrentSwap(global, { + amountIn: undefined, + amountOut: undefined, + ...resetParams, + }); + setGlobal(global); + return; + } + + const pairsBySlug = global.currentSwap.pairs?.bySlug ?? {}; + const canSwap = global.currentSwap.tokenOutSlug! in pairsBySlug; + + // Check for invalid pair + if (!canSwap) { + const amount = global.currentSwap.inputSource === SwapInputSource.In + ? { amountOut: undefined } + : { amountIn: undefined }; + + global = updateCurrentSwap(global, { + ...amount, + ...resetParams, + errorType: SwapErrorType.InvalidPair, + }); + setGlobal(global); + return; + } + + global = updateCurrentSwap(global, { + isEstimating: shouldBlock, + }); + setGlobal(global); + + const tokenIn = global.swapTokenInfo!.bySlug[global.currentSwap.tokenInSlug!]; + const tokenOut = global.swapTokenInfo!.bySlug[global.currentSwap.tokenOutSlug!]; + + const from = tokenIn.slug === TON_TOKEN_SLUG ? tokenIn.symbol : tokenIn.contract!; + const to = tokenOut.slug === TON_TOKEN_SLUG ? tokenOut.symbol : tokenOut.contract!; + const fromAmount = safeNumberToString(global.currentSwap.amountIn ?? 0, tokenIn.decimals); + const toAmount = safeNumberToString(global.currentSwap.amountOut ?? 0, tokenOut.decimals); + + const estimateAmount = global.currentSwap.inputSource === SwapInputSource.In ? { fromAmount } : { toAmount }; + + const estimate = await callApi('swapEstimate', { + ...estimateAmount, + from, + to, + slippage: global.currentSwap.slippage, + }); + + global = getGlobal(); + + if (!estimate || 'error' in estimate) { + if (estimate?.error === 'Insufficient liquidity') { + global = updateCurrentSwap(global, { + ...resetParams, + errorType: SwapErrorType.NotEnoughLiquidity, + }); + setGlobal(global); + return; + } + + global = updateCurrentSwap(global, { + ...resetParams, + errorType: SwapErrorType.InvalidPair, + }); + setGlobal(global); + return; + } + + // Check for outdated response + if ( + (global.currentSwap.inputSource === SwapInputSource.In + && global.currentSwap.amountIn !== Number(estimate.fromAmount)) + || (global.currentSwap.inputSource === SwapInputSource.Out + && global.currentSwap.amountOut !== Number(estimate.toAmount)) + ) { + global = updateCurrentSwap(global, { + ...resetParams, + }); + setGlobal(global); + return; + } + + global = updateCurrentSwap(global, { + ...( + global.currentSwap.inputSource === SwapInputSource.In + ? { amountOut: Number(estimate.toAmount) } + : { amountIn: Number(estimate.fromAmount) } + ), + amountOutMin: estimate.toMinAmount, + networkFee: estimate.networkFee, + realNetworkFee: estimate.realNetworkFee, + swapFee: estimate.swapFee, + priceImpact: estimate.impact, + dexLabel: estimate.dexLabel, + feeSource: SwapFeeSource.In, + isEstimating: false, + errorType: undefined, + }); + setGlobal(global); +}); + +addActionHandler('estimateSwapCex', async (global, actions, { shouldBlock }) => { + const amount = global.currentSwap.inputSource === SwapInputSource.In + ? { amountOut: undefined } + : { amountIn: undefined }; + + const resetParams = { + ...amount, + amountOutMin: '0', + transactionFee: '0', + swapFee: '0', + networkFee: 0, + realNetworkFee: 0, + priceImpact: 0, + errorType: undefined, + isEstimating: false, + }; + + global = updateCurrentSwap(global, { + shouldEstimate: false, + transactionFee: '0', + swapFee: '0', + networkFee: 0, + priceImpact: 0, + }); + + // Check for empty string + if ((global.currentSwap.amountIn === undefined && global.currentSwap.inputSource === SwapInputSource.In) + || (global.currentSwap.amountOut === undefined && global.currentSwap.inputSource === SwapInputSource.Out)) { + global = updateCurrentSwap(global, { + amountIn: undefined, + amountOut: undefined, + ...resetParams, + }); + setGlobal(global); + return; + } + + const pairsBySlug = global.currentSwap.pairs?.bySlug ?? {}; + const canSwap = global.currentSwap.tokenOutSlug! in pairsBySlug; + + // Check for invalid pair + if (!canSwap) { + global = updateCurrentSwap(global, { + ...resetParams, + errorType: SwapErrorType.InvalidPair, + }); + setGlobal(global); + return; + } + + global = updateCurrentSwap(global, { + isEstimating: shouldBlock, + }); + setGlobal(global); + + const tokenIn = global.swapTokenInfo!.bySlug[global.currentSwap.tokenInSlug!]; + const tokenOut = global.swapTokenInfo!.bySlug[global.currentSwap.tokenOutSlug!]; + + const from = tokenIn.slug === TON_TOKEN_SLUG ? tokenIn.symbol : tokenIn.contract!; + const to = tokenOut.slug === TON_TOKEN_SLUG ? tokenOut.symbol : tokenOut.contract!; + const fromAmount = safeNumberToString(global.currentSwap.amountIn ?? 0, tokenIn.decimals); + + const estimate = await callApi('swapCexEstimate', { + fromAmount, + from, + to, + }); + + global = getGlobal(); + + if (!estimate) { + global = updateCurrentSwap(global, { + ...resetParams, + errorType: SwapErrorType.InvalidPair, + }); + setGlobal(global); + return; + } + + const isLessThanMin = Big(fromAmount).lt(estimate.fromMin); + const isBiggerThanMax = Big(fromAmount).gt(estimate.fromMax); + + if (isLessThanMin || isBiggerThanMax) { + global = updateCurrentSwap(global, { + ...resetParams, + limits: { + fromMin: estimate.fromMin, + fromMax: estimate.fromMax, + }, + errorType: isLessThanMin ? SwapErrorType.ChangellyMinSwap : SwapErrorType.ChangellyMaxSwap, + }); + setGlobal(global); + return; + } + + // Check for outdated response + if ( + (global.currentSwap.inputSource === SwapInputSource.In + && global.currentSwap.amountIn !== Number(estimate.fromAmount)) + || (global.currentSwap.inputSource === SwapInputSource.Out + && global.currentSwap.amountOut !== Number(estimate.toAmount)) + ) { + global = updateCurrentSwap(global, { + ...resetParams, + }); + setGlobal(global); + return; + } + + let networkFee = 0; + + if (global.currentSwap.swapType === SwapType.CrosschainFromTon) { + const account = global.accounts?.byId[global.currentAccountId!]; + + const txDraft = await callApi( + 'checkTransactionDraft', + global.currentAccountId!, + global.currentSwap.tokenInSlug!, + account?.address!, + humanToBigStr(global.currentSwap.amountIn ?? 0, tokenIn.decimals), + ); + networkFee = bigStrToHuman(txDraft?.fee ?? '0'); + } + + global = getGlobal(); + + const realAmountOut = Big(estimate.toAmount); + + global = updateCurrentSwap(global, { + amountOut: realAmountOut.eq(0) ? undefined : Number(realAmountOut.toFixed(tokenOut.decimals)), + limits: { + fromMin: estimate.fromMin, + fromMax: estimate.fromMax, + }, + swapFee: estimate.swapFee, + networkFee, + realNetworkFee: networkFee, + amountOutMin: String(realAmountOut), + isEstimating: false, + errorType: undefined, + }); + setGlobal(global); +}); + +addActionHandler('setSwapScreen', (global, actions, { state }) => { + global = updateCurrentSwap(global, { state }); + setGlobal(global); +}); + +addActionHandler('clearSwapError', (global) => { + global = updateCurrentSwap(global, { error: undefined }); + setGlobal(global); +}); + +addActionHandler('setSwapType', (global, actions, { type }) => { + global = updateCurrentSwap(global, { swapType: type }); + setGlobal(global); +}); + +addActionHandler('loadSwapPairs', async (global, actions, { tokenSlug, shouldForceUpdate }) => { + const tokenIn = global.swapTokenInfo?.bySlug[tokenSlug]; + if (!tokenIn) { + return; + } + + const symbolOrMinter = tokenIn.slug === TON_TOKEN_SLUG ? tokenIn.symbol : tokenIn.contract!; + + const cache = PAIRS_CACHE[tokenSlug]; + const isCacheValid = cache && (Date.now() - cache.timestamp <= CACHE_DURATION); + if (isCacheValid && !shouldForceUpdate) { + return; + } + + const pairs = await callApi('swapGetPairs', symbolOrMinter); + global = getGlobal(); + + if (!pairs) { + global = updateCurrentSwap(global, { + pairs: { + bySlug: { + ...global.currentSwap.pairs?.bySlug, + [tokenSlug]: {}, + }, + }, + }); + setGlobal(global); + return; + } + + const bySlug = pairs.reduce((acc, pair) => { + acc[pair.slug] = pair.isReverseProhibited ? { + isReverseProhibited: pair.isReverseProhibited, + } : {}; + return acc; + }, {} as AssetPairs); + + PAIRS_CACHE[tokenSlug] = { data: pairs, timestamp: Date.now() }; + + global = updateCurrentSwap(global, { + pairs: { + bySlug: { + ...global.currentSwap.pairs?.bySlug, + [tokenSlug]: bySlug, + }, + }, + shouldEstimate: true, + }); + setGlobal(global); +}); + +addActionHandler('setSwapCexAddress', (global, actions, { toAddress }) => { + global = updateCurrentSwap(global, { toAddress }); + setGlobal(global); +}); diff --git a/src/global/actions/api/wallet.ts b/src/global/actions/api/wallet.ts index 501205fc..68c5f600 100644 --- a/src/global/actions/api/wallet.ts +++ b/src/global/actions/api/wallet.ts @@ -1,50 +1,69 @@ -import type { ApiDappTransaction, ApiToken } from '../../../api/types'; -import type { UserToken } from '../../types'; +import type { + ApiActivity, ApiBaseToken, ApiDappTransaction, ApiSwapAsset, ApiToken, +} from '../../../api/types'; +import type { UserSwapToken, UserToken } from '../../types'; import { ApiTransactionDraftError } from '../../../api/types'; -import { TransferState } from '../../types'; +import { ActiveTab, TransferState } from '../../types'; +import { IS_CAPACITOR } from '../../../config'; +import { vibrateOnError, vibrateOnSuccess } from '../../../util/capacitor'; +import { compareActivities } from '../../../util/compareActivities'; import { - buildCollectionByKey, findLast, mapValues, unique, + buildCollectionByKey, findLast, mapValues, pick, unique, } from '../../../util/iteratees'; -import { pause } from '../../../util/schedulers'; +import { onTickEnd, pause } from '../../../util/schedulers'; +import { IS_DELEGATED_BOTTOM_SHEET } from '../../../util/windowEnvironment'; import { callApi } from '../../../api'; import { ApiUserRejectsError } from '../../../api/errors'; import { - bigStrToHuman, getIsSwapId, getIsTxIdLocal, humanToBigStr, + getIsSwapId, getIsTinyTransaction, getIsTxIdLocal, humanToBigStr, } from '../../helpers'; import { addActionHandler, getActions, getGlobal, setGlobal, } from '../../index'; import { clearCurrentTransfer, + clearIsPinPadPasswordAccepted, updateAccountState, updateActivitiesIsHistoryEndReached, updateActivitiesIsLoading, updateCurrentAccountState, updateCurrentSignature, updateCurrentTransfer, + updateIsPinPadPasswordAccepted, updateSendingLoading, updateSettings, } from '../../reducers'; import { - selectAccount, selectAccountSettings, selectAccountState, selectCurrentAccountState, selectLastTxIds, + selectAccount, + selectAccountSettings, + selectAccountState, + selectCurrentAccountState, + selectLastTxIds, } from '../../selectors'; +import { callActionInMain } from '../../../hooks/useDelegatedBottomSheet'; + const IMPORT_TOKEN_PAUSE = 250; addActionHandler('startTransfer', (global, actions, payload) => { - const { - tokenSlug, toAddress, amount, comment, - } = payload || {}; + const isOpen = global.currentTransfer.state !== TransferState.None; + if (IS_DELEGATED_BOTTOM_SHEET && !isOpen) { + callActionInMain('startTransfer', payload); + return; + } + + const { isPortrait, ...rest } = payload ?? {}; setGlobal(updateCurrentTransfer(global, { - state: TransferState.Initial, + state: isPortrait ? TransferState.Initial : TransferState.None, error: undefined, - toAddress, - amount, - comment, - tokenSlug, + ...rest, })); + + if (!isPortrait) { + actions.setLandscapeActionsActiveTabIndex({ index: ActiveTab.Transfer }); + } }); addActionHandler('changeTransferToken', (global, actions, { tokenSlug }) => { @@ -134,7 +153,6 @@ addActionHandler('submitTransferInitial', async (global, actions, payload) => { error: undefined, toAddress, resolvedAddress: result.resolvedAddress, - normalizedAddress: result.normalizedAddress, amount, comment, shouldEncrypt, @@ -180,8 +198,7 @@ addActionHandler('submitTransferConfirm', (global, actions) => { setGlobal(global); }); -addActionHandler('submitTransferPassword', async (global, actions, payload) => { - const { password } = payload; +addActionHandler('submitTransferPassword', async (global, actions, { password }) => { const { resolvedAddress, comment, @@ -199,10 +216,19 @@ addActionHandler('submitTransferPassword', async (global, actions, payload) => { return; } - setGlobal(updateCurrentTransfer(getGlobal(), { + global = getGlobal(); + global = updateCurrentTransfer(getGlobal(), { isLoading: true, error: undefined, - })); + }); + if (IS_CAPACITOR) { + global = updateIsPinPadPasswordAccepted(global); + } + setGlobal(global); + + if (IS_CAPACITOR) { + await vibrateOnSuccess(true); + } if (promiseId) { void callApi('confirmDappRequest', promiseId, password); @@ -222,12 +248,21 @@ addActionHandler('submitTransferPassword', async (global, actions, payload) => { const result = await callApi('submitTransfer', options); - setGlobal(updateCurrentTransfer(getGlobal(), { + global = getGlobal(); + global = updateCurrentTransfer(global, { isLoading: false, - })); + }); + setGlobal(global); if (!result || 'error' in result) { + if (IS_CAPACITOR) { + global = clearIsPinPadPasswordAccepted(global); + setGlobal(global); + void vibrateOnError(); + } actions.showError({ error: result?.error }); + } else if (IS_CAPACITOR) { + void vibrateOnSuccess(); } }); @@ -305,47 +340,73 @@ addActionHandler('clearTransferError', (global) => { setGlobal(updateCurrentTransfer(global, { error: undefined })); }); -addActionHandler('cancelTransfer', (global) => { +addActionHandler('cancelTransfer', (global, actions, { shouldReset } = {}) => { const { promiseId, tokenSlug } = global.currentTransfer; - if (promiseId) { - void callApi('cancelDappRequest', promiseId, 'Canceled by the user'); - } + if (shouldReset) { + if (promiseId) { + void callApi('cancelDappRequest', promiseId, 'Canceled by the user'); + } - global = clearCurrentTransfer(global); - global = updateCurrentTransfer(global, { tokenSlug }); + global = clearCurrentTransfer(global); + global = updateCurrentTransfer(global, { tokenSlug }); + + setGlobal(global); + return; + } + if (IS_CAPACITOR) { + global = clearIsPinPadPasswordAccepted(global); + } + global = updateCurrentTransfer(global, { state: TransferState.None }); setGlobal(global); }); -addActionHandler('fetchTokenTransactions', async (global, actions, payload) => { - const { limit } = payload || {}; - const slug = payload.slug; - +addActionHandler('fetchTokenTransactions', async (global, actions, { limit, slug, shouldLoadWithBudget }) => { global = updateActivitiesIsLoading(global, true); setGlobal(global); let { idsBySlug } = selectCurrentAccountState(global)?.activities || {}; + let shouldFetchMore = true; + let fetchedActivities: ApiActivity[] = []; let tokenIds = (idsBySlug && idsBySlug[slug]) || []; + let offsetId = findLast(tokenIds, (id) => !getIsTxIdLocal(id) && !getIsSwapId(id)); - const offsetId = findLast(tokenIds, (id) => !getIsTxIdLocal(id) && !getIsSwapId(id)); + while (shouldFetchMore) { + const result = await callApi('fetchTokenActivitySlice', global.currentAccountId!, slug, offsetId, limit); - const result = await callApi('fetchTokenActivitySlice', global.currentAccountId!, slug, offsetId, limit); - global = getGlobal(); - global = updateActivitiesIsLoading(global, false); + global = getGlobal(); - if (!result || !result.length) { - global = updateActivitiesIsHistoryEndReached(global, true); - setGlobal(global); - return; + if (!result || 'error' in result) { + break; + } + + if (!result.length) { + global = updateActivitiesIsHistoryEndReached(global, true, slug); + break; + } + + const filteredResult = global.settings.areTinyTransfersHidden + ? result.filter((tx) => tx.kind === 'transaction' && !getIsTinyTransaction(tx)) + : result; + + fetchedActivities = fetchedActivities.concat(result); + shouldFetchMore = filteredResult.length < limit && fetchedActivities.length < limit; + + tokenIds = unique(tokenIds.concat(filteredResult.map((tx) => tx.id))); + offsetId = findLast(tokenIds, (id) => !getIsTxIdLocal(id) && !getIsSwapId(id)); } - const newById = buildCollectionByKey(result, 'id'); + fetchedActivities.sort((a, b) => compareActivities(a, b, false)); + + global = updateActivitiesIsLoading(global, false); + + const newById = buildCollectionByKey(fetchedActivities, 'id'); const newOrderedIds = Object.keys(newById); const currentActivities = selectCurrentAccountState(global)?.activities; idsBySlug = currentActivities?.idsBySlug || {}; - tokenIds = unique(idsBySlug[slug] || []).concat(newOrderedIds); + tokenIds = unique((idsBySlug[slug] || []).concat(newOrderedIds)); global = updateCurrentAccountState(global, { activities: { @@ -356,34 +417,65 @@ addActionHandler('fetchTokenTransactions', async (global, actions, payload) => { }); setGlobal(global); -}); -addActionHandler('fetchAllTransactions', async (global, actions, payload) => { - const { limit } = payload || {}; + if (shouldLoadWithBudget) { + onTickEnd(() => { + actions.fetchTokenTransactions({ limit, slug }); + }); + } +}); +addActionHandler('fetchAllTransactions', async (global, actions, { limit, shouldLoadWithBudget }) => { global = updateActivitiesIsLoading(global, true); setGlobal(global); const accountId = global.currentAccountId!; - const lastTxIds = selectLastTxIds(global, accountId); - const result = await callApi('fetchAllActivitySlice', global.currentAccountId!, lastTxIds, limit); - global = getGlobal(); - global = updateActivitiesIsLoading(global, false); + let lastTxIds = selectLastTxIds(global, accountId); + let shouldFetchMore = true; + let fetchedActivities: ApiActivity[] = []; - if (!result || !result.length) { - global = updateActivitiesIsHistoryEndReached(global, true); - setGlobal(global); - return; + while (shouldFetchMore) { + const result = await callApi('fetchAllActivitySlice', accountId, lastTxIds, limit); + + global = getGlobal(); + + if (!result || 'error' in result) { + break; + } + + if (!result.length) { + global = updateActivitiesIsHistoryEndReached(global, true); + break; + } + + const filteredResult = global.settings.areTinyTransfersHidden + ? result.filter((tx) => tx.kind === 'transaction' && !getIsTinyTransaction(tx)) + : result; + + fetchedActivities = fetchedActivities.concat(result); + shouldFetchMore = filteredResult.length < limit && fetchedActivities.length < limit; + + lastTxIds = selectLastTxIds(global, accountId); } - const newById = buildCollectionByKey(result, 'id'); + global = updateActivitiesIsLoading(global, false); + + const newById = buildCollectionByKey(fetchedActivities, 'id'); const currentActivities = selectCurrentAccountState(global)?.activities; let idsBySlug = { ...currentActivities?.idsBySlug }; - idsBySlug = result.reduce((acc, activity) => { - const { id, slug } = activity; - acc[slug] = (acc[slug] || []).concat([id]); + fetchedActivities = fetchedActivities.sort((a, b) => compareActivities(a, b, false)); + + idsBySlug = fetchedActivities.reduce((acc, activity) => { + if (activity.kind === 'swap') { + const { id, from, to } = activity; + acc[from] = (acc[from] || []).concat([id]); + acc[to] = (acc[to] || []).concat([id]); + } else { + const { id, slug } = activity; + acc[slug] = (acc[slug] || []).concat([id]); + } return acc; }, idsBySlug); @@ -398,15 +490,21 @@ addActionHandler('fetchAllTransactions', async (global, actions, payload) => { }); setGlobal(global); + + if (shouldLoadWithBudget) { + onTickEnd(() => { + actions.fetchAllTransactions({ limit }); + }); + } }); -addActionHandler('resetIsHistoryEndReached', (global) => { - global = updateActivitiesIsHistoryEndReached(global, false); +addActionHandler('resetIsHistoryEndReached', (global, actions, payload) => { + global = updateActivitiesIsHistoryEndReached(global, false, payload?.slug); setGlobal(global); }); addActionHandler('setIsBackupRequired', (global, actions, { isMnemonicChecked }) => { - const { isBackupRequired } = selectCurrentAccountState(global); + const { isBackupRequired } = selectCurrentAccountState(global) ?? {}; setGlobal(updateCurrentAccountState(global, { isBackupRequired: isMnemonicChecked ? undefined : isBackupRequired, @@ -447,14 +545,17 @@ addActionHandler('cancelSignature', (global) => { addActionHandler('addToken', (global, actions, { token }) => { const accountId = global.currentAccountId!; + const { areTokensWithNoPriceHidden, areTokensWithNoBalanceHidden } = global.settings; const { balances } = selectAccountState(global, accountId) || {}; const accountSettings = selectAccountSettings(global, accountId) ?? {}; + const { orderedSlugs = [], exceptionSlugs = [], deletedSlugs } = accountSettings; if (balances?.bySlug[token.slug]) { return; } - const apiToken: ApiToken = { + const existingToken = global.tokenInfo?.bySlug?.[token.slug]; + const apiToken: ApiToken = existingToken ?? { name: token.name, symbol: token.symbol, slug: token.slug, @@ -473,6 +574,13 @@ addActionHandler('addToken', (global, actions, { token }) => { }, }; + const exceptionSlugsCopy = exceptionSlugs.slice(); + const deletedSlugsCopy = deletedSlugs?.filter((slug) => slug !== token.slug); + + if ((areTokensWithNoBalanceHidden && token.amount === 0) || (areTokensWithNoPriceHidden && token.price === 0)) { + exceptionSlugsCopy.push(token.slug); + } + global = updateAccountState(global, accountId, { balances: { ...balances, @@ -489,9 +597,11 @@ addActionHandler('addToken', (global, actions, { token }) => { [accountId]: { ...accountSettings, orderedSlugs: [ - ...accountSettings.orderedSlugs ?? [], + ...orderedSlugs ?? [], apiToken.slug, ], + exceptionSlugs: exceptionSlugsCopy, + deletedSlugs: deletedSlugsCopy, }, }, }); @@ -506,75 +616,77 @@ addActionHandler('addToken', (global, actions, { token }) => { }, }, }); - - actions.toggleDisabledToken({ slug: apiToken.slug }); }); -addActionHandler('importToken', async (global, actions, { address }) => { - setGlobal( - updateSettings(global, { - importToken: { - isLoading: true, - token: undefined, - }, - }), - ); +addActionHandler('importToken', async (global, actions, { address, isSwap }) => { + global = updateSettings(global, { + importToken: { + isLoading: true, + token: undefined, + }, + }); + setGlobal(global); + + const slug = (await callApi('buildTokenSlug', address))!; + global = getGlobal(); - const baseToken = await callApi('importToken', global.currentAccountId!, address); - await pause(IMPORT_TOKEN_PAUSE); + let token: ApiToken | ApiBaseToken | undefined = global.tokenInfo.bySlug?.[slug!]; - if (!baseToken) { - global = getGlobal(); - setGlobal( - updateSettings(global, { + if (!token) { + token = await callApi('fetchToken', global.currentAccountId!, address); + await pause(IMPORT_TOKEN_PAUSE); + + if (!token) { + global = getGlobal(); + global = updateSettings(global, { importToken: { isLoading: false, token: undefined, }, - }), - ); - return; + }); + setGlobal(global); + return; + } } - const { - slug, symbol, name, image, decimals, keywords, - } = baseToken; - const amount = bigStrToHuman('0', decimals); - - const token: UserToken = { - symbol, - slug, - amount, - name, - image, - decimals, + const userToken: UserToken | UserSwapToken = { + ...pick(token, [ + 'symbol', + 'slug', + 'name', + 'image', + 'decimals', + 'keywords', + ]), + amount: 0, price: 0, change24h: 0, change7d: 0, change30d: 0, - keywords, + ...(isSwap && { + blockchain: 'ton', + contract: token.minterAddress, + }), }; global = getGlobal(); - setGlobal( - updateSettings(global, { - importToken: { - isLoading: false, - token, - }, - }), - ); + global = updateSettings(global, { + importToken: { + isLoading: false, + token: userToken, + }, + }); + setGlobal(global); }); addActionHandler('resetImportToken', (global) => { - setGlobal( - updateSettings(global, { - importToken: { - isLoading: false, - token: undefined, - }, - }), - ); + global = updateSettings(global, { + importToken: { + isLoading: false, + token: undefined, + }, + }); + setGlobal(global); }); addActionHandler('verifyHardwareAddress', async (global) => { @@ -594,9 +706,33 @@ addActionHandler('verifyHardwareAddress', async (global) => { } }); -addActionHandler('setActiveContentTabIndex', (global, actions, { index }) => { +addActionHandler('setActiveContentTab', (global, actions, { tab }) => { global = updateCurrentAccountState(global, { - activeContentTabIndex: index, + activeContentTab: tab, }); setGlobal(global); }); + +addActionHandler('addSwapToken', (global, actions, { token }) => { + const apiSwapAsset: ApiSwapAsset = { + name: token.name, + symbol: token.symbol, + blockchain: token.blockchain, + slug: token.slug, + decimals: token.decimals, + image: token.image, + contract: token.contract, + keywords: token.keywords, + }; + + setGlobal({ + ...global, + swapTokenInfo: { + ...global.swapTokenInfo, + bySlug: { + ...global.swapTokenInfo.bySlug, + [apiSwapAsset.slug]: apiSwapAsset, + }, + }, + }); +}); diff --git a/src/global/actions/apiUpdates/activities.ts b/src/global/actions/apiUpdates/activities.ts index 9bebd1c1..4a9f4715 100644 --- a/src/global/actions/apiUpdates/activities.ts +++ b/src/global/actions/apiUpdates/activities.ts @@ -1,16 +1,21 @@ import { TransferState } from '../../types'; +import { IS_CAPACITOR } from '../../../config'; import { playIncomingTransactionSound } from '../../../util/appSounds'; +import { compareActivities } from '../../../util/compareActivities'; import { bigStrToHuman, getIsTinyTransaction } from '../../helpers'; import { addActionHandler, setGlobal } from '../../index'; import { - removeTransaction, + addLocalTransaction, + assignRemoteTxId, + clearIsPinPadPasswordAccepted, + removeLocalTransaction, updateAccountState, updateActivitiesIsLoadingByAccount, updateActivity, updateCurrentTransfer, } from '../../reducers'; -import { selectAccountState } from '../../selectors'; +import { selectAccountState, selectLocalTransactions } from '../../selectors'; const TX_AGE_TO_PLAY_SOUND = 60000; // 1 min @@ -20,25 +25,23 @@ addActionHandler('apiUpdate', (global, actions, update) => { const { accountId, transaction, - transaction: { amount, toAddress, txId }, + transaction: { amount, txId }, } = update; const { decimals } = global.tokenInfo!.bySlug[transaction.slug!]!; global = updateActivity(global, accountId, transaction); + global = addLocalTransaction(global, accountId, transaction); - if ( - -bigStrToHuman(amount, decimals) === global.currentTransfer.amount - && toAddress === global.currentTransfer.normalizedAddress - ) { + // TODO $decimal + if ((-bigStrToHuman(amount, decimals)).toFixed(decimals) === global.currentTransfer.amount?.toFixed(decimals)) { global = updateCurrentTransfer(global, { txId, state: TransferState.Complete, isLoading: false, }); - } - - if (transaction.type === 'stake' || transaction.type === 'unstake') { - actions.fetchStakingState(); + if (IS_CAPACITOR) { + global = clearIsPinPadPasswordAccepted(global); + } } setGlobal(global); @@ -47,60 +50,67 @@ addActionHandler('apiUpdate', (global, actions, update) => { } case 'newActivities': { - const { activities, accountId } = update; + const { accountId } = update; + const activities = update.activities.sort((a, b) => compareActivities(a, b, true)); + let shouldPlaySound = false; - let wasStakingTransaction = false; global = updateActivitiesIsLoadingByAccount(global, accountId, false); + const localTransactions = selectLocalTransactions(global, accountId) ?? []; + for (const activity of activities) { + if (activity.kind === 'transaction') { + const localTransaction = localTransactions.find(({ amount, isIncoming, slug }) => { + return amount === activity.amount && !isIncoming && slug === activity.slug; + }); + + if (localTransaction) { + const { txId } = activity; + const localTxId = localTransaction.txId; + global = assignRemoteTxId(global, accountId, localTxId, txId); + + if (global.currentTransfer.txId === localTxId) { + global = updateCurrentTransfer(global, { txId }); + } + + const { currentActivityId } = selectAccountState(global, accountId) || {}; + if (currentActivityId === localTxId) { + global = updateAccountState(global, accountId, { currentActivityId: txId }); + } + + global = removeLocalTransaction(global, accountId, localTxId); + + continue; + } + } + global = updateActivity(global, accountId, activity); + if (activity.kind === 'swap') { + continue; + } + if ( activity.isIncoming && global.settings.canPlaySounds && (Date.now() - activity.timestamp < TX_AGE_TO_PLAY_SOUND) - && ( - !global.settings.areTinyTransfersHidden - || getIsTinyTransaction(activity, global.tokenInfo?.bySlug[activity.slug!]) + && !( + global.settings.areTinyTransfersHidden + && getIsTinyTransaction(activity, global.tokenInfo?.bySlug[activity.slug!]) ) ) { shouldPlaySound = true; } - - if (activity.type === 'stake' || activity.type === 'unstake') { - wasStakingTransaction = true; - } } if (shouldPlaySound) { playIncomingTransactionSound(); } - if (wasStakingTransaction) { - actions.fetchStakingState(); - } - setGlobal(global); break; } - - case 'updateTxComplete': { - const { txId, localTxId, accountId } = update; - - global = removeTransaction(global, accountId, localTxId); - - if (global.currentTransfer.txId === localTxId) { - global = updateCurrentTransfer(global, { txId }); - } - - const { currentActivityId } = selectAccountState(global, accountId) || {}; - if (currentActivityId === localTxId) { - global = updateAccountState(global, accountId, { currentActivityId: txId }); - } - - setGlobal(global); - } } }); diff --git a/src/global/actions/apiUpdates/dapp.ts b/src/global/actions/apiUpdates/dapp.ts index 6da5a257..299c2cae 100644 --- a/src/global/actions/apiUpdates/dapp.ts +++ b/src/global/actions/apiUpdates/dapp.ts @@ -1,9 +1,8 @@ import { DappConnectState, TransferState } from '../../types'; import { TON_TOKEN_SLUG } from '../../../config'; -import { callApi } from '../../../api'; import { bigStrToHuman } from '../../helpers'; -import { addActionHandler, getGlobal, setGlobal } from '../../index'; +import { addActionHandler, setGlobal } from '../../index'; import { clearCurrentDappTransfer, clearCurrentSignature, @@ -15,9 +14,11 @@ import { updateDappConnectRequest, } from '../../reducers'; import { - selectAccount, selectAccountState, selectIsHardwareAccount, selectNewestTxIds, + selectAccountState, } from '../../selectors'; +import { callActionInNative } from '../../../hooks/useDelegatedBottomSheet'; + addActionHandler('apiUpdate', (global, actions, update) => { switch (update.type) { case 'createTransaction': { @@ -71,28 +72,8 @@ addActionHandler('apiUpdate', (global, actions, update) => { } case 'dappConnect': { - const { - promiseId, - dapp, - accountId, - permissions, - proof, - } = update; - - const { isHardware } = selectAccount(global, accountId)!; - - global = updateDappConnectRequest(global, { - state: DappConnectState.Info, - promiseId, - accountId, - dapp, - permissions: { - isAddressRequired: permissions.address, - isPasswordRequired: permissions.proof && !isHardware, - }, - proof, - }); - setGlobal(global); + actions.apiUpdateDappConnect(update); + callActionInNative('apiUpdateDappConnect', update); break; } @@ -121,41 +102,25 @@ addActionHandler('apiUpdate', (global, actions, update) => { break; } - case 'dappSendTransactions': { - const { - promiseId, - transactions, - fee, - accountId, - dapp, - } = update; + case 'dappLoading': { + const { connectionType } = update; - (async () => { - const { currentAccountId } = global; - if (currentAccountId !== accountId) { - const newestTxIds = selectNewestTxIds(global, accountId); - await callApi('activateAccount', accountId, newestTxIds); - setGlobal({ - ...getGlobal(), - currentAccountId: accountId, - }); - } - - const state = selectIsHardwareAccount(global) && transactions.length > 1 - ? TransferState.WarningHardware - : TransferState.Initial; - - global = getGlobal(); - global = clearCurrentDappTransfer(global); + if (connectionType === 'connect') { + global = updateDappConnectRequest(global, { + state: DappConnectState.Info, + }); + } else if (connectionType === 'sendTransaction') { global = updateCurrentDappTransfer(global, { - state, - promiseId, - transactions, - fee, - dapp, + state: TransferState.Initial, }); - setGlobal(global); - })(); + } + setGlobal(global); + break; + } + + case 'dappSendTransactions': { + actions.apiUpdateDappSendTransaction(update); + callActionInNative('apiUpdateDappSendTransaction', update); break; } diff --git a/src/global/actions/apiUpdates/initial.ts b/src/global/actions/apiUpdates/initial.ts index 2df95205..3292bcce 100644 --- a/src/global/actions/apiUpdates/initial.ts +++ b/src/global/actions/apiUpdates/initial.ts @@ -1,14 +1,23 @@ -import { buildCollectionByKey } from '../../../util/iteratees'; +import { StakingState } from '../../types'; + +import { buildCollectionByKey, pick } from '../../../util/iteratees'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { addNft, removeNft, + updateAccount, + updateAccountStakingState, updateAccountState, updateBalance, + updateBalances, updateNft, - updatePoolState, + updateSettings, + updateStaking, + updateStakingInfo, + updateSwapTokens, updateTokens, } from '../../reducers'; +import { selectAccountState } from '../../selectors'; addActionHandler('apiUpdate', (global, actions, update) => { switch (update.type) { @@ -16,28 +25,108 @@ addActionHandler('apiUpdate', (global, actions, update) => { global = updateBalance(global, update.accountId, update.slug, update.balance); setGlobal(global); + actions.updateDeletionListForActiveTokens({ accountId: update.accountId }); break; } - case 'updateStakingState': { - global = updateAccountState(global, update.accountId, { - stakingBalance: update.stakingState.amount + update.stakingState.pendingDepositAmount, - isUnstakeRequested: update.stakingState.isUnstakeRequested, - }, true); + case 'updateBalances': { + global = updateBalances(global, update.accountId, update.balancesToUpdate); setGlobal(global); + + actions.updateDeletionListForActiveTokens({ accountId: update.accountId }); break; } - case 'updateBackendStakingState': { - const { poolState } = update.backendStakingState; - global = updatePoolState(global, poolState, true); + case 'updateStaking': { + const { + accountId, + stakingCommonData, + stakingState, + backendStakingState, + } = update; + + const oldBalance = selectAccountState(global, accountId)?.staking?.balance ?? 0; + let balance = 0; + + if (stakingState.type === 'nominators') { + balance = stakingState.amount + stakingState.pendingDepositAmount; + global = updateAccountStakingState(global, accountId, { + type: stakingState.type, + balance, + isUnstakeRequested: stakingState.isUnstakeRequested, + unstakeRequestedAmount: undefined, + apy: backendStakingState.nominatorsPool.apy, + start: backendStakingState.nominatorsPool.start, + end: backendStakingState.nominatorsPool.end, + totalProfit: backendStakingState.totalProfit, + }, true); + } else { + const isPrevRoundUnlocked = Date.now() > stakingCommonData.prevRound.unlock; + const state = { + start: isPrevRoundUnlocked ? stakingCommonData.round.start : stakingCommonData.prevRound.start, + end: isPrevRoundUnlocked ? stakingCommonData.round.unlock : stakingCommonData.prevRound.unlock, + apy: stakingCommonData.liquid.apy, + totalProfit: backendStakingState.totalProfit, + }; + + if (stakingState.type === 'liquid') { + balance = stakingState.amount; + global = updateAccountStakingState(global, accountId, { + type: stakingState.type, + balance, + isUnstakeRequested: !!stakingState.unstakeRequestAmount, + unstakeRequestedAmount: Number(stakingState.unstakeRequestAmount), + ...state, + }, true); + } else { + balance = 0; + global = updateAccountStakingState(global, accountId, { + type: 'liquid', + balance, + isUnstakeRequested: false, + unstakeRequestedAmount: undefined, + ...state, + }, true); + } + } + + let shouldOpenStakingInfo = false; + if (balance !== oldBalance && global.staking.state !== StakingState.None) { + if (balance === 0) { + global = updateStaking(global, { state: StakingState.StakeInitial }); + } else if (oldBalance === 0) { + shouldOpenStakingInfo = true; + } + } + + global = updateStakingInfo(global, { + liquid: { + instantAvailable: stakingCommonData.liquid.available, + }, + }); setGlobal(global); + if (shouldOpenStakingInfo) { + actions.cancelStaking(); + actions.openStakingInfo(); + } break; } case 'updateTokens': { - global = updateTokens(global, update.tokens, true); + const { tokens, baseCurrency } = update; + global = updateTokens(global, tokens, true); + global = updateSettings(global, { + baseCurrency, + }); + setGlobal(global); + + actions.updateDeletionListForActiveTokens(); + break; + } + + case 'updateSwapTokens': { + global = updateSwapTokens(global, update.tokens); setGlobal(global); break; @@ -79,7 +168,12 @@ addActionHandler('apiUpdate', (global, actions, update) => { setGlobal(global); break; } - } - actions.initTokensOrder(); + case 'updateAccount': { + const { accountId, partial } = update; + global = updateAccount(global, accountId, pick(partial, ['address'])); + setGlobal(global); + break; + } + } }); diff --git a/src/global/actions/index.ts b/src/global/actions/index.ts index 22c15c5c..0b2f97ce 100644 --- a/src/global/actions/index.ts +++ b/src/global/actions/index.ts @@ -3,9 +3,11 @@ import './api/auth'; import './api/wallet'; import './api/staking'; import './api/dapps'; +import './api/swap'; import './apiUpdates/initial'; import './apiUpdates/activities'; import './apiUpdates/dapp'; import './ui/initial'; import './ui/misc'; import './ui/dapps'; +import './ui/settings'; diff --git a/src/global/actions/ui/initial.ts b/src/global/actions/ui/initial.ts index 7996608a..cb7e1a29 100644 --- a/src/global/actions/ui/initial.ts +++ b/src/global/actions/ui/initial.ts @@ -1,17 +1,26 @@ import type { Account, AccountState, NotificationType } from '../../types'; -import { ApiTransactionDraftError, ApiTransactionError } from '../../../api/types'; +import { ApiCommonError, ApiTransactionDraftError, ApiTransactionError } from '../../../api/types'; -import { IS_ELECTRON, IS_EXTENSION } from '../../../config'; +import { IS_EXTENSION } from '../../../config'; import { requestMutation } from '../../../lib/fasterdom/fasterdom'; import { parseAccountId } from '../../../util/account'; import { initializeSoundsForSafari } from '../../../util/appSounds'; import { omit } from '../../../util/iteratees'; import { clearPreviousLangpacks, setLanguage } from '../../../util/langProvider'; import switchAnimationLevel from '../../../util/switchAnimationLevel'; -import switchTheme from '../../../util/switchTheme'; +import switchTheme, { setStatusBarStyle } from '../../../util/switchTheme'; import { - IS_ANDROID, IS_IOS, IS_LINUX, IS_MAC_OS, IS_OPERA, IS_SAFARI, - IS_WINDOWS, setPageSafeAreaProperty, setScrollbarWidthProperty, + IS_ANDROID, + IS_DELEGATED_BOTTOM_SHEET, + IS_ELECTRON, + IS_IOS, + IS_LINUX, + IS_MAC_OS, + IS_OPERA, + IS_SAFARI, + IS_WINDOWS, + setPageSafeAreaProperty, + setScrollbarWidthProperty, } from '../../../util/windowEnvironment'; import { callApi } from '../../../api'; import { @@ -25,6 +34,8 @@ import { selectNewestTxIds, } from '../../selectors'; +import { callActionInMain } from '../../../hooks/useDelegatedBottomSheet'; + addActionHandler('init', (_, actions) => { requestMutation(() => { const { documentElement } = document; @@ -52,6 +63,9 @@ addActionHandler('init', (_, actions) => { if (IS_ELECTRON) { documentElement.classList.add('is-electron'); } + if (IS_DELEGATED_BOTTOM_SHEET) { + documentElement.classList.add('is-native-bottom-sheet'); + } setScrollbarWidthProperty(); setPageSafeAreaProperty(); @@ -65,6 +79,7 @@ addActionHandler('afterInit', (global) => { switchTheme(theme); switchAnimationLevel(animationLevel); + setStatusBarStyle(); void setLanguage(langCode); clearPreviousLangpacks(); @@ -151,7 +166,11 @@ addActionHandler('showError', (global, actions, { error } = {}) => { actions.showDialog({ message: 'The hardware wallet does not support this data format' }); break; - case ApiTransactionError.Unexpected: + case ApiCommonError.ServerError: + actions.showDialog({ message: 'An error on the server side. Please try again.' }); + break; + + case ApiCommonError.Unexpected: case undefined: actions.showDialog({ message: 'Unexpected' }); break; @@ -163,6 +182,11 @@ addActionHandler('showError', (global, actions, { error } = {}) => { }); addActionHandler('showNotification', (global, actions, payload) => { + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('showNotification', payload); + return undefined; + } + const { message, icon } = payload; const newNotifications: NotificationType[] = [...global.notifications]; @@ -231,6 +255,10 @@ addActionHandler('toggleDeeplinkHook', (global, actions, { isEnabled }) => { }); addActionHandler('signOut', async (global, actions, payload) => { + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('signOut', payload); + } + const { isFromAllAccounts } = payload || {}; const network = selectCurrentNetwork(global); diff --git a/src/global/actions/ui/misc.ts b/src/global/actions/ui/misc.ts index 0133cf58..e449e787 100644 --- a/src/global/actions/ui/misc.ts +++ b/src/global/actions/ui/misc.ts @@ -1,35 +1,64 @@ -import type { UserToken } from '../../types'; -import { AppState, HardwareConnectState } from '../../types'; +import { + AppState, AuthState, HardwareConnectState, SettingsState, +} from '../../types'; -import { IS_EXTENSION } from '../../../config'; +import { + ANIMATION_LEVEL_MIN, APP_VERSION, DEBUG, IS_CAPACITOR, IS_EXTENSION, +} from '../../../config'; +import { + processDeeplink as processTonConnectDeeplink, + vibrateOnSuccess, +} from '../../../util/capacitor'; +import { getIsAddressValid } from '../../../util/getIsAddressValid'; +import getIsAppUpdateNeeded from '../../../util/getIsAppUpdateNeeded'; import { unique } from '../../../util/iteratees'; import { onLedgerTabClose, openLedgerTab } from '../../../util/ledger/tab'; +import { processDeeplink } from '../../../util/processDeeplink'; import { pause } from '../../../util/schedulers'; +import { isTonConnectDeeplink } from '../../../util/ton/deeplinks'; +import { IS_DELEGATED_BOTTOM_SHEET } from '../../../util/windowEnvironment'; import { callApi } from '../../../api'; import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { + clearCurrentSwap, clearCurrentTransfer, + clearIsPinPadPasswordAccepted, renameAccount, updateAccounts, updateAccountState, updateAuth, updateCurrentAccountState, updateHardware, + updateIsPinPadPasswordAccepted, updateSettings, } from '../../reducers'; import { selectAccountSettings, selectAccountState, - selectCurrentAccountState, selectCurrentAccountTokens, selectDisabledSlugs, selectFirstNonHardwareAccount, + selectCurrentAccountState, + selectCurrentAccountTokens, + selectFirstNonHardwareAccount, } from '../../selectors'; +import { callActionInMain } from '../../../hooks/useDelegatedBottomSheet'; + const OPEN_LEDGER_TAB_DELAY = 500; +const APP_VERSION_URL = 'version.txt'; + +addActionHandler('showActivityInfo', (global, actions, { id }) => { + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('showActivityInfo', { id }); + return undefined; + } -addActionHandler('showActivityInfo', (global, actions, { id } = {}) => { return updateCurrentAccountState(global, { currentActivityId: id }); }); -addActionHandler('closeActivityInfo', (global) => { +addActionHandler('closeActivityInfo', (global, actions, { id }) => { + if (selectCurrentAccountState(global)?.currentActivityId !== id) { + return undefined; + } + return updateCurrentAccountState(global, { currentActivityId: undefined }); }); @@ -77,16 +106,38 @@ addActionHandler('addAccount', async (global, actions, { method, password }) => })); return; } + + if (IS_CAPACITOR) { + global = updateIsPinPadPasswordAccepted(getGlobal()); + setGlobal(global); + + await vibrateOnSuccess(true); + } } global = getGlobal(); - global = updateAuth(global, { password }); + global = { ...global, isAddAccountModalOpen: undefined }; + setGlobal(global); + + if (!IS_DELEGATED_BOTTOM_SHEET) { + actions.addAccount2({ method, password }); + } else { + callActionInMain('addAccount2', { method, password }); + } +}); + +addActionHandler('addAccount2', (global, actions, { method, password }) => { + const isMnemonicImport = method === 'importMnemonic'; + const authState = isMnemonicImport ? AuthState.importWallet : AuthState.createBackup; + + global = { ...global, appState: AppState.Auth }; + global = updateAuth(global, { password, state: authState }); global = clearCurrentTransfer(global); - global = { ...global, isAddAccountModalOpen: undefined, appState: AppState.Auth }; + global = clearCurrentSwap(global); setGlobal(global); - if (method === 'importMnemonic') { + if (isMnemonicImport) { actions.startImportingWallet(); } else { actions.startCreatingWallet(); @@ -106,6 +157,10 @@ addActionHandler('openAddAccountModal', (global) => { }); addActionHandler('closeAddAccountModal', (global) => { + if (IS_CAPACITOR) { + global = clearIsPinPadPasswordAccepted(global); + } + return { ...global, isAddAccountModalOpen: undefined }; }); @@ -140,7 +195,18 @@ addActionHandler('changeNetwork', (global, actions, { network }) => { }); addActionHandler('openSettings', (global) => { - return { ...global, areSettingsOpen: true }; + global = updateSettings(global, { state: SettingsState.Initial }); + setGlobal({ ...global, areSettingsOpen: true }); +}); + +addActionHandler('openSettingsWithState', (global, actions, { state }) => { + global = updateSettings(global, { state }); + setGlobal({ ...global, areSettingsOpen: true }); +}); + +addActionHandler('setSettingsState', (global, actions, { state }) => { + global = updateSettings(global, { state }); + setGlobal(global); }); addActionHandler('closeSettings', (global) => { @@ -152,6 +218,11 @@ addActionHandler('closeSettings', (global) => { }); addActionHandler('openBackupWalletModal', (global) => { + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('openBackupWalletModal'); + return undefined; + } + return { ...global, isBackupWalletModalOpen: true }; }); @@ -159,12 +230,12 @@ addActionHandler('closeBackupWalletModal', (global) => { return { ...global, isBackupWalletModalOpen: undefined }; }); -addActionHandler('openHardwareWalletModal', async (global, actions) => { +addActionHandler('initializeHardwareWalletConnection', async (global, actions) => { const startConnection = () => { global = updateHardware(getGlobal(), { hardwareState: HardwareConnectState.Connecting, }); - setGlobal({ ...global, isHardwareModalOpen: true }); + setGlobal(global); actions.connectHardwareWallet(); }; @@ -180,7 +251,7 @@ addActionHandler('openHardwareWalletModal', async (global, actions) => { global = updateHardware(getGlobal(), { hardwareState: HardwareConnectState.WaitingForBrowser, }); - setGlobal({ ...global, isHardwareModalOpen: true }); + setGlobal(global); await pause(OPEN_LEDGER_TAB_DELAY); const id = await openLedgerTab(); @@ -196,14 +267,24 @@ addActionHandler('openHardwareWalletModal', async (global, actions) => { startConnection(); }); + } +}); - return; +addActionHandler('openHardwareWalletModal', async (global) => { + const ledgerApi = await import('../../../util/ledger'); + let newHardwareState; + + const isConnected = await ledgerApi.connectLedger(); + + if (!isConnected && IS_EXTENSION) { + newHardwareState = HardwareConnectState.WaitingForBrowser; + } else { + newHardwareState = HardwareConnectState.Connect; } global = updateHardware(getGlobal(), { - hardwareState: HardwareConnectState.Connect, + hardwareState: newHardwareState, }); - setGlobal({ ...global, isHardwareModalOpen: true }); }); @@ -261,8 +342,6 @@ addActionHandler('closeSecurityWarning', (global) => { addActionHandler('toggleTokensWithNoBalance', (global, actions, { isEnabled }) => { const accountId = global.currentAccountId!; const accountSettings = selectAccountSettings(global, accountId) ?? {}; - const { enabledSlugs = [] } = accountSettings; - const updatedEnabledSlugs = isEnabled ? [] : enabledSlugs; setGlobal(updateSettings(global, { areTokensWithNoBalanceHidden: isEnabled, @@ -270,19 +349,15 @@ addActionHandler('toggleTokensWithNoBalance', (global, actions, { isEnabled }) = ...global.settings.byAccountId, [accountId]: { ...accountSettings, - enabledSlugs: updatedEnabledSlugs, + exceptionSlugs: [], }, }, })); - - actions.updateDisabledSlugs(); }); addActionHandler('toggleTokensWithNoPrice', (global, actions, { isEnabled }) => { const accountId = global.currentAccountId!; const accountSettings = selectAccountSettings(global, accountId) ?? {}; - const { enabledSlugs = [] } = accountSettings; - const updatedEnabledSlugs = isEnabled ? [] : enabledSlugs; setGlobal(updateSettings(global, { areTokensWithNoPriceHidden: isEnabled, @@ -290,37 +365,19 @@ addActionHandler('toggleTokensWithNoPrice', (global, actions, { isEnabled }) => ...global.settings.byAccountId, [accountId]: { ...accountSettings, - enabledSlugs: updatedEnabledSlugs, + exceptionSlugs: [], }, }, })); - - actions.updateDisabledSlugs(); }); addActionHandler('toggleSortByValue', (global, actions, { isEnabled }) => { - const accountId = global.currentAccountId!; - const accountSettings = selectAccountSettings(global, accountId) ?? {}; - const accountToken = selectCurrentAccountTokens(global) ?? []; - const getTotalValue = ({ price, amount }: UserToken) => price * amount; - const updatedSlugs = accountToken - .slice() - .sort((a, b) => getTotalValue(b) - getTotalValue(a)) - .map(({ slug }) => slug); - setGlobal(updateSettings(global, { - byAccountId: { - ...global.settings.byAccountId, - [accountId]: { - ...accountSettings, - orderedSlugs: updatedSlugs, - }, - }, isSortByValueEnabled: isEnabled, })); }); -addActionHandler('initTokensOrder', (global, actions) => { +addActionHandler('initTokensOrder', (global) => { const accountId = global.currentAccountId!; const accountSettings = selectAccountSettings(global, accountId) ?? {}; const accountTokens = selectCurrentAccountTokens(global) ?? []; @@ -338,28 +395,48 @@ addActionHandler('initTokensOrder', (global, actions) => { }, }, })); - - actions.updateDisabledSlugs(); }); -addActionHandler('updateDisabledSlugs', (global) => { - const accountId = global.currentAccountId!; +addActionHandler('updateDeletionListForActiveTokens', (global, actions, payload) => { + const { accountId = global.currentAccountId } = payload ?? {}; + if (!accountId) { + return; + } + + const { balances } = selectAccountState(global, accountId) ?? {}; const accountSettings = selectAccountSettings(global, accountId) ?? {}; - const disabledSlugs = selectDisabledSlugs( - global, - global.settings.areTokensWithNoBalanceHidden, - global.settings.areTokensWithNoPriceHidden, + const accountTokens = selectCurrentAccountTokens(global) ?? []; + const deletedSlugs = accountSettings.deletedSlugs ?? []; + + const updatedDeletedSlugs = deletedSlugs.filter( + (slug) => !accountTokens.some((token) => token.slug === slug && token.amount > 0), ); - setGlobal(updateSettings(global, { + const balancesBySlug = balances?.bySlug ?? {}; + const updatedBalancesBySlug = Object.fromEntries( + Object.entries(balancesBySlug).filter(([slug]) => !updatedDeletedSlugs.includes(slug)), + ); + + global = updateAccountState(global, accountId, { + balances: { + ...balances, + bySlug: updatedBalancesBySlug, + }, + }); + + global = updateSettings(global, { byAccountId: { ...global.settings.byAccountId, [accountId]: { ...accountSettings, - disabledSlugs, + deletedSlugs: updatedDeletedSlugs, }, }, - })); + }); + + setGlobal(global); + + actions.initTokensOrder(); }); addActionHandler('sortTokens', (global, actions, { orderedSlugs }) => { @@ -377,23 +454,18 @@ addActionHandler('sortTokens', (global, actions, { orderedSlugs }) => { })); }); -addActionHandler('toggleDisabledToken', (global, actions, { slug }) => { +addActionHandler('toggleExceptionToken', (global, actions, { slug }) => { const accountId = global.currentAccountId!; const accountSettings = selectAccountSettings(global, accountId) ?? {}; - const { enabledSlugs = [], disabledSlugs = [] } = accountSettings; + const { exceptionSlugs = [] } = accountSettings; - const enabledSlugsCopy = enabledSlugs.slice(); - const disabledSlugsCopy = disabledSlugs.slice(); + const exceptionSlugsCopy = exceptionSlugs.slice(); + const slugIndexInAvailable = exceptionSlugsCopy.indexOf(slug); - const slugIndexInAvailable = enabledSlugsCopy.indexOf(slug); - const slugIndexInDisabled = disabledSlugsCopy.indexOf(slug); - - if (slugIndexInDisabled !== -1) { - disabledSlugsCopy.splice(slugIndexInDisabled, 1); - enabledSlugsCopy.push(slug); + if (slugIndexInAvailable !== -1) { + exceptionSlugsCopy.splice(slugIndexInAvailable, 1); } else { - enabledSlugsCopy.splice(slugIndexInAvailable, 1); - disabledSlugsCopy.push(slug); + exceptionSlugsCopy.push(slug); } setGlobal(updateSettings(global, { @@ -401,18 +473,15 @@ addActionHandler('toggleDisabledToken', (global, actions, { slug }) => { ...global.settings.byAccountId, [accountId]: { ...accountSettings, - enabledSlugs: enabledSlugsCopy, - disabledSlugs: disabledSlugsCopy, + exceptionSlugs: exceptionSlugsCopy, }, }, })); - - actions.updateDisabledSlugs(); }); addActionHandler('deleteToken', (global, actions, { slug }) => { const accountId = global.currentAccountId!; - const { balances } = selectAccountState(global, accountId) || {}; + const { balances } = selectAccountState(global, accountId) ?? {}; if (!balances?.bySlug[slug]) { return; @@ -431,8 +500,8 @@ addActionHandler('deleteToken', (global, actions, { slug }) => { const accountSettings = selectAccountSettings(global, accountId) ?? {}; const orderedSlugs = accountSettings.orderedSlugs?.filter((s) => s !== slug); - const enabledSlugs = accountSettings.enabledSlugs?.filter((s) => s !== slug); - const disabledSlugs = accountSettings.disabledSlugs?.filter((s) => s !== slug); + const exceptionSlugs = accountSettings.exceptionSlugs?.filter((s) => s !== slug); + const deletedSlugs = [...accountSettings.deletedSlugs ?? [], slug]; global = updateSettings(global, { byAccountId: { @@ -440,11 +509,100 @@ addActionHandler('deleteToken', (global, actions, { slug }) => { [accountId]: { ...accountSettings, orderedSlugs, - enabledSlugs, - disabledSlugs, + exceptionSlugs, + deletedSlugs, }, }, }); setGlobal(global); }); + +addActionHandler('checkAppVersion', (global) => { + fetch(`${APP_VERSION_URL}?${Date.now()}`) + .then((response) => response.text()) + .then((version) => { + version = version.trim(); + + if (getIsAppUpdateNeeded(version, APP_VERSION)) { + global = getGlobal(); + global = { + ...global, + isAppUpdateAvailable: true, + }; + setGlobal(global); + } + }) + .catch((err) => { + if (DEBUG) { + // eslint-disable-next-line no-console + console.error('[checkAppVersion failed] ', err); + } + }); +}); + +addActionHandler('requestConfetti', (global) => { + if (global.settings.animationLevel === ANIMATION_LEVEL_MIN) return global; + + return { + ...global, + confettiRequestedAt: Date.now(), + }; +}); + +addActionHandler('openQrScanner', (global) => { + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('openQrScanner'); + return undefined; + } + + return { + ...global, + isQrScannerOpen: true, + }; +}); + +addActionHandler('closeQrScanner', (global) => { + return { + ...global, + isQrScannerOpen: undefined, + }; +}); + +addActionHandler('openDeeplink', async (global, actions, params) => { + let { url } = params; + if (IS_DELEGATED_BOTTOM_SHEET) { + callActionInMain('openDeeplink', { url }); + return; + } + + if (isTonConnectDeeplink(url)) { + void processTonConnectDeeplink(url); + + return; + } + + if (getIsAddressValid(url)) { + url = `ton://transfer/${url}`; + } + + const result = await processDeeplink(url); + if (!result) { + actions.showNotification({ + message: 'Unrecognized QR Code', + }); + } +}); + +addActionHandler('changeBaseCurrency', async (global, actions, { currency }) => { + await callApi('setBaseCurrency', currency); + void callApi('tryUpdateTokens'); +}); + +addActionHandler('setIsPinPadPasswordAccepted', (global) => { + return updateIsPinPadPasswordAccepted(global); +}); + +addActionHandler('clearIsPinPadPasswordAccepted', (global) => { + return clearIsPinPadPasswordAccepted(global); +}); diff --git a/src/global/actions/ui/settings.ts b/src/global/actions/ui/settings.ts new file mode 100644 index 00000000..b4645e4f --- /dev/null +++ b/src/global/actions/ui/settings.ts @@ -0,0 +1,28 @@ +import { addCallback } from '../../../lib/teact/teactn'; + +import type { GlobalState } from '../../types'; + +import { setLanguage } from '../../../util/langProvider'; +import switchTheme from '../../../util/switchTheme'; + +let prevGlobal: GlobalState | undefined; + +addCallback((global: GlobalState) => { + if (!prevGlobal) { + prevGlobal = global; + return; + } + + const { settings: prevSettings } = prevGlobal; + const { settings } = global; + + if (settings.theme !== prevSettings.theme) { + switchTheme(settings.theme); + } + + if (settings.langCode !== prevSettings.langCode) { + void setLanguage(settings.langCode); + } + + prevGlobal = global; +}); diff --git a/src/global/cache.ts b/src/global/cache.ts index 82de1f7a..520d1103 100644 --- a/src/global/cache.ts +++ b/src/global/cache.ts @@ -6,13 +6,21 @@ import { } from './types'; import { - DEBUG, DEFAULT_DECIMAL_PLACES, GLOBAL_STATE_CACHE_DISABLED, GLOBAL_STATE_CACHE_KEY, IS_ELECTRON, MAIN_ACCOUNT_ID, + DEBUG, + DEFAULT_DECIMAL_PLACES, + GLOBAL_STATE_CACHE_DISABLED, + GLOBAL_STATE_CACHE_KEY, + IS_CAPACITOR, + MAIN_ACCOUNT_ID, } from '../config'; import { buildAccountId, parseAccountId } from '../util/account'; +import authApi from '../util/authApi'; +import { getIsNativeBiometricAuthSupported } from '../util/capacitor'; import { cloneDeep, mapValues, pick } from '../util/iteratees'; import { onBeforeUnload, onIdle, throttle, } from '../util/schedulers'; +import { IS_ELECTRON } from '../util/windowEnvironment'; import { getIsTxIdLocal } from './helpers'; import { addActionHandler, getGlobal } from './index'; import { INITIAL_STATE, STATE_VERSION } from './initialState'; @@ -20,14 +28,15 @@ import { updateHardware } from './reducers'; import { isHeavyAnimating } from '../hooks/useHeavyAnimationCheck'; -const UPDATE_THROTTLE = 5000; -const ACTIVITIES_LIMIT = 20; +const UPDATE_THROTTLE = IS_CAPACITOR ? 500 : 5000; +const ACTIVITIES_LIMIT = 50; const ANIMATION_DELAY_MS = 320; const updateCacheThrottled = throttle(() => onIdle(updateCache), UPDATE_THROTTLE, false); let isCaching = false; let unsubscribeFromBeforeUnload: NoneToVoidFunction | undefined; +let preloadedData: Partial | undefined; export function initCache() { if (GLOBAL_STATE_CACHE_DISABLED) { @@ -55,6 +64,12 @@ export function initCache() { ...global, state: AppState.Auth, }); + global = getGlobal(); + if (getIsNativeBiometricAuthSupported() && global.settings.authConfig?.kind === 'native-biometrics') { + authApi.removeNativeBiometrics(); + } + + preloadedData = pick(global, ['swapTokenInfo', 'tokenInfo']); actions.resetApiSettings({ areAllDisabled: true }); @@ -138,6 +153,7 @@ function readCache(initialState: GlobalState): GlobalState { return { ...initialState, + ...preloadedData, ...cached, }; } @@ -318,6 +334,15 @@ function migrateCache(cached: GlobalState, initialState: GlobalState) { cached.stateVersion = 9; } + if (cached.stateVersion === 9) { + if (cached.byAccountId) { + for (const accountId of Object.keys(cached.byAccountId)) { + delete (cached.byAccountId[accountId] as any).activities; + } + } + cached.stateVersion = 10; + } + // When adding migration here, increase `STATE_VERSION` } @@ -357,18 +382,27 @@ function reduceByAccountId(global: GlobalState) { 'currentTokenSlug', 'currentTokenPeriod', 'savedAddresses', - 'stakingBalance', - 'isUnstakeRequested', - 'poolState', + 'staking', 'stakingHistory', ]); - const { idsBySlug, newestTransactionsBySlug } = state.activities || {}; + const { idsBySlug, newestTransactionsBySlug, byId } = state.activities || {}; + + if (byId && idsBySlug && Object.keys(idsBySlug).length) { + const reducedIdsBySlug = mapValues(idsBySlug, (ids) => { + const result: string[] = []; + let visibleIdCount = 0; + ids.filter((id) => !getIsTxIdLocal(id)).forEach((id) => { + if (visibleIdCount === ACTIVITIES_LIMIT) return; - if (idsBySlug && Object.keys(idsBySlug).length) { - const reducedIdsBySlug = mapValues(idsBySlug, (ids) => ids.filter( - (id) => !getIsTxIdLocal(id), - ).slice(0, ACTIVITIES_LIMIT)); + if (!byId[id].shouldHide) { + visibleIdCount += 1; + } + result.push(id); + }); + + return result; + }); acc[accountId].activities = { byId: pick(state.activities!.byId, Object.values(reducedIdsBySlug).flat()), diff --git a/src/global/init.ts b/src/global/init.ts index f9d761d5..22832efd 100644 --- a/src/global/init.ts +++ b/src/global/init.ts @@ -1,5 +1,5 @@ import { cloneDeep } from '../util/iteratees'; -import { DETACHED_TAB_URL } from '../util/ledger/tab'; +import { IS_LEDGER_EXTENSION_TAB } from '../util/windowEnvironment'; import { initCache, loadCache } from './cache'; import { addActionHandler } from './index'; import { INITIAL_STATE } from './initialState'; @@ -10,7 +10,7 @@ initCache(); addActionHandler('init', (_, actions) => { const initial = cloneDeep(INITIAL_STATE); - if (window.location.href.includes(DETACHED_TAB_URL)) { + if (IS_LEDGER_EXTENSION_TAB) { actions.initLedgerPage(); return initial; } diff --git a/src/global/initialState.ts b/src/global/initialState.ts index 57206466..7bb4fbe8 100644 --- a/src/global/initialState.ts +++ b/src/global/initialState.ts @@ -1,17 +1,24 @@ import type { GlobalState } from './types'; import { AppState, - AuthState, StakingState, TransferState, + AuthState, + BiometricsState, + SettingsState, + StakingState, + SwapState, + TransferState, } from './types'; import { ANIMATION_LEVEL_DEFAULT, + DEFAULT_SLIPPAGE_VALUE, + INIT_SWAP_ASSETS, THEME_DEFAULT, TOKEN_INFO, } from '../config'; import { USER_AGENT_LANG_CODE } from '../util/windowEnvironment'; -export const STATE_VERSION = 9; +export const STATE_VERSION = 10; export const INITIAL_STATE: GlobalState = { appState: AppState.Auth, @@ -20,12 +27,21 @@ export const INITIAL_STATE: GlobalState = { state: AuthState.none, }, + biometrics: { + state: BiometricsState.None, + }, + hardware: {}, currentTransfer: { state: TransferState.None, }, + currentSwap: { + state: SwapState.None, + slippage: DEFAULT_SLIPPAGE_VALUE, + }, + currentDappTransfer: { state: TransferState.None, }, @@ -34,11 +50,18 @@ export const INITIAL_STATE: GlobalState = { state: StakingState.None, }, + stakingInfo: {}, + tokenInfo: { bySlug: TOKEN_INFO, }, + swapTokenInfo: { + bySlug: INIT_SWAP_ASSETS, + }, + settings: { + state: SettingsState.Initial, theme: THEME_DEFAULT, animationLevel: ANIMATION_LEVEL_DEFAULT, areTinyTransfersHidden: true, diff --git a/src/global/reducers/activities.ts b/src/global/reducers/activities.ts index a2f0e50f..e0ce7837 100644 --- a/src/global/reducers/activities.ts +++ b/src/global/reducers/activities.ts @@ -1,4 +1,4 @@ -import type { ApiActivity } from '../../api/types'; +import type { ApiActivity, ApiTransactionActivity } from '../../api/types'; import type { GlobalState } from '../types'; import { getIsTxIdLocal } from '../helpers'; @@ -9,7 +9,29 @@ export function updateActivity(global: GlobalState, accountId: string, activity: const { activities } = selectAccountState(global, accountId) || {}; const idsBySlug = activities?.idsBySlug || {}; - const { id, timestamp } = activity; + const { id, timestamp, kind } = activity; + + if (kind === 'swap') { + const { from, to } = activity; + + let fromTokenIds = idsBySlug[from] || []; + let toTokenIds = idsBySlug[to] || []; + + if (!fromTokenIds.includes(id)) { + fromTokenIds = [id].concat(fromTokenIds); + } + if (!toTokenIds.includes(id)) { + toTokenIds = [id].concat(toTokenIds); + } + + return updateAccountState(global, accountId, { + activities: { + ...activities, + byId: { ...activities?.byId, [id]: activity }, + idsBySlug: { ...idsBySlug, [from]: fromTokenIds, [to]: toTokenIds }, + }, + }); + } const { slug } = activity; const isLocal = getIsTxIdLocal(id); @@ -38,21 +60,65 @@ export function updateActivity(global: GlobalState, accountId: string, activity: }); } -export function removeTransaction(global: GlobalState, accountId: string, txId: string) { +export function assignRemoteTxId(global: GlobalState, accountId: string, txId: string, newTxId: string) { const { activities } = selectAccountState(global, accountId) || {}; - let { idsBySlug } = activities || {}; - const { [txId]: removedActivity, ...byTxId } = activities?.byId || {}; - const slug = removedActivity?.kind === 'transaction' && removedActivity.slug; + const { byId, idsBySlug } = activities || { byId: {}, idsBySlug: {} }; + const replacedActivity: ApiActivity | undefined = byId[txId]; + + if (!replacedActivity || replacedActivity.kind !== 'transaction') return global; - if (slug && idsBySlug && idsBySlug[slug]) { - idsBySlug = { ...idsBySlug, [slug]: idsBySlug[slug].filter((x) => x !== txId) }; + const slug = replacedActivity.slug; + const updatedIdsBySlug = { ...idsBySlug }; + + if (slug in updatedIdsBySlug) { + const indexOfTxId = updatedIdsBySlug[slug].indexOf(txId); + if (indexOfTxId === -1) return global; + + updatedIdsBySlug[slug] = [ + ...updatedIdsBySlug[slug].slice(0, indexOfTxId), + newTxId, + ...updatedIdsBySlug[slug].slice(indexOfTxId + 1), + ]; } + const updatedByTxId = { + ...byId, + [newTxId]: { ...replacedActivity, id: newTxId, txId: newTxId }, + }; + + delete updatedByTxId[txId]; + + return updateAccountState(global, accountId, { + activities: { + ...activities, + byId: updatedByTxId, + idsBySlug: updatedIdsBySlug, + }, + }); +} + +export function addLocalTransaction(global: GlobalState, accountId: string, transaction: ApiTransactionActivity) { + const { activities } = selectAccountState(global, accountId) || {}; + const localTransactions = (activities?.localTransactions ?? []).concat(transaction); + + return updateAccountState(global, accountId, { + activities: { + ...activities, + byId: activities?.byId ?? {}, + localTransactions, + }, + }); +} + +export function removeLocalTransaction(global: GlobalState, accountId: string, txId: string) { + const { activities } = selectAccountState(global, accountId) || {}; + const localTransactions = (activities?.localTransactions ?? []).filter(({ id }) => id !== txId); + return updateAccountState(global, accountId, { activities: { ...activities, - byId: byTxId, - idsBySlug, + byId: activities?.byId ?? {}, + localTransactions, }, }); } diff --git a/src/global/reducers/index.ts b/src/global/reducers/index.ts index a1a9c32c..48e9aea5 100644 --- a/src/global/reducers/index.ts +++ b/src/global/reducers/index.ts @@ -4,3 +4,4 @@ export * from './misc'; export * from './dapp'; export * from './activities'; export * from './nfts'; +export * from './swap'; diff --git a/src/global/reducers/misc.ts b/src/global/reducers/misc.ts index 701fcb64..b30c32c2 100644 --- a/src/global/reducers/misc.ts +++ b/src/global/reducers/misc.ts @@ -1,4 +1,4 @@ -import type { ApiToken } from '../../api/types'; +import type { ApiSwapAsset, ApiToken } from '../../api/types'; import type { Account, AccountState, GlobalState } from '../types'; import { TON_TOKEN_SLUG } from '../../config'; @@ -33,6 +33,20 @@ export function updateAccounts( }; } +export function updateIsPinPadPasswordAccepted(global: GlobalState) { + return { + ...global, + isPinPadPasswordAccepted: true, + }; +} + +export function clearIsPinPadPasswordAccepted(global: GlobalState) { + return { + ...global, + isPinPadPasswordAccepted: undefined, + }; +} + export function createAccount(global: GlobalState, accountId: string, address: string, partial?: Partial) { if (!partial?.title) { const network = selectCurrentNetwork(global); @@ -87,6 +101,31 @@ export function updateBalance( }); } +export function updateBalances( + global: GlobalState, + accountId: string, + balancesToUpdate: Record, +): GlobalState { + if (Object.keys(balancesToUpdate).length === 0) { + return global; + } + + const { balances } = selectAccountState(global, accountId) || {}; + + const updatedBalancesBySlug = { ...(balances?.bySlug || {}) }; + + for (const [slug, balance] of Object.entries(balancesToUpdate)) { + updatedBalancesBySlug[slug] = balance; + } + + return updateAccountState(global, accountId, { + balances: { + ...balances, + bySlug: updatedBalancesBySlug, + }, + }); +} + export function updateSendingLoading(global: GlobalState, isLoading: boolean): GlobalState { return { ...global, @@ -131,6 +170,24 @@ export function updateTokens( }; } +export function updateSwapTokens( + global: GlobalState, + partial: Record, +): GlobalState { + const currentTokens = global.swapTokenInfo?.bySlug; + + return { + ...global, + swapTokenInfo: { + ...global.swapTokenInfo, + bySlug: { + ...currentTokens, + ...partial, + }, + }, + }; +} + export function updateCurrentAccountState(global: GlobalState, partial: Partial): GlobalState { return updateAccountState(global, global.currentAccountId!, partial); } @@ -175,3 +232,13 @@ export function updateSettings(global: GlobalState, settingsUpdate: Partial) { + return { + ...global, + biometrics: { + ...global.biometrics, + ...biometricsUpdate, + }, + }; +} diff --git a/src/global/reducers/staking.ts b/src/global/reducers/staking.ts index 12aad5a4..a9b27f7c 100644 --- a/src/global/reducers/staking.ts +++ b/src/global/reducers/staking.ts @@ -1,10 +1,9 @@ -import type { ApiPoolState } from '../../api/types'; -import type { GlobalState } from '../types'; +import type { AccountState, GlobalState } from '../types'; import { StakingState } from '../types'; import isPartialDeepEqual from '../../util/isPartialDeepEqual'; -import { selectCurrentAccountState } from '../selectors'; -import { updateCurrentAccountState } from './misc'; +import { selectAccountState } from '../selectors'; +import { updateAccountState } from './misc'; export function updateStaking(global: GlobalState, update: Partial): GlobalState { return { @@ -25,20 +24,48 @@ export function clearStaking(global: GlobalState) { }; } -export function updatePoolState(global: GlobalState, partial: ApiPoolState, withDeepCompare = false): GlobalState { - const currentPoolState = selectCurrentAccountState(global)?.poolState; +export function updateAccountStakingState( + global: GlobalState, + accountId: string, + state: NonNullable, + withDeepCompare = false, +): GlobalState { + const currentState = selectAccountState(global, accountId)?.staking; - if ( - !global.currentAccountId - || (withDeepCompare && currentPoolState && isPartialDeepEqual(currentPoolState, partial)) - ) { + if (withDeepCompare && currentState && isPartialDeepEqual(currentState, state)) { return global; } - return updateCurrentAccountState(global, { - poolState: { - ...currentPoolState, + return updateAccountState(global, accountId, { + staking: { + ...currentState, + ...state, + }, + }); +} + +export function updateAccountStakingStatePartial( + global: GlobalState, + accountId: string, + partial: Partial, +): GlobalState { + const currentState = selectAccountState(global, accountId)?.staking; + + if (!currentState) { + return global; + } + + return updateAccountState(global, accountId, { + staking: { + ...currentState, ...partial, }, }); } + +export function updateStakingInfo(global: GlobalState, stakingInfo: GlobalState['stakingInfo']) { + return { + ...global, + stakingInfo, + }; +} diff --git a/src/global/reducers/swap.ts b/src/global/reducers/swap.ts new file mode 100644 index 00000000..458d75ba --- /dev/null +++ b/src/global/reducers/swap.ts @@ -0,0 +1,24 @@ +import type { GlobalState } from '../types'; +import { SwapState } from '../types'; + +import { DEFAULT_SLIPPAGE_VALUE } from '../../config'; + +export function updateCurrentSwap(global: GlobalState, update: Partial) { + return { + ...global, + currentSwap: { + ...global.currentSwap, + ...update, + }, + }; +} + +export function clearCurrentSwap(global: GlobalState) { + return { + ...global, + currentSwap: { + state: SwapState.None, + slippage: DEFAULT_SLIPPAGE_VALUE, + }, + }; +} diff --git a/src/global/reducers/wallet.ts b/src/global/reducers/wallet.ts index ae76dc50..1019919a 100644 --- a/src/global/reducers/wallet.ts +++ b/src/global/reducers/wallet.ts @@ -51,13 +51,27 @@ export function updateActivitiesIsLoading(global: GlobalState, isLoading: boolea }); } -export function updateActivitiesIsHistoryEndReached(global: GlobalState, isReached: boolean) { +export function updateActivitiesIsHistoryEndReached(global: GlobalState, isReached: boolean, slug?: string) { const { activities } = selectCurrentAccountState(global) || {}; + if (slug) { + const bySlug = activities?.isHistoryEndReachedBySlug ?? {}; + + return updateCurrentAccountState(global, { + activities: { + ...activities || { byId: {} }, + isHistoryEndReachedBySlug: { + ...bySlug, + [slug]: isReached, + }, + }, + }); + } + return updateCurrentAccountState(global, { activities: { ...activities || { byId: {} }, - isHistoryEndReached: isReached, + isMainHistoryEndReached: isReached, }, }); } diff --git a/src/global/selectors/index.ts b/src/global/selectors/index.ts index 2aaa454e..b8d4d8d3 100644 --- a/src/global/selectors/index.ts +++ b/src/global/selectors/index.ts @@ -1,10 +1,16 @@ import type { ApiNetwork, ApiTxIdBySlug } from '../../api/types'; import type { - Account, AccountSettings, GlobalState, UserToken, + Account, + AccountSettings, + AccountState, + GlobalState, + UserSwapToken, + UserToken, } from '../types'; +import { TON_TOKEN_SLUG } from '../../config'; import { parseAccountId } from '../../util/account'; -import { findLast, mapValues, unique } from '../../util/iteratees'; +import { findLast, mapValues } from '../../util/iteratees'; import memoized from '../../util/memoized'; import { round } from '../../util/round'; import { bigStrToHuman, getIsSwapId, getIsTxIdLocal } from '../helpers'; @@ -13,30 +19,37 @@ export function selectHasSession(global: GlobalState) { return Boolean(global.currentAccountId); } -export const selectAccountTokensMemoized = memoized(( +const selectAccountTokensMemoized = memoized(( balancesBySlug: Record, tokenInfo: GlobalState['tokenInfo'], accountSettings: AccountSettings, + isSortByValueEnabled = false, + areTokensWithNoBalanceHidden = false, + areTokensWithNoPriceHidden = false, ) => { + const getTotalValue = ({ price, amount }: UserToken) => price * amount; + return Object .entries(balancesBySlug) .filter(([slug]) => (slug in tokenInfo.bySlug)) - .sort(([slugA], [slugB]) => { - if (!accountSettings.orderedSlugs) { - return 1; - } - const indexA = accountSettings.orderedSlugs.indexOf(slugA); - const indexB = accountSettings.orderedSlugs.indexOf(slugB); - return indexA - indexB; - }) .map(([slug, balance]) => { const { - symbol, name, image, decimals, quote: { + symbol, name, image, decimals, cmcSlug, quote: { price, percentChange24h, percentChange7d, percentChange30d, history7d, history24h, history30d, }, } = tokenInfo.bySlug[slug]; - const isDisabled = accountSettings.disabledSlugs?.includes(slug); const amount = bigStrToHuman(balance, decimals); + const isException = accountSettings.exceptionSlugs?.includes(slug); + let isDisabled = (areTokensWithNoPriceHidden && price === 0) + || (areTokensWithNoBalanceHidden && amount === 0); + + if (isException) { + isDisabled = !isDisabled; + } + + if (slug === TON_TOKEN_SLUG) { + isDisabled = false; + } return { symbol, @@ -53,7 +66,21 @@ export const selectAccountTokensMemoized = memoized(( history7d, history30d, isDisabled, + cmcSlug, } as UserToken; + }) + .sort((tokenA, tokenB) => { + if (isSortByValueEnabled) { + return getTotalValue(tokenB) - getTotalValue(tokenA); + } + + if (!accountSettings.orderedSlugs) { + return 1; + } + + const indexA = accountSettings.orderedSlugs.indexOf(tokenA.slug); + const indexB = accountSettings.orderedSlugs.indexOf(tokenB.slug); + return indexA - indexB; }); }); @@ -64,8 +91,16 @@ export function selectCurrentAccountTokens(global: GlobalState) { } const accountSettings = selectAccountSettings(global, global.currentAccountId!) ?? {}; - - return selectAccountTokensMemoized(balancesBySlug, global.tokenInfo, accountSettings); + const { areTokensWithNoBalanceHidden, areTokensWithNoPriceHidden, isSortByValueEnabled } = global.settings; + + return selectAccountTokensMemoized( + balancesBySlug, + global.tokenInfo, + accountSettings, + isSortByValueEnabled, + areTokensWithNoBalanceHidden, + areTokensWithNoPriceHidden, + ); } export const selectPopularTokensMemoized = memoized(( @@ -73,8 +108,7 @@ export const selectPopularTokensMemoized = memoized(( tokenInfo: GlobalState['tokenInfo'], ) => { return Object.entries(tokenInfo.bySlug) - .filter(([, token]) => token.isPopular) - .filter(([slug]) => !(slug in balancesBySlug)) + .filter(([slug, token]) => token.isPopular && !(slug in balancesBySlug)) .map(([slug]) => { const { symbol, name, image, decimals, keywords, quote: { @@ -111,6 +145,50 @@ export function selectPopularTokensWithoutAccountTokens(global: GlobalState) { return selectPopularTokensMemoized(balancesBySlug, global.tokenInfo); } +const selectSwapTokensMemoized = memoized(( + balancesBySlug: Record, + swapTokenInfo: GlobalState['swapTokenInfo'], +) => { + const tokenList: UserSwapToken[] = Object.entries(swapTokenInfo.bySlug) + .map(([slug]) => { + const { + symbol, name, image, decimals, keywords, blockchain, contract, + } = swapTokenInfo.bySlug[slug]; + const amount = bigStrToHuman(balancesBySlug[slug] ?? '0', decimals); + + return { + symbol, + slug, + amount, + name, + image, + decimals, + isDisabled: false, + canSwap: true, + keywords, + blockchain, + contract, + } satisfies UserSwapToken; + }); + + const userTokenList = tokenList.slice() + .sort((a, b) => a.name.trim().toLowerCase().localeCompare(b.name.trim().toLowerCase())); + + return userTokenList; +}); + +export function selectSwapTokens(global: GlobalState) { + const balancesBySlug = selectCurrentAccountState(global)?.balances?.bySlug; + if (!balancesBySlug || !global.swapTokenInfo) { + return undefined; + } + + return selectSwapTokensMemoized( + balancesBySlug, + global.swapTokenInfo, + ); +} + export function selectIsNewWallet(global: GlobalState) { const tokens = selectCurrentAccountTokens(global); @@ -151,7 +229,7 @@ export function selectCurrentAccountState(global: GlobalState) { return selectAccountState(global, global.currentAccountId!); } -export function selectAccountState(global: GlobalState, accountId: string) { +export function selectAccountState(global: GlobalState, accountId: string): AccountState | undefined { return global.byAccountId[accountId]; } @@ -175,40 +253,17 @@ export function selectNewestTxIds(global: GlobalState, accountId: string): ApiTx export function selectLastTxIds(global: GlobalState, accountId: string): ApiTxIdBySlug { const idsBySlug = selectAccountState(global, accountId)?.activities?.idsBySlug || {}; - return mapValues(idsBySlug, (tokenTxIds) => { - return findLast(tokenTxIds, (id) => !getIsTxIdLocal(id) && !getIsSwapId(id)); - }); + return Object.entries(idsBySlug).reduce((result, [slug, ids]) => { + const txId = findLast(ids, (id) => !getIsTxIdLocal(id) && !getIsSwapId(id)); + if (txId) result[slug] = txId; + return result; + }, {} as ApiTxIdBySlug); } export function selectAccountSettings(global: GlobalState, accountId: string): AccountSettings | undefined { return global.settings.byAccountId[accountId]; } -export function selectDisabledSlugs( - global: GlobalState, - areTokensWithNoBalanceHidden = false, - areTokensWithNoPriceHidden = false, -): string[] { - const accountId = global.currentAccountId!; - const tokens = selectCurrentAccountTokens(global) ?? []; - const { enabledSlugs = [], disabledSlugs = [] } = selectAccountSettings(global, accountId) ?? {}; - - const newDisabledSlugs = tokens - .filter(({ slug }) => !enabledSlugs.includes(slug)) - .filter(({ amount, price }) => { - if (areTokensWithNoBalanceHidden && areTokensWithNoPriceHidden) { - return amount === 0 || price === 0; - } else if (areTokensWithNoBalanceHidden) { - return amount === 0; - } else if (areTokensWithNoPriceHidden) { - return price === 0; - } - return false; - }).map(({ slug }) => slug); - - return unique([...disabledSlugs, ...newDisabledSlugs]); -} - export function selectIsHardwareAccount(global: GlobalState) { const state = selectAccount(global, global.currentAccountId!); @@ -218,3 +273,23 @@ export function selectIsHardwareAccount(global: GlobalState) { export function selectIsOneAccount(global: GlobalState) { return Object.keys(selectAccounts(global) || {}).length === 1; } + +export const selectEnabledTokensCountMemoized = memoized((tokens?: UserToken[]) => { + return (tokens ?? []).filter(({ isDisabled }) => !isDisabled).length; +}); + +export function selectLastLedgerAccountIndex(global: GlobalState, network: ApiNetwork) { + const byId = global.accounts?.byId ?? {}; + return Object.entries(byId).reduce((previousValue, [accountId, account]) => { + if (!account.ledger || parseAccountId(accountId).network !== network) { + return previousValue; + } + return Math.max(account.ledger.index, previousValue ?? 0); + }, undefined as number | undefined); +} + +export function selectLocalTransactions(global: GlobalState, accountId: string) { + const accountState = global.byAccountId?.[accountId]; + + return accountState?.activities?.localTransactions; +} diff --git a/src/global/types.ts b/src/global/types.ts index c117ad7e..467ec67e 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -2,7 +2,7 @@ import type { ApiTonConnectProof } from '../api/tonConnect/types'; import type { ApiActivity, ApiAnyDisplayError, - ApiBackendStakingState, + ApiBaseCurrency, ApiDapp, ApiDappPermissions, ApiDappTransaction, @@ -11,11 +11,17 @@ import type { ApiNetwork, ApiNft, ApiParsedPayload, - ApiPoolState, + ApiStakingHistory, + ApiStakingType, + ApiSwapAsset, ApiToken, ApiTransaction, + ApiTransactionActivity, ApiUpdate, + ApiUpdateDappConnect, + ApiUpdateDappSendTransactions, } from '../api/types'; +import type { AuthConfig } from '../util/authApi/types'; import type { LedgerWalletInfo } from '../util/ledger/types'; export type AnimationLevel = 0 | 1 | 2; @@ -53,16 +59,36 @@ export enum AppState { export enum AuthState { none, creatingWallet, + createPin, + confirmPin, + createBiometrics, + createNativeBiometrics, createPassword, createBackup, disclaimerAndBackup, importWallet, - disclaimer, + importWalletCreatePin, + importWalletConfirmPin, + importWalletCreateNativeBiometrics, + importWalletCreateBiometrics, importWalletCreatePassword, + disclaimer, ready, about, } +export enum BiometricsState { + None, + TurnOnPasswordConfirmation, + TurnOnRegistration, + TurnOnVerification, + TurnOnComplete, + TurnOffWarning, + TurnOffBiometricConfirmation, + TurnOffCreatePassword, + TurnOffComplete, +} + export enum TransferState { None, WarningHardware, @@ -74,6 +100,43 @@ export enum TransferState { Complete, } +export enum SwapState { + None, + Initial, + Blockchain, + WaitTokens, + Password, + ConnectHardware, + ConfirmHardware, + Complete, + SelectTokenFrom, + SelectTokenTo, +} + +export enum SwapFeeSource { + In, + Out, +} + +export enum SwapInputSource { + In, + Out, +} + +export enum SwapErrorType { + InvalidPair, + NotEnoughLiquidity, + + ChangellyMinSwap, + ChangellyMaxSwap, +} + +export enum SwapType { + OnChain, + CrosschainFromTon, + CrosschainToTon, +} + export enum DappConnectState { Info, Password, @@ -85,7 +148,8 @@ export enum HardwareConnectState { Connect, Connecting, Failed, - Connected, + ConnectedWithSeveralWallets, + ConnectedWithSingleWallet, WaitingForBrowser, } @@ -101,9 +165,22 @@ export enum StakingState { UnstakeComplete, } +export enum SettingsState { + Initial, + Appearance, + Assets, + Dapps, + Language, + About, + Disclaimer, + NativeBiometricsTurnOn, + SelectTokenList, +} + export enum ActiveTab { Receive, Transfer, + Swap, Stake, } @@ -128,9 +205,16 @@ export type UserToken = { history7d?: ApiHistoryList; history30d?: ApiHistoryList; isDisabled?: boolean; + canSwap?: boolean; keywords?: string[]; + cmcSlug?: string; }; +export type UserSwapToken = { + blockchain: string; + contract?: string; +} & Omit; + export type TokenPeriod = '24h' | '7d' | '30d'; export interface Account { @@ -143,6 +227,12 @@ export interface Account { }; } +export interface AssetPairs { + [slug: string]: { + isReverseProhibited?: boolean; + }; +} + export interface AccountState { balances?: { bySlug: Record; @@ -152,7 +242,9 @@ export interface AccountState { byId: Record; idsBySlug?: Record; newestTransactionsBySlug?: Record; - isHistoryEndReached?: boolean; + isMainHistoryEndReached?: boolean; + isHistoryEndReachedBySlug?: Record; + localTransactions?: ApiTransactionActivity[]; }; nfts?: { byAddress: Record; @@ -164,25 +256,40 @@ export interface AccountState { currentActivityId?: string; currentTokenPeriod?: TokenPeriod; savedAddresses?: Record; - stakingBalance?: number; - isUnstakeRequested?: boolean; - poolState?: ApiPoolState; - stakingHistory?: ApiBackendStakingState; - activeContentTabIndex?: ContentTab; + activeContentTab?: ContentTab; landscapeActionsActiveTabIndex?: ActiveTab; + + // Staking + staking?: { + type: ApiStakingType; + balance: number; + apy: number; + isUnstakeRequested: boolean; + start: number; + end: number; + totalProfit: number; + // liquid + unstakeRequestedAmount?: number; + tokenBalance?: number; + isInstantUnstakeRequested?: boolean; + }; + stakingHistory?: ApiStakingHistory; } export interface AccountSettings { orderedSlugs?: string[]; - enabledSlugs?: string[]; - disabledSlugs?: string[]; + exceptionSlugs?: string[]; + deletedSlugs?: string[]; } export type GlobalState = { + DEBUG_capturedId?: number; + appState: AppState; auth: { state: AuthState; + biometricsStep?: 1 | 2; method?: AuthMethod; isLoading?: boolean; mnemonic?: string[]; @@ -191,8 +298,17 @@ export type GlobalState = { address?: string; error?: string; password?: string; + isBackupModalOpen?: boolean; + }; + + biometrics: { + state: BiometricsState; + error?: string; + password?: string; }; + nativeBiometricsError?: string; + hardware: { hardwareState?: HardwareConnectState; hardwareWallets?: LedgerWalletInfo[]; @@ -209,7 +325,6 @@ export type GlobalState = { toAddress?: string; toAddressName?: string; resolvedAddress?: string; - normalizedAddress?: string; error?: string; amount?: number; fee?: string; @@ -223,6 +338,40 @@ export type GlobalState = { isToNewAddress?: boolean; }; + currentSwap: { + state: SwapState; + slippage: number; + tokenInSlug?: string; + tokenOutSlug?: string; + amountIn?: number; + amountOut?: number; + amountOutMin?: string; + transactionFee?: string; + networkFee?: number; + realNetworkFee?: number; + swapFee?: string; + priceImpact?: number; + dexLabel?: string; + activityId?: string; + error?: string; + errorType?: SwapErrorType; + isLoading?: boolean; + shouldEstimate?: boolean; + isEstimating?: boolean; + inputSource?: SwapInputSource; + swapType?: SwapType; + feeSource?: SwapFeeSource; + toAddress?: string; + payinAddress?: string; + pairs?: { + bySlug: Record; + }; + limits?: { + fromMin?: string; + fromMax?: string; + }; + }; + currentSignature?: { promiseId: string; dataHex: string; @@ -256,8 +405,16 @@ export type GlobalState = { isLoading?: boolean; isUnstaking?: boolean; amount?: number; + tokenAmount?: string; fee?: string; error?: string; + type?: ApiStakingType; + }; + + stakingInfo: { + liquid?: { + instantAvailable: string; + }; }; accounts?: { @@ -270,9 +427,14 @@ export type GlobalState = { bySlug: Record; }; + swapTokenInfo: { + bySlug: Record; + }; + byAccountId: Record; settings: { + state: SettingsState; theme: Theme; animationLevel: AnimationLevel; langCode: LangCode; @@ -284,6 +446,7 @@ export type GlobalState = { isTonProxyEnabled?: boolean; isTonMagicEnabled?: boolean; isDeeplinkHookEnabled?: boolean; + isPasswordNumeric?: boolean; // Backwards compatibility for non-numeric passwords from older versions isTestnet?: boolean; isSecurityWarningHidden?: boolean; areTokensWithNoBalanceHidden?: boolean; @@ -291,8 +454,10 @@ export type GlobalState = { isSortByValueEnabled?: boolean; importToken?: { isLoading?: boolean; - token?: UserToken; + token?: UserToken | UserSwapToken; }; + authConfig?: AuthConfig; + baseCurrency?: ApiBaseCurrency; }; dialogs: string[]; @@ -301,7 +466,12 @@ export type GlobalState = { isAddAccountModalOpen?: boolean; isBackupWalletModalOpen?: boolean; isHardwareModalOpen?: boolean; + isStakingInfoModalOpen?: boolean; + isQrScannerOpen?: boolean; areSettingsOpen?: boolean; + isAppUpdateAvailable?: boolean; + confettiRequestedAt?: number; + isPinPadPasswordAccepted?: boolean; stateVersion: number; }; @@ -317,19 +487,31 @@ export interface ActionPayloads { afterCheckMnemonic: undefined; skipCheckMnemonic: undefined; restartCheckMnemonicIndexes: undefined; - afterCreatePassword: { password: string }; + cancelDisclaimer: undefined; + afterCreatePassword: { password: string; isPasswordNumeric?: boolean }; + afterCreateBiometrics: undefined; + afterCreateNativeBiometrics: undefined; + skipCreateNativeBiometrics: undefined; + createPin: { pin: string; isImporting: boolean }; + confirmPin: { isImporting: boolean }; + cancelConfirmPin: { isImporting: boolean }; startImportingWallet: undefined; afterImportMnemonic: { mnemonic: string[] }; startImportingHardwareWallet: { driver: ApiLedgerDriver }; confirmDisclaimer: undefined; + afterConfirmDisclaimer: undefined; cleanAuthError: undefined; openAbout: undefined; closeAbout: undefined; + openAuthBackupWalletModal: undefined; + closeAuthBackupWalletModal: { isBackupCreated?: boolean } | undefined; + initializeHardwareWalletConnection: undefined; connectHardwareWallet: undefined; createHardwareAccounts: undefined; - createAccount: { password: string; isImporting: boolean }; + createAccount: { password: string; isImporting: boolean; isPasswordNumeric?: boolean }; afterSelectHardwareWallets: { hardwareSelectedIndices: number[] }; resetApiSettings: { areAllDisabled?: boolean } | undefined; + checkAppVersion: undefined; selectToken: { slug?: string } | undefined; openBackupWalletModal: undefined; @@ -343,7 +525,13 @@ export interface ActionPayloads { setTransferToAddress: { toAddress?: string }; setTransferComment: { comment?: string }; setTransferShouldEncrypt: { shouldEncrypt?: boolean }; - startTransfer: { tokenSlug?: string; amount?: number; toAddress?: string; comment?: string } | undefined; + startTransfer: { + isPortrait?: boolean; + tokenSlug?: string; + amount?: number; + toAddress?: string; + comment?: string; + } | undefined; changeTransferToken: { tokenSlug: string }; fetchFee: { tokenSlug: string; @@ -363,7 +551,7 @@ export interface ActionPayloads { submitTransferPassword: { password: string }; submitTransferHardware: undefined; clearTransferError: undefined; - cancelTransfer: undefined; + cancelTransfer: { shouldReset?: boolean } | undefined; showDialog: { message: string }; dismissDialog: undefined; showError: { error?: ApiAnyDisplayError | string }; @@ -375,18 +563,19 @@ export interface ActionPayloads { cancelCaching: undefined; afterSignOut: { isFromAllAccounts?: boolean } | undefined; addAccount: { method: AuthMethod; password: string }; + addAccount2: { method: AuthMethod; password: string }; switchAccount: { accountId: string; newNetwork?: ApiNetwork }; renameAccount: { accountId: string; title: string }; clearAccountError: undefined; validatePassword: { password: string }; verifyHardwareAddress: undefined; - fetchTokenTransactions: { limit: number; slug: string; offsetId?: string }; - fetchAllTransactions: { limit: number }; - resetIsHistoryEndReached: undefined; + fetchTokenTransactions: { limit: number; slug: string; shouldLoadWithBudget?: boolean }; + fetchAllTransactions: { limit: number; shouldLoadWithBudget?: boolean }; + resetIsHistoryEndReached: { slug: string } | undefined; fetchNfts: undefined; - showActivityInfo: { id?: string } | undefined; - closeActivityInfo: undefined; + showActivityInfo: { id: string }; + closeActivityInfo: { id: string }; submitSignature: { password: string }; clearSignatureError: undefined; @@ -400,7 +589,15 @@ export interface ActionPayloads { closeAddAccountModal: undefined; setLandscapeActionsActiveTabIndex: { index: ActiveTab }; - setActiveContentTabIndex: { index: ContentTab }; + setActiveContentTab: { tab: ContentTab }; + + requestConfetti: undefined; + setIsPinPadPasswordAccepted: undefined; + clearIsPinPadPasswordAccepted: undefined; + + openQrScanner: undefined; + closeQrScanner: undefined; + openDeeplink: { url: string }; // Staking startStaking: { isUnstaking?: boolean } | undefined; @@ -409,12 +606,15 @@ export interface ActionPayloads { submitStakingPassword: { password: string; isUnstaking?: boolean }; clearStakingError: undefined; cancelStaking: undefined; - fetchStakingState: undefined; - fetchBackendStakingState: undefined; + fetchStakingHistory: { limit?: number; offset?: number } | undefined; fetchStakingFee: { amount: number }; + openStakingInfo: undefined; + closeStakingInfo: undefined; // Settings openSettings: undefined; + openSettingsWithState: { state: SettingsState }; + setSettingsState: { state?: SettingsState }; closeSettings: undefined; setTheme: { theme: Theme }; setAnimationLevel: { level: AnimationLevel }; @@ -432,13 +632,23 @@ export interface ActionPayloads { toggleTokensWithNoPrice: { isEnabled: boolean }; toggleSortByValue: { isEnabled: boolean }; initTokensOrder: undefined; + updateDeletionListForActiveTokens: { accountId: string } | undefined; sortTokens: { orderedSlugs: string[] }; - updateDisabledSlugs: undefined; - toggleDisabledToken: { slug: string }; + toggleExceptionToken: { slug: string }; addToken: { token: UserToken }; deleteToken: { slug: string }; - importToken: { address: string }; + importToken: { address: string; isSwap?: boolean }; resetImportToken: undefined; + closeBiometricSettings: undefined; + openBiometricsTurnOn: undefined; + openBiometricsTurnOffWarning: undefined; + openBiometricsTurnOff: undefined; + enableBiometrics: { password: string }; + disableBiometrics: { password: string; isPasswordNumeric?: boolean }; + enableNativeBiometrics: { password: string }; + disableNativeBiometrics: undefined; + changeBaseCurrency: { currency: ApiBaseCurrency }; + clearNativeBiometricsError: undefined; // TON Connect submitDappConnectRequestConfirm: { accountId: string; password?: string }; @@ -457,4 +667,34 @@ export interface ActionPayloads { getDapps: undefined; deleteAllDapps: undefined; deleteDapp: { origin: string }; + + apiUpdateDappConnect: ApiUpdateDappConnect; + apiUpdateDappSendTransaction: ApiUpdateDappSendTransactions; + + // Swap + submitSwap: { password: string }; + startSwap: { tokenInSlug?: string; tokenOutSlug?: string; amountIn?: number; isPortrait?: boolean } | undefined; + cancelSwap: { shouldReset?: boolean } | undefined; + setDefaultSwapParams: { tokenInSlug?: string; tokenOutSlug?: string } | undefined; + switchSwapTokens: undefined; + setSwapTokenIn: { tokenSlug: string }; + setSwapTokenOut: { tokenSlug: string }; + setSwapAmountIn: { amount?: number }; + setSwapAmountOut: { amount?: number }; + setSlippage: { slippage: number }; + loadSwapPairs: { tokenSlug: string; shouldForceUpdate?: boolean }; + estimateSwap: { shouldBlock: boolean }; + setSwapScreen: { state: SwapState }; + clearSwapError: undefined; + estimateSwapCex: { shouldBlock: boolean }; + submitSwapCexFromTon: { password: string }; + submitSwapCexToTon: { password: string }; + setSwapType: { type: SwapType }; + setSwapCexAddress: { toAddress: string }; + addSwapToken: { token: UserSwapToken }; +} + +export enum LoadMoreDirection { + Forwards, + Backwards, } diff --git a/src/hooks/freezeWhenClosed.ts b/src/hooks/freezeWhenClosed.ts new file mode 100644 index 00000000..5968eb6f --- /dev/null +++ b/src/hooks/freezeWhenClosed.ts @@ -0,0 +1,20 @@ +import { type FC, type Props, useRef } from '../lib/teact/teact'; + +export default function freezeWhenClosed(Component: T) { + function ComponentWrapper(props: Props) { + const newProps = useRef(props); + + if (props.isOpen) { + newProps.current = props; + } else { + newProps.current = { + ...newProps.current, + isOpen: false, + }; + } + + return Component(newProps.current); + } + + return ComponentWrapper as T; +} diff --git a/src/hooks/useDelegatedBottomSheet.ts b/src/hooks/useDelegatedBottomSheet.ts new file mode 100644 index 00000000..1d72db7c --- /dev/null +++ b/src/hooks/useDelegatedBottomSheet.ts @@ -0,0 +1,207 @@ +import type { HTMLInputTypeAttribute } from 'react'; +import type { SafeAreaInsets } from 'capacitor-plugin-safe-area'; +import { SafeArea } from 'capacitor-plugin-safe-area'; +import type { BottomSheetKeys } from 'native-bottom-sheet'; +import { BottomSheet } from 'native-bottom-sheet'; +import { useEffect, useLayoutEffect, useState } from '../lib/teact/teact'; +import { forceOnHeavyAnimationOnce } from '../lib/teact/teactn'; +import { getActions, setGlobal } from '../global'; + +import type { ActionPayloads, GlobalState } from '../global/types'; + +import cssColorToHex from '../util/cssColorToHex'; +import { setStatusBarStyle } from '../util/switchTheme'; +import { IS_DELEGATED_BOTTOM_SHEET } from '../util/windowEnvironment'; +import { useDeviceScreen } from './useDeviceScreen'; +import useEffectWithPrevDeps from './useEffectWithPrevDeps'; + +const BLUR_TIMEOUT = 50; + +const controlledByMain = new Map(); + +const textInputTypes: Set = new Set([ + 'color', 'date', 'datetime-local', 'email', 'month', 'number', + 'password', 'search', 'tel', 'text', 'time', 'url', 'week', +]); + +let safeAreaCache: SafeAreaInsets['insets'] | undefined; +const safeArePromise = SafeArea.getSafeAreaInsets().then(({ insets }) => { + safeAreaCache = insets; + return safeAreaCache; +}); + +let currentKey: BottomSheetKeys | undefined; + +if (IS_DELEGATED_BOTTOM_SHEET) { + BottomSheet.addListener('delegate', ({ key, globalJson }: { key: BottomSheetKeys; globalJson: string }) => { + currentKey = key; + controlledByMain.get(key)?.(); + + setGlobal( + JSON.parse(globalJson) as GlobalState, + { forceOutdated: true, forceSyncOnIOs: true }, + ); + }); + + BottomSheet.addListener('move', () => { + window.dispatchEvent(new Event('viewportmove')); + }); + + BottomSheet.addListener( + 'callActionInNative', + ({ name, optionsJson }: { name: string; optionsJson: string }) => { + const action = getActions()[name as K]; + action((JSON.parse(optionsJson) || undefined) as ActionPayloads[K]); + }, + ); +} + +export function useDelegatedBottomSheet( + key: BottomSheetKeys | undefined, + isOpen: boolean | undefined, + onClose: AnyToVoidFunction, + dialogRef: React.RefObject, + forceFullNative = false, + noResetHeightOnBlur = false, +) { + useEffectWithPrevDeps(([prevIsOpen]) => { + if (!IS_DELEGATED_BOTTOM_SHEET || !key || key !== currentKey) return; + + if (isOpen) { + const dialogEl = dialogRef.current!; + + BottomSheet.openSelf({ + key, + height: String(dialogEl.offsetHeight), + backgroundColor: cssColorToHex(getComputedStyle(dialogEl).backgroundColor), + }).then(() => { + forceOnHeavyAnimationOnce(); + onClose(); + }); + } else if (prevIsOpen) { + BottomSheet.closeSelf({ key }); + setStatusBarStyle(); + } + }, [dialogRef, isOpen, key, onClose]); + + const { screenHeight } = useDeviceScreen(); + const [safeArea, setSafeArea] = useState(safeAreaCache); + safeArePromise.then(setSafeArea); + // We use Safe Area plugin instead of CSS `env()` function as it does not depend on modal position + const maxHeight = screenHeight - (safeArea?.top || 0); + + useLayoutEffect(() => { + if (!IS_DELEGATED_BOTTOM_SHEET || !isOpen) return; + + dialogRef.current!.style[forceFullNative ? 'height' : 'maxHeight'] = `${maxHeight}px`; + }, [dialogRef, forceFullNative, isOpen, maxHeight]); + + useEffectWithPrevDeps(([prevForceFullNative]) => { + if (!IS_DELEGATED_BOTTOM_SHEET || !isOpen) return; + + // Skip initial opening + if (prevForceFullNative === undefined) return; + + BottomSheet.setSelfSize({ size: forceFullNative ? 'full' : 'half' }); + }, [forceFullNative, isOpen]); + + useLayoutEffect(() => { + if (!IS_DELEGATED_BOTTOM_SHEET || !isOpen) return undefined; + + const dialogEl = dialogRef.current!; + let blurTimeout: number | undefined; + + function onFocus(e: FocusEvent) { + if (!isInput(e.target)) { + return; + } + + if (blurTimeout) { + clearTimeout(blurTimeout); + blurTimeout = undefined; + return; + } + + preventScrollOnFocus(dialogEl); + + BottomSheet.setSelfSize({ size: 'full' }); + } + + function onBlur(e: FocusEvent) { + if (!isInput(e.target) || noResetHeightOnBlur) { + return; + } + + blurTimeout = window.setTimeout(() => { + blurTimeout = undefined; + BottomSheet.setSelfSize({ size: 'half' }); + }, BLUR_TIMEOUT); + } + + document.addEventListener('focusin', onFocus); + document.addEventListener('focusout', onBlur); + + return () => { + document.removeEventListener('focusout', onBlur); + document.removeEventListener('focusin', onFocus); + }; + }, [dialogRef, isOpen, key, noResetHeightOnBlur]); +} + +export function useOpenFromMainBottomSheet( + key: BottomSheetKeys, + open: NoneToVoidFunction, +) { + useEffect(() => { + if (!IS_DELEGATED_BOTTOM_SHEET) return undefined; + + controlledByMain.set(key, open); + + if (currentKey === key) { + open(); + } + + return () => { + if (controlledByMain.get(key) === open) { + controlledByMain.delete(key); + } + }; + }, [key, open]); +} + +export function callActionInMain(name: K, options?: ActionPayloads[K]) { + BottomSheet.callActionInMain({ + name, + // eslint-disable-next-line no-null/no-null + optionsJson: JSON.stringify(options ?? null), + }); +} + +export function callActionInNative(name: K, options?: ActionPayloads[K]) { + BottomSheet.callActionInNative({ + name, + // eslint-disable-next-line no-null/no-null + optionsJson: JSON.stringify(options ?? null), + }); +} + +export function openInMain(key: BottomSheetKeys) { + BottomSheet.openInMain({ key }); +} + +function isInput(el?: EventTarget | null) { + if (!el || !(el instanceof HTMLElement)) return false; + + return (el.tagName === 'INPUT' && textInputTypes.has((el as HTMLInputElement).type)) + || el.tagName === 'TEXTAREA' + || (el.tagName === 'DIV' && el.isContentEditable); +} + +function preventScrollOnFocus(el: HTMLDivElement) { + el.style.opacity = '0'; + setTimeout(() => { + el.style.opacity = '1'; + }); + + document.documentElement.scrollTop = 0; +} diff --git a/src/hooks/useDelegatingBottomSheet.ts b/src/hooks/useDelegatingBottomSheet.ts new file mode 100644 index 00000000..4974fb9f --- /dev/null +++ b/src/hooks/useDelegatingBottomSheet.ts @@ -0,0 +1,103 @@ +import type { BottomSheetKeys } from 'native-bottom-sheet'; +import { BottomSheet } from 'native-bottom-sheet'; +import { useEffect } from '../lib/teact/teact'; +import { forceOnHeavyAnimationOnce } from '../lib/teact/teactn'; +import { getActions, getGlobal } from '../global'; + +import type { ActionPayloads } from '../global/types'; + +import { pause } from '../util/schedulers'; +import { CAN_DELEGATE_BOTTOM_SHEET } from '../util/windowEnvironment'; +import useEffectWithPrevDeps from './useEffectWithPrevDeps'; + +const RACE_TIMEOUT = 1000; +const CLOSING_DURATION = 100; + +const controlledByNative = new Map(); + +if (CAN_DELEGATE_BOTTOM_SHEET) { + BottomSheet.prepare(); + + BottomSheet.addListener( + 'callActionInMain', + ({ name, optionsJson }: { name: string; optionsJson: string }) => { + const action = getActions()[name as K]; + action((JSON.parse(optionsJson) || undefined) as ActionPayloads[K]); + }, + ); + + BottomSheet.addListener( + 'openInMain', + ({ key }: { key: BottomSheetKeys }) => { + controlledByNative.get(key)?.(); + }, + ); +} + +let lastOpenCall = Promise.resolve(); +let closeCurrent: NoneToVoidFunction | undefined; + +export function useDelegatingBottomSheet( + key: BottomSheetKeys | undefined, + isPortrait: boolean | undefined, + isOpen: boolean | undefined, + onClose: AnyToVoidFunction, +) { + const isDelegating = CAN_DELEGATE_BOTTOM_SHEET && key; + const shouldOpen = isOpen && isPortrait; + + useEffectWithPrevDeps(([prevShouldOpen]) => { + if (!isDelegating) return; + + if (shouldOpen) { + closeCurrent?.(); + + const closeNext = () => { + forceOnHeavyAnimationOnce(); + onClose(); + }; + + closeCurrent = closeNext; + + // Wait until previous call resolves to get an up-to-date global + lastOpenCall = Promise.race([ + lastOpenCall, + pause(RACE_TIMEOUT), // Sometimes the last open call is stuck for some unknown reason + ]) + .then(() => { + return BottomSheet.delegate({ + key, + globalJson: JSON.stringify(getGlobal()), + }); + }) + .then(() => { + if (closeCurrent === closeNext) { + closeCurrent(); + closeCurrent = undefined; + } + }) + .then(() => pause(CLOSING_DURATION)); + } else if (prevShouldOpen) { + BottomSheet.release({ key }); + } + }, [shouldOpen, isDelegating, key, onClose]); + + return isDelegating && isPortrait; +} + +export function useOpenFromNativeBottomSheet( + key: BottomSheetKeys, + open: NoneToVoidFunction, +) { + useEffect(() => { + if (!CAN_DELEGATE_BOTTOM_SHEET) return undefined; + + controlledByNative.set(key, open); + + return () => { + if (controlledByNative.get(key) === open) { + controlledByNative.delete(key); + } + }; + }, [key, open]); +} diff --git a/src/hooks/useDeviceScreen.ts b/src/hooks/useDeviceScreen.ts index 01ba58da..2fe8b313 100644 --- a/src/hooks/useDeviceScreen.ts +++ b/src/hooks/useDeviceScreen.ts @@ -3,9 +3,12 @@ import { useMediaQuery } from './useMediaQuery'; export function useDeviceScreen() { const isPortrait = useMediaQuery(`(max-width: ${MOBILE_SCREEN_MAX_WIDTH - 0.02}px)`); + const isSmallHeight = useMediaQuery('(max-height: 43.5rem)'); return { isPortrait, + isSmallHeight, isLandscape: !isPortrait, + screenHeight: window.screen.height, }; } diff --git a/src/hooks/useElectronDrag.ts b/src/hooks/useElectronDrag.ts index 782c1daa..8749428e 100644 --- a/src/hooks/useElectronDrag.ts +++ b/src/hooks/useElectronDrag.ts @@ -1,8 +1,7 @@ import type { RefObject } from 'react'; import { useEffect, useRef } from '../lib/teact/teact'; -import { IS_ELECTRON } from '../config'; -import { IS_MAC_OS } from '../util/windowEnvironment'; +import { IS_ELECTRON, IS_MAC_OS } from '../util/windowEnvironment'; const DRAG_DISTANCE_THRESHOLD = 5; diff --git a/src/hooks/useHistoryBack.ts b/src/hooks/useHistoryBack.ts new file mode 100644 index 00000000..c210018b --- /dev/null +++ b/src/hooks/useHistoryBack.ts @@ -0,0 +1,266 @@ +import { useCallback, useRef } from '../lib/teact/teact'; + +import { IS_TEST } from '../config'; +import { requestMeasure } from '../lib/fasterdom/fasterdom'; +import { IS_IOS, IS_LEDGER_EXTENSION_TAB } from '../util/windowEnvironment'; +import useEffectOnce from './useEffectOnce'; +import useLastCallback from './useLastCallback'; +import useSyncEffect from './useSyncEffect'; + +const PATH_BASE = `${window.location.pathname}${window.location.search}`; + +type HistoryRecord = { + index: number; + // Should this record be replaced by the next record (for example Menu) + shouldBeReplaced?: boolean; + // Mark this record as replaced by the next record. Only used to check if needed to perform effectBack + markReplaced?: VoidFunction; + onBack?: VoidFunction; + // Set if the element is closed in the UI, but not in the real history + isClosed?: boolean; +}; + +type HistoryOperationGo = { + type: 'go'; + delta: number; +}; + +type HistoryOperationState = { + type: 'pushState' | 'replaceState'; + data: any; +}; + +type HistoryOperation = HistoryOperationGo | HistoryOperationState; + +// Needed to dismiss any 'trashed' history records from the previous page reloads. +const historyUniqueSessionId = Number(new Date()); +// Reflects real history state, but also contains information on which records should be replaced by the next record and +// which records are deferred to close on the next operation +let historyState: HistoryRecord[]; +// Reflects current real history index +let historyCursor: number; +// If we alter real history programmatically, the popstate event will be fired, which we don't need +let isAlteringHistory = false; +// Unfortunately Safari doesn't really like when there's 2+ consequent history operations in one frame, so we need +// to delay them to the next raf +let deferredHistoryOperations: HistoryOperation[] = []; +let deferredPopstateOperations: HistoryOperationState[] = []; + +// Do not remove: used for history unit tests +if (IS_TEST) { + (window as any).TEST_getHistoryState = () => historyState; + (window as any).TEST_getHistoryCursor = () => historyCursor; +} + +function applyDeferredHistoryOperations() { + const goOperations = deferredHistoryOperations.filter((op) => op.type === 'go') as HistoryOperationGo[]; + const stateOperations = deferredHistoryOperations.filter((op) => op.type !== 'go') as HistoryOperationState[]; + const goCount = goOperations.reduce((acc, op) => acc + op.delta, 0); + + deferredHistoryOperations = []; + + if (goCount) { + window.history.go(goCount); + + // If we have some `state` operations after the `go` operations, we need to wait until the popstate event + // so the order of operations is correctly preserved + if (stateOperations.length) { + deferredPopstateOperations.push(...stateOperations); + return; + } + } + + processStateOperations(stateOperations); +} + +function processStateOperations(stateOperations: HistoryOperationState[]) { + stateOperations.forEach((op) => window.history[op.type](op.data, '')); +} + +function deferHistoryOperation(historyOperation: HistoryOperation) { + if (!deferredHistoryOperations.length) { + requestMeasure(applyDeferredHistoryOperations); + } + + deferredHistoryOperations.push(historyOperation); +} + +// Resets history to the `root` state +function resetHistory() { + historyCursor = 0; + historyState = [{ + index: 0, + onBack: () => window.history.back(), + }]; + + if (!IS_LEDGER_EXTENSION_TAB) { + window.history.replaceState({ index: 0, historyUniqueSessionId }, '', PATH_BASE); + } +} + +resetHistory(); + +function cleanupClosed(alreadyClosedCount = 1) { + let countClosed = alreadyClosedCount; + for (let i = historyCursor - 1; i > 0; i--) { + if (!historyState[i].isClosed) break; + countClosed++; + } + if (countClosed) { + isAlteringHistory = true; + deferHistoryOperation({ + type: 'go', + delta: -countClosed, + }); + } + return countClosed; +} + +function cleanupTrashedState() { + for (let i = historyState.length - 1; i > 0; i--) { + if (historyState[i].isClosed) { + continue; + } + historyState[i].onBack?.(); + } + + resetHistory(); +} + +window.addEventListener('popstate', ({ state }: PopStateEvent) => { + if (isAlteringHistory) { + isAlteringHistory = false; + if (deferredPopstateOperations.length) { + processStateOperations(deferredPopstateOperations); + deferredPopstateOperations = []; + } + return; + } + + if (!state) { + cleanupTrashedState(); + return; + } + + const { index, historyUniqueSessionId: previousUniqueSessionId } = state; + if (previousUniqueSessionId !== historyUniqueSessionId) { + cleanupTrashedState(); + return; + } + + // New real history state matches the old virtual one. Not possible in theory, but in practice we have Safari + if (index === historyCursor) { + return; + } + + if (index < historyCursor) { + // Navigating back + let alreadyClosedCount = 0; + for (let i = historyCursor; i > index - alreadyClosedCount; i--) { + if (historyState[i].isClosed) { + alreadyClosedCount++; + continue; + } + historyState[i].onBack?.(); + } + + const countClosed = cleanupClosed(alreadyClosedCount); + historyCursor += index - historyCursor - countClosed; + + // Can happen when we have deferred a real back for some element (for example Menu), closed via UI, + // pressed back button and caused a pushState. + if (historyCursor < 0) { + historyCursor = 0; + } + } else if (index > historyCursor) { + // Forward navigation is not yet supported + isAlteringHistory = true; + deferHistoryOperation({ + type: 'go', + delta: -(index - historyCursor), + }); + } +}); + +export default function useHistoryBack({ + isActive, + shouldBeReplaced, + onBack, +}: { + isActive?: boolean; + shouldBeReplaced?: boolean; + onBack: VoidFunction; +}) { + const lastOnBack = useLastCallback(onBack); + + // Active index of the record + const indexRef = useRef(); + const wasReplaced = useRef(false); + + const isFirstRender = useRef(true); + + const pushState = useCallback((forceReplace = false) => { + // Check if the old state should be replaced + const shouldReplace = forceReplace || historyState[historyCursor].shouldBeReplaced; + indexRef.current = shouldReplace ? historyCursor : ++historyCursor; + + historyCursor = indexRef.current; + + // Mark the previous record as replaced so effectBack doesn't perform back operation on the new record + const previousRecord = historyState[indexRef.current]; + if (previousRecord && !previousRecord.isClosed) { + previousRecord.markReplaced?.(); + } + + historyState[indexRef.current] = { + index: indexRef.current, + onBack: lastOnBack, + shouldBeReplaced, + markReplaced: () => { + wasReplaced.current = true; + }, + }; + + deferHistoryOperation({ + type: shouldReplace ? 'replaceState' : 'pushState', + data: { + index: indexRef.current, + historyUniqueSessionId, + }, + }); + }, [lastOnBack, shouldBeReplaced]); + + const processBack = useCallback(() => { + // Only process back on open records + if (indexRef.current && historyState[indexRef.current] && !wasReplaced.current) { + historyState[indexRef.current].isClosed = true; + wasReplaced.current = true; + if (indexRef.current === historyCursor && !shouldBeReplaced) { + historyCursor -= cleanupClosed(); + } + } + }, [shouldBeReplaced]); + + // Process back navigation when element is unmounted + useEffectOnce(() => { + if (IS_IOS) return undefined; + + isFirstRender.current = false; + return () => { + if (!isActive || wasReplaced.current) return; + processBack(); + }; + }); + + useSyncEffect(([prevIsActive]) => { + if (IS_IOS) return; + if (prevIsActive === isActive) return; + if (isFirstRender.current && !isActive) return; + + if (isActive) { + pushState(); + } else { + processBack(); + } + }, [isActive, processBack, pushState]); +} diff --git a/src/hooks/useInfiniteScroll.ts b/src/hooks/useInfiniteScroll.ts new file mode 100644 index 00000000..38ccbf4a --- /dev/null +++ b/src/hooks/useInfiniteScroll.ts @@ -0,0 +1,135 @@ +import { useRef } from '../lib/teact/teact'; + +import { LoadMoreDirection } from '../global/types'; + +import { areSortedArraysEqual } from '../util/iteratees'; +import useForceUpdate from './useForceUpdate'; +import useLastCallback from './useLastCallback'; +import usePrevious from './usePrevious'; + +type GetMore = (args: { direction: LoadMoreDirection }) => void; +type LoadMoreBackwards = (args: { offsetId?: string | number }) => void; + +const DEFAULT_LIST_SLICE = 30; + +const useInfiniteScroll = ( + loadMoreBackwards?: LoadMoreBackwards, + listIds?: ListId[], + isDisabled = false, + listSlice = DEFAULT_LIST_SLICE, +): [ListId[]?, GetMore?] => { + const requestParamsRef = useRef<{ + direction?: LoadMoreDirection; + offsetId?: ListId; + }>(); + + const currentStateRef = useRef<{ viewportIds: ListId[]; isOnTop: boolean } | undefined>(); + if (!currentStateRef.current && listIds && !isDisabled) { + const { + newViewportIds, + newIsOnTop, + } = getViewportSlice(listIds, LoadMoreDirection.Forwards, listSlice, listIds[0]); + currentStateRef.current = { viewportIds: newViewportIds, isOnTop: newIsOnTop }; + } + + const forceUpdate = useForceUpdate(); + + if (isDisabled) { + requestParamsRef.current = {}; + } + + const prevListIds = usePrevious(listIds); + const prevIsDisabled = usePrevious(isDisabled); + const areListsEqual = listIds && prevListIds + && listIds.length > 0 && prevListIds.length > 0 + && listIds[0] === prevListIds[0] + && listIds[listIds.length - 1] === prevListIds[prevListIds.length - 1]; + + if (listIds && !isDisabled && (!areListsEqual || isDisabled !== prevIsDisabled)) { + const { viewportIds, isOnTop } = currentStateRef.current || {}; + const currentMiddleId = viewportIds && !isOnTop ? viewportIds[Math.round(viewportIds.length / 2)] : undefined; + const defaultOffsetId = currentMiddleId && listIds.includes(currentMiddleId) ? currentMiddleId : listIds[0]; + const { offsetId = defaultOffsetId, direction = LoadMoreDirection.Forwards } = requestParamsRef.current || {}; + const { newViewportIds, newIsOnTop } = getViewportSlice(listIds, direction, listSlice, offsetId); + + requestParamsRef.current = {}; + + if (!viewportIds || !areSortedArraysEqual(viewportIds, newViewportIds)) { + currentStateRef.current = { viewportIds: newViewportIds, isOnTop: newIsOnTop }; + } + } else if (!listIds) { + currentStateRef.current = undefined; + } + + const getMore: GetMore = useLastCallback(({ + direction, + }: { direction: LoadMoreDirection; noScroll?: boolean }) => { + const { viewportIds } = currentStateRef.current || {}; + + const offsetId = viewportIds + ? direction === LoadMoreDirection.Backwards ? viewportIds[viewportIds.length - 1] : viewportIds[0] + : undefined; + + requestParamsRef.current = { direction, offsetId }; + + if (!listIds) { + if (loadMoreBackwards) { + loadMoreBackwards({ offsetId }); + } + + return; + } + + const { + newViewportIds, areSomeLocal, areAllLocal, newIsOnTop, + } = getViewportSlice(listIds, direction, listSlice, offsetId); + + if (areSomeLocal && !(viewportIds && areSortedArraysEqual(viewportIds, newViewportIds))) { + currentStateRef.current = { viewportIds: newViewportIds, isOnTop: newIsOnTop }; + forceUpdate(); + } + + if (!areAllLocal && loadMoreBackwards) { + loadMoreBackwards({ offsetId }); + } + }); + + return isDisabled ? [listIds] : [currentStateRef.current?.viewportIds, getMore]; +}; + +function getViewportSlice( + sourceIds: ListId[], + direction: LoadMoreDirection, + listSlice: number, + offsetId?: ListId, +) { + const { length } = sourceIds; + const index = offsetId ? sourceIds.indexOf(offsetId) : 0; + const isForwards = direction === LoadMoreDirection.Forwards; + const indexForDirection = isForwards ? index : (index + 1) || length; + const from = Math.max(0, indexForDirection - listSlice); + const to = indexForDirection + listSlice - 1; + const newViewportIds = sourceIds.slice(Math.max(0, from), to + 1); + + let areSomeLocal; + let areAllLocal; + switch (direction) { + case LoadMoreDirection.Forwards: + areSomeLocal = indexForDirection >= 0; + areAllLocal = from >= 0; + break; + case LoadMoreDirection.Backwards: + areSomeLocal = indexForDirection < length; + areAllLocal = to <= length - 1; + break; + } + + return { + newViewportIds, + areSomeLocal, + areAllLocal, + newIsOnTop: newViewportIds[0] === sourceIds[0], + }; +} + +export default useInfiniteScroll; diff --git a/src/hooks/useInterval.ts b/src/hooks/useInterval.ts index e85f41ba..bc490e31 100644 --- a/src/hooks/useInterval.ts +++ b/src/hooks/useInterval.ts @@ -1,22 +1,20 @@ -import { useEffect, useLayoutEffect, useRef } from '../lib/teact/teact'; +import { useEffect } from '../lib/teact/teact'; -function useInterval(callback: NoneToVoidFunction, delay?: number, noFirst = false) { - const savedCallback = useRef(callback); +import useLastCallback from './useLastCallback'; - useLayoutEffect(() => { - savedCallback.current = callback; - }, [callback]); +function useInterval(callback: NoneToVoidFunction, delay?: number, noFirst = false) { + const lastCallback = useLastCallback(callback); useEffect(() => { if (delay === undefined) { return undefined; } - const id = setInterval(() => savedCallback.current(), delay); - if (!noFirst) savedCallback.current(); + const id = setInterval(lastCallback, delay); + if (!noFirst) lastCallback(); return () => clearInterval(id); - }, [delay, noFirst]); + }, [delay, lastCallback, noFirst]); } export default useInterval; diff --git a/src/hooks/usePasswordValidation.ts b/src/hooks/usePasswordValidation.ts index c4936db0..0a5e1daa 100644 --- a/src/hooks/usePasswordValidation.ts +++ b/src/hooks/usePasswordValidation.ts @@ -2,11 +2,21 @@ import { useEffect, useState } from '../lib/teact/teact'; const SPECIAL_CHARS_REGEX = /[`!@#$%^&*()_+\-=\]{};':"\\|,.<>?~]/; +interface OwnProps { + firstPassword?: string; + secondPassword?: string; + requiredMinLength?: number; + requiredLength?: number; + isOnlyNumbers?: boolean; +} + export const usePasswordValidation = ({ firstPassword = '', secondPassword = '', - requiredLength = 8, -}) => { + requiredMinLength = 8, + requiredLength, + isOnlyNumbers, +}: OwnProps) => { const [invalidLength, setInvalidLength] = useState(false); const [noNumber, setNoNumber] = useState(false); const [noUpperCase, setNoUpperCase] = useState(false); @@ -15,13 +25,17 @@ export const usePasswordValidation = ({ const [noEqual, setNoEqual] = useState(false); useEffect(() => { - setInvalidLength(firstPassword.length < requiredLength); - setNoUpperCase(firstPassword.toLowerCase() === firstPassword); - setNoLowerCase(firstPassword.toUpperCase() === firstPassword); + const isInvalidLength = Boolean( + (!requiredLength && firstPassword.length < requiredMinLength) + || (requiredLength && firstPassword.length !== requiredLength), + ); + setInvalidLength(isInvalidLength); + setNoUpperCase(!isOnlyNumbers && firstPassword.toLowerCase() === firstPassword); + setNoLowerCase(!isOnlyNumbers && firstPassword.toUpperCase() === firstPassword); setNoNumber(!/\d/.test(firstPassword)); setNoEqual(Boolean(firstPassword && firstPassword !== secondPassword)); - setNoSpecialChar(!SPECIAL_CHARS_REGEX.test(firstPassword)); - }, [firstPassword, secondPassword, requiredLength]); + setNoSpecialChar(!isOnlyNumbers && !SPECIAL_CHARS_REGEX.test(firstPassword)); + }, [firstPassword, secondPassword, requiredMinLength, isOnlyNumbers, requiredLength]); return { invalidLength, noNumber, noUpperCase, noLowerCase, noEqual, noSpecialChar, diff --git a/src/hooks/usePrevious.ts b/src/hooks/usePrevious.ts index 9e7c2139..6da28f0b 100644 --- a/src/hooks/usePrevious.ts +++ b/src/hooks/usePrevious.ts @@ -1,5 +1,6 @@ import { useRef } from '../lib/teact/teact'; +// Deprecated. Use `usePrevious2` instead function usePrevious(next: T): T | undefined; function usePrevious(next: T, shouldSkipUndefined: true): Exclude | undefined; function usePrevious(next: T, shouldSkipUndefined?: boolean): Exclude | undefined; diff --git a/src/hooks/usePrevious2.ts b/src/hooks/usePrevious2.ts new file mode 100644 index 00000000..996e25ed --- /dev/null +++ b/src/hooks/usePrevious2.ts @@ -0,0 +1,15 @@ +import { useRef } from '../lib/teact/teact'; + +// This is not render-dependent and will never allow previous to match current +export default function usePrevious2(current: T) { + const prevRef = useRef(); + const lastRef = useRef(); + + if (lastRef.current !== current) { + prevRef.current = lastRef.current; + } + + lastRef.current = current; + + return prevRef.current; +} diff --git a/src/hooks/useScrolledState.ts b/src/hooks/useScrolledState.ts index cd206572..ea7d650a 100644 --- a/src/hooks/useScrolledState.ts +++ b/src/hooks/useScrolledState.ts @@ -15,5 +15,10 @@ export default function useScrolledState(threshold = THRESHOLD) { setIsAtEnd(scrollHeight - scrollTop - clientHeight < threshold); }); - return { isAtBeginning, isAtEnd, handleScroll }; + return { + isAtBeginning, + isAtEnd, + isScrolled: !isAtBeginning, + handleScroll, + }; } diff --git a/src/hooks/useTimeout.ts b/src/hooks/useTimeout.ts index 5cc3b18b..4053bf22 100644 --- a/src/hooks/useTimeout.ts +++ b/src/hooks/useTimeout.ts @@ -1,19 +1,19 @@ -import { useEffect, useLayoutEffect, useRef } from '../lib/teact/teact'; +import { useEffect } from '../lib/teact/teact'; -function useTimeout(callback: () => void, delay?: number) { - const savedCallback = useRef(callback); +import useLastCallback from './useLastCallback'; - useLayoutEffect(() => { - savedCallback.current = callback; - }, [callback]); +function useTimeout(callback: () => void, delay?: number, dependencies: readonly any[] = []) { + const savedCallback = useLastCallback(callback); useEffect(() => { if (typeof delay !== 'number') { return undefined; } - const id = setTimeout(() => savedCallback.current(), delay); + + const id = setTimeout(() => savedCallback(), delay); return () => clearTimeout(id); - }, [delay]); + // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps + }, [delay, savedCallback, ...dependencies]); } export default useTimeout; diff --git a/src/hooks/useTraceUpdatedProps.ts b/src/hooks/useTraceUpdatedProps.ts new file mode 100644 index 00000000..0cd242f5 --- /dev/null +++ b/src/hooks/useTraceUpdatedProps.ts @@ -0,0 +1,33 @@ +import { useEffect, useRef } from '../lib/teact/teact'; + +/** + * Custom React hook for tracing updates to props. + * This hook logs the changed properties of a component every time it re-renders. + * It is useful for debugging purposes to see which props have changed between renders. + * + * @param {Record} props - The current props of the component. + * @param {boolean} [shouldTrace=false] - Flag to enable or disable tracing of prop changes. + * - When true, the hook logs the changes to the console. + * - Default is false, meaning tracing is off by default. + */ +export default function useTraceUpdatedProps(props: Record, shouldTrace = false) { + const prevProps = useRef>(); + + useEffect(() => { + const changedProps = Object.entries(props).reduce((acc: Record, [key, value]) => { + if (prevProps.current) { + if (prevProps.current[key] !== value) { + acc[key] = { old: prevProps.current[key], new: value }; + } + } + return acc; + }, {}); + + if (Object.keys(changedProps).length > 0 && shouldTrace) { + // eslint-disable-next-line no-console + console.log('Changed props:', changedProps); + } + + prevProps.current = props; + }); +} diff --git a/src/hooks/useTransitionFixes.ts b/src/hooks/useTransitionFixes.ts deleted file mode 100644 index be471c11..00000000 --- a/src/hooks/useTransitionFixes.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { requestMeasure, requestMutation } from '../lib/fasterdom/fasterdom'; -import useLastCallback from './useLastCallback'; - -// Set `min-height` for transition container to prevent jumping when switching tabs -export default function useTransitionFixes( - containerRef: { current: HTMLDivElement | null }, - transitionElSelector: string, - tabsElSelector: string, -) { - const applyTransitionFix = useLastCallback(() => { - // This callback is called from `Transition.onStart` which is "mutate" phase - requestMeasure(() => { - const container = containerRef.current; - if (!container) return; - - const transitionEl = container.querySelector(transitionElSelector); - const tabsEl = container.querySelector(tabsElSelector); - if (transitionEl && tabsEl) { - const newHeight = container.offsetHeight - tabsEl.offsetHeight; - - requestMutation(() => { - transitionEl.style.minHeight = `${newHeight}px`; - }); - } - }); - }); - - const releaseTransitionFix = useLastCallback(() => { - const container = containerRef.current!; - if (!container) return; - - const transitionEl = container.querySelector(transitionElSelector); - if (transitionEl) { - transitionEl.style.minHeight = ''; - } - }); - - return { applyTransitionFix, releaseTransitionFix }; -} diff --git a/src/hooks/useWindowSize.ts b/src/hooks/useWindowSize.ts index a26253c8..514eae11 100644 --- a/src/hooks/useWindowSize.ts +++ b/src/hooks/useWindowSize.ts @@ -7,9 +7,14 @@ import useDebouncedCallback from './useDebouncedCallback'; const THROTTLE = 250; export default function useWindowSize() { - const { width: initialWidth, height: initialHeight } = windowSize.get(); + const { + width: initialWidth, + height: initialHeight, + screenHeight: initialScreenHeight, + } = windowSize.get(); const [width, setWidth] = useState(initialWidth); const [height, setHeight] = useState(initialHeight); + const [screenHeight, setScreenHeight] = useState(initialScreenHeight); const [isResizing, setIsResizing] = useState(false); const setIsResizingDebounced = useDebouncedCallback(setIsResizing, [setIsResizing], THROTTLE, true); @@ -19,9 +24,10 @@ export default function useWindowSize() { }, THROTTLE, true); const throttledSetSize = throttle(() => { - const { width: newWidth, height: newHeight } = windowSize.get(); + const { width: newWidth, height: newHeight, screenHeight: newScreenHeight } = windowSize.get(); setWidth(newWidth); setHeight(newHeight); + setScreenHeight(newScreenHeight); setIsResizingDebounced(false); }, THROTTLE, false); @@ -37,5 +43,7 @@ export default function useWindowSize() { }; }, [setIsResizingDebounced]); - return useMemo(() => ({ width, height, isResizing }), [height, isResizing, width]); + return useMemo(() => ({ + width, height, screenHeight, isResizing, + }), [height, isResizing, screenHeight, width]); } diff --git a/src/i18n/en.yaml b/src/i18n/en.yaml index 710755ba..74a5d090 100644 --- a/src/i18n/en.yaml +++ b/src/i18n/en.yaml @@ -101,21 +101,20 @@ $logout_accounts_without_backup_warning: You have not backed up %links%. If you $logout_warning: This will disconnect the wallet from this app. You will be able to restore your wallet using **%1$d secret words** - or import another wallet. Receive TON: Receive TON Your address was copied!: Your address was copied! -Show QR-Code: Show QR-Code -Create Invoice: Create Invoice -QR-code: QR-code +Show QR Code: Show QR Code +Create Deposit Link: Create Deposit Link +QR Code: QR Code $receive_invoice_description: | You can specify the amount and purpose of - the payment to save the sender some time + the payment to save the sender some time. Amount: Amount Comment: Comment Share this URL to receive TON: Share this URL to receive TON Invoice link was copied!: Invoice link was copied! -US Dollar: US Dollar Theme: Theme Language: Language Enable Animations: Enable Animations -Fiat Currency: Fiat Currency +Base Currency: Base Currency Investor View: Investor View Toggle Investor View: Toggle Investor View Hide Tiny Transfers: Hide Tiny Transfers @@ -167,12 +166,12 @@ Loading...: Loading... Recipient Address: Recipient Address Wallet address or domain: Wallet address or domain Incorrect address: Incorrect address +Incorrect address.: Incorrect address. Paste: Paste Insufficient balance: Insufficient balance InsufficientBalance: Insufficient balance Optional: Optional $send_token_symbol: Send %1$s -$balance_is: "Balance: %balance%" Is it all ok?: Is it all ok? Receiving Address: Receiving Address Fee: Fee @@ -200,8 +199,8 @@ Today: Today Yesterday: Yesterday Now: Now $receive_ton_description: | - You can share this address, show QR-code - or create invoice to receive TON + You can share your address, scan QR code + or create a deposit link to receive crypto. Your address: Your address Wrong password, please try again: Wrong password, please try again Appearance: Appearance @@ -213,7 +212,7 @@ Earn from your tokens while holding them: Earn from your tokens while holding th $est_apy_val: Est. APY %1$d% Why this is safe: Why this is safe Why staking is safe?: Why staking is safe? -$safe_staking_description1: Staking is **fully decentralized** and operated by the **official TON Nominator** smart contract. No one can gain access to your staked tokens. +$safe_staking_description1: Staking is **fully decentralized** and operated by the **official TON Liquid Staking** smart contracts. $safe_staking_description2: The deposited stake will be used for the TON network validation as part of its **proof-of-stake** essence. $safe_staking_description3: You can withdraw your stake at **any time** and it will be deposited back to your account within **two days**. $min_value: Min %value% @@ -338,6 +337,28 @@ $dapp_ledger_warning1: You are about to send a multi-way transaction using your $dapp_ledger_warning2: Please take your time and do not interrupt the process. Agree: Agree The hardware wallet does not support this data format: The hardware wallet does not support this data format +Swap: Swap +You sell: You sell +You buy: You buy +Swap Details: Swap Details +Blockchain fee: Blockchain fee +Price Impact: Price Impact +Minimum Received: Minimum Received +Slippage: Slippage +$swap_from_to: Swap %from% to %to% +The exchange rate is below market value!: The exchange rate is %value% below market value! +Invalid Pair: Invalid Pair +We do not recommend to perform an exchange, try to specify a lower amount.: We do not recommend to perform an exchange, try to specify a lower amount. +$swap_minimum_received_tooltip1: This is the least amount of new tokens you'll get from this swap, considering current market conditions, slippage tolerance, and potential price impact. +$swap_minimum_received_tooltip2: It's an expected minimum, but if the conditions change a lot while your swap is being processed, the final amount could be less. +$swap_price_impact_tooltip1: This shows how much your trade might change the token's price. +$swap_price_impact_tooltip2: Big trades can make the price go up or down more. Lower is usually better. +$swap_slippage_tooltip1: This sets how much the price is allowed to change before your swap is processed. +$swap_slippage_tooltip2: If during the processing of your swap, the price changes more than this value, your order will be canceled. +Coins have been swapped!: Coins have been swapped! +Slippage not specified: Slippage not specified +Slippage too high: Slippage too high +Not enough TON: Not enough TON Use Responsibly: Use Responsibly $auth_responsibly_description1: | MyTonWallet is a **self-custodial** wallet, which means that **only you** have full control and, most importantly, **full responsibility** for your funds. @@ -363,9 +384,128 @@ $auth_backup_description1: | $auth_backup_description2: "And with great power comes **great responsibility**." $auth_backup_description3: | You need to manually **back up secret keys** in case you forget your password or lose access to this device. +Swap Placed: Swap Placed +Swapping: Swapping +Swapped: Swapped +Swap Failed: Swap Failed +Exchange rate: Exchange rate +Swap Again: Swap Again Terms of Use: Terms of Use Privacy Policy: Privacy Policy +Insufficient liquidity: Insufficient liquidity This address is new and never received transfers before.: This address is new and never received transfers before. Create Backup: Create Backup Sending: Sending -failed: failed +Failed: Failed +Refunded: Refunded +On hold: On hold +Expired: Expired +In progress: In progress +Waiting Payment: Waiting Payment +Waiting for payment: Waiting for payment +Blockchain: Blockchain +Receiving address in blockchain: Receiving address in %blockchain% blockchain +Please provide an address of your wallet in another blockchain to receive bought tokens.: Please provide an address of your wallet in another blockchain to receive bought tokens. +The time for sending coins is over: The time for sending coins is over +Please wait a few moments...: Please wait a few moments... +You have not sent the coins to the specified address.: You have not sent the coins to the specified address. +$swap_changelly_to_ton_description1: "You must send %value% to this address in %blockchain% blockchain within %time%" +Please contact support and provide a transaction ID.: Please contact support and provide a transaction ID. +Exchange failed and coins were refunded to your wallet.: Exchange failed and coins were refunded to your wallet. +Please contact security team to pass the KYC procedure.: Please contact security team to pass the KYC procedure. +Swap Expired: Swap Expired +Swap On Hold: Swap On Hold +Swap Refunded: Swap Refunded +Changelly Payment Address: Changelly Payment Address +Cross-chain exchange provided by Changelly: Cross-chain exchange provided by Changelly +$swap_changelly_agreement_message: By continuing, you agree to the %terms% and %policy% and understand that the transaction may trigger verification according to %kyc%. +$swap_changelly_terms_of_use: terms of use +$swap_changelly_privacy_policy: privacy policy +Password must contain %length% digits.: Password must contain %length% digits. +Deposit Link: Deposit Link +$swap_changelly_from_ton_description: "Tokens will be sent to your address in %blockchain% blockchain in a few moments:" +Minimum amount: Minimum %value% +Maximum amount: Maximum %value% +Display Tray Icon: Display Tray Icon +Enable Auto-Updates: Enable Auto-Updates +Biometric Authentication: Biometric Authentication +Turn off biometrics?: Turn off biometrics? +If you turn off biometric protection, you will need to create a password.: If you turn off biometric protection, you will need to create a password. +Enter your current password: Enter your current password +Turn On Biometrics: Turn On Biometrics +Please confirm transaction using biometrics: Please confirm transaction using biometrics +Enabling biometric confirmation will reset the password.: Enabling biometric confirmation will reset the password. +Biometric Registration: Biometric Registration +Step 1 of 2. Registration: Step 1 of 2. Registration +Step 2 of 2. Verification: Step 2 of 2. Verification +Biometric setup failed: Biometric setup failed +Biometric confirmation failed: Biometric confirmation failed +Failed to disable biometrics: Failed to disable biometrics +Biometric Confirmation: Biometric Confirmation +Please verify your identity.: Please verify your identity. +Create Password: Create Password +Complete: Complete +Verification: Verification +Create a password or use biometric authentication to protect it.: Create a password or use biometric authentication to protect it. +Connect Biometrics: Connect Biometrics +Use Password: Use Password +Biometrics Disabled: Biometrics Disabled +Biometrics Enabled: Biometrics Enabled +A request is already pending: A request is already pending +Please confirm operation using biometrics: Please confirm operation using biometrics +Scan QR Code: Scan QR Code +Permission denied. Please grant camera permission to use the QR code scanner.: Permission denied. Please grant camera permission to use the QR code scanner. +Unsupported barcode format: Unsupported barcode format +An error on the server side. Please try again.: An error on the server side. Please try again. +Unrecognized QR Code: Unrecognized QR Code +$fee_value_almost_equal: Fee ≈ %fee% +Address was saved!: Address was saved! +US Dollar: US Dollar +Euro: Euro +Ruble: Ruble +Yuan: Yuan +Bitcoin: Bitcoin +Toncoin: Toncoin +$max_balance: "Max: %balance%" +$unstake_information_instantly: You will get your deposit instantly. +Select Token: Select Token +MY: MY +POPULAR: POPULAR +A-Z: A-Z +Not Found: Not Found +Wallet is ready!: Wallet is ready! +Wallet is imported!: Wallet is imported! +Create a code to protect it: Create a code to protect it +Enter your code again: Enter your code again +Codes don't match: Codes don’t match +Code set successfully: Code set successfully +Use Biometrics: Use Biometrics +Use Touch ID: Use Touch ID +Use Face ID: Use Face ID +You can connect your biometric data for more convenience: You can connect your biometric data for more convenience +Connect Touch ID: Connect Touch ID +Connect Face ID: Connect Face ID +Not Now: Not Now +Biometric Authentification: Biometric Authentification +Touch ID: Touch ID +Face ID: Face ID +Turn Off Touch ID?: Turn Off Touch ID? +Turn Off Face ID?: Turn Off Face ID? +Turn Off Biometrics?: Turn Off Biometrics? +Enter code: Enter code +Enter code or use Face ID: Enter code or use Face ID +Enter code or use Touch ID: Enter code or use Touch ID +Enter code or user biometrics: Enter code or user biometrics +Wrong code, please try again: Wrong code, please try again +Scan your fingerprint: Scan your fingerprint +Incorrect code, please try again: Incorrect code, please try again +Correct: Correct +Confirm Operation: Confirm Operation +Confirm Swap: Confirm Swap +"%amount% to %address%": "%amount% to %address%" +"%amount_from% to %amount_to%": "%amount_from% to %amount_to%" +Failed to enable biometrics: Failed to enable biometrics +Are you sure you want to disable Face ID?: Are you sure you want to disable Face ID? +Are you sure you want to disable Touch ID?: Are you sure you want to disable Touch ID? +Are you sure you want to disable biometrics?: Are you sure you want to disable biometrics? +Yes: Yes diff --git a/src/i18n/es.yaml b/src/i18n/es.yaml index b10a53ef..2023ba99 100644 --- a/src/i18n/es.yaml +++ b/src/i18n/es.yaml @@ -7,7 +7,7 @@ Import From %1$d Secret Words: Importar usando la frase semilla About MyTonWallet: Acerca de MyTonWallet Creating Wallet...: Creando monedero... On the count of three...: A la cuenta de tres... -Back Up: Hacer copia de seguridad +Back Up: Siguiente Passwords must be equal.: Las contraseñas deben coincidir. To protect your wallet as much as possible, use a password with: Para la máxima protección de su monedero, use una contraseña con $auth_password_rule_8chars: al menos 8 caracteres @@ -40,7 +40,7 @@ Let's Check: Revisemos Let's Check!: ¡Revisemos! Safety Rules: Normas de seguridad $safety_rules_one: | - En la siguiente pantalla verás las %1$s palabras de la **frase semilla**. Escríbalas en el mismo orden y **guárdelas en un lugar seguro**. + En la siguiente pantalla verás las palabras de la **frase semilla**. Escríbalas en el mismo orden y **guárdelas en un lugar seguro**. $safety_rules_two: Permiten **acceder a su mondero** si pierde su contraseña o el acceso a este dispositivo. $safety_rules_three: Si alguien más las ve, sus activos **pueden ser robados**. ¡Mire a su alrededor! Understood: Entiendo @@ -101,21 +101,20 @@ $logout_accounts_without_backup_warning: No ha realizado copia de seguridad de % $logout_warning: Esto desconectará el monedero de esta aplicación. Podrá restaurar su monedero usando la frase semilla... Receive TON: Recibir TON Your address was copied!: ¡Su dirección fue copiada! -Show QR-Code: Mostrar código QR -Create Invoice: Crear factura -QR-code: Código QR +Show QR Code: Mostrar código QR +Create Deposit Link: Crear enlace de depósito +QR Code: Código QR $receive_invoice_description: | Puede especificar la cantidad y - el propósito del pago para ahorrar tiempo al remitente + el propósito del pago para ahorrar tiempo al remitente. Amount: Cantidad Comment: Comentario Share this URL to receive TON: Comparte esta URL para recibir TON Invoice link was copied!: ¡Se copió el enlace de la factura! -US Dollar: Dólar estadounidense Theme: Tema Language: Idioma Enable Animations: Habilitar animaciones -Fiat Currency: Moneda fíat +Base Currency: Moneda base Investor View: Modo Inversor Toggle Investor View: Alternar Modo Inversor Hide Tiny Transfers: Ocultar transacciones pequeñas @@ -167,12 +166,12 @@ Loading...: Cargando... Recipient Address: Dirección del destinatario Wallet address or domain: Dirección o dominio del monedero Incorrect address: Dirección incorrecta +Incorrect address.: Dirección incorrecta. Paste: Pegar Insufficient balance: Saldo insuficiente InsufficientBalance: Saldo insuficiente Optional: Opcional $send_token_symbol: Enviar %1$s -$balance_is: "Saldo: %balance%" Is it all ok?: ¿Está todo bien? Receiving Address: Dirección del receptor Fee: Comisión @@ -199,7 +198,7 @@ $tiny_transfers_help: Desactive esta opción para mostrar transacciones de menos Today: Hoy Yesterday: Ayer Now: Ahora -$receive_ton_description: Puede compartir esta dirección, mostrar el código QR o crear una factura para recibir TON +$receive_ton_description: Puede compartir su dirección, escanear un código QR o crear un enlace de depósito para recibir criptomonedas. Your address: Tu dirección Wrong password, please try again: Contraseña incorrecta, inténtalo de nuevo Appearance: Apariencia @@ -212,7 +211,7 @@ Earn from your tokens while holding them: Gana con tus tokens mientras los manti $est_apy_val: Est. APY %1$d% Why this is safe: Por qué esto es seguro Why staking is safe?: ¿Por qué el staking es seguro? -$safe_staking_description1: El staking es **totalmente descentralizado** y operado por los **nominadores oficiales de TON**. Nadie puede obtener acceso a sus tokens en staking. +$safe_staking_description1: El staking es **totalmente descentralizado** y operado por los **TON Liquid Staking smart contracts**. $safe_staking_description2: La participación depositada se utilizará para la validación de la red TON como parte de su esencia de **prueba de participación**. $safe_staking_description3: Puede retirar su apuesta en **cualquier momento** y se depositará de nuevo en su cuenta dentro de **dos días**. $min_value: Min %value% @@ -338,6 +337,28 @@ $dapp_ledger_warning1: Estás a punto de enviar una transacción multi-direccion $dapp_ledger_warning2: Por favor, tómate tu tiempo y no interrumpas el proceso. Agree: Aceptar The hardware wallet does not support this data format: La billetera de hardware no admite este formato de datos +Swap: Intercambiar +You sell: Usted vende +You buy: Usted compra +Swap Details: Detalles del intercambio +Blockchain fee: Tarifa de blockchain +Price Impact: Impacto en el precio +Minimum Received: Cantidad mínima recibida +Slippage: Deslizamiento +$swap_from_to: Intercambiar de %from% a %to% +The exchange rate is below market value!: La tasa de cambio está un %value% por debajo del valor de mercado. +Invalid Pair: Par Inválido +We do not recommend to perform an exchange, try to specify a lower amount.: No recomendamos realizar un intercambio, intente especificar una cantidad menor. +$swap_minimum_received_tooltip1: Esta es la cantidad mínima de nuevos tokens que recibirá en este intercambio, considerando las condiciones actuales del mercado, la tolerancia al deslizamiento y el posible impacto en el precio. +$swap_minimum_received_tooltip2: Es un mínimo esperado, pero si las condiciones cambian mucho mientras su intercambio está siendo procesado, la cantidad final podría ser menor. +$swap_price_impact_tooltip1: Esto muestra cuánto puede cambiar el precio de su operación el precio del token. +$swap_price_impact_tooltip2: Las operaciones grandes pueden hacer que el precio suba o baje más. En general, es mejor un valor más bajo. +$swap_slippage_tooltip1: Esto establece cuánto se permite que el precio cambie antes de que se procese su intercambio. +$swap_slippage_tooltip2: Si durante el procesamiento de su intercambio, el precio cambia más que este valor, su orden será cancelada. +Coins have been swapped!: ¡Las monedas han sido intercambiadas! +Slippage not specified: Deslizamiento no especificado +Slippage too high: Deslizamiento demasiado alto +Not enough TON: TON insuficiente Use Responsibly: Usar responsablemente $auth_responsibly_description1: | MyTonWallet es una billetera con **autocustodia**, lo que significa que **solo usted** tiene el control total y, lo que es más importante, la **total responsabilidad** de sus fondos. @@ -363,9 +384,125 @@ $auth_backup_description1: | $auth_backup_description2: "Y un gran poder conlleva una **gran responsabilidad**." $auth_backup_description3: | Debe hacer manualmente una **copia de seguridad de la frase semilla** que le permitirá recuperar su monedero en caso de que olvide su contraseña o pierda el acceso a este dispositivo. +Swap Placed: Intercambio Colocado +Swapping: Intercambiando +Swapped: Intercambiado +Swap Failed: Intercambio Fallido +Exchange rate: Tasa de Cambio +Swap Again: Intercambiar de Nuevo Terms of Use: Términos de Uso Privacy Policy: Política de Privacidad +Insufficient liquidity: Liquidez insuficiente This address is new and never received transfers before.: Esta dirección es nueva y nunca ha recibido transferencias antes. Create Backup: Crear copia de seguridad Sending: Enviando -failed: fallido +Failed: Fallido +Refunded: Reembolsado +On hold: En espera +Expired: Expirado +In progress: En progreso +Waiting Payment: Esperando Pago +Waiting for payment: Esperando el pago +Blockchain: Blockchain +Receiving address in blockchain: Dirección en %blockchain% +Please provide an address of your wallet in another blockchain to receive bought tokens.: Por favor, proporcione una dirección de su billetera en otra cadena de bloques para recibir tokens comprados. +The time for sending coins is over: El tiempo para enviar monedas ha terminado +Please wait a few moments...: Por favor, espere unos momentos... +You have not sent the coins to the specified address.: Usted no ha enviado las monedas a la dirección especificada. +$swap_changelly_to_ton_description1: "Debe enviar %value% a esta dirección en la cadena de bloques %blockchain% dentro de %time%" +Please contact support and provide a transaction ID.: Por favor, póngase en contacto con el soporte y proporcione un ID de transacción. +Exchange failed and coins were refunded to your wallet.: El intercambio falló y las monedas fueron reembolsadas a su billetera. +Please contact security team to pass the KYC procedure.: Por favor, póngase en contacto con el equipo de seguridad para completar el procedimiento de KYC. +Swap Expired: Intercambio vencido +Swap On Hold: Intercambio en Espera +Swap Refunded: Intercambio reembolsado +Changelly Payment Address: Dirección de Pago de Changelly +Cross-chain exchange provided by Changelly: Proporcionado por Changelly +$swap_changelly_agreement_message: Al continuar, usted acepta los %terms% y %policy% y comprende que la transacción puede desencadenar una verificación según el %kyc%. +$swap_changelly_terms_of_use: términos de uso +$swap_changelly_privacy_policy: política de privacidad +Password must contain %length% digits.: La contraseña debe contener %length% dígitos. +Deposit Link: Enlace de depósito +$swap_changelly_from_ton_description: "Los tokens serán enviados a tu dirección en la blockchain de %blockchain% en unos momentos:" +Minimum amount: Mínimo %value% +Maximum amount: Máximo %value% +Display Tray Icon: Icono de bandeja +Enable Auto-Updates: Habilitar actualizaciones automáticas +Biometric Authentication: Autenticación biométrica +Turn off biometrics?: ¿Desactivar la biometría? +If you turn off biometric protection, you will need to create a password.: Si desactiva la protección biométrica, deberá crear una contraseña. +Enter your current password: Introduce tu contraseña actual +Turn On Biometrics: Activar la biometría +Please confirm transaction using biometrics: Confirme la transacción utilizando datos biométricos +Enabling biometric confirmation will reset the password.: Al habilitar la confirmación biométrica se restablecerá la contraseña. +Biometric Registration: Registro Biométrico +Step 1 of 2. Registration: Paso 1 de 2. Registro +Step 2 of 2. Verification: Paso 2 de 2. Verificación +Biometric setup failed: Error en la configuración biométrica +Biometric confirmation failed: Error de confirmación biométrica +Failed to disable biometrics: No se pudo desactivar la biometría +Biometric Confirmation: Confirmación biométrica +Please verify your identity.: Verifique por favor su Identidad. +Create Password: Crear contraseña +Complete: Completo +Verification: Verificación +Create a password or use biometric authentication to protect it.: Cree una contraseña o utilice autenticación biométrica para protegerla. +Connect Biometrics: Conectar biométrico +Use Password: Usar contraseña +Biometrics Disabled: Biometría deshabilitada +Biometrics Enabled: Biometría habilitada +A request is already pending: Ya hay una solicitud pendiente +Please confirm operation using biometrics: Confirme la operación mediante datos biométricos. +Scan QR Code: Escanear código QR +Permission denied. Please grant camera permission to use the QR code scanner.: Permiso denegado. Otorgue permiso a la cámara para usar el escáner QR. +Unsupported barcode format: Formato de código de barras no admitido +An error on the server side. Please try again.: Un error en el lado del servidor. Inténtalo de nuevo. +Unrecognized QR Code: Código QR no reconocido +$fee_value_almost_equal: Comisión ≈ %fee% +Address was saved!: ¡Dirección guardada! +US Dollar: Dólar estadounidense +Euro: Euro +Ruble: Rublo +Yuan: Yuan +$max_balance: "Máximo: %balance%" +$unstake_information_instantly: Obtendrá su depósito al instante. +Select Token: Seleccionar Token +MY: MI +POPULAR: POPULAR +A-Z: A-Z +Not Found: No Encontrado +Wallet is ready!: ¡La billetera está lista! +Wallet is imported!: ¡La billetera es importada! +Create a code to protect it: Crea un código para protegerlo. +Enter your code again: Ingresa tu código nuevamente +Codes don't match: Los códigos no coinciden +Code set successfully: Código configurado correctamente +Use Biometrics: Utilice la biometría +Use Touch ID: Usar Touch ID +Use Face ID: Usar Face ID +You can connect your biometric data for more convenience: Puede conectar sus datos biométricos para mayor comodidad +Connect Touch ID: Conectar Touch ID +Connect Face ID: Conectar Face ID +Not Now: Ahora no +Biometric Authentification: Autenticación biométrica +Touch ID: Touch ID +Face ID: Face ID +Turn Off Touch ID?: ¿Desactivar Touch ID? +Turn Off Face ID?: ¿Desactivar Face ID? +Turn Off Biometrics?: ¿Desactivar la biometría? +Enter code: Introduzca el código +Enter code or use Face ID: Ingresa el código o usa Face ID +Enter code or use Touch ID: Ingresa el código o usa Touch ID +Enter code or user biometrics: Ingrese el código o la biometría del usuario +Wrong code, please try again: Código incorrecto, inténtalo de nuevo. +Scan your fingerprint: Escanea tu huella digital +Incorrect code, please try again: Codigo Incorrecto, por favor intenta de nuevo +Correct: Correcto +Confirm Operation: Confirmar operación +Confirm Swap: Confirmar intercambio +"%amount% to %address%": "%amount% a %address%" +"%amount_from% to %amount_to%": "%amount_from% a %amount_to%" +Are you sure you want to disable Face ID?: ¿Estás seguro de que quieres deshabilitar Face ID? +Are you sure you want to disable Touch ID?: ¿Estás seguro de que quieres deshabilitar Touch ID? +Are you sure you want to disable biometrics?: ¿Estás seguro de que quieres deshabilitar la biometría? +Yes: Sí diff --git a/src/i18n/ru.yaml b/src/i18n/ru.yaml index 9aa0504e..6694d76b 100644 --- a/src/i18n/ru.yaml +++ b/src/i18n/ru.yaml @@ -93,7 +93,7 @@ Delete: Удалить Delete Saved Address: Удалить сохранённый адрес Are you sure you want to remove this address from your saved ones?: Уверены, что хотите удалить сохранённый адрес кошелька? You will be able to save it again via Transaction Info with this address.: Вы сможете сохранить его снова, открыв любой перевод с этим адресом. -Address removed from saved: Адрес кошелька удален из сохранённых +Address removed from saved: Адрес кошелька удалён из сохранённых Warning!: Важно! Log Out: Выход есть $logout_without_backup_warning: Резервная копия кошелька не создана. Доступ к токенам и NFT будет ограничен, если вы выйдете из системы. @@ -102,9 +102,9 @@ $logout_warning: Кошелёк будет удалён только **на эт $logout_confirm: Выйти из **всех** кошельков Receive TON: Получить TON Your address was copied!: Адрес скопирован! -Show QR-Code: Показать QR-код -Create Invoice: Создать инвойс -QR-code: QR-код +Show QR Code: Показать QR-код +Create Deposit Link: Ссылка на депозит +QR Code: QR-код $receive_invoice_description: | Укажите сумму и назначение платежа, если это необходимо. @@ -112,11 +112,10 @@ Amount: Сумма Comment: Комментарий Share this URL to receive TON: Поделиться ссылкой Invoice link was copied!: Ссылка скопирована! -US Dollar: Доллар США Theme: Тема Language: Язык Enable Animations: Включить анимацию -Fiat Currency: Фиатная валюта +Base Currency: Базовая валюта Investor View: Режим инвестора Toggle Investor View: Переключить в режим инвестора Hide Tiny Transfers: Скрывать мелкие переводы @@ -168,12 +167,12 @@ Loading...: Загрузка... Recipient Address: Адрес получателя Wallet address or domain: Адрес кошелька или домен Incorrect address: Неверный адрес +Incorrect address.: Неверный адрес. Paste: Вставить Insufficient balance: Недостаточный баланс InsufficientBalance: Недостаточный баланс Optional: Необязательно $send_token_symbol: Отправить %1$s -$balance_is: "Баланс: %balance%" Is it all ok?: Всё верно? Receiving Address: Адрес получателя Fee: Комиссия @@ -198,19 +197,19 @@ $tiny_transfers_help: Выключите этот параметр, чтобы Today: Сегодня Yesterday: Вчера Now: Сейчас -$receive_ton_description: Вы можете поделиться этим адресом, отсканировать QR-код или создать инвойс для получения TON +$receive_ton_description: Вы можете поделиться своим адресом, отсканировать QR-код или создать ссылку на депозит для получения криптовалюты. Your address: Ваш адрес Wrong password, please try again: Неправильный пароль, попробуйте ещё раз Appearance: Внешний вид Light: Светлая Dark: Тёмная System: Системная -Stake TON: Продолжить +Stake TON: Стейкинг Earn from your tokens while holding them: Получайте пассивный доход от хранения TON на надёжном официальном смарт-контракте $est_apy_val: Доходность ~%1$d% Why this is safe: Почему это безопасно Why staking is safe?: Это точно безопасно? -$safe_staking_description1: Стейкинг **полностью децентрализован** и управляется **официальным смарт-контрактом** TON Nominator. Ни разработчики, ни кто-либо ещё **не имеет доступа** к средствам на вашем депозите. +$safe_staking_description1: Стейкинг **полностью децентрализован** и управляется **официальными смарт-контрактами** TON Liquid Staking. $safe_staking_description2: Ваш депозит будет использован для валидации транзакций в блокчейне в рамках механизма **proof-of-stake**. $safe_staking_description3: Вы можете вывести депозит с заработанными процентами **в любой момент** — средства переведутся на ваш основной счёт **в течение двух дней**. $min_value: Мин. %value% @@ -229,7 +228,7 @@ Unstake TON: Вывести депозит $unstake_information_with_time: Текущий депозит будет полностью отправлен обратно на ваш кошелёк через %time%. Amount to unstake: Сумма к выводу Confirm Unstaking: Подтвердить вывод -Request for unstaking is sent!: Запрос на вывод депозита отправлен! +Request for unstaking is sent!: Запрос на вывод отправлен! $unstaking_when_receive: Депозит будет выведен через %time% $unstake_insufficient_balance: Вам необходимо иметь %balance% на вашем основном балансе, чтобы отправить заявку на вывод депозита. at APY %1$s%: при APY %1$s% @@ -250,7 +249,7 @@ InvalidAmount: Некорректная сумма InvalidToAddress: Некорректный адрес получателя DomainNotResolved: Ошибка резолвинга домена You can save this address for quick access while sending.: Вы можете сохранить этот адрес для быстрого доступа при отправке. -Stake Again: Stake Again +Stake Again: Повторить депозит Earned: Earned Connect Dapp: Подключить приложение Select wallets to use on this dapp: Выберите кошельки для использования с этим приложением @@ -317,7 +316,7 @@ Switch to the newly opened tab to connect Ledger.: Переключитесь н Once connected, switch back to this window to proceed.: После подключения вернитесь в это окно, чтобы продолжить. Not all transactions were sent successfully: Не все транзакции были успешно отправлены The time on your device is incorrect, sync it and try again: Время на вашем устройстве некорректно, синхронизируйте его и попробуйте снова -Name or Address...: Имя или Адрес... +Name or Address...: Имя или адрес... Such error, many tabs: Неактивная вкладка $many_tabs_error_description: | MyTonWallet поддерживает только одну активную вкладку с приложением. @@ -333,6 +332,27 @@ $dapp_ledger_warning1: Вы собираетесь отправить много $dapp_ledger_warning2: Пожалуйста, не торопитесь и не прерывайте процесс. Agree: Согласен The hardware wallet does not support this data format: Аппаратный кошелёк не поддерживает данный формат данных +Swap: Обмен +You sell: Вы продаёте +You buy: Вы покупаете +Swap Details: Детали обмена +Blockchain fee: Комиссия блокчейна +Price Impact: Влияние на цену +Minimum Received: Минимально получите +Slippage: Проскальзывание +$swap_from_to: Обменять %from% на %to% +The exchange rate is below market value!: Курс обмена на %value% ниже рыночной стоимости! +Invalid Pair: Недопустимая пара +We do not recommend to perform an exchange, try to specify a lower amount.: Не рекомендуем выполнять обмен, попробуйте указать меньшую сумму. +$swap_minimum_received_tooltip1: Это наименьшее количество новых токенов, которое вы получите от этого обмена, учитывая текущие условия рынка, допустимое проскальзывание и потенциальное влияние на цену. +$swap_minimum_received_tooltip2: Это ожидаемое минимальное значение, но, если условия сильно изменятся во время обработки вашего обмена, итоговая сумма может быть меньше. +$swap_price_impact_tooltip1: Это показывает, насколько ваша сделка может изменить цену токена. +$swap_price_impact_tooltip2: Большие сделки могут сильнее повлиять на цену. В целом, меньшее значение лучше. +$swap_slippage_tooltip1: Здесь устанавливается, насколько цена может измениться, прежде чем ваш обмен будет обработан. +$swap_slippage_tooltip2: Если во время обработки вашего обмена цена изменится больше, чем на это значение, ваш заказ будет отменён. +Coins have been swapped!: Токены были обменяны! +Slippage not specified: Проскальзывание не указано +Slippage too high: Проскальзывание слишком большое Not enough TON: Недостаточно TON Use Responsibly: Используйте ответственно $auth_responsibly_description1: | @@ -349,7 +369,7 @@ $auth_backup_warning_notice: | Later: Позже Back Up Now: Показать слова сейчас I have read and accept this information: Я прочитал и принимаю эту информацию. -$ledger_verify_address: Всегда проверяйте вставленный адрес используя Ledger. +$ledger_verify_address: Всегда проверяйте вставленный адрес, используя Ledger. $ledger_not_ready: Ledger не подключён или приложение TON не открыто. Verify now: Проверить сейчас Invalid address format. Only URL Safe Base64 format is allowed.: Некорректный формат адреса. Разрешен только URL Safe Base64 формат. @@ -365,9 +385,126 @@ $auth_backup_description3: | **резервную копию** секретных слов. Это поможет, если вы потеряете доступ к этому устройству или забудете пароль. +Swap Placed: Обмен размещён +Swapping: Идёт обмен +Swapped: Обмен +Swap Failed: Обмен не выполнен +Exchange rate: Обменный курс +Swap Again: Повторить обмен Terms of Use: Условия использования Privacy Policy: Политика конфиденциальности +Insufficient liquidity: Недостаточно ликвидности This address is new and never received transfers before.: Этот адрес новый и ранее не получал переводов. Create Backup: Создание резервной копии Sending: Отправка -failed: не выполнен +Failed: Ошибка +Refunded: Возвращено +On hold: Удержан +Expired: Истёк +In progress: В процессе +Waiting Payment: Ожидание оплаты +Waiting for payment: Ожидание оплаты +Blockchain: Блокчейн +Receiving address in blockchain: Адрес для получения в блокчейне %blockchain% +Please provide an address of your wallet in another blockchain to receive bought tokens.: Пожалуйста, укажите адрес вашего кошелька в другом блокчейне для получения купленных токенов. +The time for sending coins is over: Время отправки монет истекло +Please wait a few moments...: Пожалуйста, подождите некоторое время... +You have not sent the coins to the specified address.: Вы не отправили монеты на указанный адрес. +$swap_changelly_to_ton_description1: "Вы должны отправить %value% на этот адрес в блокчейне %blockchain% в течение %time%" +Please contact support and provide a transaction ID.: Пожалуйста, свяжитесь с поддержкой и предоставьте идентификатор транзакции. +Exchange failed and coins were refunded to your wallet.: Обмен не удался и монеты были возвращены на ваш кошелёк. +Please contact security team to pass the KYC procedure.: Пожалуйста, свяжитесь с командой безопасности, чтобы пройти процедуру KYC. +Swap Expired: Обмен истёк +Swap On Hold: Обмен удержан +Swap Refunded: Обмен возвращен +Changelly Payment Address: Адрес оплаты Changelly +Cross-chain exchange provided by Changelly: Сервис предоставлен Changelly +$swap_changelly_agreement_message: Продолжая, вы соглашаетесь с %terms% и %policy% и понимаете, что транзакция может вызвать проверку в соответствии с %kyc%. +$swap_changelly_terms_of_use: условиями использования +$swap_changelly_privacy_policy: политикой конфиденциальности +Password must contain %length% digits.: Пароль должен содержать %length% цифры. +Deposit Link: Ссылка для пополнения +$swap_changelly_from_ton_description: "Токены будут отправлены на ваш адрес в блокчейне %blockchain% в ближайшее время:" +Minimum amount: Минимум %value% +Maximum amount: Максимум %value% +Display Tray Icon: Иконка на панели задач +Enable Auto-Updates: Включить автообновления +Biometric Authentication: Биометрическая аутентификация +Turn off biometrics?: Отключить биометрию? +If you turn off biometric protection, you will need to create a password.: Если вы отключите биометрическую защиту, вам потребуется создать пароль. +Enter your current password: Введите ваш текущий пароль +Turn On Biometrics: Включение биометрии +Please confirm transaction using biometrics: Пожалуйста, подтвердите транзакцию с помощью биометрии +Enabling biometric confirmation will reset the password.: Включение биометрического подтверждения приведет к сбросу пароля. +Biometric Registration: Подключение биометрии +Step 1 of 2. Registration: Шаг 1 из 2. Регистрация +Step 2 of 2. Verification: Шаг 2 из 2. Проверка +Biometric setup failed: Настроить биометрию не удалось +Biometric confirmation failed: Подтвердить биометрию не удалось +Failed to disable biometrics: Не удалось отключить биометрию +Biometric Confirmation: Подтверждение биометрии +Please verify your identity.: Пожалуйста, подтвердите вашу личность. +Create Password: Придумайте пароль +Complete: Завершить +Verification: Проверка +Create a password or use biometric authentication to protect it.: Создайте пароль или используйте биометрическую аутентификацию для его защиты. +Connect Biometrics: Подключить биометрию +Use Password: Использовать пароль +Biometrics Disabled: Биометрия отключена +Biometrics Enabled: Биометрия включена +A request is already pending: Подключение биометрии уже выполняется +Please confirm operation using biometrics: Пожалуйста, подтвердите операцию с помощью биометрии +Scan QR Code: Сканировать QR-код +Permission denied. Please grant camera permission to use the QR code scanner.: Доступ запрещён. Пожалуйста, дайте камере разрешение на использование сканера QR-кода. +Unsupported barcode format: Неподдерживаемый формат штрих-кода +An error on the server side. Please try again.: Ошибка на стороне сервера. Пожалуйста, попробуйте ещё раз. +Unrecognized QR Code: Нераспознанный QR-код +$fee_value_almost_equal: Комиссия ≈ %fee% +Address was saved!: Адрес сохранён! +US Dollar: Доллар США +Euro: Евро +Ruble: Рубль +Yuan: Юань +$max_balance: "Максимум: %balance%" +$unstake_information_instantly: Вы получите свой депозит мгновенно. +Select Token: Выбрать токен +MY: МОИ +POPULAR: ПОПУЛЯРНЫЕ +A-Z: А-Я +Not Found: Не найдено +Wallet is ready!: Кошелёк готов! +Wallet is imported!: Кошелёк импортирован! +Create a code to protect it: Создайте код для его защиты +Enter your code again: Введите свой код ещё раз +Codes don't match: Коды не совпадают +Code set successfully: Код успешно установлен +Use Biometrics: Используйте биометрию +Use Touch ID: Используйте Touch ID +Use Face ID: Используйте Face ID +You can connect your biometric data for more convenience: Для большего удобства вы можете подключить свои биометрические данные +Connect Touch ID: Подключить Touch ID +Connect Face ID: Подключить Face ID +Not Now: Не сейчас +Biometric Authentification: Биометрическая аутентификация +Touch ID: Touch ID +Face ID: Face ID +Turn Off Touch ID?: Отключить Touch ID? +Turn Off Face ID?: Отключить Face ID? +Turn Off Biometrics?: Отключить биометрию? +Enter code: Введите код +Enter code or use Face ID: Введите код или используйте Face ID +Enter code or use Touch ID: Введите код или используйте Touch ID +Enter code or user biometrics: Введите код или используйте биометрические данные +Wrong code, please try again: Неверный код, попробуйте ещё раз +Scan your fingerprint: Отсканируйте свой отпечаток пальца +Incorrect code, please try again: Неверный код, пожалуйста, попробуйте снова +Correct: Правильно +Confirm Operation: Подтвердите операцию +Confirm Swap: Подтвердить обмен +"%amount% to %address%": "%amount% на %address%" +"%amount_from% to %amount_to%": "%amount_from% на %amount_to%" +Failed to enable biometrics: Не удалось включить биометрию +Are you sure you want to disable Face ID?: Вы уверены, что хотите отключить Face ID? +Are you sure you want to disable Touch ID?: Вы уверены, что хотите отключить Touch ID? +Are you sure you want to disable biometrics?: Вы уверены, что хотите отключить биометрию? +Yes: Да diff --git a/src/i18n/zh-Hans.yaml b/src/i18n/zh-Hans.yaml index 42be9aac..07116482 100644 --- a/src/i18n/zh-Hans.yaml +++ b/src/i18n/zh-Hans.yaml @@ -96,19 +96,18 @@ $logout_accounts_without_backup_warning: 您还没有备份此账户 %links%。 $logout_warning: 这将断开钱包与此应用程序的连接,您将能使用 **%1$d 个密钥** 恢复您的钱包 - 或导入另一个钱包。 Receive TON: 接收 TON Your address was copied!: 您的地址已复制! -Show QR-Code: 显示二维码 -Create Invoice: 创建发票 -QR-code: 二维码 -$receive_invoice_description: 您可以指定付款金额和收款地址来节省付款方的一些时间 +Show QR Code: 显示二维码 +Create Deposit Link: 创建存款链接 +QR Code: 二维码 +$receive_invoice_description: 您可以指定付款金额和收款地址来节省付款方的一些时间。 Amount: 数量 Comment: 留言 Share this URL to receive TON: 分享此链接来接收 TON Invoice link was copied!: 发票链接已复制! -US Dollar: 美元 Theme: 主题 Language: 语言 Enable Animations: 启用动画 -Fiat Currency: 法定货币 +Base Currency: 基本货币 Investor View: 投资者见解 Toggle Investor View: 切换投资者见解 Hide Tiny Transfers: 隐藏小额转账 @@ -158,12 +157,12 @@ Loading...: 读取中... Recipient Address: 收款人地址 Wallet address or domain: 钱包地址或域名 Incorrect address: 地址错误 +Incorrect address.: 地址错误。 Paste: 粘贴 Insufficient balance: 余额不足 InsufficientBalance: 余额不足 Optional: 选项 $send_token_symbol: 发送 %1$s -$balance_is: "余额:%balance%" Is it all ok?: 确认全部正确? Receiving Address: 接收地址 Fee: 手续费 @@ -191,9 +190,7 @@ Understood: 明白 Today: 今天 Yesterday: 明天 Now: 现在 -$receive_ton_description: | - 您可以分享这个地址,显示二维码 - 或创建支票来收取 TON +$receive_ton_description: 您可以分享您的地址、扫描二维码或创建存款链接来接收加密货币。 Your address: 您的地址 Wrong password, please try again: 密码错误,请再试一次。 Appearance: 外观 @@ -205,7 +202,7 @@ Earn from your tokens while holding them: 从你持有的代币身上赚一笔 $est_apy_val: 期望年回报率 %1$d% Why this is safe: 为什么这是安全的 Why staking is safe?: 为什么质押是安全的? -$safe_staking_description1: TON 质押是**完全去中心化的**,由官方 TON Nominator 智能合约运营。 没有人可以访问您抵押的代币。 +$safe_staking_description1: TON 质押是**完全去中心化的**,由官方 TON Liquid Staking 智能合约运营。 $safe_staking_description2: 作为其 **权益证明** 的一部分,您存入的 TON 将用于 TON 区块网络验证。 $safe_staking_description3: 您可以在 **任何时候** 解除您 TON 的质押,它会在 **36小时** 内存入您的账户。 $min_value: 最小 %1$s @@ -324,6 +321,28 @@ $dapp_ledger_warning1: 您即将使用您的**Ledger**钱包发送多方交易 $dapp_ledger_warning2: 请慢慢来,不要中断过程。 Agree: 同意 The hardware wallet does not support this data format: 硬件钱包不支持该数据格式 +Swap: 交换 +You sell: 您卖出 +You buy: 您购买 +Swap Details: 交换详情 +Blockchain fee: 区块链费用 +Price Impact: 价格影响 +Minimum Received: 最低接收数量 +Slippage: 下单误差 +$swap_from_to: 从 %from% 交换至 %to% +The exchange rate is below market value!: 汇率低于市场价值 %value%! +Invalid Pair: 无效交易对 +We do not recommend to perform an exchange, try to specify a lower amount.: 我们不建议进行交换,请尝试指定较小的金额。 +$swap_minimum_received_tooltip1: 这是您从此次交换中可能获得的最少新代币数量,考虑了目前市场条件、滑点容忍度和潜在价格影响。 +$swap_minimum_received_tooltip2: 这是预期的最低值,但如果在处理您的交换时条件发生很大变化,最终金额可能会较少。 +$swap_price_impact_tooltip1: 这显示了您的交易可能对代币价格造成的影响。 +$swap_price_impact_tooltip2: 大型交易可能会使价格上涨或下跌得更多。较低的值通常较好。 +$swap_slippage_tooltip1: 设置此值可以允许价格在进行交换前发生变化的程度。 +$swap_slippage_tooltip2: 如果在处理您的交换时,价格变化超过此值,您的订单将被取消。 +Coins have been swapped!: 币已交换完成! +Slippage not specified: 未指定下单误差 +Slippage too high: 下单误差过高 +Not enough TON: TON 不足 Use Responsibly: 负责任地使用 $auth_responsibly_description1: | MyTonWallet 是一个**自我托管**钱包,这意味着**只有您**拥有完全控制权,最重要的是,对您的资金**承担全部责任**。 @@ -348,10 +367,129 @@ $auth_backup_description1: | $auth_backup_description2: “能力越大,责任越大。” $auth_backup_description3: | 您需要手动**备份助记词**,以免忘记助记词以永远失去您的钱包。 -$transaction_at: 于价格 %price% +Swap Placed: 已提交交换 +Swapping: 进行交换 +Swapped: 已交换 +Swap Failed: 交换失败 +Exchange rate: 汇率 +Swap Again: 再次进行交换 Terms of Use: 使用条款 Privacy Policy: 隐私政策 +Insufficient liquidity: 流动性不足 This address is new and never received transfers before.: 这个地址是新的,以前从未收到过转帐。 Create Backup: 创建备份 Sending: 发送 -failed: 失敗 +Failed: 失败 +Refunded: 退款 +On hold: 持有 +Expired: 过期 +In progress: 进行中 +Waiting Payment: 等待付款 +Waiting for payment: 等待付款 +Blockchain: 区块链 +Receiving address in blockchain: 在 %blockchain% 区块链中的接收地址 +Please provide an address of your wallet in another blockchain to receive bought tokens.: 请提供您在另一个区块链上的钱包地址以接收已购买的代币。 +The time for sending coins is over: 发送硬币的时间已结束 +Please wait a few moments...: 请稍等片刻... +You have not sent the coins to the specified address.: 您尚未将硬币发送到指定地址。 +$swap_changelly_to_ton_description1: "您必须在 %time% 内将 %value% 发送到此地址 在 %blockchain% 区块链中" +Please contact support and provide a transaction ID.: 请联系支持并提供交易ID。 +Exchange failed and coins were refunded to your wallet.: 交换失败,硬币已退还到您的钱包。 +Please contact security team to pass the KYC procedure.: 请联系安全团队以完成KYC程序。 +Swap Expired: 交换已过期 +Swap On Hold: 交换暂停 +Swap Refunded: 交换已退款 +Changelly Payment Address: Changelly 付款地址 +Cross-chain exchange provided by Changelly: 由Changelly提供的跨链交换 +$swap_changelly_agreement_message: 继续操作,即表示您同意 %terms% 和 %policy%,并理解该交易可能会触发根据 %kyc% 进行的验证。 +$swap_changelly_terms_of_use: 使用条款 +$swap_changelly_privacy_policy: 隐私政策 +Password must contain %length% digits.: 密码必须包含%length%位数字。 +Deposit Link: 充值链接 +$swap_changelly_from_ton_description: "代币将在几分钟内发送到您在%blockchain%区块链上的地址:" +Minimum amount: 最低 %value% +Maximum amount: 最高 %value% +Display Tray Icon: 显示托盘图标 +Enable Auto-Updates: 启用自动更新 +Biometric Authentication: 生物识别认证 +Turn off biometrics?: 关闭生物识别? +If you turn off biometric protection, you will need to create a password.: 如果您关闭生物识别保护,则需要创建密码。 +Enter your current password: 输入当前密码 +Turn On Biometrics: 打开生物识别 +Please confirm transaction using biometrics: 请使用生物识别技术确认交易 +Enabling biometric confirmation will reset the password.: 启用生物识别确认将重置密码。 +Biometric Registration: 生物识别注册 +Step 1 of 2. Registration: 第 1 步(共 2 步):注册 +Step 2 of 2. Verification: 第 2 步(共 2 步):验证 +Biometric setup failed: 生物识别设置失败 +Biometric confirmation failed: 生物识别确认失败 +Failed to disable biometrics: 无法禁用生物识别 +Biometric Confirmation: 生物识别确认 +Please verify your identity.: 请证明你的身份。 +Create Password: 创建密码 +Complete: 完成 +Verification: 确认 +Create a password or use biometric authentication to protect it.: 创建密码或使用生物识别身份验证来保护它。 +Connect Biometrics: 连接生物识别 +Use Password: 使用密码 +Biometrics Disabled: 生物识别已禁用 +Biometrics Enabled: 启用生物识别 +A request is already pending: 请求已待处理 +Please confirm operation using biometrics: 请使用生物识别确认操作 +Scan QR Code: 扫描二维码 +Permission denied. Please grant camera permission to use the QR code scanner.: 没有权限。 请授予相机使用 QR 扫描仪的权限。 +Unsupported barcode format: 不支持的条形码格式 +An error on the server side. Please try again.: 服务器端的错误。请再试一次。 +Unrecognized QR Code: 无法识别的二维码 +$fee_value_almost_equal: 手续费 ≈ %fee% +Address was saved!: 地址已保存! +US Dollar: 美元 +Euro: 欧元 +Ruble: 卢布 +Yuan: 元 +$max_balance: "最大: %balance%" +$unstake_information_instantly: 您将立即获得存款。 +Select Token: 选择代币 +MY: 我的 +POPULAR: 流行 +A-Z: A-Z +Not Found: 未找到 +Wallet is ready!: 钱包准备好了! +Wallet is imported!: 钱包是进口的! +Create a code to protect it: 创建代码来保护它 +Enter your code again: 再次输入您的代码 +Codes don't match: 代码不匹配 +Code set successfully: 代码设置成功 +Use Biometrics: 使用生物识别技术 +Use Touch ID: 使用 Touch ID +Use Face ID: 使用 Face ID +You can connect your biometric data for more convenience: 您可以连接您的生物识别数据以获得更多便利 +Connect Touch ID: 连接 Touch ID +Connect Face ID: 连接 Face ID +Not Now: 现在不要 +Biometric Authentification: 生物识别认证 +Touch ID: Touch ID +Face ID: Face ID +Turn Off Touch ID?: 关闭 Touch ID? +Turn Off Face ID?: 关闭 Face ID? +Turn Off Biometrics?: 关闭生物识别技术? +Enter code: 输入代码 +Enter code or use Face ID: 输入代码或使用 Face ID +Enter code or use Touch ID: 输入代码或使用 Touch ID +Enter code or user biometrics: 输入代码或用户生物识别信息 +Wrong code, please try again: 代码错误,请重试 +Scan your fingerprint: 扫描您的指纹 +Incorrect code, please try again: 不正确的代码,请重试 +Correct: 正确 +Confirm Operation: 确认操作 +Confirm Swap: 确认兑换 +"%amount% to %address%": "%amount% 至 %address%" +"%amount_from% to %amount_to%": "%amount_from% 至 %amount_to%" +Failed to enable biometrics: 无法启用生物识别 +Disable Face ID: Disable Face ID +Disable Touch ID: Disable Touch ID +Disable Biometrics: Disable Biometrics +Are you sure you want to disable Face ID?: 您确定要禁用Face ID吗? +Are you sure you want to disable Touch ID?: 您确定要禁用Touch ID吗? +Are you sure you want to disable biometrics?: 您确定要禁用生物识别技术吗? +Yes: 是的 diff --git a/src/i18n/zh-Hant.yaml b/src/i18n/zh-Hant.yaml index 2e4ffb81..346e05f8 100644 --- a/src/i18n/zh-Hant.yaml +++ b/src/i18n/zh-Hant.yaml @@ -96,20 +96,18 @@ $logout_accounts_without_backup_warning: 您還沒有備份 %links%。 如果您 $logout_warning: 這將斷開錢包與此應用程序的連接。 您將能夠使用 **%1$d 個密語** 恢復您的錢包 - 或者導入另一個錢包。 Receive TON: 接收 TON Your address was copied!: 您的地址已被複製! -Show QR-Code: 顯示 QR-Code -Create Invoice: 建立發票 -QR-code: QR-code -$receive_invoice_description: | - 您可以指定付款金額和地址來節省發送人的一些時間 +Show QR Code: 顯示 QR Code +Create Deposit Link: 建立存款連結 +QR Code: QR Code +$receive_invoice_description: 您可以指定付款金額和地址來節省發送人的一些時間。 Amount: 數量 Comment: 註記欄 Share this URL to receive TON: 分享此連結來接收 TON Invoice link was copied!: 發票連結已複製! -US Dollar: 美金 Theme: 主題 Language: 語言 Enable Animations: 啟用動畫 -Fiat Currency: 法定貨幣 +Base Currency: 基本貨幣 Investor View: 投資者觀點 Toggle Investor View: 切換投資者觀點 Hide Tiny Transfers: 隱藏小額轉帳 @@ -154,17 +152,17 @@ $transaction_to: 到 %address% Wallet is not backed up: 錢包未備份 Testnet Version: 測試網版本 Error reading clipboard: 讀取剪貼板時出現錯誤 -$fee_value: "Fee: %fee%" +$fee_value: "手續費: %fee%" Loading...: 讀取中... Recipient Address: 接收人地址 Wallet address or domain: 錢包地址或是域名 Incorrect address: 錯誤的地址 +Incorrect address.: 錯誤的地址。 Paste: 貼上 Insufficient balance: 餘額不足 InsufficientBalance: 餘額不足 Optional: 選項 $send_token_symbol: 發送 %1$s -$balance_is: "餘額:%balance%" Is it all ok?: 全部確認都 OK? Receiving Address: 接收地址 Fee: 手續費 @@ -192,8 +190,7 @@ Understood: 了解 Today: 今天 Yesterday: 昨天 Now: 現在 -$receive_ton_description: | - 您可以分享這個地址,出示 QR-Code 或創建發票來接收 TON +$receive_ton_description: 您可以分享您的地址、掃描二維碼或建立存款連結來接收加密貨幣。 Your address: 您的地址 Wrong password, please try again: 密碼錯誤,請再嘗試一次 Appearance: 外觀 @@ -205,7 +202,7 @@ Earn from your tokens while holding them: 從你持有中的代幣獲利 $est_apy_val: Est. APY %1$d% Why this is safe: 為什麼這是安全的 Why staking is safe?: 為什麼質押是安全的? -$safe_staking_description1: Staking 是**完全去中心化的**,由官方 TON Nominator 智能合約運作。 沒有人可以使用您質押中的代幣. +$safe_staking_description1: Staking 是**完全去中心化的**,由官方 TON Liquid Staking 智能合約運作。 $safe_staking_description2: 作為其 **權益證明** 本質的一部分,存入的代幣將用於 TON 網路驗證。 $safe_staking_description3: 您可以在**任何時候**提取您的質押資產,它會在**2 天內**存入您的帳戶。 $min_value: 最少 %value% @@ -324,6 +321,28 @@ $dapp_ledger_warning1: 您即將使用您的**Ledger**錢包發送多方交易 $dapp_ledger_warning2: 請慢慢來,不要中斷過程。 Agree: 同意 The hardware wallet does not support this data format: 硬件錢包不支持該數據格式 +Swap: 交換 +You sell: 您賣出 +You buy: 您購買 +Swap Details: 交換詳情 +Blockchain fee: 區塊鏈費用 +Price Impact: 價格影響 +Minimum Received: 最低接收數量 +Slippage: 下單誤差 +$swap_from_to: 從 %from% 交換至 %to% +The exchange rate is below market value!: 匯率低於市場價值 %value%! +Invalid Pair: 無效交易對 +We do not recommend to perform an exchange, try to specify a lower amount.: 我們不建議進行交換,請嘗試指定較小的金額。 +$swap_minimum_received_tooltip1: 這是您從此次交換中可能獲得的最少新代幣數量,考慮了目前市場條件、滑點容忍度和潛在價格影響。 +$swap_minimum_received_tooltip2: 這是預期的最低值,但如果在處理您的交換時條件發生很大變化,最終金額可能會較少。 +$swap_price_impact_tooltip1: 這顯示了您的交易可能對代幣價格造成的影響。 +$swap_price_impact_tooltip2: 大型交易可能會使價格上漲或下跌得更多。較低的值通常較好。 +$swap_slippage_tooltip1: 設置此值可以允許價格在進行交換前發生變化的程度。 +$swap_slippage_tooltip2: 如果在處理您的交換時,價格變化超過此值,您的訂單將被取消。 +Coins have been swapped!: 币已交换完成! +Slippage not specified: 未指定下單誤差 +Slippage too high: 下單誤差過高 +Not enough TON: TON 不足 Use Responsibly: 負責任地使用 $auth_responsibly_description1: | MyTonWallet 是一個**自我託管**錢包,這意味著**只有您**擁有完全控制權,最重要的是,對您的資金**承擔全部責任**。 @@ -348,9 +367,129 @@ $auth_backup_description1: | $auth_backup_description2: 「能力越強,責任越大。」 $auth_backup_description3: | 你需要手動**備份註記詞**,免得你忘記助記詞而無法登入此裝置。 +Swap Placed: 已提交交換 +Swapping: 進行交換 +Swapped: 已交換 +Swap Failed: 交換失敗 +Exchange rate: 兌換率 +Swap Again: 再次進行交換 Terms of Use: 使用條款 Privacy Policy: 隱私政策 +Insufficient liquidity: 流動性不足 This address is new and never received transfers before.: 這個地址是新的,以前從未收到過轉帳。 Create Backup: 建立備份 Sending: 傳送 -failed: 失败 +Failed: 失敗 +Refunded: 退款 +On hold: 持有 +Expired: 過期 +In progress: 進行中 +Waiting Payment: 等待付款 +Waiting for payment: 等待付款 +Blockchain: 區塊鏈 +Receiving address in blockchain: 在 %blockchain% 區塊鏈中的接收地址 +Please provide an address of your wallet in another blockchain to receive bought tokens.: 請提供您在另一個區塊鏈上的錢包地址以接收已購買的代幣。 +The time for sending coins is over: 發送硬幣的時間已結束 +Please wait a few moments...: 請稍等片刻... +You have not sent the coins to the specified address.: 您尚未將硬幣發送到指定地址。 +$swap_changelly_to_ton_description1: "您必須在 %time% 內將 %value% 發送到此地址 在 %blockchain% 區塊鏈中" +Please contact support and provide a transaction ID.: 請聯繫支援並提供交易ID。 +Exchange failed and coins were refunded to your wallet.: 交換失敗,硬幣已退還到您的錢包。 +Please contact security team to pass the KYC procedure.: 請聯繫安全團隊以通過KYC程序。 +Swap Expired: 交換過期 +Swap On Hold: 交換暫停 +Swap Refunded: 交換退款 +Changelly Payment Address: Changelly 付款地址 +Cross-chain exchange provided by Changelly: Changelly提供的跨鏈交換 +$swap_changelly_agreement_message: 繼續操作即表示您同意 %terms% 和 %policy%,並理解該交易可能會觸發根據 %kyc% 的驗證。 +$swap_changelly_terms_of_use: 使用條款 +$swap_changelly_privacy_policy: 隱私政策 +Password must contain %length% digits.: 密碼必須包含%length%位數字。 +Deposit Link: 儲值連結 +$swap_changelly_from_ton_description: "代幣將在幾分鐘內發送到您在%blockchain%區塊鏈上的地址:" +Minimum amount: 最低 %value% +Maximum amount: 最高 %value% +Display Tray Icon: 顯示托盤圖標 +Enable Auto-Updates: 啟用自動更新 +Biometric Authentication: 生物辨識認證 +Turn off biometrics?: 關閉生物識別? +If you turn off biometric protection, you will need to create a password.: 如果您關閉生物辨識保護,則需要建立密碼。 +Enter your current password: 輸入目前密碼 +Turn On Biometrics: 打開生物識別 +Please confirm transaction using biometrics: 請使用生物辨識技術確認交易 +Enabling biometric confirmation will reset the password.: 啟用生物辨識確認將重設密碼。 +Biometric Registration: 生物辨識註冊 +Step 1 of 2. Registration: 第 1 步(共 2 步):註冊 +Step 2 of 2. Verification: 第 2 步(共 2 步):驗證 +Biometric setup failed: 生物辨識設定失敗 +Biometric confirmation failed: 生物辨識確認失敗 +Failed to disable biometrics: 無法禁用生物識別 +Biometric Confirmation: 生物辨識確認 +Please verify your identity.: 請證明你的身分。 +Create Password: 建立密碼 +Complete: 罷 +Verification: 確認 +Create a password or use biometric authentication to protect it.: 創建密碼或使用生物識別身份驗證來保護它。 +Connect Biometrics: 連接生物識別 +Use Password: 使用密碼 +Biometrics Disabled: 生物辨識已停用 +Biometrics Enabled: 啟用生物識別 +A request is already pending: 請求已待處理 +Please confirm operation using biometrics: 請使用生物辨識確認操作 +Scan QR Code: 掃描二維碼 +Permission denied. Please grant camera permission to use the QR code scanner.: 沒有權限。 請授予相機使用 QR 掃描器的權限。 +Unsupported barcode format: 不支援的條碼格式 +An error on the server side. Please try again.: 服務器端的錯誤。請再試一次。 +Unrecognized QR Code: 無法辨識的二維碼 +$fee_value_almost_equal: 手續費 ≈ %fee% +Address was saved!: 地址已儲存! +US Dollar: 美金 +Euro: 歐元 +Ruble: 盧布 +Yuan: 元 +$max_balance: "最大: %balance%" +$unstake_information_instantly: 您將立即收到您的存款。 +Select Token: 選擇代幣 +MY: 我的 +POPULAR: 受歡迎 +A-Z: A-Z +Not Found: 未找到 +Wallet is ready!: 錢包準備好了! +Wallet is imported!: 錢包是進口的! +Create a code to protect it: 創建程式碼來保護它 +Enter your code again: 再次輸入您的代碼 +Codes don't match: 代碼不匹配 +Code set successfully: 代碼設定成功 +Use Biometrics: 使用生物辨識技術 +Use Touch ID: 使用 Touch ID +Use Face ID: 使用 Face ID +You can connect your biometric data for more convenience: 您可以連接您的生物識別數據以獲得更多便利 +Connect Touch ID: 連接 Touch ID +Connect Face ID: 連接 Face ID +Not Now: 現在不要 +Biometric Authentification: 生物辨識認證 +Touch ID: Touch ID +Face ID: Face ID +Turn Off Touch ID?: 關閉 Touch ID? +Turn Off Face ID?: 關閉 Off Face ID? +Turn Off Biometrics?: 關閉生物辨識技術? +Enter code: 輸入代碼 +Enter code or use Face ID: 輸入代碼或使用 Face ID +Enter code or use Touch ID: 輸入代碼或使用 Touch ID +Enter code or user biometrics: 輸入代碼或使用者生物辨識訊息 +Wrong code, please try again: 程式碼錯誤,請重試 +Scan your fingerprint: 掃描您的指紋 +Incorrect code, please try again: Incorrect code, please try again +Correct: 不錯 +Confirm Operation: 確認操作 +Confirm Swap: 確認兌換 +"%amount% to %address%": "%amount% 至 %address%" +"%amount_from% to %amount_to%": "%amount_from% 至 %amount_to%" +Failed to enable biometrics: 無法啟用生物識別 +Disable Face ID: Disable Face ID +Disable Touch ID: Disable Touch ID +Disable Biometrics: Disable Biometrics +Are you sure you want to disable Face ID?: 您確定要禁用Face ID嗎? +Are you sure you want to disable Touch ID?: 您確定要禁用Touch ID嗎? +Are you sure you want to disable biometrics?: 您確定要禁用生物識別技術嗎? +Yes: 是的 diff --git a/src/index.tsx b/src/index.tsx index c0b2992f..1d8f0887 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -6,9 +6,13 @@ import React from './lib/teact/teact'; import TeactDOM from './lib/teact/teact-dom'; import { getActions, getGlobal } from './global'; -import { DEBUG, STRICTERDOM_ENABLED } from './config'; +import { DEBUG, IS_CAPACITOR, STRICTERDOM_ENABLED } from './config'; import { requestMutation } from './lib/fasterdom/fasterdom'; import { enableStrict } from './lib/fasterdom/stricterdom'; +import { betterView } from './util/betterView'; +import { initCapacitor } from './util/capacitor'; +import { initMultitab } from './util/multitab'; +import { CAN_DELEGATE_BOTTOM_SHEET, IS_DELEGATED_BOTTOM_SHEET } from './util/windowEnvironment'; import App from './components/App'; @@ -23,46 +27,62 @@ if (STRICTERDOM_ENABLED) { enableStrict(); } -getActions().init(); -getActions().initApi(); +if (IS_CAPACITOR) { + void initCapacitor(); +} -if (DEBUG) { - // eslint-disable-next-line no-console - console.log('>>> START INITIAL RENDER'); +if (CAN_DELEGATE_BOTTOM_SHEET) { + initMultitab({ noPub: true }); +} else if (IS_DELEGATED_BOTTOM_SHEET) { + initMultitab({ noSub: true }); } -requestMutation(() => { - TeactDOM.render( - , - document.getElementById('root')!, - ); -}); +(async () => { + await window.electron?.restoreStorage(); -if (DEBUG) { - // eslint-disable-next-line no-console - console.log('>>> FINISH INITIAL RENDER'); -} + getActions().init(); + getActions().initApi(); -document.addEventListener('dblclick', () => { - // eslint-disable-next-line no-console - console.warn('GLOBAL STATE', getGlobal()); -}); - -if (window.top === window) { - const selfXssWarnings: AnyLiteral = { - en: 'WARNING! This console can be a way for bad people to take over your crypto wallet through something called ' - + 'a Self-XSS attack. So, don\'t put in or paste code you don\'t understand. Stay safe!', - ru: 'ВНИМАНИЕ! Через эту консоль злоумышленники могут захватить ваш криптовалютный кошелёк с помощью так ' - + 'называемой атаки Self-XSS. Поэтому не вводите и не вставляйте код, который вы не понимаете. Берегите себя!', - es: '¡ADVERTENCIA! Esta consola puede ser una forma en que las personas malintencionadas se apoderen de su ' - + 'billetera de criptomonedas mediante un ataque llamado Self-XSS. Por lo tanto, ' - + 'no introduzca ni pegue código que no comprenda. ¡Cuídese!', - zh: '警告!这个控制台可能成为坏人通过所谓的Self-XSS攻击来接管你的加密货币钱包的方式。因此,请不要输入或粘贴您不理解的代码。请保护自己!', - }; - - const langCode = navigator.language.split('-')[0]; - const text = selfXssWarnings[langCode] || selfXssWarnings.en; + if (DEBUG) { + // eslint-disable-next-line no-console + console.log('>>> START INITIAL RENDER'); + } - // eslint-disable-next-line no-console - console.log('%c%s', 'color: red; background: yellow; font-size: 18px;', text); -} + requestMutation(() => { + TeactDOM.render( + , + document.getElementById('root')!, + ); + + betterView(); + }); + + if (DEBUG) { + // eslint-disable-next-line no-console + console.log('>>> FINISH INITIAL RENDER'); + } + + document.addEventListener('dblclick', () => { + // eslint-disable-next-line no-console + console.warn('GLOBAL STATE', getGlobal()); + }); + + if (window.top === window) { + const selfXssWarnings: AnyLiteral = { + en: 'WARNING! This console can be a way for bad people to take over your crypto wallet through something called ' + + 'a Self-XSS attack. So, don\'t put in or paste code you don\'t understand. Stay safe!', + ru: 'ВНИМАНИЕ! Через эту консоль злоумышленники могут захватить ваш криптовалютный кошелёк с помощью так ' + + 'называемой атаки Self-XSS. Поэтому не вводите и не вставляйте код, который вы не понимаете. Берегите себя!', + es: '¡ADVERTENCIA! Esta consola puede ser una forma en que las personas malintencionadas se apoderen de su ' + + 'billetera de criptomonedas mediante un ataque llamado Self-XSS. Por lo tanto, ' + + 'no introduzca ni pegue código que no comprenda. ¡Cuídese!', + zh: '警告!这个控制台可能成为坏人通过所谓的Self-XSS攻击来接管你的加密货币钱包的方式。因此,请不要输入或粘贴您不理解的代码。请保护自己!', + }; + + const langCode = navigator.language.split('-')[0]; + const text = selfXssWarnings[langCode] || selfXssWarnings.en; + + // eslint-disable-next-line no-console + console.log('%c%s', 'color: red; background: yellow; font-size: 18px;', text); + } +})(); diff --git a/src/lib/dexie/dexie.d.ts b/src/lib/dexie/dexie.d.ts new file mode 100644 index 00000000..336e1391 --- /dev/null +++ b/src/lib/dexie/dexie.d.ts @@ -0,0 +1,1028 @@ +/* + * Dexie.js - a minimalistic wrapper for IndexedDB + * =============================================== + * + * By David Fahlander, david.fahlander@gmail.com + * + * Version 3.2.4, Tue May 30 2023 + * + * https://dexie.org + * + * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/ + */ + // Generated by dts-bundle-generator v5.9.0 + +export interface IndexSpec { + name: string; + keyPath: string | Array | undefined; + unique: boolean | undefined; + multi: boolean | undefined; + auto: boolean | undefined; + compound: boolean | undefined; + src: string; +} +export interface TableSchema { + name: string; + primKey: IndexSpec; + indexes: IndexSpec[]; + mappedClass: Function; + idxByName: { + [name: string]: IndexSpec; + }; + readHook?: (x: any) => any; +} +export type IndexableTypePart = string | number | Date | ArrayBuffer | ArrayBufferView | DataView | Array>; +export type IndexableTypeArray = Array; +export type IndexableTypeArrayReadonly = ReadonlyArray; +export type IndexableType = IndexableTypePart | IndexableTypeArrayReadonly; +export interface DexieEvent { + subscribers: Function[]; + fire(...args: any[]): any; + subscribe(fn: (...args: any[]) => any): void; + unsubscribe(fn: (...args: any[]) => any): void; +} +export interface DexieEventSet { + (eventName: string): DexieEvent; // To be able to unsubscribe. + addEventType(eventName: string, chainFunction?: (f1: Function, f2: Function) => Function, defaultFunction?: Function): DexieEvent; + addEventType(events: { + [eventName: string]: ("asap" | [ + (f1: Function, f2: Function) => Function, + Function + ]); + }): DexieEvent; +} +export type TransactionMode = "readonly" | "readwrite" | "r" | "r!" | "r?" | "rw" | "rw!" | "rw?"; +export interface PromiseExtendedConstructor extends PromiseConstructor { + readonly prototype: PromiseExtended; + new (executor: (resolve: (value?: T | PromiseLike) => void, reject: (reason?: any) => void) => void): PromiseExtended; + all(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike, + T6 | PromiseLike, + T7 | PromiseLike, + T8 | PromiseLike, + T9 | PromiseLike, + T10 | PromiseLike + ]): PromiseExtended<[ + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10 + ]>; + all(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike, + T6 | PromiseLike, + T7 | PromiseLike, + T8 | PromiseLike, + T9 | PromiseLike + ]): PromiseExtended<[ + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9 + ]>; + all(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike, + T6 | PromiseLike, + T7 | PromiseLike, + T8 | PromiseLike + ]): PromiseExtended<[ + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8 + ]>; + all(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike, + T6 | PromiseLike, + T7 | PromiseLike + ]): PromiseExtended<[ + T1, + T2, + T3, + T4, + T5, + T6, + T7 + ]>; + all(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike, + T6 | PromiseLike + ]): PromiseExtended<[ + T1, + T2, + T3, + T4, + T5, + T6 + ]>; + all(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike + ]): PromiseExtended<[ + T1, + T2, + T3, + T4, + T5 + ]>; + all(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike + ]): PromiseExtended<[ + T1, + T2, + T3, + T4 + ]>; + all(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike + ]): PromiseExtended<[ + T1, + T2, + T3 + ]>; + all(values: [ + T1 | PromiseLike, + T2 | PromiseLike + ]): PromiseExtended<[ + T1, + T2 + ]>; + all(values: (T | PromiseLike)[]): PromiseExtended; + race(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike, + T6 | PromiseLike, + T7 | PromiseLike, + T8 | PromiseLike, + T9 | PromiseLike, + T10 | PromiseLike + ]): PromiseExtended; + race(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike, + T6 | PromiseLike, + T7 | PromiseLike, + T8 | PromiseLike, + T9 | PromiseLike + ]): PromiseExtended; + race(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike, + T6 | PromiseLike, + T7 | PromiseLike, + T8 | PromiseLike + ]): PromiseExtended; + race(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike, + T6 | PromiseLike, + T7 | PromiseLike + ]): PromiseExtended; + race(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike, + T6 | PromiseLike + ]): PromiseExtended; + race(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike, + T5 | PromiseLike + ]): PromiseExtended; + race(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike, + T4 | PromiseLike + ]): PromiseExtended; + race(values: [ + T1 | PromiseLike, + T2 | PromiseLike, + T3 | PromiseLike + ]): PromiseExtended; + race(values: [ + T1 | PromiseLike, + T2 | PromiseLike + ]): PromiseExtended; + race(values: (T | PromiseLike)[]): PromiseExtended; + reject(reason: any): PromiseExtended; + reject(reason: any): PromiseExtended; + resolve(value: T | PromiseLike): PromiseExtended; + resolve(): PromiseExtended; +} +/** The interface of Dexie.Promise, which basically extends standard Promise with methods: + * + * finally() - also subject for standardization + * timeout() - set a completion timeout + * catch(ErrorClass, handler) - java style error catching + * catch(errorName, handler) - cross-domain safe type error catching (checking error.name instead of instanceof) + * + */ +export interface PromiseExtended extends Promise { + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): PromiseExtended; + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): PromiseExtended; + catch(ErrorConstructor: Function, onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): PromiseExtended; + catch(errorName: string, onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): PromiseExtended; + finally(onFinally?: () => U | PromiseLike): PromiseExtended; + timeout(ms: number, msg?: string): PromiseExtended; +} +export type ThenShortcut = (value: T) => TResult | PromiseLike; +export interface Collection { + //db: Database; + and(filter: (x: T) => boolean): Collection; + clone(props?: Object): Collection; + count(): PromiseExtended; + count(thenShortcut: ThenShortcut): PromiseExtended; + distinct(): Collection; + each(callback: (obj: T, cursor: { + key: IndexableType; + primaryKey: TKey; + }) => any): PromiseExtended; + eachKey(callback: (key: IndexableType, cursor: { + key: IndexableType; + primaryKey: TKey; + }) => any): PromiseExtended; + eachPrimaryKey(callback: (key: TKey, cursor: { + key: IndexableType; + primaryKey: TKey; + }) => any): PromiseExtended; + eachUniqueKey(callback: (key: IndexableType, cursor: { + key: IndexableType; + primaryKey: TKey; + }) => any): PromiseExtended; + filter(filter: (x: T) => boolean): Collection; + first(): PromiseExtended; + first(thenShortcut: ThenShortcut): PromiseExtended; + keys(): PromiseExtended; + keys(thenShortcut: ThenShortcut): PromiseExtended; + primaryKeys(): PromiseExtended; + primaryKeys(thenShortcut: ThenShortcut): PromiseExtended; + last(): PromiseExtended; + last(thenShortcut: ThenShortcut): PromiseExtended; + limit(n: number): Collection; + offset(n: number): Collection; + or(indexOrPrimayKey: string): WhereClause; + raw(): Collection; + reverse(): Collection; + sortBy(keyPath: string): PromiseExtended; + sortBy(keyPath: string, thenShortcut: ThenShortcut): PromiseExtended; + toArray(): PromiseExtended>; + toArray(thenShortcut: ThenShortcut): PromiseExtended; + uniqueKeys(): PromiseExtended; + uniqueKeys(thenShortcut: ThenShortcut): PromiseExtended; + until(filter: (value: T) => boolean, includeStopEntry?: boolean): Collection; + // Mutating methods + delete(): PromiseExtended; + modify(changeCallback: (obj: T, ctx: { + value: T; + }) => void | boolean): PromiseExtended; + modify(changes: { + [keyPath: string]: any; + }): PromiseExtended; +} +export interface WhereClause { + above(key: any): Collection; + aboveOrEqual(key: any): Collection; + anyOf(keys: ReadonlyArray): Collection; + anyOf(...keys: Array): Collection; + anyOfIgnoreCase(keys: string[]): Collection; + anyOfIgnoreCase(...keys: string[]): Collection; + below(key: any): Collection; + belowOrEqual(key: any): Collection; + between(lower: any, upper: any, includeLower?: boolean, includeUpper?: boolean): Collection; + equals(key: IndexableType): Collection; + equalsIgnoreCase(key: string): Collection; + inAnyRange(ranges: ReadonlyArray<{ + 0: any; + 1: any; + }>, options?: { + includeLowers?: boolean; + includeUppers?: boolean; + }): Collection; + startsWith(key: string): Collection; + startsWithAnyOf(prefixes: string[]): Collection; + startsWithAnyOf(...prefixes: string[]): Collection; + startsWithIgnoreCase(key: string): Collection; + startsWithAnyOfIgnoreCase(prefixes: string[]): Collection; + startsWithAnyOfIgnoreCase(...prefixes: string[]): Collection; + noneOf(keys: ReadonlyArray): Collection; + notEqual(key: IndexableType): Collection; +} +export interface Database { + readonly name: string; + readonly tables: Table[]; + table(tableName: string): Table; + transaction(mode: TransactionMode, table: Table, scope: () => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: Table, table2: Table, scope: () => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: Table, table2: Table, table3: Table, scope: () => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: Table, table2: Table, table3: Table, table4: Table, scope: () => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: Table, table2: Table, table3: Table, table4: Table, table5: Table, scope: () => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, tables: Table[], scope: () => PromiseLike | U): PromiseExtended; +} +export interface TransactionEvents extends DexieEventSet { + (eventName: "complete", subscriber: () => any): void; + (eventName: "abort", subscriber: () => any): void; + (eventName: "error", subscriber: (error: any) => any): void; + complete: DexieEvent; + abort: DexieEvent; + error: DexieEvent; +} +export interface Transaction { + db: Database; + active: boolean; + mode: IDBTransactionMode; + //tables: { [type: string]: Table }; Deprecated since 2.0. Obsolete from v3.0. + storeNames: Array; + parent?: Transaction; + on: TransactionEvents; + abort(): void; + table(tableName: string): Table; + table(tableName: string): Table; + table(tableName: string): Table; +} +export interface CreatingHookContext { + onsuccess?: (primKey: Key) => void; + onerror?: (err: any) => void; +} +export interface UpdatingHookContext { + onsuccess?: (updatedObj: T) => void; + onerror?: (err: any) => void; +} +export interface DeletingHookContext { + onsuccess?: () => void; + onerror?: (err: any) => void; +} +export interface TableHooks extends DexieEventSet { + (eventName: "creating", subscriber: (this: CreatingHookContext, primKey: TKey, obj: T, transaction: Transaction) => any): void; + (eventName: "reading", subscriber: (obj: T) => T | any): void; + (eventName: "updating", subscriber: (this: UpdatingHookContext, modifications: Object, primKey: TKey, obj: T, transaction: Transaction) => any): void; + (eventName: "deleting", subscriber: (this: DeletingHookContext, primKey: TKey, obj: T, transaction: Transaction) => any): void; + creating: DexieEvent; + reading: DexieEvent; + updating: DexieEvent; + deleting: DexieEvent; +} +export const enum DBCoreRangeType { + Equal = 1, + Range = 2, + Any = 3, + Never = 4 +} +export interface DBCoreKeyRange { + readonly type: DBCoreRangeType; + readonly lower: any; + readonly lowerOpen?: boolean; + readonly upper: any; + readonly upperOpen?: boolean; +} +export interface DBCoreTransaction { + abort(): void; +} +export interface DbCoreTransactionOptions { + durability: ChromeTransactionDurability; +} +export type DBCoreMutateRequest = DBCoreAddRequest | DBCorePutRequest | DBCoreDeleteRequest | DBCoreDeleteRangeRequest; +export interface DBCoreMutateResponse { + numFailures: number; + failures: { + [operationNumber: number]: Error; + }; + lastResult: any; + results?: any[]; // Present on AddRequest and PutRequest. +} +export interface DBCoreAddRequest { + type: "add"; + trans: DBCoreTransaction; + values: any[]; + keys?: any[]; + /** @deprecated Will always get results since 3.1.0-alpha.5 */ + wantResults?: boolean; +} +export interface DBCorePutRequest { + type: "put"; + trans: DBCoreTransaction; + values: any[]; + keys?: any[]; + criteria?: { + index: string | null; + range: DBCoreKeyRange; + }; + changeSpec?: { + [keyPath: string]: any; + }; // Common changeSpec for each key + changeSpecs?: { + [keyPath: string]: any; + }[]; // changeSpec per key. + /** @deprecated Will always get results since 3.1.0-alpha.5 */ + wantResults?: boolean; +} +export interface DBCoreDeleteRequest { + type: "delete"; + trans: DBCoreTransaction; + keys: any[]; + criteria?: { + index: string | null; + range: DBCoreKeyRange; + }; +} +export interface DBCoreDeleteRangeRequest { + type: "deleteRange"; + trans: DBCoreTransaction; + range: DBCoreKeyRange; +} +export interface DBCoreGetManyRequest { + trans: DBCoreTransaction; + keys: any[]; + cache?: "immutable" | "clone"; +} +export interface DBCoreGetRequest { + trans: DBCoreTransaction; + key: any; +} +export interface DBCoreQuery { + index: DBCoreIndex; //keyPath: null | string | string[]; // null represents primary key. string a property, string[] several properties. + range: DBCoreKeyRange; +} +export interface DBCoreQueryRequest { + trans: DBCoreTransaction; + values?: boolean; + limit?: number; + query: DBCoreQuery; +} +export interface DBCoreQueryResponse { + result: any[]; +} +export interface DBCoreOpenCursorRequest { + trans: DBCoreTransaction; + values?: boolean; + unique?: boolean; + reverse?: boolean; + query: DBCoreQuery; +} +export interface DBCoreCountRequest { + trans: DBCoreTransaction; + query: DBCoreQuery; +} +export interface DBCoreCursor { + readonly trans: DBCoreTransaction; + readonly key: any; + readonly primaryKey: any; + readonly value?: any; + readonly done?: boolean; + continue(key?: any): void; + continuePrimaryKey(key: any, primaryKey: any): void; + advance(count: number): void; + start(onNext: () => void): Promise; + stop(value?: any | Promise): void; + next(): Promise; + fail(error: Error): void; +} +export interface DBCoreSchema { + name: string; + tables: DBCoreTableSchema[]; +} +export interface DBCoreTableSchema { + readonly name: string; + readonly primaryKey: DBCoreIndex; + readonly indexes: DBCoreIndex[]; + readonly getIndexByKeyPath: (keyPath: null | string | string[]) => DBCoreIndex | undefined; +} +export interface DBCoreIndex { + /** Name of the index, or null for primary key */ + readonly name: string | null; + /** True if this index represents the primary key */ + readonly isPrimaryKey?: boolean; + /** True if this index represents the primary key and is not inbound (https://dexie.org/docs/inbound) */ + readonly outbound?: boolean; + /** True if and only if keyPath is an array (https://dexie.org/docs/Compound-Index) */ + readonly compound?: boolean; + /** keyPath, null for primary key, string for single-property indexes, Array for compound indexes */ + readonly keyPath: null | string | string[]; + /** Auto-generated primary key (does not apply to secondary indexes) */ + readonly autoIncrement?: boolean; + /** Whether index is unique. Also true if index is primary key. */ + readonly unique?: boolean; + /** Whether index is multiEntry. */ + readonly multiEntry?: boolean; + /** Extract (using keyPath) a key from given value (object). Null for outbound primary keys */ + readonly extractKey: ((value: any) => any) | null; +} +export interface DBCore { + stack: "dbcore"; + // Transaction and Object Store + transaction(stores: string[], mode: "readonly" | "readwrite", options?: DbCoreTransactionOptions): DBCoreTransaction; + // Utility methods + readonly MIN_KEY: any; + readonly MAX_KEY: any; + readonly schema: DBCoreSchema; + table(name: string): DBCoreTable; +} +export interface DBCoreTable { + readonly name: string; + readonly schema: DBCoreTableSchema; + mutate(req: DBCoreMutateRequest): Promise; + get(req: DBCoreGetRequest): Promise; + getMany(req: DBCoreGetManyRequest): Promise; + query(req: DBCoreQueryRequest): Promise; + openCursor(req: DBCoreOpenCursorRequest): Promise; + count(req: DBCoreCountRequest): Promise; +} +export interface Table { + db: Database; + name: string; + schema: TableSchema; + hook: TableHooks; + core: DBCoreTable; + get(key: TKey): PromiseExtended; + get(key: TKey, thenShortcut: ThenShortcut): PromiseExtended; + get(equalityCriterias: { + [key: string]: any; + }): PromiseExtended; + get(equalityCriterias: { + [key: string]: any; + }, thenShortcut: ThenShortcut): PromiseExtended; + where(index: string | string[]): WhereClause; + where(equalityCriterias: { + [key: string]: any; + }): Collection; + filter(fn: (obj: T) => boolean): Collection; + count(): PromiseExtended; + count(thenShortcut: ThenShortcut): PromiseExtended; + offset(n: number): Collection; + limit(n: number): Collection; + each(callback: (obj: T, cursor: { + key: any; + primaryKey: TKey; + }) => any): PromiseExtended; + toArray(): PromiseExtended>; + toArray(thenShortcut: ThenShortcut): PromiseExtended; + toCollection(): Collection; + orderBy(index: string | string[]): Collection; + reverse(): Collection; + mapToClass(constructor: Function): Function; + add(item: T, key?: TKey): PromiseExtended; + update(key: TKey | T, changes: { + [keyPath: string]: any; + }): PromiseExtended; + put(item: T, key?: TKey): PromiseExtended; + delete(key: TKey): PromiseExtended; + clear(): PromiseExtended; + bulkGet(keys: TKey[]): PromiseExtended<(T | undefined)[]>; + bulkAdd(items: readonly T[], keys: IndexableTypeArrayReadonly, options: { + allKeys: B; + }): PromiseExtended; + bulkAdd(items: readonly T[], options: { + allKeys: B; + }): PromiseExtended; + bulkAdd(items: readonly T[], keys?: IndexableTypeArrayReadonly, options?: { + allKeys: boolean; + }): PromiseExtended; + bulkPut(items: readonly T[], keys: IndexableTypeArrayReadonly, options: { + allKeys: B; + }): PromiseExtended; + bulkPut(items: readonly T[], options: { + allKeys: B; + }): PromiseExtended; + bulkPut(items: readonly T[], keys?: IndexableTypeArrayReadonly, options?: { + allKeys: boolean; + }): PromiseExtended; + bulkDelete(keys: TKey[]): PromiseExtended; +} +export interface Version { + stores(schema: { + [tableName: string]: string | null; + }): Version; + upgrade(fn: (trans: Transaction) => PromiseLike | void): Version; +} +export type IntervalTree = IntervalTreeNode | EmptyRange; +export interface IntervalTreeNode { + from: IndexableType; // lower bound + to: IndexableType; // upper bound + l: IntervalTreeNode | null; // left + r: IntervalTreeNode | null; // right + d: number; // depth +} +export interface EmptyRange { + d: 0; +} +export interface DexieOnReadyEvent { + subscribe(fn: (vipDb: Dexie) => any, bSticky: boolean): void; + unsubscribe(fn: (vipDb: Dexie) => any): void; + fire(vipDb: Dexie): any; +} +export interface DexieVersionChangeEvent { + subscribe(fn: (event: IDBVersionChangeEvent) => any): void; + unsubscribe(fn: (event: IDBVersionChangeEvent) => any): void; + fire(event: IDBVersionChangeEvent): any; +} +export interface DexiePopulateEvent { + subscribe(fn: (trans: Transaction) => any): void; + unsubscribe(fn: (trans: Transaction) => any): void; + fire(trans: Transaction): any; +} +export interface DexieCloseEvent { + subscribe(fn: (event: Event) => any): void; + unsubscribe(fn: (event: Event) => any): void; + fire(event: Event): any; +} +export interface DbEvents extends DexieEventSet { + (eventName: "ready", subscriber: (vipDb: Dexie) => any, bSticky?: boolean): void; + (eventName: "populate", subscriber: (trans: Transaction) => any): void; + (eventName: "blocked", subscriber: (event: IDBVersionChangeEvent) => any): void; + (eventName: "versionchange", subscriber: (event: IDBVersionChangeEvent) => any): void; + (eventName: "close", subscriber: (event: Event) => any): void; + ready: DexieOnReadyEvent; + populate: DexiePopulateEvent; + blocked: DexieEvent; + versionchange: DexieVersionChangeEvent; + close: DexieCloseEvent; +} +export type ObservabilitySet = { + // `idb:${dbName}/${tableName}/changedRowContents` - keys. + // `idb:${dbName}/${tableName}/changedIndexes/${indexName}` - indexes + [part: string]: IntervalTree; +}; +export interface DexieOnStorageMutatedEvent { + subscribe(fn: (parts: ObservabilitySet) => any): void; + unsubscribe(fn: (parts: ObservabilitySet) => any): void; + fire(parts: ObservabilitySet): any; +} +export interface GlobalDexieEvents extends DexieEventSet { + (eventName: "storagemutated", subscriber: (parts: ObservabilitySet) => any): void; + storagemutated: DexieOnStorageMutatedEvent; +} +export type DbSchema = { + [tableName: string]: TableSchema; +}; +export interface Middleware { + stack: TStack["stack"]; + create: (down: TStack) => Partial; + level?: number; + name?: string; +} +export interface DexieStacks { + dbcore: DBCore; +} +export interface Dexie extends Database { + readonly name: string; + readonly tables: Table[]; + readonly verno: number; + readonly vip: Dexie; + readonly _allTables: { + [name: string]: Table; + }; + readonly core: DBCore; + _createTransaction: (this: Dexie, mode: IDBTransactionMode, storeNames: ArrayLike, dbschema: DbSchema, parentTransaction?: Transaction | null) => Transaction; + _dbSchema: DbSchema; + version(versionNumber: number): Version; + on: DbEvents; + open(): PromiseExtended; + table(tableName: string): Table; + transaction(mode: TransactionMode, table: Table, scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: string, scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: Table, table2: Table, scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: string, table2: string, scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: Table, table2: Table, table3: Table, scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: string, table2: string, table3: string, scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: Table, table2: Table, table3: Table, table4: Table, scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: string, table2: string, table3: string, table4: string, scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: Table, table2: Table, table3: Table, table4: Table, table5: Table, scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, table: string, table2: string, table3: string, table4: string, table5: string, scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, tables: Table[], scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + transaction(mode: TransactionMode, tables: string[], scope: (trans: Transaction) => PromiseLike | U): PromiseExtended; + close(): void; + delete(): PromiseExtended; + isOpen(): boolean; + hasBeenClosed(): boolean; + hasFailed(): boolean; + dynamicallyOpened(): boolean; + backendDB(): IDBDatabase; + use(middleware: Middleware): this; + // Add more supported stacks here... : use(middleware: Middleware): this; + unuse({ stack, create }: Middleware<{ + stack: keyof DexieStacks; + }>): this; + unuse({ stack, name }: { + stack: keyof DexieStacks; + name: string; + }): this; + // Make it possible to touch physical class constructors where they reside - as properties on db instance. + // For example, checking if (x instanceof db.Table). Can't do (x instanceof Dexie.Table because it's just a virtual interface) + Table: { + prototype: Table; + }; + WhereClause: { + prototype: WhereClause; + }; + Version: { + prototype: Version; + }; + Transaction: { + prototype: Transaction; + }; + Collection: { + prototype: Collection; + }; +} +/** DexieError + * + * Common base class for all errors originating from Dexie.js except TypeError, + * SyntaxError and RangeError. + * + * https://dexie.org/docs/DexieErrors/DexieError + * + */ +export interface DexieError extends Error { + name: string; + message: string; + stack: string; + inner: any; + toString(): string; +} +/** + * List of the names of auto-generated error classes that extends DexieError + * and shares the interface of DexieError. + * + * Each error should be documented at https://dexie.org/docs/DexieErrors/Dexie. + * + * The generic type DexieExceptionClasses is a map of full error name to + * error constructor. The DexieExceptionClasses is mixed in into Dexie, + * so that it is always possible to throw or catch certain errors via + * Dexie.ErrorName. Example: + * + * try { + * throw new Dexie.InvalidTableError("Invalid table foo", innerError?); + * } catch (err) { + * if (err instanceof Dexie.InvalidTableError) { + * // Could also have check for err.name === "InvalidTableError", or + * // err.name === Dexie.errnames.InvalidTableError. + * console.log("Seems to be an invalid table here..."); + * } else { + * throw err; + * } + * } + */ +export type DexieErrors = { + // https://dexie.org/docs/DexieErrors/Dexie.OpenFailedError + OpenFailed: "OpenFailedError"; + // https://dexie.org/docs/DexieErrors/Dexie.VersionChangeError + VersionChange: "VersionChangeError"; + // https://dexie.org/docs/DexieErrors/Dexie.SchemaError + Schema: "SchemaError"; + // https://dexie.org/docs/DexieErrors/Dexie.UpgradeError + Upgrade: "UpgradeError"; + // https://dexie.org/docs/DexieErrors/Dexie.InvalidTableError + InvalidTable: "InvalidTableError"; + // https://dexie.org/docs/DexieErrors/Dexie.MissingAPIError + MissingAPI: "MissingAPIError"; + // https://dexie.org/docs/DexieErrors/Dexie.NoSuchDatabaseError + NoSuchDatabase: "NoSuchDatabaseError"; + // https://dexie.org/docs/DexieErrors/Dexie.InvalidArgumentError + InvalidArgument: "InvalidArgumentError"; + // https://dexie.org/docs/DexieErrors/Dexie.SubTransactionError + SubTransaction: "SubTransactionError"; + // https://dexie.org/docs/DexieErrors/Dexie.UnsupportedError + Unsupported: "UnsupportedError"; + // https://dexie.org/docs/DexieErrors/Dexie.InternalError + Internal: "InternalError"; + // https://dexie.org/docs/DexieErrors/Dexie.DatabaseClosedError + DatabaseClosed: "DatabaseClosedError"; + // https://dexie.org/docs/DexieErrors/Dexie.PrematureCommitError + PrematureCommit: "PrematureCommitError"; + // https://dexie.org/docs/DexieErrors/Dexie.ForeignAwaitError + ForeignAwait: "ForeignAwaitError"; + // https://dexie.org/docs/DexieErrors/Dexie.UnknownError + Unknown: "UnknownError"; + // https://dexie.org/docs/DexieErrors/Dexie.ConstraintError + Constraint: "ConstraintError"; + // https://dexie.org/docs/DexieErrors/Dexie.DataError + Data: "DataError"; + // https://dexie.org/docs/DexieErrors/Dexie.TransactionInactiveError + TransactionInactive: "TransactionInactiveError"; + // https://dexie.org/docs/DexieErrors/Dexie.ReadOnlyError + ReadOnly: "ReadOnlyError"; + // https://dexie.org/docs/DexieErrors/Dexie.VersionError + Version: "VersionError"; + // https://dexie.org/docs/DexieErrors/Dexie.NotFoundError + NotFound: "NotFoundError"; + // https://dexie.org/docs/DexieErrors/Dexie.InvalidStateError + InvalidState: "InvalidStateError"; + // https://dexie.org/docs/DexieErrors/Dexie.InvalidAccessError + InvalidAccess: "InvalidAccessError"; + // https://dexie.org/docs/DexieErrors/Dexie.AbortError + Abort: "AbortError"; + // https://dexie.org/docs/DexieErrors/Dexie.TimeoutError + Timeout: "TimeoutError"; + // https://dexie.org/docs/DexieErrors/Dexie.QuotaExceededError + QuotaExceeded: "QuotaExceededError"; + // https://dexie.org/docs/DexieErrors/Dexie.DataCloneError + DataClone: "DataCloneError"; +}; +/** ModifyError + * + * https://dexie.org/docs/DexieErrors/Dexie.ModifyError + */ +export interface ModifyError extends DexieError { + failures: Array; + failedKeys: IndexableTypeArrayReadonly; + successCount: number; +} +/** BulkError + * + * https://dexie.org/docs/DexieErrors/Dexie.BulkError + */ +export interface BulkError extends DexieError { + failures: Error[]; + failuresByPos: { + [operationNumber: number]: Error; + }; +} +export interface DexieErrorConstructor { + new (msg?: string, inner?: Object): DexieError; + new (inner: Object): DexieError; + prototype: DexieError; +} +export interface ModifyErrorConstructor { + new (msg?: string, failures?: any[], successCount?: number, failedKeys?: IndexableTypeArrayReadonly): ModifyError; + prototype: ModifyError; +} +export interface BulkErrorConstructor { + new (msg?: string, failures?: { + [operationNumber: number]: Error; + }): BulkError; + prototype: BulkError; +} +export type ExceptionSet = { + [P in DexieErrors[keyof DexieErrors]]: DexieErrorConstructor; +}; +export type DexieExceptionClasses = ExceptionSet & { + DexieError: DexieErrorConstructor; + ModifyError: ModifyErrorConstructor; + BulkError: BulkErrorConstructor; +}; +export interface DexieDOMDependencies { + indexedDB: IDBFactory; + IDBKeyRange: typeof IDBKeyRange; +} +declare global { + interface SymbolConstructor { + readonly observable: symbol; + } +} +export interface Subscribable { + subscribe(observer: Partial>): Unsubscribable; +} +export interface Unsubscribable { + unsubscribe(): void; +} +export interface Observable { + subscribe(observerOrNext?: Observer | ((value: T) => void)): Subscription; + subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription; + getValue?(): T; + hasValue?(): boolean; + [Symbol.observable]: () => Subscribable; +} +export interface Subscription { + unsubscribe(): void; + readonly closed: boolean; +} +export interface Observer { + start?: (subscription: Subscription) => void; + next?: (value: T) => void; + error?: (error: any) => void; + complete?: () => void; +} +export type ChromeTransactionDurability = "default" | "strict" | "relaxed"; +export interface DexieOptions { + addons?: Array<(db: Dexie) => void>; + autoOpen?: boolean; + indexedDB?: { + open: Function; + }; + IDBKeyRange?: { + bound: Function; + lowerBound: Function; + upperBound: Function; + }; + allowEmptyDB?: boolean; + modifyChunkSize?: number; + chromeTransactionDurability?: ChromeTransactionDurability; +} +export interface DexieConstructor extends DexieExceptionClasses { + new (databaseName: string, options?: DexieOptions): Dexie; + prototype: Dexie; + addons: Array<(db: Dexie) => void>; + version: number; + semVer: string; + currentTransaction: Transaction; + waitFor(promise: PromiseLike | T, timeoutMilliseconds?: number): Promise; + getDatabaseNames(): Promise; + getDatabaseNames(thenShortcut: ThenShortcut): Promise; + vip(scopeFunction: () => U): U; + ignoreTransaction(fn: () => U): U; + liveQuery(fn: () => T | Promise): Observable; + extendObservabilitySet(target: ObservabilitySet, newSet: ObservabilitySet): ObservabilitySet; + override(origFunc: F, overridedFactory: (fn: any) => any): F; // ? + getByKeyPath(obj: Object, keyPath: string): any; + setByKeyPath(obj: Object, keyPath: string, value: any): void; + delByKeyPath(obj: Object, keyPath: string): void; + shallowClone(obj: T): T; + deepClone(obj: T): T; + asap(fn: Function): void; //? + maxKey: Array> | string; + minKey: number; + exists(dbName: string): Promise; + delete(dbName: string): Promise; + dependencies: DexieDOMDependencies; + default: Dexie; // Work-around for different build tools handling default imports differently. + Promise: PromiseExtendedConstructor; + //TableSchema: {}; // Deprecate! + //IndexSpec: {new():IndexSpec}; //? Deprecate + Events: (ctx?: any) => DexieEventSet; + on: GlobalDexieEvents; + errnames: DexieErrors; +} +export declare var Dexie: DexieConstructor; +export interface _Table extends Table { +} +export interface _Collection extends Collection { +} +export declare module Dexie { + // The "Dexie.Promise" type. + type Promise = PromiseExtended; // Because many samples have been Dexie.Promise. + // The "Dexie.Table" interface. Same as named exported interface Table. + interface Table extends _Table { + } // Because all samples have been Dexie.Table<...> + // The "Dexie.Collection" interface. Same as named exported interface Collection. + interface Collection extends _Collection { + } // Because app-code may declare it. +} +export function liveQuery(querier: () => T | Promise): Observable; +export function mergeRanges(target: IntervalTree, newSet: IntervalTree): void; +export function rangesOverlap(rangeSet1: IntervalTree, rangeSet2: IntervalTree): boolean; +/** Exporting 'Dexie' as the default export. + **/ +export default Dexie; + +export {}; diff --git a/src/lib/dexie/dexie.js b/src/lib/dexie/dexie.js new file mode 100644 index 00000000..08fce7b9 --- /dev/null +++ b/src/lib/dexie/dexie.js @@ -0,0 +1,5200 @@ +/* + * Dexie.js - a minimalistic wrapper for IndexedDB + * =============================================== + * + * By David Fahlander, david.fahlander@gmail.com + * + * Version 3.2.4, Tue May 30 2023 + * + * https://dexie.org + * + * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/ + */ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Dexie = factory()); +})(this, (function () { 'use strict'; + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + function __spreadArray(to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); + } + + var _global = typeof globalThis !== 'undefined' ? globalThis : + typeof self !== 'undefined' ? self : + typeof window !== 'undefined' ? window : + global; + + var keys = Object.keys; + var isArray = Array.isArray; + if (typeof Promise !== 'undefined' && !_global.Promise) { + _global.Promise = Promise; + } + function extend(obj, extension) { + if (typeof extension !== 'object') + return obj; + keys(extension).forEach(function (key) { + obj[key] = extension[key]; + }); + return obj; + } + var getProto = Object.getPrototypeOf; + var _hasOwn = {}.hasOwnProperty; + function hasOwn(obj, prop) { + return _hasOwn.call(obj, prop); + } + function props(proto, extension) { + if (typeof extension === 'function') + extension = extension(getProto(proto)); + (typeof Reflect === "undefined" ? keys : Reflect.ownKeys)(extension).forEach(function (key) { + setProp(proto, key, extension[key]); + }); + } + var defineProperty = Object.defineProperty; + function setProp(obj, prop, functionOrGetSet, options) { + defineProperty(obj, prop, extend(functionOrGetSet && hasOwn(functionOrGetSet, "get") && typeof functionOrGetSet.get === 'function' ? + { get: functionOrGetSet.get, set: functionOrGetSet.set, configurable: true } : + { value: functionOrGetSet, configurable: true, writable: true }, options)); + } + function derive(Child) { + return { + from: function (Parent) { + Child.prototype = Object.create(Parent.prototype); + setProp(Child.prototype, "constructor", Child); + return { + extend: props.bind(null, Child.prototype) + }; + } + }; + } + var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + function getPropertyDescriptor(obj, prop) { + var pd = getOwnPropertyDescriptor(obj, prop); + var proto; + return pd || (proto = getProto(obj)) && getPropertyDescriptor(proto, prop); + } + var _slice = [].slice; + function slice(args, start, end) { + return _slice.call(args, start, end); + } + function override(origFunc, overridedFactory) { + return overridedFactory(origFunc); + } + function assert(b) { + if (!b) + throw new Error("Assertion Failed"); + } + function asap$1(fn) { + if (_global.setImmediate) + setImmediate(fn); + else + setTimeout(fn, 0); + } + function arrayToObject(array, extractor) { + return array.reduce(function (result, item, i) { + var nameAndValue = extractor(item, i); + if (nameAndValue) + result[nameAndValue[0]] = nameAndValue[1]; + return result; + }, {}); + } + function tryCatch(fn, onerror, args) { + try { + fn.apply(null, args); + } + catch (ex) { + onerror && onerror(ex); + } + } + function getByKeyPath(obj, keyPath) { + if (hasOwn(obj, keyPath)) + return obj[keyPath]; + if (!keyPath) + return obj; + if (typeof keyPath !== 'string') { + var rv = []; + for (var i = 0, l = keyPath.length; i < l; ++i) { + var val = getByKeyPath(obj, keyPath[i]); + rv.push(val); + } + return rv; + } + var period = keyPath.indexOf('.'); + if (period !== -1) { + var innerObj = obj[keyPath.substr(0, period)]; + return innerObj === undefined ? undefined : getByKeyPath(innerObj, keyPath.substr(period + 1)); + } + return undefined; + } + function setByKeyPath(obj, keyPath, value) { + if (!obj || keyPath === undefined) + return; + if ('isFrozen' in Object && Object.isFrozen(obj)) + return; + if (typeof keyPath !== 'string' && 'length' in keyPath) { + assert(typeof value !== 'string' && 'length' in value); + for (var i = 0, l = keyPath.length; i < l; ++i) { + setByKeyPath(obj, keyPath[i], value[i]); + } + } + else { + var period = keyPath.indexOf('.'); + if (period !== -1) { + var currentKeyPath = keyPath.substr(0, period); + var remainingKeyPath = keyPath.substr(period + 1); + if (remainingKeyPath === "") + if (value === undefined) { + if (isArray(obj) && !isNaN(parseInt(currentKeyPath))) + obj.splice(currentKeyPath, 1); + else + delete obj[currentKeyPath]; + } + else + obj[currentKeyPath] = value; + else { + var innerObj = obj[currentKeyPath]; + if (!innerObj || !hasOwn(obj, currentKeyPath)) + innerObj = (obj[currentKeyPath] = {}); + setByKeyPath(innerObj, remainingKeyPath, value); + } + } + else { + if (value === undefined) { + if (isArray(obj) && !isNaN(parseInt(keyPath))) + obj.splice(keyPath, 1); + else + delete obj[keyPath]; + } + else + obj[keyPath] = value; + } + } + } + function delByKeyPath(obj, keyPath) { + if (typeof keyPath === 'string') + setByKeyPath(obj, keyPath, undefined); + else if ('length' in keyPath) + [].map.call(keyPath, function (kp) { + setByKeyPath(obj, kp, undefined); + }); + } + function shallowClone(obj) { + var rv = {}; + for (var m in obj) { + if (hasOwn(obj, m)) + rv[m] = obj[m]; + } + return rv; + } + var concat = [].concat; + function flatten(a) { + return concat.apply([], a); + } + var intrinsicTypeNames = "Boolean,String,Date,RegExp,Blob,File,FileList,FileSystemFileHandle,ArrayBuffer,DataView,Uint8ClampedArray,ImageBitmap,ImageData,Map,Set,CryptoKey" + .split(',').concat(flatten([8, 16, 32, 64].map(function (num) { return ["Int", "Uint", "Float"].map(function (t) { return t + num + "Array"; }); }))).filter(function (t) { return _global[t]; }); + var intrinsicTypes = intrinsicTypeNames.map(function (t) { return _global[t]; }); + arrayToObject(intrinsicTypeNames, function (x) { return [x, true]; }); + var circularRefs = null; + function deepClone(any) { + circularRefs = typeof WeakMap !== 'undefined' && new WeakMap(); + var rv = innerDeepClone(any); + circularRefs = null; + return rv; + } + function innerDeepClone(any) { + if (!any || typeof any !== 'object') + return any; + var rv = circularRefs && circularRefs.get(any); + if (rv) + return rv; + if (isArray(any)) { + rv = []; + circularRefs && circularRefs.set(any, rv); + for (var i = 0, l = any.length; i < l; ++i) { + rv.push(innerDeepClone(any[i])); + } + } + else if (intrinsicTypes.indexOf(any.constructor) >= 0) { + rv = any; + } + else { + var proto = getProto(any); + rv = proto === Object.prototype ? {} : Object.create(proto); + circularRefs && circularRefs.set(any, rv); + for (var prop in any) { + if (hasOwn(any, prop)) { + rv[prop] = innerDeepClone(any[prop]); + } + } + } + return rv; + } + var toString = {}.toString; + function toStringTag(o) { + return toString.call(o).slice(8, -1); + } + var iteratorSymbol = typeof Symbol !== 'undefined' ? + Symbol.iterator : + '@@iterator'; + var getIteratorOf = typeof iteratorSymbol === "symbol" ? function (x) { + var i; + return x != null && (i = x[iteratorSymbol]) && i.apply(x); + } : function () { return null; }; + var NO_CHAR_ARRAY = {}; + function getArrayOf(arrayLike) { + var i, a, x, it; + if (arguments.length === 1) { + if (isArray(arrayLike)) + return arrayLike.slice(); + if (this === NO_CHAR_ARRAY && typeof arrayLike === 'string') + return [arrayLike]; + if ((it = getIteratorOf(arrayLike))) { + a = []; + while ((x = it.next()), !x.done) + a.push(x.value); + return a; + } + if (arrayLike == null) + return [arrayLike]; + i = arrayLike.length; + if (typeof i === 'number') { + a = new Array(i); + while (i--) + a[i] = arrayLike[i]; + return a; + } + return [arrayLike]; + } + i = arguments.length; + a = new Array(i); + while (i--) + a[i] = arguments[i]; + return a; + } + var isAsyncFunction = typeof Symbol !== 'undefined' + ? function (fn) { return fn[Symbol.toStringTag] === 'AsyncFunction'; } + : function () { return false; }; + + var debug = typeof location !== 'undefined' && + /^(http|https):\/\/(localhost|127\.0\.0\.1)/.test(location.href); + function setDebug(value, filter) { + debug = value; + libraryFilter = filter; + } + var libraryFilter = function () { return true; }; + var NEEDS_THROW_FOR_STACK = !new Error("").stack; + function getErrorWithStack() { + if (NEEDS_THROW_FOR_STACK) + try { + getErrorWithStack.arguments; + throw new Error(); + } + catch (e) { + return e; + } + return new Error(); + } + function prettyStack(exception, numIgnoredFrames) { + var stack = exception.stack; + if (!stack) + return ""; + numIgnoredFrames = (numIgnoredFrames || 0); + if (stack.indexOf(exception.name) === 0) + numIgnoredFrames += (exception.name + exception.message).split('\n').length; + return stack.split('\n') + .slice(numIgnoredFrames) + .filter(libraryFilter) + .map(function (frame) { return "\n" + frame; }) + .join(''); + } + + var dexieErrorNames = [ + 'Modify', + 'Bulk', + 'OpenFailed', + 'VersionChange', + 'Schema', + 'Upgrade', + 'InvalidTable', + 'MissingAPI', + 'NoSuchDatabase', + 'InvalidArgument', + 'SubTransaction', + 'Unsupported', + 'Internal', + 'DatabaseClosed', + 'PrematureCommit', + 'ForeignAwait' + ]; + var idbDomErrorNames = [ + 'Unknown', + 'Constraint', + 'Data', + 'TransactionInactive', + 'ReadOnly', + 'Version', + 'NotFound', + 'InvalidState', + 'InvalidAccess', + 'Abort', + 'Timeout', + 'QuotaExceeded', + 'Syntax', + 'DataClone' + ]; + var errorList = dexieErrorNames.concat(idbDomErrorNames); + var defaultTexts = { + VersionChanged: "Database version changed by other database connection", + DatabaseClosed: "Database has been closed", + Abort: "Transaction aborted", + TransactionInactive: "Transaction has already completed or failed", + MissingAPI: "IndexedDB API missing. Please visit https://tinyurl.com/y2uuvskb" + }; + function DexieError(name, msg) { + this._e = getErrorWithStack(); + this.name = name; + this.message = msg; + } + derive(DexieError).from(Error).extend({ + stack: { + get: function () { + return this._stack || + (this._stack = this.name + ": " + this.message + prettyStack(this._e, 2)); + } + }, + toString: function () { return this.name + ": " + this.message; } + }); + function getMultiErrorMessage(msg, failures) { + return msg + ". Errors: " + Object.keys(failures) + .map(function (key) { return failures[key].toString(); }) + .filter(function (v, i, s) { return s.indexOf(v) === i; }) + .join('\n'); + } + function ModifyError(msg, failures, successCount, failedKeys) { + this._e = getErrorWithStack(); + this.failures = failures; + this.failedKeys = failedKeys; + this.successCount = successCount; + this.message = getMultiErrorMessage(msg, failures); + } + derive(ModifyError).from(DexieError); + function BulkError(msg, failures) { + this._e = getErrorWithStack(); + this.name = "BulkError"; + this.failures = Object.keys(failures).map(function (pos) { return failures[pos]; }); + this.failuresByPos = failures; + this.message = getMultiErrorMessage(msg, failures); + } + derive(BulkError).from(DexieError); + var errnames = errorList.reduce(function (obj, name) { return (obj[name] = name + "Error", obj); }, {}); + var BaseException = DexieError; + var exceptions = errorList.reduce(function (obj, name) { + var fullName = name + "Error"; + function DexieError(msgOrInner, inner) { + this._e = getErrorWithStack(); + this.name = fullName; + if (!msgOrInner) { + this.message = defaultTexts[name] || fullName; + this.inner = null; + } + else if (typeof msgOrInner === 'string') { + this.message = "" + msgOrInner + (!inner ? '' : '\n ' + inner); + this.inner = inner || null; + } + else if (typeof msgOrInner === 'object') { + this.message = msgOrInner.name + " " + msgOrInner.message; + this.inner = msgOrInner; + } + } + derive(DexieError).from(BaseException); + obj[name] = DexieError; + return obj; + }, {}); + exceptions.Syntax = SyntaxError; + exceptions.Type = TypeError; + exceptions.Range = RangeError; + var exceptionMap = idbDomErrorNames.reduce(function (obj, name) { + obj[name + "Error"] = exceptions[name]; + return obj; + }, {}); + function mapError(domError, message) { + if (!domError || domError instanceof DexieError || domError instanceof TypeError || domError instanceof SyntaxError || !domError.name || !exceptionMap[domError.name]) + return domError; + var rv = new exceptionMap[domError.name](message || domError.message, domError); + if ("stack" in domError) { + setProp(rv, "stack", { get: function () { + return this.inner.stack; + } }); + } + return rv; + } + var fullNameExceptions = errorList.reduce(function (obj, name) { + if (["Syntax", "Type", "Range"].indexOf(name) === -1) + obj[name + "Error"] = exceptions[name]; + return obj; + }, {}); + fullNameExceptions.ModifyError = ModifyError; + fullNameExceptions.DexieError = DexieError; + fullNameExceptions.BulkError = BulkError; + + function nop() { } + function mirror(val) { return val; } + function pureFunctionChain(f1, f2) { + if (f1 == null || f1 === mirror) + return f2; + return function (val) { + return f2(f1(val)); + }; + } + function callBoth(on1, on2) { + return function () { + on1.apply(this, arguments); + on2.apply(this, arguments); + }; + } + function hookCreatingChain(f1, f2) { + if (f1 === nop) + return f2; + return function () { + var res = f1.apply(this, arguments); + if (res !== undefined) + arguments[0] = res; + var onsuccess = this.onsuccess, + onerror = this.onerror; + this.onsuccess = null; + this.onerror = null; + var res2 = f2.apply(this, arguments); + if (onsuccess) + this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; + if (onerror) + this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; + return res2 !== undefined ? res2 : res; + }; + } + function hookDeletingChain(f1, f2) { + if (f1 === nop) + return f2; + return function () { + f1.apply(this, arguments); + var onsuccess = this.onsuccess, + onerror = this.onerror; + this.onsuccess = this.onerror = null; + f2.apply(this, arguments); + if (onsuccess) + this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; + if (onerror) + this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; + }; + } + function hookUpdatingChain(f1, f2) { + if (f1 === nop) + return f2; + return function (modifications) { + var res = f1.apply(this, arguments); + extend(modifications, res); + var onsuccess = this.onsuccess, + onerror = this.onerror; + this.onsuccess = null; + this.onerror = null; + var res2 = f2.apply(this, arguments); + if (onsuccess) + this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess; + if (onerror) + this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror; + return res === undefined ? + (res2 === undefined ? undefined : res2) : + (extend(res, res2)); + }; + } + function reverseStoppableEventChain(f1, f2) { + if (f1 === nop) + return f2; + return function () { + if (f2.apply(this, arguments) === false) + return false; + return f1.apply(this, arguments); + }; + } + function promisableChain(f1, f2) { + if (f1 === nop) + return f2; + return function () { + var res = f1.apply(this, arguments); + if (res && typeof res.then === 'function') { + var thiz = this, i = arguments.length, args = new Array(i); + while (i--) + args[i] = arguments[i]; + return res.then(function () { + return f2.apply(thiz, args); + }); + } + return f2.apply(this, arguments); + }; + } + + var INTERNAL = {}; + var LONG_STACKS_CLIP_LIMIT = 100, + MAX_LONG_STACKS = 20, ZONE_ECHO_LIMIT = 100, _a$1 = typeof Promise === 'undefined' ? + [] : + (function () { + var globalP = Promise.resolve(); + if (typeof crypto === 'undefined' || !crypto.subtle) + return [globalP, getProto(globalP), globalP]; + var nativeP = crypto.subtle.digest("SHA-512", new Uint8Array([0])); + return [ + nativeP, + getProto(nativeP), + globalP + ]; + })(), resolvedNativePromise = _a$1[0], nativePromiseProto = _a$1[1], resolvedGlobalPromise = _a$1[2], nativePromiseThen = nativePromiseProto && nativePromiseProto.then; + var NativePromise = resolvedNativePromise && resolvedNativePromise.constructor; + var patchGlobalPromise = !!resolvedGlobalPromise; + var stack_being_generated = false; + var schedulePhysicalTick = resolvedGlobalPromise ? + function () { resolvedGlobalPromise.then(physicalTick); } + : + _global.setImmediate ? + setImmediate.bind(null, physicalTick) : + _global.MutationObserver ? + function () { + var hiddenDiv = document.createElement("div"); + (new MutationObserver(function () { + physicalTick(); + hiddenDiv = null; + })).observe(hiddenDiv, { attributes: true }); + hiddenDiv.setAttribute('i', '1'); + } : + function () { setTimeout(physicalTick, 0); }; + var asap = function (callback, args) { + microtickQueue.push([callback, args]); + if (needsNewPhysicalTick) { + schedulePhysicalTick(); + needsNewPhysicalTick = false; + } + }; + var isOutsideMicroTick = true, + needsNewPhysicalTick = true, + unhandledErrors = [], + rejectingErrors = [], + currentFulfiller = null, rejectionMapper = mirror; + var globalPSD = { + id: 'global', + global: true, + ref: 0, + unhandleds: [], + onunhandled: globalError, + pgp: false, + env: {}, + finalize: function () { + this.unhandleds.forEach(function (uh) { + try { + globalError(uh[0], uh[1]); + } + catch (e) { } + }); + } + }; + var PSD = globalPSD; + var microtickQueue = []; + var numScheduledCalls = 0; + var tickFinalizers = []; + function DexiePromise(fn) { + if (typeof this !== 'object') + throw new TypeError('Promises must be constructed via new'); + this._listeners = []; + this.onuncatched = nop; + this._lib = false; + var psd = (this._PSD = PSD); + if (debug) { + this._stackHolder = getErrorWithStack(); + this._prev = null; + this._numPrev = 0; + } + if (typeof fn !== 'function') { + if (fn !== INTERNAL) + throw new TypeError('Not a function'); + this._state = arguments[1]; + this._value = arguments[2]; + if (this._state === false) + handleRejection(this, this._value); + return; + } + this._state = null; + this._value = null; + ++psd.ref; + executePromiseTask(this, fn); + } + var thenProp = { + get: function () { + var psd = PSD, microTaskId = totalEchoes; + function then(onFulfilled, onRejected) { + var _this = this; + var possibleAwait = !psd.global && (psd !== PSD || microTaskId !== totalEchoes); + var cleanup = possibleAwait && !decrementExpectedAwaits(); + var rv = new DexiePromise(function (resolve, reject) { + propagateToListener(_this, new Listener(nativeAwaitCompatibleWrap(onFulfilled, psd, possibleAwait, cleanup), nativeAwaitCompatibleWrap(onRejected, psd, possibleAwait, cleanup), resolve, reject, psd)); + }); + debug && linkToPreviousPromise(rv, this); + return rv; + } + then.prototype = INTERNAL; + return then; + }, + set: function (value) { + setProp(this, 'then', value && value.prototype === INTERNAL ? + thenProp : + { + get: function () { + return value; + }, + set: thenProp.set + }); + } + }; + props(DexiePromise.prototype, { + then: thenProp, + _then: function (onFulfilled, onRejected) { + propagateToListener(this, new Listener(null, null, onFulfilled, onRejected, PSD)); + }, + catch: function (onRejected) { + if (arguments.length === 1) + return this.then(null, onRejected); + var type = arguments[0], handler = arguments[1]; + return typeof type === 'function' ? this.then(null, function (err) { + return err instanceof type ? handler(err) : PromiseReject(err); + }) + : this.then(null, function (err) { + return err && err.name === type ? handler(err) : PromiseReject(err); + }); + }, + finally: function (onFinally) { + return this.then(function (value) { + onFinally(); + return value; + }, function (err) { + onFinally(); + return PromiseReject(err); + }); + }, + stack: { + get: function () { + if (this._stack) + return this._stack; + try { + stack_being_generated = true; + var stacks = getStack(this, [], MAX_LONG_STACKS); + var stack = stacks.join("\nFrom previous: "); + if (this._state !== null) + this._stack = stack; + return stack; + } + finally { + stack_being_generated = false; + } + } + }, + timeout: function (ms, msg) { + var _this = this; + return ms < Infinity ? + new DexiePromise(function (resolve, reject) { + var handle = setTimeout(function () { return reject(new exceptions.Timeout(msg)); }, ms); + _this.then(resolve, reject).finally(clearTimeout.bind(null, handle)); + }) : this; + } + }); + if (typeof Symbol !== 'undefined' && Symbol.toStringTag) + setProp(DexiePromise.prototype, Symbol.toStringTag, 'Dexie.Promise'); + globalPSD.env = snapShot(); + function Listener(onFulfilled, onRejected, resolve, reject, zone) { + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; + this.onRejected = typeof onRejected === 'function' ? onRejected : null; + this.resolve = resolve; + this.reject = reject; + this.psd = zone; + } + props(DexiePromise, { + all: function () { + var values = getArrayOf.apply(null, arguments) + .map(onPossibleParallellAsync); + return new DexiePromise(function (resolve, reject) { + if (values.length === 0) + resolve([]); + var remaining = values.length; + values.forEach(function (a, i) { return DexiePromise.resolve(a).then(function (x) { + values[i] = x; + if (!--remaining) + resolve(values); + }, reject); }); + }); + }, + resolve: function (value) { + if (value instanceof DexiePromise) + return value; + if (value && typeof value.then === 'function') + return new DexiePromise(function (resolve, reject) { + value.then(resolve, reject); + }); + var rv = new DexiePromise(INTERNAL, true, value); + linkToPreviousPromise(rv, currentFulfiller); + return rv; + }, + reject: PromiseReject, + race: function () { + var values = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); + return new DexiePromise(function (resolve, reject) { + values.map(function (value) { return DexiePromise.resolve(value).then(resolve, reject); }); + }); + }, + PSD: { + get: function () { return PSD; }, + set: function (value) { return PSD = value; } + }, + totalEchoes: { get: function () { return totalEchoes; } }, + newPSD: newScope, + usePSD: usePSD, + scheduler: { + get: function () { return asap; }, + set: function (value) { asap = value; } + }, + rejectionMapper: { + get: function () { return rejectionMapper; }, + set: function (value) { rejectionMapper = value; } + }, + follow: function (fn, zoneProps) { + return new DexiePromise(function (resolve, reject) { + return newScope(function (resolve, reject) { + var psd = PSD; + psd.unhandleds = []; + psd.onunhandled = reject; + psd.finalize = callBoth(function () { + var _this = this; + run_at_end_of_this_or_next_physical_tick(function () { + _this.unhandleds.length === 0 ? resolve() : reject(_this.unhandleds[0]); + }); + }, psd.finalize); + fn(); + }, zoneProps, resolve, reject); + }); + } + }); + if (NativePromise) { + if (NativePromise.allSettled) + setProp(DexiePromise, "allSettled", function () { + var possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); + return new DexiePromise(function (resolve) { + if (possiblePromises.length === 0) + resolve([]); + var remaining = possiblePromises.length; + var results = new Array(remaining); + possiblePromises.forEach(function (p, i) { return DexiePromise.resolve(p).then(function (value) { return results[i] = { status: "fulfilled", value: value }; }, function (reason) { return results[i] = { status: "rejected", reason: reason }; }) + .then(function () { return --remaining || resolve(results); }); }); + }); + }); + if (NativePromise.any && typeof AggregateError !== 'undefined') + setProp(DexiePromise, "any", function () { + var possiblePromises = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync); + return new DexiePromise(function (resolve, reject) { + if (possiblePromises.length === 0) + reject(new AggregateError([])); + var remaining = possiblePromises.length; + var failures = new Array(remaining); + possiblePromises.forEach(function (p, i) { return DexiePromise.resolve(p).then(function (value) { return resolve(value); }, function (failure) { + failures[i] = failure; + if (!--remaining) + reject(new AggregateError(failures)); + }); }); + }); + }); + } + function executePromiseTask(promise, fn) { + try { + fn(function (value) { + if (promise._state !== null) + return; + if (value === promise) + throw new TypeError('A promise cannot be resolved with itself.'); + var shouldExecuteTick = promise._lib && beginMicroTickScope(); + if (value && typeof value.then === 'function') { + executePromiseTask(promise, function (resolve, reject) { + value instanceof DexiePromise ? + value._then(resolve, reject) : + value.then(resolve, reject); + }); + } + else { + promise._state = true; + promise._value = value; + propagateAllListeners(promise); + } + if (shouldExecuteTick) + endMicroTickScope(); + }, handleRejection.bind(null, promise)); + } + catch (ex) { + handleRejection(promise, ex); + } + } + function handleRejection(promise, reason) { + rejectingErrors.push(reason); + if (promise._state !== null) + return; + var shouldExecuteTick = promise._lib && beginMicroTickScope(); + reason = rejectionMapper(reason); + promise._state = false; + promise._value = reason; + debug && reason !== null && typeof reason === 'object' && !reason._promise && tryCatch(function () { + var origProp = getPropertyDescriptor(reason, "stack"); + reason._promise = promise; + setProp(reason, "stack", { + get: function () { + return stack_being_generated ? + origProp && (origProp.get ? + origProp.get.apply(reason) : + origProp.value) : + promise.stack; + } + }); + }); + addPossiblyUnhandledError(promise); + propagateAllListeners(promise); + if (shouldExecuteTick) + endMicroTickScope(); + } + function propagateAllListeners(promise) { + var listeners = promise._listeners; + promise._listeners = []; + for (var i = 0, len = listeners.length; i < len; ++i) { + propagateToListener(promise, listeners[i]); + } + var psd = promise._PSD; + --psd.ref || psd.finalize(); + if (numScheduledCalls === 0) { + ++numScheduledCalls; + asap(function () { + if (--numScheduledCalls === 0) + finalizePhysicalTick(); + }, []); + } + } + function propagateToListener(promise, listener) { + if (promise._state === null) { + promise._listeners.push(listener); + return; + } + var cb = promise._state ? listener.onFulfilled : listener.onRejected; + if (cb === null) { + return (promise._state ? listener.resolve : listener.reject)(promise._value); + } + ++listener.psd.ref; + ++numScheduledCalls; + asap(callListener, [cb, promise, listener]); + } + function callListener(cb, promise, listener) { + try { + currentFulfiller = promise; + var ret, value = promise._value; + if (promise._state) { + ret = cb(value); + } + else { + if (rejectingErrors.length) + rejectingErrors = []; + ret = cb(value); + if (rejectingErrors.indexOf(value) === -1) + markErrorAsHandled(promise); + } + listener.resolve(ret); + } + catch (e) { + listener.reject(e); + } + finally { + currentFulfiller = null; + if (--numScheduledCalls === 0) + finalizePhysicalTick(); + --listener.psd.ref || listener.psd.finalize(); + } + } + function getStack(promise, stacks, limit) { + if (stacks.length === limit) + return stacks; + var stack = ""; + if (promise._state === false) { + var failure = promise._value, errorName, message; + if (failure != null) { + errorName = failure.name || "Error"; + message = failure.message || failure; + stack = prettyStack(failure, 0); + } + else { + errorName = failure; + message = ""; + } + stacks.push(errorName + (message ? ": " + message : "") + stack); + } + if (debug) { + stack = prettyStack(promise._stackHolder, 2); + if (stack && stacks.indexOf(stack) === -1) + stacks.push(stack); + if (promise._prev) + getStack(promise._prev, stacks, limit); + } + return stacks; + } + function linkToPreviousPromise(promise, prev) { + var numPrev = prev ? prev._numPrev + 1 : 0; + if (numPrev < LONG_STACKS_CLIP_LIMIT) { + promise._prev = prev; + promise._numPrev = numPrev; + } + } + function physicalTick() { + beginMicroTickScope() && endMicroTickScope(); + } + function beginMicroTickScope() { + var wasRootExec = isOutsideMicroTick; + isOutsideMicroTick = false; + needsNewPhysicalTick = false; + return wasRootExec; + } + function endMicroTickScope() { + var callbacks, i, l; + do { + while (microtickQueue.length > 0) { + callbacks = microtickQueue; + microtickQueue = []; + l = callbacks.length; + for (i = 0; i < l; ++i) { + var item = callbacks[i]; + item[0].apply(null, item[1]); + } + } + } while (microtickQueue.length > 0); + isOutsideMicroTick = true; + needsNewPhysicalTick = true; + } + function finalizePhysicalTick() { + var unhandledErrs = unhandledErrors; + unhandledErrors = []; + unhandledErrs.forEach(function (p) { + p._PSD.onunhandled.call(null, p._value, p); + }); + var finalizers = tickFinalizers.slice(0); + var i = finalizers.length; + while (i) + finalizers[--i](); + } + function run_at_end_of_this_or_next_physical_tick(fn) { + function finalizer() { + fn(); + tickFinalizers.splice(tickFinalizers.indexOf(finalizer), 1); + } + tickFinalizers.push(finalizer); + ++numScheduledCalls; + asap(function () { + if (--numScheduledCalls === 0) + finalizePhysicalTick(); + }, []); + } + function addPossiblyUnhandledError(promise) { + if (!unhandledErrors.some(function (p) { return p._value === promise._value; })) + unhandledErrors.push(promise); + } + function markErrorAsHandled(promise) { + var i = unhandledErrors.length; + while (i) + if (unhandledErrors[--i]._value === promise._value) { + unhandledErrors.splice(i, 1); + return; + } + } + function PromiseReject(reason) { + return new DexiePromise(INTERNAL, false, reason); + } + function wrap(fn, errorCatcher) { + var psd = PSD; + return function () { + var wasRootExec = beginMicroTickScope(), outerScope = PSD; + try { + switchToZone(psd, true); + return fn.apply(this, arguments); + } + catch (e) { + errorCatcher && errorCatcher(e); + } + finally { + switchToZone(outerScope, false); + if (wasRootExec) + endMicroTickScope(); + } + }; + } + var task = { awaits: 0, echoes: 0, id: 0 }; + var taskCounter = 0; + var zoneStack = []; + var zoneEchoes = 0; + var totalEchoes = 0; + var zone_id_counter = 0; + function newScope(fn, props, a1, a2) { + var parent = PSD, psd = Object.create(parent); + psd.parent = parent; + psd.ref = 0; + psd.global = false; + psd.id = ++zone_id_counter; + var globalEnv = globalPSD.env; + psd.env = patchGlobalPromise ? { + Promise: DexiePromise, + PromiseProp: { value: DexiePromise, configurable: true, writable: true }, + all: DexiePromise.all, + race: DexiePromise.race, + allSettled: DexiePromise.allSettled, + any: DexiePromise.any, + resolve: DexiePromise.resolve, + reject: DexiePromise.reject, + nthen: getPatchedPromiseThen(globalEnv.nthen, psd), + gthen: getPatchedPromiseThen(globalEnv.gthen, psd) + } : {}; + if (props) + extend(psd, props); + ++parent.ref; + psd.finalize = function () { + --this.parent.ref || this.parent.finalize(); + }; + var rv = usePSD(psd, fn, a1, a2); + if (psd.ref === 0) + psd.finalize(); + return rv; + } + function incrementExpectedAwaits() { + if (!task.id) + task.id = ++taskCounter; + ++task.awaits; + task.echoes += ZONE_ECHO_LIMIT; + return task.id; + } + function decrementExpectedAwaits() { + if (!task.awaits) + return false; + if (--task.awaits === 0) + task.id = 0; + task.echoes = task.awaits * ZONE_ECHO_LIMIT; + return true; + } + if (('' + nativePromiseThen).indexOf('[native code]') === -1) { + incrementExpectedAwaits = decrementExpectedAwaits = nop; + } + function onPossibleParallellAsync(possiblePromise) { + if (task.echoes && possiblePromise && possiblePromise.constructor === NativePromise) { + incrementExpectedAwaits(); + return possiblePromise.then(function (x) { + decrementExpectedAwaits(); + return x; + }, function (e) { + decrementExpectedAwaits(); + return rejection(e); + }); + } + return possiblePromise; + } + function zoneEnterEcho(targetZone) { + ++totalEchoes; + if (!task.echoes || --task.echoes === 0) { + task.echoes = task.id = 0; + } + zoneStack.push(PSD); + switchToZone(targetZone, true); + } + function zoneLeaveEcho() { + var zone = zoneStack[zoneStack.length - 1]; + zoneStack.pop(); + switchToZone(zone, false); + } + function switchToZone(targetZone, bEnteringZone) { + var currentZone = PSD; + if (bEnteringZone ? task.echoes && (!zoneEchoes++ || targetZone !== PSD) : zoneEchoes && (!--zoneEchoes || targetZone !== PSD)) { + enqueueNativeMicroTask(bEnteringZone ? zoneEnterEcho.bind(null, targetZone) : zoneLeaveEcho); + } + if (targetZone === PSD) + return; + PSD = targetZone; + if (currentZone === globalPSD) + globalPSD.env = snapShot(); + if (patchGlobalPromise) { + var GlobalPromise_1 = globalPSD.env.Promise; + var targetEnv = targetZone.env; + nativePromiseProto.then = targetEnv.nthen; + GlobalPromise_1.prototype.then = targetEnv.gthen; + if (currentZone.global || targetZone.global) { + Object.defineProperty(_global, 'Promise', targetEnv.PromiseProp); + GlobalPromise_1.all = targetEnv.all; + GlobalPromise_1.race = targetEnv.race; + GlobalPromise_1.resolve = targetEnv.resolve; + GlobalPromise_1.reject = targetEnv.reject; + if (targetEnv.allSettled) + GlobalPromise_1.allSettled = targetEnv.allSettled; + if (targetEnv.any) + GlobalPromise_1.any = targetEnv.any; + } + } + } + function snapShot() { + var GlobalPromise = _global.Promise; + return patchGlobalPromise ? { + Promise: GlobalPromise, + PromiseProp: Object.getOwnPropertyDescriptor(_global, "Promise"), + all: GlobalPromise.all, + race: GlobalPromise.race, + allSettled: GlobalPromise.allSettled, + any: GlobalPromise.any, + resolve: GlobalPromise.resolve, + reject: GlobalPromise.reject, + nthen: nativePromiseProto.then, + gthen: GlobalPromise.prototype.then + } : {}; + } + function usePSD(psd, fn, a1, a2, a3) { + var outerScope = PSD; + try { + switchToZone(psd, true); + return fn(a1, a2, a3); + } + finally { + switchToZone(outerScope, false); + } + } + function enqueueNativeMicroTask(job) { + nativePromiseThen.call(resolvedNativePromise, job); + } + function nativeAwaitCompatibleWrap(fn, zone, possibleAwait, cleanup) { + return typeof fn !== 'function' ? fn : function () { + var outerZone = PSD; + if (possibleAwait) + incrementExpectedAwaits(); + switchToZone(zone, true); + try { + return fn.apply(this, arguments); + } + finally { + switchToZone(outerZone, false); + if (cleanup) + enqueueNativeMicroTask(decrementExpectedAwaits); + } + }; + } + function getPatchedPromiseThen(origThen, zone) { + return function (onResolved, onRejected) { + return origThen.call(this, nativeAwaitCompatibleWrap(onResolved, zone), nativeAwaitCompatibleWrap(onRejected, zone)); + }; + } + var UNHANDLEDREJECTION = "unhandledrejection"; + function globalError(err, promise) { + var rv; + try { + rv = promise.onuncatched(err); + } + catch (e) { } + if (rv !== false) + try { + var event, eventData = { promise: promise, reason: err }; + if (_global.document && document.createEvent) { + event = document.createEvent('Event'); + event.initEvent(UNHANDLEDREJECTION, true, true); + extend(event, eventData); + } + else if (_global.CustomEvent) { + event = new CustomEvent(UNHANDLEDREJECTION, { detail: eventData }); + extend(event, eventData); + } + if (event && _global.dispatchEvent) { + dispatchEvent(event); + if (!_global.PromiseRejectionEvent && _global.onunhandledrejection) + try { + _global.onunhandledrejection(event); + } + catch (_) { } + } + if (debug && event && !event.defaultPrevented) { + console.warn("Unhandled rejection: " + (err.stack || err)); + } + } + catch (e) { } + } + var rejection = DexiePromise.reject; + + function tempTransaction(db, mode, storeNames, fn) { + if (!db.idbdb || (!db._state.openComplete && (!PSD.letThrough && !db._vip))) { + if (db._state.openComplete) { + return rejection(new exceptions.DatabaseClosed(db._state.dbOpenError)); + } + if (!db._state.isBeingOpened) { + if (!db._options.autoOpen) + return rejection(new exceptions.DatabaseClosed()); + db.open().catch(nop); + } + return db._state.dbReadyPromise.then(function () { return tempTransaction(db, mode, storeNames, fn); }); + } + else { + var trans = db._createTransaction(mode, storeNames, db._dbSchema); + try { + trans.create(); + db._state.PR1398_maxLoop = 3; + } + catch (ex) { + if (ex.name === errnames.InvalidState && db.isOpen() && --db._state.PR1398_maxLoop > 0) { + console.warn('Dexie: Need to reopen db'); + db._close(); + return db.open().then(function () { return tempTransaction(db, mode, storeNames, fn); }); + } + return rejection(ex); + } + return trans._promise(mode, function (resolve, reject) { + return newScope(function () { + PSD.trans = trans; + return fn(resolve, reject, trans); + }); + }).then(function (result) { + return trans._completion.then(function () { return result; }); + }); + } + } + + var DEXIE_VERSION = '3.2.4'; + var maxString = String.fromCharCode(65535); + var minKey = -Infinity; + var INVALID_KEY_ARGUMENT = "Invalid key provided. Keys must be of type string, number, Date or Array."; + var STRING_EXPECTED = "String expected."; + var connections = []; + var isIEOrEdge = typeof navigator !== 'undefined' && /(MSIE|Trident|Edge)/.test(navigator.userAgent); + var hasIEDeleteObjectStoreBug = isIEOrEdge; + var hangsOnDeleteLargeKeyRange = isIEOrEdge; + var dexieStackFrameFilter = function (frame) { return !/(dexie\.js|dexie\.min\.js)/.test(frame); }; + var DBNAMES_DB = '__dbnames'; + var READONLY = 'readonly'; + var READWRITE = 'readwrite'; + + function combine(filter1, filter2) { + return filter1 ? + filter2 ? + function () { return filter1.apply(this, arguments) && filter2.apply(this, arguments); } : + filter1 : + filter2; + } + + var AnyRange = { + type: 3 , + lower: -Infinity, + lowerOpen: false, + upper: [[]], + upperOpen: false + }; + + function workaroundForUndefinedPrimKey(keyPath) { + return typeof keyPath === "string" && !/\./.test(keyPath) + ? function (obj) { + if (obj[keyPath] === undefined && (keyPath in obj)) { + obj = deepClone(obj); + delete obj[keyPath]; + } + return obj; + } + : function (obj) { return obj; }; + } + + var Table = (function () { + function Table() { + } + Table.prototype._trans = function (mode, fn, writeLocked) { + var trans = this._tx || PSD.trans; + var tableName = this.name; + function checkTableInTransaction(resolve, reject, trans) { + if (!trans.schema[tableName]) + throw new exceptions.NotFound("Table " + tableName + " not part of transaction"); + return fn(trans.idbtrans, trans); + } + var wasRootExec = beginMicroTickScope(); + try { + return trans && trans.db === this.db ? + trans === PSD.trans ? + trans._promise(mode, checkTableInTransaction, writeLocked) : + newScope(function () { return trans._promise(mode, checkTableInTransaction, writeLocked); }, { trans: trans, transless: PSD.transless || PSD }) : + tempTransaction(this.db, mode, [this.name], checkTableInTransaction); + } + finally { + if (wasRootExec) + endMicroTickScope(); + } + }; + Table.prototype.get = function (keyOrCrit, cb) { + var _this = this; + if (keyOrCrit && keyOrCrit.constructor === Object) + return this.where(keyOrCrit).first(cb); + return this._trans('readonly', function (trans) { + return _this.core.get({ trans: trans, key: keyOrCrit }) + .then(function (res) { return _this.hook.reading.fire(res); }); + }).then(cb); + }; + Table.prototype.where = function (indexOrCrit) { + if (typeof indexOrCrit === 'string') + return new this.db.WhereClause(this, indexOrCrit); + if (isArray(indexOrCrit)) + return new this.db.WhereClause(this, "[" + indexOrCrit.join('+') + "]"); + var keyPaths = keys(indexOrCrit); + if (keyPaths.length === 1) + return this + .where(keyPaths[0]) + .equals(indexOrCrit[keyPaths[0]]); + var compoundIndex = this.schema.indexes.concat(this.schema.primKey).filter(function (ix) { + return ix.compound && + keyPaths.every(function (keyPath) { return ix.keyPath.indexOf(keyPath) >= 0; }) && + ix.keyPath.every(function (keyPath) { return keyPaths.indexOf(keyPath) >= 0; }); + })[0]; + if (compoundIndex && this.db._maxKey !== maxString) + return this + .where(compoundIndex.name) + .equals(compoundIndex.keyPath.map(function (kp) { return indexOrCrit[kp]; })); + if (!compoundIndex && debug) + console.warn("The query " + JSON.stringify(indexOrCrit) + " on " + this.name + " would benefit of a " + + ("compound index [" + keyPaths.join('+') + "]")); + var idxByName = this.schema.idxByName; + var idb = this.db._deps.indexedDB; + function equals(a, b) { + try { + return idb.cmp(a, b) === 0; + } + catch (e) { + return false; + } + } + var _a = keyPaths.reduce(function (_a, keyPath) { + var prevIndex = _a[0], prevFilterFn = _a[1]; + var index = idxByName[keyPath]; + var value = indexOrCrit[keyPath]; + return [ + prevIndex || index, + prevIndex || !index ? + combine(prevFilterFn, index && index.multi ? + function (x) { + var prop = getByKeyPath(x, keyPath); + return isArray(prop) && prop.some(function (item) { return equals(value, item); }); + } : function (x) { return equals(value, getByKeyPath(x, keyPath)); }) + : prevFilterFn + ]; + }, [null, null]), idx = _a[0], filterFunction = _a[1]; + return idx ? + this.where(idx.name).equals(indexOrCrit[idx.keyPath]) + .filter(filterFunction) : + compoundIndex ? + this.filter(filterFunction) : + this.where(keyPaths).equals(''); + }; + Table.prototype.filter = function (filterFunction) { + return this.toCollection().and(filterFunction); + }; + Table.prototype.count = function (thenShortcut) { + return this.toCollection().count(thenShortcut); + }; + Table.prototype.offset = function (offset) { + return this.toCollection().offset(offset); + }; + Table.prototype.limit = function (numRows) { + return this.toCollection().limit(numRows); + }; + Table.prototype.each = function (callback) { + return this.toCollection().each(callback); + }; + Table.prototype.toArray = function (thenShortcut) { + return this.toCollection().toArray(thenShortcut); + }; + Table.prototype.toCollection = function () { + return new this.db.Collection(new this.db.WhereClause(this)); + }; + Table.prototype.orderBy = function (index) { + return new this.db.Collection(new this.db.WhereClause(this, isArray(index) ? + "[" + index.join('+') + "]" : + index)); + }; + Table.prototype.reverse = function () { + return this.toCollection().reverse(); + }; + Table.prototype.mapToClass = function (constructor) { + this.schema.mappedClass = constructor; + var readHook = function (obj) { + if (!obj) + return obj; + var res = Object.create(constructor.prototype); + for (var m in obj) + if (hasOwn(obj, m)) + try { + res[m] = obj[m]; + } + catch (_) { } + return res; + }; + if (this.schema.readHook) { + this.hook.reading.unsubscribe(this.schema.readHook); + } + this.schema.readHook = readHook; + this.hook("reading", readHook); + return constructor; + }; + Table.prototype.defineClass = function () { + function Class(content) { + extend(this, content); + } + return this.mapToClass(Class); + }; + Table.prototype.add = function (obj, key) { + var _this = this; + var _a = this.schema.primKey, auto = _a.auto, keyPath = _a.keyPath; + var objToAdd = obj; + if (keyPath && auto) { + objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj); + } + return this._trans('readwrite', function (trans) { + return _this.core.mutate({ trans: trans, type: 'add', keys: key != null ? [key] : null, values: [objToAdd] }); + }).then(function (res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult; }) + .then(function (lastResult) { + if (keyPath) { + try { + setByKeyPath(obj, keyPath, lastResult); + } + catch (_) { } + } + return lastResult; + }); + }; + Table.prototype.update = function (keyOrObject, modifications) { + if (typeof keyOrObject === 'object' && !isArray(keyOrObject)) { + var key = getByKeyPath(keyOrObject, this.schema.primKey.keyPath); + if (key === undefined) + return rejection(new exceptions.InvalidArgument("Given object does not contain its primary key")); + try { + if (typeof modifications !== "function") { + keys(modifications).forEach(function (keyPath) { + setByKeyPath(keyOrObject, keyPath, modifications[keyPath]); + }); + } + else { + modifications(keyOrObject, { value: keyOrObject, primKey: key }); + } + } + catch (_a) { + } + return this.where(":id").equals(key).modify(modifications); + } + else { + return this.where(":id").equals(keyOrObject).modify(modifications); + } + }; + Table.prototype.put = function (obj, key) { + var _this = this; + var _a = this.schema.primKey, auto = _a.auto, keyPath = _a.keyPath; + var objToAdd = obj; + if (keyPath && auto) { + objToAdd = workaroundForUndefinedPrimKey(keyPath)(obj); + } + return this._trans('readwrite', function (trans) { return _this.core.mutate({ trans: trans, type: 'put', values: [objToAdd], keys: key != null ? [key] : null }); }) + .then(function (res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : res.lastResult; }) + .then(function (lastResult) { + if (keyPath) { + try { + setByKeyPath(obj, keyPath, lastResult); + } + catch (_) { } + } + return lastResult; + }); + }; + Table.prototype.delete = function (key) { + var _this = this; + return this._trans('readwrite', function (trans) { return _this.core.mutate({ trans: trans, type: 'delete', keys: [key] }); }) + .then(function (res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined; }); + }; + Table.prototype.clear = function () { + var _this = this; + return this._trans('readwrite', function (trans) { return _this.core.mutate({ trans: trans, type: 'deleteRange', range: AnyRange }); }) + .then(function (res) { return res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined; }); + }; + Table.prototype.bulkGet = function (keys) { + var _this = this; + return this._trans('readonly', function (trans) { + return _this.core.getMany({ + keys: keys, + trans: trans + }).then(function (result) { return result.map(function (res) { return _this.hook.reading.fire(res); }); }); + }); + }; + Table.prototype.bulkAdd = function (objects, keysOrOptions, options) { + var _this = this; + var keys = Array.isArray(keysOrOptions) ? keysOrOptions : undefined; + options = options || (keys ? undefined : keysOrOptions); + var wantResults = options ? options.allKeys : undefined; + return this._trans('readwrite', function (trans) { + var _a = _this.schema.primKey, auto = _a.auto, keyPath = _a.keyPath; + if (keyPath && keys) + throw new exceptions.InvalidArgument("bulkAdd(): keys argument invalid on tables with inbound keys"); + if (keys && keys.length !== objects.length) + throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length"); + var numObjects = objects.length; + var objectsToAdd = keyPath && auto ? + objects.map(workaroundForUndefinedPrimKey(keyPath)) : + objects; + return _this.core.mutate({ trans: trans, type: 'add', keys: keys, values: objectsToAdd, wantResults: wantResults }) + .then(function (_a) { + var numFailures = _a.numFailures, results = _a.results, lastResult = _a.lastResult, failures = _a.failures; + var result = wantResults ? results : lastResult; + if (numFailures === 0) + return result; + throw new BulkError(_this.name + ".bulkAdd(): " + numFailures + " of " + numObjects + " operations failed", failures); + }); + }); + }; + Table.prototype.bulkPut = function (objects, keysOrOptions, options) { + var _this = this; + var keys = Array.isArray(keysOrOptions) ? keysOrOptions : undefined; + options = options || (keys ? undefined : keysOrOptions); + var wantResults = options ? options.allKeys : undefined; + return this._trans('readwrite', function (trans) { + var _a = _this.schema.primKey, auto = _a.auto, keyPath = _a.keyPath; + if (keyPath && keys) + throw new exceptions.InvalidArgument("bulkPut(): keys argument invalid on tables with inbound keys"); + if (keys && keys.length !== objects.length) + throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length"); + var numObjects = objects.length; + var objectsToPut = keyPath && auto ? + objects.map(workaroundForUndefinedPrimKey(keyPath)) : + objects; + return _this.core.mutate({ trans: trans, type: 'put', keys: keys, values: objectsToPut, wantResults: wantResults }) + .then(function (_a) { + var numFailures = _a.numFailures, results = _a.results, lastResult = _a.lastResult, failures = _a.failures; + var result = wantResults ? results : lastResult; + if (numFailures === 0) + return result; + throw new BulkError(_this.name + ".bulkPut(): " + numFailures + " of " + numObjects + " operations failed", failures); + }); + }); + }; + Table.prototype.bulkDelete = function (keys) { + var _this = this; + var numKeys = keys.length; + return this._trans('readwrite', function (trans) { + return _this.core.mutate({ trans: trans, type: 'delete', keys: keys }); + }).then(function (_a) { + var numFailures = _a.numFailures, lastResult = _a.lastResult, failures = _a.failures; + if (numFailures === 0) + return lastResult; + throw new BulkError(_this.name + ".bulkDelete(): " + numFailures + " of " + numKeys + " operations failed", failures); + }); + }; + return Table; + }()); + + function Events(ctx) { + var evs = {}; + var rv = function (eventName, subscriber) { + if (subscriber) { + var i = arguments.length, args = new Array(i - 1); + while (--i) + args[i - 1] = arguments[i]; + evs[eventName].subscribe.apply(null, args); + return ctx; + } + else if (typeof (eventName) === 'string') { + return evs[eventName]; + } + }; + rv.addEventType = add; + for (var i = 1, l = arguments.length; i < l; ++i) { + add(arguments[i]); + } + return rv; + function add(eventName, chainFunction, defaultFunction) { + if (typeof eventName === 'object') + return addConfiguredEvents(eventName); + if (!chainFunction) + chainFunction = reverseStoppableEventChain; + if (!defaultFunction) + defaultFunction = nop; + var context = { + subscribers: [], + fire: defaultFunction, + subscribe: function (cb) { + if (context.subscribers.indexOf(cb) === -1) { + context.subscribers.push(cb); + context.fire = chainFunction(context.fire, cb); + } + }, + unsubscribe: function (cb) { + context.subscribers = context.subscribers.filter(function (fn) { return fn !== cb; }); + context.fire = context.subscribers.reduce(chainFunction, defaultFunction); + } + }; + evs[eventName] = rv[eventName] = context; + return context; + } + function addConfiguredEvents(cfg) { + keys(cfg).forEach(function (eventName) { + var args = cfg[eventName]; + if (isArray(args)) { + add(eventName, cfg[eventName][0], cfg[eventName][1]); + } + else if (args === 'asap') { + var context = add(eventName, mirror, function fire() { + var i = arguments.length, args = new Array(i); + while (i--) + args[i] = arguments[i]; + context.subscribers.forEach(function (fn) { + asap$1(function fireEvent() { + fn.apply(null, args); + }); + }); + }); + } + else + throw new exceptions.InvalidArgument("Invalid event config"); + }); + } + } + + function makeClassConstructor(prototype, constructor) { + derive(constructor).from({ prototype: prototype }); + return constructor; + } + + function createTableConstructor(db) { + return makeClassConstructor(Table.prototype, function Table(name, tableSchema, trans) { + this.db = db; + this._tx = trans; + this.name = name; + this.schema = tableSchema; + this.hook = db._allTables[name] ? db._allTables[name].hook : Events(null, { + "creating": [hookCreatingChain, nop], + "reading": [pureFunctionChain, mirror], + "updating": [hookUpdatingChain, nop], + "deleting": [hookDeletingChain, nop] + }); + }); + } + + function isPlainKeyRange(ctx, ignoreLimitFilter) { + return !(ctx.filter || ctx.algorithm || ctx.or) && + (ignoreLimitFilter ? ctx.justLimit : !ctx.replayFilter); + } + function addFilter(ctx, fn) { + ctx.filter = combine(ctx.filter, fn); + } + function addReplayFilter(ctx, factory, isLimitFilter) { + var curr = ctx.replayFilter; + ctx.replayFilter = curr ? function () { return combine(curr(), factory()); } : factory; + ctx.justLimit = isLimitFilter && !curr; + } + function addMatchFilter(ctx, fn) { + ctx.isMatch = combine(ctx.isMatch, fn); + } + function getIndexOrStore(ctx, coreSchema) { + if (ctx.isPrimKey) + return coreSchema.primaryKey; + var index = coreSchema.getIndexByKeyPath(ctx.index); + if (!index) + throw new exceptions.Schema("KeyPath " + ctx.index + " on object store " + coreSchema.name + " is not indexed"); + return index; + } + function openCursor(ctx, coreTable, trans) { + var index = getIndexOrStore(ctx, coreTable.schema); + return coreTable.openCursor({ + trans: trans, + values: !ctx.keysOnly, + reverse: ctx.dir === 'prev', + unique: !!ctx.unique, + query: { + index: index, + range: ctx.range + } + }); + } + function iter(ctx, fn, coreTrans, coreTable) { + var filter = ctx.replayFilter ? combine(ctx.filter, ctx.replayFilter()) : ctx.filter; + if (!ctx.or) { + return iterate(openCursor(ctx, coreTable, coreTrans), combine(ctx.algorithm, filter), fn, !ctx.keysOnly && ctx.valueMapper); + } + else { + var set_1 = {}; + var union = function (item, cursor, advance) { + if (!filter || filter(cursor, advance, function (result) { return cursor.stop(result); }, function (err) { return cursor.fail(err); })) { + var primaryKey = cursor.primaryKey; + var key = '' + primaryKey; + if (key === '[object ArrayBuffer]') + key = '' + new Uint8Array(primaryKey); + if (!hasOwn(set_1, key)) { + set_1[key] = true; + fn(item, cursor, advance); + } + } + }; + return Promise.all([ + ctx.or._iterate(union, coreTrans), + iterate(openCursor(ctx, coreTable, coreTrans), ctx.algorithm, union, !ctx.keysOnly && ctx.valueMapper) + ]); + } + } + function iterate(cursorPromise, filter, fn, valueMapper) { + var mappedFn = valueMapper ? function (x, c, a) { return fn(valueMapper(x), c, a); } : fn; + var wrappedFn = wrap(mappedFn); + return cursorPromise.then(function (cursor) { + if (cursor) { + return cursor.start(function () { + var c = function () { return cursor.continue(); }; + if (!filter || filter(cursor, function (advancer) { return c = advancer; }, function (val) { cursor.stop(val); c = nop; }, function (e) { cursor.fail(e); c = nop; })) + wrappedFn(cursor.value, cursor, function (advancer) { return c = advancer; }); + c(); + }); + } + }); + } + + function cmp(a, b) { + try { + var ta = type(a); + var tb = type(b); + if (ta !== tb) { + if (ta === 'Array') + return 1; + if (tb === 'Array') + return -1; + if (ta === 'binary') + return 1; + if (tb === 'binary') + return -1; + if (ta === 'string') + return 1; + if (tb === 'string') + return -1; + if (ta === 'Date') + return 1; + if (tb !== 'Date') + return NaN; + return -1; + } + switch (ta) { + case 'number': + case 'Date': + case 'string': + return a > b ? 1 : a < b ? -1 : 0; + case 'binary': { + return compareUint8Arrays(getUint8Array(a), getUint8Array(b)); + } + case 'Array': + return compareArrays(a, b); + } + } + catch (_a) { } + return NaN; + } + function compareArrays(a, b) { + var al = a.length; + var bl = b.length; + var l = al < bl ? al : bl; + for (var i = 0; i < l; ++i) { + var res = cmp(a[i], b[i]); + if (res !== 0) + return res; + } + return al === bl ? 0 : al < bl ? -1 : 1; + } + function compareUint8Arrays(a, b) { + var al = a.length; + var bl = b.length; + var l = al < bl ? al : bl; + for (var i = 0; i < l; ++i) { + if (a[i] !== b[i]) + return a[i] < b[i] ? -1 : 1; + } + return al === bl ? 0 : al < bl ? -1 : 1; + } + function type(x) { + var t = typeof x; + if (t !== 'object') + return t; + if (ArrayBuffer.isView(x)) + return 'binary'; + var tsTag = toStringTag(x); + return tsTag === 'ArrayBuffer' ? 'binary' : tsTag; + } + function getUint8Array(a) { + if (a instanceof Uint8Array) + return a; + if (ArrayBuffer.isView(a)) + return new Uint8Array(a.buffer, a.byteOffset, a.byteLength); + return new Uint8Array(a); + } + + var Collection = (function () { + function Collection() { + } + Collection.prototype._read = function (fn, cb) { + var ctx = this._ctx; + return ctx.error ? + ctx.table._trans(null, rejection.bind(null, ctx.error)) : + ctx.table._trans('readonly', fn).then(cb); + }; + Collection.prototype._write = function (fn) { + var ctx = this._ctx; + return ctx.error ? + ctx.table._trans(null, rejection.bind(null, ctx.error)) : + ctx.table._trans('readwrite', fn, "locked"); + }; + Collection.prototype._addAlgorithm = function (fn) { + var ctx = this._ctx; + ctx.algorithm = combine(ctx.algorithm, fn); + }; + Collection.prototype._iterate = function (fn, coreTrans) { + return iter(this._ctx, fn, coreTrans, this._ctx.table.core); + }; + Collection.prototype.clone = function (props) { + var rv = Object.create(this.constructor.prototype), ctx = Object.create(this._ctx); + if (props) + extend(ctx, props); + rv._ctx = ctx; + return rv; + }; + Collection.prototype.raw = function () { + this._ctx.valueMapper = null; + return this; + }; + Collection.prototype.each = function (fn) { + var ctx = this._ctx; + return this._read(function (trans) { return iter(ctx, fn, trans, ctx.table.core); }); + }; + Collection.prototype.count = function (cb) { + var _this = this; + return this._read(function (trans) { + var ctx = _this._ctx; + var coreTable = ctx.table.core; + if (isPlainKeyRange(ctx, true)) { + return coreTable.count({ + trans: trans, + query: { + index: getIndexOrStore(ctx, coreTable.schema), + range: ctx.range + } + }).then(function (count) { return Math.min(count, ctx.limit); }); + } + else { + var count = 0; + return iter(ctx, function () { ++count; return false; }, trans, coreTable) + .then(function () { return count; }); + } + }).then(cb); + }; + Collection.prototype.sortBy = function (keyPath, cb) { + var parts = keyPath.split('.').reverse(), lastPart = parts[0], lastIndex = parts.length - 1; + function getval(obj, i) { + if (i) + return getval(obj[parts[i]], i - 1); + return obj[lastPart]; + } + var order = this._ctx.dir === "next" ? 1 : -1; + function sorter(a, b) { + var aVal = getval(a, lastIndex), bVal = getval(b, lastIndex); + return aVal < bVal ? -order : aVal > bVal ? order : 0; + } + return this.toArray(function (a) { + return a.sort(sorter); + }).then(cb); + }; + Collection.prototype.toArray = function (cb) { + var _this = this; + return this._read(function (trans) { + var ctx = _this._ctx; + if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) { + var valueMapper_1 = ctx.valueMapper; + var index = getIndexOrStore(ctx, ctx.table.core.schema); + return ctx.table.core.query({ + trans: trans, + limit: ctx.limit, + values: true, + query: { + index: index, + range: ctx.range + } + }).then(function (_a) { + var result = _a.result; + return valueMapper_1 ? result.map(valueMapper_1) : result; + }); + } + else { + var a_1 = []; + return iter(ctx, function (item) { return a_1.push(item); }, trans, ctx.table.core).then(function () { return a_1; }); + } + }, cb); + }; + Collection.prototype.offset = function (offset) { + var ctx = this._ctx; + if (offset <= 0) + return this; + ctx.offset += offset; + if (isPlainKeyRange(ctx)) { + addReplayFilter(ctx, function () { + var offsetLeft = offset; + return function (cursor, advance) { + if (offsetLeft === 0) + return true; + if (offsetLeft === 1) { + --offsetLeft; + return false; + } + advance(function () { + cursor.advance(offsetLeft); + offsetLeft = 0; + }); + return false; + }; + }); + } + else { + addReplayFilter(ctx, function () { + var offsetLeft = offset; + return function () { return (--offsetLeft < 0); }; + }); + } + return this; + }; + Collection.prototype.limit = function (numRows) { + this._ctx.limit = Math.min(this._ctx.limit, numRows); + addReplayFilter(this._ctx, function () { + var rowsLeft = numRows; + return function (cursor, advance, resolve) { + if (--rowsLeft <= 0) + advance(resolve); + return rowsLeft >= 0; + }; + }, true); + return this; + }; + Collection.prototype.until = function (filterFunction, bIncludeStopEntry) { + addFilter(this._ctx, function (cursor, advance, resolve) { + if (filterFunction(cursor.value)) { + advance(resolve); + return bIncludeStopEntry; + } + else { + return true; + } + }); + return this; + }; + Collection.prototype.first = function (cb) { + return this.limit(1).toArray(function (a) { return a[0]; }).then(cb); + }; + Collection.prototype.last = function (cb) { + return this.reverse().first(cb); + }; + Collection.prototype.filter = function (filterFunction) { + addFilter(this._ctx, function (cursor) { + return filterFunction(cursor.value); + }); + addMatchFilter(this._ctx, filterFunction); + return this; + }; + Collection.prototype.and = function (filter) { + return this.filter(filter); + }; + Collection.prototype.or = function (indexName) { + return new this.db.WhereClause(this._ctx.table, indexName, this); + }; + Collection.prototype.reverse = function () { + this._ctx.dir = (this._ctx.dir === "prev" ? "next" : "prev"); + if (this._ondirectionchange) + this._ondirectionchange(this._ctx.dir); + return this; + }; + Collection.prototype.desc = function () { + return this.reverse(); + }; + Collection.prototype.eachKey = function (cb) { + var ctx = this._ctx; + ctx.keysOnly = !ctx.isMatch; + return this.each(function (val, cursor) { cb(cursor.key, cursor); }); + }; + Collection.prototype.eachUniqueKey = function (cb) { + this._ctx.unique = "unique"; + return this.eachKey(cb); + }; + Collection.prototype.eachPrimaryKey = function (cb) { + var ctx = this._ctx; + ctx.keysOnly = !ctx.isMatch; + return this.each(function (val, cursor) { cb(cursor.primaryKey, cursor); }); + }; + Collection.prototype.keys = function (cb) { + var ctx = this._ctx; + ctx.keysOnly = !ctx.isMatch; + var a = []; + return this.each(function (item, cursor) { + a.push(cursor.key); + }).then(function () { + return a; + }).then(cb); + }; + Collection.prototype.primaryKeys = function (cb) { + var ctx = this._ctx; + if (ctx.dir === 'next' && isPlainKeyRange(ctx, true) && ctx.limit > 0) { + return this._read(function (trans) { + var index = getIndexOrStore(ctx, ctx.table.core.schema); + return ctx.table.core.query({ + trans: trans, + values: false, + limit: ctx.limit, + query: { + index: index, + range: ctx.range + } + }); + }).then(function (_a) { + var result = _a.result; + return result; + }).then(cb); + } + ctx.keysOnly = !ctx.isMatch; + var a = []; + return this.each(function (item, cursor) { + a.push(cursor.primaryKey); + }).then(function () { + return a; + }).then(cb); + }; + Collection.prototype.uniqueKeys = function (cb) { + this._ctx.unique = "unique"; + return this.keys(cb); + }; + Collection.prototype.firstKey = function (cb) { + return this.limit(1).keys(function (a) { return a[0]; }).then(cb); + }; + Collection.prototype.lastKey = function (cb) { + return this.reverse().firstKey(cb); + }; + Collection.prototype.distinct = function () { + var ctx = this._ctx, idx = ctx.index && ctx.table.schema.idxByName[ctx.index]; + if (!idx || !idx.multi) + return this; + var set = {}; + addFilter(this._ctx, function (cursor) { + var strKey = cursor.primaryKey.toString(); + var found = hasOwn(set, strKey); + set[strKey] = true; + return !found; + }); + return this; + }; + Collection.prototype.modify = function (changes) { + var _this = this; + var ctx = this._ctx; + return this._write(function (trans) { + var modifyer; + if (typeof changes === 'function') { + modifyer = changes; + } + else { + var keyPaths = keys(changes); + var numKeys = keyPaths.length; + modifyer = function (item) { + var anythingModified = false; + for (var i = 0; i < numKeys; ++i) { + var keyPath = keyPaths[i], val = changes[keyPath]; + if (getByKeyPath(item, keyPath) !== val) { + setByKeyPath(item, keyPath, val); + anythingModified = true; + } + } + return anythingModified; + }; + } + var coreTable = ctx.table.core; + var _a = coreTable.schema.primaryKey, outbound = _a.outbound, extractKey = _a.extractKey; + var limit = _this.db._options.modifyChunkSize || 200; + var totalFailures = []; + var successCount = 0; + var failedKeys = []; + var applyMutateResult = function (expectedCount, res) { + var failures = res.failures, numFailures = res.numFailures; + successCount += expectedCount - numFailures; + for (var _i = 0, _a = keys(failures); _i < _a.length; _i++) { + var pos = _a[_i]; + totalFailures.push(failures[pos]); + } + }; + return _this.clone().primaryKeys().then(function (keys) { + var nextChunk = function (offset) { + var count = Math.min(limit, keys.length - offset); + return coreTable.getMany({ + trans: trans, + keys: keys.slice(offset, offset + count), + cache: "immutable" + }).then(function (values) { + var addValues = []; + var putValues = []; + var putKeys = outbound ? [] : null; + var deleteKeys = []; + for (var i = 0; i < count; ++i) { + var origValue = values[i]; + var ctx_1 = { + value: deepClone(origValue), + primKey: keys[offset + i] + }; + if (modifyer.call(ctx_1, ctx_1.value, ctx_1) !== false) { + if (ctx_1.value == null) { + deleteKeys.push(keys[offset + i]); + } + else if (!outbound && cmp(extractKey(origValue), extractKey(ctx_1.value)) !== 0) { + deleteKeys.push(keys[offset + i]); + addValues.push(ctx_1.value); + } + else { + putValues.push(ctx_1.value); + if (outbound) + putKeys.push(keys[offset + i]); + } + } + } + var criteria = isPlainKeyRange(ctx) && + ctx.limit === Infinity && + (typeof changes !== 'function' || changes === deleteCallback) && { + index: ctx.index, + range: ctx.range + }; + return Promise.resolve(addValues.length > 0 && + coreTable.mutate({ trans: trans, type: 'add', values: addValues }) + .then(function (res) { + for (var pos in res.failures) { + deleteKeys.splice(parseInt(pos), 1); + } + applyMutateResult(addValues.length, res); + })).then(function () { return (putValues.length > 0 || (criteria && typeof changes === 'object')) && + coreTable.mutate({ + trans: trans, + type: 'put', + keys: putKeys, + values: putValues, + criteria: criteria, + changeSpec: typeof changes !== 'function' + && changes + }).then(function (res) { return applyMutateResult(putValues.length, res); }); }).then(function () { return (deleteKeys.length > 0 || (criteria && changes === deleteCallback)) && + coreTable.mutate({ + trans: trans, + type: 'delete', + keys: deleteKeys, + criteria: criteria + }).then(function (res) { return applyMutateResult(deleteKeys.length, res); }); }).then(function () { + return keys.length > offset + count && nextChunk(offset + limit); + }); + }); + }; + return nextChunk(0).then(function () { + if (totalFailures.length > 0) + throw new ModifyError("Error modifying one or more objects", totalFailures, successCount, failedKeys); + return keys.length; + }); + }); + }); + }; + Collection.prototype.delete = function () { + var ctx = this._ctx, range = ctx.range; + if (isPlainKeyRange(ctx) && + ((ctx.isPrimKey && !hangsOnDeleteLargeKeyRange) || range.type === 3 )) + { + return this._write(function (trans) { + var primaryKey = ctx.table.core.schema.primaryKey; + var coreRange = range; + return ctx.table.core.count({ trans: trans, query: { index: primaryKey, range: coreRange } }).then(function (count) { + return ctx.table.core.mutate({ trans: trans, type: 'deleteRange', range: coreRange }) + .then(function (_a) { + var failures = _a.failures; _a.lastResult; _a.results; var numFailures = _a.numFailures; + if (numFailures) + throw new ModifyError("Could not delete some values", Object.keys(failures).map(function (pos) { return failures[pos]; }), count - numFailures); + return count - numFailures; + }); + }); + }); + } + return this.modify(deleteCallback); + }; + return Collection; + }()); + var deleteCallback = function (value, ctx) { return ctx.value = null; }; + + function createCollectionConstructor(db) { + return makeClassConstructor(Collection.prototype, function Collection(whereClause, keyRangeGenerator) { + this.db = db; + var keyRange = AnyRange, error = null; + if (keyRangeGenerator) + try { + keyRange = keyRangeGenerator(); + } + catch (ex) { + error = ex; + } + var whereCtx = whereClause._ctx; + var table = whereCtx.table; + var readingHook = table.hook.reading.fire; + this._ctx = { + table: table, + index: whereCtx.index, + isPrimKey: (!whereCtx.index || (table.schema.primKey.keyPath && whereCtx.index === table.schema.primKey.name)), + range: keyRange, + keysOnly: false, + dir: "next", + unique: "", + algorithm: null, + filter: null, + replayFilter: null, + justLimit: true, + isMatch: null, + offset: 0, + limit: Infinity, + error: error, + or: whereCtx.or, + valueMapper: readingHook !== mirror ? readingHook : null + }; + }); + } + + function simpleCompare(a, b) { + return a < b ? -1 : a === b ? 0 : 1; + } + function simpleCompareReverse(a, b) { + return a > b ? -1 : a === b ? 0 : 1; + } + + function fail(collectionOrWhereClause, err, T) { + var collection = collectionOrWhereClause instanceof WhereClause ? + new collectionOrWhereClause.Collection(collectionOrWhereClause) : + collectionOrWhereClause; + collection._ctx.error = T ? new T(err) : new TypeError(err); + return collection; + } + function emptyCollection(whereClause) { + return new whereClause.Collection(whereClause, function () { return rangeEqual(""); }).limit(0); + } + function upperFactory(dir) { + return dir === "next" ? + function (s) { return s.toUpperCase(); } : + function (s) { return s.toLowerCase(); }; + } + function lowerFactory(dir) { + return dir === "next" ? + function (s) { return s.toLowerCase(); } : + function (s) { return s.toUpperCase(); }; + } + function nextCasing(key, lowerKey, upperNeedle, lowerNeedle, cmp, dir) { + var length = Math.min(key.length, lowerNeedle.length); + var llp = -1; + for (var i = 0; i < length; ++i) { + var lwrKeyChar = lowerKey[i]; + if (lwrKeyChar !== lowerNeedle[i]) { + if (cmp(key[i], upperNeedle[i]) < 0) + return key.substr(0, i) + upperNeedle[i] + upperNeedle.substr(i + 1); + if (cmp(key[i], lowerNeedle[i]) < 0) + return key.substr(0, i) + lowerNeedle[i] + upperNeedle.substr(i + 1); + if (llp >= 0) + return key.substr(0, llp) + lowerKey[llp] + upperNeedle.substr(llp + 1); + return null; + } + if (cmp(key[i], lwrKeyChar) < 0) + llp = i; + } + if (length < lowerNeedle.length && dir === "next") + return key + upperNeedle.substr(key.length); + if (length < key.length && dir === "prev") + return key.substr(0, upperNeedle.length); + return (llp < 0 ? null : key.substr(0, llp) + lowerNeedle[llp] + upperNeedle.substr(llp + 1)); + } + function addIgnoreCaseAlgorithm(whereClause, match, needles, suffix) { + var upper, lower, compare, upperNeedles, lowerNeedles, direction, nextKeySuffix, needlesLen = needles.length; + if (!needles.every(function (s) { return typeof s === 'string'; })) { + return fail(whereClause, STRING_EXPECTED); + } + function initDirection(dir) { + upper = upperFactory(dir); + lower = lowerFactory(dir); + compare = (dir === "next" ? simpleCompare : simpleCompareReverse); + var needleBounds = needles.map(function (needle) { + return { lower: lower(needle), upper: upper(needle) }; + }).sort(function (a, b) { + return compare(a.lower, b.lower); + }); + upperNeedles = needleBounds.map(function (nb) { return nb.upper; }); + lowerNeedles = needleBounds.map(function (nb) { return nb.lower; }); + direction = dir; + nextKeySuffix = (dir === "next" ? "" : suffix); + } + initDirection("next"); + var c = new whereClause.Collection(whereClause, function () { return createRange(upperNeedles[0], lowerNeedles[needlesLen - 1] + suffix); }); + c._ondirectionchange = function (direction) { + initDirection(direction); + }; + var firstPossibleNeedle = 0; + c._addAlgorithm(function (cursor, advance, resolve) { + var key = cursor.key; + if (typeof key !== 'string') + return false; + var lowerKey = lower(key); + if (match(lowerKey, lowerNeedles, firstPossibleNeedle)) { + return true; + } + else { + var lowestPossibleCasing = null; + for (var i = firstPossibleNeedle; i < needlesLen; ++i) { + var casing = nextCasing(key, lowerKey, upperNeedles[i], lowerNeedles[i], compare, direction); + if (casing === null && lowestPossibleCasing === null) + firstPossibleNeedle = i + 1; + else if (lowestPossibleCasing === null || compare(lowestPossibleCasing, casing) > 0) { + lowestPossibleCasing = casing; + } + } + if (lowestPossibleCasing !== null) { + advance(function () { cursor.continue(lowestPossibleCasing + nextKeySuffix); }); + } + else { + advance(resolve); + } + return false; + } + }); + return c; + } + function createRange(lower, upper, lowerOpen, upperOpen) { + return { + type: 2 , + lower: lower, + upper: upper, + lowerOpen: lowerOpen, + upperOpen: upperOpen + }; + } + function rangeEqual(value) { + return { + type: 1 , + lower: value, + upper: value + }; + } + + var WhereClause = (function () { + function WhereClause() { + } + Object.defineProperty(WhereClause.prototype, "Collection", { + get: function () { + return this._ctx.table.db.Collection; + }, + enumerable: false, + configurable: true + }); + WhereClause.prototype.between = function (lower, upper, includeLower, includeUpper) { + includeLower = includeLower !== false; + includeUpper = includeUpper === true; + try { + if ((this._cmp(lower, upper) > 0) || + (this._cmp(lower, upper) === 0 && (includeLower || includeUpper) && !(includeLower && includeUpper))) + return emptyCollection(this); + return new this.Collection(this, function () { return createRange(lower, upper, !includeLower, !includeUpper); }); + } + catch (e) { + return fail(this, INVALID_KEY_ARGUMENT); + } + }; + WhereClause.prototype.equals = function (value) { + if (value == null) + return fail(this, INVALID_KEY_ARGUMENT); + return new this.Collection(this, function () { return rangeEqual(value); }); + }; + WhereClause.prototype.above = function (value) { + if (value == null) + return fail(this, INVALID_KEY_ARGUMENT); + return new this.Collection(this, function () { return createRange(value, undefined, true); }); + }; + WhereClause.prototype.aboveOrEqual = function (value) { + if (value == null) + return fail(this, INVALID_KEY_ARGUMENT); + return new this.Collection(this, function () { return createRange(value, undefined, false); }); + }; + WhereClause.prototype.below = function (value) { + if (value == null) + return fail(this, INVALID_KEY_ARGUMENT); + return new this.Collection(this, function () { return createRange(undefined, value, false, true); }); + }; + WhereClause.prototype.belowOrEqual = function (value) { + if (value == null) + return fail(this, INVALID_KEY_ARGUMENT); + return new this.Collection(this, function () { return createRange(undefined, value); }); + }; + WhereClause.prototype.startsWith = function (str) { + if (typeof str !== 'string') + return fail(this, STRING_EXPECTED); + return this.between(str, str + maxString, true, true); + }; + WhereClause.prototype.startsWithIgnoreCase = function (str) { + if (str === "") + return this.startsWith(str); + return addIgnoreCaseAlgorithm(this, function (x, a) { return x.indexOf(a[0]) === 0; }, [str], maxString); + }; + WhereClause.prototype.equalsIgnoreCase = function (str) { + return addIgnoreCaseAlgorithm(this, function (x, a) { return x === a[0]; }, [str], ""); + }; + WhereClause.prototype.anyOfIgnoreCase = function () { + var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); + if (set.length === 0) + return emptyCollection(this); + return addIgnoreCaseAlgorithm(this, function (x, a) { return a.indexOf(x) !== -1; }, set, ""); + }; + WhereClause.prototype.startsWithAnyOfIgnoreCase = function () { + var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); + if (set.length === 0) + return emptyCollection(this); + return addIgnoreCaseAlgorithm(this, function (x, a) { return a.some(function (n) { return x.indexOf(n) === 0; }); }, set, maxString); + }; + WhereClause.prototype.anyOf = function () { + var _this = this; + var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); + var compare = this._cmp; + try { + set.sort(compare); + } + catch (e) { + return fail(this, INVALID_KEY_ARGUMENT); + } + if (set.length === 0) + return emptyCollection(this); + var c = new this.Collection(this, function () { return createRange(set[0], set[set.length - 1]); }); + c._ondirectionchange = function (direction) { + compare = (direction === "next" ? + _this._ascending : + _this._descending); + set.sort(compare); + }; + var i = 0; + c._addAlgorithm(function (cursor, advance, resolve) { + var key = cursor.key; + while (compare(key, set[i]) > 0) { + ++i; + if (i === set.length) { + advance(resolve); + return false; + } + } + if (compare(key, set[i]) === 0) { + return true; + } + else { + advance(function () { cursor.continue(set[i]); }); + return false; + } + }); + return c; + }; + WhereClause.prototype.notEqual = function (value) { + return this.inAnyRange([[minKey, value], [value, this.db._maxKey]], { includeLowers: false, includeUppers: false }); + }; + WhereClause.prototype.noneOf = function () { + var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); + if (set.length === 0) + return new this.Collection(this); + try { + set.sort(this._ascending); + } + catch (e) { + return fail(this, INVALID_KEY_ARGUMENT); + } + var ranges = set.reduce(function (res, val) { return res ? + res.concat([[res[res.length - 1][1], val]]) : + [[minKey, val]]; }, null); + ranges.push([set[set.length - 1], this.db._maxKey]); + return this.inAnyRange(ranges, { includeLowers: false, includeUppers: false }); + }; + WhereClause.prototype.inAnyRange = function (ranges, options) { + var _this = this; + var cmp = this._cmp, ascending = this._ascending, descending = this._descending, min = this._min, max = this._max; + if (ranges.length === 0) + return emptyCollection(this); + if (!ranges.every(function (range) { + return range[0] !== undefined && + range[1] !== undefined && + ascending(range[0], range[1]) <= 0; + })) { + return fail(this, "First argument to inAnyRange() must be an Array of two-value Arrays [lower,upper] where upper must not be lower than lower", exceptions.InvalidArgument); + } + var includeLowers = !options || options.includeLowers !== false; + var includeUppers = options && options.includeUppers === true; + function addRange(ranges, newRange) { + var i = 0, l = ranges.length; + for (; i < l; ++i) { + var range = ranges[i]; + if (cmp(newRange[0], range[1]) < 0 && cmp(newRange[1], range[0]) > 0) { + range[0] = min(range[0], newRange[0]); + range[1] = max(range[1], newRange[1]); + break; + } + } + if (i === l) + ranges.push(newRange); + return ranges; + } + var sortDirection = ascending; + function rangeSorter(a, b) { return sortDirection(a[0], b[0]); } + var set; + try { + set = ranges.reduce(addRange, []); + set.sort(rangeSorter); + } + catch (ex) { + return fail(this, INVALID_KEY_ARGUMENT); + } + var rangePos = 0; + var keyIsBeyondCurrentEntry = includeUppers ? + function (key) { return ascending(key, set[rangePos][1]) > 0; } : + function (key) { return ascending(key, set[rangePos][1]) >= 0; }; + var keyIsBeforeCurrentEntry = includeLowers ? + function (key) { return descending(key, set[rangePos][0]) > 0; } : + function (key) { return descending(key, set[rangePos][0]) >= 0; }; + function keyWithinCurrentRange(key) { + return !keyIsBeyondCurrentEntry(key) && !keyIsBeforeCurrentEntry(key); + } + var checkKey = keyIsBeyondCurrentEntry; + var c = new this.Collection(this, function () { return createRange(set[0][0], set[set.length - 1][1], !includeLowers, !includeUppers); }); + c._ondirectionchange = function (direction) { + if (direction === "next") { + checkKey = keyIsBeyondCurrentEntry; + sortDirection = ascending; + } + else { + checkKey = keyIsBeforeCurrentEntry; + sortDirection = descending; + } + set.sort(rangeSorter); + }; + c._addAlgorithm(function (cursor, advance, resolve) { + var key = cursor.key; + while (checkKey(key)) { + ++rangePos; + if (rangePos === set.length) { + advance(resolve); + return false; + } + } + if (keyWithinCurrentRange(key)) { + return true; + } + else if (_this._cmp(key, set[rangePos][1]) === 0 || _this._cmp(key, set[rangePos][0]) === 0) { + return false; + } + else { + advance(function () { + if (sortDirection === ascending) + cursor.continue(set[rangePos][0]); + else + cursor.continue(set[rangePos][1]); + }); + return false; + } + }); + return c; + }; + WhereClause.prototype.startsWithAnyOf = function () { + var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments); + if (!set.every(function (s) { return typeof s === 'string'; })) { + return fail(this, "startsWithAnyOf() only works with strings"); + } + if (set.length === 0) + return emptyCollection(this); + return this.inAnyRange(set.map(function (str) { return [str, str + maxString]; })); + }; + return WhereClause; + }()); + + function createWhereClauseConstructor(db) { + return makeClassConstructor(WhereClause.prototype, function WhereClause(table, index, orCollection) { + this.db = db; + this._ctx = { + table: table, + index: index === ":id" ? null : index, + or: orCollection + }; + var indexedDB = db._deps.indexedDB; + if (!indexedDB) + throw new exceptions.MissingAPI(); + this._cmp = this._ascending = indexedDB.cmp.bind(indexedDB); + this._descending = function (a, b) { return indexedDB.cmp(b, a); }; + this._max = function (a, b) { return indexedDB.cmp(a, b) > 0 ? a : b; }; + this._min = function (a, b) { return indexedDB.cmp(a, b) < 0 ? a : b; }; + this._IDBKeyRange = db._deps.IDBKeyRange; + }); + } + + function eventRejectHandler(reject) { + return wrap(function (event) { + preventDefault(event); + reject(event.target.error); + return false; + }); + } + function preventDefault(event) { + if (event.stopPropagation) + event.stopPropagation(); + if (event.preventDefault) + event.preventDefault(); + } + + var DEXIE_STORAGE_MUTATED_EVENT_NAME = 'storagemutated'; + var STORAGE_MUTATED_DOM_EVENT_NAME = 'x-storagemutated-1'; + var globalEvents = Events(null, DEXIE_STORAGE_MUTATED_EVENT_NAME); + + var Transaction = (function () { + function Transaction() { + } + Transaction.prototype._lock = function () { + assert(!PSD.global); + ++this._reculock; + if (this._reculock === 1 && !PSD.global) + PSD.lockOwnerFor = this; + return this; + }; + Transaction.prototype._unlock = function () { + assert(!PSD.global); + if (--this._reculock === 0) { + if (!PSD.global) + PSD.lockOwnerFor = null; + while (this._blockedFuncs.length > 0 && !this._locked()) { + var fnAndPSD = this._blockedFuncs.shift(); + try { + usePSD(fnAndPSD[1], fnAndPSD[0]); + } + catch (e) { } + } + } + return this; + }; + Transaction.prototype._locked = function () { + return this._reculock && PSD.lockOwnerFor !== this; + }; + Transaction.prototype.create = function (idbtrans) { + var _this = this; + if (!this.mode) + return this; + var idbdb = this.db.idbdb; + var dbOpenError = this.db._state.dbOpenError; + assert(!this.idbtrans); + if (!idbtrans && !idbdb) { + switch (dbOpenError && dbOpenError.name) { + case "DatabaseClosedError": + throw new exceptions.DatabaseClosed(dbOpenError); + case "MissingAPIError": + throw new exceptions.MissingAPI(dbOpenError.message, dbOpenError); + default: + throw new exceptions.OpenFailed(dbOpenError); + } + } + if (!this.active) + throw new exceptions.TransactionInactive(); + assert(this._completion._state === null); + idbtrans = this.idbtrans = idbtrans || + (this.db.core + ? this.db.core.transaction(this.storeNames, this.mode, { durability: this.chromeTransactionDurability }) + : idbdb.transaction(this.storeNames, this.mode, { durability: this.chromeTransactionDurability })); + idbtrans.onerror = wrap(function (ev) { + preventDefault(ev); + _this._reject(idbtrans.error); + }); + idbtrans.onabort = wrap(function (ev) { + preventDefault(ev); + _this.active && _this._reject(new exceptions.Abort(idbtrans.error)); + _this.active = false; + _this.on("abort").fire(ev); + }); + idbtrans.oncomplete = wrap(function () { + _this.active = false; + _this._resolve(); + if ('mutatedParts' in idbtrans) { + globalEvents.storagemutated.fire(idbtrans["mutatedParts"]); + } + }); + return this; + }; + Transaction.prototype._promise = function (mode, fn, bWriteLock) { + var _this = this; + if (mode === 'readwrite' && this.mode !== 'readwrite') + return rejection(new exceptions.ReadOnly("Transaction is readonly")); + if (!this.active) + return rejection(new exceptions.TransactionInactive()); + if (this._locked()) { + return new DexiePromise(function (resolve, reject) { + _this._blockedFuncs.push([function () { + _this._promise(mode, fn, bWriteLock).then(resolve, reject); + }, PSD]); + }); + } + else if (bWriteLock) { + return newScope(function () { + var p = new DexiePromise(function (resolve, reject) { + _this._lock(); + var rv = fn(resolve, reject, _this); + if (rv && rv.then) + rv.then(resolve, reject); + }); + p.finally(function () { return _this._unlock(); }); + p._lib = true; + return p; + }); + } + else { + var p = new DexiePromise(function (resolve, reject) { + var rv = fn(resolve, reject, _this); + if (rv && rv.then) + rv.then(resolve, reject); + }); + p._lib = true; + return p; + } + }; + Transaction.prototype._root = function () { + return this.parent ? this.parent._root() : this; + }; + Transaction.prototype.waitFor = function (promiseLike) { + var root = this._root(); + var promise = DexiePromise.resolve(promiseLike); + if (root._waitingFor) { + root._waitingFor = root._waitingFor.then(function () { return promise; }); + } + else { + root._waitingFor = promise; + root._waitingQueue = []; + var store = root.idbtrans.objectStore(root.storeNames[0]); + (function spin() { + ++root._spinCount; + while (root._waitingQueue.length) + (root._waitingQueue.shift())(); + if (root._waitingFor) + store.get(-Infinity).onsuccess = spin; + }()); + } + var currentWaitPromise = root._waitingFor; + return new DexiePromise(function (resolve, reject) { + promise.then(function (res) { return root._waitingQueue.push(wrap(resolve.bind(null, res))); }, function (err) { return root._waitingQueue.push(wrap(reject.bind(null, err))); }).finally(function () { + if (root._waitingFor === currentWaitPromise) { + root._waitingFor = null; + } + }); + }); + }; + Transaction.prototype.abort = function () { + if (this.active) { + this.active = false; + if (this.idbtrans) + this.idbtrans.abort(); + this._reject(new exceptions.Abort()); + } + }; + Transaction.prototype.table = function (tableName) { + var memoizedTables = (this._memoizedTables || (this._memoizedTables = {})); + if (hasOwn(memoizedTables, tableName)) + return memoizedTables[tableName]; + var tableSchema = this.schema[tableName]; + if (!tableSchema) { + throw new exceptions.NotFound("Table " + tableName + " not part of transaction"); + } + var transactionBoundTable = new this.db.Table(tableName, tableSchema, this); + transactionBoundTable.core = this.db.core.table(tableName); + memoizedTables[tableName] = transactionBoundTable; + return transactionBoundTable; + }; + return Transaction; + }()); + + function createTransactionConstructor(db) { + return makeClassConstructor(Transaction.prototype, function Transaction(mode, storeNames, dbschema, chromeTransactionDurability, parent) { + var _this = this; + this.db = db; + this.mode = mode; + this.storeNames = storeNames; + this.schema = dbschema; + this.chromeTransactionDurability = chromeTransactionDurability; + this.idbtrans = null; + this.on = Events(this, "complete", "error", "abort"); + this.parent = parent || null; + this.active = true; + this._reculock = 0; + this._blockedFuncs = []; + this._resolve = null; + this._reject = null; + this._waitingFor = null; + this._waitingQueue = null; + this._spinCount = 0; + this._completion = new DexiePromise(function (resolve, reject) { + _this._resolve = resolve; + _this._reject = reject; + }); + this._completion.then(function () { + _this.active = false; + _this.on.complete.fire(); + }, function (e) { + var wasActive = _this.active; + _this.active = false; + _this.on.error.fire(e); + _this.parent ? + _this.parent._reject(e) : + wasActive && _this.idbtrans && _this.idbtrans.abort(); + return rejection(e); + }); + }); + } + + function createIndexSpec(name, keyPath, unique, multi, auto, compound, isPrimKey) { + return { + name: name, + keyPath: keyPath, + unique: unique, + multi: multi, + auto: auto, + compound: compound, + src: (unique && !isPrimKey ? '&' : '') + (multi ? '*' : '') + (auto ? "++" : "") + nameFromKeyPath(keyPath) + }; + } + function nameFromKeyPath(keyPath) { + return typeof keyPath === 'string' ? + keyPath : + keyPath ? ('[' + [].join.call(keyPath, '+') + ']') : ""; + } + + function createTableSchema(name, primKey, indexes) { + return { + name: name, + primKey: primKey, + indexes: indexes, + mappedClass: null, + idxByName: arrayToObject(indexes, function (index) { return [index.name, index]; }) + }; + } + + function safariMultiStoreFix(storeNames) { + return storeNames.length === 1 ? storeNames[0] : storeNames; + } + var getMaxKey = function (IdbKeyRange) { + try { + IdbKeyRange.only([[]]); + getMaxKey = function () { return [[]]; }; + return [[]]; + } + catch (e) { + getMaxKey = function () { return maxString; }; + return maxString; + } + }; + + function getKeyExtractor(keyPath) { + if (keyPath == null) { + return function () { return undefined; }; + } + else if (typeof keyPath === 'string') { + return getSinglePathKeyExtractor(keyPath); + } + else { + return function (obj) { return getByKeyPath(obj, keyPath); }; + } + } + function getSinglePathKeyExtractor(keyPath) { + var split = keyPath.split('.'); + if (split.length === 1) { + return function (obj) { return obj[keyPath]; }; + } + else { + return function (obj) { return getByKeyPath(obj, keyPath); }; + } + } + + function arrayify(arrayLike) { + return [].slice.call(arrayLike); + } + var _id_counter = 0; + function getKeyPathAlias(keyPath) { + return keyPath == null ? + ":id" : + typeof keyPath === 'string' ? + keyPath : + "[" + keyPath.join('+') + "]"; + } + function createDBCore(db, IdbKeyRange, tmpTrans) { + function extractSchema(db, trans) { + var tables = arrayify(db.objectStoreNames); + return { + schema: { + name: db.name, + tables: tables.map(function (table) { return trans.objectStore(table); }).map(function (store) { + var keyPath = store.keyPath, autoIncrement = store.autoIncrement; + var compound = isArray(keyPath); + var outbound = keyPath == null; + var indexByKeyPath = {}; + var result = { + name: store.name, + primaryKey: { + name: null, + isPrimaryKey: true, + outbound: outbound, + compound: compound, + keyPath: keyPath, + autoIncrement: autoIncrement, + unique: true, + extractKey: getKeyExtractor(keyPath) + }, + indexes: arrayify(store.indexNames).map(function (indexName) { return store.index(indexName); }) + .map(function (index) { + var name = index.name, unique = index.unique, multiEntry = index.multiEntry, keyPath = index.keyPath; + var compound = isArray(keyPath); + var result = { + name: name, + compound: compound, + keyPath: keyPath, + unique: unique, + multiEntry: multiEntry, + extractKey: getKeyExtractor(keyPath) + }; + indexByKeyPath[getKeyPathAlias(keyPath)] = result; + return result; + }), + getIndexByKeyPath: function (keyPath) { return indexByKeyPath[getKeyPathAlias(keyPath)]; } + }; + indexByKeyPath[":id"] = result.primaryKey; + if (keyPath != null) { + indexByKeyPath[getKeyPathAlias(keyPath)] = result.primaryKey; + } + return result; + }) + }, + hasGetAll: tables.length > 0 && ('getAll' in trans.objectStore(tables[0])) && + !(typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent) && + !/(Chrome\/|Edge\/)/.test(navigator.userAgent) && + [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604) + }; + } + function makeIDBKeyRange(range) { + if (range.type === 3 ) + return null; + if (range.type === 4 ) + throw new Error("Cannot convert never type to IDBKeyRange"); + var lower = range.lower, upper = range.upper, lowerOpen = range.lowerOpen, upperOpen = range.upperOpen; + var idbRange = lower === undefined ? + upper === undefined ? + null : + IdbKeyRange.upperBound(upper, !!upperOpen) : + upper === undefined ? + IdbKeyRange.lowerBound(lower, !!lowerOpen) : + IdbKeyRange.bound(lower, upper, !!lowerOpen, !!upperOpen); + return idbRange; + } + function createDbCoreTable(tableSchema) { + var tableName = tableSchema.name; + function mutate(_a) { + var trans = _a.trans, type = _a.type, keys = _a.keys, values = _a.values, range = _a.range; + return new Promise(function (resolve, reject) { + resolve = wrap(resolve); + var store = trans.objectStore(tableName); + var outbound = store.keyPath == null; + var isAddOrPut = type === "put" || type === "add"; + if (!isAddOrPut && type !== 'delete' && type !== 'deleteRange') + throw new Error("Invalid operation type: " + type); + var length = (keys || values || { length: 1 }).length; + if (keys && values && keys.length !== values.length) { + throw new Error("Given keys array must have same length as given values array."); + } + if (length === 0) + return resolve({ numFailures: 0, failures: {}, results: [], lastResult: undefined }); + var req; + var reqs = []; + var failures = []; + var numFailures = 0; + var errorHandler = function (event) { + ++numFailures; + preventDefault(event); + }; + if (type === 'deleteRange') { + if (range.type === 4 ) + return resolve({ numFailures: numFailures, failures: failures, results: [], lastResult: undefined }); + if (range.type === 3 ) + reqs.push(req = store.clear()); + else + reqs.push(req = store.delete(makeIDBKeyRange(range))); + } + else { + var _a = isAddOrPut ? + outbound ? + [values, keys] : + [values, null] : + [keys, null], args1 = _a[0], args2 = _a[1]; + if (isAddOrPut) { + for (var i = 0; i < length; ++i) { + reqs.push(req = (args2 && args2[i] !== undefined ? + store[type](args1[i], args2[i]) : + store[type](args1[i]))); + req.onerror = errorHandler; + } + } + else { + for (var i = 0; i < length; ++i) { + reqs.push(req = store[type](args1[i])); + req.onerror = errorHandler; + } + } + } + var done = function (event) { + var lastResult = event.target.result; + reqs.forEach(function (req, i) { return req.error != null && (failures[i] = req.error); }); + resolve({ + numFailures: numFailures, + failures: failures, + results: type === "delete" ? keys : reqs.map(function (req) { return req.result; }), + lastResult: lastResult + }); + }; + req.onerror = function (event) { + errorHandler(event); + done(event); + }; + req.onsuccess = done; + }); + } + function openCursor(_a) { + var trans = _a.trans, values = _a.values, query = _a.query, reverse = _a.reverse, unique = _a.unique; + return new Promise(function (resolve, reject) { + resolve = wrap(resolve); + var index = query.index, range = query.range; + var store = trans.objectStore(tableName); + var source = index.isPrimaryKey ? + store : + store.index(index.name); + var direction = reverse ? + unique ? + "prevunique" : + "prev" : + unique ? + "nextunique" : + "next"; + var req = values || !('openKeyCursor' in source) ? + source.openCursor(makeIDBKeyRange(range), direction) : + source.openKeyCursor(makeIDBKeyRange(range), direction); + req.onerror = eventRejectHandler(reject); + req.onsuccess = wrap(function (ev) { + var cursor = req.result; + if (!cursor) { + resolve(null); + return; + } + cursor.___id = ++_id_counter; + cursor.done = false; + var _cursorContinue = cursor.continue.bind(cursor); + var _cursorContinuePrimaryKey = cursor.continuePrimaryKey; + if (_cursorContinuePrimaryKey) + _cursorContinuePrimaryKey = _cursorContinuePrimaryKey.bind(cursor); + var _cursorAdvance = cursor.advance.bind(cursor); + var doThrowCursorIsNotStarted = function () { throw new Error("Cursor not started"); }; + var doThrowCursorIsStopped = function () { throw new Error("Cursor not stopped"); }; + cursor.trans = trans; + cursor.stop = cursor.continue = cursor.continuePrimaryKey = cursor.advance = doThrowCursorIsNotStarted; + cursor.fail = wrap(reject); + cursor.next = function () { + var _this = this; + var gotOne = 1; + return this.start(function () { return gotOne-- ? _this.continue() : _this.stop(); }).then(function () { return _this; }); + }; + cursor.start = function (callback) { + var iterationPromise = new Promise(function (resolveIteration, rejectIteration) { + resolveIteration = wrap(resolveIteration); + req.onerror = eventRejectHandler(rejectIteration); + cursor.fail = rejectIteration; + cursor.stop = function (value) { + cursor.stop = cursor.continue = cursor.continuePrimaryKey = cursor.advance = doThrowCursorIsStopped; + resolveIteration(value); + }; + }); + var guardedCallback = function () { + if (req.result) { + try { + callback(); + } + catch (err) { + cursor.fail(err); + } + } + else { + cursor.done = true; + cursor.start = function () { throw new Error("Cursor behind last entry"); }; + cursor.stop(); + } + }; + req.onsuccess = wrap(function (ev) { + req.onsuccess = guardedCallback; + guardedCallback(); + }); + cursor.continue = _cursorContinue; + cursor.continuePrimaryKey = _cursorContinuePrimaryKey; + cursor.advance = _cursorAdvance; + guardedCallback(); + return iterationPromise; + }; + resolve(cursor); + }, reject); + }); + } + function query(hasGetAll) { + return function (request) { + return new Promise(function (resolve, reject) { + resolve = wrap(resolve); + var trans = request.trans, values = request.values, limit = request.limit, query = request.query; + var nonInfinitLimit = limit === Infinity ? undefined : limit; + var index = query.index, range = query.range; + var store = trans.objectStore(tableName); + var source = index.isPrimaryKey ? store : store.index(index.name); + var idbKeyRange = makeIDBKeyRange(range); + if (limit === 0) + return resolve({ result: [] }); + if (hasGetAll) { + var req = values ? + source.getAll(idbKeyRange, nonInfinitLimit) : + source.getAllKeys(idbKeyRange, nonInfinitLimit); + req.onsuccess = function (event) { return resolve({ result: event.target.result }); }; + req.onerror = eventRejectHandler(reject); + } + else { + var count_1 = 0; + var req_1 = values || !('openKeyCursor' in source) ? + source.openCursor(idbKeyRange) : + source.openKeyCursor(idbKeyRange); + var result_1 = []; + req_1.onsuccess = function (event) { + var cursor = req_1.result; + if (!cursor) + return resolve({ result: result_1 }); + result_1.push(values ? cursor.value : cursor.primaryKey); + if (++count_1 === limit) + return resolve({ result: result_1 }); + cursor.continue(); + }; + req_1.onerror = eventRejectHandler(reject); + } + }); + }; + } + return { + name: tableName, + schema: tableSchema, + mutate: mutate, + getMany: function (_a) { + var trans = _a.trans, keys = _a.keys; + return new Promise(function (resolve, reject) { + resolve = wrap(resolve); + var store = trans.objectStore(tableName); + var length = keys.length; + var result = new Array(length); + var keyCount = 0; + var callbackCount = 0; + var req; + var successHandler = function (event) { + var req = event.target; + if ((result[req._pos] = req.result) != null) + ; + if (++callbackCount === keyCount) + resolve(result); + }; + var errorHandler = eventRejectHandler(reject); + for (var i = 0; i < length; ++i) { + var key = keys[i]; + if (key != null) { + req = store.get(keys[i]); + req._pos = i; + req.onsuccess = successHandler; + req.onerror = errorHandler; + ++keyCount; + } + } + if (keyCount === 0) + resolve(result); + }); + }, + get: function (_a) { + var trans = _a.trans, key = _a.key; + return new Promise(function (resolve, reject) { + resolve = wrap(resolve); + var store = trans.objectStore(tableName); + var req = store.get(key); + req.onsuccess = function (event) { return resolve(event.target.result); }; + req.onerror = eventRejectHandler(reject); + }); + }, + query: query(hasGetAll), + openCursor: openCursor, + count: function (_a) { + var query = _a.query, trans = _a.trans; + var index = query.index, range = query.range; + return new Promise(function (resolve, reject) { + var store = trans.objectStore(tableName); + var source = index.isPrimaryKey ? store : store.index(index.name); + var idbKeyRange = makeIDBKeyRange(range); + var req = idbKeyRange ? source.count(idbKeyRange) : source.count(); + req.onsuccess = wrap(function (ev) { return resolve(ev.target.result); }); + req.onerror = eventRejectHandler(reject); + }); + } + }; + } + var _a = extractSchema(db, tmpTrans), schema = _a.schema, hasGetAll = _a.hasGetAll; + var tables = schema.tables.map(function (tableSchema) { return createDbCoreTable(tableSchema); }); + var tableMap = {}; + tables.forEach(function (table) { return tableMap[table.name] = table; }); + return { + stack: "dbcore", + transaction: db.transaction.bind(db), + table: function (name) { + var result = tableMap[name]; + if (!result) + throw new Error("Table '" + name + "' not found"); + return tableMap[name]; + }, + MIN_KEY: -Infinity, + MAX_KEY: getMaxKey(IdbKeyRange), + schema: schema + }; + } + + function createMiddlewareStack(stackImpl, middlewares) { + return middlewares.reduce(function (down, _a) { + var create = _a.create; + return (__assign(__assign({}, down), create(down))); + }, stackImpl); + } + function createMiddlewareStacks(middlewares, idbdb, _a, tmpTrans) { + var IDBKeyRange = _a.IDBKeyRange; _a.indexedDB; + var dbcore = createMiddlewareStack(createDBCore(idbdb, IDBKeyRange, tmpTrans), middlewares.dbcore); + return { + dbcore: dbcore + }; + } + function generateMiddlewareStacks(_a, tmpTrans) { + var db = _a._novip; + var idbdb = tmpTrans.db; + var stacks = createMiddlewareStacks(db._middlewares, idbdb, db._deps, tmpTrans); + db.core = stacks.dbcore; + db.tables.forEach(function (table) { + var tableName = table.name; + if (db.core.schema.tables.some(function (tbl) { return tbl.name === tableName; })) { + table.core = db.core.table(tableName); + if (db[tableName] instanceof db.Table) { + db[tableName].core = table.core; + } + } + }); + } + + function setApiOnPlace(_a, objs, tableNames, dbschema) { + var db = _a._novip; + tableNames.forEach(function (tableName) { + var schema = dbschema[tableName]; + objs.forEach(function (obj) { + var propDesc = getPropertyDescriptor(obj, tableName); + if (!propDesc || ("value" in propDesc && propDesc.value === undefined)) { + if (obj === db.Transaction.prototype || obj instanceof db.Transaction) { + setProp(obj, tableName, { + get: function () { return this.table(tableName); }, + set: function (value) { + defineProperty(this, tableName, { value: value, writable: true, configurable: true, enumerable: true }); + } + }); + } + else { + obj[tableName] = new db.Table(tableName, schema); + } + } + }); + }); + } + function removeTablesApi(_a, objs) { + var db = _a._novip; + objs.forEach(function (obj) { + for (var key in obj) { + if (obj[key] instanceof db.Table) + delete obj[key]; + } + }); + } + function lowerVersionFirst(a, b) { + return a._cfg.version - b._cfg.version; + } + function runUpgraders(db, oldVersion, idbUpgradeTrans, reject) { + var globalSchema = db._dbSchema; + var trans = db._createTransaction('readwrite', db._storeNames, globalSchema); + trans.create(idbUpgradeTrans); + trans._completion.catch(reject); + var rejectTransaction = trans._reject.bind(trans); + var transless = PSD.transless || PSD; + newScope(function () { + PSD.trans = trans; + PSD.transless = transless; + if (oldVersion === 0) { + keys(globalSchema).forEach(function (tableName) { + createTable(idbUpgradeTrans, tableName, globalSchema[tableName].primKey, globalSchema[tableName].indexes); + }); + generateMiddlewareStacks(db, idbUpgradeTrans); + DexiePromise.follow(function () { return db.on.populate.fire(trans); }).catch(rejectTransaction); + } + else + updateTablesAndIndexes(db, oldVersion, trans, idbUpgradeTrans).catch(rejectTransaction); + }); + } + function updateTablesAndIndexes(_a, oldVersion, trans, idbUpgradeTrans) { + var db = _a._novip; + var queue = []; + var versions = db._versions; + var globalSchema = db._dbSchema = buildGlobalSchema(db, db.idbdb, idbUpgradeTrans); + var anyContentUpgraderHasRun = false; + var versToRun = versions.filter(function (v) { return v._cfg.version >= oldVersion; }); + versToRun.forEach(function (version) { + queue.push(function () { + var oldSchema = globalSchema; + var newSchema = version._cfg.dbschema; + adjustToExistingIndexNames(db, oldSchema, idbUpgradeTrans); + adjustToExistingIndexNames(db, newSchema, idbUpgradeTrans); + globalSchema = db._dbSchema = newSchema; + var diff = getSchemaDiff(oldSchema, newSchema); + diff.add.forEach(function (tuple) { + createTable(idbUpgradeTrans, tuple[0], tuple[1].primKey, tuple[1].indexes); + }); + diff.change.forEach(function (change) { + if (change.recreate) { + throw new exceptions.Upgrade("Not yet support for changing primary key"); + } + else { + var store_1 = idbUpgradeTrans.objectStore(change.name); + change.add.forEach(function (idx) { return addIndex(store_1, idx); }); + change.change.forEach(function (idx) { + store_1.deleteIndex(idx.name); + addIndex(store_1, idx); + }); + change.del.forEach(function (idxName) { return store_1.deleteIndex(idxName); }); + } + }); + var contentUpgrade = version._cfg.contentUpgrade; + if (contentUpgrade && version._cfg.version > oldVersion) { + generateMiddlewareStacks(db, idbUpgradeTrans); + trans._memoizedTables = {}; + anyContentUpgraderHasRun = true; + var upgradeSchema_1 = shallowClone(newSchema); + diff.del.forEach(function (table) { + upgradeSchema_1[table] = oldSchema[table]; + }); + removeTablesApi(db, [db.Transaction.prototype]); + setApiOnPlace(db, [db.Transaction.prototype], keys(upgradeSchema_1), upgradeSchema_1); + trans.schema = upgradeSchema_1; + var contentUpgradeIsAsync_1 = isAsyncFunction(contentUpgrade); + if (contentUpgradeIsAsync_1) { + incrementExpectedAwaits(); + } + var returnValue_1; + var promiseFollowed = DexiePromise.follow(function () { + returnValue_1 = contentUpgrade(trans); + if (returnValue_1) { + if (contentUpgradeIsAsync_1) { + var decrementor = decrementExpectedAwaits.bind(null, null); + returnValue_1.then(decrementor, decrementor); + } + } + }); + return (returnValue_1 && typeof returnValue_1.then === 'function' ? + DexiePromise.resolve(returnValue_1) : promiseFollowed.then(function () { return returnValue_1; })); + } + }); + queue.push(function (idbtrans) { + if (!anyContentUpgraderHasRun || !hasIEDeleteObjectStoreBug) { + var newSchema = version._cfg.dbschema; + deleteRemovedTables(newSchema, idbtrans); + } + removeTablesApi(db, [db.Transaction.prototype]); + setApiOnPlace(db, [db.Transaction.prototype], db._storeNames, db._dbSchema); + trans.schema = db._dbSchema; + }); + }); + function runQueue() { + return queue.length ? DexiePromise.resolve(queue.shift()(trans.idbtrans)).then(runQueue) : + DexiePromise.resolve(); + } + return runQueue().then(function () { + createMissingTables(globalSchema, idbUpgradeTrans); + }); + } + function getSchemaDiff(oldSchema, newSchema) { + var diff = { + del: [], + add: [], + change: [] + }; + var table; + for (table in oldSchema) { + if (!newSchema[table]) + diff.del.push(table); + } + for (table in newSchema) { + var oldDef = oldSchema[table], newDef = newSchema[table]; + if (!oldDef) { + diff.add.push([table, newDef]); + } + else { + var change = { + name: table, + def: newDef, + recreate: false, + del: [], + add: [], + change: [] + }; + if (( + '' + (oldDef.primKey.keyPath || '')) !== ('' + (newDef.primKey.keyPath || '')) || + (oldDef.primKey.auto !== newDef.primKey.auto && !isIEOrEdge)) + { + change.recreate = true; + diff.change.push(change); + } + else { + var oldIndexes = oldDef.idxByName; + var newIndexes = newDef.idxByName; + var idxName = void 0; + for (idxName in oldIndexes) { + if (!newIndexes[idxName]) + change.del.push(idxName); + } + for (idxName in newIndexes) { + var oldIdx = oldIndexes[idxName], newIdx = newIndexes[idxName]; + if (!oldIdx) + change.add.push(newIdx); + else if (oldIdx.src !== newIdx.src) + change.change.push(newIdx); + } + if (change.del.length > 0 || change.add.length > 0 || change.change.length > 0) { + diff.change.push(change); + } + } + } + } + return diff; + } + function createTable(idbtrans, tableName, primKey, indexes) { + var store = idbtrans.db.createObjectStore(tableName, primKey.keyPath ? + { keyPath: primKey.keyPath, autoIncrement: primKey.auto } : + { autoIncrement: primKey.auto }); + indexes.forEach(function (idx) { return addIndex(store, idx); }); + return store; + } + function createMissingTables(newSchema, idbtrans) { + keys(newSchema).forEach(function (tableName) { + if (!idbtrans.db.objectStoreNames.contains(tableName)) { + createTable(idbtrans, tableName, newSchema[tableName].primKey, newSchema[tableName].indexes); + } + }); + } + function deleteRemovedTables(newSchema, idbtrans) { + [].slice.call(idbtrans.db.objectStoreNames).forEach(function (storeName) { + return newSchema[storeName] == null && idbtrans.db.deleteObjectStore(storeName); + }); + } + function addIndex(store, idx) { + store.createIndex(idx.name, idx.keyPath, { unique: idx.unique, multiEntry: idx.multi }); + } + function buildGlobalSchema(db, idbdb, tmpTrans) { + var globalSchema = {}; + var dbStoreNames = slice(idbdb.objectStoreNames, 0); + dbStoreNames.forEach(function (storeName) { + var store = tmpTrans.objectStore(storeName); + var keyPath = store.keyPath; + var primKey = createIndexSpec(nameFromKeyPath(keyPath), keyPath || "", false, false, !!store.autoIncrement, keyPath && typeof keyPath !== "string", true); + var indexes = []; + for (var j = 0; j < store.indexNames.length; ++j) { + var idbindex = store.index(store.indexNames[j]); + keyPath = idbindex.keyPath; + var index = createIndexSpec(idbindex.name, keyPath, !!idbindex.unique, !!idbindex.multiEntry, false, keyPath && typeof keyPath !== "string", false); + indexes.push(index); + } + globalSchema[storeName] = createTableSchema(storeName, primKey, indexes); + }); + return globalSchema; + } + function readGlobalSchema(_a, idbdb, tmpTrans) { + var db = _a._novip; + db.verno = idbdb.version / 10; + var globalSchema = db._dbSchema = buildGlobalSchema(db, idbdb, tmpTrans); + db._storeNames = slice(idbdb.objectStoreNames, 0); + setApiOnPlace(db, [db._allTables], keys(globalSchema), globalSchema); + } + function verifyInstalledSchema(db, tmpTrans) { + var installedSchema = buildGlobalSchema(db, db.idbdb, tmpTrans); + var diff = getSchemaDiff(installedSchema, db._dbSchema); + return !(diff.add.length || diff.change.some(function (ch) { return ch.add.length || ch.change.length; })); + } + function adjustToExistingIndexNames(_a, schema, idbtrans) { + var db = _a._novip; + var storeNames = idbtrans.db.objectStoreNames; + for (var i = 0; i < storeNames.length; ++i) { + var storeName = storeNames[i]; + var store = idbtrans.objectStore(storeName); + db._hasGetAll = 'getAll' in store; + for (var j = 0; j < store.indexNames.length; ++j) { + var indexName = store.indexNames[j]; + var keyPath = store.index(indexName).keyPath; + var dexieName = typeof keyPath === 'string' ? keyPath : "[" + slice(keyPath).join('+') + "]"; + if (schema[storeName]) { + var indexSpec = schema[storeName].idxByName[dexieName]; + if (indexSpec) { + indexSpec.name = indexName; + delete schema[storeName].idxByName[dexieName]; + schema[storeName].idxByName[indexName] = indexSpec; + } + } + } + } + if (typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent) && + !/(Chrome\/|Edge\/)/.test(navigator.userAgent) && + _global.WorkerGlobalScope && _global instanceof _global.WorkerGlobalScope && + [].concat(navigator.userAgent.match(/Safari\/(\d*)/))[1] < 604) { + db._hasGetAll = false; + } + } + function parseIndexSyntax(primKeyAndIndexes) { + return primKeyAndIndexes.split(',').map(function (index, indexNum) { + index = index.trim(); + var name = index.replace(/([&*]|\+\+)/g, ""); + var keyPath = /^\[/.test(name) ? name.match(/^\[(.*)\]$/)[1].split('+') : name; + return createIndexSpec(name, keyPath || null, /\&/.test(index), /\*/.test(index), /\+\+/.test(index), isArray(keyPath), indexNum === 0); + }); + } + + var Version = (function () { + function Version() { + } + Version.prototype._parseStoresSpec = function (stores, outSchema) { + keys(stores).forEach(function (tableName) { + if (stores[tableName] !== null) { + var indexes = parseIndexSyntax(stores[tableName]); + var primKey = indexes.shift(); + if (primKey.multi) + throw new exceptions.Schema("Primary key cannot be multi-valued"); + indexes.forEach(function (idx) { + if (idx.auto) + throw new exceptions.Schema("Only primary key can be marked as autoIncrement (++)"); + if (!idx.keyPath) + throw new exceptions.Schema("Index must have a name and cannot be an empty string"); + }); + outSchema[tableName] = createTableSchema(tableName, primKey, indexes); + } + }); + }; + Version.prototype.stores = function (stores) { + var db = this.db; + this._cfg.storesSource = this._cfg.storesSource ? + extend(this._cfg.storesSource, stores) : + stores; + var versions = db._versions; + var storesSpec = {}; + var dbschema = {}; + versions.forEach(function (version) { + extend(storesSpec, version._cfg.storesSource); + dbschema = (version._cfg.dbschema = {}); + version._parseStoresSpec(storesSpec, dbschema); + }); + db._dbSchema = dbschema; + removeTablesApi(db, [db._allTables, db, db.Transaction.prototype]); + setApiOnPlace(db, [db._allTables, db, db.Transaction.prototype, this._cfg.tables], keys(dbschema), dbschema); + db._storeNames = keys(dbschema); + return this; + }; + Version.prototype.upgrade = function (upgradeFunction) { + this._cfg.contentUpgrade = promisableChain(this._cfg.contentUpgrade || nop, upgradeFunction); + return this; + }; + return Version; + }()); + + function createVersionConstructor(db) { + return makeClassConstructor(Version.prototype, function Version(versionNumber) { + this.db = db; + this._cfg = { + version: versionNumber, + storesSource: null, + dbschema: {}, + tables: {}, + contentUpgrade: null + }; + }); + } + + function getDbNamesTable(indexedDB, IDBKeyRange) { + var dbNamesDB = indexedDB["_dbNamesDB"]; + if (!dbNamesDB) { + dbNamesDB = indexedDB["_dbNamesDB"] = new Dexie$1(DBNAMES_DB, { + addons: [], + indexedDB: indexedDB, + IDBKeyRange: IDBKeyRange, + }); + dbNamesDB.version(1).stores({ dbnames: "name" }); + } + return dbNamesDB.table("dbnames"); + } + function hasDatabasesNative(indexedDB) { + return indexedDB && typeof indexedDB.databases === "function"; + } + function getDatabaseNames(_a) { + var indexedDB = _a.indexedDB, IDBKeyRange = _a.IDBKeyRange; + return hasDatabasesNative(indexedDB) + ? Promise.resolve(indexedDB.databases()).then(function (infos) { + return infos + .map(function (info) { return info.name; }) + .filter(function (name) { return name !== DBNAMES_DB; }); + }) + : getDbNamesTable(indexedDB, IDBKeyRange).toCollection().primaryKeys(); + } + function _onDatabaseCreated(_a, name) { + var indexedDB = _a.indexedDB, IDBKeyRange = _a.IDBKeyRange; + !hasDatabasesNative(indexedDB) && + name !== DBNAMES_DB && + getDbNamesTable(indexedDB, IDBKeyRange).put({ name: name }).catch(nop); + } + function _onDatabaseDeleted(_a, name) { + var indexedDB = _a.indexedDB, IDBKeyRange = _a.IDBKeyRange; + !hasDatabasesNative(indexedDB) && + name !== DBNAMES_DB && + getDbNamesTable(indexedDB, IDBKeyRange).delete(name).catch(nop); + } + + function vip(fn) { + return newScope(function () { + PSD.letThrough = true; + return fn(); + }); + } + + function idbReady() { + var isSafari = !navigator.userAgentData && + /Safari\//.test(navigator.userAgent) && + !/Chrom(e|ium)\//.test(navigator.userAgent); + if (!isSafari || !indexedDB.databases) + return Promise.resolve(); + var intervalId; + return new Promise(function (resolve) { + var tryIdb = function () { return indexedDB.databases().finally(resolve); }; + intervalId = setInterval(tryIdb, 100); + tryIdb(); + }).finally(function () { return clearInterval(intervalId); }); + } + + function dexieOpen(db) { + var state = db._state; + var indexedDB = db._deps.indexedDB; + if (state.isBeingOpened || db.idbdb) + return state.dbReadyPromise.then(function () { return state.dbOpenError ? + rejection(state.dbOpenError) : + db; }); + debug && (state.openCanceller._stackHolder = getErrorWithStack()); + state.isBeingOpened = true; + state.dbOpenError = null; + state.openComplete = false; + var openCanceller = state.openCanceller; + function throwIfCancelled() { + if (state.openCanceller !== openCanceller) + throw new exceptions.DatabaseClosed('db.open() was cancelled'); + } + var resolveDbReady = state.dbReadyResolve, + upgradeTransaction = null, wasCreated = false; + return DexiePromise.race([openCanceller, (typeof navigator === 'undefined' ? DexiePromise.resolve() : idbReady()).then(function () { return new DexiePromise(function (resolve, reject) { + throwIfCancelled(); + if (!indexedDB) + throw new exceptions.MissingAPI(); + var dbName = db.name; + var req = state.autoSchema ? + indexedDB.open(dbName) : + indexedDB.open(dbName, Math.round(db.verno * 10)); + if (!req) + throw new exceptions.MissingAPI(); + req.onerror = eventRejectHandler(reject); + req.onblocked = wrap(db._fireOnBlocked); + req.onupgradeneeded = wrap(function (e) { + upgradeTransaction = req.transaction; + if (state.autoSchema && !db._options.allowEmptyDB) { + req.onerror = preventDefault; + upgradeTransaction.abort(); + req.result.close(); + var delreq = indexedDB.deleteDatabase(dbName); + delreq.onsuccess = delreq.onerror = wrap(function () { + reject(new exceptions.NoSuchDatabase("Database " + dbName + " doesnt exist")); + }); + } + else { + upgradeTransaction.onerror = eventRejectHandler(reject); + var oldVer = e.oldVersion > Math.pow(2, 62) ? 0 : e.oldVersion; + wasCreated = oldVer < 1; + db._novip.idbdb = req.result; + runUpgraders(db, oldVer / 10, upgradeTransaction, reject); + } + }, reject); + req.onsuccess = wrap(function () { + upgradeTransaction = null; + var idbdb = db._novip.idbdb = req.result; + var objectStoreNames = slice(idbdb.objectStoreNames); + if (objectStoreNames.length > 0) + try { + var tmpTrans = idbdb.transaction(safariMultiStoreFix(objectStoreNames), 'readonly'); + if (state.autoSchema) + readGlobalSchema(db, idbdb, tmpTrans); + else { + adjustToExistingIndexNames(db, db._dbSchema, tmpTrans); + if (!verifyInstalledSchema(db, tmpTrans)) { + console.warn("Dexie SchemaDiff: Schema was extended without increasing the number passed to db.version(). Some queries may fail."); + } + } + generateMiddlewareStacks(db, tmpTrans); + } + catch (e) { + } + connections.push(db); + idbdb.onversionchange = wrap(function (ev) { + state.vcFired = true; + db.on("versionchange").fire(ev); + }); + idbdb.onclose = wrap(function (ev) { + db.on("close").fire(ev); + }); + if (wasCreated) + _onDatabaseCreated(db._deps, dbName); + resolve(); + }, reject); + }); })]).then(function () { + throwIfCancelled(); + state.onReadyBeingFired = []; + return DexiePromise.resolve(vip(function () { return db.on.ready.fire(db.vip); })).then(function fireRemainders() { + if (state.onReadyBeingFired.length > 0) { + var remainders_1 = state.onReadyBeingFired.reduce(promisableChain, nop); + state.onReadyBeingFired = []; + return DexiePromise.resolve(vip(function () { return remainders_1(db.vip); })).then(fireRemainders); + } + }); + }).finally(function () { + state.onReadyBeingFired = null; + state.isBeingOpened = false; + }).then(function () { + return db; + }).catch(function (err) { + state.dbOpenError = err; + try { + upgradeTransaction && upgradeTransaction.abort(); + } + catch (_a) { } + if (openCanceller === state.openCanceller) { + db._close(); + } + return rejection(err); + }).finally(function () { + state.openComplete = true; + resolveDbReady(); + }); + } + + function awaitIterator(iterator) { + var callNext = function (result) { return iterator.next(result); }, doThrow = function (error) { return iterator.throw(error); }, onSuccess = step(callNext), onError = step(doThrow); + function step(getNext) { + return function (val) { + var next = getNext(val), value = next.value; + return next.done ? value : + (!value || typeof value.then !== 'function' ? + isArray(value) ? Promise.all(value).then(onSuccess, onError) : onSuccess(value) : + value.then(onSuccess, onError)); + }; + } + return step(callNext)(); + } + + function extractTransactionArgs(mode, _tableArgs_, scopeFunc) { + var i = arguments.length; + if (i < 2) + throw new exceptions.InvalidArgument("Too few arguments"); + var args = new Array(i - 1); + while (--i) + args[i - 1] = arguments[i]; + scopeFunc = args.pop(); + var tables = flatten(args); + return [mode, tables, scopeFunc]; + } + function enterTransactionScope(db, mode, storeNames, parentTransaction, scopeFunc) { + return DexiePromise.resolve().then(function () { + var transless = PSD.transless || PSD; + var trans = db._createTransaction(mode, storeNames, db._dbSchema, parentTransaction); + var zoneProps = { + trans: trans, + transless: transless + }; + if (parentTransaction) { + trans.idbtrans = parentTransaction.idbtrans; + } + else { + try { + trans.create(); + db._state.PR1398_maxLoop = 3; + } + catch (ex) { + if (ex.name === errnames.InvalidState && db.isOpen() && --db._state.PR1398_maxLoop > 0) { + console.warn('Dexie: Need to reopen db'); + db._close(); + return db.open().then(function () { return enterTransactionScope(db, mode, storeNames, null, scopeFunc); }); + } + return rejection(ex); + } + } + var scopeFuncIsAsync = isAsyncFunction(scopeFunc); + if (scopeFuncIsAsync) { + incrementExpectedAwaits(); + } + var returnValue; + var promiseFollowed = DexiePromise.follow(function () { + returnValue = scopeFunc.call(trans, trans); + if (returnValue) { + if (scopeFuncIsAsync) { + var decrementor = decrementExpectedAwaits.bind(null, null); + returnValue.then(decrementor, decrementor); + } + else if (typeof returnValue.next === 'function' && typeof returnValue.throw === 'function') { + returnValue = awaitIterator(returnValue); + } + } + }, zoneProps); + return (returnValue && typeof returnValue.then === 'function' ? + DexiePromise.resolve(returnValue).then(function (x) { return trans.active ? + x + : rejection(new exceptions.PrematureCommit("Transaction committed too early. See http://bit.ly/2kdckMn")); }) + : promiseFollowed.then(function () { return returnValue; })).then(function (x) { + if (parentTransaction) + trans._resolve(); + return trans._completion.then(function () { return x; }); + }).catch(function (e) { + trans._reject(e); + return rejection(e); + }); + }); + } + + function pad(a, value, count) { + var result = isArray(a) ? a.slice() : [a]; + for (var i = 0; i < count; ++i) + result.push(value); + return result; + } + function createVirtualIndexMiddleware(down) { + return __assign(__assign({}, down), { table: function (tableName) { + var table = down.table(tableName); + var schema = table.schema; + var indexLookup = {}; + var allVirtualIndexes = []; + function addVirtualIndexes(keyPath, keyTail, lowLevelIndex) { + var keyPathAlias = getKeyPathAlias(keyPath); + var indexList = (indexLookup[keyPathAlias] = indexLookup[keyPathAlias] || []); + var keyLength = keyPath == null ? 0 : typeof keyPath === 'string' ? 1 : keyPath.length; + var isVirtual = keyTail > 0; + var virtualIndex = __assign(__assign({}, lowLevelIndex), { isVirtual: isVirtual, keyTail: keyTail, keyLength: keyLength, extractKey: getKeyExtractor(keyPath), unique: !isVirtual && lowLevelIndex.unique }); + indexList.push(virtualIndex); + if (!virtualIndex.isPrimaryKey) { + allVirtualIndexes.push(virtualIndex); + } + if (keyLength > 1) { + var virtualKeyPath = keyLength === 2 ? + keyPath[0] : + keyPath.slice(0, keyLength - 1); + addVirtualIndexes(virtualKeyPath, keyTail + 1, lowLevelIndex); + } + indexList.sort(function (a, b) { return a.keyTail - b.keyTail; }); + return virtualIndex; + } + var primaryKey = addVirtualIndexes(schema.primaryKey.keyPath, 0, schema.primaryKey); + indexLookup[":id"] = [primaryKey]; + for (var _i = 0, _a = schema.indexes; _i < _a.length; _i++) { + var index = _a[_i]; + addVirtualIndexes(index.keyPath, 0, index); + } + function findBestIndex(keyPath) { + var result = indexLookup[getKeyPathAlias(keyPath)]; + return result && result[0]; + } + function translateRange(range, keyTail) { + return { + type: range.type === 1 ? + 2 : + range.type, + lower: pad(range.lower, range.lowerOpen ? down.MAX_KEY : down.MIN_KEY, keyTail), + lowerOpen: true, + upper: pad(range.upper, range.upperOpen ? down.MIN_KEY : down.MAX_KEY, keyTail), + upperOpen: true + }; + } + function translateRequest(req) { + var index = req.query.index; + return index.isVirtual ? __assign(__assign({}, req), { query: { + index: index, + range: translateRange(req.query.range, index.keyTail) + } }) : req; + } + var result = __assign(__assign({}, table), { schema: __assign(__assign({}, schema), { primaryKey: primaryKey, indexes: allVirtualIndexes, getIndexByKeyPath: findBestIndex }), count: function (req) { + return table.count(translateRequest(req)); + }, query: function (req) { + return table.query(translateRequest(req)); + }, openCursor: function (req) { + var _a = req.query.index, keyTail = _a.keyTail, isVirtual = _a.isVirtual, keyLength = _a.keyLength; + if (!isVirtual) + return table.openCursor(req); + function createVirtualCursor(cursor) { + function _continue(key) { + key != null ? + cursor.continue(pad(key, req.reverse ? down.MAX_KEY : down.MIN_KEY, keyTail)) : + req.unique ? + cursor.continue(cursor.key.slice(0, keyLength) + .concat(req.reverse + ? down.MIN_KEY + : down.MAX_KEY, keyTail)) : + cursor.continue(); + } + var virtualCursor = Object.create(cursor, { + continue: { value: _continue }, + continuePrimaryKey: { + value: function (key, primaryKey) { + cursor.continuePrimaryKey(pad(key, down.MAX_KEY, keyTail), primaryKey); + } + }, + primaryKey: { + get: function () { + return cursor.primaryKey; + } + }, + key: { + get: function () { + var key = cursor.key; + return keyLength === 1 ? + key[0] : + key.slice(0, keyLength); + } + }, + value: { + get: function () { + return cursor.value; + } + } + }); + return virtualCursor; + } + return table.openCursor(translateRequest(req)) + .then(function (cursor) { return cursor && createVirtualCursor(cursor); }); + } }); + return result; + } }); + } + var virtualIndexMiddleware = { + stack: "dbcore", + name: "VirtualIndexMiddleware", + level: 1, + create: createVirtualIndexMiddleware + }; + + function getObjectDiff(a, b, rv, prfx) { + rv = rv || {}; + prfx = prfx || ''; + keys(a).forEach(function (prop) { + if (!hasOwn(b, prop)) { + rv[prfx + prop] = undefined; + } + else { + var ap = a[prop], bp = b[prop]; + if (typeof ap === 'object' && typeof bp === 'object' && ap && bp) { + var apTypeName = toStringTag(ap); + var bpTypeName = toStringTag(bp); + if (apTypeName !== bpTypeName) { + rv[prfx + prop] = b[prop]; + } + else if (apTypeName === 'Object') { + getObjectDiff(ap, bp, rv, prfx + prop + '.'); + } + else if (ap !== bp) { + rv[prfx + prop] = b[prop]; + } + } + else if (ap !== bp) + rv[prfx + prop] = b[prop]; + } + }); + keys(b).forEach(function (prop) { + if (!hasOwn(a, prop)) { + rv[prfx + prop] = b[prop]; + } + }); + return rv; + } + + function getEffectiveKeys(primaryKey, req) { + if (req.type === 'delete') + return req.keys; + return req.keys || req.values.map(primaryKey.extractKey); + } + + var hooksMiddleware = { + stack: "dbcore", + name: "HooksMiddleware", + level: 2, + create: function (downCore) { return (__assign(__assign({}, downCore), { table: function (tableName) { + var downTable = downCore.table(tableName); + var primaryKey = downTable.schema.primaryKey; + var tableMiddleware = __assign(__assign({}, downTable), { mutate: function (req) { + var dxTrans = PSD.trans; + var _a = dxTrans.table(tableName).hook, deleting = _a.deleting, creating = _a.creating, updating = _a.updating; + switch (req.type) { + case 'add': + if (creating.fire === nop) + break; + return dxTrans._promise('readwrite', function () { return addPutOrDelete(req); }, true); + case 'put': + if (creating.fire === nop && updating.fire === nop) + break; + return dxTrans._promise('readwrite', function () { return addPutOrDelete(req); }, true); + case 'delete': + if (deleting.fire === nop) + break; + return dxTrans._promise('readwrite', function () { return addPutOrDelete(req); }, true); + case 'deleteRange': + if (deleting.fire === nop) + break; + return dxTrans._promise('readwrite', function () { return deleteRange(req); }, true); + } + return downTable.mutate(req); + function addPutOrDelete(req) { + var dxTrans = PSD.trans; + var keys = req.keys || getEffectiveKeys(primaryKey, req); + if (!keys) + throw new Error("Keys missing"); + req = req.type === 'add' || req.type === 'put' ? __assign(__assign({}, req), { keys: keys }) : __assign({}, req); + if (req.type !== 'delete') + req.values = __spreadArray([], req.values, true); + if (req.keys) + req.keys = __spreadArray([], req.keys, true); + return getExistingValues(downTable, req, keys).then(function (existingValues) { + var contexts = keys.map(function (key, i) { + var existingValue = existingValues[i]; + var ctx = { onerror: null, onsuccess: null }; + if (req.type === 'delete') { + deleting.fire.call(ctx, key, existingValue, dxTrans); + } + else if (req.type === 'add' || existingValue === undefined) { + var generatedPrimaryKey = creating.fire.call(ctx, key, req.values[i], dxTrans); + if (key == null && generatedPrimaryKey != null) { + key = generatedPrimaryKey; + req.keys[i] = key; + if (!primaryKey.outbound) { + setByKeyPath(req.values[i], primaryKey.keyPath, key); + } + } + } + else { + var objectDiff = getObjectDiff(existingValue, req.values[i]); + var additionalChanges_1 = updating.fire.call(ctx, objectDiff, key, existingValue, dxTrans); + if (additionalChanges_1) { + var requestedValue_1 = req.values[i]; + Object.keys(additionalChanges_1).forEach(function (keyPath) { + if (hasOwn(requestedValue_1, keyPath)) { + requestedValue_1[keyPath] = additionalChanges_1[keyPath]; + } + else { + setByKeyPath(requestedValue_1, keyPath, additionalChanges_1[keyPath]); + } + }); + } + } + return ctx; + }); + return downTable.mutate(req).then(function (_a) { + var failures = _a.failures, results = _a.results, numFailures = _a.numFailures, lastResult = _a.lastResult; + for (var i = 0; i < keys.length; ++i) { + var primKey = results ? results[i] : keys[i]; + var ctx = contexts[i]; + if (primKey == null) { + ctx.onerror && ctx.onerror(failures[i]); + } + else { + ctx.onsuccess && ctx.onsuccess(req.type === 'put' && existingValues[i] ? + req.values[i] : + primKey + ); + } + } + return { failures: failures, results: results, numFailures: numFailures, lastResult: lastResult }; + }).catch(function (error) { + contexts.forEach(function (ctx) { return ctx.onerror && ctx.onerror(error); }); + return Promise.reject(error); + }); + }); + } + function deleteRange(req) { + return deleteNextChunk(req.trans, req.range, 10000); + } + function deleteNextChunk(trans, range, limit) { + return downTable.query({ trans: trans, values: false, query: { index: primaryKey, range: range }, limit: limit }) + .then(function (_a) { + var result = _a.result; + return addPutOrDelete({ type: 'delete', keys: result, trans: trans }).then(function (res) { + if (res.numFailures > 0) + return Promise.reject(res.failures[0]); + if (result.length < limit) { + return { failures: [], numFailures: 0, lastResult: undefined }; + } + else { + return deleteNextChunk(trans, __assign(__assign({}, range), { lower: result[result.length - 1], lowerOpen: true }), limit); + } + }); + }); + } + } }); + return tableMiddleware; + } })); } + }; + function getExistingValues(table, req, effectiveKeys) { + return req.type === "add" + ? Promise.resolve([]) + : table.getMany({ trans: req.trans, keys: effectiveKeys, cache: "immutable" }); + } + + function getFromTransactionCache(keys, cache, clone) { + try { + if (!cache) + return null; + if (cache.keys.length < keys.length) + return null; + var result = []; + for (var i = 0, j = 0; i < cache.keys.length && j < keys.length; ++i) { + if (cmp(cache.keys[i], keys[j]) !== 0) + continue; + result.push(clone ? deepClone(cache.values[i]) : cache.values[i]); + ++j; + } + return result.length === keys.length ? result : null; + } + catch (_a) { + return null; + } + } + var cacheExistingValuesMiddleware = { + stack: "dbcore", + level: -1, + create: function (core) { + return { + table: function (tableName) { + var table = core.table(tableName); + return __assign(__assign({}, table), { getMany: function (req) { + if (!req.cache) { + return table.getMany(req); + } + var cachedResult = getFromTransactionCache(req.keys, req.trans["_cache"], req.cache === "clone"); + if (cachedResult) { + return DexiePromise.resolve(cachedResult); + } + return table.getMany(req).then(function (res) { + req.trans["_cache"] = { + keys: req.keys, + values: req.cache === "clone" ? deepClone(res) : res, + }; + return res; + }); + }, mutate: function (req) { + if (req.type !== "add") + req.trans["_cache"] = null; + return table.mutate(req); + } }); + }, + }; + }, + }; + + var _a; + function isEmptyRange(node) { + return !("from" in node); + } + var RangeSet = function (fromOrTree, to) { + if (this) { + extend(this, arguments.length ? { d: 1, from: fromOrTree, to: arguments.length > 1 ? to : fromOrTree } : { d: 0 }); + } + else { + var rv = new RangeSet(); + if (fromOrTree && ("d" in fromOrTree)) { + extend(rv, fromOrTree); + } + return rv; + } + }; + props(RangeSet.prototype, (_a = { + add: function (rangeSet) { + mergeRanges(this, rangeSet); + return this; + }, + addKey: function (key) { + addRange(this, key, key); + return this; + }, + addKeys: function (keys) { + var _this = this; + keys.forEach(function (key) { return addRange(_this, key, key); }); + return this; + } + }, + _a[iteratorSymbol] = function () { + return getRangeSetIterator(this); + }, + _a)); + function addRange(target, from, to) { + var diff = cmp(from, to); + if (isNaN(diff)) + return; + if (diff > 0) + throw RangeError(); + if (isEmptyRange(target)) + return extend(target, { from: from, to: to, d: 1 }); + var left = target.l; + var right = target.r; + if (cmp(to, target.from) < 0) { + left + ? addRange(left, from, to) + : (target.l = { from: from, to: to, d: 1, l: null, r: null }); + return rebalance(target); + } + if (cmp(from, target.to) > 0) { + right + ? addRange(right, from, to) + : (target.r = { from: from, to: to, d: 1, l: null, r: null }); + return rebalance(target); + } + if (cmp(from, target.from) < 0) { + target.from = from; + target.l = null; + target.d = right ? right.d + 1 : 1; + } + if (cmp(to, target.to) > 0) { + target.to = to; + target.r = null; + target.d = target.l ? target.l.d + 1 : 1; + } + var rightWasCutOff = !target.r; + if (left && !target.l) { + mergeRanges(target, left); + } + if (right && rightWasCutOff) { + mergeRanges(target, right); + } + } + function mergeRanges(target, newSet) { + function _addRangeSet(target, _a) { + var from = _a.from, to = _a.to, l = _a.l, r = _a.r; + addRange(target, from, to); + if (l) + _addRangeSet(target, l); + if (r) + _addRangeSet(target, r); + } + if (!isEmptyRange(newSet)) + _addRangeSet(target, newSet); + } + function rangesOverlap(rangeSet1, rangeSet2) { + var i1 = getRangeSetIterator(rangeSet2); + var nextResult1 = i1.next(); + if (nextResult1.done) + return false; + var a = nextResult1.value; + var i2 = getRangeSetIterator(rangeSet1); + var nextResult2 = i2.next(a.from); + var b = nextResult2.value; + while (!nextResult1.done && !nextResult2.done) { + if (cmp(b.from, a.to) <= 0 && cmp(b.to, a.from) >= 0) + return true; + cmp(a.from, b.from) < 0 + ? (a = (nextResult1 = i1.next(b.from)).value) + : (b = (nextResult2 = i2.next(a.from)).value); + } + return false; + } + function getRangeSetIterator(node) { + var state = isEmptyRange(node) ? null : { s: 0, n: node }; + return { + next: function (key) { + var keyProvided = arguments.length > 0; + while (state) { + switch (state.s) { + case 0: + state.s = 1; + if (keyProvided) { + while (state.n.l && cmp(key, state.n.from) < 0) + state = { up: state, n: state.n.l, s: 1 }; + } + else { + while (state.n.l) + state = { up: state, n: state.n.l, s: 1 }; + } + case 1: + state.s = 2; + if (!keyProvided || cmp(key, state.n.to) <= 0) + return { value: state.n, done: false }; + case 2: + if (state.n.r) { + state.s = 3; + state = { up: state, n: state.n.r, s: 0 }; + continue; + } + case 3: + state = state.up; + } + } + return { done: true }; + }, + }; + } + function rebalance(target) { + var _a, _b; + var diff = (((_a = target.r) === null || _a === void 0 ? void 0 : _a.d) || 0) - (((_b = target.l) === null || _b === void 0 ? void 0 : _b.d) || 0); + var r = diff > 1 ? "r" : diff < -1 ? "l" : ""; + if (r) { + var l = r === "r" ? "l" : "r"; + var rootClone = __assign({}, target); + var oldRootRight = target[r]; + target.from = oldRootRight.from; + target.to = oldRootRight.to; + target[r] = oldRootRight[r]; + rootClone[r] = oldRootRight[l]; + target[l] = rootClone; + rootClone.d = computeDepth(rootClone); + } + target.d = computeDepth(target); + } + function computeDepth(_a) { + var r = _a.r, l = _a.l; + return (r ? (l ? Math.max(r.d, l.d) : r.d) : l ? l.d : 0) + 1; + } + + var observabilityMiddleware = { + stack: "dbcore", + level: 0, + create: function (core) { + var dbName = core.schema.name; + var FULL_RANGE = new RangeSet(core.MIN_KEY, core.MAX_KEY); + return __assign(__assign({}, core), { table: function (tableName) { + var table = core.table(tableName); + var schema = table.schema; + var primaryKey = schema.primaryKey; + var extractKey = primaryKey.extractKey, outbound = primaryKey.outbound; + var tableClone = __assign(__assign({}, table), { mutate: function (req) { + var trans = req.trans; + var mutatedParts = trans.mutatedParts || (trans.mutatedParts = {}); + var getRangeSet = function (indexName) { + var part = "idb://" + dbName + "/" + tableName + "/" + indexName; + return (mutatedParts[part] || + (mutatedParts[part] = new RangeSet())); + }; + var pkRangeSet = getRangeSet(""); + var delsRangeSet = getRangeSet(":dels"); + var type = req.type; + var _a = req.type === "deleteRange" + ? [req.range] + : req.type === "delete" + ? [req.keys] + : req.values.length < 50 + ? [[], req.values] + : [], keys = _a[0], newObjs = _a[1]; + var oldCache = req.trans["_cache"]; + return table.mutate(req).then(function (res) { + if (isArray(keys)) { + if (type !== "delete") + keys = res.results; + pkRangeSet.addKeys(keys); + var oldObjs = getFromTransactionCache(keys, oldCache); + if (!oldObjs && type !== "add") { + delsRangeSet.addKeys(keys); + } + if (oldObjs || newObjs) { + trackAffectedIndexes(getRangeSet, schema, oldObjs, newObjs); + } + } + else if (keys) { + var range = { from: keys.lower, to: keys.upper }; + delsRangeSet.add(range); + pkRangeSet.add(range); + } + else { + pkRangeSet.add(FULL_RANGE); + delsRangeSet.add(FULL_RANGE); + schema.indexes.forEach(function (idx) { return getRangeSet(idx.name).add(FULL_RANGE); }); + } + return res; + }); + } }); + var getRange = function (_a) { + var _b, _c; + var _d = _a.query, index = _d.index, range = _d.range; + return [ + index, + new RangeSet((_b = range.lower) !== null && _b !== void 0 ? _b : core.MIN_KEY, (_c = range.upper) !== null && _c !== void 0 ? _c : core.MAX_KEY), + ]; + }; + var readSubscribers = { + get: function (req) { return [primaryKey, new RangeSet(req.key)]; }, + getMany: function (req) { return [primaryKey, new RangeSet().addKeys(req.keys)]; }, + count: getRange, + query: getRange, + openCursor: getRange, + }; + keys(readSubscribers).forEach(function (method) { + tableClone[method] = function (req) { + var subscr = PSD.subscr; + if (subscr) { + var getRangeSet = function (indexName) { + var part = "idb://" + dbName + "/" + tableName + "/" + indexName; + return (subscr[part] || + (subscr[part] = new RangeSet())); + }; + var pkRangeSet_1 = getRangeSet(""); + var delsRangeSet_1 = getRangeSet(":dels"); + var _a = readSubscribers[method](req), queriedIndex = _a[0], queriedRanges = _a[1]; + getRangeSet(queriedIndex.name || "").add(queriedRanges); + if (!queriedIndex.isPrimaryKey) { + if (method === "count") { + delsRangeSet_1.add(FULL_RANGE); + } + else { + var keysPromise_1 = method === "query" && + outbound && + req.values && + table.query(__assign(__assign({}, req), { values: false })); + return table[method].apply(this, arguments).then(function (res) { + if (method === "query") { + if (outbound && req.values) { + return keysPromise_1.then(function (_a) { + var resultingKeys = _a.result; + pkRangeSet_1.addKeys(resultingKeys); + return res; + }); + } + var pKeys = req.values + ? res.result.map(extractKey) + : res.result; + if (req.values) { + pkRangeSet_1.addKeys(pKeys); + } + else { + delsRangeSet_1.addKeys(pKeys); + } + } + else if (method === "openCursor") { + var cursor_1 = res; + var wantValues_1 = req.values; + return (cursor_1 && + Object.create(cursor_1, { + key: { + get: function () { + delsRangeSet_1.addKey(cursor_1.primaryKey); + return cursor_1.key; + }, + }, + primaryKey: { + get: function () { + var pkey = cursor_1.primaryKey; + delsRangeSet_1.addKey(pkey); + return pkey; + }, + }, + value: { + get: function () { + wantValues_1 && pkRangeSet_1.addKey(cursor_1.primaryKey); + return cursor_1.value; + }, + }, + })); + } + return res; + }); + } + } + } + return table[method].apply(this, arguments); + }; + }); + return tableClone; + } }); + }, + }; + function trackAffectedIndexes(getRangeSet, schema, oldObjs, newObjs) { + function addAffectedIndex(ix) { + var rangeSet = getRangeSet(ix.name || ""); + function extractKey(obj) { + return obj != null ? ix.extractKey(obj) : null; + } + var addKeyOrKeys = function (key) { return ix.multiEntry && isArray(key) + ? key.forEach(function (key) { return rangeSet.addKey(key); }) + : rangeSet.addKey(key); }; + (oldObjs || newObjs).forEach(function (_, i) { + var oldKey = oldObjs && extractKey(oldObjs[i]); + var newKey = newObjs && extractKey(newObjs[i]); + if (cmp(oldKey, newKey) !== 0) { + if (oldKey != null) + addKeyOrKeys(oldKey); + if (newKey != null) + addKeyOrKeys(newKey); + } + }); + } + schema.indexes.forEach(addAffectedIndex); + } + + var Dexie$1 = (function () { + function Dexie(name, options) { + var _this = this; + this._middlewares = {}; + this.verno = 0; + var deps = Dexie.dependencies; + this._options = options = __assign({ + addons: Dexie.addons, autoOpen: true, + indexedDB: deps.indexedDB, IDBKeyRange: deps.IDBKeyRange }, options); + this._deps = { + indexedDB: options.indexedDB, + IDBKeyRange: options.IDBKeyRange + }; + var addons = options.addons; + this._dbSchema = {}; + this._versions = []; + this._storeNames = []; + this._allTables = {}; + this.idbdb = null; + this._novip = this; + var state = { + dbOpenError: null, + isBeingOpened: false, + onReadyBeingFired: null, + openComplete: false, + dbReadyResolve: nop, + dbReadyPromise: null, + cancelOpen: nop, + openCanceller: null, + autoSchema: true, + PR1398_maxLoop: 3 + }; + state.dbReadyPromise = new DexiePromise(function (resolve) { + state.dbReadyResolve = resolve; + }); + state.openCanceller = new DexiePromise(function (_, reject) { + state.cancelOpen = reject; + }); + this._state = state; + this.name = name; + this.on = Events(this, "populate", "blocked", "versionchange", "close", { ready: [promisableChain, nop] }); + this.on.ready.subscribe = override(this.on.ready.subscribe, function (subscribe) { + return function (subscriber, bSticky) { + Dexie.vip(function () { + var state = _this._state; + if (state.openComplete) { + if (!state.dbOpenError) + DexiePromise.resolve().then(subscriber); + if (bSticky) + subscribe(subscriber); + } + else if (state.onReadyBeingFired) { + state.onReadyBeingFired.push(subscriber); + if (bSticky) + subscribe(subscriber); + } + else { + subscribe(subscriber); + var db_1 = _this; + if (!bSticky) + subscribe(function unsubscribe() { + db_1.on.ready.unsubscribe(subscriber); + db_1.on.ready.unsubscribe(unsubscribe); + }); + } + }); + }; + }); + this.Collection = createCollectionConstructor(this); + this.Table = createTableConstructor(this); + this.Transaction = createTransactionConstructor(this); + this.Version = createVersionConstructor(this); + this.WhereClause = createWhereClauseConstructor(this); + this.on("versionchange", function (ev) { + if (ev.newVersion > 0) + console.warn("Another connection wants to upgrade database '" + _this.name + "'. Closing db now to resume the upgrade."); + else + console.warn("Another connection wants to delete database '" + _this.name + "'. Closing db now to resume the delete request."); + _this.close(); + }); + this.on("blocked", function (ev) { + if (!ev.newVersion || ev.newVersion < ev.oldVersion) + console.warn("Dexie.delete('" + _this.name + "') was blocked"); + else + console.warn("Upgrade '" + _this.name + "' blocked by other connection holding version " + ev.oldVersion / 10); + }); + this._maxKey = getMaxKey(options.IDBKeyRange); + this._createTransaction = function (mode, storeNames, dbschema, parentTransaction) { return new _this.Transaction(mode, storeNames, dbschema, _this._options.chromeTransactionDurability, parentTransaction); }; + this._fireOnBlocked = function (ev) { + _this.on("blocked").fire(ev); + connections + .filter(function (c) { return c.name === _this.name && c !== _this && !c._state.vcFired; }) + .map(function (c) { return c.on("versionchange").fire(ev); }); + }; + this.use(virtualIndexMiddleware); + this.use(hooksMiddleware); + this.use(observabilityMiddleware); + this.use(cacheExistingValuesMiddleware); + this.vip = Object.create(this, { _vip: { value: true } }); + addons.forEach(function (addon) { return addon(_this); }); + } + Dexie.prototype.version = function (versionNumber) { + if (isNaN(versionNumber) || versionNumber < 0.1) + throw new exceptions.Type("Given version is not a positive number"); + versionNumber = Math.round(versionNumber * 10) / 10; + if (this.idbdb || this._state.isBeingOpened) + throw new exceptions.Schema("Cannot add version when database is open"); + this.verno = Math.max(this.verno, versionNumber); + var versions = this._versions; + var versionInstance = versions.filter(function (v) { return v._cfg.version === versionNumber; })[0]; + if (versionInstance) + return versionInstance; + versionInstance = new this.Version(versionNumber); + versions.push(versionInstance); + versions.sort(lowerVersionFirst); + versionInstance.stores({}); + this._state.autoSchema = false; + return versionInstance; + }; + Dexie.prototype._whenReady = function (fn) { + var _this = this; + return (this.idbdb && (this._state.openComplete || PSD.letThrough || this._vip)) ? fn() : new DexiePromise(function (resolve, reject) { + if (_this._state.openComplete) { + return reject(new exceptions.DatabaseClosed(_this._state.dbOpenError)); + } + if (!_this._state.isBeingOpened) { + if (!_this._options.autoOpen) { + reject(new exceptions.DatabaseClosed()); + return; + } + _this.open().catch(nop); + } + _this._state.dbReadyPromise.then(resolve, reject); + }).then(fn); + }; + Dexie.prototype.use = function (_a) { + var stack = _a.stack, create = _a.create, level = _a.level, name = _a.name; + if (name) + this.unuse({ stack: stack, name: name }); + var middlewares = this._middlewares[stack] || (this._middlewares[stack] = []); + middlewares.push({ stack: stack, create: create, level: level == null ? 10 : level, name: name }); + middlewares.sort(function (a, b) { return a.level - b.level; }); + return this; + }; + Dexie.prototype.unuse = function (_a) { + var stack = _a.stack, name = _a.name, create = _a.create; + if (stack && this._middlewares[stack]) { + this._middlewares[stack] = this._middlewares[stack].filter(function (mw) { + return create ? mw.create !== create : + name ? mw.name !== name : + false; + }); + } + return this; + }; + Dexie.prototype.open = function () { + return dexieOpen(this); + }; + Dexie.prototype._close = function () { + var state = this._state; + var idx = connections.indexOf(this); + if (idx >= 0) + connections.splice(idx, 1); + if (this.idbdb) { + try { + this.idbdb.close(); + } + catch (e) { } + this._novip.idbdb = null; + } + state.dbReadyPromise = new DexiePromise(function (resolve) { + state.dbReadyResolve = resolve; + }); + state.openCanceller = new DexiePromise(function (_, reject) { + state.cancelOpen = reject; + }); + }; + Dexie.prototype.close = function () { + this._close(); + var state = this._state; + this._options.autoOpen = false; + state.dbOpenError = new exceptions.DatabaseClosed(); + if (state.isBeingOpened) + state.cancelOpen(state.dbOpenError); + }; + Dexie.prototype.delete = function () { + var _this = this; + var hasArguments = arguments.length > 0; + var state = this._state; + return new DexiePromise(function (resolve, reject) { + var doDelete = function () { + _this.close(); + var req = _this._deps.indexedDB.deleteDatabase(_this.name); + req.onsuccess = wrap(function () { + _onDatabaseDeleted(_this._deps, _this.name); + resolve(); + }); + req.onerror = eventRejectHandler(reject); + req.onblocked = _this._fireOnBlocked; + }; + if (hasArguments) + throw new exceptions.InvalidArgument("Arguments not allowed in db.delete()"); + if (state.isBeingOpened) { + state.dbReadyPromise.then(doDelete); + } + else { + doDelete(); + } + }); + }; + Dexie.prototype.backendDB = function () { + return this.idbdb; + }; + Dexie.prototype.isOpen = function () { + return this.idbdb !== null; + }; + Dexie.prototype.hasBeenClosed = function () { + var dbOpenError = this._state.dbOpenError; + return dbOpenError && (dbOpenError.name === 'DatabaseClosed'); + }; + Dexie.prototype.hasFailed = function () { + return this._state.dbOpenError !== null; + }; + Dexie.prototype.dynamicallyOpened = function () { + return this._state.autoSchema; + }; + Object.defineProperty(Dexie.prototype, "tables", { + get: function () { + var _this = this; + return keys(this._allTables).map(function (name) { return _this._allTables[name]; }); + }, + enumerable: false, + configurable: true + }); + Dexie.prototype.transaction = function () { + var args = extractTransactionArgs.apply(this, arguments); + return this._transaction.apply(this, args); + }; + Dexie.prototype._transaction = function (mode, tables, scopeFunc) { + var _this = this; + var parentTransaction = PSD.trans; + if (!parentTransaction || parentTransaction.db !== this || mode.indexOf('!') !== -1) + parentTransaction = null; + var onlyIfCompatible = mode.indexOf('?') !== -1; + mode = mode.replace('!', '').replace('?', ''); + var idbMode, storeNames; + try { + storeNames = tables.map(function (table) { + var storeName = table instanceof _this.Table ? table.name : table; + if (typeof storeName !== 'string') + throw new TypeError("Invalid table argument to Dexie.transaction(). Only Table or String are allowed"); + return storeName; + }); + if (mode == "r" || mode === READONLY) + idbMode = READONLY; + else if (mode == "rw" || mode == READWRITE) + idbMode = READWRITE; + else + throw new exceptions.InvalidArgument("Invalid transaction mode: " + mode); + if (parentTransaction) { + if (parentTransaction.mode === READONLY && idbMode === READWRITE) { + if (onlyIfCompatible) { + parentTransaction = null; + } + else + throw new exceptions.SubTransaction("Cannot enter a sub-transaction with READWRITE mode when parent transaction is READONLY"); + } + if (parentTransaction) { + storeNames.forEach(function (storeName) { + if (parentTransaction && parentTransaction.storeNames.indexOf(storeName) === -1) { + if (onlyIfCompatible) { + parentTransaction = null; + } + else + throw new exceptions.SubTransaction("Table " + storeName + + " not included in parent transaction."); + } + }); + } + if (onlyIfCompatible && parentTransaction && !parentTransaction.active) { + parentTransaction = null; + } + } + } + catch (e) { + return parentTransaction ? + parentTransaction._promise(null, function (_, reject) { reject(e); }) : + rejection(e); + } + var enterTransaction = enterTransactionScope.bind(null, this, idbMode, storeNames, parentTransaction, scopeFunc); + return (parentTransaction ? + parentTransaction._promise(idbMode, enterTransaction, "lock") : + PSD.trans ? + usePSD(PSD.transless, function () { return _this._whenReady(enterTransaction); }) : + this._whenReady(enterTransaction)); + }; + Dexie.prototype.table = function (tableName) { + if (!hasOwn(this._allTables, tableName)) { + throw new exceptions.InvalidTable("Table " + tableName + " does not exist"); + } + return this._allTables[tableName]; + }; + return Dexie; + }()); + + var symbolObservable = typeof Symbol !== "undefined" && "observable" in Symbol + ? Symbol.observable + : "@@observable"; + var Observable = (function () { + function Observable(subscribe) { + this._subscribe = subscribe; + } + Observable.prototype.subscribe = function (x, error, complete) { + return this._subscribe(!x || typeof x === "function" ? { next: x, error: error, complete: complete } : x); + }; + Observable.prototype[symbolObservable] = function () { + return this; + }; + return Observable; + }()); + + function extendObservabilitySet(target, newSet) { + keys(newSet).forEach(function (part) { + var rangeSet = target[part] || (target[part] = new RangeSet()); + mergeRanges(rangeSet, newSet[part]); + }); + return target; + } + + function liveQuery(querier) { + var hasValue = false; + var currentValue = undefined; + var observable = new Observable(function (observer) { + var scopeFuncIsAsync = isAsyncFunction(querier); + function execute(subscr) { + if (scopeFuncIsAsync) { + incrementExpectedAwaits(); + } + var exec = function () { return newScope(querier, { subscr: subscr, trans: null }); }; + var rv = PSD.trans + ? + usePSD(PSD.transless, exec) + : exec(); + if (scopeFuncIsAsync) { + rv.then(decrementExpectedAwaits, decrementExpectedAwaits); + } + return rv; + } + var closed = false; + var accumMuts = {}; + var currentObs = {}; + var subscription = { + get closed() { + return closed; + }, + unsubscribe: function () { + closed = true; + globalEvents.storagemutated.unsubscribe(mutationListener); + }, + }; + observer.start && observer.start(subscription); + var querying = false, startedListening = false; + function shouldNotify() { + return keys(currentObs).some(function (key) { + return accumMuts[key] && rangesOverlap(accumMuts[key], currentObs[key]); + }); + } + var mutationListener = function (parts) { + extendObservabilitySet(accumMuts, parts); + if (shouldNotify()) { + doQuery(); + } + }; + var doQuery = function () { + if (querying || closed) + return; + accumMuts = {}; + var subscr = {}; + var ret = execute(subscr); + if (!startedListening) { + globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, mutationListener); + startedListening = true; + } + querying = true; + Promise.resolve(ret).then(function (result) { + hasValue = true; + currentValue = result; + querying = false; + if (closed) + return; + if (shouldNotify()) { + doQuery(); + } + else { + accumMuts = {}; + currentObs = subscr; + observer.next && observer.next(result); + } + }, function (err) { + querying = false; + hasValue = false; + observer.error && observer.error(err); + subscription.unsubscribe(); + }); + }; + doQuery(); + return subscription; + }); + observable.hasValue = function () { return hasValue; }; + observable.getValue = function () { return currentValue; }; + return observable; + } + + var domDeps; + try { + domDeps = { + indexedDB: _global.indexedDB || _global.mozIndexedDB || _global.webkitIndexedDB || _global.msIndexedDB, + IDBKeyRange: _global.IDBKeyRange || _global.webkitIDBKeyRange + }; + } + catch (e) { + domDeps = { indexedDB: null, IDBKeyRange: null }; + } + + var Dexie = Dexie$1; + props(Dexie, __assign(__assign({}, fullNameExceptions), { + delete: function (databaseName) { + var db = new Dexie(databaseName, { addons: [] }); + return db.delete(); + }, + exists: function (name) { + return new Dexie(name, { addons: [] }).open().then(function (db) { + db.close(); + return true; + }).catch('NoSuchDatabaseError', function () { return false; }); + }, + getDatabaseNames: function (cb) { + try { + return getDatabaseNames(Dexie.dependencies).then(cb); + } + catch (_a) { + return rejection(new exceptions.MissingAPI()); + } + }, + defineClass: function () { + function Class(content) { + extend(this, content); + } + return Class; + }, ignoreTransaction: function (scopeFunc) { + return PSD.trans ? + usePSD(PSD.transless, scopeFunc) : + scopeFunc(); + }, vip: vip, async: function (generatorFn) { + return function () { + try { + var rv = awaitIterator(generatorFn.apply(this, arguments)); + if (!rv || typeof rv.then !== 'function') + return DexiePromise.resolve(rv); + return rv; + } + catch (e) { + return rejection(e); + } + }; + }, spawn: function (generatorFn, args, thiz) { + try { + var rv = awaitIterator(generatorFn.apply(thiz, args || [])); + if (!rv || typeof rv.then !== 'function') + return DexiePromise.resolve(rv); + return rv; + } + catch (e) { + return rejection(e); + } + }, + currentTransaction: { + get: function () { return PSD.trans || null; } + }, waitFor: function (promiseOrFunction, optionalTimeout) { + var promise = DexiePromise.resolve(typeof promiseOrFunction === 'function' ? + Dexie.ignoreTransaction(promiseOrFunction) : + promiseOrFunction) + .timeout(optionalTimeout || 60000); + return PSD.trans ? + PSD.trans.waitFor(promise) : + promise; + }, + Promise: DexiePromise, + debug: { + get: function () { return debug; }, + set: function (value) { + setDebug(value, value === 'dexie' ? function () { return true; } : dexieStackFrameFilter); + } + }, + derive: derive, extend: extend, props: props, override: override, + Events: Events, on: globalEvents, liveQuery: liveQuery, extendObservabilitySet: extendObservabilitySet, + getByKeyPath: getByKeyPath, setByKeyPath: setByKeyPath, delByKeyPath: delByKeyPath, shallowClone: shallowClone, deepClone: deepClone, getObjectDiff: getObjectDiff, cmp: cmp, asap: asap$1, + minKey: minKey, + addons: [], + connections: connections, + errnames: errnames, + dependencies: domDeps, + semVer: DEXIE_VERSION, version: DEXIE_VERSION.split('.') + .map(function (n) { return parseInt(n); }) + .reduce(function (p, c, i) { return p + (c / Math.pow(10, i * 2)); }) })); + Dexie.maxKey = getMaxKey(Dexie.dependencies.IDBKeyRange); + + if (typeof dispatchEvent !== 'undefined' && typeof addEventListener !== 'undefined') { + globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, function (updatedParts) { + if (!propagatingLocally) { + var event_1; + if (isIEOrEdge) { + event_1 = document.createEvent('CustomEvent'); + event_1.initCustomEvent(STORAGE_MUTATED_DOM_EVENT_NAME, true, true, updatedParts); + } + else { + event_1 = new CustomEvent(STORAGE_MUTATED_DOM_EVENT_NAME, { + detail: updatedParts + }); + } + propagatingLocally = true; + dispatchEvent(event_1); + propagatingLocally = false; + } + }); + addEventListener(STORAGE_MUTATED_DOM_EVENT_NAME, function (_a) { + var detail = _a.detail; + if (!propagatingLocally) { + propagateLocally(detail); + } + }); + } + function propagateLocally(updateParts) { + var wasMe = propagatingLocally; + try { + propagatingLocally = true; + globalEvents.storagemutated.fire(updateParts); + } + finally { + propagatingLocally = wasMe; + } + } + var propagatingLocally = false; + + if (typeof BroadcastChannel !== 'undefined') { + var bc_1 = new BroadcastChannel(STORAGE_MUTATED_DOM_EVENT_NAME); + if (typeof bc_1.unref === 'function') { + bc_1.unref(); + } + globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, function (changedParts) { + if (!propagatingLocally) { + bc_1.postMessage(changedParts); + } + }); + bc_1.onmessage = function (ev) { + if (ev.data) + propagateLocally(ev.data); + }; + } + else if (typeof self !== 'undefined' && typeof navigator !== 'undefined') { + globalEvents(DEXIE_STORAGE_MUTATED_EVENT_NAME, function (changedParts) { + try { + if (!propagatingLocally) { + if (typeof localStorage !== 'undefined') { + localStorage.setItem(STORAGE_MUTATED_DOM_EVENT_NAME, JSON.stringify({ + trig: Math.random(), + changedParts: changedParts, + })); + } + if (typeof self['clients'] === 'object') { + __spreadArray([], self['clients'].matchAll({ includeUncontrolled: true }), true).forEach(function (client) { + return client.postMessage({ + type: STORAGE_MUTATED_DOM_EVENT_NAME, + changedParts: changedParts, + }); + }); + } + } + } + catch (_a) { } + }); + if (typeof addEventListener !== 'undefined') { + addEventListener('storage', function (ev) { + if (ev.key === STORAGE_MUTATED_DOM_EVENT_NAME) { + var data = JSON.parse(ev.newValue); + if (data) + propagateLocally(data.changedParts); + } + }); + } + var swContainer = self.document && navigator.serviceWorker; + if (swContainer) { + swContainer.addEventListener('message', propagateMessageLocally); + } + } + function propagateMessageLocally(_a) { + var data = _a.data; + if (data && data.type === STORAGE_MUTATED_DOM_EVENT_NAME) { + propagateLocally(data.changedParts); + } + } + + DexiePromise.rejectionMapper = mapError; + setDebug(debug, dexieStackFrameFilter); + + var namedExports = /*#__PURE__*/Object.freeze({ + __proto__: null, + Dexie: Dexie$1, + liveQuery: liveQuery, + 'default': Dexie$1, + RangeSet: RangeSet, + mergeRanges: mergeRanges, + rangesOverlap: rangesOverlap + }); + + __assign(Dexie$1, namedExports, { default: Dexie$1 }); + + return Dexie$1; + +})); +//# sourceMappingURL=dexie.js.map diff --git a/src/lib/rlottie/RLottie.ts b/src/lib/rlottie/RLottie.ts index 4a6c8fc4..bd9fb720 100644 --- a/src/lib/rlottie/RLottie.ts +++ b/src/lib/rlottie/RLottie.ts @@ -159,6 +159,8 @@ class RLottie { } pause(viewId?: string) { + this.lastRenderAt = undefined; + if (viewId) { this.views.get(viewId)!.isPaused = true; @@ -492,7 +494,7 @@ class RLottie { const now = Date.now(); const currentSpeed = this.lastRenderAt ? this.msPerFrame / (now - this.lastRenderAt) : 1; - const delta = Math.min(1, (this.direction * this.speed) / currentSpeed); + const delta = (this.direction * this.speed) / currentSpeed; const expectedNextFrameIndex = Math.round(this.approxFrameIndex + delta); this.lastRenderAt = now; diff --git a/src/lib/teact/dom-events.ts b/src/lib/teact/dom-events.ts index f303fba1..605b7f49 100644 --- a/src/lib/teact/dom-events.ts +++ b/src/lib/teact/dom-events.ts @@ -1,15 +1,15 @@ import { DEBUG } from '../../config'; type Handler = (e: Event) => void; -type DelegationRegistry = Map; +type DelegationRegistry = Map; const NON_BUBBLEABLE_EVENTS = new Set(['scroll', 'mouseenter', 'mouseleave', 'load']); const documentEventCounters: Record = {}; const delegationRegistryByEventType: Record = {}; -const delegatedEventTypesByElement = new Map>(); +const delegatedEventTypesByElement = new Map>(); -export function addEventListener(element: HTMLElement, propName: string, handler: Handler, asCapture = false) { +export function addEventListener(element: Element, propName: string, handler: Handler, asCapture = false) { const eventType = resolveEventType(propName, element); if (canUseEventDelegation(eventType, element, asCapture)) { addDelegatedListener(eventType, element, handler); @@ -18,7 +18,7 @@ export function addEventListener(element: HTMLElement, propName: string, handler } } -export function removeEventListener(element: HTMLElement, propName: string, handler: Handler, asCapture = false) { +export function removeEventListener(element: Element, propName: string, handler: Handler, asCapture = false) { const eventType = resolveEventType(propName, element); if (canUseEventDelegation(eventType, element, asCapture)) { removeDelegatedListener(eventType, element); @@ -27,7 +27,7 @@ export function removeEventListener(element: HTMLElement, propName: string, hand } } -function resolveEventType(propName: string, element: HTMLElement) { +function resolveEventType(propName: string, element: Element) { const eventType = propName .replace(/^on/, '') .replace(/Capture$/, '').toLowerCase(); @@ -54,7 +54,7 @@ function resolveEventType(propName: string, element: HTMLElement) { return eventType; } -function canUseEventDelegation(realEventType: string, element: HTMLElement, asCapture: boolean) { +function canUseEventDelegation(realEventType: string, element: Element, asCapture: boolean) { return ( !asCapture && !NON_BUBBLEABLE_EVENTS.has(realEventType) @@ -63,7 +63,7 @@ function canUseEventDelegation(realEventType: string, element: HTMLElement, asCa ); } -function addDelegatedListener(eventType: string, element: HTMLElement, handler: Handler) { +function addDelegatedListener(eventType: string, element: Element, handler: Handler) { if (!documentEventCounters[eventType]) { documentEventCounters[eventType] = 0; document.addEventListener(eventType, handleEvent); @@ -74,7 +74,7 @@ function addDelegatedListener(eventType: string, element: HTMLElement, handler: documentEventCounters[eventType]++; } -function removeDelegatedListener(eventType: string, element: HTMLElement) { +function removeDelegatedListener(eventType: string, element: Element) { documentEventCounters[eventType]--; if (!documentEventCounters[eventType]) { // Synchronous deletion on 0 will cause perf degradation in the case of 1 element @@ -86,7 +86,7 @@ function removeDelegatedListener(eventType: string, element: HTMLElement) { delegatedEventTypesByElement.get(element)!.delete(eventType); } -export function removeAllDelegatedListeners(element: HTMLElement) { +export function removeAllDelegatedListeners(element: Element) { const eventTypes = delegatedEventTypesByElement.get(element); if (!eventTypes) { return; @@ -101,7 +101,7 @@ function handleEvent(realEvent: Event) { if (events) { let furtherCallsPrevented = false; - let current: HTMLElement = realEvent.target as HTMLElement; + let current: Element = realEvent.target as Element; const stopPropagation = () => { furtherCallsPrevented = true; @@ -138,7 +138,7 @@ function handleEvent(realEvent: Event) { } } - current = current.parentNode as HTMLElement; + current = current.parentNode as Element; } } } @@ -151,7 +151,7 @@ function resolveDelegationRegistry(eventType: string) { return delegationRegistryByEventType[eventType]; } -function resolveDelegatedEventTypes(element: HTMLElement) { +function resolveDelegatedEventTypes(element: Element) { const existing = delegatedEventTypesByElement.get(element); if (existing) { return existing; diff --git a/src/lib/teact/teact-dom.ts b/src/lib/teact/teact-dom.ts index 31214262..4c9e288c 100644 --- a/src/lib/teact/teact-dom.ts +++ b/src/lib/teact/teact-dom.ts @@ -34,6 +34,8 @@ interface SelectionState { isCaretAtEnd: boolean; } +type DOMElement = HTMLElement | SVGElement; + const FILTERED_ATTRIBUTES = new Set(['key', 'ref', 'teactFastList', 'teactOrderKey']); const HTML_ATTRIBUTES = new Set(['dir', 'role', 'form']); const CONTROLLABLE_TAGS = ['INPUT', 'TEXTAREA', 'SELECT']; @@ -73,7 +75,7 @@ function render($element: VirtualElement | undefined, parentEl: HTMLElement) { } function renderWithVirtual( - parentEl: HTMLElement, + parentEl: DOMElement, $current: VirtualElement | undefined, $new: T, $parent: VirtualElementParent | VirtualDomHead, @@ -83,17 +85,22 @@ function renderWithVirtual( nextSibling?: ChildNode; forceMoveToEnd?: boolean; fragment?: DocumentFragment; + isSvg?: true; } = {}, ): T { const { skipComponentUpdate, fragment } = options; - let { nextSibling } = options; + let { nextSibling, isSvg } = options; - const isCurrentComponent = $current && $current.type === VirtualType.Component; - const isNewComponent = $new && $new.type === VirtualType.Component; + const isCurrentComponent = $current?.type === VirtualType.Component; + const isNewComponent = $new?.type === VirtualType.Component; const $newAsReal = $new as VirtualElementReal; - const isCurrentFragment = $current && !isCurrentComponent && $current.type === VirtualType.Fragment; - const isNewFragment = $new && !isNewComponent && $new.type === VirtualType.Fragment; + const isCurrentFragment = !isCurrentComponent && $current?.type === VirtualType.Fragment; + const isNewFragment = !isNewComponent && $new?.type === VirtualType.Fragment; + + if ($new?.type === VirtualType.Tag && $new.tag === 'svg') { + isSvg = true; + } if ( !skipComponentUpdate @@ -129,7 +136,9 @@ function renderWithVirtual( $new = initComponent(parentEl, $new as VirtualElementComponent, $parent, index) as unknown as typeof $new; } - mountChildren(parentEl, $new as VirtualElementComponent | VirtualElementFragment, { nextSibling, fragment }); + mountChildren(parentEl, $new as VirtualElementComponent | VirtualElementFragment, { + nextSibling, fragment, isSvg, + }); } else { const canSetTextContent = !fragment && !nextSibling @@ -141,12 +150,12 @@ function renderWithVirtual( parentEl.textContent = $newAsReal.value; $newAsReal.target = parentEl.firstChild!; } else { - const node = createNode($newAsReal); + const node = createNode($newAsReal, isSvg); $newAsReal.target = node; insertBefore(fragment || parentEl, node, nextSibling); if ($newAsReal.type === VirtualType.Tag) { - setElementRef($newAsReal, node as HTMLElement); + setElementRef($newAsReal, node as DOMElement); } } } @@ -164,14 +173,16 @@ function renderWithVirtual( } remount(parentEl, $current, undefined); - mountChildren(parentEl, $new as VirtualElementComponent | VirtualElementFragment, { nextSibling, fragment }); + mountChildren(parentEl, $new as VirtualElementComponent | VirtualElementFragment, { + nextSibling, fragment, isSvg, + }); } else { - const node = createNode($newAsReal); + const node = createNode($newAsReal, isSvg); $newAsReal.target = node; remount(parentEl, $current, node, nextSibling); if ($newAsReal.type === VirtualType.Tag) { - setElementRef($newAsReal, node as HTMLElement); + setElementRef($newAsReal, node as DOMElement); } } } else { @@ -198,14 +209,14 @@ function renderWithVirtual( const $newAsTag = $new as VirtualElementTag; setElementRef($current, undefined); - setElementRef($newAsTag, currentTarget as HTMLElement); + setElementRef($newAsTag, currentTarget as DOMElement); if (nextSibling || options.forceMoveToEnd) { insertBefore(parentEl, currentTarget, nextSibling); } - updateAttributes($current, $newAsTag, currentTarget as HTMLElement); - renderChildren($current, $newAsTag, currentTarget as HTMLElement); + updateAttributes($current, $newAsTag, currentTarget as DOMElement, isSvg); + renderChildren($current, $newAsTag, currentTarget as DOMElement, undefined, undefined, isSvg); } } } @@ -215,7 +226,7 @@ function renderWithVirtual( } function initComponent( - parentEl: HTMLElement, + parentEl: DOMElement, $element: VirtualElementComponent, $parent: VirtualElementParent | VirtualDomHead, index: number, @@ -237,7 +248,7 @@ function updateComponent($current: VirtualElementComponent, $new: VirtualElement } function setupComponentUpdateListener( - parentEl: HTMLElement, + parentEl: DOMElement, $element: VirtualElementComponent, $parent: VirtualElementParent | VirtualDomHead, index: number, @@ -257,11 +268,12 @@ function setupComponentUpdateListener( } function mountChildren( - parentEl: HTMLElement, + parentEl: DOMElement, $element: VirtualElementComponent | VirtualElementFragment, options: { nextSibling?: ChildNode; fragment?: DocumentFragment; + isSvg?: true; }, ) { const { children } = $element; @@ -274,13 +286,13 @@ function mountChildren( } } -function unmountChildren(parentEl: HTMLElement, $element: VirtualElementComponent | VirtualElementFragment) { +function unmountChildren(parentEl: DOMElement, $element: VirtualElementComponent | VirtualElementFragment) { for (const $child of $element.children) { renderWithVirtual(parentEl, $child, undefined, $element, -1); } } -function createNode($element: VirtualElementReal): Node { +function createNode($element: VirtualElementReal, isSvg?: true): Node { if ($element.type === VirtualType.Empty) { return document.createTextNode(''); } @@ -290,7 +302,7 @@ function createNode($element: VirtualElementReal): Node { } const { tag, props, children } = $element; - const element = document.createElement(tag); + const element = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', tag) : document.createElement(tag); processControlled(tag, props); @@ -299,7 +311,7 @@ function createNode($element: VirtualElementReal): Node { if (!props.hasOwnProperty(key)) continue; if (props[key] !== undefined) { - setAttribute(element, key, props[key]); + setAttribute(element, key, props[key], isSvg); } } @@ -307,7 +319,7 @@ function createNode($element: VirtualElementReal): Node { for (let i = 0, l = children.length; i < l; i++) { const $child = children[i]; - const $renderedChild = renderWithVirtual(element, undefined, $child, $element, i); + const $renderedChild = renderWithVirtual(element, undefined, $child, $element, i, { isSvg }); if ($renderedChild !== $child) { children[i] = $renderedChild; } @@ -317,7 +329,7 @@ function createNode($element: VirtualElementReal): Node { } function remount( - parentEl: HTMLElement, + parentEl: DOMElement, $current: VirtualElement, node: Node | undefined, componentNextSibling?: ChildNode, @@ -368,7 +380,7 @@ function unmountRealTree($element: VirtualElement) { } } -function insertBefore(parentEl: HTMLElement | DocumentFragment, node: Node, nextSibling?: ChildNode) { +function insertBefore(parentEl: DOMElement | DocumentFragment, node: Node, nextSibling?: ChildNode) { if (nextSibling) { parentEl.insertBefore(node, nextSibling); } else { @@ -388,9 +400,10 @@ function getNextSibling($current: VirtualElement): ChildNode | undefined { function renderChildren( $current: VirtualElementParent, $new: VirtualElementParent, - currentEl: HTMLElement, + currentEl: DOMElement, nextSibling?: ChildNode, forceMoveToEnd = false, + isSvg?: true, ) { if (DEBUG) { DEBUG_checkKeyUniqueness($new.children); @@ -421,7 +434,7 @@ function renderChildren( newChildren[i], $new, i, - i >= currentChildrenLength ? { fragment } : { nextSibling, forceMoveToEnd }, + i >= currentChildrenLength ? { fragment, isSvg } : { nextSibling, forceMoveToEnd, isSvg }, ); if ($renderedChild && $renderedChild !== newChildren[i]) { @@ -436,7 +449,7 @@ function renderChildren( // This function allows to prepend/append a bunch of new DOM nodes to the top/bottom of preserved ones. // It also allows to selectively move particular preserved nodes within their DOM list. -function renderFastListChildren($current: VirtualElementParent, $new: VirtualElementParent, currentEl: HTMLElement) { +function renderFastListChildren($current: VirtualElementParent, $new: VirtualElementParent, currentEl: DOMElement) { const currentChildren = $current.children; const newChildren = $new.children; @@ -551,7 +564,7 @@ function renderFastListChildren($current: VirtualElementParent, $new: VirtualEle } function renderFragment( - fragmentIndex: number, fragmentSize: number, parentEl: HTMLElement, $parent: VirtualElementParent, + fragmentIndex: number, fragmentSize: number, parentEl: DOMElement, $parent: VirtualElementParent, ) { const nextSibling = parentEl.childNodes[fragmentIndex]; @@ -578,13 +591,13 @@ function renderFragment( insertBefore(parentEl, fragment, nextSibling); } -function setElementRef($element: VirtualElementTag, htmlElement: HTMLElement | undefined) { +function setElementRef($element: VirtualElementTag, DOMElement: DOMElement | undefined) { const { ref } = $element.props; if (typeof ref === 'object') { - ref.current = htmlElement; + ref.current = DOMElement; } else if (typeof ref === 'function') { - ref(htmlElement); + ref(DOMElement); } } @@ -631,7 +644,7 @@ function processControlled(tag: string, props: AnyLiteral) { }; } -function processUncontrolledOnMount(element: HTMLElement, props: AnyLiteral) { +function processUncontrolledOnMount(element: DOMElement, props: AnyLiteral) { if (!CONTROLLABLE_TAGS.includes(element.tagName)) { return; } @@ -645,7 +658,7 @@ function processUncontrolledOnMount(element: HTMLElement, props: AnyLiteral) { } } -function updateAttributes($current: VirtualElementTag, $new: VirtualElementTag, element: HTMLElement) { +function updateAttributes($current: VirtualElementTag, $new: VirtualElementTag, element: DOMElement, isSvg?: true) { processControlled(element.tagName, $new.props); const currentEntries = Object.entries($current.props); @@ -669,14 +682,14 @@ function updateAttributes($current: VirtualElementTag, $new: VirtualElementTag, const currentValue = $current.props[key]; if (newValue !== undefined && newValue !== currentValue) { - setAttribute(element, key, newValue); + setAttribute(element, key, newValue, isSvg); } } } -function setAttribute(element: HTMLElement, key: string, value: any) { +function setAttribute(element: DOMElement, key: string, value: any, isSvg?: true) { if (key === 'className') { - updateClassName(element, value); + updateClassName(element, value, isSvg); } else if (key === 'value') { const inputEl = element as HTMLInputElement; @@ -703,14 +716,14 @@ function setAttribute(element: HTMLElement, key: string, value: any) { element.innerHTML = value.__html; } else if (key.startsWith('on')) { addEventListener(element, key, value, key.endsWith('Capture')); - } else if (key.startsWith('data-') || key.startsWith('aria-') || HTML_ATTRIBUTES.has(key)) { + } else if (isSvg || key.startsWith('data-') || key.startsWith('aria-') || HTML_ATTRIBUTES.has(key)) { element.setAttribute(key, value); } else if (!FILTERED_ATTRIBUTES.has(key)) { (element as any)[MAPPED_ATTRIBUTES[key] || key] = value; } } -function removeAttribute(element: HTMLElement, key: string, value: any) { +function removeAttribute(element: DOMElement, key: string, value: any) { if (key === 'className') { updateClassName(element, ''); } else if (key === 'value') { @@ -726,10 +739,16 @@ function removeAttribute(element: HTMLElement, key: string, value: any) { } } -function updateClassName(element: HTMLElement, value: string) { +function updateClassName(element: DOMElement, value: string, isSvg?: true) { + if (isSvg) { + element.setAttribute('class', value); + return; + } + + const htmlElement = element as HTMLElement; const extra = extraClasses.get(element); if (!extra) { - element.className = value; + htmlElement.className = value; return; } @@ -738,10 +757,10 @@ function updateClassName(element: HTMLElement, value: string) { extraArray.push(value); } - element.className = extraArray.join(' '); + htmlElement.className = extraArray.join(' '); } -function updateStyle(element: HTMLElement, value: string) { +function updateStyle(element: DOMElement, value: string) { element.style.cssText = value; const extraObject = extraStyles.get(element); @@ -750,7 +769,7 @@ function updateStyle(element: HTMLElement, value: string) { } } -export function addExtraClass(element: Element, className: string, forceSingle = false) { +export function addExtraClass(element: DOMElement, className: string, forceSingle = false) { if (!forceSingle) { const classNames = className.split(' '); if (classNames.length > 1) { @@ -772,7 +791,7 @@ export function addExtraClass(element: Element, className: string, forceSingle = } } -export function removeExtraClass(element: Element, className: string, forceSingle = false) { +export function removeExtraClass(element: DOMElement, className: string, forceSingle = false) { if (!forceSingle) { const classNames = className.split(' '); if (classNames.length > 1) { @@ -796,7 +815,7 @@ export function removeExtraClass(element: Element, className: string, forceSingl } } -export function toggleExtraClass(element: Element, className: string, force?: boolean, forceSingle = false) { +export function toggleExtraClass(element: DOMElement, className: string, force?: boolean, forceSingle = false) { if (!forceSingle) { const classNames = className.split(' '); if (classNames.length > 1) { @@ -817,12 +836,12 @@ export function toggleExtraClass(element: Element, className: string, force?: bo } } -export function setExtraStyles(element: HTMLElement, styles: Partial & AnyLiteral) { +export function setExtraStyles(element: DOMElement, styles: Partial & AnyLiteral) { extraStyles.set(element, styles); applyExtraStyles(element); } -function applyExtraStyles(element: HTMLElement) { +function applyExtraStyles(element: DOMElement) { const standardStyles = Object.entries(extraStyles.get(element)!).reduce>( (acc, [prop, value]) => { if (prop.startsWith('--')) { diff --git a/src/lib/teact/teact.ts b/src/lib/teact/teact.ts index b6526925..57ed281d 100644 --- a/src/lib/teact/teact.ts +++ b/src/lib/teact/teact.ts @@ -37,7 +37,7 @@ interface VirtualElementText { export interface VirtualElementTag { type: VirtualType.Tag; - target?: HTMLElement; + target?: HTMLElement | SVGElement; tag: string; props: Props; children: VirtualElementChildren; diff --git a/src/lib/teact/teactn.tsx b/src/lib/teact/teactn.tsx index ef888ce7..83f59960 100644 --- a/src/lib/teact/teactn.tsx +++ b/src/lib/teact/teactn.tsx @@ -14,6 +14,17 @@ import useUniqueId from '../../hooks/useUniqueId'; export default React; +interface Container { + mapStateToProps: MapStateToProps; + activationFn?: ActivationFn; + stuckTo?: any; + ownProps: Props; + mappedProps?: Props; + forceUpdate: Function; + DEBUG_updates: number; + DEBUG_componentName: string; +} + type GlobalState = AnyLiteral & { DEBUG_capturedId?: number }; @@ -24,7 +35,7 @@ export interface ActionOptions { forceOnHeavyAnimation?: boolean; // Workaround for iOS gesture history navigation forceSyncOnIOs?: boolean; - noUpdate?: boolean; + forceOutdated?: boolean; } type Actions = Record void>; @@ -35,10 +46,11 @@ type ActionHandler = ( payload: any, ) => GlobalState | void | Promise; -type DetachWhenChanged = (current: any) => void; -type MapStateToProps = ( - (global: GlobalState, ownProps: OwnProps, detachWhenChanged: DetachWhenChanged) => AnyLiteral - ); +type MapStateToProps = (global: GlobalState, ownProps: OwnProps) => AnyLiteral; +type StickToFirstFn = (value: any) => boolean; +type ActivationFn = ( + global: GlobalState, ownProps: OwnProps, stickToFirst: StickToFirstFn, +) => boolean; let currentGlobal = {} as GlobalState; @@ -51,28 +63,13 @@ const DEBUG_releaseCapturedIdThrottled = throttleWithTickEnd(() => { const actionHandlers: Record = {}; const callbacks: Function[] = [updateContainers]; -const immediateCallbacks: Function[] = []; const actions = {} as Actions; -const containers = new Map; - ownProps: Props; - mappedProps?: Props; - forceUpdate: Function; - isDetached: boolean; - detachReason: any; - detachWhenChanged: DetachWhenChanged; - DEBUG_updates: number; - DEBUG_componentName: string; -}>(); +const containers = new Map(); const runCallbacksThrottled = throttleWithTickEnd(runCallbacks); let forceOnHeavyAnimation = true; -function runImmediateCallbacks() { - immediateCallbacks.forEach((cb) => cb(currentGlobal)); -} - function runCallbacks() { if (forceOnHeavyAnimation) { forceOnHeavyAnimation = false; @@ -87,7 +84,10 @@ function runCallbacks() { export function setGlobal(newGlobal?: GlobalState, options?: ActionOptions) { if (typeof newGlobal === 'object' && newGlobal !== currentGlobal) { if (DEBUG) { - if (newGlobal.DEBUG_capturedId && newGlobal.DEBUG_capturedId !== DEBUG_currentCapturedId) { + if ( + !options?.forceOutdated + && newGlobal.DEBUG_capturedId && newGlobal.DEBUG_capturedId !== DEBUG_currentCapturedId + ) { throw new Error('[TeactN.setGlobal] Attempt to set an outdated global'); } @@ -96,8 +96,6 @@ export function setGlobal(newGlobal?: GlobalState, options?: ActionOptions) { currentGlobal = newGlobal; - if (!options?.noUpdate) runImmediateCallbacks(); - if (options?.forceSyncOnIOs) { forceOnHeavyAnimation = true; runCallbacks(); @@ -128,6 +126,10 @@ export function getActions() { return actions; } +export function forceOnHeavyAnimationOnce() { + forceOnHeavyAnimation = true; +} + let actionQueue: NoneToVoidFunction[] = []; function handleAction(name: string, payload?: ActionPayload, options?: ActionOptions) { @@ -164,21 +166,17 @@ function updateContainers() { // eslint-disable-next-line no-restricted-syntax for (const container of containers.values()) { const { - mapStateToProps, ownProps, mappedProps, forceUpdate, isDetached, detachWhenChanged, + mapStateToProps, ownProps, mappedProps, forceUpdate, } = container; - if (isDetached) { + if (!activateContainer(container, currentGlobal, ownProps)) { continue; } let newMappedProps; try { - newMappedProps = mapStateToProps(currentGlobal, ownProps, detachWhenChanged); - - if (container.isDetached) { - continue; - } + newMappedProps = mapStateToProps(currentGlobal, ownProps); } catch (err: any) { handleError(err); @@ -232,19 +230,20 @@ export function addActionHandler(name: ActionNames, handler: ActionHandler) { actionHandlers[name].push(handler); } -export function addCallback(cb: Function, isImmediate = false) { - (isImmediate ? immediateCallbacks : callbacks).push(cb); +export function addCallback(cb: Function) { + callbacks.push(cb); } -export function removeCallback(cb: Function, isImmediate = false) { - const index = (isImmediate ? immediateCallbacks : callbacks).indexOf(cb); +export function removeCallback(cb: Function) { + const index = callbacks.indexOf(cb); if (index !== -1) { - (isImmediate ? immediateCallbacks : callbacks).splice(index, 1); + callbacks.splice(index, 1); } } export function withGlobal( mapStateToProps: MapStateToProps = () => ({}), + activationFn?: ActivationFn, ) { return (Component: FC) => { function TeactNContainer(props: OwnProps) { @@ -261,20 +260,9 @@ export function withGlobal( if (!container) { container = { mapStateToProps, + activationFn, ownProps: props, forceUpdate, - isDetached: false, - detachReason: undefined, - // This allows to ignore changes in global during animation before unmount - detachWhenChanged: (current) => { - const { detachReason } = container!; - - if (detachReason === undefined && current !== undefined) { - container!.detachReason = current; - } else if (detachReason !== undefined && detachReason !== current) { - container!.isDetached = true; - } - }, DEBUG_updates: 0, DEBUG_componentName: Component.name, }; @@ -282,18 +270,18 @@ export function withGlobal( containers.set(id, container); } - if (!container.mappedProps || !arePropsShallowEqual(container.ownProps, props)) { - container.ownProps = props; - - if (!container.isDetached) { - try { - container.mappedProps = mapStateToProps(currentGlobal, props, container.detachWhenChanged); - } catch (err: any) { - handleError(err); - } + if (!container.mappedProps || ( + !arePropsShallowEqual(container.ownProps, props) && activateContainer(container, currentGlobal, props) + )) { + try { + container.mappedProps = mapStateToProps(currentGlobal, props); + } catch (err: any) { + handleError(err); } } + container.ownProps = props; + // eslint-disable-next-line react/jsx-props-no-spreading return ; } @@ -304,6 +292,21 @@ export function withGlobal( }; } +function activateContainer(container: Container, global: GlobalState, props: Props) { + const { activationFn, stuckTo } = container; + if (!activationFn) { + return true; + } + + return activationFn(global, props, (stickTo: any) => { + if (stickTo && !stuckTo) { + container.stuckTo = stickTo; + } + + return stickTo && (!stuckTo || stuckTo === stickTo); + }); +} + export function typify< ProjectGlobalState, ActionPayloads, @@ -337,8 +340,8 @@ export function typify< handler: ActionHandlers[ActionName], ) => void, withGlobal: withGlobal as ( - mapStateToProps: ( - (global: ProjectGlobalState, ownProps: OwnProps, detachWhenChanged: DetachWhenChanged) => AnyLiteral), + mapStateToProps: (global: ProjectGlobalState, ownProps: OwnProps) => AnyLiteral, + activationFn?: (global: ProjectGlobalState, ownProps: OwnProps, stickToFirst: StickToFirstFn) => boolean, ) => (Component: FC) => FC, }; } diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 9cc50606..900feaef 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -21,6 +21,7 @@ --color-background-drop-down-search: #F1F5FA; --color-background-purple-1: #F9FAFE; --color-background-purple-2: #DCDEFF; + --color-app-background: var(--color-background-second); --color-input-button-text: #53657B; --color-input-button-background: #F6F8FB; @@ -87,6 +88,8 @@ --color-activity-purple-text: #6875E9; --color-activity-purple-background: #EDEEFC; --color-activity-red-background: #FDEAEA; + --color-activity-blue: #0088CC; + --color-activity-blue-background: rgba(0, 136, 204, 0.10); --color-transaction-gray-text: #53657B; --color-transaction-gray-background: #E2E9F1; @@ -94,6 +97,8 @@ --color-transaction-red-background: #F5E6E7; --color-transaction-green-text: #1EC160; --color-transaction-green-background: #DDF2E8; + --color-transaction-purple-text: #6673E6; + --color-transaction-purple-background: #E2E7F7; --color-separator: rgba(132, 146, 171, 0.3); --color-separator-input-stroke: #DFE7F0; @@ -135,14 +140,20 @@ --color-apy-active-text: #FFFFFF; --color-apy-active-background: #8892EB; + --color-flashlight-button-background: rgba(255, 255, 255, 0.35); + --color-flashlight-button-enabled-background: rgba(255, 255, 255, 0.95); + --color-flashlight-button-text: #000; + --color-flashlight-button-enabled-text: #53657B; + --default-shadow: 0 0 1.5625rem 0 #00000026; --spinner-white-data: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEwLjggMjIuNEM2IDIxLjkgMi4xIDE4IDEuNiAxMy4yLjkgNy4xIDUuNCAxLjkgMTEuMyAxLjVjLjQgMCAuNy0uMy43LS43IDAtLjQtLjQtLjgtLjgtLjhDNC44LjQtLjIgNS45IDAgMTIuNS4yIDE4LjYgNS40IDIzLjggMTEuNSAyNGM2LjYuMiAxMi00LjggMTIuNC0xMS4yIDAtLjQtLjMtLjgtLjgtLjgtLjQgMC0uNy4zLS43LjctLjMgNS45LTUuNSAxMC40LTExLjYgOS43eiIgZmlsbD0iI2ZmZmZmZiIvPjwvc3ZnPg==); --spinner-white-thin-data: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTEyIDIzQzUuOSAyMyAxIDE4LjEgMSAxMlM1LjkgMSAxMiAxVjBDNS40IDAgMCA1LjQgMCAxMnM1LjQgMTIgMTIgMTIgMTItNS40IDEyLTEyaC0xYzAgNi4xLTQuOSAxMS0xMSAxMXoiLz48L3N2Zz4=); --spinner-blue-data: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEwLjggMjIuNEM2IDIxLjkgMi4xIDE4IDEuNiAxMy4yLjkgNy4xIDUuNCAxLjkgMTEuMyAxLjVjLjQgMCAuNy0uMy43LS43IDAtLjQtLjQtLjgtLjgtLjhDNC44LjQtLjIgNS45IDAgMTIuNS4yIDE4LjYgNS40IDIzLjggMTEuNSAyNGM2LjYuMiAxMi00LjggMTIuNC0xMS4yIDAtLjQtLjMtLjgtLjgtLjgtLjQgMC0uNy4zLS43LjctLjMgNS45LTUuNSAxMC40LTExLjYgOS43eiIgZmlsbD0iIzAwODhDQyIvPjwvc3ZnPg==); --spinner-dark-blue-data: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEwLjggMjIuNEM2IDIxLjkgMi4xIDE4IDEuNiAxMy4yLjkgNy4xIDUuNCAxLjkgMTEuMyAxLjVjLjQgMCAuNy0uMy43LS43IDAtLjQtLjQtLjgtLjgtLjhDNC44LjQtLjIgNS45IDAgMTIuNS4yIDE4LjYgNS40IDIzLjggMTEuNSAyNGM2LjYuMiAxMi00LjggMTIuNC0xMS4yIDAtLjQtLjMtLjgtLjgtLjgtLjQgMC0uNy4zLS43LjctLjMgNS45LTUuNSAxMC40LTExLjYgOS43eiIgZmlsbD0iIzAwODhDQyIvPjwvc3ZnPg==); + --spinner-green-data: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEwLjggMjIuNEM2IDIxLjkgMi4xIDE4IDEuNiAxMy4yLjkgNy4xIDUuNCAxLjkgMTEuMyAxLjVjLjQgMCAuNy0uMy43LS43IDAtLjQtLjQtLjgtLjgtLjhDNC44LjQtLjIgNS45IDAgMTIuNS4yIDE4LjYgNS40IDIzLjggMTEuNSAyNGM2LjYuMiAxMi00LjggMTIuNC0xMS4yIDAtLjQtLjMtLjgtLjgtLjgtLjQgMC0uNy4zLS43LjctLjMgNS45LTUuNSAxMC40LTExLjYgOS43eiIgZmlsbD0iIzJDRDM2RiIvPjwvc3ZnPg==); - --layer-blackout-opacity: 0.3; + --layer-blackout-opacity: 0.1; --border-radius-tiny: 0.5rem; --border-radius-small: 0.625rem; @@ -153,6 +164,7 @@ --scrollbar-width: 0; --safe-area-bottom-value: env(safe-area-inset-bottom); + --safe-area-top-value: env(safe-area-inset-top); --z-below: -1; --z-modal: 100; @@ -160,9 +172,11 @@ --z-menu-bubble: 200; --z-notification: 250; --z-tooltip: 300; + --z-confetti: 400; --no-animation-transition: 200ms opacity ease; --layer-transition: 300ms cubic-bezier(0.33, 1, 0.68, 1); + --layer-transition-behind: 300ms cubic-bezier(0.33, 1, 0.68, 1); --slide-transition: 300ms cubic-bezier(0.25, 1, 0.5, 1); --select-transition: 200ms ease-out; --dropdown-transition: opacity 200ms cubic-bezier(0.2, 0, 0.2, 1), transform 200ms cubic-bezier(0.2, 0, 0.2, 1), @@ -170,13 +184,13 @@ --dropdown-transition-backwards: opacity 200ms ease-in, transform 200ms ease-in; &.is-ios { - --layer-transition: 450ms cubic-bezier(0.33, 1, 0.68, 1); + --layer-transition: 650ms cubic-bezier(0.22, 1, 0.36, 1); + --layer-transition-behind: 650ms cubic-bezier(0.33, 1, 0.68, 1); --slide-transition: 450ms cubic-bezier(0.25, 1, 0.5, 1); } &.is-android { - --layer-transition: 450ms cubic-bezier(0.25, 1, 0.5, 1); - --slide-transition: 400ms cubic-bezier(0.25, 1, 0.5, 1); + --slide-transition: 350ms cubic-bezier(0.16, 1, 0.3, 1); } &:global(.theme-dark) { @@ -243,10 +257,12 @@ --color-activity-gray-background: #2E3947; --color-activity-gray-text: #A3B8CA; --color-activity-green-background: #1F3838; - --color-activity-green-text: #2CD36F; + --color-activity-green-text: #2BC469; --color-activity-purple-text: #6875E9; --color-activity-purple-background: #2D3651; --color-activity-red-background: #422F39; + --color-activity-blue: #469CEC; + --color-activity-blue-background: rgba(70, 156, 236, 0.1); --color-transaction-gray-text: #AABCCC; --color-transaction-gray-background: #2E3947; @@ -254,6 +270,8 @@ --color-transaction-red-background: #452A2E; --color-transaction-green-text: #2CD36F; --color-transaction-green-background: #1F3838; + --color-transaction-purple-text: #8A95FF; + --color-transaction-purple-background: #2A3147; --color-separator: rgba(84, 96, 112, 0.3); --color-separator-input-stroke: #364354; diff --git a/src/styles/brilliant-icons.css b/src/styles/brilliant-icons.css index 602b00b0..84acab8a 100644 --- a/src/styles/brilliant-icons.css +++ b/src/styles/brilliant-icons.css @@ -1,7 +1,7 @@ @font-face { font-family: "brilliant-icons"; - src: url("./brilliant-icons.woff?bc5164b37bfd0b73b5ecadf95aa71fe5") format("woff"), -url("./brilliant-icons.woff2?bc5164b37bfd0b73b5ecadf95aa71fe5") format("woff2"); + src: url("./brilliant-icons.woff?c432e9e8a4a2ab95fcf5874fa9f9b512") format("woff"), +url("./brilliant-icons.woff2?c432e9e8a4a2ab95fcf5874fa9f9b512") format("woff2"); font-weight: normal; font-style: normal; } @@ -20,144 +20,168 @@ url("./brilliant-icons.woff2?bc5164b37bfd0b73b5ecadf95aa71fe5") format("woff2"); .icon-arrow-down::before { content: "\f102"; } -.icon-arrow-right::before { +.icon-arrow-right-swap::before { content: "\f103"; } -.icon-arrow-up::before { +.icon-arrow-right::before { content: "\f104"; } -.icon-caret-down::before { +.icon-arrow-up-swap::before { content: "\f105"; } -.icon-chevron-down::before { +.icon-arrow-up::before { content: "\f106"; } -.icon-chevron-left::before { +.icon-backspace::before { content: "\f107"; } -.icon-chevron-right::before { +.icon-caret-down::before { content: "\f108"; } -.icon-clock::before { +.icon-changelly::before { content: "\f109"; } -.icon-close-filled::before { +.icon-chevron-down::before { content: "\f10a"; } -.icon-close::before { +.icon-chevron-left::before { content: "\f10b"; } -.icon-cog::before { +.icon-chevron-right::before { content: "\f10c"; } -.icon-coinmarket::before { +.icon-clock::before { content: "\f10d"; } -.icon-copy::before { +.icon-close-filled::before { content: "\f10e"; } -.icon-dot::before { +.icon-close::before { content: "\f10f"; } -.icon-download::before { +.icon-cog::before { content: "\f110"; } -.icon-earn::before { +.icon-coinmarket::before { content: "\f111"; } -.icon-eye-closed::before { +.icon-copy::before { content: "\f112"; } -.icon-eye::before { +.icon-dot::before { content: "\f113"; } -.icon-github::before { +.icon-download::before { content: "\f114"; } -.icon-laptop::before { +.icon-earn::before { content: "\f115"; } -.icon-ledger::before { +.icon-eye-closed::before { content: "\f116"; } -.icon-lock::before { +.icon-eye::before { content: "\f117"; } -.icon-params::before { +.icon-face-id::before { content: "\f118"; } -.icon-paste::before { +.icon-flashlight::before { content: "\f119"; } -.icon-pen::before { +.icon-github::before { content: "\f11a"; } -.icon-percent::before { +.icon-laptop::before { content: "\f11b"; } -.icon-plus::before { +.icon-ledger::before { content: "\f11c"; } -.icon-qrcode::before { +.icon-lock::before { content: "\f11d"; } -.icon-question::before { +.icon-params::before { content: "\f11e"; } -.icon-receive-alt::before { +.icon-paste::before { content: "\f11f"; } -.icon-receive::before { +.icon-pen::before { content: "\f120"; } -.icon-replace::before { +.icon-percent::before { content: "\f121"; } -.icon-search::before { +.icon-plus::before { content: "\f122"; } -.icon-send-alt::before { +.icon-qr-scanner-alt::before { content: "\f123"; } -.icon-send::before { +.icon-qr-scanner::before { content: "\f124"; } -.icon-share::before { +.icon-question::before { content: "\f125"; } -.icon-sort::before { +.icon-receive-alt::before { content: "\f126"; } -.icon-star-filled::before { +.icon-receive::before { content: "\f127"; } -.icon-star::before { +.icon-replace::before { content: "\f128"; } -.icon-swap::before { +.icon-search::before { content: "\f129"; } -.icon-telegram::before { +.icon-send-alt::before { content: "\f12a"; } -.icon-ton::before { +.icon-send::before { content: "\f12b"; } -.icon-tonscan::before { +.icon-share::before { content: "\f12c"; } -.icon-trash::before { +.icon-sort::before { content: "\f12d"; } -.icon-update::before { +.icon-star-filled::before { content: "\f12e"; } -.icon-windows-close::before { +.icon-star::before { content: "\f12f"; } -.icon-windows-maximize::before { +.icon-swap::before { content: "\f130"; } -.icon-windows-minimize::before { +.icon-telegram::before { content: "\f131"; } +.icon-ton::before { + content: "\f132"; +} +.icon-tonscan::before { + content: "\f133"; +} +.icon-touch-id::before { + content: "\f134"; +} +.icon-trash::before { + content: "\f135"; +} +.icon-update::before { + content: "\f136"; +} +.icon-windows-close::before { + content: "\f137"; +} +.icon-windows-maximize::before { + content: "\f138"; +} +.icon-windows-minimize::before { + content: "\f139"; +} diff --git a/src/styles/brilliant-icons.woff b/src/styles/brilliant-icons.woff index 6a7db1027c599b99cf1a82c94d77ab5bf9ab842b..19187eca85337b08d9b8ca130e4f694cf0b433d8 100644 GIT binary patch delta 6992 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 delta 5574 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 { - if (callback && localStorage.getItem(STORAGE_KEY) !== tabKey && !window.location.href.includes(DETACHED_TAB_URL)) { + if (!IS_DISABLED && callback && localStorage.getItem(ACTIVE_TAB_STORAGE_KEY) !== tabKey) { callback(); clearInterval(interval); } diff --git a/src/util/animation.ts b/src/util/animation.ts index 79d4ae3b..d6d65264 100644 --- a/src/util/animation.ts +++ b/src/util/animation.ts @@ -48,15 +48,15 @@ export function animateInstantly(tick: Function, schedulerFn: Scheduler) { } } -export type TimingFn = (t: number) => number; +type TimingFn = (t: number) => number; -export type AnimateNumberProps = { - to: number | number[]; - from: number | number[]; +type AnimateNumberProps = { + to: T; + from: T; duration: number; - onUpdate: (value: any) => void; + onUpdate: (value: T) => void; timing?: TimingFn; - onEnd?: () => void; + onEnd?: (isCanceled?: boolean) => void; }; export const timingFunctions = { @@ -78,35 +78,41 @@ export const timingFunctions = { easeInOutQuint: (t: number) => (t < 0.5 ? 16 * t ** 5 : 1 + 16 * (--t) * t ** 4), }; -export function animateNumber({ +export function animateNumber({ timing = timingFunctions.linear, onUpdate, duration, onEnd, from, to, -}: AnimateNumberProps) { +}: AnimateNumberProps) { const t0 = Date.now(); - let canceled = false; + + let isCanceled = false; animateInstantly(() => { - if (canceled) return false; + if (isCanceled) return false; + const t1 = Date.now(); - let t = (t1 - t0) / duration; - if (t > 1) t = 1; + const t = Math.min((t1 - t0) / duration, 1); + const progress = timing(t); if (typeof from === 'number' && typeof to === 'number') { - onUpdate(from + ((to - from) * progress)); + onUpdate((from + ((to - from) * progress)) as T); } else if (Array.isArray(from) && Array.isArray(to)) { const result = from.map((f, i) => f + ((to[i] - f) * progress)); - onUpdate(result); + onUpdate(result as T); } - if (t === 1 && onEnd) onEnd(); + + if (t === 1) { + onEnd?.(); + } + return t < 1; }, requestMeasure); return () => { - canceled = true; - if (onEnd) onEnd(); + isCanceled = true; + onEnd?.(true); }; } diff --git a/src/util/arePropsShallowEqual.ts b/src/util/arePropsShallowEqual.ts index 56330a77..a6ba9453 100644 --- a/src/util/arePropsShallowEqual.ts +++ b/src/util/arePropsShallowEqual.ts @@ -38,7 +38,7 @@ export function logUnequalProps(currentProps: AnyLiteral, newProps: AnyLiteral, // eslint-disable-next-line no-console console.log(msg); - currentKeys.forEach((res, prop) => { + currentKeys.forEach((prop) => { if (currentProps[prop] !== newProps[prop]) { // eslint-disable-next-line no-console console.log(debugKey, prop, ':', currentProps[prop], '=>', newProps[prop]); diff --git a/src/util/authApi/index.ts b/src/util/authApi/index.ts new file mode 100644 index 00000000..89530034 --- /dev/null +++ b/src/util/authApi/index.ts @@ -0,0 +1,100 @@ +import { NativeBiometric } from '@capgo/capacitor-native-biometric'; + +import type { AuthConfig } from './types'; +import type { CredentialCreationResult } from './webAuthn'; + +import { APP_NAME, NATIVE_BIOMETRICS_SERVER, NATIVE_BIOMETRICS_USERNAME } from '../../config'; +import { logDebugError } from '../logs'; +import { randomBytes } from '../random'; +import webAuthn from './webAuthn'; + +const CREDENTIAL_SIZE = 32; + +async function setupBiometrics({ credential }: { credential?: CredentialCreationResult }) { + let result: { password: string; config: AuthConfig } | undefined; + + try { + if (!credential) { + const password = Buffer.from(randomBytes(CREDENTIAL_SIZE)).toString('hex'); + const encryptedPassword = await window.electron?.encryptPassword(password); + if (!encryptedPassword) { + return result; + } + + result = { + password, + config: { + kind: 'electron-safe-storage', + encryptedPassword, + }, + }; + } else { + result = await webAuthn.verify(credential); + } + } catch (err) { + logDebugError('setupBiometrics', err); + } + + return result; +} + +async function setupNativeBiometrics(password: string) { + await NativeBiometric.setCredentials({ + username: NATIVE_BIOMETRICS_USERNAME, + password, + server: NATIVE_BIOMETRICS_SERVER, + }); + + return { + password, + config: { + kind: 'native-biometrics', + } as AuthConfig, + }; +} + +function removeNativeBiometrics() { + return NativeBiometric.deleteCredentials({ + server: NATIVE_BIOMETRICS_SERVER, + }); +} + +async function getPassword(config: AuthConfig) { + let password: string | undefined; + + try { + if (config.kind === 'webauthn') { + password = await webAuthn.getPassword(config); + } else if (config.kind === 'electron-safe-storage') { + password = await window.electron?.decryptPassword(config.encryptedPassword); + } else if (config.kind === 'native-biometrics') { + const isVerified = await NativeBiometric.verifyIdentity({ + title: APP_NAME, + subtitle: '', + }) + .then(() => true) + .catch(() => false); + + if (!isVerified) return undefined; + + const credentials = await NativeBiometric.getCredentials({ + server: NATIVE_BIOMETRICS_SERVER, + }); + password = credentials.password; + } else { + throw new Error('Unexpected auth kind'); + } + } catch (err) { + logDebugError('submitTransferPassword', err); + } + + return password; +} + +export default { + setupBiometrics, + setupNativeBiometrics, + removeNativeBiometrics, + getPassword, + webAuthn, +}; diff --git a/src/util/authApi/types.ts b/src/util/authApi/types.ts new file mode 100644 index 00000000..0b72c117 --- /dev/null +++ b/src/util/authApi/types.ts @@ -0,0 +1,21 @@ +export type AuthConfig = AuthPassword | WebAuthn | ElectronSafeStorage | NativeBiometrics; + +export interface AuthPassword { + kind: 'password'; +} + +export interface WebAuthn { + kind: 'webauthn'; + type: 'largeBlob' | 'credBlob' | 'userHandle'; + credentialId: string; + transports?: AuthenticatorTransport[]; +} + +export interface ElectronSafeStorage { + kind: 'electron-safe-storage'; + encryptedPassword: string; +} + +export interface NativeBiometrics { + kind: 'native-biometrics'; +} diff --git a/src/util/authApi/webAuthn.ts b/src/util/authApi/webAuthn.ts new file mode 100644 index 00000000..cbe8cd6c --- /dev/null +++ b/src/util/authApi/webAuthn.ts @@ -0,0 +1,223 @@ +import type { AuthConfig, WebAuthn } from './types'; + +import { randomBytes } from '../random'; +import { pause } from '../schedulers'; + +declare global { + interface AuthenticationExtensionsClientInputs { + credBlob?: Uint8Array; // max 32 bytes + getCredBlob?: boolean; + hmacCreateSecret?: boolean; + hmacGetSecret?: { salt1: Uint8Array }; // 32-byte random data + } + + interface AuthenticationExtensionsClientOutputs { + credBlob?: boolean; + getCredBlob?: Uint8Array; + hmacCreateSecret?: boolean; + hmacGetSecret?: { output1: Uint8Array }; + } + + interface AuthenticatorResponse { + getTransports?: () => AuthenticatorTransport[]; + } +} + +export interface CredentialCreationResult { + type: 'credBlob' | 'userHandle'; + password: { + credBlob: string; + userHandle: string; + }; + credential: PublicKeyCredential; +} + +enum PubkeyAlg { + Ed25519 = -8, + ES256 = -7, + RS256 = -257, +} + +const CREDENTIAL_SIZE = 32; +const RP_NAME = 'MyTonWallet'; +const USER_NAME = 'MyTonWallet'; +const PAUSE = 300; +const CREDENTIAL_TIMEOUT = 120000; + +async function createCredential() { + const rpId = window.location.hostname; + + const userHandle = randomBytes(CREDENTIAL_SIZE); + const credBlob = randomBytes(CREDENTIAL_SIZE); + + const options: CredentialCreationOptions = { + publicKey: { + challenge: randomBytes(CREDENTIAL_SIZE), + rp: { + name: RP_NAME, + id: rpId, + }, + user: { + id: userHandle, + name: USER_NAME, + displayName: RP_NAME, + }, + pubKeyCredParams: [ + { + type: 'public-key', + alg: PubkeyAlg.ES256, + }, + { + type: 'public-key', + alg: PubkeyAlg.RS256, + }, + { + type: 'public-key', + alg: PubkeyAlg.Ed25519, + }, + ], + authenticatorSelection: { + requireResidentKey: true, + userVerification: 'preferred', + }, + extensions: { + credBlob, + hmacCreateSecret: true, + }, + timeout: CREDENTIAL_TIMEOUT, + excludeCredentials: [], + }, + }; + + const credential = (await navigator.credentials.create(options)) as PublicKeyCredential; + + if (!credential) { + throw new Error('Missing credential'); + } + + const extensions = credential.getClientExtensionResults(); + const type = extensions.credBlob ? 'credBlob' : 'userHandle'; + + return { + type, + password: { + credBlob: Buffer.from(credBlob).toString('hex'), + userHandle: Buffer.from(userHandle).toString('hex'), + }, + credential, + } as CredentialCreationResult; +} + +async function verify({ credential, password, type }: CredentialCreationResult) { + await pause(PAUSE); + + const transports = credential.response + && credential.response.getTransports + && credential.response.getTransports(); + + const credentialId = Buffer.from(credential.rawId).toString('hex'); + + const options: CredentialRequestOptions = { + publicKey: { + challenge: randomBytes(CREDENTIAL_SIZE), + allowCredentials: [ + { + id: credential.rawId, + type: 'public-key', + transports, + }, + ], + userVerification: 'required', + extensions: { + getCredBlob: true, + }, + }, + }; + + const assertion = (await navigator.credentials.get(options)) as PublicKeyCredential; + + if (!assertion) { + throw new Error('Missing authentication'); + } + + const response = assertion.response as AuthenticatorAssertionResponse; + let result: string | undefined; + switch (type) { + case 'userHandle': { + if (!response.userHandle) { + throw new Error('Missing stored userHandle'); + } + if (!Buffer.from(password.userHandle, 'hex').equals(Buffer.from(response.userHandle))) { + throw new Error('Stored blob not equals passed blob'); + } + result = password.userHandle; + break; + } + } + + if (!result) { + throw new Error('Missing stored blob'); + } + + const config: WebAuthn = { + kind: 'webauthn', + type, + credentialId, + transports, + }; + + return { config, password: result }; +} + +async function getPassword(config: AuthConfig) { + if (config.kind !== 'webauthn') { + throw new Error('Unexpected auth kind'); + } + + const { credentialId, transports, type } = config; + + const controller = new AbortController(); + const signal = controller.signal; + + const options: CredentialRequestOptions = { + publicKey: { + challenge: randomBytes(CREDENTIAL_SIZE), + allowCredentials: [ + { + id: Buffer.from(credentialId, 'hex'), + type: 'public-key', + transports, + }, + ], + userVerification: 'required', + extensions: { + getCredBlob: true, + }, + }, + signal, + }; + + const assertion = (await navigator.credentials.get(options)) as PublicKeyCredential; + + if (signal.aborted) { + throw new Error('Verification canceled'); + } + + const response = assertion.response as AuthenticatorAssertionResponse; + + const extensions = assertion.getClientExtensionResults(); + if (type === 'userHandle') { + if (!response.userHandle) { + throw new Error('missing userHandle'); + } + return Buffer.from(response.userHandle).toString('hex'); + } else { + return Buffer.from(extensions.getCredBlob ?? '').toString('hex'); + } +} + +export default { + createCredential, + verify, + getPassword, +}; diff --git a/src/util/betterView.ts b/src/util/betterView.ts new file mode 100644 index 00000000..cbde96f3 --- /dev/null +++ b/src/util/betterView.ts @@ -0,0 +1,92 @@ +import { animate } from './animation'; +import { fastRaf } from './schedulers'; +import { IS_IOS } from './windowEnvironment'; + +const TEST_INTERVAL = 5000; // 5 sec +const FRAMES_TO_TEST = 10; +const REDUCED_FPS = 35; + +let isImproved = false; + +export function betterView() { + if (!IS_IOS) return; + + let interval: number | undefined; + let lastFocusAt = Date.now(); + + function setupInterval() { + if (interval || isImproved) return; + + interval = window.setInterval(testAndImprove, TEST_INTERVAL); + } + + window.addEventListener('focus', () => { + const now = Date.now(); + if (now - lastFocusAt < 100) return; // iOS triggers two `focus` events for some reason + lastFocusAt = now; + + setupInterval(); + testAndImprove(); + }); + + window.addEventListener('blur', () => { + clearInterval(interval); + interval = undefined; + }); + + if (document.hasFocus()) { + setupInterval(); + testAndImprove(); + } +} + +async function testAndImprove() { + const fps = await testFps(); + if (fps <= REDUCED_FPS) { + improveView(); + } +} + +function testFps() { + return new Promise((resolve) => { + const frames: number[] = []; + let lastFrameAt = performance.now(); + + animate(() => { + const now = performance.now(); + frames.push(now - lastFrameAt); + lastFrameAt = now; + + if (frames.length === FRAMES_TO_TEST) { + const mean = frames.sort()[Math.floor(frames.length / 2)]; + resolve(Math.round(1000 / mean)); + return false; + } + + return true; + }, fastRaf); + }); +} + +function improveView() { + isImproved = true; + + const containerEl = document.createElement('div'); + containerEl.style.cssText = 'position: absolute; top: 0; left: 0; width: 0; height: 100%; overflow: hidden;'; + + const boosterEl = document.createElement('div'); + const height = window.screen.height * 1.5; + boosterEl.style.cssText = `width: 0; height: ${height}px; transform: translateX(100%); transition: transform 100ms;`; + boosterEl.innerHTML = ' '; + + containerEl.appendChild(boosterEl); + document.body.appendChild(containerEl); + + requestAnimationFrame(() => { + boosterEl.addEventListener('transitionend', () => { + containerEl.remove(); + }); + + boosterEl.style.transform = ''; + }); +} diff --git a/src/util/cacheApi.ts b/src/util/cacheApi.ts index a11229e3..abd078c8 100644 --- a/src/util/cacheApi.ts +++ b/src/util/cacheApi.ts @@ -1,4 +1,4 @@ -import { ELECTRON_HOST_URL, IS_ELECTRON } from '../config'; +import { ELECTRON_HOST_URL, IS_ELECTRON_BUILD } from '../config'; // eslint-disable-next-line no-restricted-globals const cacheApi = self.caches; @@ -10,7 +10,7 @@ export async function fetch(cacheName: string, key: string) { try { // To avoid the error "Request scheme 'webdocument' is unsupported" - const request = IS_ELECTRON + const request = IS_ELECTRON_BUILD ? `${ELECTRON_HOST_URL}/${key.replace(/:/g, '_')}` : new Request(key.replace(/:/g, '_')); const cache = await cacheApi.open(cacheName); @@ -37,7 +37,7 @@ export async function save(cacheName: string, key: string, data: AnyLiteral | Bl ? data : JSON.stringify(data); // To avoid the error "Request scheme 'webdocument' is unsupported" - const request = IS_ELECTRON + const request = IS_ELECTRON_BUILD ? `${ELECTRON_HOST_URL}/${key.replace(/:/g, '_')}` : new Request(key.replace(/:/g, '_')); const response = new Response(cacheData); diff --git a/src/util/callbacks.ts b/src/util/callbacks.ts index ef796fd9..c43b21ff 100644 --- a/src/util/callbacks.ts +++ b/src/util/callbacks.ts @@ -1,7 +1,7 @@ -export function createCallbackManager() { - const callbacks = new Set(); +export function createCallbackManager() { + const callbacks = new Set(); - function addCallback(cb: AnyToVoidFunction) { + function addCallback(cb: T) { callbacks.add(cb); return () => { @@ -9,11 +9,11 @@ export function createCallbackManager() { }; } - function removeCallback(cb: AnyToVoidFunction) { + function removeCallback(cb: T) { callbacks.delete(cb); } - function runCallbacks(...args: any[]) { + function runCallbacks(...args: Parameters) { callbacks.forEach((callback) => { callback(...args); }); @@ -31,7 +31,8 @@ export function createCallbackManager() { }; } -export type CallbackManager = ReturnType; +export type CallbackManager + = ReturnType>; export class EventEmitter { private channels = new Map(); diff --git a/src/util/capacitor.ts b/src/util/capacitor.ts new file mode 100644 index 00000000..6d597bc1 --- /dev/null +++ b/src/util/capacitor.ts @@ -0,0 +1,156 @@ +import type { URLOpenListenerEvent } from '@capacitor/app'; +import { App } from '@capacitor/app'; +import { Capacitor } from '@capacitor/core'; +import { Haptics, ImpactStyle } from '@capacitor/haptics'; +import { StatusBar, Style } from '@capacitor/status-bar'; +import { BiometryType, NativeBiometric } from '@capgo/capacitor-native-biometric'; +import { NavigationBar } from '@mauricewegner/capacitor-navigation-bar'; +import { SafeArea } from 'capacitor-plugin-safe-area'; +import { SplashScreen } from 'capacitor-splash-screen'; + +import type { Theme } from '../global/types'; + +import { callApi } from '../api'; +import { isTonConnectDeeplink } from './ton/deeplinks'; +import { pause } from './schedulers'; +import { tonConnectGetDeviceInfo } from './tonConnectEnvironment'; +import { IS_BIOMETRIC_AUTH_SUPPORTED, IS_DELEGATED_BOTTOM_SHEET } from './windowEnvironment'; + +let launchUrl: string | undefined; +const IOS_SPLASH_SCREEN_HIDE_DELAY = 500; +const IOS_SPLASH_SCREEN_HIDE_DURATION = 600; +export const VIBRATE_SUCCESS_END_PAUSE_MS = 1300; + +let platform: 'ios' | 'android' | undefined; +let isNativeBiometricAuthSupported = false; +let isFaceIdAvailable = false; +let isTouchIdAvailable = false; +let statusBarHeight = 0; + +export async function initCapacitor() { + platform = Capacitor.getPlatform() as 'ios' | 'android'; + + const biometricsAvailableResult = await NativeBiometric.isAvailable(); + + isNativeBiometricAuthSupported = biometricsAvailableResult.isAvailable; + isFaceIdAvailable = biometricsAvailableResult.biometryType === BiometryType.FACE_ID; + isTouchIdAvailable = biometricsAvailableResult.biometryType === BiometryType.TOUCH_ID; + + if (IS_DELEGATED_BOTTOM_SHEET) { + void SplashScreen.hide({ fadeOutDuration: 0 }); + return; + } + + if (platform === 'ios') { + setTimeout(() => { + void SplashScreen.hide({ fadeOutDuration: IOS_SPLASH_SCREEN_HIDE_DURATION }); + }, IOS_SPLASH_SCREEN_HIDE_DELAY); + } + + launchUrl = (await App.getLaunchUrl())?.url; + + App.addListener('appUrlOpen', (event: URLOpenListenerEvent) => { + processDeeplink(event.url); + }); + + if (launchUrl) { + void processDeeplink(launchUrl); + } + + if (platform === 'android') { + // Until this bug is fixed, the `overlay` must be `false` + // https://bugs.chromium.org/p/chromium/issues/detail?id=1094366 + void StatusBar.setOverlaysWebView({ overlay: false }); + void NavigationBar.setTransparency({ isTransparent: false }); + } + + SafeArea.getStatusBarHeight().then(({ statusBarHeight: height }) => { + statusBarHeight = height; + document.documentElement.style.setProperty('--status-bar-height', `${height}px`); + }); + + await SafeArea.addListener('safeAreaChanged', (data) => { + const { insets } = data; + + for (const [key, value] of Object.entries(insets)) { + document.documentElement.style.setProperty( + `--safe-area-${key}`, + `${value}px`, + ); + } + }); +} + +export async function processDeeplink(url: string) { + if (isTonConnectDeeplink(url)) { + const deviceInfo = tonConnectGetDeviceInfo(); + const returnStrategy = await callApi('startSseConnection', url, deviceInfo); + if (returnStrategy === 'ret') { + await App.minimizeApp(); + } + } +} + +export function switchStatusBar(currentAppTheme: Theme, isSystemDark: boolean, forceDarkBackground?: boolean) { + if (platform !== 'ios') return; + + const style = forceDarkBackground || currentAppTheme === 'dark' + ? Style.Dark + : (isSystemDark && currentAppTheme === 'system' ? Style.Dark : Style.Light); + + void StatusBar.setStyle({ style }); +} + +export function getLaunchUrl() { + return launchUrl; +} + +export function clearLaunchUrl() { + launchUrl = undefined; +} + +export function getCapacitorPlatform() { + return platform; +} + +export function getStatusBarHeight() { + return statusBarHeight; +} + +export async function vibrate() { + await Haptics.impact({ style: ImpactStyle.Light }); +} + +export async function vibrateOnError() { + for (let i = 0; i < 3; i++) { + await Haptics.impact({ style: ImpactStyle.Medium }); + await pause(150); + } +} + +export async function vibrateOnSuccess(withPauseOnEnd = false) { + await pause(300); + await Haptics.impact({ style: ImpactStyle.Heavy }); + await pause(150); + await Haptics.impact({ style: ImpactStyle.Light }); + + if (withPauseOnEnd) { + await pause(VIBRATE_SUCCESS_END_PAUSE_MS); + } +} + +export function getIsNativeBiometricAuthSupported() { + return isNativeBiometricAuthSupported; +} + +export function getIsBiometricAuthSupported() { + return IS_BIOMETRIC_AUTH_SUPPORTED || getIsNativeBiometricAuthSupported(); +} + +export function getIsFaceIdAvailable() { + return isFaceIdAvailable; +} + +export function getIsTouchIdAvailable() { + return isTouchIdAvailable; +} diff --git a/src/util/captureEvents.ts b/src/util/captureEvents.ts new file mode 100644 index 00000000..3a2bd28f --- /dev/null +++ b/src/util/captureEvents.ts @@ -0,0 +1,455 @@ +import { Lethargy } from './lethargy'; +import { clamp, round } from './math'; +import { debounce } from './schedulers'; +import { IS_IOS } from './windowEnvironment'; +import windowSize from './windowSize'; + +export enum SwipeDirection { + Up, + Down, + Left, + Right, +} + +export interface MoveOffsets { + dragOffsetX: number; + dragOffsetY: number; +} + +interface CaptureOptions { + onCapture?: (e: MouseEvent | TouchEvent | WheelEvent) => void; + onRelease?: (e: MouseEvent | TouchEvent | WheelEvent) => void; + onDrag?: ( + e: MouseEvent | TouchEvent | WheelEvent, + captureEvent: MouseEvent | TouchEvent | WheelEvent, + offsets: MoveOffsets, + cancelDrag?: (x: boolean, y: boolean) => void, + ) => void; + onSwipe?: (e: Event, direction: SwipeDirection, offsets: MoveOffsets) => boolean; + onZoom?: (e: TouchEvent | WheelEvent, params: { + // Absolute zoom level + zoom?: number; + // Relative zoom factor + zoomFactor?: number; + + // center coordinate of the initial pinch + initialCenterX: number; + initialCenterY: number; + + // offset of the pinch center (current from initial) + dragOffsetX: number; + dragOffsetY: number; + + // center coordinate of the current pinch + currentCenterX: number; + currentCenterY: number; + }) => void; + onClick?: (e: MouseEvent | TouchEvent) => void; + onDoubleClick?: (e: MouseEvent | RealTouchEvent | WheelEvent, params: { centerX: number; centerY: number }) => void; + includedClosestSelector?: string; + excludedClosestSelector?: string; + selectorToPreventScroll?: string; + withNativeDrag?: boolean; + maxZoom?: number; + minZoom?: number; + doubleTapZoom?: number; + initialZoom?: number; + isNotPassive?: boolean; + withCursor?: boolean; + swipeThreshold?: number; +} + +// https://stackoverflow.com/questions/11287877/how-can-i-get-e-offsetx-on-mobile-ipad +// Android does not have this value, and iOS has it but as read-only +export interface RealTouchEvent extends TouchEvent { + pageX?: number; + pageY?: number; +} + +type TSwipeAxis = + 'x' + | 'y' + | undefined; + +export const IOS_SCREEN_EDGE_THRESHOLD = 20; +const MOVE_THRESHOLD = 15; +const SWIPE_THRESHOLD_DEFAULT = 20; +const RELEASE_WHEEL_ZOOM_DELAY = 150; +const RELEASE_WHEEL_DRAG_DELAY = 150; + +function getDistance(a: Touch, b?: Touch) { + if (!b) return 0; + return Math.hypot((b.pageX - a.pageX), (b.pageY - a.pageY)); +} + +function getTouchCenter(a: Touch, b: Touch) { + return { + x: (a.pageX + b.pageX) / 2, + y: (a.pageY + b.pageY) / 2, + }; +} + +let lastClickTime = 0; +const lethargy = new Lethargy({ + stability: 5, + sensitivity: 25, + tolerance: 0.6, + delay: 150, +}); + +export function captureEvents(element: HTMLElement, options: CaptureOptions) { + let captureEvent: MouseEvent | RealTouchEvent | WheelEvent | undefined; + let hasMoved = false; + let hasSwiped = false; + let isZooming = false; + let initialDistance = 0; + let wheelZoom = options.initialZoom ?? 1; + let initialDragOffset = { + x: 0, + y: 0, + }; + let isDragCanceled = { + x: false, + y: false, + }; + const currentWindowSize = windowSize.get(); + let initialTouchCenter = { + x: currentWindowSize.width / 2, + y: currentWindowSize.height / 2, + }; + let initialSwipeAxis: TSwipeAxis | undefined; + const minZoom = options.minZoom ?? 1; + const maxZoom = options.maxZoom ?? 4; + + function onCapture(e: MouseEvent | RealTouchEvent) { + const target = e.target as HTMLElement; + const { + excludedClosestSelector, + includedClosestSelector, + withNativeDrag, + withCursor, + onDrag, + } = options; + + if ( + (excludedClosestSelector && (target.matches(excludedClosestSelector) || target.closest(excludedClosestSelector))) + || ( + includedClosestSelector && !(target.matches(includedClosestSelector) || target.closest(includedClosestSelector)) + ) + ) { + return; + } + + captureEvent = e; + + if (e.type === 'mousedown') { + if (!withNativeDrag && onDrag) { + e.preventDefault(); + } + + document.addEventListener('mousemove', onMove); + document.addEventListener('mouseup', onRelease); + } else if (e.type === 'touchstart') { + // We need to always listen on `touchstart` target: + // https://stackoverflow.com/questions/33298828/touch-move-event-dont-fire-after-touch-start-target-is-removed + target.addEventListener('touchmove', onMove, { passive: true }); + target.addEventListener('touchend', onRelease, { passive: true }); + target.addEventListener('touchcancel', onRelease, { passive: true }); + + if ('touches' in e) { + if (e.pageX === undefined) { + e.pageX = e.touches[0].pageX; + } + + if (e.pageY === undefined) { + e.pageY = e.touches[0].pageY; + } + + if (e.touches.length === 2) { + initialDistance = getDistance(e.touches[0], e.touches[1]); + initialTouchCenter = getTouchCenter(e.touches[0], e.touches[1]); + } + } + } + + if (withCursor) { + document.body.classList.add('cursor-grabbing'); + } + + options.onCapture?.(e); + } + + function onRelease(e?: MouseEvent | TouchEvent) { + if (captureEvent) { + if (options.withCursor) { + document.body.classList.remove('cursor-grabbing'); + } + + document.removeEventListener('mouseup', onRelease); + document.removeEventListener('mousemove', onMove); + (captureEvent.target as HTMLElement).removeEventListener('touchcancel', onRelease); + (captureEvent.target as HTMLElement).removeEventListener('touchend', onRelease); + (captureEvent.target as HTMLElement).removeEventListener('touchmove', onMove); + + if (IS_IOS && options.selectorToPreventScroll) { + Array.from(document.querySelectorAll(options.selectorToPreventScroll)) + .forEach((scrollable) => { + scrollable.style.overflow = ''; + }); + } + + if (e) { + if (hasMoved) { + if (options.onRelease) { + options.onRelease(e); + } + } else if (e.type === 'mouseup') { + if (options.onDoubleClick && Date.now() - lastClickTime < 300) { + options.onDoubleClick(e, { + centerX: captureEvent!.pageX!, + centerY: captureEvent!.pageY!, + }); + } else if (options.onClick && (!('button' in e) || e.button === 0)) { + options.onClick(e); + } + lastClickTime = Date.now(); + } + } + } + + hasMoved = false; + hasSwiped = false; + isZooming = false; + initialDistance = 0; + wheelZoom = clamp(wheelZoom, minZoom, maxZoom); + initialSwipeAxis = undefined; + initialDragOffset = { + x: 0, + y: 0, + }; + isDragCanceled = { + x: false, + y: false, + }; + const newWindowSize = windowSize.get(); + initialTouchCenter = { + x: newWindowSize.width / 2, + y: newWindowSize.height / 2, + }; + captureEvent = undefined; + } + + function onMove(e: MouseEvent | RealTouchEvent) { + if (captureEvent) { + if (e.type === 'touchmove' && ('touches' in e)) { + if (e.pageX === undefined) { + e.pageX = e.touches[0].pageX; + } + + if (e.pageY === undefined) { + e.pageY = e.touches[0].pageY; + } + + if (options.onZoom && initialDistance > 0 && e.touches.length === 2) { + const endDistance = getDistance(e.touches[0], e.touches[1]); + const touchCenter = getTouchCenter(e.touches[0], e.touches[1]); + const dragOffsetX = touchCenter.x - initialTouchCenter.x; + const dragOffsetY = touchCenter.y - initialTouchCenter.y; + const zoomFactor = endDistance / initialDistance; + options.onZoom(e, { + zoomFactor, + initialCenterX: initialTouchCenter.x, + initialCenterY: initialTouchCenter.y, + dragOffsetX, + dragOffsetY, + currentCenterX: touchCenter.x, + currentCenterY: touchCenter.y, + }); + if (zoomFactor !== 1) hasMoved = true; + } + } + + const dragOffsetX = e.pageX! - captureEvent.pageX!; + const dragOffsetY = e.pageY! - captureEvent.pageY!; + + if (Math.abs(dragOffsetX) >= MOVE_THRESHOLD || Math.abs(dragOffsetY) >= MOVE_THRESHOLD) { + hasMoved = true; + } + + let shouldPreventScroll = false; + + if (options.onDrag) { + options.onDrag(e, captureEvent, { + dragOffsetX, + dragOffsetY, + }); + shouldPreventScroll = true; + } + + if (options.onSwipe && !hasSwiped) { + hasSwiped = onSwipe(e, dragOffsetX, dragOffsetY); + shouldPreventScroll = hasSwiped; + } + + if (IS_IOS && shouldPreventScroll && options.selectorToPreventScroll) { + Array.from(document.querySelectorAll(options.selectorToPreventScroll)) + .forEach((scrollable) => { + scrollable.style.overflow = 'hidden'; + }); + } + } + } + + function onSwipe(e: MouseEvent | RealTouchEvent, dragOffsetX: number, dragOffsetY: number) { + // Avoid conflicts with swipe-to-back gestures + if (IS_IOS) { + const x = (e as RealTouchEvent).touches[0].pageX; + if (x <= IOS_SCREEN_EDGE_THRESHOLD || x >= windowSize.get().width - IOS_SCREEN_EDGE_THRESHOLD) { + return false; + } + } + + const xAbs = Math.abs(dragOffsetX); + const yAbs = Math.abs(dragOffsetY); + const threshold = options.swipeThreshold ?? SWIPE_THRESHOLD_DEFAULT; + + let axis: TSwipeAxis | undefined; + if (xAbs > yAbs && xAbs >= threshold) { + axis = 'x'; + } else if (yAbs > xAbs && yAbs >= threshold) { + axis = 'y'; + } + + if (!axis) { + return false; + } + + if (!initialSwipeAxis) { + initialSwipeAxis = axis; + } else if (initialSwipeAxis !== axis) { + // Prevent horizontal swipe after vertical to prioritize scroll + return false; + } + + return processSwipe(e, axis, dragOffsetX, dragOffsetY, options.onSwipe!); + } + + const releaseWheelDrag = debounce(onRelease, RELEASE_WHEEL_DRAG_DELAY, false); + const releaseWheelZoom = debounce(onRelease, RELEASE_WHEEL_ZOOM_DELAY, false); + + function onWheelCapture(e: WheelEvent) { + if (hasMoved) return; + onCapture(e); + hasMoved = true; + initialTouchCenter = { x: e.x, y: e.y }; + } + + function onWheelZoom(e: WheelEvent) { + if (!options.onZoom) return; + onWheelCapture(e); + const dragOffsetX = e.x - initialTouchCenter.x; + const dragOffsetY = e.y - initialTouchCenter.y; + const delta = clamp(e.deltaY, -25, 25); + wheelZoom -= delta * 0.01; + wheelZoom = clamp(wheelZoom, minZoom * 0.5, maxZoom * 3); + isZooming = true; + options.onZoom(e, { + zoom: round(wheelZoom, 2), + initialCenterX: initialTouchCenter.x, + initialCenterY: initialTouchCenter.y, + dragOffsetX, + dragOffsetY, + currentCenterX: e.x, + currentCenterY: e.y, + }); + releaseWheelZoom(e); + } + + function onWheelDrag(e: WheelEvent) { + if (!options.onDrag) return; + onWheelCapture(e); + // Ignore wheel inertia if drag is canceled in this direction + if (!isDragCanceled.x || Math.sign(initialDragOffset.x) === Math.sign(e.deltaX)) { + initialDragOffset.x -= e.deltaX; + } + if (!isDragCanceled.y || Math.sign(initialDragOffset.y) === Math.sign(e.deltaY)) { + initialDragOffset.y -= e.deltaY; + } + const { x, y } = initialDragOffset; + options.onDrag(e, captureEvent!, { + dragOffsetX: x, + dragOffsetY: y, + }, (dx, dy) => { + isDragCanceled = { x: dx, y: dy }; + }); + releaseWheelDrag(e); + } + + function onWheel(e: WheelEvent) { + if (!options.onZoom && !options.onDrag) return; + if (options.excludedClosestSelector && ( + (e.target as HTMLElement).matches(options.excludedClosestSelector) + || (e.target as HTMLElement).closest(options.excludedClosestSelector) + )) { + return; + } + e.preventDefault(); + e.stopPropagation(); + const { doubleTapZoom = 3 } = options; + if (options.onDoubleClick && Object.is(e.deltaX, -0) && Object.is(e.deltaY, -0) && e.ctrlKey) { + onWheelCapture(e); + wheelZoom = wheelZoom > 1 ? 1 : doubleTapZoom; + options.onDoubleClick(e, { centerX: e.pageX, centerY: e.pageY }); + hasMoved = false; + return; + } + const metaKeyPressed = e.metaKey || e.ctrlKey || e.shiftKey; + if (metaKeyPressed) { + onWheelZoom(e); + } + if (!metaKeyPressed && !isZooming) { + // Check if this event produced by user scroll and not by inertia + const isUserEvent = lethargy.check(e); + if (wheelZoom !== 1 || isUserEvent) { + onWheelDrag(e); + } + } + } + + element.addEventListener('wheel', onWheel); + element.addEventListener('mousedown', onCapture); + document.body.addEventListener('touchstart', onCapture, { passive: !options.isNotPassive }); + + return () => { + onRelease(); + element.removeEventListener('wheel', onWheel); + document.body.removeEventListener('touchstart', onCapture); + element.removeEventListener('mousedown', onCapture); + }; +} + +function processSwipe( + e: Event, + currentSwipeAxis: TSwipeAxis, + dragOffsetX: number, + dragOffsetY: number, + onSwipe: (e: Event, direction: SwipeDirection, offsets: MoveOffsets) => boolean, +) { + const offsets = { dragOffsetX, dragOffsetY }; + + if (currentSwipeAxis === 'x') { + if (dragOffsetX < 0) { + return onSwipe(e, SwipeDirection.Left, offsets); + } else { + return onSwipe(e, SwipeDirection.Right, offsets); + } + } else if (currentSwipeAxis === 'y') { + if (dragOffsetY < 0) { + return onSwipe(e, SwipeDirection.Up, offsets); + } else { + return onSwipe(e, SwipeDirection.Down, offsets); + } + } + + return false; +} diff --git a/src/util/captureSwipe.ts b/src/util/captureSwipe.ts deleted file mode 100644 index d6827b16..00000000 --- a/src/util/captureSwipe.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { IS_IOS } from './windowEnvironment'; -import windowSize from './windowSize'; - -export enum SwipeDirection { - Up, - Down, - Left, - Right, -} - -// https://stackoverflow.com/questions/11287877/how-can-i-get-e-offsetx-on-mobile-ipad -// Android does not have this value, and iOS has it but as read-only -export interface RealTouchEvent extends TouchEvent { - pageX?: number; - pageY?: number; -} - -type TSwipeAxis = - 'x' - | 'y' - | undefined; - -export const IOS_SCREEN_EDGE_THRESHOLD = 20; -const SWIPE_THRESHOLD = 50; - -export function captureSwipe(element: HTMLElement, handleSwipe: (e: Event, direction: SwipeDirection) => boolean) { - let captureEvent: MouseEvent | RealTouchEvent | undefined; - let hasSwiped = false; - let initialSwipeAxis: TSwipeAxis | undefined; - - function onCapture(e: MouseEvent | RealTouchEvent) { - captureEvent = e; - - if (e.type === 'touchstart') { - // We need to always listen on `touchstart` target: - // https://stackoverflow.com/questions/33298828/touch-move-event-dont-fire-after-touch-start-target-is-removed - const target = e.target as HTMLElement; - target.addEventListener('touchmove', onMove, { passive: true }); - target.addEventListener('touchend', onRelease); - target.addEventListener('touchcancel', onRelease); - - if ('touches' in e) { - if (e.pageX === undefined) { - e.pageX = e.touches[0].pageX; - } - - if (e.pageY === undefined) { - e.pageY = e.touches[0].pageY; - } - } - } - } - - function onRelease() { - if (captureEvent) { - (captureEvent.target as HTMLElement).removeEventListener('touchcancel', onRelease); - (captureEvent.target as HTMLElement).removeEventListener('touchend', onRelease); - (captureEvent.target as HTMLElement).removeEventListener('touchmove', onMove); - } - - hasSwiped = false; - initialSwipeAxis = undefined; - captureEvent = undefined; - } - - function onMove(e: MouseEvent | RealTouchEvent) { - if (captureEvent) { - if (e.type === 'touchmove' && ('touches' in e)) { - if (e.pageX === undefined) { - e.pageX = e.touches[0].pageX; - } - - if (e.pageY === undefined) { - e.pageY = e.touches[0].pageY; - } - } - - const dragOffsetX = e.pageX! - captureEvent.pageX!; - const dragOffsetY = e.pageY! - captureEvent.pageY!; - - if (!hasSwiped) { - hasSwiped = onSwipe(e, dragOffsetX, dragOffsetY); - } - } - } - - function onSwipe(e: MouseEvent | RealTouchEvent, dragOffsetX: number, dragOffsetY: number) { - // Avoid conflicts with swipe-to-back gestures - if (IS_IOS) { - const x = (e as RealTouchEvent).touches[0].pageX; - if (x <= IOS_SCREEN_EDGE_THRESHOLD || x >= windowSize.get().width - IOS_SCREEN_EDGE_THRESHOLD) { - return false; - } - } - - const xAbs = Math.abs(dragOffsetX); - const yAbs = Math.abs(dragOffsetY); - - if (dragOffsetX && dragOffsetY) { - const ratio = Math.max(xAbs, yAbs) / Math.min(xAbs, yAbs); - // Diagonal swipe - if (ratio < 2) { - return false; - } - } - - let axis: TSwipeAxis | undefined; - if (xAbs >= SWIPE_THRESHOLD) { - axis = 'x'; - } else if (yAbs >= SWIPE_THRESHOLD) { - axis = 'y'; - } - - if (!axis) { - return false; - } - - if (!initialSwipeAxis) { - initialSwipeAxis = axis; - } else if (initialSwipeAxis !== axis) { - // Prevent horizontal swipe after vertical to prioritize scroll - return false; - } - - return processSwipe(e, axis, dragOffsetX, dragOffsetY, handleSwipe); - } - - element.addEventListener('touchstart', onCapture, { passive: true }); - - return () => { - onRelease(); - element.removeEventListener('touchstart', onCapture); - }; -} - -function processSwipe( - e: Event, - currentSwipeAxis: TSwipeAxis, - dragOffsetX: number, - dragOffsetY: number, - onSwipe: (e: Event, direction: SwipeDirection) => boolean, -) { - if (currentSwipeAxis === 'x') { - if (dragOffsetX < 0) { - return onSwipe(e, SwipeDirection.Left); - } else { - return onSwipe(e, SwipeDirection.Right); - } - } else if (currentSwipeAxis === 'y') { - if (dragOffsetY < 0) { - return onSwipe(e, SwipeDirection.Up); - } else { - return onSwipe(e, SwipeDirection.Down); - } - } - - return false; -} diff --git a/src/util/clipboard.ts b/src/util/clipboard.ts index 91f3c488..1cf99d44 100644 --- a/src/util/clipboard.ts +++ b/src/util/clipboard.ts @@ -7,24 +7,8 @@ textCopyEl.setAttribute('readonly', ''); textCopyEl.tabIndex = -1; textCopyEl.className = 'visually-hidden'; -export const copyTextToClipboard = (str: string): void => { - textCopyEl.value = str; - document.body.appendChild(textCopyEl); - const selection = document.getSelection(); - - if (selection) { - // Store previous selection - const rangeToRestore = selection.rangeCount > 0 && selection.getRangeAt(0); - textCopyEl.select(); - document.execCommand('copy'); - // Restore the original selection - if (rangeToRestore) { - selection.removeAllRanges(); - selection.addRange(rangeToRestore); - } - } - - document.body.removeChild(textCopyEl); +export const copyTextToClipboard = (str: string): Promise => { + return navigator.clipboard.writeText(str); }; export const copyImageToClipboard = (imageUrl?: string) => { diff --git a/src/util/createPostMessageInterface.ts b/src/util/createPostMessageInterface.ts index bd8f225d..df86fa7b 100644 --- a/src/util/createPostMessageInterface.ts +++ b/src/util/createPostMessageInterface.ts @@ -151,7 +151,7 @@ async function onMessage( ); } } catch (err: any) { - logDebugError('onMessage:callMethod', err); + logDebugError(name, err); if (messageId) { sendToOrigin({ diff --git a/src/util/cssAnimationEndListeners.ts b/src/util/cssAnimationEndListeners.ts index bd89eb15..2731339d 100644 --- a/src/util/cssAnimationEndListeners.ts +++ b/src/util/cssAnimationEndListeners.ts @@ -4,13 +4,13 @@ const ANIMATION_END_DELAY = 50; export function waitForTransitionEnd( node: Node, handler: NoneToVoidFunction, propertyName?: string, fallbackMs?: number, ) { - waitForEndEvent('transitionend', node, handler, propertyName, fallbackMs); + return waitForEndEvent('transitionend', node, handler, propertyName, fallbackMs); } export function waitForAnimationEnd( node: Node, handler: NoneToVoidFunction, animationName?: string, fallbackMs?: number, ) { - waitForEndEvent('animationend', node, handler, animationName, fallbackMs); + return waitForEndEvent('animationend', node, handler, animationName, fallbackMs); } function waitForEndEvent( @@ -22,6 +22,10 @@ function waitForEndEvent( ) { let isHandled = false; + function cleanup() { + node.removeEventListener(eventType, handleAnimationEnd); + } + function handleAnimationEnd(e: TransitionEvent | AnimationEvent | Event) { if (isHandled || e.target !== e.currentTarget) { return; @@ -36,7 +40,7 @@ function waitForEndEvent( isHandled = true; - node.removeEventListener(eventType, handleAnimationEnd); + cleanup(); setTimeout(() => { handler(); @@ -49,9 +53,11 @@ function waitForEndEvent( setTimeout(() => { if (isHandled) return; - node.removeEventListener(eventType, handleAnimationEnd); + cleanup(); handler(); }, fallbackMs); } + + return cleanup; } diff --git a/src/util/cssColorToHex.ts b/src/util/cssColorToHex.ts new file mode 100644 index 00000000..2ee2bc57 --- /dev/null +++ b/src/util/cssColorToHex.ts @@ -0,0 +1,11 @@ +export default function cssColorToHex(cssColor: string) { + if (/^#[0-9A-F]{6}$/i.test(cssColor)) return cssColor; + + return `#${cssColor.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.{0,1}\d*))?\)$/)! + .slice(1) + .map((n: string, i: number) => (i === 3 ? Math.round(parseFloat(n) * 255) : parseFloat(n)) + .toString(16) + .padStart(2, '0') + .replace('NaN', '')) + .join('')}`; +} diff --git a/src/util/debugOverlay.ts b/src/util/debugOverlay.ts index e30378c0..2f9d9917 100644 --- a/src/util/debugOverlay.ts +++ b/src/util/debugOverlay.ts @@ -1,4 +1,5 @@ -import { throttle } from './schedulers'; +import { animate } from './animation'; +import { fastRaf, throttle } from './schedulers'; const KEYS_TO_IGNORE = new Set([ 'TeactMemoWrapper renders', @@ -87,10 +88,33 @@ export function renderCounters() { .join('\n'); } +export function debugFps() { + if (!loggerEl) { + setupOverlay(); + } + + let ticks: number[] = []; + let lastFrameAt = performance.now(); + + animate(() => { + const now = performance.now(); + ticks.push(now - lastFrameAt); + lastFrameAt = now; + + if (ticks.length > 100) { + ticks = ticks.slice(-100); + } + + const avg = ticks.reduce((acc, t) => acc + t, 0) / ticks.length; + loggerEl!.innerHTML = `${Math.round(1000 / avg)} FPS`; + return true; + }, fastRaf); +} + function setupOverlay() { loggerEl = document.createElement('div'); loggerEl.style.cssText = 'position: absolute; left: 0; bottom: 25px; z-index: 9998; width: 260px; height: 200px;' - + ' border: 1px solid #555; background: rgba(255, 255, 255, 0.9); overflow: auto; font-size: 10px;'; + + ' border: 1px solid #555; background: rgba(255, 255, 255, 0.9); overflow: auto; font-size: 50px; color: black;'; document.body.appendChild(loggerEl); const clearEl = document.createElement('a'); diff --git a/src/util/deepDiff.ts b/src/util/deepDiff.ts new file mode 100644 index 00000000..5d0ec2b3 --- /dev/null +++ b/src/util/deepDiff.ts @@ -0,0 +1,63 @@ +const EQUAL = Symbol('EQUAL'); + +function deepAreSortedArraysEqual>(array1: T, array2: T) { + if (array1.length !== array2.length) { + return false; + } + + return array1.every((item, i) => deepDiff(item, array2[i]) === EQUAL); +} + +export function deepDiff(value1: T, value2: T): Partial | typeof EQUAL { + const type1 = typeof value1; + const type2 = typeof value2; + + if (value1 === value2) { + return EQUAL; + } + + if (type1 !== type2) { + return value2; + } + + if (type2 !== 'object') { + return value2; + } + + if (Array.isArray(value1) && Array.isArray(value2)) { + if (deepAreSortedArraysEqual(value1, value2)) return EQUAL; + + return value2; + } + + const object1 = value1 as AnyLiteral; + const object2 = value2 as AnyLiteral; + const keys1 = Array.from(new Set([...Object.keys(object1), ...Object.keys(object2)])); + + const reduced = keys1.reduce((acc: any, el) => { + if (object1[el] === object2[el]) { + return acc; + } + + const o1has = object1.hasOwnProperty(el); + const o2has = object2.hasOwnProperty(el); + if (!o2has) { + acc[el] = { __delete: true }; + return acc; + } + if (!o1has && o2has) { + acc[el] = object2[el]; + return acc; + } + + const diff = deepDiff(object1[el], object2[el]); + if (diff !== EQUAL) acc[el] = diff; + return acc; + }, {}); + + if (Object.keys(reduced).length === 0) { + return EQUAL; + } + + return reduced; +} diff --git a/src/util/deepMerge.ts b/src/util/deepMerge.ts new file mode 100644 index 00000000..50fccd97 --- /dev/null +++ b/src/util/deepMerge.ts @@ -0,0 +1,34 @@ +import { omit } from './iteratees'; + +export function deepMerge(value1: T, value2: Partial): T { + const type1 = typeof value1; + const type2 = typeof value2; + if (type1 !== 'object') { + return value2 as T; + } + + if (Array.isArray(value2)) { + return value2 as T; + } + + if (type1 !== type2) { + return value2 as T; + } + + if (value1 === value2) { + return value2 as T; + } + + const object1 = value1 as AnyLiteral; + const object2 = value2 as AnyLiteral; + const keys = Object.keys(object2); + // eslint-disable-next-line no-underscore-dangle + const keysDeleted = keys.filter((k) => object2[k]?.__delete); + // eslint-disable-next-line no-underscore-dangle + const keysNotDeleted = keys.filter((k) => !object2[k]?.__delete); + return keysNotDeleted.reduce((acc: any, key) => { + acc[key] = deepMerge(object1[key], object2[key]); + + return acc; + }, { ...omit(object1, keysDeleted) }); +} diff --git a/src/util/formatNumber.ts b/src/util/formatNumber.ts index 8cae785f..ba104f36 100644 --- a/src/util/formatNumber.ts +++ b/src/util/formatNumber.ts @@ -1,8 +1,12 @@ -import { DEFAULT_DECIMAL_PLACES, DEFAULT_PRICE_CURRENCY } from '../config'; +import type { ApiBaseCurrency } from '../api/types'; + +import { DEFAULT_DECIMAL_PLACES, DEFAULT_PRICE_CURRENCY, SHORT_CURRENCY_SYMBOL_MAP } from '../config'; import withCache from './withCache'; +const SHORT_SYMBOLS = new Set(Object.values(SHORT_CURRENCY_SYMBOL_MAP)); + export const formatInteger = withCache((value: number, fractionDigits = 2, noRadix = false) => { - const dp = value > 1 ? fractionDigits : DEFAULT_DECIMAL_PLACES; + const dp = value >= 1 ? fractionDigits : DEFAULT_DECIMAL_PLACES; const fixed = value.toFixed(dp); let [wholePart, fractionPart = ''] = fixed.split('.'); @@ -23,7 +27,7 @@ export const formatInteger = withCache((value: number, fractionDigits = 2, noRad export function formatCurrency(value: number, currency: string, fractionDigits?: number) { const formatted = formatInteger(value, fractionDigits); - return currency === '$' ? `$${formatted}`.replace('$-', '-$') : `${formatted} ${currency}`; + return addCurrency(formatted, currency); } export function formatCurrencyExtended(value: number, currency: string, noSign = false, fractionDigits?: number) { @@ -32,8 +36,24 @@ export function formatCurrencyExtended(value: number, currency: string, noSign = return prefix + formatCurrency(noSign ? value : Math.abs(value), currency, fractionDigits); } -export function formatCurrencyForBigValue(value: number, threshold = 1000) { - const formattedValue = formatCurrency(value, DEFAULT_PRICE_CURRENCY); +export function formatCurrencySimple(value: number, currency: string, decimals?: number) { + const stringValue = clearZeros(value.toFixed(decimals ?? DEFAULT_DECIMAL_PLACES)); + return addCurrency(stringValue, currency); +} + +function addCurrency(value: number | string, currency: string) { + return SHORT_SYMBOLS.has(currency) + ? `${currency}${value}`.replace(`${currency}-`, `-${currency}`) + : `${value} ${currency}`; +} + +function clearZeros(value: string) { + if (value.indexOf('.') === -1) return value; + return value.replace(/\.?0*$/, ''); +} + +export function formatCurrencyForBigValue(value: number, currency: string, threshold = 1000) { + const formattedValue = formatCurrency(value, currency); if (value < threshold) { return formattedValue; @@ -74,3 +94,8 @@ function toSignificant(value: string, fractionDigits: number): string { return value.slice(0, digitsLastIndex).replace(/0+$/, ''); } + +export function getShortCurrencySymbol(currency?: ApiBaseCurrency) { + if (!currency) currency = DEFAULT_PRICE_CURRENCY; + return SHORT_CURRENCY_SYMBOL_MAP[currency as keyof typeof SHORT_CURRENCY_SYMBOL_MAP] ?? currency; +} diff --git a/src/util/getIsAppUpdateNeeded.ts b/src/util/getIsAppUpdateNeeded.ts new file mode 100644 index 00000000..23f5ab1a --- /dev/null +++ b/src/util/getIsAppUpdateNeeded.ts @@ -0,0 +1,7 @@ +const APP_VERSION_REGEX = /^\d+\.\d+(\.\d+)?$/; + +export default function getIsAppUpdateNeeded(remoteVersion: string, appVersion: string) { + const sanitizedRemoteVersion = remoteVersion.trim(); + + return APP_VERSION_REGEX.test(sanitizedRemoteVersion) && sanitizedRemoteVersion !== appVersion; +} diff --git a/src/util/iteratees.ts b/src/util/iteratees.ts index f5f6acf0..54d5eb03 100644 --- a/src/util/iteratees.ts +++ b/src/util/iteratees.ts @@ -8,7 +8,7 @@ interface OrderCallback { (member: T): any; } -export function buildCollectionByKey(collection: T[], key: keyof T) { +export function buildCollectionByKey(collection: T[], key: keyof T): CollectionByKey { return collection.reduce((byKey: CollectionByKey, member: T) => { byKey[member[key]] = member; diff --git a/src/util/langProvider.ts b/src/util/langProvider.ts index 0983e78e..745f79b8 100644 --- a/src/util/langProvider.ts +++ b/src/util/langProvider.ts @@ -2,7 +2,7 @@ import type { TeactNode } from '../lib/teact/teact'; import type { LangCode, LangPack, LangString } from '../global/types'; -import { IS_ELECTRON, LANG_CACHE_NAME, LANG_LIST } from '../config'; +import { IS_ELECTRON_BUILD, LANG_CACHE_NAME, LANG_LIST } from '../config'; import renderText from '../global/helpers/renderText'; // @ts-ignore this file is autogenerated import defaultLangPackJson from '../i18n/en.json'; @@ -109,7 +109,7 @@ async function fetchRemote(langCode: string): Promise { return defaultLangPack; } - const response = await fetch(`${IS_ELECTRON ? '.' : '..'}/i18n/${langCode}.json`); + const response = await fetch(`${IS_ELECTRON_BUILD ? '.' : '..'}/i18n/${langCode}.json`); if (!response.ok) { const message = `An error has occured: ${response.status}`; diff --git a/src/util/ledger/index.ts b/src/util/ledger/index.ts index 1b4c6354..50e1202a 100644 --- a/src/util/ledger/index.ts +++ b/src/util/ledger/index.ts @@ -22,19 +22,19 @@ import { import { TON_TOKEN_SLUG } from '../../config'; import { callApi } from '../../api'; -import { getWalletBalance } from '../../api/blockchains/ton'; -import { TOKEN_TRANSFER_TON_AMOUNT, TOKEN_TRANSFER_TON_FORWARD_AMOUNT } from '../../api/blockchains/ton/constants'; -import { toBase64Address } from '../../api/blockchains/ton/util/tonweb'; -import { ApiUserRejectsError } from '../../api/errors'; +import { + DEFAULT_IS_BOUNCEABLE, + TOKEN_TRANSFER_TON_AMOUNT, + TOKEN_TRANSFER_TON_FORWARD_AMOUNT, +} from '../../api/blockchains/ton/constants'; +import { ApiUserRejectsError, handleServerError } from '../../api/errors'; import { parseAccountId } from '../account'; -import { range } from '../iteratees'; import { logDebugError } from '../logs'; import { pause } from '../schedulers'; import { isValidLedgerComment } from './utils'; const CHAIN = 0; // workchain === -1 ? 255 : 0; const VERSION = 'v4R2'; -const ACCOUNTS_PAGE = 9; const ATTEMPTS = 10; const PAUSE = 125; const IS_BOUNCEABLE = false; @@ -145,9 +145,10 @@ export async function submitLedgerTransfer(options: ApiSubmitTransferOptions) { ]); let payload: TonPayloadFormat | undefined; - let isBounceable = Address.parseFriendly(toAddress).isBounceable; + const parsedAddress = Address.parseFriendly(toAddress); + let isBounceable = parsedAddress.isBounceable; // Force default bounceable address for `waitTxComplete` to work properly - const normalizedAddress = toBase64Address(toAddress); + const normalizedAddress = parsedAddress.address.toString({ urlSafe: true, bounceable: DEFAULT_IS_BOUNCEABLE }); if (slug !== TON_TOKEN_SLUG) { ({ toAddress, amount, payload } = await buildLedgerTokenTransfer( @@ -272,7 +273,7 @@ export async function signLedgerTransactions( ledgerPayload = undefined; break; } - case 'transfer-nft': { + case 'nft:transfer': { const { queryId, newOwner, @@ -296,7 +297,7 @@ export async function signLedgerTransactions( }; break; } - case 'transfer-tokens': { + case 'tokens:transfer': { const { queryId, amount: jettonAmount, @@ -389,12 +390,28 @@ export async function signLedgerProof(accountId: string, proof: ApiTonConnectPro return result.signature.toString('base64'); } -export function getFirstLedgerWallets(network: ApiNetwork) { - const accountIndexes = range(0, ACCOUNTS_PAGE); +export async function getNextLedgerWallets(network: ApiNetwork, lastExistingIndex = -1) { + const result: LedgerWalletInfo[] = []; + let index = lastExistingIndex + 1; + + try { + // eslint-disable-next-line no-constant-condition + while (true) { + const walletInfo = await getLedgerWalletInfo(network, index, IS_BOUNCEABLE); + if (walletInfo.balance !== '0') { + result.push(walletInfo); + index += 1; + continue; + } - return Promise.all(accountIndexes.map((index) => { - return getLedgerWalletInfo(network, index, IS_BOUNCEABLE); - })); + if (!result.length) { + result.push(walletInfo); + } + return result; + } + } catch (err) { + return handleServerError(err); + } } export async function getLedgerWalletInfo( @@ -403,7 +420,7 @@ export async function getLedgerWalletInfo( isBounceable: boolean, ): Promise { const { address, publicKey } = await getLedgerWalletAddress(accountIndex, isBounceable); - const balance = await getWalletBalance(network, address); + const balance = (await callApi('getWalletBalance', network, address))!; return { index: accountIndex, diff --git a/src/util/ledger/tab.ts b/src/util/ledger/tab.ts index bb5d75bc..661d7893 100644 --- a/src/util/ledger/tab.ts +++ b/src/util/ledger/tab.ts @@ -1,15 +1,29 @@ export const DETACHED_TAB_URL = '#detached'; +let ledgerTabId: number | undefined; + export function openLedgerTab() { return createLedgerTab(); } +export async function closeLedgerTab() { + if (!ledgerTabId) return; + + await chrome.tabs.query({ active: true }, () => { + if (!ledgerTabId) return; + + chrome.tabs.remove(ledgerTabId); + }); +} + export function onLedgerTabClose(id: number, onClose: () => void) { chrome.tabs.onRemoved.addListener((closedTabId: number) => { if (closedTabId !== id) { return; } + ledgerTabId = undefined; + onClose(); }); } @@ -17,5 +31,8 @@ export function onLedgerTabClose(id: number, onClose: () => void) { async function createLedgerTab() { const tab = await chrome.tabs.create({ url: `index.html${DETACHED_TAB_URL}`, active: true }); await chrome.windows.update(tab.windowId!, { focused: true }); - return tab.id!; + + ledgerTabId = tab.id!; + + return ledgerTabId; } diff --git a/src/util/lethargy.ts b/src/util/lethargy.ts new file mode 100644 index 00000000..c5019895 --- /dev/null +++ b/src/util/lethargy.ts @@ -0,0 +1,99 @@ +/** + * Lethargy help distinguish between scroll events initiated by the user, and those by inertial scrolling. + * Lethargy does not have external dependencies. + * + * @param stability - Specifies the length of the rolling average. + * In effect, the larger the value, the smoother the curve will be. + * This attempts to prevent anomalies from firing 'real' events. Valid values are all positive integers, + * but in most cases, you would need to stay between 5 and around 30. + * + * @param sensitivity - Specifies the minimum value for wheelDelta for it to register as a valid scroll event. + * Because the tail of the curve have low wheelDelta values, + * this will stop them from registering as valid scroll events. + * The unofficial standard wheelDelta is 120, so valid values are positive integers below 120. + * + * @param tolerance - Prevent small fluctuations from affecting results. + * Valid values are decimals from 0, but should ideally be between 0.05 and 0.3. + * + * Based on https://github.com/d4nyll/lethargy + */ + +export type LethargyConfig = { + stability?: number; + sensitivity?: number; + tolerance?: number; + delay?: number; +}; + +export class Lethargy { + stability: number; + + sensitivity: number; + + tolerance: number; + + delay: number; + + lastUpDeltas: Array; + + lastDownDeltas: Array; + + deltasTimestamp: Array; + + constructor({ + stability = 8, + sensitivity = 100, + tolerance = 1.1, + delay = 150, + }: LethargyConfig = {}) { + this.stability = stability; + this.sensitivity = sensitivity; + this.tolerance = tolerance; + this.delay = delay; + this.lastUpDeltas = new Array(this.stability * 2).fill(0); + this.lastDownDeltas = new Array(this.stability * 2).fill(0); + this.deltasTimestamp = new Array(this.stability * 2).fill(0); + } + + check(e: any) { + let lastDelta; + e = e.originalEvent || e; + if (e.wheelDelta !== undefined) { + lastDelta = e.wheelDelta; + } else if (e.deltaY !== undefined) { + lastDelta = e.deltaY * -40; + } else if (e.detail !== undefined || e.detail === 0) { + lastDelta = e.detail * -40; + } + this.deltasTimestamp.push(Date.now()); + this.deltasTimestamp.shift(); + if (lastDelta > 0) { + this.lastUpDeltas.push(lastDelta); + this.lastUpDeltas.shift(); + return this.isInertia(1); + } else { + this.lastDownDeltas.push(lastDelta); + this.lastDownDeltas.shift(); + return this.isInertia(-1); + } + } + + isInertia(direction: number) { + const lastDeltas = direction === -1 ? this.lastDownDeltas : this.lastUpDeltas; + if (lastDeltas[0] === undefined) return direction; + if ( + this.deltasTimestamp[this.stability * 2 - 2] + this.delay > Date.now() + && lastDeltas[0] === lastDeltas[this.stability * 2 - 1] + ) { + return false; + } + const lastDeltasOld = lastDeltas.slice(0, this.stability); + const lastDeltasNew = lastDeltas.slice(this.stability, this.stability * 2); + const oldSum = lastDeltasOld.reduce((t, s) => t + s); + const newSum = lastDeltasNew.reduce((t, s) => t + s); + const oldAverage = oldSum / lastDeltasOld.length; + const newAverage = newSum / lastDeltasNew.length; + return Math.abs(oldAverage) < Math.abs(newAverage * this.tolerance) + && this.sensitivity < Math.abs(newAverage); + } +} diff --git a/src/util/logs.ts b/src/util/logs.ts index c347f582..d5e8f351 100644 --- a/src/util/logs.ts +++ b/src/util/logs.ts @@ -10,6 +10,6 @@ export function logDebugError(message: string, ...args: any[]) { export function logDebug(message: any, ...args: any[]) { if (DEBUG) { // eslint-disable-next-line no-console - console.log('[DEBUG]', message, ...args); + console.log(`[DEBUG] ${message}`, ...args); } } diff --git a/src/util/math.ts b/src/util/math.ts index 40c989bf..c9561d22 100644 --- a/src/util/math.ts +++ b/src/util/math.ts @@ -1 +1,11 @@ +export const clamp = (num: number, min: number, max: number) => (Math.min(max, Math.max(min, num))); export const isBetween = (num: number, min: number, max: number) => (num >= min && num <= max); +export const round = (num: number, decimals: number = 0) => Math.round(num * 10 ** decimals) / 10 ** decimals; +export const lerp = (start: number, end: number, interpolationRatio: number) => { + return (1 - interpolationRatio) * start + interpolationRatio * end; +}; + +// Fractional values cause blurry text & canvas. Round to even to keep whole numbers while centering +export function roundToNearestEven(value: number) { + return Math.round(value / 2) * 2; +} diff --git a/src/util/metadata.ts b/src/util/metadata.ts new file mode 100644 index 00000000..1953191a --- /dev/null +++ b/src/util/metadata.ts @@ -0,0 +1,29 @@ +import { BRILLIANT_API_BASE_URL, IS_CAPACITOR } from '../config'; + +const IPFS_GATEWAY_BASE_URL: string = 'https://ipfs.io/ipfs/'; + +export function fetchJsonMetadata(url: string) { + url = fixIpfsUrl(url); + + const reserveUrl = `${BRILLIANT_API_BASE_URL}/utils/download-json?url=${url}`; + + if (IS_CAPACITOR) { + return fetchJson(reserveUrl); + } + + return fetchJson(url).catch(() => { + return fetchJson(reserveUrl); + }); +} + +export function fixIpfsUrl(url: string) { + return url.replace('ipfs://', IPFS_GATEWAY_BASE_URL); +} + +async function fetchJson(url: string) { + const response = await fetch(url); + if (!response.ok) { + throw Error(`Http error ${response.status}`); + } + return response.json(); +} diff --git a/src/util/modalSwipeManager.ts b/src/util/modalSwipeManager.ts new file mode 100644 index 00000000..689f6308 --- /dev/null +++ b/src/util/modalSwipeManager.ts @@ -0,0 +1,13 @@ +let counter = 0; + +export function disableSwipeToClose() { + counter += 1; +} + +export function enableSwipeToClose() { + counter -= 1; +} + +export function getIsSwipeToCloseDisabled() { + return counter > 0; +} diff --git a/src/util/multitab.ts b/src/util/multitab.ts new file mode 100644 index 00000000..6544a9f8 --- /dev/null +++ b/src/util/multitab.ts @@ -0,0 +1,80 @@ +import { addCallback } from '../lib/teact/teactn'; +import { getGlobal, setGlobal } from '../global'; + +import type { GlobalState } from '../global/types'; + +import { MULTITAB_DATA_CHANNEL_NAME } from '../config'; +import { deepDiff } from './deepDiff'; +import { deepMerge } from './deepMerge'; +import { omit } from './iteratees'; +import { IS_MULTITAB_SUPPORTED } from './windowEnvironment'; + +import { isBackgroundModeActive } from '../hooks/useBackgroundMode'; + +interface BroadcastChannelGlobalDiff { + type: 'globalDiffUpdate'; + diff: any; +} + +type BroadcastChannelMessage = BroadcastChannelGlobalDiff; +type EventListener = (type: 'message', listener: (event: { data: BroadcastChannelMessage }) => void) => void; + +export type TypedBroadcastChannel = { + postMessage: (message: BroadcastChannelMessage) => void; + addEventListener: EventListener; + removeEventListener: EventListener; +}; + +const channel = IS_MULTITAB_SUPPORTED + ? new BroadcastChannel(MULTITAB_DATA_CHANNEL_NAME) as TypedBroadcastChannel + : undefined; + +let currentGlobal = getGlobal(); + +export function initMultitab({ noPub, noSub }: { noPub?: boolean; noSub?: boolean } = {}) { + if (!channel) return; + + if (!noPub) { + addCallback(handleGlobalChange); + } + + if (!noSub) { + channel.addEventListener('message', handleMultitabMessage); + } +} + +function handleGlobalChange(global: GlobalState) { + if (global === currentGlobal) return; + + if (isBackgroundModeActive()) { + currentGlobal = global; + return; + } + + const diff = deepDiff(omitLocalOnlyKeys(currentGlobal), omitLocalOnlyKeys(global)); + + if (typeof diff !== 'symbol') { + channel!.postMessage({ + type: 'globalDiffUpdate', + diff, + }); + } + + currentGlobal = global; +} + +function omitLocalOnlyKeys(global: GlobalState) { + return omit(global, ['DEBUG_capturedId']); +} + +function handleMultitabMessage({ data }: { data: BroadcastChannelMessage }) { + switch (data.type) { + case 'globalDiffUpdate': { + currentGlobal = deepMerge(getGlobal(), data.diff); + + setGlobal(currentGlobal); + + break; + } + } +} diff --git a/src/util/processDeeplink.ts b/src/util/processDeeplink.ts new file mode 100644 index 00000000..1e038055 --- /dev/null +++ b/src/util/processDeeplink.ts @@ -0,0 +1,30 @@ +import { BottomSheet } from 'native-bottom-sheet'; +import { getActions } from '../global'; + +import { TON_TOKEN_SLUG } from '../config'; +import { bigStrToHuman } from '../global/helpers'; +import { parseTonDeeplink } from './ton/deeplinks'; +import { pause } from './schedulers'; +import { CAN_DELEGATE_BOTTOM_SHEET } from './windowEnvironment'; + +// Both to close current Transfer Modal and delay when app launch +const PAUSE = 700; +export async function processDeeplink(url: string) { + const params = parseTonDeeplink(url); + if (!params) return false; + + if (CAN_DELEGATE_BOTTOM_SHEET) { + await BottomSheet.release({ key: '*' }); + await pause(PAUSE); + } + + getActions().startTransfer({ + isPortrait: true, + tokenSlug: TON_TOKEN_SLUG, + toAddress: params.to, + amount: params.amount ? bigStrToHuman(params.amount) : undefined, + comment: params.comment, + }); + + return true; +} diff --git a/src/util/random.ts b/src/util/random.ts index f1dec00f..1c83e6ca 100644 --- a/src/util/random.ts +++ b/src/util/random.ts @@ -5,3 +5,8 @@ export function random(min: number, max: number) { export function sample(arr: T[]) { return arr[random(0, arr.length - 1)]; } + +export function randomBytes(size: number) { + // eslint-disable-next-line no-restricted-globals + return self.crypto.getRandomValues(new Uint8Array(size)); +} diff --git a/src/util/resetScroll.ts b/src/util/resetScroll.ts new file mode 100644 index 00000000..8e1045d9 --- /dev/null +++ b/src/util/resetScroll.ts @@ -0,0 +1,17 @@ +import { IS_IOS } from './windowEnvironment'; + +const resetScroll = (container: HTMLDivElement, scrollTop?: number) => { + if (IS_IOS) { + container.style.overflow = 'hidden'; + } + + if (scrollTop !== undefined) { + container.scrollTop = scrollTop; + } + + if (IS_IOS) { + container.style.overflow = ''; + } +}; + +export default resetScroll; diff --git a/src/util/resolveModalTransitionName.ts b/src/util/resolveModalTransitionName.ts new file mode 100644 index 00000000..40c6642b --- /dev/null +++ b/src/util/resolveModalTransitionName.ts @@ -0,0 +1,5 @@ +import { IS_ANDROID, IS_IOS } from './windowEnvironment'; + +export default function resolveModalTransitionName() { + return IS_ANDROID ? 'slideFadeAndroid' : IS_IOS ? 'slideLayers' : 'slideFade'; +} diff --git a/src/util/saveCaretPosition.ts b/src/util/saveCaretPosition.ts index a1d6b9ae..39f6f3f4 100644 --- a/src/util/saveCaretPosition.ts +++ b/src/util/saveCaretPosition.ts @@ -6,7 +6,7 @@ export function saveCaretPosition(context: HTMLElement, decimals: number) { const range = selection.getRangeAt(0); range.setStart(context, 0); - const clearedValue = range.toString().match(new RegExp(`(\\d+(?:\\.\\d{0,${decimals}})?)`)); + const clearedValue = range.toString().match(new RegExp(`(\\d+(?:[.,]\\d{0,${decimals}})?)`)); const len = clearedValue?.[0]?.length || range.toString().length || 0; return function restore() { diff --git a/src/util/schedulers.ts b/src/util/schedulers.ts index ee988e9e..8a4e22ee 100644 --- a/src/util/schedulers.ts +++ b/src/util/schedulers.ts @@ -104,7 +104,7 @@ export function rafPromise() { }); } -const FAST_RAF_TIMEOUT_FALLBACK_MS = 300; +const FAST_RAF_TIMEOUT_FALLBACK_MS = 35; // < 30 FPS let fastRafCallbacks: Set | undefined; let fastRafFallbackCallbacks: Set | undefined; diff --git a/src/util/swap/buildSwapId.ts b/src/util/swap/buildSwapId.ts new file mode 100644 index 00000000..fece7716 --- /dev/null +++ b/src/util/swap/buildSwapId.ts @@ -0,0 +1,3 @@ +export function buildSwapId(backendId: string) { + return `swap:${backendId}`; +} diff --git a/src/util/swap/getBlockchainNetworkIcon.ts b/src/util/swap/getBlockchainNetworkIcon.ts new file mode 100644 index 00000000..12eafe09 --- /dev/null +++ b/src/util/swap/getBlockchainNetworkIcon.ts @@ -0,0 +1,53 @@ +import avalancheBlockchainIcon from '../../assets/blockchain/chain_avalanche.png'; +import bitcoinBlockchainIcon from '../../assets/blockchain/chain_bitcoin.png'; +import bitcoincashBlockchainIcon from '../../assets/blockchain/chain_bitcoincash.png'; +import bnbBlockchainIcon from '../../assets/blockchain/chain_bnb.png'; +import cardanoBlockchainIcon from '../../assets/blockchain/chain_cardano.png'; +import cosmosBlockchainIcon from '../../assets/blockchain/chain_cosmos.png'; +import dashBlockchainIcon from '../../assets/blockchain/chain_dash.png'; +import dogeBlockchainIcon from '../../assets/blockchain/chain_doge.png'; +import eosBlockchainIcon from '../../assets/blockchain/chain_eos.png'; +import ethereumBlockchainIcon from '../../assets/blockchain/chain_ethereum.png'; +import ethereumclassicBlockchainIcon from '../../assets/blockchain/chain_ethereumclassic.png'; +import internetcomputerBlockchainIcon from '../../assets/blockchain/chain_internetcomputer.png'; +import iotaBlockchainIcon from '../../assets/blockchain/chain_iota.png'; +import litecoinBlockchainIcon from '../../assets/blockchain/chain_litecoin.png'; +import moneroBlockchainIcon from '../../assets/blockchain/chain_monero.png'; +import polkadotBlockchainIcon from '../../assets/blockchain/chain_polkadot.png'; +import rippleBlockchainIcon from '../../assets/blockchain/chain_ripple.png'; +import solanaBlockchainIcon from '../../assets/blockchain/chain_solana.png'; +import stellarBlockchainIcon from '../../assets/blockchain/chain_stellar.png'; +import tonBlockchainIcon from '../../assets/blockchain/chain_ton.png'; +import tronBlockchainIcon from '../../assets/blockchain/chain_tron.png'; +import zcashBlockchainIcon from '../../assets/blockchain/chain_zcash.png'; + +const BLOCKCHAIN_ICON_MAP: Record = { + avalanche: avalancheBlockchainIcon, + bitcoin: bitcoinBlockchainIcon, + bitcoin_cash: bitcoincashBlockchainIcon, + binance_smart_chain: bnbBlockchainIcon, + binance_dex: bnbBlockchainIcon, + cardano: cardanoBlockchainIcon, + cosmos: cosmosBlockchainIcon, + dash: dashBlockchainIcon, + doge: dogeBlockchainIcon, + eos: eosBlockchainIcon, + ethereum: ethereumBlockchainIcon, + ethereum_classic: ethereumclassicBlockchainIcon, + internet_computer: internetcomputerBlockchainIcon, + iota: iotaBlockchainIcon, + litecoin: litecoinBlockchainIcon, + monero: moneroBlockchainIcon, + polkadot: polkadotBlockchainIcon, + ripple: rippleBlockchainIcon, + solana: solanaBlockchainIcon, + stellar: stellarBlockchainIcon, + ton: tonBlockchainIcon, + tron: tronBlockchainIcon, + zcash: zcashBlockchainIcon, +}; +export default function getBlockchainNetworkIcon(networkName?: string) { + if (!networkName) return ''; + + return BLOCKCHAIN_ICON_MAP[networkName] ?? networkName; +} diff --git a/src/util/swap/getBlockchainNetworkName.ts b/src/util/swap/getBlockchainNetworkName.ts new file mode 100644 index 00000000..6f288532 --- /dev/null +++ b/src/util/swap/getBlockchainNetworkName.ts @@ -0,0 +1,31 @@ +const NETWORK_NAMES_EXCEPTIONS: Record = { + binance_smart_chain: 'Binance Smart Chain', + internet_computer: 'Internet Computer', + ethereum_classic: 'Ethereum Classic', + bitcoin_cash: 'Bitcoin Cash', + binance_dex: 'Binance Dex', + ton: 'TON', + bitcoin: 'Bitcoin', + ethereum: 'Ethereum', + solana: 'Solana', + tron: 'TRON', + stellar: 'Stellar', + doge: 'DOGE', + eos: 'EOS', + avalanche: 'Avalanche', + cardano: 'Cardano', + monero: 'Monero', + dash: 'Dash', + ripple: 'Ripple', + cosmos: 'Cosmos', + litecoin: 'Litecoin', + zcash: 'Zcash', + polkadot: 'Polkadot', + iota: 'IOTA', +}; + +export default function getBlockchainNetworkName(networkName?: string) { + if (!networkName) return ''; + + return NETWORK_NAMES_EXCEPTIONS[networkName] ?? networkName; +} diff --git a/src/util/swap/getSwapRate.ts b/src/util/swap/getSwapRate.ts new file mode 100644 index 00000000..870e4f6c --- /dev/null +++ b/src/util/swap/getSwapRate.ts @@ -0,0 +1,49 @@ +import type { ApiSwapAsset } from '../../api/types'; + +import { TON_SYMBOL } from '../../config'; +import { Big } from '../../lib/big.js'; +import { formatInteger } from '../formatNumber'; + +const BTC = new Set(['jWBTC', 'oWBTC', 'BTC']); +const USD = new Set(['jUSDT', 'oUSDT', 'USDT', 'jUSDC', 'oUSDC', 'USDC']); + +const LARGE_NUMBER = 1000; + +export default function getSwapRate( + fromAmount?: string, + toAmount?: string, + fromToken?: ApiSwapAsset, + toToken?: ApiSwapAsset, + shouldTrimLargeNumber = false, +) { + if (!fromAmount || !toAmount || !fromToken || !toToken) { + return undefined; + } + + let firstCurrencySymbol = fromToken.symbol; + let secondCurrencySymbol = toToken.symbol; + let price: string; + + if ( + BTC.has(secondCurrencySymbol) + || (USD.has(secondCurrencySymbol) && firstCurrencySymbol !== TON_SYMBOL) + || (USD.has(firstCurrencySymbol) && secondCurrencySymbol === TON_SYMBOL) + || (firstCurrencySymbol === TON_SYMBOL && !USD.has(secondCurrencySymbol)) + ) { + firstCurrencySymbol = toToken.symbol; + secondCurrencySymbol = fromToken.symbol; + const ratio = new Big(fromAmount).div(toAmount); + const isLargeNumber = shouldTrimLargeNumber && ratio.gte(LARGE_NUMBER); + price = formatInteger(ratio.toNumber(), isLargeNumber ? 0 : 2); + } else { + const ratio = new Big(toAmount).div(fromAmount); + const isLargeNumber = shouldTrimLargeNumber && ratio.gte(LARGE_NUMBER); + price = formatInteger(ratio.toNumber(), isLargeNumber ? 0 : 2); + } + + return { + firstCurrencySymbol, + secondCurrencySymbol, + price, + }; +} diff --git a/src/util/swipeController.ts b/src/util/swipeController.ts new file mode 100644 index 00000000..02268faa --- /dev/null +++ b/src/util/swipeController.ts @@ -0,0 +1,186 @@ +import type { MoveOffsets } from './captureEvents'; + +import { requestMeasure, requestMutation } from '../lib/fasterdom/fasterdom'; +import { animateNumber, timingFunctions } from './animation'; +import { captureEvents, SwipeDirection } from './captureEvents'; +import { waitForAnimationEnd } from './cssAnimationEndListeners'; +import { clamp } from './math'; +import { IS_IOS } from './windowEnvironment'; + +const INERTIA_DURATION = 300; +const INERTIA_EASING = timingFunctions.easeOutCubic; + +let isSwipeActive = false; +let swipeOffsets: MoveOffsets | undefined; +let onDrag: ((offsets: MoveOffsets) => void) | undefined; +let onRelease: ((onCancel: NoneToVoidFunction) => void) | undefined; +let cancelCurrentReleaseAnimation: NoneToVoidFunction | undefined; + +export function captureControlledSwipe( + element: HTMLElement, options: { + onSwipeLeftStart?: NoneToVoidFunction; + onSwipeRightStart?: NoneToVoidFunction; + onCancel: NoneToVoidFunction; + }, +) { + return captureEvents(element, { + swipeThreshold: 10, + + onSwipe(e, direction, offsets) { + if (direction === SwipeDirection.Left) { + options.onSwipeLeftStart?.(); + } else if (direction === SwipeDirection.Right) { + options.onSwipeRightStart?.(); + } else { + return false; + } + + if (IS_IOS) { + isSwipeActive = true; + swipeOffsets = offsets; + } + + return true; + }, + + onDrag(e, captureEvent, offsets) { + if (!isSwipeActive) return; + + onDrag?.(offsets); + }, + + onRelease() { + if (!isSwipeActive) return; + + isSwipeActive = false; + + onRelease?.(options.onCancel); + + onDrag = undefined; + onRelease = undefined; + }, + }); +} + +export function allowSwipeControlForTransition( + currentSlide: HTMLElement, + nextSlide: HTMLElement, + onCancelForTransition: NoneToVoidFunction, +) { + cancelCurrentReleaseAnimation?.(); + + if (!isSwipeActive) return; + + const targetPosition = extractAnimationEndPosition(currentSlide); + if (!targetPosition) return; + + currentSlide.getAnimations().forEach((a) => a.pause()); + nextSlide.getAnimations().forEach((a) => a.pause()); + + currentSlide.style.animationTimingFunction = 'linear'; + nextSlide.style.animationTimingFunction = 'linear'; + + let currentDirection: 1 | -1 | undefined; + + requestMeasure(() => { + const computedStyle = getComputedStyle(currentSlide); + const initialPositionPx = extractPositionFromMatrix(computedStyle.transform, targetPosition.axis); + const targetPositionPx = targetPosition.units === 'px' + ? targetPosition.value + : ((targetPosition.value / 100) * ( + targetPosition.axis === 'X' ? currentSlide.offsetWidth : currentSlide.offsetHeight + )); + const distance = targetPositionPx - initialPositionPx; + + let progress = 0; + + onDrag = ({ dragOffsetX, dragOffsetY }) => { + const dragOffset = targetPosition.axis === 'X' + ? dragOffsetX - swipeOffsets!.dragOffsetX + : dragOffsetY - swipeOffsets!.dragOffsetY; + + const newProgress = clamp(dragOffset / distance, 0, 1); + currentDirection = newProgress > progress ? 1 : -1; + progress = newProgress; + + updateAnimationProgress([currentSlide, nextSlide], progress); + }; + + onRelease = (onCancelForClient: NoneToVoidFunction) => { + const isRevertSwipe = currentDirection === -1; + + function cleanup() { + currentSlide.getAnimations().forEach((a) => a.cancel()); + nextSlide.getAnimations().forEach((a) => a.cancel()); + + requestMutation(() => { + currentSlide.style.animationTimingFunction = ''; + nextSlide.style.animationTimingFunction = ''; + }); + } + + if (!isRevertSwipe) { + // For some reason animations are not cleared when CSS class is removed + waitForAnimationEnd(currentSlide, cleanup); + } + + cancelCurrentReleaseAnimation = animateNumber({ + from: progress, + to: isRevertSwipe ? 0 : 1, + duration: INERTIA_DURATION, + timing: INERTIA_EASING, + onUpdate(releaseProgress) { + updateAnimationProgress([currentSlide, nextSlide], releaseProgress); + }, + onEnd(isCanceled = false) { + cancelCurrentReleaseAnimation = undefined; + + if (isCanceled || isRevertSwipe) { + cleanup(); + onCancelForTransition(); + onCancelForClient(); + } + }, + }); + }; + }); +} + +function updateAnimationProgress(elements: HTMLElement[], progress: number) { + elements.map((e) => e.getAnimations()).flat().forEach((animation) => { + animation.currentTime = (animation.effect!.getTiming().duration as number) * progress; + }); +} + +function extractAnimationEndPosition(element: HTMLElement) { + for (const animation of element.getAnimations()) { + if (!(animation.effect instanceof KeyframeEffect)) continue; + + for (const keyframe of animation.effect.getKeyframes()) { + if (keyframe.offset !== 1 || !keyframe.transform) continue; + + const position = extractPositionFromTransform(keyframe.transform as string); + if (position) { + return position; + } + } + } + + return undefined; +} + +function extractPositionFromTransform(transformRule: string) { + const match = transformRule.match(/([XY])\((-?\d+)(%|px)\)/); + if (!match) return undefined; + + return { + axis: match[1] as 'X' | 'Y', + value: Number(match[2]), + units: match[3], + }; +} + +function extractPositionFromMatrix(transform: string, axis: 'X' | 'Y') { + const matrix = transform.slice(7, -1).split(',').map(Number); + return matrix[axis === 'X' ? 4 : 5]; +} diff --git a/src/util/switchTheme.ts b/src/util/switchTheme.ts index 9d928c71..2d5309ef 100644 --- a/src/util/switchTheme.ts +++ b/src/util/switchTheme.ts @@ -1,6 +1,8 @@ import type { Theme } from '../global/types'; +import { IS_CAPACITOR } from '../config'; import { requestMeasure } from '../lib/fasterdom/fasterdom'; +import { switchStatusBar } from './capacitor'; const prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); let currentTheme: Theme; @@ -19,6 +21,11 @@ function setThemeValue() { ); } +function handlePrefersColorSchemeChange() { + setThemeValue(); + setStatusBarStyle(); +} + function setThemeColor() { requestMeasure(() => { const color = getComputedStyle(document.documentElement) @@ -30,4 +37,10 @@ function setThemeColor() { }); } -prefersDark.addEventListener('change', setThemeValue); +export function setStatusBarStyle(forceDarkBackground?: boolean) { + if (!IS_CAPACITOR) return; + + switchStatusBar(currentTheme, prefersDark.matches, forceDarkBackground); +} + +prefersDark.addEventListener('change', handlePrefersColorSchemeChange); diff --git a/src/util/ton/deeplinks.ts b/src/util/ton/deeplinks.ts new file mode 100644 index 00000000..dadaf686 --- /dev/null +++ b/src/util/ton/deeplinks.ts @@ -0,0 +1,26 @@ +import { TON_PROTOCOL, TONCONNECT_PROTOCOL, TONCONNECT_UNIVERSAL_URL } from '../../config'; + +export function parseTonDeeplink(value: string | unknown) { + if (typeof value !== 'string' || !isTonDeeplink(value) || !value.includes('/transfer/')) { + return undefined; + } + + try { + const url = new URL(value); + return { + to: url.pathname.replace(/.*\//, ''), + amount: url.searchParams.get('amount') ?? undefined, + comment: url.searchParams.get('text') ?? undefined, + }; + } catch (err) { + return undefined; + } +} + +export function isTonDeeplink(url: string) { + return url.startsWith(TON_PROTOCOL); +} + +export function isTonConnectDeeplink(url: string) { + return url.startsWith(TONCONNECT_PROTOCOL) || url.startsWith(TONCONNECT_UNIVERSAL_URL); +} diff --git a/src/util/windowEnvironment.ts b/src/util/windowEnvironment.ts index 8ebe625c..6585b127 100644 --- a/src/util/windowEnvironment.ts +++ b/src/util/windowEnvironment.ts @@ -1,6 +1,6 @@ import type { LangCode } from '../global/types'; -import { IS_FIREFOX_EXTENSION, LANG_LIST } from '../config'; +import { IS_CAPACITOR, IS_FIREFOX_EXTENSION, LANG_LIST } from '../config'; import { requestForcedReflow, requestMutation } from '../lib/fasterdom/fasterdom'; const SAFE_AREA_INITIALIZATION_DELAY = 1000; @@ -8,6 +8,10 @@ const SAFE_AREA_INITIALIZATION_DELAY = 1000; export function getPlatform() { const { userAgent, platform } = window.navigator; + if (/Android/.test(userAgent)) return 'Android'; + + if (/Linux/.test(platform)) return 'Linux'; + const iosPlatforms = ['iPhone', 'iPad', 'iPod']; if ( iosPlatforms.indexOf(platform) !== -1 @@ -21,10 +25,6 @@ export function getPlatform() { const windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE']; if (windowsPlatforms.indexOf(platform) !== -1) return 'Windows'; - if (/Android/.test(userAgent)) return 'Android'; - - if (/Linux/.test(platform)) return 'Linux'; - return undefined; } @@ -44,13 +44,23 @@ export const IS_IOS = PLATFORM_ENV === 'iOS'; export const IS_ANDROID = PLATFORM_ENV === 'Android'; export const IS_SAFARI = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); export const IS_OPERA = navigator.userAgent.includes(' OPR/'); +export const IS_FIREFOX = navigator.userAgent.includes('Firefox/'); export const IS_TOUCH_ENV = window.matchMedia('(pointer: coarse)').matches; export const IS_CHROME_EXTENSION = Boolean(window.chrome?.system); +export const IS_ELECTRON = Boolean(window.electron); export const DEFAULT_LANG_CODE = 'en'; export const USER_AGENT_LANG_CODE = getBrowserLanguage(); export const DPR = window.devicePixelRatio || 1; - export const IS_LEDGER_SUPPORTED = !(IS_IOS || IS_ANDROID || IS_FIREFOX_EXTENSION); +export const IS_LEDGER_EXTENSION_TAB = global.location.hash.startsWith('#detached'); +// Disable biometric auth on electron for now until this issue is fixed: +// https://github.com/electron/electron/issues/24573 +export const IS_BIOMETRIC_AUTH_SUPPORTED = Boolean( + !IS_CAPACITOR && window.navigator.credentials && (!IS_ELECTRON || IS_MAC_OS), +); +export const IS_DELEGATED_BOTTOM_SHEET = IS_CAPACITOR && global.location.search.startsWith('?bottom-sheet'); +export const CAN_DELEGATE_BOTTOM_SHEET = IS_CAPACITOR && IS_IOS && !IS_DELEGATED_BOTTOM_SHEET; +export const IS_MULTITAB_SUPPORTED = 'BroadcastChannel' in window && !IS_LEDGER_EXTENSION_TAB; export function setScrollbarWidthProperty() { const el = document.createElement('div'); @@ -74,8 +84,14 @@ export function setPageSafeAreaProperty() { // WebKit has issues with this property on page load // https://bugs.webkit.org/show_bug.cgi?id=191872 setTimeout(() => { - const safeAreaBottom = parseInt(getComputedStyle(documentElement).getPropertyValue('--safe-area-bottom-value'), 10); + const safeAreaTop = getSafeAreaTop(); + const safeAreaBottom = getSafeAreaBottom(); + if (!Number.isNaN(safeAreaTop) && safeAreaTop > 0) { + requestMutation(() => { + documentElement.classList.add('with-safe-area-top'); + }); + } if (!Number.isNaN(safeAreaBottom) && safeAreaBottom > 0) { requestMutation(() => { documentElement.classList.add('with-safe-area-bottom'); @@ -84,4 +100,12 @@ export function setPageSafeAreaProperty() { }, SAFE_AREA_INITIALIZATION_DELAY); } +export function getSafeAreaTop() { + return parseInt(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-top-value'), 10); +} + +export function getSafeAreaBottom() { + return parseInt(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-bottom-value'), 10); +} + export const REM = parseInt(getComputedStyle(document.documentElement).fontSize, 10); diff --git a/src/util/windowSize.ts b/src/util/windowSize.ts index a7f8a2f6..89107650 100644 --- a/src/util/windowSize.ts +++ b/src/util/windowSize.ts @@ -1,65 +1,58 @@ +import { IS_CAPACITOR } from '../config'; import { requestMutation } from '../lib/fasterdom/fasterdom'; import { throttle } from './schedulers'; import { IS_ANDROID, IS_IOS } from './windowEnvironment'; -export type IDimensions = { - width: number; - height: number; -}; - const WINDOW_RESIZE_THROTTLE_MS = 250; const WINDOW_ORIENTATION_CHANGE_THROTTLE_MS = IS_IOS ? 350 : 250; const initialHeight = window.innerHeight; -let currentWindowSize = updateSizes(); - -const handleResize = throttle(() => { - currentWindowSize = updateSizes(); -}, WINDOW_RESIZE_THROTTLE_MS, true); -const handleViewportResize = throttle((e: Event) => { - const target = e.target as VisualViewport; - currentWindowSize = { - width: window.innerWidth, - height: target.height, - }; -}, WINDOW_RESIZE_THROTTLE_MS, true); +let currentWindowSize = updateSizes(); -const handleOrientationChange = throttle(() => { +window.addEventListener('orientationchange', throttle(() => { currentWindowSize = updateSizes(); -}, WINDOW_ORIENTATION_CHANGE_THROTTLE_MS, false); +}, WINDOW_ORIENTATION_CHANGE_THROTTLE_MS, false)); -window.addEventListener('orientationchange', handleOrientationChange); if (!IS_IOS) { - window.addEventListener('resize', handleResize); + window.addEventListener('resize', throttle(() => { + currentWindowSize = updateSizes(); + }, WINDOW_RESIZE_THROTTLE_MS, true)); } if ('visualViewport' in window && (IS_IOS || IS_ANDROID)) { - window.visualViewport!.addEventListener('resize', handleViewportResize); + window.visualViewport!.addEventListener('resize', throttle((e: Event) => { + const target = e.target as VisualViewport; + currentWindowSize = { + width: window.innerWidth, + height: target.height, + screenHeight: window.screen.height, + }; + }, WINDOW_RESIZE_THROTTLE_MS, true)); } -export function updateSizes(): IDimensions { - let height: number; - if (IS_IOS) { - height = window.visualViewport!.height + window.visualViewport!.pageTop; - } else { - height = window.innerHeight; - } - - requestMutation(() => { - const vh = height * 0.01; - document.documentElement.style.setProperty('--vh', `${vh}px`); - }); +export function updateSizes() { + patchVh(); return { width: window.innerWidth, height: window.innerHeight, + screenHeight: window.screen.height, }; } -const windowSize = { +export default { get: () => currentWindowSize, getIsKeyboardVisible: () => initialHeight > currentWindowSize.height, }; -export default windowSize; +function patchVh() { + if (!(IS_IOS || IS_ANDROID) || IS_CAPACITOR) return; + + const height = IS_IOS ? window.visualViewport!.height + window.visualViewport!.pageTop : window.innerHeight; + + requestMutation(() => { + const vh = height * 0.01; + document.documentElement.style.setProperty('--vh', `${vh}px`); + }); +} diff --git a/src/util/withCacheAsync.ts b/src/util/withCacheAsync.ts index 36b60a9f..a23bd399 100644 --- a/src/util/withCacheAsync.ts +++ b/src/util/withCacheAsync.ts @@ -1,9 +1,9 @@ const cache = new WeakMap>(); export default function withCacheAsync( - fn: T, canBeCached: (value: ReturnType) => boolean = (value) => !!value, + fn: T, canBeCached: (value: Awaited>) => boolean = (value) => !!value, ) { - return async (...args: Parameters): Promise> => { + return async (...args: Parameters): Promise>> => { let fnCache = cache.get(fn); const cacheKey = buildCacheKey(args); diff --git a/tsconfig.json b/tsconfig.json index d7883d7e..3e98d6be 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,6 @@ "noEmit": true, "jsx": "react" }, - "include": ["src", "tests", "plugins", "webpack.config.ts", "webpack-electron.config.ts"], + "include": ["src", "tests", "plugins", "webpack.config.ts", "webpack-electron.config.ts", "capacitor.config.ts"], "exclude": ["trash"] } diff --git a/webpack-electron.config.ts b/webpack-electron.config.ts index 386a1be1..28520896 100644 --- a/webpack-electron.config.ts +++ b/webpack-electron.config.ts @@ -1,7 +1,16 @@ import path from 'path'; import { EnvironmentPlugin } from 'webpack'; -const { APP_ENV = 'production' } = process.env; +import { PRODUCTION_URL } from './src/config'; + +// GitHub workflow uses an empty string as the default value if it's not in repository variables, so we cannot define a default value here +process.env.BASE_URL = process.env.BASE_URL || PRODUCTION_URL; + +const { + APP_ENV = 'production', + BASE_URL, + IS_PREVIEW, +} = process.env; export default { mode: 'production', @@ -23,7 +32,11 @@ export default { }, plugins: [ - new EnvironmentPlugin({ APP_ENV }), + new EnvironmentPlugin({ + APP_ENV, + BASE_URL, + IS_PREVIEW: false, + }), ], module: { diff --git a/webpack.config.ts b/webpack.config.ts index b35f620b..317438ef 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -19,18 +19,24 @@ import { ProvidePlugin, } from 'webpack'; -const { APP_ENV, HEAD } = process.env; +import { PRODUCTION_URL } from './src/config'; + +dotenv.config(); + +// GitHub workflow uses an empty string as the default value if it's not in repository variables, so we cannot define a default value here +process.env.BASE_URL = process.env.BASE_URL || PRODUCTION_URL; + +const { APP_ENV, BASE_URL, HEAD } = process.env; const IS_EXTENSION = process.env.IS_EXTENSION === '1'; -const IS_ELECTRON = process.env.IS_ELECTRON === '1'; +const IS_ELECTRON_BUILD = process.env.IS_ELECTRON_BUILD === '1'; const IS_FIREFOX_EXTENSION = process.env.IS_FIREFOX_EXTENSION === '1'; const IS_OPERA_EXTENSION = process.env.IS_OPERA_EXTENSION === '1'; const gitRevisionPlugin = new GitRevisionPlugin(); const branch = HEAD || gitRevisionPlugin.branch(); const appRevision = !branch || branch === 'HEAD' ? gitRevisionPlugin.commithash()?.substring(0, 7) : branch; -const STATOSCOPE_REFERENCE_URL = 'https://beta.mytonwallet.app/statoscope-build-statistics.json'; -const canUseStatoscope = !IS_EXTENSION && !IS_ELECTRON; -const canUseBuildReference = canUseStatoscope && APP_ENV === 'staging'; +const canUseStatoscope = !IS_EXTENSION && !IS_ELECTRON_BUILD; +const connectSrcExtraUrl = APP_ENV === 'development' ? process.env.CSP_CONNECT_SRC_EXTRA_URL ?? '' : ''; // The `connect-src` rule contains `https:` due to arbitrary requests are needed for jetton JSON configs. // The `img-src` rule contains `https:` due to arbitrary image URLs being used as jetton logos. @@ -38,7 +44,7 @@ const canUseBuildReference = canUseStatoscope && APP_ENV === 'staging'; const CSP = ` default-src 'none'; manifest-src 'self'; - connect-src 'self' https: http://localhost:3000; + connect-src 'self' https: http://localhost:3000 ${connectSrcExtraUrl}; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' https://fonts.googleapis.com/; img-src 'self' data: https:; @@ -53,8 +59,6 @@ const appVersion = require('./package.json').version; const defaultI18nFilename = path.resolve(__dirname, './src/i18n/en.json'); -dotenv.config(); - export default function createConfig( _: any, { mode = 'production' }: { mode: 'none' | 'development' | 'production' }, @@ -141,7 +145,7 @@ export default function createConfig( modules: { exportLocalsConvention: 'camelCase', auto: true, - localIdentName: mode === 'production' ? '[hash:base64]' : '[name]__[local]', + localIdentName: APP_ENV === 'production' ? '[hash:base64]' : '[name]__[local]', }, }, }, @@ -181,25 +185,6 @@ export default function createConfig( }, plugins: [ - ...(canUseBuildReference ? [{ - apply: (compiler: Compiler) => { - compiler.hooks.compile.tap('Before Compilation', async () => { - try { - const stats = await fetch(STATOSCOPE_REFERENCE_URL).then((res) => res.text()); - // Quick and simple json validator - JSON.parse(stats); - fs.writeFileSync(path.resolve('./public/statoscope-master-reference.json'), stats); - // eslint-disable-next-line no-console - console.info('Reference statoscope stats fetched'); - } catch (err: any) { - fs.writeFileSync(path.resolve('./public/statoscope-master-reference.json'), '{}'); - - // eslint-disable-next-line no-console - console.warn('Failed to fetch reference statoscope stats: ', err.message); - } - }); - }, - }] : []), ...(IS_OPERA_EXTENSION ? [{ apply: (compiler: Compiler) => { compiler.hooks.afterDone.tap('After Compilation', async () => { @@ -241,8 +226,10 @@ export default function createConfig( include: 'allAssets', fileWhitelist: [ /duck_.*?\.png/, // Lottie thumbs - /theme_.*?\.png/, // All theme icons - /settings_.*?\.svg/, // All settings svg icons + /coin_.*?\.png/, // Coin icons + /theme_.*?\.png/, // Theme icons + /chain_.*?\.png/, // Chain icons + /settings_.*?\.svg/, // Settings icons (svg) ], as(entry: string) { if (/\.png$/.test(entry)) return 'image'; @@ -268,14 +255,20 @@ export default function createConfig( TONHTTPAPI_TESTNET_API_KEY: null, TONAPIIO_MAINNET_URL: null, TONAPIIO_TESTNET_URL: null, + TONINDEXER_MAINNET_URL: null, + TONINDEXER_TESTNET_URL: null, BRILLIANT_API_BASE_URL: null, PROXY_HOSTS: null, STAKING_POOLS: null, - IS_ELECTRON: false, + LIQUID_POOL: null, + LIQUID_JETTON: null, + IS_ELECTRON_BUILD: false, ELECTRON_TONHTTPAPI_MAINNET_API_KEY: null, ELECTRON_TONHTTPAPI_TESTNET_API_KEY: null, + BASE_URL, IS_EXTENSION: false, IS_FIREFOX_EXTENSION: false, + IS_CAPACITOR: false, }), /* eslint-enable no-null/no-null */ new DefinePlugin({ @@ -336,9 +329,6 @@ export default function createConfig( normalizeStats: true, open: false, extensions: [new WebpackContextExtension()], // eslint-disable-line @typescript-eslint/no-use-before-define - ...(canUseBuildReference ? { - additionalStats: ['./public/statoscope-master-reference.json'], - } : {}), })] : []), ...(IS_EXTENSION ? [ @@ -351,7 +341,7 @@ export default function createConfig( ], devtool: - IS_EXTENSION ? 'cheap-source-map' : APP_ENV === 'production' && IS_ELECTRON ? undefined : 'source-map', + IS_EXTENSION ? 'cheap-source-map' : APP_ENV === 'production' && IS_ELECTRON_BUILD ? undefined : 'source-map', }; }