From c99794370e722b8e7f38c8ab6e7b6743c3bc6cd6 Mon Sep 17 00:00:00 2001 From: beerosagos Date: Tue, 25 Mar 2025 17:30:45 +0100 Subject: [PATCH 1/2] frontend/formatting: move eslint formatting rules to prettier Eslint formatting rules are deprecated (see https://eslint.org/blog/2023/10/deprecating-formatting-rules/ ). This commit configures the prettier plugin and moves the formatting rules in the new .prettierrc file. --- frontends/web/.eslintrc.json | 31 ++--------- frontends/web/.prettierrc | 9 +++ frontends/web/package-lock.json | 99 +++++++++++++++++++++++++++++++++ frontends/web/package.json | 3 +- 4 files changed, 115 insertions(+), 27 deletions(-) create mode 100644 frontends/web/.prettierrc diff --git a/frontends/web/.eslintrc.json b/frontends/web/.eslintrc.json index 8198ff8c93..f7dcc5b071 100644 --- a/frontends/web/.eslintrc.json +++ b/frontends/web/.eslintrc.json @@ -12,28 +12,12 @@ } }, "rules": { - "brace-style": ["error", "1tbs"], - "comma-spacing": ["error", { "before": false, "after": true }], + "prettier/prettier": "error", "curly": "error", - "indent": ["error", 2, { "SwitchCase": 0 }], "jsx-a11y/anchor-is-valid": 0, - "jsx-a11y/alt-text" : 0, - "jsx-quotes": ["error", "prefer-double"], - "keyword-spacing": "error", + "jsx-a11y/alt-text": 0, "eqeqeq": "error", - "no-multi-spaces": "error", - "no-trailing-spaces": "error", - "object-curly-spacing": ["error", "always"], - "quotes": ["error", "single"], - "semi": "error", - "space-before-blocks": ["error", "always"], - "space-in-parens": ["error", "never"], - "no-extra-semi": "error", - "@typescript-eslint/type-annotation-spacing": "error", - "arrow-spacing": "error", - "space-infix-ops": "error", "react/no-unused-prop-types": "error", - "react/jsx-equals-spacing": ["error", "never"], "react/react-in-jsx-scope": "off", "@typescript-eslint/no-explicit-any": "off", "no-case-declarations": "off", @@ -48,14 +32,9 @@ "react/display-name": "off", "react-hooks/exhaustive-deps": "error", "@typescript-eslint/ban-ts-comment": "off", - "no-async-promise-executor": "off", - "react/jsx-wrap-multilines": ["error", { - "arrow": "parens-new-line", - "assignment": "parens-new-line", - "condition": "parens-new-line", - "logical": "parens-new-line" - }] + "no-async-promise-executor": "off" }, + "plugins": ["prettier"], "overrides": [ { "files": ["**/*.ts?(x)"], @@ -74,7 +53,7 @@ { "files": ["**/*.test.ts?(x)"], "rules": { - "import/first": 0 + "import/first": "off" } } ] diff --git a/frontends/web/.prettierrc b/frontends/web/.prettierrc new file mode 100644 index 0000000000..e7755740dc --- /dev/null +++ b/frontends/web/.prettierrc @@ -0,0 +1,9 @@ +{ + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "jsxSingleQuote": false, + "bracketSpacing": true, + "arrowParens": "always" +} \ No newline at end of file diff --git a/frontends/web/package-lock.json b/frontends/web/package-lock.json index c474f5c426..3f85f55cbc 100644 --- a/frontends/web/package-lock.json +++ b/frontends/web/package-lock.json @@ -33,6 +33,7 @@ "@typescript-eslint/parser": "^7.15.0", "@vitejs/plugin-react": "^4.3.3", "eslint": "^8.57.0", + "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-react": "^7.34.3", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.7", @@ -1599,6 +1600,18 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@pkgr/core": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz", + "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@remix-run/router": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.3.tgz", @@ -4054,6 +4067,36 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", + "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react": { "version": "7.34.3", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz", @@ -4505,6 +4548,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -6637,6 +6686,34 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", @@ -7510,6 +7587,28 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/synckit/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, "node_modules/system-architecture": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz", diff --git a/frontends/web/package.json b/frontends/web/package.json index ce5e27efa2..b5c4eb8ddd 100644 --- a/frontends/web/package.json +++ b/frontends/web/package.json @@ -13,6 +13,7 @@ "@typescript-eslint/parser": "^7.15.0", "@vitejs/plugin-react": "^4.3.3", "eslint": "^8.57.0", + "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-react": "^7.34.3", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.7", @@ -26,9 +27,9 @@ "vitest": "^2.1.4" }, "dependencies": { + "@reown/walletkit": "1.2.0", "@walletconnect/jsonrpc-types": "1.0.4", "@walletconnect/types": "2.17.2", - "@reown/walletkit": "1.2.0", "flag-icons": "7.2.3", "i18next": "23.11.5", "lightweight-charts": "4.1.4", From 5a4822648e44e32ba4c3c694f7ff36e780ff4404 Mon Sep 17 00:00:00 2001 From: beerosagos Date: Tue, 25 Mar 2025 18:16:02 +0100 Subject: [PATCH 2/2] frontend/formatting: format files with prettier `prettier --write "**/*.{ts,tsx,js,jsx}"` launched from frontend/web/src/ Please note that `hooks/api.ts` has been slightly modified, to keep the `react-hooks/exhaustive-deps` lint disable active. --- frontends/web/src/api/account.ts | 485 +++++++---- frontends/web/src/api/accountsync.ts | 21 +- frontends/web/src/api/aopp.ts | 71 +- frontends/web/src/api/backend.ts | 89 +- frontends/web/src/api/backup.ts | 22 +- frontends/web/src/api/banners.ts | 4 +- frontends/web/src/api/bitbox01.ts | 4 +- frontends/web/src/api/bitbox02.ts | 56 +- frontends/web/src/api/bitbox02bootloader.ts | 38 +- frontends/web/src/api/bitsurance.ts | 12 +- frontends/web/src/api/bluetooth.ts | 15 +- frontends/web/src/api/coins.ts | 60 +- frontends/web/src/api/devices.ts | 2 +- frontends/web/src/api/devicessync.ts | 20 +- frontends/web/src/api/exchanges.ts | 89 +- frontends/web/src/api/keystores.ts | 4 +- frontends/web/src/api/mobiledata.ts | 7 +- frontends/web/src/api/node.ts | 34 +- frontends/web/src/api/response.ts | 12 +- frontends/web/src/api/subscribe.ts | 24 +- frontends/web/src/api/transactions.ts | 9 +- frontends/web/src/api/version.ts | 8 +- frontends/web/src/app.tsx | 85 +- .../actionable-item/actionable-item.tsx | 9 +- .../web/src/components/alert/Alert.test.tsx | 37 +- frontends/web/src/components/alert/Alert.tsx | 27 +- .../components/amount/amount-with-unit.tsx | 68 +- .../web/src/components/amount/amount.test.tsx | 511 ++++++----- .../web/src/components/amount/amount.tsx | 81 +- .../components/amount/conversion-amount.tsx | 16 +- .../web/src/components/anchor/anchor.tsx | 13 +- frontends/web/src/components/aopp/aopp.tsx | 339 ++++---- frontends/web/src/components/aopp/vasp.tsx | 22 +- .../web/src/components/aopp/verifyaddress.tsx | 16 +- .../appdownloadlink/appdownloadlink.tsx | 16 +- .../web/src/components/appupgraderequired.tsx | 3 +- .../web/src/components/auth/authrequired.tsx | 76 +- frontends/web/src/components/badge/badge.tsx | 5 +- .../components/balance/balance-skeleton.tsx | 2 +- .../src/components/balance/balance.test.tsx | 28 +- .../web/src/components/balance/balance.tsx | 50 +- .../web/src/components/banners/banner.tsx | 14 +- .../components/banners/mobiledatawarning.tsx | 8 +- .../web/src/components/banners/testing.tsx | 6 +- .../web/src/components/banners/update.tsx | 3 +- .../src/components/bluetooth/bluetooth.tsx | 57 +- .../bottom-navigation/bottom-navigation.tsx | 7 +- .../bottom-navigation/menu-icons.tsx | 56 +- .../web/src/components/confirm/Confirm.tsx | 37 +- .../contentwrapper/contentwrapper.tsx | 10 +- frontends/web/src/components/copy/Copy.tsx | 62 +- .../bitbox02bootloader/bitbox02bootloader.tsx | 59 +- .../toggleshowfirmwarehash.tsx | 12 +- .../src/components/dialog/dialog-legacy.tsx | 94 +- .../web/src/components/dialog/dialog.tsx | 265 +++--- .../web/src/components/dropdown/dropdown.tsx | 33 +- .../web/src/components/forms/button.test.tsx | 25 +- frontends/web/src/components/forms/button.tsx | 47 +- .../src/components/forms/checkbox.test.tsx | 8 +- .../web/src/components/forms/checkbox.tsx | 23 +- frontends/web/src/components/forms/field.tsx | 4 +- .../components/forms/input-number.test.tsx | 72 +- .../web/src/components/forms/input-number.tsx | 118 ++- .../web/src/components/forms/input.test.tsx | 6 +- frontends/web/src/components/forms/input.tsx | 113 +-- .../web/src/components/forms/radio.test.tsx | 4 +- frontends/web/src/components/forms/radio.tsx | 11 +- .../web/src/components/forms/select.test.tsx | 10 +- frontends/web/src/components/forms/select.tsx | 13 +- .../groupedaccountselector.tsx | 97 ++- .../groupedaccountselector/services.ts | 25 +- .../web/src/components/guide/entry.test.tsx | 20 +- frontends/web/src/components/guide/entry.tsx | 36 +- frontends/web/src/components/guide/guide.tsx | 22 +- .../headerssync/headerssync.test.tsx | 10 +- .../components/headerssync/headerssync.tsx | 20 +- .../web/src/components/icon/combined.tsx | 9 +- frontends/web/src/components/icon/icon.tsx | 353 ++++++-- frontends/web/src/components/icon/logo.tsx | 136 ++- .../src/components/infobutton/infobutton.tsx | 2 +- .../src/components/keystoreconnectprompt.tsx | 153 ++-- .../src/components/language/language.test.tsx | 8 +- .../web/src/components/language/language.tsx | 98 ++- .../web/src/components/language/types.ts | 23 +- .../web/src/components/layout/footer.tsx | 9 +- frontends/web/src/components/layout/grid.tsx | 30 +- .../src/components/layout/guide-wrapper.tsx | 14 +- .../web/src/components/layout/header.tsx | 24 +- frontends/web/src/components/layout/main.tsx | 8 +- .../web/src/components/layout/version.tsx | 6 +- .../src/components/message/message.test.tsx | 18 +- .../web/src/components/message/message.tsx | 43 +- frontends/web/src/components/password.tsx | 166 ++-- .../pillbuttongroup/pillbuttongroup.tsx | 27 +- .../components/progressRing/progressRing.tsx | 23 +- .../web/src/components/qrcode/qrcode.tsx | 63 +- .../outlined-settings-button.tsx | 6 +- .../settingsButton/settingsButton.tsx | 50 +- .../settingsButton/settingsItem.tsx | 13 +- .../settingsButton/settingsToggle.tsx | 5 +- .../web/src/components/sidebar/sidebar.tsx | 215 +++-- .../web/src/components/skeleton/skeleton.tsx | 6 +- .../web/src/components/spinner/Spinner.tsx | 13 +- .../web/src/components/status/status.tsx | 9 +- .../src/components/terms/bitsurance-terms.tsx | 25 +- .../components/terms/btcdirect-otc-terms.tsx | 105 ++- .../src/components/terms/btcdirect-terms.tsx | 61 +- .../src/components/terms/moonpay-terms.tsx | 15 +- .../web/src/components/terms/pocket-terms.tsx | 57 +- .../web/src/components/title/subtitle.tsx | 15 +- frontends/web/src/components/toast/toast.tsx | 18 +- .../web/src/components/toggle/toggle.tsx | 13 +- .../components/address-or-txid.tsx | 2 +- .../transactions/components/arrows.tsx | 27 +- .../transactions/components/date.tsx | 9 +- .../transactions/components/detail.tsx | 5 +- .../components/details-dialog.tsx | 152 ++-- .../transactions/components/note.tsx | 34 +- .../transactions/components/status.tsx | 12 +- .../src/components/transactions/details.tsx | 22 +- .../transactions/transaction-skeleton.tsx | 10 +- .../components/transactions/transaction.tsx | 111 ++- frontends/web/src/components/view/view.tsx | 94 +- .../components/wait-dialog/wait-dialog.tsx | 52 +- .../incoming-signing-request-dialog.tsx | 154 ++-- .../incoming-signing-request.tsx | 68 +- frontends/web/src/connected.tsx | 6 +- frontends/web/src/contexts/AppContext.tsx | 44 +- frontends/web/src/contexts/AppProvider.tsx | 23 +- .../web/src/contexts/BackButtonContext.tsx | 16 +- frontends/web/src/contexts/DarkmodeContext.ts | 6 +- .../web/src/contexts/DarkmodeProvider.tsx | 57 +- frontends/web/src/contexts/RatesContext.tsx | 22 +- frontends/web/src/contexts/RatesProvider.tsx | 12 +- .../web/src/contexts/WCWeb3WalletContext.tsx | 12 +- .../web/src/contexts/WCWeb3WalletProvider.tsx | 28 +- .../web/src/contexts/localization-context.ts | 11 +- .../src/contexts/localization-provider.tsx | 6 +- frontends/web/src/contexts/providers.tsx | 4 +- frontends/web/src/decorators/translate.ts | 2 +- frontends/web/src/globals.d.ts | 36 +- frontends/web/src/hooks/api.test.ts | 28 +- frontends/web/src/hooks/api.ts | 20 +- frontends/web/src/hooks/backbutton.ts | 2 +- frontends/web/src/hooks/backend.ts | 2 +- frontends/web/src/hooks/devicename.ts | 48 +- frontends/web/src/hooks/keyboard.test.ts | 2 +- frontends/web/src/hooks/keyboard.ts | 8 +- frontends/web/src/hooks/localized.test.ts | 29 +- frontends/web/src/hooks/localized.ts | 51 +- frontends/web/src/hooks/mount.test.ts | 1 - .../src/hooks/onlyvisitableonmobile.test.ts | 6 +- .../web/src/hooks/onlyvisitableonmobile.ts | 2 +- frontends/web/src/hooks/platform.ts | 17 +- frontends/web/src/hooks/qrcodescanner.ts | 38 +- frontends/web/src/hooks/sdcard.test.ts | 4 - frontends/web/src/hooks/sdcard.ts | 36 +- frontends/web/src/i18n/config.test.tsx | 183 ++-- frontends/web/src/i18n/config.ts | 4 +- frontends/web/src/i18n/i18n.test.tsx | 14 +- frontends/web/src/i18n/i18n.ts | 9 +- frontends/web/src/index.tsx | 2 +- frontends/web/src/routes/account/account.tsx | 295 ++++--- .../web/src/routes/account/actionButtons.tsx | 85 +- .../routes/account/add/add-account-guide.tsx | 71 +- frontends/web/src/routes/account/add/add.tsx | 208 ++--- .../account/add/components/coin-dropdown.tsx | 9 +- .../routes/account/add/components/steps.tsx | 36 +- .../routes/account/components/insuredtag.tsx | 4 +- frontends/web/src/routes/account/guide.tsx | 102 ++- .../routes/account/info/buy-receive-cta.tsx | 48 +- .../web/src/routes/account/info/guide.tsx | 29 +- .../web/src/routes/account/info/info.tsx | 40 +- .../account/info/signingconfiguration.tsx | 85 +- .../account/receive/components/bb01paired.tsx | 8 +- .../account/receive/components/guide.tsx | 40 +- .../receive/components/verifybutton.tsx | 6 +- .../web/src/routes/account/receive/index.tsx | 22 +- .../routes/account/receive/receive-bb01.tsx | 213 +++-- .../src/routes/account/receive/receive.tsx | 214 +++-- .../src/routes/account/send/coin-control.tsx | 53 +- .../send/components/confirm/confirm.tsx | 133 +-- .../confirm/dialogs/confirm-wait-dialog.tsx | 156 ++-- .../account/send/components/confirm/types.ts | 41 +- .../components/dialogs/scan-qr-dialog.tsx | 16 +- .../account/send/components/fiat-value.tsx | 13 +- .../send/components/inputs/coin-input.tsx | 37 +- .../send/components/inputs/fiat-input.tsx | 6 +- .../send/components/inputs/note-input.tsx | 10 +- .../inputs/receiver-address-input.tsx | 50 +- .../send/components/inputs/scan-qr-video.tsx | 10 +- .../routes/account/send/components/result.tsx | 100 ++- .../routes/account/send/feetargets.test.tsx | 75 +- .../src/routes/account/send/feetargets.tsx | 227 ++--- .../src/routes/account/send/send-guide.tsx | 42 +- .../src/routes/account/send/send-wrapper.tsx | 39 +- .../web/src/routes/account/send/send.tsx | 196 +++-- .../src/routes/account/send/services.test.ts | 20 +- .../web/src/routes/account/send/services.ts | 34 +- .../web/src/routes/account/send/utxos.tsx | 71 +- .../account/summary/accountssummary.tsx | 168 ++-- .../src/routes/account/summary/balancerow.tsx | 27 +- .../web/src/routes/account/summary/chart.tsx | 372 ++++---- .../routes/account/summary/coinbalance.tsx | 45 +- .../src/routes/account/summary/filters.tsx | 7 +- .../account/summary/percentage-diff.tsx | 11 +- .../routes/account/summary/subtotalrow.tsx | 38 +- .../routes/account/summary/summarybalance.tsx | 77 +- .../web/src/routes/account/summary/types.ts | 5 +- .../web/src/routes/account/utils.test.ts | 75 +- frontends/web/src/routes/account/utils.ts | 133 +-- .../components/connect-form/connect-form.tsx | 31 +- .../components/header/header.tsx | 8 +- .../incoming-pairing/incoming-pairing.tsx | 86 +- .../components/session-card/session-card.tsx | 52 +- .../success-pairing/success-pairing.tsx | 12 +- .../routes/account/walletconnect/connect.tsx | 25 +- .../account/walletconnect/dashboard.tsx | 84 +- .../routes/account/walletconnect/guide.tsx | 12 +- .../src/routes/account/walletconnect/types.ts | 6 +- .../routes/accounts/all-accounts-guide.tsx | 5 +- .../web/src/routes/accounts/all-accounts.tsx | 50 +- .../src/routes/accounts/select-receive.tsx | 14 +- .../web/src/routes/bitsurance/account.tsx | 27 +- .../web/src/routes/bitsurance/bitsurance.tsx | 73 +- .../web/src/routes/bitsurance/dashboard.tsx | 249 ++++-- frontends/web/src/routes/bitsurance/guide.tsx | 80 +- .../web/src/routes/bitsurance/widget.tsx | 89 +- .../src/routes/device/bitbox01/backups.tsx | 108 ++- .../src/routes/device/bitbox01/bitbox01.jsx | 154 ++-- .../web/src/routes/device/bitbox01/check.jsx | 108 ++- .../bitbox01/components/upgradefirmware.jsx | 157 ++-- .../web/src/routes/device/bitbox01/create.jsx | 83 +- .../src/routes/device/bitbox01/restore.tsx | 142 ++-- .../bitbox01/settings/components/blink.jsx | 4 +- .../settings/components/changepin.jsx | 120 +-- .../settings/components/device-lock.jsx | 56 +- .../settings/components/hiddenwallet.jsx | 124 +-- .../components/legacyhiddenwallet.jsx | 15 +- .../settings/components/mobile-pairing.tsx | 228 ++--- .../settings/components/randomnumber.jsx | 21 +- .../bitbox01/settings/components/reset.jsx | 97 ++- .../device/bitbox01/settings/settings.jsx | 211 +++-- .../src/routes/device/bitbox01/setup/goal.jsx | 17 +- .../device/bitbox01/setup/initialize.tsx | 83 +- .../bitbox01/setup/security-information.tsx | 111 ++- .../device/bitbox01/setup/seed-create-new.jsx | 155 ++-- .../device/bitbox01/setup/seed-restore.jsx | 150 ++-- .../routes/device/bitbox01/setup/success.jsx | 24 +- .../web/src/routes/device/bitbox01/unlock.jsx | 117 +-- .../device/bitbox01/upgrade/bootloader.jsx | 50 +- .../bitbox01/upgrade/require_upgrade.jsx | 23 +- .../src/routes/device/bitbox02/backups.tsx | 136 ++- .../web/src/routes/device/bitbox02/bip85.tsx | 298 +++---- .../src/routes/device/bitbox02/bitbox02.tsx | 17 +- .../routes/device/bitbox02/checkbackup.tsx | 44 +- .../password-entry/password-entry.tsx | 10 +- .../routes/device/bitbox02/createbackup.tsx | 13 +- .../src/routes/device/bitbox02/passphrase.tsx | 209 +++-- .../routes/device/bitbox02/sdcardcheck.tsx | 30 +- .../routes/device/bitbox02/settings-guide.tsx | 55 +- .../device/bitbox02/setup/checklist.tsx | 55 +- .../routes/device/bitbox02/setup/choose.tsx | 128 ++- .../src/routes/device/bitbox02/setup/name.tsx | 57 +- .../routes/device/bitbox02/setup/pairing.tsx | 40 +- .../routes/device/bitbox02/setup/password.tsx | 46 +- .../routes/device/bitbox02/setup/restore.tsx | 13 +- .../routes/device/bitbox02/setup/sdcard.tsx | 9 +- .../routes/device/bitbox02/setup/success.tsx | 38 +- .../src/routes/device/bitbox02/setup/wait.tsx | 8 +- .../device/bitbox02/setup/wallet-create.tsx | 128 +-- .../device/bitbox02/setup/wallet-restore.tsx | 96 +-- .../web/src/routes/device/bitbox02/unlock.tsx | 17 +- .../web/src/routes/device/bitbox02/wizard.tsx | 121 ++- .../src/routes/device/components/backup.tsx | 3 +- .../device/components/skipfortesting.tsx | 16 +- .../web/src/routes/device/deviceswitch.tsx | 32 +- .../device/manage-backups/manage-backups.tsx | 155 ++-- .../src/routes/device/no-device-connected.tsx | 3 +- .../device/upgrade-firmware-required.tsx | 13 +- frontends/web/src/routes/device/waiting.tsx | 91 +- .../web/src/routes/exchange/btcdirect-otc.tsx | 14 +- .../web/src/routes/exchange/btcdirect.tsx | 111 ++- .../routes/exchange/components/buysell.tsx | 96 ++- .../exchange/components/countryselect.tsx | 37 +- .../components/exchange-providers.tsx | 52 +- .../exchange/components/exchangetab.tsx | 14 +- .../exchange/components/infocontent.tsx | 280 ++++-- .../exchange/components/providerselection.tsx | 5 +- .../web/src/routes/exchange/exchange.tsx | 59 +- frontends/web/src/routes/exchange/guide.tsx | 54 +- frontends/web/src/routes/exchange/info.tsx | 37 +- frontends/web/src/routes/exchange/moonpay.tsx | 33 +- frontends/web/src/routes/exchange/pocket.tsx | 158 ++-- frontends/web/src/routes/exchange/utils.ts | 28 +- frontends/web/src/routes/router.tsx | 379 +++++---- frontends/web/src/routes/settings/about.tsx | 15 +- .../src/routes/settings/advanced-settings.tsx | 64 +- .../web/src/routes/settings/bb02-settings.tsx | 103 ++- .../components/about/app-version-setting.tsx | 6 +- .../connect-full-node-setting.tsx | 9 +- .../advanced-settings/enable-auth-setting.tsx | 22 +- .../enable-coin-control-setting.tsx | 15 +- .../enable-custom-fees-toggle-setting.tsx | 15 +- .../enable-tor-proxy-setting.tsx | 21 +- .../advanced-settings/export-log-setting.tsx | 7 +- .../restart-in-testnet-setting.tsx | 31 +- .../advanced-settings/tor-proxy-dialog.tsx | 35 +- .../activeCurrenciesDropdownSetting.tsx | 4 +- .../appearance/activecurrenciesdropdown.tsx | 49 +- .../appearance/darkmodeToggleSetting.tsx | 9 +- .../defaultCurrencyDropdownSetting.tsx | 28 +- .../appearance/languageDropdownSetting.tsx | 26 +- .../components/appearance/notesExport.tsx | 7 +- .../components/appearance/notesImport.tsx | 15 +- .../attestation-check-setting.tsx | 18 +- .../device-settings/bip85-setting.tsx | 2 +- .../device-settings/device-name-setting.tsx | 49 +- .../device-settings/factory-reset-setting.tsx | 54 +- .../device-settings/firmware-setting.tsx | 71 +- .../go-to-startup-settings.tsx | 21 +- .../device-settings/manage-backup-setting.tsx | 5 +- .../device-settings/passphrase-setting.tsx | 20 +- .../root-fingerprint-setting.tsx | 17 +- .../device-settings/secure-chip-setting.tsx | 10 +- .../show-recovery-words-setting.tsx | 43 +- .../dialogs/disableRememberWalletDialog.tsx | 23 +- .../dialogs/enableRememberWalletDialog.tsx | 27 +- .../manage-accounts/watchonlySetting.tsx | 13 +- .../settings/components/mobile-header.tsx | 16 +- .../components/settingsItem/settingsItem.tsx | 44 +- .../src/routes/settings/components/tabs.tsx | 59 +- .../routes/settings/electrum-add-server.tsx | 70 +- .../src/routes/settings/electrum-server.tsx | 44 +- .../src/routes/settings/electrum-servers.tsx | 49 +- .../web/src/routes/settings/electrum.tsx | 97 ++- frontends/web/src/routes/settings/general.tsx | 48 +- .../routes/settings/manage-account-guide.tsx | 78 +- .../src/routes/settings/manage-accounts.tsx | 288 ++++--- .../src/routes/settings/mobile-settings.tsx | 13 +- frontends/web/src/routes/settings/more.tsx | 20 +- frontends/web/src/utils/config.ts | 32 +- frontends/web/src/utils/custom.d.ts | 15 +- frontends/web/src/utils/date.ts | 60 +- frontends/web/src/utils/equal.js | 6 +- frontends/web/src/utils/event-legacy.ts | 17 +- frontends/web/src/utils/event.ts | 9 +- frontends/web/src/utils/language.ts | 2 +- frontends/web/src/utils/localize.test.ts | 3 +- frontends/web/src/utils/localize.ts | 16 +- frontends/web/src/utils/markup.test.tsx | 41 +- frontends/web/src/utils/markup.tsx | 27 +- frontends/web/src/utils/qwebchannel.js | 802 +++++++++--------- frontends/web/src/utils/request-addess.ts | 5 +- frontends/web/src/utils/request.ts | 64 +- frontends/web/src/utils/subscriptions.ts | 2 +- frontends/web/src/utils/transaction.ts | 14 +- frontends/web/src/utils/transport-common.ts | 6 +- frontends/web/src/utils/transport-mobile.ts | 7 +- frontends/web/src/utils/transport-qt.ts | 19 +- .../web/src/utils/transport-websocket.ts | 18 +- frontends/web/src/utils/types.ts | 10 +- frontends/web/src/utils/url_constants.ts | 2 +- .../utils/walletconnect-eth-sign-handlers.ts | 121 +-- frontends/web/src/utils/walletconnect.ts | 53 +- frontends/web/src/vite-env.d.ts | 2 +- 366 files changed, 11830 insertions(+), 8783 deletions(-) diff --git a/frontends/web/src/api/account.ts b/frontends/web/src/api/account.ts index 450aab7000..a02bcc1d15 100644 --- a/frontends/web/src/api/account.ts +++ b/frontends/web/src/api/account.ts @@ -20,26 +20,80 @@ import type { TDetailStatus } from './bitsurance'; import type { SuccessResponse } from './response'; import { Slip24 } from 'request-address'; -export type NativeCoinCode = 'btc' | 'tbtc' | 'rbtc' | 'ltc' | 'tltc' | 'eth' | 'sepeth'; +export type NativeCoinCode = + | 'btc' + | 'tbtc' + | 'rbtc' + | 'ltc' + | 'tltc' + | 'eth' + | 'sepeth'; export type AccountCode = string; -export type Fiat = 'AUD' | 'BRL' | 'BTC' | 'CAD' | 'CHF' | 'CNY' | 'CZK' | 'EUR' | 'GBP' | 'HKD' | 'ILS' | 'JPY' | 'KRW' | 'NOK' | 'PLN' | 'RUB' | 'sat' | 'SEK' | 'SGD' | 'USD'; - -export type ConversionUnit = Fiat | 'sat' - -export type CoinUnit = 'BTC' | 'sat' | 'LTC' | 'ETH' | 'TBTC' | 'tsat' | 'TLTC' | 'SEPETH'; - -export type ERC20TokenUnit = 'USDT' | 'USDC' | 'LINK' | 'BAT' | 'MKR' | 'ZRX' | 'WBTC' | 'PAXG' | 'DAI'; - -export type ERC20CoinCode = 'erc20Test' | 'eth-erc20-usdt' | 'eth-erc20-usdc' | 'eth-erc20-link' | 'eth-erc20-bat' | 'eth-erc20-mkr' | 'eth-erc20-zrx' | 'eth-erc20-wbtc' | 'eth-erc20-paxg' | 'eth-erc20-dai0x6b17'; +export type Fiat = + | 'AUD' + | 'BRL' + | 'BTC' + | 'CAD' + | 'CHF' + | 'CNY' + | 'CZK' + | 'EUR' + | 'GBP' + | 'HKD' + | 'ILS' + | 'JPY' + | 'KRW' + | 'NOK' + | 'PLN' + | 'RUB' + | 'sat' + | 'SEK' + | 'SGD' + | 'USD'; + +export type ConversionUnit = Fiat | 'sat'; + +export type CoinUnit = + | 'BTC' + | 'sat' + | 'LTC' + | 'ETH' + | 'TBTC' + | 'tsat' + | 'TLTC' + | 'SEPETH'; + +export type ERC20TokenUnit = + | 'USDT' + | 'USDC' + | 'LINK' + | 'BAT' + | 'MKR' + | 'ZRX' + | 'WBTC' + | 'PAXG' + | 'DAI'; + +export type ERC20CoinCode = + | 'erc20Test' + | 'eth-erc20-usdt' + | 'eth-erc20-usdc' + | 'eth-erc20-link' + | 'eth-erc20-bat' + | 'eth-erc20-mkr' + | 'eth-erc20-zrx' + | 'eth-erc20-wbtc' + | 'eth-erc20-paxg' + | 'eth-erc20-dai0x6b17'; export type CoinCode = NativeCoinCode | ERC20CoinCode; export type FiatWithDisplayName = { - currency: Fiat, - displayName: string -} + currency: Fiat; + displayName: string; +}; export type Terc20Token = { code: ERC20CoinCode; @@ -92,26 +146,29 @@ export const getAccountsBalance = (): Promise => { }; export type TAccountTotalBalance = { - fiatUnit: ConversionUnit; - total: string; + fiatUnit: ConversionUnit; + total: string; }; export type TAccountsTotalBalance = { [rootFingerprint in TKeystore['rootFingerprint']]: TAccountTotalBalance; }; -export type TAccountsTotalBalanceResponse = { - success: true; - totalBalance: TAccountsTotalBalance; -} | { - success: false; - errorCode?: 'ratesNotAvailable'; - errorMessage?: string; -} - -export const getAccountsTotalBalance = (): Promise => { - return apiGet('accounts/total-balance'); -}; +export type TAccountsTotalBalanceResponse = + | { + success: true; + totalBalance: TAccountsTotalBalance; + } + | { + success: false; + errorCode?: 'ratesNotAvailable'; + errorMessage?: string; + }; + +export const getAccountsTotalBalance = + (): Promise => { + return apiGet('accounts/total-balance'); + }; type CoinFormattedAmount = { coinCode: CoinCode; @@ -128,17 +185,19 @@ export const getCoinsTotalBalance = (): Promise => { type TEthAccountCodeAndNameByAddress = SuccessResponse & { code: AccountCode; name: string; -} +}; -export const getEthAccountCodeAndNameByAddress = (address: string): Promise => { +export const getEthAccountCodeAndNameByAddress = ( + address: string, +): Promise => { return apiPost('accounts/eth-account-code', { address }); }; export interface IStatus { - disabled: boolean; - synced: boolean; - fatalError: boolean; - offlineError: string | null; + disabled: boolean; + synced: boolean; + fatalError: boolean; + offlineError: string | null; } export const getStatus = (code: AccountCode): Promise => { @@ -147,34 +206,41 @@ export const getStatus = (code: AccountCode): Promise => { export type ScriptType = 'p2pkh' | 'p2wpkh-p2sh' | 'p2wpkh' | 'p2tr'; -export const allScriptTypes: ScriptType[] = ['p2pkh', 'p2wpkh-p2sh', 'p2wpkh', 'p2tr']; +export const allScriptTypes: ScriptType[] = [ + 'p2pkh', + 'p2wpkh-p2sh', + 'p2wpkh', + 'p2tr', +]; export interface IKeyInfo { - keypath: string; - rootFingerprint: string; - xpub: string; + keypath: string; + rootFingerprint: string; + xpub: string; } export type TBitcoinSimple = { - keyInfo: IKeyInfo; - scriptType: ScriptType; -} + keyInfo: IKeyInfo; + scriptType: ScriptType; +}; export type TEthereumSimple = { - keyInfo: IKeyInfo; -} + keyInfo: IKeyInfo; +}; -export type TSigningConfiguration = { - bitcoinSimple: TBitcoinSimple; - ethereumSimple?: never; -} | { - bitcoinSimple?: never; - ethereumSimple: TEthereumSimple; -} +export type TSigningConfiguration = + | { + bitcoinSimple: TBitcoinSimple; + ethereumSimple?: never; + } + | { + bitcoinSimple?: never; + ethereumSimple: TEthereumSimple; + }; export type TSigningConfigurationList = null | { - signingConfigurations: TSigningConfiguration[]; -} + signingConfigurations: TSigningConfiguration[]; +}; export const getInfo = (code: AccountCode) => { return (): Promise => { @@ -192,45 +258,47 @@ export type FormattedLineData = LineData & { export type ChartData = FormattedLineData[]; -export type TSummaryResponse = { - success: true; - data: TSummary; -} | { - success: false; - error: string; -} +export type TSummaryResponse = + | { + success: true; + data: TSummary; + } + | { + success: false; + error: string; + }; export type TSummary = { - chartDataMissing: boolean; - chartDataDaily: ChartData; - chartDataHourly: ChartData; - chartFiat: ConversionUnit; - chartTotal: number | null; - formattedChartTotal: string | null; - chartIsUpToDate: boolean; // only valid if chartDataMissing is false - lastTimestamp: number; -} + chartDataMissing: boolean; + chartDataDaily: ChartData; + chartDataHourly: ChartData; + chartFiat: ConversionUnit; + chartTotal: number | null; + formattedChartTotal: string | null; + chartIsUpToDate: boolean; // only valid if chartDataMissing is false + lastTimestamp: number; +}; export const getSummary = (): Promise => { return apiGet('account-summary'); }; export type Conversions = { - [key in Fiat]?: string; + [key in Fiat]?: string; }; export interface IAmount { - amount: string; - conversions?: Conversions; - unit: CoinUnit; - estimated: boolean; + amount: string; + conversions?: Conversions; + unit: CoinUnit; + estimated: boolean; } export interface IBalance { - hasAvailable: boolean; - available: IAmount; - hasIncoming: boolean; - incoming: IAmount; + hasAvailable: boolean; + available: IAmount; + hasIncoming: boolean; + incoming: IAmount; } export const getBalance = (code: AccountCode): Promise => { @@ -241,53 +309,60 @@ export type TTransactionStatus = 'complete' | 'pending' | 'failed'; export type TTransactionType = 'send' | 'receive' | 'send_to_self'; export interface ITransaction { - addresses: string[]; - amount: IAmount; - amountAtTime: IAmount; - fee: IAmount; - feeRatePerKb: IAmount; - deductedAmountAtTime: IAmount; - gas: number; - nonce: number | null; - internalID: string; - note: string; - numConfirmations: number; - numConfirmationsComplete: number; - size: number; - status: TTransactionStatus; - time: string | null; - type: TTransactionType; - txID: string; - vsize: number; - weight: number; + addresses: string[]; + amount: IAmount; + amountAtTime: IAmount; + fee: IAmount; + feeRatePerKb: IAmount; + deductedAmountAtTime: IAmount; + gas: number; + nonce: number | null; + internalID: string; + note: string; + numConfirmations: number; + numConfirmationsComplete: number; + size: number; + status: TTransactionStatus; + time: string | null; + type: TTransactionType; + txID: string; + vsize: number; + weight: number; } -export type TTransactions = { success: false } | { success: true; list: ITransaction[]; }; +export type TTransactions = + | { success: false } + | { success: true; list: ITransaction[] }; export interface INoteTx { - internalTxID: string; - note: string; + internalTxID: string; + note: string; } -export const postNotesTx = (code: AccountCode, { - internalTxID, - note, -}: INoteTx): Promise => { +export const postNotesTx = ( + code: AccountCode, + { internalTxID, note }: INoteTx, +): Promise => { return apiPost(`account/${code}/notes/tx`, { internalTxID, note }); }; -export const getTransactionList = (code: AccountCode): Promise => { +export const getTransactionList = ( + code: AccountCode, +): Promise => { return apiGet(`account/${code}/transactions`); }; -export const getTransaction = (code: AccountCode, id: ITransaction['internalID']): Promise => { +export const getTransaction = ( + code: AccountCode, + id: ITransaction['internalID'], +): Promise => { return apiGet(`account/${code}/transaction?id=${id}`); }; export interface IExport { - success: boolean; - path: string; - errorMessage: string; + success: boolean; + path: string; + errorMessage: string; } export const exportAccount = (code: AccountCode): Promise => { @@ -297,18 +372,20 @@ export const exportAccount = (code: AccountCode): Promise => { export const verifyXPub = ( code: AccountCode, signingConfigIndex: number, -): Promise<{ success: true; } | { success: false; errorMessage: string; }> => { - return apiPost(`account/${code}/verify-extended-public-key`, { signingConfigIndex }); +): Promise<{ success: true } | { success: false; errorMessage: string }> => { + return apiPost(`account/${code}/verify-extended-public-key`, { + signingConfigIndex, + }); }; export interface IReceiveAddress { - addressID: string; - address: string; + addressID: string; + address: string; } export interface ReceiveAddressList { - scriptType: ScriptType | null; - addresses: IReceiveAddress[]; + scriptType: ScriptType | null; + addresses: IReceiveAddress[]; } export const getReceiveAddressList = (code: AccountCode) => { @@ -327,15 +404,17 @@ export type TTxInput = { paymentRequest: Slip24 | null; }; -export type TTxProposalResult = { - amount: IAmount; - fee: IAmount; - success: true; - total: IAmount; -} | { - errorCode: string; - success: false; -}; +export type TTxProposalResult = + | { + amount: IAmount; + fee: IAmount; + success: true; + total: IAmount; + } + | { + errorCode: string; + success: false; + }; export const proposeTx = ( accountCode: AccountCode, @@ -344,57 +423,62 @@ export const proposeTx = ( return apiPost(`account/${accountCode}/tx-proposal`, txInput); }; -export type TSendTx = { - success: true; -} | { - success: false; - aborted: true; -} | { - success: false; - errorMessage: string; - errorCode?: string; -}; - -export const sendTx = ( - code: AccountCode, - txNote: string, -): Promise => { +export type TSendTx = + | { + success: true; + } + | { + success: false; + aborted: true; + } + | { + success: false; + errorMessage: string; + errorCode?: string; + }; + +export const sendTx = (code: AccountCode, txNote: string): Promise => { return apiPost(`account/${code}/sendtx`, txNote); }; export type FeeTargetCode = 'custom' | 'low' | 'economy' | 'normal' | 'high'; export interface IProposeTxData { - address?: string; - amount?: number; - // data?: string; - feePerByte: string; - feeTarget: FeeTargetCode; - selectedUTXOs: string[]; - sendAll: 'yes' | 'no'; + address?: string; + amount?: number; + // data?: string; + feePerByte: string; + feeTarget: FeeTargetCode; + selectedUTXOs: string[]; + sendAll: 'yes' | 'no'; } export interface IProposeTx { - aborted?: boolean; - success?: boolean; - errorMessage?: string; + aborted?: boolean; + success?: boolean; + errorMessage?: string; } export interface IFeeTarget { - code: FeeTargetCode; - feeRateInfo: string; + code: FeeTargetCode; + feeRateInfo: string; } export interface IFeeTargetList { - feeTargets: IFeeTarget[]; - defaultFeeTarget: FeeTargetCode; + feeTargets: IFeeTarget[]; + defaultFeeTarget: FeeTargetCode; } -export const getFeeTargetList = (code: AccountCode): Promise => { +export const getFeeTargetList = ( + code: AccountCode, +): Promise => { return apiGet(`account/${code}/fee-targets`); }; -export const verifyAddress = (code: AccountCode, addressID: string): Promise => { +export const verifyAddress = ( + code: AccountCode, + addressID: string, +): Promise => { return apiPost(`account/${code}/verify-address`, addressID); }; @@ -415,8 +499,8 @@ export const getUTXOs = (code: AccountCode): Promise => { }; type TSecureOutput = { - hasSecureOutput: boolean; - optional: boolean; + hasSecureOutput: boolean; + optional: boolean; }; export const hasSecureOutput = (code: AccountCode) => { @@ -431,7 +515,9 @@ type THasPaymentRequest = { errorCode?: 'firmwareUpgradeRequired' | 'unsupportedFeature'; }; -export const hasPaymentRequest = (code: AccountCode): Promise => { +export const hasPaymentRequest = ( + code: AccountCode, +): Promise => { return apiGet(`account/${code}/has-payment-request`); }; @@ -440,53 +526,84 @@ export type TAddAccount = { accountCode?: string; errorCode?: 'accountAlreadyExists' | 'accountLimitReached'; errorMessage?: string; -} +}; -export const addAccount = (coinCode: string, name: string): Promise => { +export const addAccount = ( + coinCode: string, + name: string, +): Promise => { return apiPost('account-add', { coinCode, name, }); }; -export const connectKeystore = (code: AccountCode): Promise<{ success: boolean; }> => { +export const connectKeystore = ( + code: AccountCode, +): Promise<{ success: boolean }> => { return apiPost(`account/${code}/connect-keystore`); }; -export type TSignMessage = { success: false, aborted?: boolean; errorMessage?: string; } | { success: true; signature: string; } +export type TSignMessage = + | { success: false; aborted?: boolean; errorMessage?: string } + | { success: true; signature: string }; -export type TSignWalletConnectTx = { - success: false; - aborted?: boolean; - errorMessage?: string; -} | { - success: true; - txHash: string; - rawTx: string; -} +export type TSignWalletConnectTx = + | { + success: false; + aborted?: boolean; + errorMessage?: string; + } + | { + success: true; + txHash: string; + rawTx: string; + }; -export const ethSignMessage = (code: AccountCode, message: string): Promise => { +export const ethSignMessage = ( + code: AccountCode, + message: string, +): Promise => { return apiPost(`account/${code}/eth-sign-msg`, message); }; -export const ethSignTypedMessage = (code: AccountCode, chainId: number, data: any): Promise => { +export const ethSignTypedMessage = ( + code: AccountCode, + chainId: number, + data: any, +): Promise => { return apiPost(`account/${code}/eth-sign-typed-msg`, { chainId, data }); }; -export const ethSignWalletConnectTx = (code: AccountCode, send: boolean, chainId: number, tx: any): Promise => { - return apiPost(`account/${code}/eth-sign-wallet-connect-tx`, { send, chainId, tx }); +export const ethSignWalletConnectTx = ( + code: AccountCode, + send: boolean, + chainId: number, + tx: any, +): Promise => { + return apiPost(`account/${code}/eth-sign-wallet-connect-tx`, { + send, + chainId, + tx, + }); }; -export type AddressSignResponse = { - success: true; - signature: string; - address: string; -} | { - success: false; - errorMessage?: string; - errorCode?: 'userAbort' | 'wrongKeystore'; -} - -export const signAddress = (format: ScriptType | '', msg: string, code: AccountCode): Promise => { +export type AddressSignResponse = + | { + success: true; + signature: string; + address: string; + } + | { + success: false; + errorMessage?: string; + errorCode?: 'userAbort' | 'wrongKeystore'; + }; + +export const signAddress = ( + format: ScriptType | '', + msg: string, + code: AccountCode, +): Promise => { return apiPost(`account/${code}/sign-address`, { format, msg, code }); }; diff --git a/frontends/web/src/api/accountsync.ts b/frontends/web/src/api/accountsync.ts index 1c50035b58..22df67d096 100644 --- a/frontends/web/src/api/accountsync.ts +++ b/frontends/web/src/api/accountsync.ts @@ -25,7 +25,7 @@ import { subscribe as subscribeLegacy } from '@/utils/event-legacy'; * Returns a method to unsubscribe. */ export const syncAccountsList = ( - cb: (accounts: accountAPI.IAccount[],) => void + cb: (accounts: accountAPI.IAccount[]) => void, ): TUnsubscribe => { return subscribeEndpoint('accounts', cb); }; @@ -36,14 +36,13 @@ export const syncAccountsList = ( * Meant to be used with `useSubscribe`. */ export const syncAddressesCount = (code: accountAPI.AccountCode) => { - return ( - cb: TSubscriptionCallback - ) => { - return subscribeEndpoint(`account/${code}/synced-addresses-count`, ( - count: number, - ) => { - cb(count); - }); + return (cb: TSubscriptionCallback) => { + return subscribeEndpoint( + `account/${code}/synced-addresses-count`, + (count: number) => { + cb(count); + }, + ); }; }; @@ -55,7 +54,7 @@ export const syncAddressesCount = (code: accountAPI.AccountCode) => { export const statusChanged = ( cb: (code: accountAPI.AccountCode) => void, ): TUnsubscribe => { - const unsubscribe = subscribeLegacy('statusChanged', event => { + const unsubscribe = subscribeLegacy('statusChanged', (event) => { if (event.type === 'account' && event.code) { cb(event.code); } @@ -70,7 +69,7 @@ export const statusChanged = ( export const syncdone = ( cb: (code: accountAPI.AccountCode) => void, ): TUnsubscribe => { - return subscribeLegacy('syncdone', event => { + return subscribeLegacy('syncdone', (event) => { if (event.type === 'account' && event.code) { cb(event.code); } diff --git a/frontends/web/src/api/aopp.ts b/frontends/web/src/api/aopp.ts index f0ca3f794f..889846db9d 100644 --- a/frontends/web/src/api/aopp.ts +++ b/frontends/web/src/api/aopp.ts @@ -20,38 +20,51 @@ import type { TUnsubscribe } from '@/utils/transport-common'; import { subscribeEndpoint } from './subscribe'; export interface Account { - name: string; - code: AccountCode; + name: string; + code: AccountCode; } interface Accounts extends Array { - 0: Account, + 0: Account; } -export type Aopp = { - state: 'error'; - errorCode: 'aoppUnsupportedAsset' | 'aoppVersion' | 'aoppInvalidRequest' | 'aoppNoAccounts' | 'aoppUnsupportedKeystore' | 'aoppUnknown' | 'aoppSigningAborted' | 'aoppCallback'; - callback: string; -} | { - state: 'inactive'; -} | { - state: 'user-approval' | 'awaiting-keystore' | 'syncing'; - message: string; - callback: string; - xpubRequired: boolean; -} | { - state: 'choosing-account'; - accounts: Accounts; - message: string; - callback: string; -} | { - state: 'signing' | 'success'; - address: string; - addressID: string; - message: string; - callback: string; - accountCode: AccountCode; -}; +export type Aopp = + | { + state: 'error'; + errorCode: + | 'aoppUnsupportedAsset' + | 'aoppVersion' + | 'aoppInvalidRequest' + | 'aoppNoAccounts' + | 'aoppUnsupportedKeystore' + | 'aoppUnknown' + | 'aoppSigningAborted' + | 'aoppCallback'; + callback: string; + } + | { + state: 'inactive'; + } + | { + state: 'user-approval' | 'awaiting-keystore' | 'syncing'; + message: string; + callback: string; + xpubRequired: boolean; + } + | { + state: 'choosing-account'; + accounts: Accounts; + message: string; + callback: string; + } + | { + state: 'signing' | 'success'; + address: string; + addressID: string; + message: string; + callback: string; + accountCode: AccountCode; + }; export const cancel = (): Promise => { return apiPost('aopp/cancel'); @@ -69,8 +82,6 @@ export const getAOPP = (): Promise => { return apiGet('aopp'); }; -export const subscribeAOPP = ( - cb: (aopp: Aopp) => void -): TUnsubscribe => { +export const subscribeAOPP = (cb: (aopp: Aopp) => void): TUnsubscribe => { return subscribeEndpoint('aopp', cb); }; diff --git a/frontends/web/src/api/backend.ts b/frontends/web/src/api/backend.ts index 4796aeee26..381811a0b5 100644 --- a/frontends/web/src/api/backend.ts +++ b/frontends/web/src/api/backend.ts @@ -20,23 +20,26 @@ import { apiGet, apiPost } from '@/utils/request'; import { TSubscriptionCallback, subscribeEndpoint } from './subscribe'; export interface ICoin { - coinCode: CoinCode; - name: string; - canAddAccount: boolean; - suggestedAccountName: string; + coinCode: CoinCode; + name: string; + canAddAccount: boolean; + suggestedAccountName: string; } export interface ISuccess { - success: boolean; - errorMessage?: string; - errorCode?: string; + success: boolean; + errorMessage?: string; + errorCode?: string; } export const getSupportedCoins = (): Promise => { return apiGet('supported-coins'); }; -export const setAccountActive = (accountCode: AccountCode, active: boolean): Promise => { +export const setAccountActive = ( + accountCode: AccountCode, + active: boolean, +): Promise => { return apiPost('set-account-active', { accountCode, active }); }; @@ -48,7 +51,10 @@ export const setTokenActive = ( return apiPost('set-token-active', { accountCode, tokenCode, active }); }; -export const renameAccount = (accountCode: AccountCode, name: string): Promise => { +export const renameAccount = ( + accountCode: AccountCode, + name: string, +): Promise => { return apiPost('rename-account', { accountCode, name }); }; @@ -64,7 +70,7 @@ export const getDevServers = (): Promise => { return apiGet('dev-servers'); }; -export type TQRCode = FailResponse | (SuccessResponse & { data: string; }); +export type TQRCode = FailResponse | (SuccessResponse & { data: string }); export const getQRCode = (data: string) => { return (): Promise => { @@ -82,28 +88,30 @@ export const socksProxyCheck = (proxyAddress: string): Promise => { export type TConnectKeystoreErrorCode = 'wrongKeystore' | 'timeout'; -export type TSyncConnectKeystore = null | { - typ: 'connect'; - keystoreName: string; -} | { - typ: 'error'; - errorCode?: TConnectKeystoreErrorCode; - errorMessage: string; -}; +export type TSyncConnectKeystore = + | null + | { + typ: 'connect'; + keystoreName: string; + } + | { + typ: 'error'; + errorCode?: TConnectKeystoreErrorCode; + errorMessage: string; + }; /** * Returns a function that subscribes a callback on a "connect-keystore". * Meant to be used with `useSubscribe`. */ export const syncConnectKeystore = () => { - return ( - cb: TSubscriptionCallback - ) => { - return subscribeEndpoint('connect-keystore', ( - obj: TSyncConnectKeystore, - ) => { - cb(obj); - }); + return (cb: TSubscriptionCallback) => { + return subscribeEndpoint( + 'connect-keystore', + (obj: TSyncConnectKeystore) => { + cb(obj); + }, + ); }; }; @@ -111,7 +119,10 @@ export const cancelConnectKeystore = (): Promise => { return apiPost('cancel-connect-keystore'); }; -export const setWatchonly = (rootFingerprint: string, watchonly: boolean): Promise => { +export const setWatchonly = ( + rootFingerprint: string, + watchonly: boolean, +): Promise => { return apiPost('set-watchonly', { rootFingerprint, watchonly }); }; @@ -124,14 +135,16 @@ export const forceAuth = (): Promise => { }; export type TAuthEventObject = { - typ: 'auth-required' | 'auth-forced' | 'auth-canceled' | 'auth-ok' | 'auth-err' ; + typ: + | 'auth-required' + | 'auth-forced' + | 'auth-canceled' + | 'auth-ok' + | 'auth-err'; }; -export const subscribeAuth = ( - cb: TSubscriptionCallback -) => ( - subscribeEndpoint('auth', cb) -); +export const subscribeAuth = (cb: TSubscriptionCallback) => + subscribeEndpoint('auth', cb); export const onAuthSettingChanged = (): Promise => { return apiPost('on-auth-setting-changed'); @@ -141,7 +154,9 @@ export const exportLogs = (): Promise => { return apiPost('export-log'); }; -export const exportNotes = (): Promise<(FailResponse & { aborted: boolean; }) | SuccessResponse> => { +export const exportNotes = (): Promise< + (FailResponse & { aborted: boolean }) | SuccessResponse +> => { return apiPost('notes/export'); }; @@ -150,9 +165,11 @@ export type TImportNotes = { transactionCount: number; }; -export const importNotes = (fileContents: ArrayBuffer): Promise => { +export const importNotes = ( + fileContents: ArrayBuffer, +): Promise => { const hexString = Array.from(new Uint8Array(fileContents)) - .map(byte => byte.toString(16).padStart(2, '0')) + .map((byte) => byte.toString(16).padStart(2, '0')) .join(''); return apiPost('notes/import', hexString); }; diff --git a/frontends/web/src/api/backup.ts b/frontends/web/src/api/backup.ts index 5a4bd6b20a..083784f559 100644 --- a/frontends/web/src/api/backup.ts +++ b/frontends/web/src/api/backup.ts @@ -3,24 +3,22 @@ import { FailResponse } from './response'; import { TSubscriptionCallback, subscribeEndpoint } from './subscribe'; export type Backup = { - id: string; - date: string; - name: string; + id: string; + date: string; + name: string; }; type BackupResponse = { - success: true; - backups: Backup[]; -} + success: true; + backups: Backup[]; +}; export const getBackupList = ( - deviceID: string + deviceID: string, ): Promise => { return apiGet(`devices/bitbox02/${deviceID}/backups/list`); }; -export const subscribeBackupList = (deviceID: string) => ( - (cb: TSubscriptionCallback) => ( - subscribeEndpoint(`devices/bitbox02/${deviceID}/backups/list`, cb) - ) -); \ No newline at end of file +export const subscribeBackupList = + (deviceID: string) => (cb: TSubscriptionCallback) => + subscribeEndpoint(`devices/bitbox02/${deviceID}/backups/list`, cb); diff --git a/frontends/web/src/api/banners.ts b/frontends/web/src/api/banners.ts index aae838b2ac..afe51ac2e8 100644 --- a/frontends/web/src/api/banners.ts +++ b/frontends/web/src/api/banners.ts @@ -20,14 +20,14 @@ import type { TMessageTypes } from '@/utils/types'; export type TBannerInfo = { id: string; - message: { [key: string]: string; }; + message: { [key: string]: string }; link?: { href: string; text?: string; }; dismissible?: boolean; type?: TMessageTypes; -} +}; export const getBanner = (msgKey: string): Promise => { return apiGet(`banners/${msgKey}`); diff --git a/frontends/web/src/api/bitbox01.ts b/frontends/web/src/api/bitbox01.ts index e95f1158d2..0c84f90e4b 100644 --- a/frontends/web/src/api/bitbox01.ts +++ b/frontends/web/src/api/bitbox01.ts @@ -32,8 +32,6 @@ export type DeviceInfo = { version: string; }; -export const getDeviceInfo = ( - deviceID: string, -): Promise => { +export const getDeviceInfo = (deviceID: string): Promise => { return apiGet(`devices/${deviceID}/info`); }; diff --git a/frontends/web/src/api/bitbox02.ts b/frontends/web/src/api/bitbox02.ts index cf00c240b1..49a8283828 100644 --- a/frontends/web/src/api/bitbox02.ts +++ b/frontends/web/src/api/bitbox02.ts @@ -21,18 +21,20 @@ import { SuccessResponse, FailResponse } from './response'; export const errUserAbort = 104; export type DeviceInfo = { - initialized: boolean; - mnemonicPassphraseEnabled: boolean; - name: string; - securechipModel: string; - version: string; -} + initialized: boolean; + mnemonicPassphraseEnabled: boolean; + name: string; + securechipModel: string; + version: string; +}; type DeviceInfoResponse = SuccessResponse & { - deviceInfo: DeviceInfo; + deviceInfo: DeviceInfo; }; -export const resetDevice = (deviceID: string): Promise => { +export const resetDevice = ( + deviceID: string, +): Promise => { return apiPost(`devices/bitbox02/${deviceID}/reset`); }; @@ -42,9 +44,7 @@ export const getDeviceInfo = ( return apiGet(`devices/bitbox02/${deviceID}/info`); }; -export const checkSDCard = ( - deviceID: string, -): Promise => { +export const checkSDCard = (deviceID: string): Promise => { return apiGet(`devices/bitbox02/${deviceID}/check-sdcard`); }; @@ -59,7 +59,7 @@ export const setDeviceName = ( newDeviceName: string, ): Promise => { return apiPost(`devices/bitbox02/${deviceID}/set-device-name`, { - name: newDeviceName + name: newDeviceName, }); }; @@ -76,15 +76,12 @@ type VersionInfoCommon = { canBackupWithRecoveryWords: boolean; canCreate12Words: boolean; canBIP85: boolean; -} +}; -export type VersionInfo = VersionInfoCommon & ( - { canUpgrade: true, newVersion: string; } | - { canUpgrade: false; }) +export type VersionInfo = VersionInfoCommon & + ({ canUpgrade: true; newVersion: string } | { canUpgrade: false }); -export const getVersion = ( - deviceID: string -): Promise => { +export const getVersion = (deviceID: string): Promise => { return apiGet(`devices/bitbox02/${deviceID}/version`); }; @@ -92,7 +89,10 @@ export const setMnemonicPassphraseEnabled = ( deviceID: string, enabled: boolean, ): Promise => { - return apiPost(`devices/bitbox02/${deviceID}/set-mnemonic-passphrase-enabled`, enabled); + return apiPost( + `devices/bitbox02/${deviceID}/set-mnemonic-passphrase-enabled`, + enabled, + ); }; export const verifyAttestation = ( @@ -104,7 +104,7 @@ export const verifyAttestation = ( export const checkBackup = ( deviceID: string, silent: boolean, -): Promise => { +): Promise => { return apiPost(`devices/bitbox02/${deviceID}/backups/check`, { silent }); }; @@ -121,7 +121,10 @@ export const restoreBackup = ( deviceID: string, selectedBackup: string, ): Promise => { - return apiPost(`devices/bitbox02/${deviceID}/backups/restore`, selectedBackup); + return apiPost( + `devices/bitbox02/${deviceID}/backups/restore`, + selectedBackup, + ); }; export const upgradeDeviceFirmware = (deviceID: string): Promise => { @@ -138,7 +141,8 @@ export const restoreFromMnemonic = ( return apiPost(`devices/bitbox02/${deviceID}/restore-from-mnemonic`); }; -export type TStatus = 'connected' +export type TStatus = + | 'connected' | 'initialized' | 'pairingFailed' | 'require_firmware_upgrade' @@ -175,12 +179,14 @@ export const setPassword = ( }; export const getRootFingerprint = ( - deviceID: string + deviceID: string, ): Promise<(SuccessResponse & { rootFingerprint: string }) | FailResponse> => { return apiGet(`devices/bitbox02/${deviceID}/root-fingerprint`); }; -export const invokeBIP85 = (deviceID: string): Promise => { +export const invokeBIP85 = ( + deviceID: string, +): Promise => { return apiPost(`devices/bitbox02/${deviceID}/invoke-bip85`); }; diff --git a/frontends/web/src/api/bitbox02bootloader.ts b/frontends/web/src/api/bitbox02bootloader.ts index da27737e30..f67c02efd0 100644 --- a/frontends/web/src/api/bitbox02bootloader.ts +++ b/frontends/web/src/api/bitbox02bootloader.ts @@ -24,17 +24,16 @@ export type TStatus = { upgradeSuccessful: boolean; }; -export const getStatus = ( - deviceID: string, -): Promise => { +export const getStatus = (deviceID: string): Promise => { return apiGet(`devices/bitbox02-bootloader/${deviceID}/status`); }; export const syncStatus = (deviceID: string) => { - return ( - cb: TSubscriptionCallback - ) => { - return subscribeEndpoint(`devices/bitbox02-bootloader/${deviceID}/status`, cb); + return (cb: TSubscriptionCallback) => { + return subscribeEndpoint( + `devices/bitbox02-bootloader/${deviceID}/status`, + cb, + ); }; }; @@ -49,40 +48,31 @@ export type TVersionInfo = { additionalUpgradeFollows: boolean; }; -export const getVersionInfo = ( - deviceID: string, -): Promise => { +export const getVersionInfo = (deviceID: string): Promise => { return apiGet(`devices/bitbox02-bootloader/${deviceID}/version-info`); }; -export const upgradeFirmware = ( - deviceID: string, -): Promise => { +export const upgradeFirmware = (deviceID: string): Promise => { return apiPost(`devices/bitbox02-bootloader/${deviceID}/upgrade-firmware`); }; -export const reboot = ( - deviceID: string, -): Promise => { +export const reboot = (deviceID: string): Promise => { return apiPost(`devices/bitbox02-bootloader/${deviceID}/reboot`); }; -export const screenRotate = ( - deviceID: string, -): Promise => { +export const screenRotate = (deviceID: string): Promise => { return apiPost(`devices/bitbox02-bootloader/${deviceID}/screen-rotate`); }; export const getShowFirmwareHash = (deviceID: string) => { return (): Promise => { - return apiGet(`devices/bitbox02-bootloader/${deviceID}/show-firmware-hash-enabled`); + return apiGet( + `devices/bitbox02-bootloader/${deviceID}/show-firmware-hash-enabled`, + ); }; }; -export const setShowFirmwareHash = ( - deviceID: string, - enabled: boolean, -) => { +export const setShowFirmwareHash = (deviceID: string, enabled: boolean) => { return apiPost( `devices/bitbox02-bootloader/${deviceID}/set-firmware-hash-enabled`, enabled, diff --git a/frontends/web/src/api/bitsurance.ts b/frontends/web/src/api/bitsurance.ts index b98b5c5755..fd2225ba46 100644 --- a/frontends/web/src/api/bitsurance.ts +++ b/frontends/web/src/api/bitsurance.ts @@ -17,7 +17,13 @@ import { apiGet, apiPost } from '@/utils/request'; import { AccountCode } from './account'; -export type TDetailStatus = 'active' | 'processing' | 'refused' | 'waitpayment' | 'inactive' | 'canceled'; +export type TDetailStatus = + | 'active' + | 'processing' + | 'refused' + | 'waitpayment' + | 'inactive' + | 'canceled'; export type TAccountDetails = { code: AccountCode; @@ -43,6 +49,8 @@ export const getBitsuranceURL = (): Promise => { // and updates the account configuration based on the retrieved information. If the accountCode is // provided, it checks the insurance status for that specific account; otherwise, it checks // the status for all active BTC accounts. -export const bitsuranceLookup = (code: AccountCode = ''): Promise => { +export const bitsuranceLookup = ( + code: AccountCode = '', +): Promise => { return apiPost('bitsurance/lookup', { code }); }; diff --git a/frontends/web/src/api/bluetooth.ts b/frontends/web/src/api/bluetooth.ts index 2ecbe42ebb..629975e0e1 100644 --- a/frontends/web/src/api/bluetooth.ts +++ b/frontends/web/src/api/bluetooth.ts @@ -21,12 +21,13 @@ export type TPeripheral = { identifier: string; name: string; } & ( - { - connectionState: 'discovered' | 'connecting' | 'connected'; - } | { - connectionState: 'error'; - connectionError: string; - } + | { + connectionState: 'discovered' | 'connecting' | 'connected'; + } + | { + connectionState: 'error'; + connectionError: string; + } ); type TBluetoothState = { @@ -44,7 +45,7 @@ export const connect = (identifier: string): Promise => { }; export const syncState = ( - cb: (state: TBluetoothState) => void + cb: (state: TBluetoothState) => void, ): TUnsubscribe => { return subscribeEndpoint('bluetooth/state', cb); }; diff --git a/frontends/web/src/api/coins.ts b/frontends/web/src/api/coins.ts index 2a047353ac..55f037fd36 100644 --- a/frontends/web/src/api/coins.ts +++ b/frontends/web/src/api/coins.ts @@ -21,17 +21,15 @@ import { apiPost, apiGet } from '@/utils/request'; export type BtcUnit = 'default' | 'sat'; export type TStatus = { - targetHeight: number; - tip: number; - tipAtInitTime: number; - tipHashHex: string; -} + targetHeight: number; + tip: number; + tipAtInitTime: number; + tipHashHex: string; +}; -export const subscribeCoinHeaders = (coinCode: CoinCode) => ( - (cb: TSubscriptionCallback) => ( - subscribeEndpoint(`coins/${coinCode}/headers/status`, cb) - ) -); +export const subscribeCoinHeaders = + (coinCode: CoinCode) => (cb: TSubscriptionCallback) => + subscribeEndpoint(`coins/${coinCode}/headers/status`, cb); export type TSetBtcUnitResponse = { success: boolean; @@ -44,7 +42,7 @@ export const setBtcUnit = (unit: BtcUnit): Promise => { export type TAmount = { success: boolean; amount: string; -} +}; export const parseExternalBtcAmount = (amount: string): Promise => { return apiGet(`coins/btc/parse-external-amount?amount=${amount}`); @@ -56,34 +54,42 @@ type TConvertCurrency = { fiatUnit: Fiat; }; -type TConvertFromCurrencyResponse = { - success: true; - amount: string; -} | { - success: false; - errMsg: string; // TODO: backend should return useful errorMessage -}; +type TConvertFromCurrencyResponse = + | { + success: true; + amount: string; + } + | { + success: false; + errMsg: string; // TODO: backend should return useful errorMessage + }; export const convertFromCurrency = ({ amount, coinCode, fiatUnit, }: TConvertCurrency): Promise => { - return apiGet(`coins/convert-from-fiat?from=${fiatUnit}&to=${coinCode}&amount=${amount}`); + return apiGet( + `coins/convert-from-fiat?from=${fiatUnit}&to=${coinCode}&amount=${amount}`, + ); }; -type TConvertToCurrencyResponse = { - success: true; - fiatAmount: string; -} | { - success: false; - // errMsg: string; // TODO: backend should return useful errorMessage -}; +type TConvertToCurrencyResponse = + | { + success: true; + fiatAmount: string; + } + | { + success: false; + // errMsg: string; // TODO: backend should return useful errorMessage + }; export const convertToCurrency = ({ amount, coinCode, fiatUnit, }: TConvertCurrency): Promise => { - return apiGet(`coins/convert-to-plain-fiat?from=${coinCode}&to=${fiatUnit}&amount=${amount}`); + return apiGet( + `coins/convert-to-plain-fiat?from=${coinCode}&to=${fiatUnit}&amount=${amount}`, + ); }; diff --git a/frontends/web/src/api/devices.ts b/frontends/web/src/api/devices.ts index df2a4698e0..d903e85f59 100644 --- a/frontends/web/src/api/devices.ts +++ b/frontends/web/src/api/devices.ts @@ -19,7 +19,7 @@ import { apiGet } from '@/utils/request'; export type TProductName = 'bitbox' | 'bitbox02' | 'bitbox02-bootloader'; export type TDevices = { - readonly [key in string]: TProductName; + readonly [key in string]: TProductName; }; export const getDeviceList = (): Promise => { diff --git a/frontends/web/src/api/devicessync.ts b/frontends/web/src/api/devicessync.ts index ff2f18199f..f270237236 100644 --- a/frontends/web/src/api/devicessync.ts +++ b/frontends/web/src/api/devicessync.ts @@ -24,7 +24,7 @@ import { TDevices } from './devices'; * Returns a method to unsubscribe. */ export const syncDeviceList = ( - cb: (accounts: TDevices,) => void + cb: (accounts: TDevices) => void, ): TUnsubscribe => { return subscribeEndpoint('devices/registered', cb); }; @@ -38,7 +38,7 @@ export const statusChanged = ( deviceID: string, cb: () => void, ): TUnsubscribe => { - const unsubscribe = subscribeLegacy('statusChanged', event => { + const unsubscribe = subscribeLegacy('statusChanged', (event) => { if (event.type === 'device' && event.deviceID === deviceID) { cb(); } @@ -54,7 +54,7 @@ export const channelHashChanged = ( deviceID: string, cb: (deviceID: string) => void, ): TUnsubscribe => { - const unsubscribe = subscribeLegacy('channelHashChanged', event => { + const unsubscribe = subscribeLegacy('channelHashChanged', (event) => { if (event.type === 'device' && event.deviceID === deviceID) { cb(deviceID); } @@ -70,7 +70,7 @@ export const attestationCheckDone = ( deviceID: string, cb: () => void, ): TUnsubscribe => { - const unsubscribe = subscribeLegacy('attestationCheckDone', event => { + const unsubscribe = subscribeLegacy('attestationCheckDone', (event) => { if (event.type === 'device' && event.deviceID === deviceID) { cb(); } @@ -81,13 +81,17 @@ export const attestationCheckDone = ( export type TSignProgress = { steps: number; step: number; -} +}; export const syncSignProgress = ( - cb: (progress: TSignProgress) => void + cb: (progress: TSignProgress) => void, ): TUnsubscribe => { - const unsubscribe = subscribeLegacy('signProgress', event => { - if ('type' in event && event.type === 'device' && event.data === 'signProgress') { + const unsubscribe = subscribeLegacy('signProgress', (event) => { + if ( + 'type' in event && + event.type === 'device' && + event.data === 'signProgress' + ) { cb(event.meta); } }); diff --git a/frontends/web/src/api/exchanges.ts b/frontends/web/src/api/exchanges.ts index e23f7ddd13..2071280c77 100644 --- a/frontends/web/src/api/exchanges.ts +++ b/frontends/web/src/api/exchanges.ts @@ -29,38 +29,46 @@ export type ExchangeDeal = { isFast: boolean; isBest: boolean; isHidden: boolean; -} +}; -export type TExchangeName = 'moonpay' | 'pocket' | 'btcdirect' | 'btcdirect-otc'; +export type TExchangeName = + | 'moonpay' + | 'pocket' + | 'btcdirect' + | 'btcdirect-otc'; export type ExchangeDeals = { exchangeName: TExchangeName; deals: ExchangeDeal[]; -} +}; export type ExchangeDealsList = { exchanges: ExchangeDeals[]; success: true; -} +}; export type ExchangeError = { success: false; errorCode?: 'coinNotSupported' | 'regionNotSupported'; errorMessage?: string; -} +}; -export type TExchangeDealsResponse = ExchangeDealsList | ExchangeError +export type TExchangeDealsResponse = ExchangeDealsList | ExchangeError; export type TExchangeAction = 'buy' | 'sell'; -export const getExchangeDeals = (action: TExchangeAction, accountCode: AccountCode, region: string): Promise => { +export const getExchangeDeals = ( + action: TExchangeAction, + accountCode: AccountCode, + region: string, +): Promise => { return apiGet(`exchange/deals/${action}/${accountCode}?region=${region}`); }; export type MoonpayBuyInfo = { url: string; address: string; -} +}; export const getMoonpayBuyInfo = (code: AccountCode) => { return (): Promise => { @@ -72,33 +80,42 @@ export type AddressVerificationResponse = { success: boolean; errorMessage?: string; errorCode?: 'addressNotFound' | 'userAbort'; -} - -export const verifyAddress = (address: string, accountCode: AccountCode): Promise => { - return apiPost('exchange/pocket/verify-address', { address, accountCode }); }; -export type TPocketUrlResponse = { - success: true; - url: string; -} | { - success: false; - errorMessage: string; +export const verifyAddress = ( + address: string, + accountCode: AccountCode, +): Promise => { + return apiPost('exchange/pocket/verify-address', { address, accountCode }); }; -export const getPocketURL = (action: TExchangeAction): Promise => { +export type TPocketUrlResponse = + | { + success: true; + url: string; + } + | { + success: false; + errorMessage: string; + }; + +export const getPocketURL = ( + action: TExchangeAction, +): Promise => { return apiGet(`exchange/pocket/api-url/${action}`); }; -export type TBTCDirectInfoResponse = { - success: true; - url: string; - apiKey: string; - address?: string; -} | { - success: false; - errorMessage: string; -}; +export type TBTCDirectInfoResponse = + | { + success: true; + url: string; + apiKey: string; + address?: string; + } + | { + success: false; + errorMessage: string; + }; export const getBTCDirectInfo = ( action: TExchangeAction, @@ -107,7 +124,7 @@ export const getBTCDirectInfo = ( return apiGet(`exchange/btcdirect/info/${action}/${code}`); }; -export type SupportedExchanges= { +export type SupportedExchanges = { exchanges: string[]; }; @@ -117,12 +134,14 @@ export const getExchangeSupported = (code: AccountCode) => { }; }; -export type TBtcDirectResponse = { - success: true; - supported: boolean; -} | { - success: false; -}; +export type TBtcDirectResponse = + | { + success: true; + supported: boolean; + } + | { + success: false; + }; export const getBtcDirectOTCSupported = (code: AccountCode, region: string) => { return (): Promise => { diff --git a/frontends/web/src/api/keystores.ts b/frontends/web/src/api/keystores.ts index c506022ce2..dc60317aba 100644 --- a/frontends/web/src/api/keystores.ts +++ b/frontends/web/src/api/keystores.ts @@ -22,9 +22,7 @@ export type { TUnsubscribe }; type TKeystore = { type: 'hardware' | 'software' }; export type TKeystores = TKeystore[]; -export const subscribeKeystores = ( - cb: (keystores: TKeystores) => void -) => { +export const subscribeKeystores = (cb: (keystores: TKeystores) => void) => { return subscribeEndpoint('keystores', cb); }; diff --git a/frontends/web/src/api/mobiledata.ts b/frontends/web/src/api/mobiledata.ts index 748084efbf..ce6e39caa3 100644 --- a/frontends/web/src/api/mobiledata.ts +++ b/frontends/web/src/api/mobiledata.ts @@ -21,8 +21,5 @@ export const getUsingMobileData = (): Promise => { return apiGet('using-mobile-data'); }; -export const subscribeUsingMobileData = ( - cb: TSubscriptionCallback -) => ( - subscribeEndpoint('using-mobile-data', cb) -); +export const subscribeUsingMobileData = (cb: TSubscriptionCallback) => + subscribeEndpoint('using-mobile-data', cb); diff --git a/frontends/web/src/api/node.ts b/frontends/web/src/api/node.ts index 9ad668e669..f7d28082a4 100644 --- a/frontends/web/src/api/node.ts +++ b/frontends/web/src/api/node.ts @@ -17,15 +17,19 @@ import { apiPost } from '@/utils/request'; import { SuccessResponse } from './response'; -type TCertResponse = { - success: true; - pemCert: string; -} | { - success: false; - errorMessage: string; -}; +type TCertResponse = + | { + success: true; + pemCert: string; + } + | { + success: false; + errorMessage: string; + }; -export const downloadCert = (electrumServer: string): Promise => { +export const downloadCert = ( + electrumServer: string, +): Promise => { return apiPost('certs/download', electrumServer); }; @@ -35,11 +39,15 @@ export type TElectrumServer = { pemCert: string; }; -type TCheckElectrumResponse = SuccessResponse | { - success: false; - errorMessage: string; -}; +type TCheckElectrumResponse = + | SuccessResponse + | { + success: false; + errorMessage: string; + }; -export const checkElectrum = (server: TElectrumServer): Promise => { +export const checkElectrum = ( + server: TElectrumServer, +): Promise => { return apiPost('electrum/check', server); }; diff --git a/frontends/web/src/api/response.ts b/frontends/web/src/api/response.ts index f55b3c0b46..a5743bfdeb 100644 --- a/frontends/web/src/api/response.ts +++ b/frontends/web/src/api/response.ts @@ -15,12 +15,12 @@ */ export type SuccessResponse = { - success: true; -} + success: true; +}; // if the backend uses maybeBB02Err export type FailResponse = { - code?: number; - message?: string; - success: false; -} + code?: number; + message?: string; + success: false; +}; diff --git a/frontends/web/src/api/subscribe.ts b/frontends/web/src/api/subscribe.ts index 602a893c23..a20426ad44 100644 --- a/frontends/web/src/api/subscribe.ts +++ b/frontends/web/src/api/subscribe.ts @@ -32,17 +32,17 @@ export const subscribeEndpoint = ( ): TUnsubscribe => { return apiSubscribe(endpoint, (event: TEvent) => { switch (event.action) { - case 'replace': - cb(event.object); - break; - case 'reload': - // TODO: backend should push data with "replace" and not use "reload" - apiGet(event.subject) - .then(object => cb(object)) - .catch(console.error); - break; - default: - throw new Error(`Event: ${JSON.stringify(event)} not supported`); + case 'replace': + cb(event.object); + break; + case 'reload': + // TODO: backend should push data with "replace" and not use "reload" + apiGet(event.subject) + .then((object) => cb(object)) + .catch(console.error); + break; + default: + throw new Error(`Event: ${JSON.stringify(event)} not supported`); } }); }; @@ -54,7 +54,7 @@ export const subscribeEndpoint = ( * See utils/websocket.js */ export const backendConnected = ( - cb: (connected: boolean) => void + cb: (connected: boolean) => void, ): TUnsubscribe => { return subscribeEndpoint('backend/connected', cb); }; diff --git a/frontends/web/src/api/transactions.ts b/frontends/web/src/api/transactions.ts index f82a67f29f..64dfa41d37 100644 --- a/frontends/web/src/api/transactions.ts +++ b/frontends/web/src/api/transactions.ts @@ -17,14 +17,9 @@ import { subscribe as subscribeLegacy } from '@/utils/event-legacy'; export const syncNewTxs = ( - cb: ( - meta: { - count: number, - accountName: string, - } - ) => void, + cb: (meta: { count: number; accountName: string }) => void, ) => { - return subscribeLegacy('newTxs', event => { + return subscribeLegacy('newTxs', (event) => { if (event.type === 'backend') { cb(event.meta); } diff --git a/frontends/web/src/api/version.ts b/frontends/web/src/api/version.ts index 0614698a6b..a41896d202 100644 --- a/frontends/web/src/api/version.ts +++ b/frontends/web/src/api/version.ts @@ -20,10 +20,10 @@ import { apiGet } from '@/utils/request'; * Describes the file that is loaded from 'https://bitbox.swiss/updates/desktop.json'. */ export type TUpdateFile = { - current: string; - version: string; - description: string; -} + current: string; + version: string; + description: string; +}; export const getVersion = (): Promise => { return apiGet('version'); diff --git a/frontends/web/src/app.tsx b/frontends/web/src/app.tsx index 7632c67330..e524da83d0 100644 --- a/frontends/web/src/app.tsx +++ b/frontends/web/src/app.tsx @@ -58,10 +58,12 @@ export const App = () => { useEffect(() => { return syncNewTxs((meta) => { - notifyUser(t('notification.newTxs', { - count: meta.count, - accountName: meta.accountName, - })); + notifyUser( + t('notification.newTxs', { + count: meta.count, + accountName: meta.accountName, + }), + ); }); }, [t]); @@ -78,35 +80,39 @@ export const App = () => { return; } // if no accounts are registered on specified views route to / - if (accounts.length === 0 && ( - currentURL.startsWith('/account-summary') - || currentURL.startsWith('/add-account') - || currentURL.startsWith('/settings/manage-accounts') - )) { + if ( + accounts.length === 0 && + (currentURL.startsWith('/account-summary') || + currentURL.startsWith('/add-account') || + currentURL.startsWith('/settings/manage-accounts')) + ) { navigate('/'); return; } // if no devices are registered on specified views route to / if ( - deviceIDs.length === 0 - && ( - currentURL.startsWith('/settings/device-settings/') - || currentURL.startsWith('/manage-backups/') - ) + deviceIDs.length === 0 && + (currentURL.startsWith('/settings/device-settings/') || + currentURL.startsWith('/manage-backups/')) ) { navigate('/'); return; } // if device is connected route to device settings if ( - deviceIDs.length === 1 - && currentURL === '/settings/no-device-connected' + deviceIDs.length === 1 && + currentURL === '/settings/no-device-connected' ) { navigate(`/settings/device-settings/${deviceIDs[0]}`); return; } // if on an account that isn't registered route to / - if (inAccounts && !accounts.some(account => currentURL.startsWith('/account/' + account.code))) { + if ( + inAccounts && + !accounts.some((account) => + currentURL.startsWith('/account/' + account.code), + ) + ) { navigate('/'); return; } @@ -131,7 +137,6 @@ export const App = () => { navigate('/settings/manage-accounts'); return; } - }, [accounts, devices, navigate]); useEffect(() => { @@ -140,8 +145,8 @@ export const App = () => { // If a device is newly connected, we route to the settings. if ( - newDeviceIDList.length > 0 - && newDeviceIDList[0] !== oldDeviceIDList[0] + newDeviceIDList.length > 0 && + newDeviceIDList[0] !== oldDeviceIDList[0] ) { // We only route to settings if it is a bb01 or a bb02 bootloader. // The bitbox02 wizard itself is mounted globally (see BitBox02Wizard) so it can be unlocked @@ -165,38 +170,34 @@ export const App = () => { }; const deviceIDs: string[] = Object.keys(devices); - const activeAccounts = accounts.filter(acct => acct.active); + const activeAccounts = accounts.filter((acct) => acct.active); - const showBottomNavigation = deviceIDs.length > 0 || activeAccounts.length > 0; + const showBottomNavigation = + deviceIDs.length > 0 || activeAccounts.length > 0; return (
- - -
+ + +
- { - Object.entries(devices).map(([deviceID, productName]) => { - if (productName === 'bitbox02') { - return ( - - - - ); - } - return null; - }) - } + {Object.entries(devices).map(([deviceID, productName]) => { + if (productName === 'bitbox02') { + return ( + + + + ); + } + return null; + })} void; -} +}; export const ActionableItem = ({ className = '', @@ -46,11 +46,10 @@ export const ActionableItem = ({ )} diff --git a/frontends/web/src/components/alert/Alert.test.tsx b/frontends/web/src/components/alert/Alert.test.tsx index f578b4b8ab..ee1cfc1e99 100644 --- a/frontends/web/src/components/alert/Alert.test.tsx +++ b/frontends/web/src/components/alert/Alert.test.tsx @@ -25,31 +25,36 @@ vi.mock('@/utils/request', () => ({ import { apiGet } from '@/utils/request'; -(apiGet as Mock).mockImplementation(endpoint => { +(apiGet as Mock).mockImplementation((endpoint) => { switch (endpoint) { - case 'config': { return Promise.resolve({ backend: { userLanguage: 'it' } }); } - // case 'native-locale': { return Promise.resolve('de'); } - default: { return Promise.resolve(); } + case 'config': { + return Promise.resolve({ backend: { userLanguage: 'it' } }); + } + // case 'native-locale': { return Promise.resolve('de'); } + default: { + return Promise.resolve(); + } } }); describe('Alert', () => { - beforeAll(() => { - window.matchMedia = window.matchMedia || function(query) { - return { - matches: false, - media: query, - onchange: null, - addEventListener: vi.fn(), - removeEventListener: vi.fn(), - dispatchEvent: vi.fn(), + window.matchMedia = + window.matchMedia || + function (query) { + return { + matches: false, + media: query, + onchange: null, + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + }; }; - }; }); function renderAlert() { - return render(); + return render(); } it('should render the Alert component properly', () => { @@ -100,4 +105,4 @@ describe('Alert', () => { // assert that the element is not rendered as a dialog expect(container.querySelector('.dialog')).not.toBeInTheDocument(); }); -}); \ No newline at end of file +}); diff --git a/frontends/web/src/components/alert/Alert.tsx b/frontends/web/src/components/alert/Alert.tsx index 3ebea31412..e6e791057c 100644 --- a/frontends/web/src/components/alert/Alert.tsx +++ b/frontends/web/src/components/alert/Alert.tsx @@ -45,9 +45,7 @@ const Alert = () => { const { t } = useTranslation(); alertUser = (message: string, options: AlertUserOptions = {}) => { - const { - asDialog = true, - } = options; + const { asDialog = true } = options; callback = options.callback; setActive(true); setAsDialog(asDialog); @@ -61,23 +59,26 @@ const Alert = () => { setActive(false); }; - return (active && message) ? ( + return active && message ? (
setActive(false)}> - { - setActive(false); return false; - }} /> + { + setActive(false); + return false; + }} + /> - } /> + verticallyCentered + > + } + /> - diff --git a/frontends/web/src/components/amount/amount-with-unit.tsx b/frontends/web/src/components/amount/amount-with-unit.tsx index 221c27c340..453026fc2a 100644 --- a/frontends/web/src/components/amount/amount-with-unit.tsx +++ b/frontends/web/src/components/amount/amount-with-unit.tsx @@ -22,14 +22,14 @@ import { isBitcoinCoin } from '@/routes/account/utils'; import style from './amount-with-unit.module.css'; type TAmountWithUnitProps = { - amount: IAmount; - tableRow?: boolean; - enableRotateUnit?: boolean; - sign?: string; - removeBtcTrailingZeroes?: boolean; - alwaysShowAmounts?: boolean; - convertToFiat?: boolean; -} + amount: IAmount; + tableRow?: boolean; + enableRotateUnit?: boolean; + sign?: string; + removeBtcTrailingZeroes?: boolean; + alwaysShowAmounts?: boolean; + convertToFiat?: boolean; +}; export const AmountWithUnit = ({ amount, @@ -38,9 +38,10 @@ export const AmountWithUnit = ({ sign, removeBtcTrailingZeroes, convertToFiat, - alwaysShowAmounts = false + alwaysShowAmounts = false, }: TAmountWithUnitProps) => { - const { rotateDefaultCurrency, defaultCurrency, rotateBtcUnit } = useContext(RatesContext); + const { rotateDefaultCurrency, defaultCurrency, rotateBtcUnit } = + useContext(RatesContext); let displayedAmount: string = ''; let displayedUnit: CoinUnit | ConversionUnit; @@ -59,19 +60,26 @@ export const AmountWithUnit = ({ onClick = rotateBtcUnit; } - const enableClick = rotateUnit && (convertToFiat || isBitcoinCoin(amount.unit)); - const formattedAmount = !!displayedAmount ? - ( - - ) : '---'; + const enableClick = + rotateUnit && (convertToFiat || isBitcoinCoin(amount.unit)); + const formattedAmount = !!displayedAmount ? ( + + ) : ( + '---' + ); - const amountUnit = ; + const amountUnit = ( + + ); if (tableRow) { return ( @@ -82,11 +90,11 @@ export const AmountWithUnit = ({ ); } return ( - + {!!displayedAmount ? sign : ''} - {formattedAmount} - {' '} - {amountUnit} + {formattedAmount} {amountUnit} ); }; @@ -94,10 +102,14 @@ export const AmountWithUnit = ({ type TAmountUnitProps = { rotateUnit?: () => Promise; unit: ConversionUnit | CoinUnit; -} +}; export const AmountUnit = ({ rotateUnit, unit }: TAmountUnitProps) => { const classRototable = rotateUnit ? style.rotatable : ''; const textStyle = `${style.unit} ${classRototable}`; - return {unit}; + return ( + + {unit} + + ); }; diff --git a/frontends/web/src/components/amount/amount.test.tsx b/frontends/web/src/components/amount/amount.test.tsx index b5ef530787..f9e067a262 100644 --- a/frontends/web/src/components/amount/amount.test.tsx +++ b/frontends/web/src/components/amount/amount.test.tsx @@ -15,7 +15,16 @@ */ import { useContext } from 'react'; -import { Mock, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { + Mock, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; import { render } from '@testing-library/react'; import { Amount } from './amount'; import { CoinUnit, ConversionUnit } from '@/api/account'; @@ -23,7 +32,7 @@ import { CoinUnit, ConversionUnit } from '@/api/account'; vi.mock('react', async () => ({ ...(await vi.importActual('react')), useContext: vi.fn(), - createContext: vi.fn() + createContext: vi.fn(), })); vi.mock('@/i18n/i18n', () => ({ @@ -44,14 +53,13 @@ const validateSpacing = (values: string[], elements: Element[]) => { // makes sure each value corresponds to an element values.length === elements.length && // makes sure every element is a span - elements.every(element => element.tagName.toLowerCase() === 'span') && + elements.every((element) => element.tagName.toLowerCase() === 'span') && // and it has the correct value elements.every((element, index) => element.innerHTML === values[index]) ); }; describe('Amount formatting', () => { - beforeAll(() => { Object.defineProperty(window, 'matchMedia', { writable: true, @@ -71,239 +79,348 @@ describe('Amount formatting', () => { hideAmounts: false, nativeLocale: 'de-CH', group: '’', - decimal: '.' + decimal: '.', }); }); describe('hide amounts', () => { - it('should render triple-asterisks (***) when amount is set to be hidden', () => { (useContext as Mock).mockReturnValue({ hideAmounts: true }); - const { container } = render( - - ); + const { container } = render(); expect(container).toHaveTextContent('***'); }); - }); describe('sat amounts', () => { let coins: CoinUnit[] = ['sat', 'tsat']; coins.forEach((coin) => { - it('12345678901234 ' + coin + ' with removeBtcTrailingZeroes enabled gets spaced', () => { - const { getByTestId } = render(); - const blocks = getByTestId('amountBlocks'); - - const values = [ - '12', - '345', - '678', - '901', - '234' - ]; - const allSpacedElements = [...blocks.children]; - expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); - }); + it( + '12345678901234 ' + + coin + + ' with removeBtcTrailingZeroes enabled gets spaced', + () => { + const { getByTestId } = render( + , + ); + const blocks = getByTestId('amountBlocks'); - it('1234567 ' + coin + ' with removeBtcTrailingZeroes enabled gets spaced', () => { - const { getByTestId } = render(); - const blocks = getByTestId('amountBlocks'); - const values = [ - '1', - '234', - '567', - ]; - const allSpacedElements = [...blocks.children]; - expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); - }); - - - it('12345 ' + coin + ' with removeBtcTrailingZeroes enabled gets spaced', () => { - const { getByTestId } = render(); - const blocks = getByTestId('amountBlocks'); - const values = [ - '12', - '345', - ]; - const allSpacedElements = [...blocks.children]; - expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); - }); + const values = ['12', '345', '678', '901', '234']; + const allSpacedElements = [...blocks.children]; + expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); + }, + ); - it('21 ' + coin + ' with removeBtcTrailingZeroes enabled gets spaced', () => { - const { getByTestId } = render(); - const blocks = getByTestId('amountBlocks'); - const values = [ - '21', - ]; - const allSpacedElements = [...blocks.children]; - expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); - }); + it( + '1234567 ' + coin + ' with removeBtcTrailingZeroes enabled gets spaced', + () => { + const { getByTestId } = render( + , + ); + const blocks = getByTestId('amountBlocks'); + const values = ['1', '234', '567']; + const allSpacedElements = [...blocks.children]; + expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); + }, + ); - it('12345678901234 ' + coin + ' with removeBtcTrailingZeroes disabled gets spaced', () => { - const { getByTestId } = render(); - const blocks = getByTestId('amountBlocks'); - const values = [ - '12', - '345', - '678', - '901', - '234', - ]; - const allSpacedElements = [...blocks.children]; - expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); - }); + it( + '12345 ' + coin + ' with removeBtcTrailingZeroes enabled gets spaced', + () => { + const { getByTestId } = render( + , + ); + const blocks = getByTestId('amountBlocks'); + const values = ['12', '345']; + const allSpacedElements = [...blocks.children]; + expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); + }, + ); + it( + '21 ' + coin + ' with removeBtcTrailingZeroes enabled gets spaced', + () => { + const { getByTestId } = render( + , + ); + const blocks = getByTestId('amountBlocks'); + const values = ['21']; + const allSpacedElements = [...blocks.children]; + expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); + }, + ); - it('1234567 ' + coin + ' with removeBtcTrailingZeroes disabled gets spaced', () => { - const { getByTestId } = render(); - const blocks = getByTestId('amountBlocks'); - const values = [ - '1', - '234', - '567', - ]; - const allSpacedElements = [...blocks.children]; - expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); - }); + it( + '12345678901234 ' + + coin + + ' with removeBtcTrailingZeroes disabled gets spaced', + () => { + const { getByTestId } = render( + , + ); + const blocks = getByTestId('amountBlocks'); + const values = ['12', '345', '678', '901', '234']; + const allSpacedElements = [...blocks.children]; + expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); + }, + ); - it('12345 ' + coin + ' with removeBtcTrailingZeroes disabled gets spaced', () => { - const { getByTestId } = render(); - const blocks = getByTestId('amountBlocks'); - const values = [ - '12', - '345', - ]; - const allSpacedElements = [...blocks.children]; - expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); - }); + it( + '1234567 ' + + coin + + ' with removeBtcTrailingZeroes disabled gets spaced', + () => { + const { getByTestId } = render( + , + ); + const blocks = getByTestId('amountBlocks'); + const values = ['1', '234', '567']; + const allSpacedElements = [...blocks.children]; + expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); + }, + ); - it('21 ' + coin + ' with removeBtcTrailingZeroes disabled gets spaced', () => { - const { getByTestId } = render(); - const blocks = getByTestId('amountBlocks'); - const values = [ - '21', - ]; - const allSpacedElements = [...blocks.children]; - expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); - }); + it( + '12345 ' + coin + ' with removeBtcTrailingZeroes disabled gets spaced', + () => { + const { getByTestId } = render(); + const blocks = getByTestId('amountBlocks'); + const values = ['12', '345']; + const allSpacedElements = [...blocks.children]; + expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); + }, + ); + it( + '21 ' + coin + ' with removeBtcTrailingZeroes disabled gets spaced', + () => { + const { getByTestId } = render(); + const blocks = getByTestId('amountBlocks'); + const values = ['21']; + const allSpacedElements = [...blocks.children]; + expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); + }, + ); }); }); describe('BTC/LTC coins amounts', () => { let coins: CoinUnit[] = ['BTC', 'TBTC', 'LTC', 'TLTC']; - coins.forEach(coin => { - it('10.00000000 ' + coin + ' with removeBtcTrailingZeroes enabled becomes 10', () => { - const { container } = render(); - expect(container).toHaveTextContent('10'); - }); - it('12345.12300000 ' + coin + ' with removeBtcTrailingZeroes enabled becomes 12345.123', () => { - const { container } = render(); - expect(container).toHaveTextContent('12345.123'); - }); - it('42 ' + coin + ' with removeBtcTrailingZeroes enabled stays 42', () => { - const { container } = render(); - expect(container).toHaveTextContent('42'); - }); - it('0.12345678 ' + coin + ' with removeBtcTrailingZeroes enabled stays 0.12345678', () => { - const { container } = render(); - expect(container).toHaveTextContent('0.12345678'); - }); - it('10.00000000 ' + coin + ' with removeBtcTrailingZeroes disabled gets spaced', () => { - const { getByTestId } = render(); - const blocks = getByTestId('amountBlocks'); - const values = [ - '10.00', - '000', - '000' - ]; - const allSpacedElements = [...blocks.children]; - expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); - }); - it('12345.12300000 ' + coin + ' with removeBtcTrailingZeroes disabled gets spaced', () => { - const { getByTestId } = render(); - const blocks = getByTestId('amountBlocks'); - const values = [ - '12345.12', - '300', - '000' - ]; - const allSpacedElements = [...blocks.children]; - expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); - }); - it('42 ' + coin + ' with removeBtcTrailingZeroes disabled stays 42', () => { - const { container } = render(); - expect(container).toHaveTextContent('42'); - }); - it('0.12345678 ' + coin + ' with removeBtcTrailingZeroes disabled gets spaced', () => { - const { getByTestId } = render(); - const blocks = getByTestId('amountBlocks'); - const values = [ - '0.12', - '345', - '678' - ]; - const allSpacedElements = [...blocks.children]; - expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); - }); + coins.forEach((coin) => { + it( + '10.00000000 ' + + coin + + ' with removeBtcTrailingZeroes enabled becomes 10', + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('10'); + }, + ); + it( + '12345.12300000 ' + + coin + + ' with removeBtcTrailingZeroes enabled becomes 12345.123', + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('12345.123'); + }, + ); + it( + '42 ' + coin + ' with removeBtcTrailingZeroes enabled stays 42', + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('42'); + }, + ); + it( + '0.12345678 ' + + coin + + ' with removeBtcTrailingZeroes enabled stays 0.12345678', + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('0.12345678'); + }, + ); + it( + '10.00000000 ' + + coin + + ' with removeBtcTrailingZeroes disabled gets spaced', + () => { + const { getByTestId } = render( + , + ); + const blocks = getByTestId('amountBlocks'); + const values = ['10.00', '000', '000']; + const allSpacedElements = [...blocks.children]; + expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); + }, + ); + it( + '12345.12300000 ' + + coin + + ' with removeBtcTrailingZeroes disabled gets spaced', + () => { + const { getByTestId } = render( + , + ); + const blocks = getByTestId('amountBlocks'); + const values = ['12345.12', '300', '000']; + const allSpacedElements = [...blocks.children]; + expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); + }, + ); + it( + '42 ' + coin + ' with removeBtcTrailingZeroes disabled stays 42', + () => { + const { container } = render(); + expect(container).toHaveTextContent('42'); + }, + ); + it( + '0.12345678 ' + + coin + + ' with removeBtcTrailingZeroes disabled gets spaced', + () => { + const { getByTestId } = render( + , + ); + const blocks = getByTestId('amountBlocks'); + const values = ['0.12', '345', '678']; + const allSpacedElements = [...blocks.children]; + expect(validateSpacing(values, allSpacedElements)).toBeTruthy(); + }, + ); }); }); describe('non BTC coins amounts', () => { let coins: CoinUnit[] = ['ETH', 'SEPETH']; - coins.forEach(coin => { - it('10.00000000 ' + coin + ' with removeBtcTrailingZeroes enabled stays 10.00000000', () => { - const { container } = render(); - expect(container).toHaveTextContent('10.00000000'); - }); - it('10.12300000 ' + coin + ' with removeBtcTrailingZeroes enabled stays 10.12300000', () => { - const { container } = render(); - expect(container).toHaveTextContent('10.12300000'); - }); - it('42 ' + coin + ' with removeBtcTrailingZeroes enabled stays 42', () => { - const { container } = render(); - expect(container).toHaveTextContent('42'); - }); - it('10.00000000 ' + coin + ' with removeBtcTrailingZeroes disabled stays 10.00000000', () => { - const { container } = render(); - expect(container).toHaveTextContent('10.00000000'); - }); - it('10.12300000 ' + coin + ' with removeBtcTrailingZeroes disabled stays 10.12300000', () => { - const { container } = render(); - expect(container).toHaveTextContent('10.12300000'); - }); - it('42 ' + coin + ' with removeBtcTrailingZeroes disabled stays 42', () => { - const { container } = render(); - expect(container).toHaveTextContent('42'); - }); + coins.forEach((coin) => { + it( + '10.00000000 ' + + coin + + ' with removeBtcTrailingZeroes enabled stays 10.00000000', + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('10.00000000'); + }, + ); + it( + '10.12300000 ' + + coin + + ' with removeBtcTrailingZeroes enabled stays 10.12300000', + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('10.12300000'); + }, + ); + it( + '42 ' + coin + ' with removeBtcTrailingZeroes enabled stays 42', + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('42'); + }, + ); + it( + '10.00000000 ' + + coin + + ' with removeBtcTrailingZeroes disabled stays 10.00000000', + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('10.00000000'); + }, + ); + it( + '10.12300000 ' + + coin + + ' with removeBtcTrailingZeroes disabled stays 10.12300000', + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('10.12300000'); + }, + ); + it( + '42 ' + coin + ' with removeBtcTrailingZeroes disabled stays 42', + () => { + const { container } = render(); + expect(container).toHaveTextContent('42'); + }, + ); }); }); describe('fiat amounts', () => { let fiatCoins: ConversionUnit[] = ['USD', 'EUR', 'CHF']; - fiatCoins.forEach(coin => { - it('1\'340.25 ' + coin + ' with removeBtcTrailingZeroes enabled stays 1\'340.25', () => { - const { container } = render(); - expect(container).toHaveTextContent('1’340.25'); - }); - it('218.00 ' + coin + ' with removeBtcTrailingZeroes enabled stays 218.00', () => { - const { container } = render(); - expect(container).toHaveTextContent('218.00'); - }); - it('1\'340.25 ' + coin + ' with removeBtcTrailingZeroes disabled stays 1\'340.25', () => { - const { container } = render(); - expect(container).toHaveTextContent('1’340.25'); - }); - it('218.00 ' + coin + ' with removeBtcTrailingZeroes disabled stays 218.00', () => { - const { container } = render(); - expect(container).toHaveTextContent('218.00'); - }); - + fiatCoins.forEach((coin) => { + it( + "1'340.25 " + + coin + + " with removeBtcTrailingZeroes enabled stays 1'340.25", + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('1’340.25'); + }, + ); + it( + '218.00 ' + coin + ' with removeBtcTrailingZeroes enabled stays 218.00', + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('218.00'); + }, + ); + it( + "1'340.25 " + + coin + + " with removeBtcTrailingZeroes disabled stays 1'340.25", + () => { + const { container } = render( + , + ); + expect(container).toHaveTextContent('1’340.25'); + }, + ); + it( + '218.00 ' + + coin + + ' with removeBtcTrailingZeroes disabled stays 218.00', + () => { + const { container } = render(); + expect(container).toHaveTextContent('218.00'); + }, + ); }); }); afterEach(() => { vi.clearAllMocks(); }); - }); diff --git a/frontends/web/src/components/amount/amount.tsx b/frontends/web/src/components/amount/amount.tsx index 45482b7480..181f52ea57 100644 --- a/frontends/web/src/components/amount/amount.tsx +++ b/frontends/web/src/components/amount/amount.tsx @@ -25,60 +25,49 @@ const formatSats = (amount: string): JSX.Element => { const blocks: JSX.Element[] = []; const blockSize = 3; - for (let i = amount.length; i > 0 ; i -= blockSize) { + for (let i = amount.length; i > 0; i -= blockSize) { const start = Math.max(0, i - blockSize); blocks.push( + className={start === 0 ? '' : style.space} + > {amount.slice(start, i)} - + , ); } - return ( - - {blocks.reverse()} - - ); + return {blocks.reverse()}; }; const formatLocalizedAmount = ( amount: string, group: string, - decimal: string + decimal: string, ) => { - return ( - amount - .replace('.', '_') // convert decimal first, in case group separator uses dot - .replace(/[']/g, group) // replace group separator - .replace('_', decimal) - ); + return amount + .replace('.', '_') // convert decimal first, in case group separator uses dot + .replace(/[']/g, group) // replace group separator + .replace('_', decimal); }; -const formatBtc = ( - amount: string, - group: string, - decimal: string -) => { +const formatBtc = (amount: string, group: string, decimal: string) => { const dot = amount.indexOf('.'); if (dot === -1) { return amount; } // localize the first part, everything up to the second decimal place, the rest is grouped by spaces - const formattedPart = formatLocalizedAmount(amount.slice(0, dot + 3), group, decimal); + const formattedPart = formatLocalizedAmount( + amount.slice(0, dot + 3), + group, + decimal, + ); return ( - - {formattedPart} - - - {amount.slice(dot + 3, dot + 6)} - - - {amount.slice(dot + 6, dot + 9)} - + {formattedPart} + {amount.slice(dot + 3, dot + 6)} + {amount.slice(dot + 6, dot + 9)} ); }; @@ -136,22 +125,22 @@ export const FormattedAmount = ({ } switch (unit) { - case 'BTC': - case 'TBTC': - case 'LTC': - case 'TLTC': - if (removeBtcTrailingZeroes && amount.includes('.')) { - return ( - formatLocalizedAmount( - amount.replace(/\.?0+$/, ''), group, decimal - ) - ); - } else { - return formatBtc(amount, group, decimal); - } - case 'sat': - case 'tsat': - return formatSats(amount); + case 'BTC': + case 'TBTC': + case 'LTC': + case 'TLTC': + if (removeBtcTrailingZeroes && amount.includes('.')) { + return formatLocalizedAmount( + amount.replace(/\.?0+$/, ''), + group, + decimal, + ); + } else { + return formatBtc(amount, group, decimal); + } + case 'sat': + case 'tsat': + return formatSats(amount); } return formatLocalizedAmount(amount, group, decimal); diff --git a/frontends/web/src/components/amount/conversion-amount.tsx b/frontends/web/src/components/amount/conversion-amount.tsx index 2597adfcc7..3446db7efb 100644 --- a/frontends/web/src/components/amount/conversion-amount.tsx +++ b/frontends/web/src/components/amount/conversion-amount.tsx @@ -39,7 +39,8 @@ export const ConversionAmount = ({ type, }: TConversionAmountProps) => { const { defaultCurrency } = useContext(RatesContext); - const conversion = amount?.conversions && amount?.conversions[defaultCurrency]; + const conversion = + amount?.conversions && amount?.conversions[defaultCurrency]; const sign = getTxSign(type); const estimatedPrefix = '\u2248'; // ≈ @@ -49,7 +50,9 @@ export const ConversionAmount = ({ const conversionUnit = sendToSelf ? amountToShow.unit : defaultCurrency; // we skip the estimated conversion prefix when the Tx is send to self, or both coin and conversion are in BTC units. - const skipEstimatedPrefix = sendToSelf || (btcUnits.includes(conversionUnit) && btcUnits.includes(amountToShow.unit)); + const skipEstimatedPrefix = + sendToSelf || + (btcUnits.includes(conversionUnit) && btcUnits.includes(amountToShow.unit)); return ( @@ -61,19 +64,16 @@ export const ConversionAmount = ({ )} {amountToShow.estimated && !skipEstimatedPrefix && ( - {estimatedPrefix}{' '} + {estimatedPrefix} )} {conversion && !sendToSelf ? sign : null} - - {' '} - {conversionUnit} - + {conversionUnit} - ) : null } + ) : null} ); }; diff --git a/frontends/web/src/components/anchor/anchor.tsx b/frontends/web/src/components/anchor/anchor.tsx index b5a87a4bdb..b34d4e0e33 100644 --- a/frontends/web/src/components/anchor/anchor.tsx +++ b/frontends/web/src/components/anchor/anchor.tsx @@ -25,7 +25,7 @@ type TProps = { href: string; icon?: ReactNode; title?: string; -} +}; /** * Renders a link to an external URL or file, which will open in the native browser or application. @@ -39,13 +39,7 @@ type TProps = { * * @param {LinkProps} props - The props object containing properties for the Link component. */ -export const A = ({ - href, - icon, - className, - children, - ...props -}: TProps) => { +export const A = ({ href, icon, className, children, ...props }: TProps) => { return ( + {...props} + > {icon ? icon : null} {children} diff --git a/frontends/web/src/components/aopp/aopp.tsx b/frontends/web/src/components/aopp/aopp.tsx index 14d67213ef..b801ab7440 100644 --- a/frontends/web/src/components/aopp/aopp.tsx +++ b/frontends/web/src/components/aopp/aopp.tsx @@ -20,7 +20,12 @@ import * as accountAPI from '@/api/account'; import * as aoppAPI from '@/api/aopp'; import { equal } from '@/utils/equal'; import { SimpleMarkup } from '@/utils/markup'; -import { View, ViewHeader, ViewContent, ViewButtons } from '@/components/view/view'; +import { + View, + ViewHeader, + ViewContent, + ViewButtons, +} from '@/components/view/view'; import { Message } from '@/components/message/message'; import { Button, Field, Label, Select } from '@/components/forms'; import { CopyableInput } from '@/components/copy/Copy'; @@ -31,7 +36,7 @@ import styles from './aopp.module.css'; type TProps = { children: ReactNode; -} +}; const Banner = ({ children }: TProps) => (
{children}
@@ -55,9 +60,12 @@ export const Aopp = () => { useEffect(() => { if (aopp !== prevAopp) { setPrevAopp(aopp); - if (aopp?.state === 'choosing-account' - && aopp.accounts.length - && (prevAopp?.state !== 'choosing-account' || !equal(aopp.accounts, prevAopp?.accounts))) { + if ( + aopp?.state === 'choosing-account' && + aopp.accounts.length && + (prevAopp?.state !== 'choosing-account' || + !equal(aopp.accounts, prevAopp?.accounts)) + ) { setAccountCode(aopp.accounts[0].code); } } @@ -74,176 +82,173 @@ export const Aopp = () => { return null; } switch (aopp.state) { - case 'error': - return ( - - -

{domain(aopp.callback)}

-
- - - {t(`error.${aopp.errorCode}`, { host: domain(aopp.callback) })} - - - - - -
- ); - case 'inactive': - // Inactive, waiting for action. - return null; - case 'user-approval': - const host = domain(aopp.callback); - const addressRequestMsg = aopp.xpubRequired ? 'aopp.addressRequestWithXPub' : 'aopp.addressRequest'; - const addressRequestWithLogoMsg = aopp.xpubRequired ? 'aopp.addressRequestWithLogoAndXPub' : 'aopp.addressRequestWithLogo'; - return ( - - - - ${host}` - })} /> + case 'error': + return ( + + +

{domain(aopp.callback)}

+
+ + + {t(`error.${aopp.errorCode}`, { host: domain(aopp.callback) })} + + + + + +
+ ); + case 'inactive': + // Inactive, waiting for action. + return null; + case 'user-approval': + const host = domain(aopp.callback); + const addressRequestMsg = aopp.xpubRequired + ? 'aopp.addressRequestWithXPub' + : 'aopp.addressRequest'; + const addressRequestWithLogoMsg = aopp.xpubRequired + ? 'aopp.addressRequestWithLogoAndXPub' + : 'aopp.addressRequestWithLogo'; + return ( + + + + ${host}`, + })} + /> + } + withLogoText={t(addressRequestWithLogoMsg)} + /> + {aopp.xpubRequired ? ( + + {' '} + {t('aopp.xpubRequested', { host: `${host}` })}{' '} + + ) : ( + '' )} - withLogoText={t(addressRequestWithLogoMsg)} /> - { - aopp.xpubRequired ? - ( - {t('aopp.xpubRequested', { host: `${host}` })} - ) : '' - } - - - - - - - ); - case 'awaiting-keystore': - return ( - {t('aopp.banner')} - ); - case 'choosing-account': { - const options = aopp.accounts.map(account => { - return { - text: account.name, - value: account.code, - }; - }); - return ( - - +
+ + + + +
+ ); + case 'awaiting-keystore': + return {t('aopp.banner')}; + case 'choosing-account': { + const options = aopp.accounts.map((account) => { + return { + text: account.name, + value: account.code, + }; + }); + return ( + + + + + + + setAccountCode((e.target as HTMLSelectElement)?.value)} - id="account" /> +

{t('aopp.syncing')}

- - +
- - ); - } - case 'syncing': - return ( - - - - - -

{t('aopp.syncing')}

-
- - - -
- ); - case 'signing': - return ( - - - - - -

{t('aopp.signing')}

- - - - - - -
- {aopp.message} -
-
- -
-
- ); - case 'success': - return ( - - -

{t('aopp.success.title')}

-

- {t('aopp.success.message', { host: domain(aopp.callback) })} -

- - - - - - -
- {aopp.message} -
-
-
- - - - -
- ); + ); + case 'signing': + return ( + + + + + +

{t('aopp.signing')}

+ + + + + + +
{aopp.message}
+
+ +
+
+ ); + case 'success': + return ( + + +

{t('aopp.success.title')}

+

+ {t('aopp.success.message', { host: domain(aopp.callback) })} +

+ + + + + + +
{aopp.message}
+
+
+ + + + +
+ ); } }; - diff --git a/frontends/web/src/components/aopp/vasp.tsx b/frontends/web/src/components/aopp/vasp.tsx index cdb09a5e1b..df0b8b6a23 100644 --- a/frontends/web/src/components/aopp/vasp.tsx +++ b/frontends/web/src/components/aopp/vasp.tsx @@ -22,15 +22,15 @@ import BityLogo from '@/assets/exchanges/logos/bity.png'; import PocketBitcoinLogo from '@/assets/exchanges/logos/pocketbitcoin.svg'; type TVASPProps = { - fallback?: JSX.Element; - hostname: string; - prominent?: boolean; - withLogoText?: string; -} + fallback?: JSX.Element; + hostname: string; + prominent?: boolean; + withLogoText?: string; +}; type TVASPMap = { - [hostname: string]: string -} + [hostname: string]: string; +}; const VASPLogoMap: TVASPMap = { 'demo.aopp.group': AOPPGroupLogo, @@ -54,16 +54,18 @@ export const Vasp = ({ }: TVASPProps) => { const hasLogo = hostname in VASPLogoMap; if (!hasLogo) { - return fallback || (

{hostname}

); + return fallback ||

{hostname}

; } - const logoClasses = prominent ? `${styles.logo} ${styles.prominent}` : styles.logo; + const logoClasses = prominent + ? `${styles.logo} ${styles.prominent}` + : styles.logo; return (
{hostname}

{hostname in VASPHostnameMap ? VASPHostnameMap[hostname] : hostname}

- {withLogoText ? (

{withLogoText}

) : null} + {withLogoText ?

{withLogoText}

: null}
); }; diff --git a/frontends/web/src/components/aopp/verifyaddress.tsx b/frontends/web/src/components/aopp/verifyaddress.tsx index 1151ac4dfe..80213b6baa 100644 --- a/frontends/web/src/components/aopp/verifyaddress.tsx +++ b/frontends/web/src/components/aopp/verifyaddress.tsx @@ -21,10 +21,10 @@ import { Button } from '@/components/forms'; import { WaitDialog } from '@/components/wait-dialog/wait-dialog'; type TProps = { - accountCode: accountAPI.AccountCode; - address: string; - addressID: string; -} + accountCode: accountAPI.AccountCode; + address: string; + addressID: string; +}; export const VerifyAddress = ({ accountCode, address, addressID }: TProps) => { const [verifying, setVerifying] = useState(false); @@ -40,11 +40,9 @@ export const VerifyAddress = ({ accountCode, address, addressID }: TProps) => { - { verifying ? ( - - { address } - - ) : null } + {verifying ? ( + {address} + ) : null}
); }; diff --git a/frontends/web/src/components/appdownloadlink/appdownloadlink.tsx b/frontends/web/src/components/appdownloadlink/appdownloadlink.tsx index db0759155c..39e61bf3e2 100644 --- a/frontends/web/src/components/appdownloadlink/appdownloadlink.tsx +++ b/frontends/web/src/components/appdownloadlink/appdownloadlink.tsx @@ -22,12 +22,12 @@ import { A } from '@/components/anchor/anchor'; export const downloadLinkByLanguage = () => { switch (i18n.resolvedLanguage) { - case 'de': - return URL_CONSTANTS.DOWNLOAD_LINK_DE; - case 'es': - return URL_CONSTANTS.DOWNLOAD_LINK_ES; - default: - return URL_CONSTANTS.DOWNLOAD_LINK_GLOBAL; + case 'de': + return URL_CONSTANTS.DOWNLOAD_LINK_DE; + case 'es': + return URL_CONSTANTS.DOWNLOAD_LINK_ES; + default: + return URL_CONSTANTS.DOWNLOAD_LINK_GLOBAL; } }; @@ -46,9 +46,7 @@ export const AppDownloadButton = ({ ...props }) => { // button as child of an anchor element would be invalid HTML, but our A component does not use element. However Button should probably accept href directly so that isn't needed. return ( - + ); }; diff --git a/frontends/web/src/components/appupgraderequired.tsx b/frontends/web/src/components/appupgraderequired.tsx index d2b6a6fb46..8cd5e69b1f 100644 --- a/frontends/web/src/components/appupgraderequired.tsx +++ b/frontends/web/src/components/appupgraderequired.tsx @@ -30,7 +30,8 @@ export const AppUpgradeRequired = () => { textCenter verticallyCentered width="840px" - withBottomBar> + withBottomBar + > diff --git a/frontends/web/src/components/auth/authrequired.tsx b/frontends/web/src/components/auth/authrequired.tsx index 7991aef5f6..5f521790ed 100644 --- a/frontends/web/src/components/auth/authrequired.tsx +++ b/frontends/web/src/components/auth/authrequired.tsx @@ -17,7 +17,12 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { TAuthEventObject, authenticate, subscribeAuth } from '@/api/backend'; -import { View, ViewButtons, ViewContent, ViewHeader } from '@/components/view/view'; +import { + View, + ViewButtons, + ViewContent, + ViewHeader, +} from '@/components/view/view'; import { Button } from '@/components/forms'; import style from './authrequired.module.css'; @@ -35,38 +40,38 @@ export const AuthRequired = () => { useEffect(() => { const unsubscribe = subscribeAuth((data: TAuthEventObject) => { switch (data.typ) { - case 'auth-forced': - authForced.current = true; - break; - case 'auth-required': - // It is a bit strange to call authenticate inside `setAuthRequired`, - // but doing so we avoid declaring `authRequired` as a useEffect's - // dependency, which would cause it to unsubscribe/subscribe every - // time the state changes. - setAuthRequired((prevAuthRequired) => { - if (!prevAuthRequired) { - newAuthentication(); + case 'auth-forced': + authForced.current = true; + break; + case 'auth-required': + // It is a bit strange to call authenticate inside `setAuthRequired`, + // but doing so we avoid declaring `authRequired` as a useEffect's + // dependency, which would cause it to unsubscribe/subscribe every + // time the state changes. + setAuthRequired((prevAuthRequired) => { + if (!prevAuthRequired) { + newAuthentication(); + } + return true; + }); + break; + case 'auth-err': + setAuthenticating(false); + break; + case 'auth-canceled': + if (authForced.current) { + // forced auth can be dismissed and won't be repeated, as it is + // tied to a specific UI event (e.g. enabling the auth toggle in + // the advanced settings. + setAuthRequired(false); + authForced.current = false; + } else { + setAuthenticating(false); } - return true; - }); - break; - case 'auth-err': - setAuthenticating(false); - break; - case 'auth-canceled': - if (authForced.current) { - // forced auth can be dismissed and won't be repeated, as it is - // tied to a specific UI event (e.g. enabling the auth toggle in - // the advanced settings. + break; + case 'auth-ok': setAuthRequired(false); authForced.current = false; - } else { - setAuthenticating(false); - } - break; - case 'auth-ok': - setAuthRequired(false); - authForced.current = false; } }); @@ -84,12 +89,8 @@ export const AuthRequired = () => { return (
- - { !authenticating && ( + + {!authenticating && ( <> @@ -98,7 +99,8 @@ export const AuthRequired = () => { autoFocus primary hidden={authForced.current} - onClick={newAuthentication}> + onClick={newAuthentication} + > {t('auth.authButton')} diff --git a/frontends/web/src/components/badge/badge.tsx b/frontends/web/src/components/badge/badge.tsx index 8dd8dc6524..85a4533f56 100644 --- a/frontends/web/src/components/badge/badge.tsx +++ b/frontends/web/src/components/badge/badge.tsx @@ -33,11 +33,12 @@ export const Badge = ({ ...props }: TProps) => { const withChildrenStyle = children !== undefined ? style.withChildren : ''; - const iconOnlyStyle = (children === undefined && icon) ? style.iconOnly : ''; + const iconOnlyStyle = children === undefined && icon ? style.iconOnly : ''; return ( + {...props} + > {icon && icon({ className: style.badgeIcon })} {children} diff --git a/frontends/web/src/components/balance/balance-skeleton.tsx b/frontends/web/src/components/balance/balance-skeleton.tsx index 1f8fa48fd9..d08cedafe2 100644 --- a/frontends/web/src/components/balance/balance-skeleton.tsx +++ b/frontends/web/src/components/balance/balance-skeleton.tsx @@ -20,7 +20,7 @@ import style from './balance-skeleton.module.css'; export const BalanceSkeleton = () => { return (
- +
); }; diff --git a/frontends/web/src/components/balance/balance.test.tsx b/frontends/web/src/components/balance/balance.test.tsx index 85f714b58e..d1aa25fd4d 100644 --- a/frontends/web/src/components/balance/balance.test.tsx +++ b/frontends/web/src/components/balance/balance.test.tsx @@ -26,13 +26,13 @@ vi.mock('@/utils/request', () => ({ })); vi.mock('@/hooks/mediaquery', () => ({ - useMediaQuery: vi.fn().mockReturnValue(true) + useMediaQuery: vi.fn().mockReturnValue(true), })); vi.mock('react', () => ({ useMemo: vi.fn().mockImplementation((fn) => fn()), useContext: vi.fn(), - createContext: vi.fn() + createContext: vi.fn(), })); describe('components/balance/balance', () => { @@ -41,7 +41,7 @@ describe('components/balance/balance', () => { btcUnit: 'default', defaultCurrency: 'USD', decimal: '.', - group: ',' + group: ',', }); const MOCK_BALANCE: IBalance = { hasAvailable: true, @@ -71,7 +71,7 @@ describe('components/balance/balance', () => { SEK: '512', SGD: '512', USD: '512', - } + }, }, incoming: { amount: '0.003', @@ -98,12 +98,14 @@ describe('components/balance/balance', () => { SEK: '512', SGD: '512', USD: '512', - } - } + }, + }, }; const { getByTestId } = render(); expect(getByTestId('availableBalance').textContent).toBe('0.005BTC512USD'); - expect(getByTestId('incomingBalance').textContent).toBe('+0.003 BTC / 512 USD'); + expect(getByTestId('incomingBalance').textContent).toBe( + '+0.003 BTC / 512 USD', + ); }); }); @@ -114,7 +116,7 @@ describe('components/balance/balance', () => { defaultCurrency: 'USD', nativeLocale: 'en-US', decimal: '.', - group: ',' + group: ',', }); const MOCK_BALANCE: IBalance = { @@ -171,13 +173,15 @@ describe('components/balance/balance', () => { sat: '512', SEK: '512', SGD: '512', - USD: '1\'511.99', - } - } + USD: "1'511.99", + }, + }, }; const { getByTestId } = render(); expect(getByTestId('availableBalance').textContent).toBe('0.005BTC512USD'); - expect(getByTestId('incomingBalance').textContent).toBe('+0.003 BTC / 1,511.99 USD'); + expect(getByTestId('incomingBalance').textContent).toBe( + '+0.003 BTC / 1,511.99 USD', + ); }); afterEach(() => { vi.restoreAllMocks(); diff --git a/frontends/web/src/components/balance/balance.tsx b/frontends/web/src/components/balance/balance.tsx index b9a5ebd47b..5536e3dc78 100644 --- a/frontends/web/src/components/balance/balance.tsx +++ b/frontends/web/src/components/balance/balance.tsx @@ -25,17 +25,12 @@ import style from './balance.module.css'; type TProps = { balance?: IBalance; noRotateFiat?: boolean; -} +}; -export const Balance = ({ - balance, - noRotateFiat, -}: TProps) => { +export const Balance = ({ balance, noRotateFiat }: TProps) => { const { t } = useTranslation(); if (!balance) { - return ( - - ); + return ; } return ( @@ -57,25 +52,28 @@ export const Balance = ({ /> - { - balance.hasIncoming && ( -

- {t('account.incoming')} - {' '} - - + - {' '}{balance.incoming.unit} / - - {' '} - - + {balance.hasIncoming && ( +

+ {t('account.incoming')}{' '} + + + + {' '} + {balance.incoming.unit} / + + {' '} + -

- ) - } + +

+ )} ); }; diff --git a/frontends/web/src/components/banners/banner.tsx b/frontends/web/src/components/banners/banner.tsx index f2ac22b107..c4b392a556 100644 --- a/frontends/web/src/components/banners/banner.tsx +++ b/frontends/web/src/components/banners/banner.tsx @@ -24,7 +24,7 @@ import style from './banner.module.css'; type TBannerProps = { msgKey: 'bitbox01' | 'bitbox02'; -} +}; export const Banner = ({ msgKey }: TBannerProps) => { const { i18n, t } = useTranslation(); @@ -35,11 +35,7 @@ export const Banner = ({ msgKey }: TBannerProps) => { syncBanner(msgKey, setBanner); }, [msgKey]); - if ( - !banner - || !i18n.options.fallbackLng - || !i18n.resolvedLanguage - ) { + if (!banner || !i18n.options.fallbackLng || !i18n.resolvedLanguage) { return null; } const { message, link } = banner; @@ -47,8 +43,10 @@ export const Banner = ({ msgKey }: TBannerProps) => { return ( - {message[i18n.resolvedLanguage] || message[(i18n.options.fallbackLng as string[])[0]]} + type={banner.type ? banner.type : 'warning'} + > + {message[i18n.resolvedLanguage] || + message[(i18n.options.fallbackLng as string[])[0]]}   {link && ( diff --git a/frontends/web/src/components/banners/mobiledatawarning.tsx b/frontends/web/src/components/banners/mobiledatawarning.tsx index 72b1ef8806..cd34494ff8 100644 --- a/frontends/web/src/components/banners/mobiledatawarning.tsx +++ b/frontends/web/src/components/banners/mobiledatawarning.tsx @@ -22,7 +22,10 @@ import { Status } from '@/components/status/status'; export const MobileDataWarning = () => { const { t } = useTranslation(); - const isUsingMobileData = useSync(getUsingMobileData, subscribeUsingMobileData); + const isUsingMobileData = useSync( + getUsingMobileData, + subscribeUsingMobileData, + ); if (isUsingMobileData === undefined) { return null; } @@ -30,7 +33,8 @@ export const MobileDataWarning = () => { ); diff --git a/frontends/web/src/components/banners/testing.tsx b/frontends/web/src/components/banners/testing.tsx index ac9a892ce7..e7a07fe2ca 100644 --- a/frontends/web/src/components/banners/testing.tsx +++ b/frontends/web/src/components/banners/testing.tsx @@ -27,9 +27,5 @@ export const Testing = () => { return null; } - return ( - - {t('warning.testnet')} - - ); + return {t('warning.testnet')}; }; diff --git a/frontends/web/src/components/banners/update.tsx b/frontends/web/src/components/banners/update.tsx index 68552cd672..edc44563f4 100644 --- a/frontends/web/src/components/banners/update.tsx +++ b/frontends/web/src/components/banners/update.tsx @@ -35,8 +35,7 @@ export const Update = () => { current: file.current, version: file.version, })} - {file.description} - {' '} + {file.description}{' '} {/* Don't show download link on Android because they should update from stores */} {!runningInAndroid() && } diff --git a/frontends/web/src/components/bluetooth/bluetooth.tsx b/frontends/web/src/components/bluetooth/bluetooth.tsx index 106a840407..7005710d25 100644 --- a/frontends/web/src/components/bluetooth/bluetooth.tsx +++ b/frontends/web/src/components/bluetooth/bluetooth.tsx @@ -21,11 +21,17 @@ import { runningInIOS } from '@/utils/env'; import { Status } from '@/components/status/status'; import { ActionableItem } from '@/components/actionable-item/actionable-item'; import { Badge } from '@/components/badge/badge'; -import { HorizontallyCenteredSpinner, SpinnerRingAnimated } from '@/components/spinner/SpinnerAnimation'; +import { + HorizontallyCenteredSpinner, + SpinnerRingAnimated, +} from '@/components/spinner/SpinnerAnimation'; import styles from './bluetooth.module.css'; const isConnectedOrConnecting = (peripheral: TPeripheral) => { - return peripheral.connectionState === 'connecting' || peripheral.connectionState === 'connected'; + return ( + peripheral.connectionState === 'connecting' || + peripheral.connectionState === 'connected' + ); }; const _Bluetooth = () => { @@ -35,52 +41,47 @@ const _Bluetooth = () => { return null; } if (!state.bluetoothAvailable) { - return ( - - {t('bluetooth.enable')} - - ); + return {t('bluetooth.enable')}; } const hasConnection = state.peripherals.some(isConnectedOrConnecting); return ( <> -
- {t('bluetooth.select')} -
+
{t('bluetooth.select')}
- {state.peripherals.map(peripheral => { - const onClick = !hasConnection ? () => connect(peripheral.identifier) : undefined; - const connectingIcon = peripheral.connectionState === 'connecting' ? ( - - ) : undefined; + {state.peripherals.map((peripheral) => { + const onClick = !hasConnection + ? () => connect(peripheral.identifier) + : undefined; + const connectingIcon = + peripheral.connectionState === 'connecting' ? ( + + ) : undefined; return ( + onClick={onClick} + > - { peripheral.name !== '' ? peripheral.name : peripheral.identifier } - {' '} - { peripheral.connectionState === 'connected' ? ( - - {t('bluetooth.connected')} - - ) : null } - { peripheral.connectionState === 'error' ? ( + {peripheral.name !== '' + ? peripheral.name + : peripheral.identifier}{' '} + {peripheral.connectionState === 'connected' ? ( + {t('bluetooth.connected')} + ) : null} + {peripheral.connectionState === 'error' ? ( {peripheral.connectionError} - ) : null } + ) : null} ); })}
- {state.scanning && ( - - )} + {state.scanning && } ); }; diff --git a/frontends/web/src/components/bottom-navigation/bottom-navigation.tsx b/frontends/web/src/components/bottom-navigation/bottom-navigation.tsx index 32fdbe3f44..e894e83ff7 100644 --- a/frontends/web/src/components/bottom-navigation/bottom-navigation.tsx +++ b/frontends/web/src/components/bottom-navigation/bottom-navigation.tsx @@ -16,7 +16,12 @@ import { useTranslation } from 'react-i18next'; import { Link, useLocation } from 'react-router-dom'; -import { AccountIconSVG, ExchangeIconSVG, MoreIconSVG, PortfolioIconSVG } from '@/components/bottom-navigation/menu-icons'; +import { + AccountIconSVG, + ExchangeIconSVG, + MoreIconSVG, + PortfolioIconSVG, +} from '@/components/bottom-navigation/menu-icons'; import styles from './bottom-navigation.module.css'; export const BottomNavigation = () => { diff --git a/frontends/web/src/components/bottom-navigation/menu-icons.tsx b/frontends/web/src/components/bottom-navigation/menu-icons.tsx index 7f4ed32fbf..8a5dc53dce 100644 --- a/frontends/web/src/components/bottom-navigation/menu-icons.tsx +++ b/frontends/web/src/components/bottom-navigation/menu-icons.tsx @@ -15,27 +15,61 @@ */ export const PortfolioIconSVG = () => ( - - + + ); - export const AccountIconSVG = () => ( - - + + ); export const ExchangeIconSVG = () => ( - - + + ); export const MoreIconSVG = () => ( - - + + - -); \ No newline at end of file +); diff --git a/frontends/web/src/components/confirm/Confirm.tsx b/frontends/web/src/components/confirm/Confirm.tsx index 30d606d310..00d1a5c061 100644 --- a/frontends/web/src/components/confirm/Confirm.tsx +++ b/frontends/web/src/components/confirm/Confirm.tsx @@ -26,12 +26,16 @@ type TCallback = (response: boolean) => void; * @deprecated * shows an alert when called, triggers the callback with user reply */ -export let confirmation: (message: string, callback: TCallback, customButtonText?: string) => void; +export let confirmation: ( + message: string, + callback: TCallback, + customButtonText?: string, +) => void; interface State { - active: boolean; - message?: string; - customButtonText?: string; + active: boolean; + message?: string; + customButtonText?: string; } /** @@ -43,7 +47,11 @@ export const Confirm = () => { const { t } = useTranslation(); const callback = useRef(() => {}); - confirmation = (message: string, cb: TCallback, customButtonText?: string) => { + confirmation = ( + message: string, + cb: TCallback, + customButtonText?: string, + ) => { callback.current = cb; setState({ active: true, @@ -64,17 +72,20 @@ export const Confirm = () => { return null; } return ( - respond(false)}> + respond(false)} + >
- { message ? message.split('\n').map((line, i) => ( -

- -

- )) : null } + {message + ? message.split('\n').map((line, i) => ( +

+ +

+ )) + : null}
diff --git a/frontends/web/src/components/contentwrapper/contentwrapper.tsx b/frontends/web/src/components/contentwrapper/contentwrapper.tsx index b1694f77ac..0f160eabdd 100644 --- a/frontends/web/src/components/contentwrapper/contentwrapper.tsx +++ b/frontends/web/src/components/contentwrapper/contentwrapper.tsx @@ -18,12 +18,12 @@ import { ReactNode } from 'react'; import style from './contentwrapper.module.css'; type TProps = { - className?: string - children: ReactNode - } + className?: string; + children: ReactNode; +}; -export const ContentWrapper = (({ className = '', children }: TProps) => { +export const ContentWrapper = ({ className = '', children }: TProps) => { return (
{children}
); -}); +}; diff --git a/frontends/web/src/components/copy/Copy.tsx b/frontends/web/src/components/copy/Copy.tsx index 949c87b53f..04dabc7f43 100644 --- a/frontends/web/src/components/copy/Copy.tsx +++ b/frontends/web/src/components/copy/Copy.tsx @@ -21,16 +21,24 @@ import { Check, Copy } from '@/components/icon/icon'; import style from './Copy.module.css'; type TProps = { - alignLeft?: boolean; - alignRight?: boolean; - borderLess?: boolean; - className?: string; - disabled?: boolean; - flexibleHeight?: boolean; - value: string; -} + alignLeft?: boolean; + alignRight?: boolean; + borderLess?: boolean; + className?: string; + disabled?: boolean; + flexibleHeight?: boolean; + value: string; +}; -export const CopyableInput = ({ alignLeft, alignRight, borderLess, value, className, disabled, flexibleHeight }: TProps) => { +export const CopyableInput = ({ + alignLeft, + alignRight, + borderLess, + value, + className, + disabled, + flexibleHeight, +}: TProps) => { const [success, setSuccess] = useState(false); const { t } = useTranslation(); @@ -51,13 +59,20 @@ export const CopyableInput = ({ alignLeft, alignRight, borderLess, value, classN if (!textarea) { return; } - const fontSize = window.getComputedStyle(textarea, null).getPropertyValue('font-size'); + const fontSize = window + .getComputedStyle(textarea, null) + .getPropertyValue('font-size'); const units = Number(fontSize.replace('px', '')) + 2; textarea.setAttribute('rows', '1'); - textarea.setAttribute('rows', String(Math.round((textarea.scrollHeight / units) - 2))); + textarea.setAttribute( + 'rows', + String(Math.round(textarea.scrollHeight / units - 2)), + ); }; - const onFocus = (e: React.SyntheticEvent) => { + const onFocus = ( + e: React.SyntheticEvent, + ) => { e.currentTarget.focus(); }; @@ -69,11 +84,13 @@ export const CopyableInput = ({ alignLeft, alignRight, borderLess, value, classN }; return ( -
+