diff --git a/craco.config.js b/craco.config.js index 935b942..3acb036 100644 --- a/craco.config.js +++ b/craco.config.js @@ -1,6 +1,7 @@ // Copyright (c) 2020-2021 Drew Lemmy // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt +const CracoAlias = require("craco-alias"); const CracoLessPlugin = require("craco-less"); const AntdDayjsWebpackPlugin = require("antd-dayjs-webpack-plugin"); const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); @@ -21,6 +22,14 @@ plugins: [ { + plugin: CracoAlias, + options: { + source: "tsconfig", + baseUrl: "./src", + tsConfigPath: "./tsconfig.extend.json" + } + }, + { plugin: CracoLessPlugin, options: { cssLoaderOptions: { diff --git a/package.json b/package.json index 820835a..cfacd76 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,7 @@ "@typescript-eslint/parser": "4.15.3-alpha.17", "antd-dayjs-webpack-plugin": "^1.0.6", "babel-plugin-lodash": "^3.3.4", + "craco-alias": "^2.2.0", "craco-less": "^1.17.1", "eslint": "^7.21.0", "eslint-plugin-react": "^7.22.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e46f87..ef5faf0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,7 @@ '@typescript-eslint/parser': 4.15.3-alpha.17_eslint@7.21.0+typescript@4.1.5 antd-dayjs-webpack-plugin: 1.0.6_dayjs@1.10.4 babel-plugin-lodash: 3.3.4 + craco-alias: 2.2.0 craco-less: 1.17.1_077094f002a207b28261f7f7ea13418e eslint: 7.21.0 eslint-plugin-react: 7.22.0_eslint@7.21.0 @@ -4303,6 +4304,10 @@ node: '>=10' resolution: integrity: sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + /craco-alias/2.2.0: + dev: true + resolution: + integrity: sha512-FLSRaCWI/CKLUO+Cb/GH9ljSYWdrlzkYf3N373Kuof0hckJ1tj+wPN0XyToR8KPUggoIB5+IDkGs1uKUBuGUiA== /craco-less/1.17.1_077094f002a207b28261f7f7ea13418e: dependencies: '@craco/craco': 6.1.1_react-scripts@4.0.3 @@ -13813,6 +13818,7 @@ base64-arraybuffer: ^0.2.0 chart.js: ^2.9.4 classnames: ^2.2.6 + craco-alias: ^2.2.0 craco-less: ^1.17.1 csv-stringify: ^5.6.2 dayjs: ^1.10.4 diff --git a/public/locales/en.json b/public/locales/en.json index 4fecf0d..ef2c0c9 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -580,11 +580,16 @@ "import": { "description": "Paste the backup code (or import from a file below) and enter the corresponding master password.", + "masterPasswordPlaceholder": "Master password", "masterPasswordRequired": "Master password is required.", "masterPasswordIncorrect": "Master password is incorrect.", + "fromFileButton": "Import from file", "textareaPlaceholder": "Paste backup code here", + "textareaRequired": "Backup code is required.", + + "modalTitle": "Import backup", "modalButton": "Import", "detectedFormat": "<0>Detected format: <2 />", diff --git a/src/__tests__/App.tsx b/src/__tests__/App.tsx index cb0cede..fbc005b 100644 --- a/src/__tests__/App.tsx +++ b/src/__tests__/App.tsx @@ -3,7 +3,7 @@ // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import React from "react"; import { render, screen } from "@testing-library/react"; -import App from "../App"; +import App from "@app"; test("renders the app", async () => { render(); diff --git a/src/components/DateTime.tsx b/src/components/DateTime.tsx index 291604b..d5db61d 100644 --- a/src/components/DateTime.tsx +++ b/src/components/DateTime.tsx @@ -5,7 +5,7 @@ import classNames from "classnames"; import { Tooltip } from "antd"; -import { useBooleanSetting } from "../utils/settings"; +import { useBooleanSetting } from "@utils/settings"; import dayjs from "dayjs"; import TimeAgo from "react-timeago"; diff --git a/src/components/addresses/ContextualAddress.tsx b/src/components/addresses/ContextualAddress.tsx index 4419d54..4f64676 100644 --- a/src/components/addresses/ContextualAddress.tsx +++ b/src/components/addresses/ContextualAddress.tsx @@ -6,15 +6,15 @@ import { Tooltip, Typography } from "antd"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; -import { KristAddress } from "../../krist/api/types"; -import { Wallet, useWallets } from "../../krist/wallets/Wallet"; -import { parseCommonMeta, CommonMeta } from "../../utils/commonmeta"; -import { stripNameSuffix } from "../../utils/currency"; -import { useBooleanSetting } from "../../utils/settings"; +import { KristAddress } from "@api/types"; +import { Wallet, useWallets } from "@wallets/Wallet"; +import { parseCommonMeta, CommonMeta } from "@utils/commonmeta"; +import { stripNameSuffix } from "@utils/currency"; +import { useBooleanSetting } from "@utils/settings"; import { KristNameLink } from "../names/KristNameLink"; diff --git a/src/components/auth/AuthMasterPasswordPopover.tsx b/src/components/auth/AuthMasterPasswordPopover.tsx index 5f02f6a..fd64868 100644 --- a/src/components/auth/AuthMasterPasswordPopover.tsx +++ b/src/components/auth/AuthMasterPasswordPopover.tsx @@ -6,13 +6,14 @@ import { TooltipPlacement } from "antd/lib/tooltip"; import { useSelector, shallowEqual } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { useTranslation } from "react-i18next"; +import { translateError } from "@utils/i18n"; import { FakeUsernameInput } from "./FakeUsernameInput"; import { getMasterPasswordInput } from "./MasterPasswordInput"; -import { authMasterPassword } from "../../krist/wallets/WalletManager"; +import { authMasterPassword } from "@wallets/WalletManager"; interface FormValues { masterPassword: string; @@ -36,12 +37,8 @@ try { await authMasterPassword(salt, tester, values.masterPassword); onSubmit(); - } catch (e) { - const message = e.message // Translate the error if we can - ? e.message.startsWith("masterPassword.") ? t(e.message) : e.message - : t("masterPassword.errorUnknown"); - - setPasswordError(message); + } catch (err) { + setPasswordError(translateError(t, err, "masterPassword.errorUnknown")); } } diff --git a/src/components/auth/AuthorisedAction.tsx b/src/components/auth/AuthorisedAction.tsx index bcbcb16..f73bfbf 100644 --- a/src/components/auth/AuthorisedAction.tsx +++ b/src/components/auth/AuthorisedAction.tsx @@ -5,7 +5,7 @@ import { TooltipPlacement } from "antd/lib/tooltip"; import { useSelector, shallowEqual } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { AuthMasterPasswordPopover } from "./AuthMasterPasswordPopover"; import { SetMasterPasswordModal } from "./SetMasterPasswordModal"; diff --git a/src/components/auth/ForcedAuth.tsx b/src/components/auth/ForcedAuth.tsx index 936e0bf..0a21e5d 100644 --- a/src/components/auth/ForcedAuth.tsx +++ b/src/components/auth/ForcedAuth.tsx @@ -5,11 +5,11 @@ import { useTranslation, TFunction } from "react-i18next"; import { useSelector, shallowEqual } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; -import { authMasterPassword } from "../../krist/wallets/WalletManager"; +import { authMasterPassword } from "@wallets/WalletManager"; -import { useMountEffect } from "../../utils"; +import { useMountEffect } from "@utils"; async function forceAuth(t: TFunction, salt: string, tester: string): Promise { try { diff --git a/src/components/auth/SetMasterPasswordModal.tsx b/src/components/auth/SetMasterPasswordModal.tsx index 58052c2..8b883d7 100644 --- a/src/components/auth/SetMasterPasswordModal.tsx +++ b/src/components/auth/SetMasterPasswordModal.tsx @@ -8,7 +8,7 @@ import { FakeUsernameInput } from "./FakeUsernameInput"; import { getMasterPasswordInput } from "./MasterPasswordInput"; -import { setMasterPassword } from "../../krist/wallets/WalletManager"; +import { setMasterPassword } from "@wallets/WalletManager"; interface Props { visible: boolean; diff --git a/src/components/krist/KristValue.tsx b/src/components/krist/KristValue.tsx index 1ccdc16..b633cda 100644 --- a/src/components/krist/KristValue.tsx +++ b/src/components/krist/KristValue.tsx @@ -5,7 +5,7 @@ import classNames from "classnames"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { KristSymbol } from "./KristSymbol"; diff --git a/src/components/names/KristNameLink.tsx b/src/components/names/KristNameLink.tsx index 6a8e080..6e8440e 100644 --- a/src/components/names/KristNameLink.tsx +++ b/src/components/names/KristNameLink.tsx @@ -6,11 +6,11 @@ import { Typography } from "antd"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { Link } from "react-router-dom"; -import { useBooleanSetting } from "../../utils/settings"; +import { useBooleanSetting } from "@utils/settings"; const { Text } = Typography; diff --git a/src/components/names/NameARecordLink.tsx b/src/components/names/NameARecordLink.tsx index 1d56d03..7abe915 100644 --- a/src/components/names/NameARecordLink.tsx +++ b/src/components/names/NameARecordLink.tsx @@ -5,8 +5,8 @@ import classNames from "classnames"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; -import { stripNameSuffix } from "../../utils/currency"; +import { RootState } from "@store"; +import { stripNameSuffix } from "@utils/currency"; import { KristNameLink } from "./KristNameLink"; diff --git a/src/components/results/APIErrorResult.tsx b/src/components/results/APIErrorResult.tsx index 2545ab2..56bedf6 100644 --- a/src/components/results/APIErrorResult.tsx +++ b/src/components/results/APIErrorResult.tsx @@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next"; import { SmallResult, ResultProps } from "./SmallResult"; -import { APIError } from "../../krist/api"; +import { APIError } from "@api"; interface Props { error: Error; diff --git a/src/components/transactions/TransactionConciseMetadata.tsx b/src/components/transactions/TransactionConciseMetadata.tsx index 734b4a0..b1bc354 100644 --- a/src/components/transactions/TransactionConciseMetadata.tsx +++ b/src/components/transactions/TransactionConciseMetadata.tsx @@ -5,10 +5,10 @@ import classNames from "classnames"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; -import { KristTransaction } from "../../krist/api/types"; -import { stripNameFromMetadata } from "../../utils/currency"; +import { KristTransaction } from "@api/types"; +import { stripNameFromMetadata } from "@utils/currency"; import "./TransactionConciseMetadata.less"; diff --git a/src/components/transactions/TransactionItem.tsx b/src/components/transactions/TransactionItem.tsx index f61c01f..8c5563f 100644 --- a/src/components/transactions/TransactionItem.tsx +++ b/src/components/transactions/TransactionItem.tsx @@ -8,8 +8,8 @@ import { useTranslation, Trans } from "react-i18next"; import { Link } from "react-router-dom"; -import { KristTransaction } from "../../krist/api/types"; -import { Wallet } from "../../krist/wallets/Wallet"; +import { KristTransaction } from "@api/types"; +import { Wallet } from "@wallets/Wallet"; import { DateTime } from "../DateTime"; import { KristValue } from "../krist/KristValue"; import { KristNameLink } from "../names/KristNameLink"; diff --git a/src/components/transactions/TransactionSummary.tsx b/src/components/transactions/TransactionSummary.tsx index ccb9310..846639f 100644 --- a/src/components/transactions/TransactionSummary.tsx +++ b/src/components/transactions/TransactionSummary.tsx @@ -7,9 +7,9 @@ import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; -import { useWallets } from "../../krist/wallets/Wallet"; +import { useWallets } from "@wallets/Wallet"; -import { KristTransaction } from "../../krist/api/types"; +import { KristTransaction } from "@api/types"; import { TransactionItem } from "./TransactionItem"; import "./TransactionSummary.less"; diff --git a/src/components/transactions/TransactionType.tsx b/src/components/transactions/TransactionType.tsx index 5a5280f..6e97299 100644 --- a/src/components/transactions/TransactionType.tsx +++ b/src/components/transactions/TransactionType.tsx @@ -7,8 +7,8 @@ import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; -import { KristTransaction, KristTransactionType } from "../../krist/api/types"; -import { Wallet, useWallets } from "../../krist/wallets/Wallet"; +import { KristTransaction, KristTransactionType } from "@api/types"; +import { Wallet, useWallets } from "@wallets/Wallet"; import "./TransactionType.less"; diff --git a/src/components/wallets/SelectWalletCategory.tsx b/src/components/wallets/SelectWalletCategory.tsx index 46561c9..fa06c1a 100644 --- a/src/components/wallets/SelectWalletCategory.tsx +++ b/src/components/wallets/SelectWalletCategory.tsx @@ -7,8 +7,8 @@ import { useTranslation } from "react-i18next"; -import { localeSort } from "../../utils"; -import { useWallets } from "../../krist/wallets/Wallet"; +import { localeSort } from "@utils"; +import { useWallets } from "@wallets/Wallet"; const { Text } = Typography; diff --git a/src/components/wallets/SelectWalletFormat.tsx b/src/components/wallets/SelectWalletFormat.tsx index 269fcc7..4346849 100644 --- a/src/components/wallets/SelectWalletFormat.tsx +++ b/src/components/wallets/SelectWalletFormat.tsx @@ -6,8 +6,8 @@ import { useTranslation } from "react-i18next"; -import { WalletFormatName, ADVANCED_FORMATS } from "../../krist/wallets/formats/WalletFormat"; -import { useBooleanSetting } from "../../utils/settings"; +import { WalletFormatName, ADVANCED_FORMATS } from "@wallets/formats/WalletFormat"; +import { useBooleanSetting } from "@utils/settings"; interface Props { initialFormat: WalletFormatName; diff --git a/src/components/wallets/SyncWallets.tsx b/src/components/wallets/SyncWallets.tsx index 07f8e69..a91a12a 100644 --- a/src/components/wallets/SyncWallets.tsx +++ b/src/components/wallets/SyncWallets.tsx @@ -1,8 +1,8 @@ // Copyright (c) 2020-2021 Drew Lemmy // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt -import { syncWallets } from "../../krist/wallets/Wallet"; -import { useMountEffect } from "../../utils"; +import { syncWallets } from "@wallets/Wallet"; +import { useMountEffect } from "@utils"; /** Sync the wallets with the Krist node on startup. */ export function SyncWallets(): JSX.Element | null { diff --git a/src/global/AppRouter.tsx b/src/global/AppRouter.tsx index 0f3af54..6a1c091 100644 --- a/src/global/AppRouter.tsx +++ b/src/global/AppRouter.tsx @@ -4,25 +4,25 @@ import React from "react"; import { Switch, Route } from "react-router-dom"; -import { DashboardPage } from "../pages/dashboard/DashboardPage"; -import { WalletsPage } from "../pages/wallets/WalletsPage"; +import { DashboardPage } from "@pages/dashboard/DashboardPage"; +import { WalletsPage } from "@pages/wallets/WalletsPage"; -import { AddressPage } from "../pages/addresses/AddressPage"; -import { BlocksPage } from "../pages/blocks/BlocksPage"; -import { BlockPage } from "../pages/blocks/BlockPage"; -import { TransactionsPage, ListingType as TXListing } from "../pages/transactions/TransactionsPage"; -import { TransactionPage } from "../pages/transactions/TransactionPage"; -import { NamesPage, ListingType as NamesListing } from "../pages/names/NamesPage"; -import { NamePage } from "../pages/names/NamePage"; +import { AddressPage } from "@pages/addresses/AddressPage"; +import { BlocksPage } from "@pages/blocks/BlocksPage"; +import { BlockPage } from "@pages/blocks/BlockPage"; +import { TransactionsPage, ListingType as TXListing } from "@pages/transactions/TransactionsPage"; +import { TransactionPage } from "@pages/transactions/TransactionPage"; +import { NamesPage, ListingType as NamesListing } from "@pages/names/NamesPage"; +import { NamePage } from "@pages/names/NamePage"; -import { SettingsPage } from "../pages/settings/SettingsPage"; -import { SettingsTranslations } from "../pages/settings/SettingsTranslations"; +import { SettingsPage } from "@pages/settings/SettingsPage"; +import { SettingsTranslations } from "@pages/settings/SettingsTranslations"; -import { CreditsPage } from "../pages/credits/CreditsPage"; +import { CreditsPage } from "@pages/credits/CreditsPage"; -import { DevPage } from "../pages/dev/DevPage"; +import { DevPage } from "@pages/dev/DevPage"; -import { NotFoundPage } from "../pages/NotFoundPage"; +import { NotFoundPage } from "@pages/NotFoundPage"; interface AppRoute { path: string; diff --git a/src/global/AppServices.tsx b/src/global/AppServices.tsx index b59c838..562e069 100644 --- a/src/global/AppServices.tsx +++ b/src/global/AppServices.tsx @@ -3,8 +3,8 @@ // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import React from "react"; -import { SyncWallets } from "../components/wallets/SyncWallets"; -import { ForcedAuth } from "../components/auth/ForcedAuth"; +import { SyncWallets } from "@comp/wallets/SyncWallets"; +import { ForcedAuth } from "@comp/auth/ForcedAuth"; import { WebsocketService } from "./ws/WebsocketService"; import { SyncWork } from "./ws/SyncWork"; import { SyncMOTD } from "./ws/SyncMOTD"; diff --git a/src/global/ws/SyncMOTD.tsx b/src/global/ws/SyncMOTD.tsx index e442dcc..5ce3826 100644 --- a/src/global/ws/SyncMOTD.tsx +++ b/src/global/ws/SyncMOTD.tsx @@ -4,15 +4,15 @@ import { useEffect } from "react"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; -import * as nodeActions from "../../store/actions/NodeActions"; +import { RootState } from "@store"; +import * as nodeActions from "@actions/NodeActions"; -import { store } from "../../App"; +import { store } from "@app"; -import * as api from "../../krist/api"; -import { KristMOTD, KristMOTDBase } from "../../krist/api/types"; +import * as api from "@api"; +import { KristMOTD, KristMOTDBase } from "@api/types"; -import { recalculateWallets, useWallets } from "../../krist/wallets/Wallet"; +import { recalculateWallets, useWallets } from "@wallets/Wallet"; import Debug from "debug"; const debug = Debug("kristweb:sync-motd"); diff --git a/src/global/ws/SyncWork.tsx b/src/global/ws/SyncWork.tsx index c488570..d56649f 100644 --- a/src/global/ws/SyncWork.tsx +++ b/src/global/ws/SyncWork.tsx @@ -4,13 +4,13 @@ import { useEffect } from "react"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; -import * as nodeActions from "../../store/actions/NodeActions"; +import { RootState } from "@store"; +import * as nodeActions from "@actions/NodeActions"; -import { store } from "../../App"; +import { store } from "@app"; -import * as api from "../../krist/api"; -import { KristWorkDetailed } from "../../krist/api/types"; +import * as api from "@api"; +import { KristWorkDetailed } from "@api/types"; import Debug from "debug"; const debug = Debug("kristweb:sync-work"); diff --git a/src/global/ws/WebsocketService.tsx b/src/global/ws/WebsocketService.tsx index 7915477..e25cc2c 100644 --- a/src/global/ws/WebsocketService.tsx +++ b/src/global/ws/WebsocketService.tsx @@ -3,14 +3,14 @@ // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import { useState, useEffect } from "react"; -import { store } from "../../App"; -import { WalletMap } from "../../store/reducers/WalletsReducer"; -import * as wsActions from "../../store/actions/WebsocketActions"; -import * as nodeActions from "../../store/actions/NodeActions"; +import { store } from "@app"; +import { WalletMap } from "@reducers/WalletsReducer"; +import * as wsActions from "@actions/WebsocketActions"; +import * as nodeActions from "@actions/NodeActions"; -import * as api from "../../krist/api"; -import { KristAddress, KristBlock, KristTransaction, WSConnectionState, WSIncomingMessage, WSSubscriptionLevel } from "../../krist/api/types"; -import { useWallets, findWalletByAddress, syncWallet, syncWalletUpdate, Wallet } from "../../krist/wallets/Wallet"; +import * as api from "@api"; +import { KristAddress, KristBlock, KristTransaction, WSConnectionState, WSIncomingMessage, WSSubscriptionLevel } from "@api/types"; +import { useWallets, findWalletByAddress, syncWallet, syncWalletUpdate, Wallet } from "@wallets/Wallet"; import WebSocketAsPromised from "websocket-as-promised"; import { throttle } from "lodash-es"; diff --git a/src/krist/AddressAlgo.ts b/src/krist/AddressAlgo.ts index db74732..19b539a 100644 --- a/src/krist/AddressAlgo.ts +++ b/src/krist/AddressAlgo.ts @@ -1,7 +1,7 @@ // Copyright (c) 2020-2021 Drew Lemmy // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt -import { sha256, doubleSHA256 } from "../utils/crypto"; +import { sha256, doubleSHA256 } from "@utils/crypto"; const hexToBase36 = (input: number): string => { const byte = 48 + Math.floor(input / 7); diff --git a/src/krist/api/index.ts b/src/krist/api/index.ts index a5c3fd9..44d0643 100644 --- a/src/krist/api/index.ts +++ b/src/krist/api/index.ts @@ -2,11 +2,11 @@ // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import { notification } from "antd"; -import i18n from "../../utils/i18n"; +import i18n from "@utils/i18n"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; -import { store } from "../../App"; +import { RootState } from "@store"; +import { store } from "@app"; import { APIResponse } from "./types"; import { throttle } from "lodash-es"; diff --git a/src/krist/api/lookup.ts b/src/krist/api/lookup.ts index 6089c9c..72c3f2a 100644 --- a/src/krist/api/lookup.ts +++ b/src/krist/api/lookup.ts @@ -4,7 +4,7 @@ import { KristAddress, KristTransaction, KristName, KristBlock } from "./types"; import * as api from "."; -import { LookupFilterOptionsBase, LookupResponseBase, getFilterOptionsQuery } from "../../utils/table"; +import { LookupFilterOptionsBase, LookupResponseBase, getFilterOptionsQuery } from "@utils/table"; // ============================================================================= // Addresses diff --git a/src/krist/wallets/Wallet.ts b/src/krist/wallets/Wallet.ts index e285fff..d05fbc9 100644 --- a/src/krist/wallets/Wallet.ts +++ b/src/krist/wallets/Wallet.ts @@ -6,16 +6,16 @@ import { applyWalletFormat, WalletFormatName } from "./formats/WalletFormat"; import { makeV2Address } from "../AddressAlgo"; -import { aesGcmDecrypt, aesGcmEncrypt } from "../../utils/crypto"; +import { aesGcmDecrypt, aesGcmEncrypt } from "@utils/crypto"; import { KristAddressWithNames, lookupAddresses } from "../api/lookup"; -import { store } from "../../App"; -import * as actions from "../../store/actions/WalletsActions"; -import { WalletMap } from "../../store/reducers/WalletsReducer"; +import { store } from "@app"; +import * as actions from "@actions/WalletsActions"; +import { WalletMap } from "@reducers/WalletsReducer"; import { useSelector, shallowEqual } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { Mutex } from "async-mutex"; diff --git a/src/krist/wallets/WalletManager.ts b/src/krist/wallets/WalletManager.ts index 64d0b2d..ef54e66 100644 --- a/src/krist/wallets/WalletManager.ts +++ b/src/krist/wallets/WalletManager.ts @@ -1,11 +1,13 @@ // Copyright (c) 2020-2021 Drew Lemmy // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt -import { toHex } from "../../utils"; -import { aesGcmEncrypt, aesGcmDecrypt } from "../../utils/crypto"; +import { toHex } from "@utils"; +import { aesGcmEncrypt, aesGcmDecrypt } from "@utils/crypto"; -import { store } from "../../App"; -import * as actions from "../../store/actions/WalletManagerActions"; +import { store } from "@app"; +import * as actions from "@actions/WalletManagerActions"; + +import { TranslatedError } from "@utils/i18n"; /** Verifies that the given master password is correct, and dispatches the * auth action to the Redux store. */ @@ -14,8 +16,10 @@ tester: string | undefined, password: string ): Promise { - if (!password) throw new Error("masterPassword.errorPasswordRequired"); - if (!salt || !tester) throw new Error("masterPassword.errorPasswordUnset"); + if (!password) + throw new TranslatedError("masterPassword.errorPasswordRequired"); + if (!salt || !tester) + throw new TranslatedError("masterPassword.errorPasswordUnset"); try { // Attempt to decrypt the tester with the given password @@ -23,10 +27,12 @@ // Verify that the decrypted tester is equal to the salt, if not, the // provided master password is incorrect. - if (testerDec !== salt) throw new Error("masterPassword.errorPasswordIncorrect"); + if (testerDec !== salt) + throw new TranslatedError("masterPassword.errorPasswordIncorrect"); } catch (e) { // OperationError usually means decryption failure - if (e.name === "OperationError") throw new Error("masterPassword.errorPasswordIncorrect"); + if (e.name === "OperationError") + throw new TranslatedError("masterPassword.errorPasswordIncorrect"); else throw e; } @@ -37,7 +43,8 @@ /** Generates a salt and tester, sets the master password, and dispatches the * action to the Redux store. */ export async function setMasterPassword(password: string): Promise { - if (!password) throw new Error("masterPassword.errorPasswordRequired"); + if (!password) + throw new TranslatedError("masterPassword.errorPasswordRequired"); // Generate the salt (to be encrypted with the master password) const salt = window.crypto.getRandomValues(new Uint8Array(32)); diff --git a/src/krist/wallets/formats/WalletFormat.ts b/src/krist/wallets/formats/WalletFormat.ts index 397c123..b754268 100644 --- a/src/krist/wallets/formats/WalletFormat.ts +++ b/src/krist/wallets/formats/WalletFormat.ts @@ -1,7 +1,7 @@ // Copyright (c) 2020-2021 Drew Lemmy // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt -import { sha256 } from "../../../utils/crypto"; +import { sha256 } from "@utils/crypto"; export interface WalletFormat { (password: string, username?: string): Promise; diff --git a/src/layout/nav/ConnectionIndicator.tsx b/src/layout/nav/ConnectionIndicator.tsx index c64efee..ca96676 100644 --- a/src/layout/nav/ConnectionIndicator.tsx +++ b/src/layout/nav/ConnectionIndicator.tsx @@ -5,10 +5,10 @@ import { Tooltip } from "antd"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { useTranslation } from "react-i18next"; -import { WSConnectionState } from "../../krist/api/types"; +import { WSConnectionState } from "@api/types"; import "./ConnectionIndicator.less"; diff --git a/src/layout/nav/CymbalIndicator.tsx b/src/layout/nav/CymbalIndicator.tsx index 68b6761..f927889 100644 --- a/src/layout/nav/CymbalIndicator.tsx +++ b/src/layout/nav/CymbalIndicator.tsx @@ -5,8 +5,8 @@ import Icon from "@ant-design/icons"; import { useSelector, shallowEqual } from "react-redux"; -import { RootState } from "../../store"; -import { SettingsState } from "../../utils/settings"; +import { RootState } from "@store"; +import { SettingsState } from "@utils/settings"; export const CymbalIconSvg = (): JSX.Element => ( diff --git a/src/layout/nav/Search.tsx b/src/layout/nav/Search.tsx index 0d84fd7..ab1163c 100644 --- a/src/layout/nav/Search.tsx +++ b/src/layout/nav/Search.tsx @@ -9,10 +9,10 @@ import { useHistory } from "react-router-dom"; import { GlobalHotKeys } from "react-hotkeys"; -import { ctrl } from "../../utils"; +import { ctrl } from "@utils"; -import { RateLimitError } from "../../krist/api"; -import { SearchResult, search, searchExtended, SearchExtendedResult } from "../../krist/api/search"; +import { RateLimitError } from "@api"; +import { SearchResult, search, searchExtended, SearchExtendedResult } from "@api/search"; import { throttle, debounce } from "lodash-es"; import LRU from "lru-cache"; diff --git a/src/layout/nav/SearchResults.tsx b/src/layout/nav/SearchResults.tsx index 92c623f..4b8d3bd 100644 --- a/src/layout/nav/SearchResults.tsx +++ b/src/layout/nav/SearchResults.tsx @@ -7,10 +7,10 @@ import { Trans, useTranslation } from "react-i18next"; -import { KristAddress, KristName, KristBlock, KristTransaction } from "../../krist/api/types"; -import { KristValue } from "../../components/krist/KristValue"; -import { KristNameLink } from "../../components/names/KristNameLink"; -import { DateTime } from "../../components/DateTime"; +import { KristAddress, KristName, KristBlock, KristTransaction } from "@api/types"; +import { KristValue } from "@comp/krist/KristValue"; +import { KristNameLink } from "@comp/names/KristNameLink"; +import { DateTime } from "@comp/DateTime"; import "./SearchResults.less"; diff --git a/src/layout/sidebar/ServiceWorkerCheck.tsx b/src/layout/sidebar/ServiceWorkerCheck.tsx index f727e5a..97c1725 100644 --- a/src/layout/sidebar/ServiceWorkerCheck.tsx +++ b/src/layout/sidebar/ServiceWorkerCheck.tsx @@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next"; -import * as serviceWorker from "../../utils/serviceWorkerRegistration"; +import * as serviceWorker from "@utils/serviceWorkerRegistration"; import Debug from "debug"; const debug = Debug("kristweb:service-worker-check"); diff --git a/src/layout/sidebar/SidebarFooter.tsx b/src/layout/sidebar/SidebarFooter.tsx index fa00d23..aaad619 100644 --- a/src/layout/sidebar/SidebarFooter.tsx +++ b/src/layout/sidebar/SidebarFooter.tsx @@ -6,8 +6,8 @@ import { Link } from "react-router-dom"; -import { useMountEffect } from "../../utils"; -import { getAuthorInfo } from "../../utils/credits"; +import { useMountEffect } from "@utils"; +import { getAuthorInfo } from "@utils/credits"; export function SidebarFooter(): JSX.Element { const { t } = useTranslation(); diff --git a/src/layout/sidebar/SidebarTotalBalance.tsx b/src/layout/sidebar/SidebarTotalBalance.tsx index 9965b90..975e275 100644 --- a/src/layout/sidebar/SidebarTotalBalance.tsx +++ b/src/layout/sidebar/SidebarTotalBalance.tsx @@ -4,8 +4,8 @@ import React from "react"; import { useTranslation } from "react-i18next"; -import { useWallets } from "../../krist/wallets/Wallet"; -import { KristValue } from "../../components/krist/KristValue"; +import { useWallets } from "@wallets/Wallet"; +import { KristValue } from "@comp/krist/KristValue"; export function SidebarTotalBalance(): JSX.Element { const { t } = useTranslation(); diff --git a/src/pages/NotFoundPage.tsx b/src/pages/NotFoundPage.tsx index 16fcc1b..3ae5a4a 100644 --- a/src/pages/NotFoundPage.tsx +++ b/src/pages/NotFoundPage.tsx @@ -8,7 +8,7 @@ import { useHistory } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { SmallResult } from "../components/results/SmallResult"; +import { SmallResult } from "@comp/results/SmallResult"; export function NotFoundPage(): JSX.Element { const { t } = useTranslation(); diff --git a/src/pages/addresses/AddressButtonRow.tsx b/src/pages/addresses/AddressButtonRow.tsx index 5fbf5cc..df45569 100644 --- a/src/pages/addresses/AddressButtonRow.tsx +++ b/src/pages/addresses/AddressButtonRow.tsx @@ -7,8 +7,8 @@ import { useTranslation } from "react-i18next"; -import { KristAddressWithNames } from "../../krist/api/lookup"; -import { Wallet } from "../../krist/wallets/Wallet"; +import { KristAddressWithNames } from "@api/lookup"; +import { Wallet } from "@wallets/Wallet"; import { WalletEditButton } from "../wallets/WalletEditButton"; interface Props { diff --git a/src/pages/addresses/AddressNamesCard.tsx b/src/pages/addresses/AddressNamesCard.tsx index 86d9791..00c673a 100644 --- a/src/pages/addresses/AddressNamesCard.tsx +++ b/src/pages/addresses/AddressNamesCard.tsx @@ -9,11 +9,11 @@ import { Link } from "react-router-dom"; import { NameItem } from "./NameItem"; -import { lookupNames, LookupNamesResponse } from "../../krist/api/lookup"; +import { lookupNames, LookupNamesResponse } from "@api/lookup"; -import { useSyncNode } from "../../krist/api"; +import { useSyncNode } from "@api"; -import { SmallResult } from "../../components/results/SmallResult"; +import { SmallResult } from "@comp/results/SmallResult"; import Debug from "debug"; const debug = Debug("kristweb:address-names-card"); diff --git a/src/pages/addresses/AddressPage.tsx b/src/pages/addresses/AddressPage.tsx index 2e6c08c..3a7dcbc 100644 --- a/src/pages/addresses/AddressPage.tsx +++ b/src/pages/addresses/AddressPage.tsx @@ -8,15 +8,15 @@ import { useParams } from "react-router-dom"; import { PageLayout } from "../../layout/PageLayout"; -import { APIErrorResult } from "../../components/results/APIErrorResult"; +import { APIErrorResult } from "@comp/results/APIErrorResult"; -import { Statistic } from "../../components/Statistic"; -import { KristValue } from "../../components/krist/KristValue"; -import { DateTime } from "../../components/DateTime"; +import { Statistic } from "@comp/Statistic"; +import { KristValue } from "@comp/krist/KristValue"; +import { DateTime } from "@comp/DateTime"; -import * as api from "../../krist/api"; -import { lookupAddress, KristAddressWithNames } from "../../krist/api/lookup"; -import { useWallets } from "../../krist/wallets/Wallet"; +import * as api from "@api"; +import { lookupAddress, KristAddressWithNames } from "@api/lookup"; +import { useWallets } from "@wallets/Wallet"; import { AddressButtonRow } from "./AddressButtonRow"; import { AddressTransactionsCard } from "./AddressTransactionsCard"; diff --git a/src/pages/addresses/AddressTransactionsCard.tsx b/src/pages/addresses/AddressTransactionsCard.tsx index 54228a9..ea97eac 100644 --- a/src/pages/addresses/AddressTransactionsCard.tsx +++ b/src/pages/addresses/AddressTransactionsCard.tsx @@ -7,12 +7,12 @@ import { useTranslation } from "react-i18next"; -import { TransactionSummary } from "../../components/transactions/TransactionSummary"; -import { lookupTransactions, LookupTransactionsResponse } from "../../krist/api/lookup"; +import { TransactionSummary } from "@comp/transactions/TransactionSummary"; +import { lookupTransactions, LookupTransactionsResponse } from "@api/lookup"; -import { useSyncNode } from "../../krist/api"; +import { useSyncNode } from "@api"; -import { SmallResult } from "../../components/results/SmallResult"; +import { SmallResult } from "@comp/results/SmallResult"; import Debug from "debug"; const debug = Debug("kristweb:address-transactions-card"); diff --git a/src/pages/addresses/NameItem.tsx b/src/pages/addresses/NameItem.tsx index 22be9c7..3256094 100644 --- a/src/pages/addresses/NameItem.tsx +++ b/src/pages/addresses/NameItem.tsx @@ -7,9 +7,9 @@ import { useTranslation, Trans } from "react-i18next"; import { Link } from "react-router-dom"; -import { KristName } from "../../krist/api/types"; -import { KristNameLink } from "../../components/names/KristNameLink"; -import { DateTime } from "../../components/DateTime"; +import { KristName } from "@api/types"; +import { KristNameLink } from "@comp/names/KristNameLink"; +import { DateTime } from "@comp/DateTime"; export function NameItem({ name }: { name: KristName }): JSX.Element { const { t } = useTranslation(); diff --git a/src/pages/backup/ImportBackupModal.tsx b/src/pages/backup/ImportBackupModal.tsx new file mode 100644 index 0000000..386e269 --- /dev/null +++ b/src/pages/backup/ImportBackupModal.tsx @@ -0,0 +1,138 @@ +// Copyright (c) 2020-2021 Drew Lemmy +// This file is part of KristWeb 2 under GPL-3.0. +// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt +import React, { useState, Dispatch, SetStateAction} from "react"; +import { Modal, Form, Input, Button, Typography } from "antd"; + +import { useTranslation, Trans } from "react-i18next"; +import { translateError } from "@utils/i18n"; + +import { FakeUsernameInput } from "@comp/auth/FakeUsernameInput"; +import { getMasterPasswordInput } from "@comp/auth/MasterPasswordInput"; + +import { ImportDetectFormat } from "./ImportDetectFormat"; +import { detectBackupFormat } from "../backup/backupParser"; + +import Debug from "debug"; +const debug = Debug("kristweb:import-backup-modal"); + +const { Text, Paragraph } = Typography; +const { TextArea } = Input; + +interface FormValues { + masterPassword: string; + code: string; +} + +interface Props { + visible: boolean; + setVisible: Dispatch>; +} + +export function ImportBackupModal({ visible, setVisible }: Props): JSX.Element { + const { t } = useTranslation(); + + const [form] = Form.useForm(); + const [code, setCode] = useState(""); + const [decodeError, setDecodeError] = useState(); + + function closeModal() { + debug("closing modal and resetting fields"); + form.resetFields(); + setCode(""); + setVisible(false); + } + + function onValuesChange(changed: Partial) { + if (changed.code) setCode(changed.code); + } + + // Detect the backup format for the final time, validate the password, and + // if all is well, begin the import + async function onFinish() { + const values = await form.validateFields(); + if (!values.masterPassword || !values.code) return; + + try { + // Decode first + const format = detectBackupFormat(code); + debug("detected format: %s", format.type); + + setDecodeError(undefined); + } catch (err) { + console.error(err); + setDecodeError(translateError(t, err, "import.decodeErrors.unknown")); + } + } + + return +
+ {/* Import lead */} + {t("import.description")} + + {/* Detected format information */} + + + {/* Password input */} + + {getMasterPasswordInput({ + placeholder: t("import.masterPasswordPlaceholder"), + autoFocus: true + })} + + + + {/* Code textarea */} + + + +
+
; +} diff --git a/src/pages/backup/ImportDetectFormat.tsx b/src/pages/backup/ImportDetectFormat.tsx new file mode 100644 index 0000000..28ae692 --- /dev/null +++ b/src/pages/backup/ImportDetectFormat.tsx @@ -0,0 +1,88 @@ +// Copyright (c) 2020-2021 Drew Lemmy +// This file is part of KristWeb 2 under GPL-3.0. +// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt +import React, { useState, useEffect, useMemo, Dispatch, SetStateAction } from "react"; +import { Typography } from "antd"; + +import { useTranslation, Trans, TFunction } from "react-i18next"; +import { translateError } from "@utils/i18n"; + +import { BackupFormatType } from "../backup/backupFormats"; +import { detectBackupFormat } from "../backup/backupParser"; + +import { debounce } from "lodash-es"; + +import Debug from "debug"; +const debug = Debug("kristweb:import-detect-format"); + +const { Text, Paragraph } = Typography; + +interface Props { + code: string; + setDecodeError: Dispatch>; +} + +const DECODE_THROTTLE = 500; + +// This is debounced to prevent wasting CPU time if the user is...for some +// reason...typing out their backup code (wtf??) +function _checkDecode( + t: TFunction, + code: string, + setDetectedFormatType: Dispatch>, + setDecodeError: Dispatch> +) { + debug("checking decode"); + try { + const format = detectBackupFormat(code); + debug("detected format: %s", format.type); + + setDecodeError(undefined); + setDetectedFormatType(format.type); + } catch (err) { + console.error(err); + setDecodeError(translateError(t, err, "import.decodeErrors.unknown")); + setDetectedFormatType(false); + } +} + +export function ImportDetectFormat({ code, setDecodeError }: Props): JSX.Element { + const { t } = useTranslation(); + + const [detectedFormatType, setDetectedFormatType] = useState(); + + const checkDecode = useMemo(() => debounce(_checkDecode, DECODE_THROTTLE, { leading: true }), []); + + // Attempt to decode (throttled) when the code changes + useEffect(() => { + // Ignore empty codes + if (!code || !code.trim()) { + debug("clearing detected format"); + setDetectedFormatType(undefined); + setDecodeError(undefined); + return; + } + + checkDecode(t, code.trim(), setDetectedFormatType, setDecodeError); + }, [t, checkDecode, code, setDecodeError]); + + // Display the detected format as a string, or "Invalid!" if there was an + // error decoding it. + function DetectFormatText(): JSX.Element | null { + if (detectedFormatType === false) { // Decode error + return {t("import.detectedFormatInvalid")}; + } else if (detectedFormatType) { // Successful decode + return {t(detectedFormatType)}; + } + + return null; + } + + return detectedFormatType !== undefined ? ( + + +
Detected format: + + + ) : <>; +} diff --git a/src/pages/backup/backupFormats.ts b/src/pages/backup/backupFormats.ts index d916029..e8b01c5 100644 --- a/src/pages/backup/backupFormats.ts +++ b/src/pages/backup/backupFormats.ts @@ -1,7 +1,7 @@ // Copyright (c) 2020-2021 Drew Lemmy // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt -import { Wallet } from "../../krist/wallets/Wallet"; +import { Wallet } from "@wallets/Wallet"; // The values here are the translation keys for the formats. export enum BackupFormatType { diff --git a/src/pages/backup/backupImport.tsx b/src/pages/backup/backupImport.tsx index a01e6e3..24af6ad 100644 --- a/src/pages/backup/backupImport.tsx +++ b/src/pages/backup/backupImport.tsx @@ -3,8 +3,8 @@ // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import React from "react"; -import { aesGcmDecrypt } from "../../utils/crypto"; -import { decryptCryptoJS } from "../../utils/CryptoJS"; +import { aesGcmDecrypt } from "@utils/crypto"; +import { decryptCryptoJS } from "@utils/CryptoJS"; import { BackupFormatType } from "./backupFormats"; diff --git a/src/pages/backup/backupParser.ts b/src/pages/backup/backupParser.ts index 39a9fc5..b52dd30 100644 --- a/src/pages/backup/backupParser.ts +++ b/src/pages/backup/backupParser.ts @@ -6,6 +6,8 @@ BackupFormatKristWebV1, BackupFormatKristWebV2 } from "./backupFormats"; +import { TranslatedError } from "@utils/i18n"; + import { isPlainObject } from "lodash-es"; export function detectBackupFormat(rawData: string): BackupFormat { @@ -17,12 +19,14 @@ const data = JSON.parse(plainData); // Check for required properties - if (!data.tester) throw new Error("import.decodeErrors.missingTester"); - if (!data.salt) throw new Error("import.decodeErrors.missingSalt"); + if (!data.tester) + throw new TranslatedError("import.decodeErrors.missingTester"); + if (!data.salt) + throw new TranslatedError("import.decodeErrors.missingSalt"); if (!isPlainObject(data.wallets)) - throw new Error("import.decodeErrors.invalidWallets"); + throw new TranslatedError("import.decodeErrors.invalidWallets"); if (data.friends !== undefined && !isPlainObject(data.friends)) - throw new Error("import.decodeErrors.invalidFriends"); + throw new TranslatedError("import.decodeErrors.invalidFriends"); // Determine the format if (data.version === 2) { @@ -37,11 +41,11 @@ } catch (err) { // Invalid base64 if (err instanceof DOMException && err.name === "InvalidCharacterError") - throw new Error("import.decodeErrors.atob"); + throw new TranslatedError("import.decodeErrors.atob"); // Invalid json if (err instanceof SyntaxError) - throw new Error("import.decodeErrors.json"); + throw new TranslatedError("import.decodeErrors.json"); throw err; } diff --git a/src/pages/blocks/BlockHash.tsx b/src/pages/blocks/BlockHash.tsx index da55526..6d7b91f 100644 --- a/src/pages/blocks/BlockHash.tsx +++ b/src/pages/blocks/BlockHash.tsx @@ -5,7 +5,7 @@ import classNames from "classnames"; import { Typography } from "antd"; -import { useBooleanSetting } from "../../utils/settings"; +import { useBooleanSetting } from "@utils/settings"; import "./BlockHash.less"; diff --git a/src/pages/blocks/BlockPage.tsx b/src/pages/blocks/BlockPage.tsx index 68660b5..e0c40d2 100644 --- a/src/pages/blocks/BlockPage.tsx +++ b/src/pages/blocks/BlockPage.tsx @@ -9,19 +9,19 @@ import { useParams, Link } from "react-router-dom"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { PageLayout } from "../../layout/PageLayout"; -import { APIErrorResult } from "../../components/results/APIErrorResult"; +import { APIErrorResult } from "@comp/results/APIErrorResult"; -import { Statistic } from "../../components/Statistic"; -import { ContextualAddress } from "../../components/addresses/ContextualAddress"; +import { Statistic } from "@comp/Statistic"; +import { ContextualAddress } from "@comp/addresses/ContextualAddress"; import { BlockHash } from "./BlockHash"; -import { KristValue } from "../../components/krist/KristValue"; -import { DateTime } from "../../components/DateTime"; +import { KristValue } from "@comp/krist/KristValue"; +import { DateTime } from "@comp/DateTime"; -import * as api from "../../krist/api"; -import { KristBlock } from "../../krist/api/types"; +import * as api from "@api"; +import { KristBlock } from "@api/types"; import "./BlockPage.less"; diff --git a/src/pages/blocks/BlocksPage.tsx b/src/pages/blocks/BlocksPage.tsx index 506c585..526e69e 100644 --- a/src/pages/blocks/BlocksPage.tsx +++ b/src/pages/blocks/BlocksPage.tsx @@ -4,14 +4,14 @@ import React, { useState, useMemo } from "react"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { PageLayout } from "../../layout/PageLayout"; -import { APIErrorResult } from "../../components/results/APIErrorResult"; +import { APIErrorResult } from "@comp/results/APIErrorResult"; import { BlocksTable } from "./BlocksTable"; -import { useBooleanSetting } from "../../utils/settings"; -import { useLinkedPagination } from "../../utils/table"; +import { useBooleanSetting } from "@utils/settings"; +import { useLinkedPagination } from "@utils/table"; interface Props { lowest?: boolean; diff --git a/src/pages/blocks/BlocksTable.tsx b/src/pages/blocks/BlocksTable.tsx index 40b6203..4a4d029 100644 --- a/src/pages/blocks/BlocksTable.tsx +++ b/src/pages/blocks/BlocksTable.tsx @@ -7,15 +7,15 @@ import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; -import { KristBlock } from "../../krist/api/types"; -import { lookupBlocks, LookupBlocksOptions, LookupBlocksResponse } from "../../krist/api/lookup"; -import { useMalleablePagination } from "../../utils/table"; -import { useIntegerSetting } from "../../utils/settings"; +import { KristBlock } from "@api/types"; +import { lookupBlocks, LookupBlocksOptions, LookupBlocksResponse } from "@api/lookup"; +import { useMalleablePagination } from "@utils/table"; +import { useIntegerSetting } from "@utils/settings"; -import { ContextualAddress } from "../../components/addresses/ContextualAddress"; +import { ContextualAddress } from "@comp/addresses/ContextualAddress"; import { BlockHash } from "./BlockHash"; -import { KristValue } from "../../components/krist/KristValue"; -import { DateTime } from "../../components/DateTime"; +import { KristValue } from "@comp/krist/KristValue"; +import { DateTime } from "@comp/DateTime"; import Debug from "debug"; const debug = Debug("kristweb:blocks-table"); diff --git a/src/pages/credits/CreditsPage.tsx b/src/pages/credits/CreditsPage.tsx index 98347a9..b95ce52 100644 --- a/src/pages/credits/CreditsPage.tsx +++ b/src/pages/credits/CreditsPage.tsx @@ -10,7 +10,7 @@ import { Translators } from "./Translators"; import "./CreditsPage.less"; -import { getAuthorInfo } from "../../utils/credits"; +import { getAuthorInfo } from "@utils/credits"; const { Title } = Typography; diff --git a/src/pages/credits/Supporters.tsx b/src/pages/credits/Supporters.tsx index 8c99d1b..ebf3cf8 100644 --- a/src/pages/credits/Supporters.tsx +++ b/src/pages/credits/Supporters.tsx @@ -8,7 +8,7 @@ import packageJson from "../../../package.json"; -import { useMountEffect } from "../../utils"; +import { useMountEffect } from "@utils"; interface Supporter { name: string; diff --git a/src/pages/credits/Translators.tsx b/src/pages/credits/Translators.tsx index 24252e5..292d3a5 100644 --- a/src/pages/credits/Translators.tsx +++ b/src/pages/credits/Translators.tsx @@ -6,9 +6,9 @@ import { GlobalOutlined } from "@ant-design/icons"; import { useTranslation } from "react-i18next"; -import { Flag } from "../../components/Flag"; +import { Flag } from "@comp/Flag"; -import { getLanguages } from "../../utils/i18n"; +import { getLanguages } from "@utils/i18n"; import packageJson from "../../../package.json"; const { Text } = Typography; diff --git a/src/pages/dashboard/BlockDifficultyCard.tsx b/src/pages/dashboard/BlockDifficultyCard.tsx index 254bebd..7ae9f18 100644 --- a/src/pages/dashboard/BlockDifficultyCard.tsx +++ b/src/pages/dashboard/BlockDifficultyCard.tsx @@ -6,18 +6,18 @@ import { Card, Skeleton, Empty, Row, Col, Tooltip, Select } from "antd"; import { useSelector, shallowEqual } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { useTranslation, Trans } from "react-i18next"; import { Line } from "react-chartjs-2"; -import * as api from "../../krist/api"; -import { estimateHashRate } from "../../utils/currency"; -import { KristConstants } from "../../krist/api/types"; -import { trailingThrottleState } from "../../utils/promiseThrottle"; +import * as api from "@api"; +import { estimateHashRate } from "@utils/currency"; +import { KristConstants } from "@api/types"; +import { trailingThrottleState } from "@utils/promiseThrottle"; -import { SmallResult } from "../../components/results/SmallResult"; -import { Statistic } from "../../components/Statistic"; +import { SmallResult } from "@comp/results/SmallResult"; +import { Statistic } from "@comp/Statistic"; import Debug from "debug"; const debug = Debug("kristweb:block-difficulty-card"); diff --git a/src/pages/dashboard/BlockValueCard.tsx b/src/pages/dashboard/BlockValueCard.tsx index 9d65b71..e89d879 100644 --- a/src/pages/dashboard/BlockValueCard.tsx +++ b/src/pages/dashboard/BlockValueCard.tsx @@ -5,12 +5,12 @@ import { Card, Skeleton, Typography, Progress } from "antd"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { useTranslation, Trans } from "react-i18next"; import { Link } from "react-router-dom"; -import { KristValue } from "../../components/krist/KristValue"; +import { KristValue } from "@comp/krist/KristValue"; const { Text } = Typography; diff --git a/src/pages/dashboard/InDevBanner.tsx b/src/pages/dashboard/InDevBanner.tsx index 7bf5720..555fa11 100644 --- a/src/pages/dashboard/InDevBanner.tsx +++ b/src/pages/dashboard/InDevBanner.tsx @@ -6,7 +6,7 @@ import { useTranslation, Trans } from "react-i18next"; -import { getAuthorInfo } from "../../utils/credits"; +import { getAuthorInfo } from "@utils/credits"; export function InDevBanner(): JSX.Element { const { t } = useTranslation(); diff --git a/src/pages/dashboard/MOTDCard.tsx b/src/pages/dashboard/MOTDCard.tsx index 92538bb..7b326c0 100644 --- a/src/pages/dashboard/MOTDCard.tsx +++ b/src/pages/dashboard/MOTDCard.tsx @@ -5,12 +5,12 @@ import { Card, Alert } from "antd"; import { useSelector } from "react-redux"; -import { RootState } from "../../store"; +import { RootState } from "@store"; import { useTranslation } from "react-i18next"; import Linkify from "react-linkify"; -import { DateTime } from "../../components/DateTime"; +import { DateTime } from "@comp/DateTime"; export function MOTDCard(): JSX.Element { const { t } = useTranslation(); diff --git a/src/pages/dashboard/TransactionsCard.tsx b/src/pages/dashboard/TransactionsCard.tsx index 12d5ce7..0a9e9b1 100644 --- a/src/pages/dashboard/TransactionsCard.tsx +++ b/src/pages/dashboard/TransactionsCard.tsx @@ -7,16 +7,16 @@ import { useTranslation } from "react-i18next"; -import { TransactionSummary } from "../../components/transactions/TransactionSummary"; -import { lookupTransactions, LookupTransactionsResponse } from "../../krist/api/lookup"; +import { TransactionSummary } from "@comp/transactions/TransactionSummary"; +import { lookupTransactions, LookupTransactionsResponse } from "@api/lookup"; -import { useSyncNode } from "../../krist/api"; -import { useWallets } from "../../krist/wallets/Wallet"; -import { WalletMap } from "../../store/reducers/WalletsReducer"; +import { useSyncNode } from "@api"; +import { useWallets } from "@wallets/Wallet"; +import { WalletMap } from "@reducers/WalletsReducer"; -import { SmallResult } from "../../components/results/SmallResult"; +import { SmallResult } from "@comp/results/SmallResult"; -import { trailingThrottleState } from "../../utils/promiseThrottle"; +import { trailingThrottleState } from "@utils/promiseThrottle"; import Debug from "debug"; const debug = Debug("kristweb:transactions-card"); diff --git a/src/pages/dashboard/WalletItem.tsx b/src/pages/dashboard/WalletItem.tsx index 9aa06f4..67b18eb 100644 --- a/src/pages/dashboard/WalletItem.tsx +++ b/src/pages/dashboard/WalletItem.tsx @@ -4,9 +4,9 @@ import React from "react"; import { Row, Col } from "antd"; -import { Wallet } from "../../krist/wallets/Wallet"; +import { Wallet } from "@wallets/Wallet"; -import { KristValue } from "../../components/krist/KristValue"; +import { KristValue } from "@comp/krist/KristValue"; export function WalletItem({ wallet }: { wallet: Wallet }): JSX.Element { return diff --git a/src/pages/dashboard/WalletOverviewCard.tsx b/src/pages/dashboard/WalletOverviewCard.tsx index 1cd90b4..a618365 100644 --- a/src/pages/dashboard/WalletOverviewCard.tsx +++ b/src/pages/dashboard/WalletOverviewCard.tsx @@ -7,13 +7,13 @@ import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; -import { Wallet, useWallets } from "../../krist/wallets/Wallet"; +import { Wallet, useWallets } from "@wallets/Wallet"; -import { KristValue } from "../../components/krist/KristValue"; -import { Statistic } from "../../components/Statistic"; +import { KristValue } from "@comp/krist/KristValue"; +import { Statistic } from "@comp/Statistic"; import { WalletItem } from "./WalletItem"; -import { keyedNullSort } from "../../utils"; +import { keyedNullSort } from "@utils"; export function WalletOverviewCard(): JSX.Element { const { wallets } = useWallets(); diff --git a/src/pages/dev/DevPage.tsx b/src/pages/dev/DevPage.tsx index 530291a..0fbc11a 100644 --- a/src/pages/dev/DevPage.tsx +++ b/src/pages/dev/DevPage.tsx @@ -2,78 +2,22 @@ // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import React, { useState } from "react"; -import { Input, Button, Typography } from "antd"; - -import { useTranslation, Trans } from "react-i18next"; - +import { Button } from "antd"; import { PageLayout } from "../../layout/PageLayout"; -import { BackupFormatType } from "../backup/backupFormats"; -import { detectBackupFormat } from "../backup/backupParser"; - -const { TextArea } = Input; -const { Text, Paragraph } = Typography; +import { ImportBackupModal } from "../backup/ImportBackupModal"; export function DevPage(): JSX.Element { - const { t } = useTranslation(); - - const [code, setCode] = useState(""); - const [detectedFormatType, setDetectedFormatType] = useState(); - const [decodeError, setDecodeError] = useState(); - - async function onImport() { - console.log(code); - try { - const format = detectBackupFormat(code); - setDecodeError(undefined); - setDetectedFormatType(format.type); - } catch (err) { - console.error(err); - - const message = err.message // Translate the error if we can - ? err.message.startsWith("import.") ? t(err.message) : err.message - : t("import.decodeErrors.unknown"); - - setDecodeError(message); - } - } - - // Display the detected format as a string, or "Invalid!" if there was an - // error decoding it. - function DetectFormatText(): JSX.Element | null { - if (decodeError) return {t("import.detectedFormatInvalid")}; - if (detectedFormatType) return {t(detectedFormatType)}; - return null; - } + const [modalVisible, setModalVisible] = useState(false); return - {/* Detected format */} - {(detectedFormatType || decodeError) ? ( - - - Detected format: - - - ) : <>} - - {/* Decode error */} - {decodeError && {decodeError}} - - {/* Backup code textarea */} -