From aded2876e82534d4541d16947a585438d7959f64 Mon Sep 17 00:00:00 2001 From: egor0798 Date: Mon, 7 Aug 2023 12:35:59 +0400 Subject: [PATCH 01/34] Feat/feature sliced design (#985) * chore: removed old ui kit components * chore: app and shared folders with public api * chore: test fix * chore: tets deps fix * chore: entities folder * chore: moved chains file and storage, renamed Balance and Chain components * chore: import errors fix * chore: fixed tests imports and mocks * chore: fixed InactiveChain test * chore: linter errors fix * chore: update ReceiveModal from dev --------- Co-authored-by: Egor Co-authored-by: Yaroslav Grachev --- README.md | 4 +- app.config.js | 4 +- jest.config.ts | 3 +- src/main/main.ts | 4 +- src/renderer/{ => app}/App.tsx | 22 ++- src/renderer/{ => app}/i18n.ts | 2 +- src/renderer/app/index.css | 62 +++++++ src/renderer/{ => app}/index.html | 0 src/renderer/{ => app}/index.tsx | 2 +- .../ConfirmContext/ConfirmContext.test.tsx | 4 +- .../context/ConfirmContext/ConfirmContext.tsx | 6 +- .../providers/context/ConfirmContext/index.ts | 1 + .../GraphqlContext/GraphqlContext.test.tsx | 6 +- .../context/GraphqlContext/GraphqlContext.tsx | 4 +- .../providers/context/GraphqlContext/index.ts | 1 + .../context/I18nContext/I18nContext.tsx | 2 +- .../providers/context/I18nContext/index.ts | 1 + .../MatrixContext/MatrixContext.test.tsx | 32 ++-- .../context/MatrixContext/MatrixContext.tsx | 43 ++--- .../providers/context/MatrixContext/index.ts | 1 + .../MultisigChainContext.tsx | 17 +- .../context/MultisigChainContext/index.ts | 1 + .../NetworkContext/NetworkContext.test.tsx | 13 +- .../context/NetworkContext/NetworkContext.tsx | 12 +- .../providers/context/NetworkContext/index.ts | 1 + src/renderer/app/providers/context/index.ts | 6 + src/renderer/app/providers/index.ts | 2 + src/renderer/app/providers/routes/index.ts | 3 + .../{ => app/providers}/routes/paths.ts | 4 +- .../providers/routes/routesConfig.tsx} | 10 +- .../{ => app/providers}/routes/utils.test.ts | 4 +- .../{ => app/providers}/routes/utils.ts | 0 src/renderer/app/styles/fonts.css | 59 ++++++ src/renderer/{ => app/styles}/theme/dark.css | 0 .../{ => app/styles}/theme/default.css | 0 .../common => assets}/chains/chains.json | 0 .../common => assets}/chains/chains_dev.json | 0 .../common/ExplorerLink/ExplorerLink.tsx | 10 +- .../common/ExplorerLink/constants.ts | 2 +- .../ExtrinsicExplorers/ExtrinsicExplorers.tsx | 5 +- .../ExtrinsicExplorers/useExtrinsicInfo.tsx | 4 +- .../FallbackScreen/FallbackScreen.test.tsx | 2 +- .../common/FallbackScreen/FallbackScreen.tsx | 5 +- .../components/common/Header/Header.tsx | 4 +- .../QrGeneratorContainer.tsx | 7 +- .../QrReader/QrMultiframeSignatureReader.tsx | 4 +- .../common/QrCode/QrReader/QrReader.test.tsx | 2 +- .../common/QrCode/QrReader/QrReader.tsx | 5 +- .../QrCode/QrReader/QrReaderWrapper.tsx | 11 +- .../QrCode/QrReader/QrSignatureReader.tsx | 4 +- .../QrCode/QrReader/SignatureReaderError.tsx | 4 +- .../common/Scanning/ScanMultiframeQr.tsx | 11 +- .../common/Scanning/ScanSingleframeQr.tsx | 12 +- src/renderer/components/common/index.ts | 14 -- .../forms/ContactForm/ContactForm.test.tsx | 13 +- .../forms/ContactForm/ContactForm.tsx | 11 +- .../layout/PrimaryLayout/NavItem/NavItem.tsx | 9 +- .../Navigation/Navigation.test.tsx | 10 +- .../PrimaryLayout/Navigation/Navigation.tsx | 14 +- .../Wallets/ActiveAccountCard.tsx | 12 +- .../PrimaryLayout/Wallets/WalletGroup.tsx | 11 +- .../PrimaryLayout/Wallets/WalletMenu.tsx | 19 +- .../PrimaryLayout/Wallets/common/constants.ts | 2 +- .../PrimaryLayout/Wallets/common/types.ts | 4 +- .../Wallets/common/useGroupedWallets.ts | 9 +- .../PrimaryLayout/Wallets/common/utils.ts | 4 +- .../CreateMultisigAccount.test.tsx | 31 ++-- .../CreateMultisigAccount.tsx | 24 +-- .../CreateMultisigAccount/common/types.ts | 2 +- .../components/ConfirmSignatories.tsx | 7 +- .../components/SelectSignatories.tsx | 30 ++- .../components/WalletForm.tsx | 17 +- .../components/WalletsTabItem.tsx | 10 +- .../modals/MatrixModal/Matrix.test.tsx | 7 +- .../modals/MatrixModal/MatrixModal.tsx | 5 +- .../Credentials/Credentials.test.tsx | 9 +- .../components/Credentials/Credentials.tsx | 7 +- .../components/LoginForm/LoginForm.test.tsx | 5 +- .../components/LoginForm/LoginForm.tsx | 12 +- .../MatrixInfoPopover.test.tsx | 4 +- .../MatrixInfoPopover/MatrixInfoPopover.tsx | 4 +- .../Verification/Verification.test.tsx | 18 +- .../components/Verification/Verification.tsx | 8 +- .../Buttons/ButtonLink/ButtonLink.stories.tsx | 75 -------- .../Inputs/InputArea/InputArea.test.tsx | 35 ---- .../ui/Address/ChainAddress.stories.tsx | 23 --- .../ui/Address/ChainAddress.test.tsx | 20 -- .../components/ui/Address/ChainAddress.tsx | 87 --------- .../components/ui/Balance/Balance.stories.tsx | 24 --- .../components/ui/Balance/Balance.test.tsx | 27 --- .../components/ui/Balance/Balance.tsx | 29 --- .../components/ui/Block/Block.stories.tsx | 16 -- .../components/ui/Block/Block.test.tsx | 13 -- src/renderer/components/ui/Block/Block.tsx | 13 -- .../ui/Buttons/Button/Button.stories.tsx | 61 ------- .../ui/Buttons/Button/Button.test.tsx | 52 ------ .../components/ui/Buttons/Button/Button.tsx | 52 ------ .../Buttons/ButtonBack/ButtonBack.stories.tsx | 25 --- .../ui/Buttons/ButtonBack/ButtonBack.test.tsx | 43 ----- .../ui/Buttons/ButtonBack/ButtonBack.tsx | 35 ---- .../ui/Buttons/ButtonLink/ButtonLink.test.tsx | 53 ------ .../ui/Buttons/ButtonLink/ButtonLink.tsx | 56 ------ .../components/ui/Buttons/common/constants.ts | 35 ---- .../components/ui/Buttons/common/types.ts | 3 - .../ui/Carousel/Carousel.stories.tsx | 30 --- .../components/ui/Carousel/Carousel.test.tsx | 28 --- .../components/ui/Carousel/Carousel.tsx | 77 -------- .../components/ui/Checkbox/Checkbox.css | 16 -- .../ui/Checkbox/Checkbox.stories.tsx | 28 --- .../components/ui/Checkbox/Checkbox.test.tsx | 37 ---- .../components/ui/Checkbox/Checkbox.tsx | 53 ------ .../Dropdowns/Combobox/Combobox.stories.tsx | 86 --------- .../ui/Dropdowns/Combobox/Combobox.test.tsx | 23 --- .../ui/Dropdowns/Combobox/Combobox.tsx | 116 ------------ .../Dropdowns/Dropdown/Dropdown.stories.tsx | 86 --------- .../ui/Dropdowns/Dropdown/Dropdown.test.tsx | 55 ------ .../ui/Dropdowns/Dropdown/Dropdown.tsx | 132 -------------- .../ui/Dropdowns/Filter/Filter.stories.tsx | 40 ---- .../ui/Dropdowns/Filter/Filter.test.tsx | 53 ------ .../components/ui/Dropdowns/Filter/Filter.tsx | 72 -------- .../ui/Dropdowns/Select/Select.stories.tsx | 64 ------- .../ui/Dropdowns/Select/Select.test.tsx | 66 ------- .../components/ui/Dropdowns/Select/Select.tsx | 122 ------------- .../ui/Dropdowns/common/constants.ts | 53 ------ .../components/ui/Dropdowns/common/types.ts | 16 -- .../ui/InfoLink/InfoLink.stories.tsx | 17 -- .../components/ui/InfoLink/InfoLink.test.tsx | 27 --- .../components/ui/InfoLink/InfoLink.tsx | 22 --- .../ui/InputHint/InputHint.stories.tsx | 25 --- .../ui/InputHint/InputHint.test.tsx | 28 --- .../components/ui/InputHint/InputHint.tsx | 30 --- .../ui/Inputs/Input/Input.stories.tsx | 41 ----- .../components/ui/Inputs/Input/Input.test.tsx | 24 --- .../components/ui/Inputs/Input/Input.tsx | 66 ------- .../ui/Inputs/InputArea/InputArea.stories.tsx | 39 ---- .../ui/Inputs/InputArea/InputArea.tsx | 7 +- .../components/ui/Inputs/common/types.ts | 10 - .../components/ui/Plate/Plate.stories.tsx | 16 -- .../components/ui/Plate/Plate.test.tsx | 22 --- src/renderer/components/ui/Plate/Plate.tsx | 14 -- .../components/ui/Stepper/Stepper.stories.tsx | 44 ----- .../components/ui/Stepper/Stepper.test.tsx | 47 ----- .../components/ui/Stepper/Stepper.tsx | 78 -------- .../components/ui/Table/Table.stories.tsx | 62 ------- src/renderer/components/ui/Table/Table.tsx | 135 -------------- .../components/ui/Table/TableContext.tsx | 28 --- .../components/ui/Table/TableParts.tsx | 171 ------------------ .../components/ui/Table/common/constants.ts | 13 -- .../components/ui/Table/common/types.ts | 22 --- .../components/ui/Table/common/utils.ts | 44 ----- src/renderer/components/ui/index.ts | 55 ------ src/renderer/context/ConfirmContext/index.ts | 2 - src/renderer/context/GraphqlContext/index.ts | 2 - src/renderer/context/I18nContext/index.ts | 2 - src/renderer/context/MatrixContext/index.ts | 2 - .../context/MultisigChainContext/index.ts | 2 - src/renderer/context/NetworkContext/index.ts | 2 - src/renderer/domain/connection.ts | 2 +- src/renderer/domain/utility.ts | 2 + src/renderer/entities/account/index.ts | 3 + .../lib}/__tests__/accountService.test.ts | 6 +- .../account/lib}/accountService.ts | 4 +- .../account/lib}/common/types.ts | 4 +- src/renderer/entities/account/lib/index.ts | 3 + .../account/lib}/useAddressInfo.tsx | 16 +- .../account/model}/account.ts | 18 +- .../AccountAddress/AccountAddress.stories.tsx | 4 +- .../AccountAddress/AccountAddress.test.tsx | 4 +- .../ui}/AccountAddress/AccountAddress.tsx | 10 +- .../account/ui}/AccountsList/AccountsList.tsx | 18 +- .../AddressWithExplorers.tsx | 23 ++- .../AddressWithName.stories.tsx | 4 +- .../AddressWithName/AddressWithName.test.tsx | 4 +- .../ui}/AddressWithName/AddressWithName.tsx | 16 +- src/renderer/entities/account/ui/index.ts | 5 + src/renderer/entities/asset/index.ts | 4 + .../asset/lib}/balanceService.ts | 12 +- .../asset/lib}/common/constants.ts | 0 .../asset/lib}/common/types.ts | 6 +- .../matrix => entities/asset/lib}/index.ts | 2 +- .../{domain => entities/asset/model}/asset.ts | 0 .../asset/model}/balance.ts | 2 +- .../ui/AssetBalance/AssetBalance.stories.tsx} | 8 +- .../asset/ui/AssetBalance/AssetBalance.tsx} | 12 +- .../asset/ui}/AssetCard/AssetCard.test.tsx | 11 +- .../asset/ui}/AssetCard/AssetCard.tsx | 17 +- .../asset/ui/AssetDetails/AssetDetails.tsx | 21 +++ .../asset/ui}/AssetIcon/AssetIcon.test.tsx | 4 +- .../asset/ui}/AssetIcon/AssetIcon.tsx | 4 +- src/renderer/entities/asset/ui/index.ts | 4 + src/renderer/entities/chain/index.ts | 3 + .../chain/lib}/chainSubscriptionService.ts | 0 .../chain/lib}/common/types.ts | 0 src/renderer/entities/chain/lib/index.ts | 2 + .../{domain => entities/chain/model}/chain.ts | 4 +- .../chain/ui}/ChainIcon/ChainIcon.test.tsx | 4 +- .../chain/ui}/ChainIcon/ChainIcon.tsx | 6 +- .../ui/ChainTitle/ChainTitle.stories.tsx} | 10 +- .../chain/ui/ChainTitle/ChainTitle.test.tsx} | 8 +- .../chain/ui/ChainTitle/ChainTitle.tsx} | 11 +- src/renderer/entities/chain/ui/index.ts | 2 + src/renderer/entities/contact/index.ts | 3 + .../contact/lib}/common/types.ts | 4 +- .../contact/lib}/contactService.ts | 4 +- src/renderer/entities/contact/lib/index.ts | 2 + .../contact/model}/contact.ts | 2 +- .../contact/ui}/ContactList/ContactList.tsx | 12 +- src/renderer/entities/contact/ui/index.ts | 1 + src/renderer/entities/matrix/index.ts | 1 + .../lib}/__tests__/credentialStorage.test.ts | 0 .../matrix/lib}/__tests__/matrix.test.ts | 0 .../lib}/__tests__/secretStorage.test.ts | 0 .../matrix/lib}/common/constants.ts | 0 .../matrix/lib}/common/errors.ts | 0 .../matrix/lib}/common/types.ts | 2 +- .../matrix/lib}/credentialStorage.ts | 0 src/renderer/entities/matrix/lib/index.ts | 3 + .../matrix => entities/matrix/lib}/matrix.ts | 2 +- .../matrix/lib}/secretStorage.ts | 0 src/renderer/entities/multisig/index.ts | 1 + src/renderer/entities/multisig/lib/index.ts | 6 + .../lib}/multisigEvent/common/types.ts | 4 +- .../multisigEvent/multisigEventService.ts | 6 +- .../multisig/lib}/multisigTx/common/consts.ts | 0 .../multisig/lib}/multisigTx/common/types.ts | 6 +- .../multisig/lib}/multisigTx/common/utils.ts | 10 +- .../lib}/multisigTx/multisigTxService.ts | 19 +- src/renderer/entities/network/index.ts | 1 + .../lib}/__tests__/chainSpecService.test.ts | 2 +- .../lib}/__tests__/chainsService.test.ts | 0 .../network/lib}/chainSpecService.ts | 0 .../network/lib}/chainsService.ts | 13 +- .../network/lib}/common/constants.ts | 0 .../network/lib}/common/types.ts | 4 +- .../network/lib}/common/utils.ts | 2 +- src/renderer/entities/network/lib/index.ts | 6 + .../network/lib}/networkService.ts | 6 +- src/renderer/entities/notification/index.ts | 3 + .../notification/lib}/common/types.ts | 4 +- .../entities/notification/lib/index.ts | 2 + .../notification/lib}/notificationService.ts | 4 +- .../notification/model}/notification.ts | 19 +- .../ui/NotificationRow}/NotificationRow.tsx | 13 +- .../entities/notification/ui/index.ts | 1 + src/renderer/entities/settings/index.ts | 1 + .../lib}/__tests__/settingsStorage.test.ts | 0 .../settings/lib}/common/constants.ts | 0 .../settings/lib}/common/types.ts | 0 src/renderer/entities/settings/lib/index.ts | 3 + .../settings/lib}/settingsStorage.ts | 0 src/renderer/entities/signatory/index.ts | 2 + .../signatory/model}/signatory.ts | 2 +- .../SelectableSignatory.tsx | 29 ++- .../SignatoryCard/SignatoryCard.stories.tsx | 4 +- .../ui}/SignatoryCard/SignatoryCard.test.tsx | 11 +- .../ui}/SignatoryCard/SignatoryCard.tsx | 19 +- src/renderer/entities/signatory/ui/index.ts | 2 + src/renderer/entities/staking/index.ts | 3 + .../lib}/__tests__/apyCalculator.test.ts | 0 .../lib}/__tests__/stakingDataService.test.ts | 0 .../__tests__/stakingRewardsService.test.ts | 0 .../staking/lib}/apyCalculator.ts | 0 .../staking/lib}/common/constants.ts | 0 .../staking/lib}/common/types.ts | 2 +- .../staking/lib}/eraService.ts | 0 src/renderer/entities/staking/lib/index.ts | 7 + .../staking/lib}/stakingDataService.ts | 0 .../staking/lib}/stakingRewardsService.ts | 2 +- .../staking/lib}/validatorsService.ts | 0 .../staking/model}/stake.ts | 0 src/renderer/entities/transaction/index.ts | 3 + .../transaction/lib}/callDataDecoder.ts | 2 +- .../transaction/lib}/common/constants.ts | 0 .../transaction/lib}/common/types.ts | 2 +- .../transaction/lib}/common/utils.ts | 2 +- .../entities/transaction/lib/index.ts | 5 + .../transaction/lib}/transactionService.ts | 5 +- .../transaction/model}/transaction.ts | 4 +- .../transaction/ui}/Deposit/Deposit.test.tsx | 22 +-- .../transaction/ui}/Deposit/Deposit.tsx | 13 +- .../DepositWithLabel.test.tsx | 8 +- .../ui}/DepositWithLabel/DepositWithLabel.tsx | 7 +- .../transaction/ui}/Fee/Fee.test.tsx | 24 +-- .../transaction/ui}/Fee/Fee.tsx | 16 +- .../OperationResult.stories.tsx | 0 .../OperationResult/OperationResult.test.tsx | 2 +- .../ui}/OperationResult/OperationResult.tsx | 8 +- .../ui}/OperationResult/common/constants.ts | 4 +- .../ui}/OperationResult/common/types.ts | 0 src/renderer/entities/transaction/ui/index.ts | 9 + src/renderer/entities/wallet/index.ts | 2 + .../wallet/lib}/common/types.ts | 4 +- src/renderer/entities/wallet/lib/index.ts | 2 + .../wallet/lib}/walletService.ts | 4 +- .../wallet/model}/wallet.ts | 2 +- src/renderer/index.css | 120 ------------ .../ManageContact/ManageContact.test.tsx | 6 +- .../ManageContact/ManageContact.tsx | 13 +- .../AddressBook/Overview/Overview.test.tsx | 4 +- .../AddressBook/Overview/Overview.tsx | 10 +- .../EmptyState/EmptyContacts.test.tsx | 4 +- .../components/EmptyState/EmptyContacts.tsx | 5 +- .../EmptyState/EmptySearch.test.tsx | 4 +- .../components/EmptyState/EmptySearch.tsx | 5 +- .../AddressBook/Overview/components/index.ts | 2 +- .../{screens => pages}/AddressBook/index.ts | 0 .../{screens => pages}/Assets/Assets.test.tsx | 36 ++-- .../{screens => pages}/Assets/Assets.tsx | 22 +-- .../{screens => pages}/Assets/common/utils.ts | 6 +- .../components/AssetsFilter/AssetsFilters.tsx | 5 +- .../Modals/ReceiveModal/ReceiveModal.test.tsx | 32 +--- .../Modals/ReceiveModal/ReceiveModal.tsx | 24 +-- .../SelectShardModal/SelectShardModal.tsx | 13 +- .../NetworkAssets/NetworkAssets.test.tsx | 15 +- .../NetworkAssets/NetworkAssets.tsx | 25 +-- .../Assets/components/index.ts | 0 .../Notifications/Notifications.tsx | 11 +- .../Notifications/common/utils.ts | 0 .../components/EmptyNotifications.tsx | 5 +- .../Onboarding/FinalStep/FinalStep.test.tsx | 4 +- .../Onboarding/FinalStep/FinalStep.tsx | 3 +- .../Vault/KeyQrReader/KeyQrReader.test.tsx | 22 +-- .../Vault/KeyQrReader/KeyQrReader.tsx | 9 +- .../Vault/ManageStep/ManageStep.tsx | 29 ++- .../ManageStepSingle/ManageStepSingle.tsx | 12 +- .../Onboarding/Vault/ScanStep/ScanStep.tsx | 4 +- .../Onboarding/Vault/Vault.tsx | 2 +- .../Onboarding/WatchOnly/EmptyState.tsx | 5 +- .../Onboarding/WatchOnly/WatchOnly.tsx | 24 ++- .../Onboarding/Welcome/PrivacyPolicy.tsx | 4 +- .../Onboarding/Welcome/Welcome.tsx | 15 +- .../Onboarding/Welcome/WelcomeCard.tsx | 9 +- src/renderer/pages/Onboarding/index.ts | 1 + .../Operations/Operations.test.tsx | 43 ++--- .../Operations/Operations.tsx | 19 +- .../Operations/common/constants.ts | 0 .../Operations/common/utils.ts | 15 +- .../components/ActionSteps/Confirmation.tsx | 13 +- .../components/ActionSteps/Submit.tsx | 23 +-- .../Operations/components/Details.tsx | 23 +-- .../EmptyState/EmptyOperations.test.tsx | 4 +- .../components/EmptyState/EmptyOperations.tsx | 7 +- .../Operations/components/Filters.tsx | 11 +- .../Operations/components/Log.tsx | 22 +-- .../Operations/components/Operation.tsx | 15 +- .../components/OperationFullInfo.tsx | 37 ++-- .../components/OperationModalTitle.tsx | 4 +- .../Operations/components/OperationStatus.tsx | 6 +- .../components/ShortTransactionInfo.test.tsx | 6 +- .../components/TransactionAmount.tsx | 31 ++++ .../TransactionTitle.test.tsx | 6 +- .../TransactionTitle/TransactionTitle.tsx | 9 +- .../components/modals/ApproveTx.tsx | 41 ++--- .../components/modals/CallDataModal.tsx | 10 +- .../components/modals/RejectReasonModal.tsx | 6 +- .../Operations/components/modals/RejectTx.tsx | 29 ++- .../modals/SignatorySelectModal.tsx | 12 +- .../Settings/Networks/Networks.test.tsx | 27 ++- .../Settings/Networks/Networks.tsx | 22 +-- .../CustomRpcModal/CustomRpcModal.test.tsx | 9 +- .../CustomRpcModal/CustomRpcModal.tsx | 13 +- .../components/NetworkItem/NetworkItem.css | 0 .../NetworkItem/NetworkItem.test.tsx | 4 +- .../components/NetworkItem/NetworkItem.tsx | 9 +- .../NetworkList/NetworkList.test.tsx | 2 +- .../components/NetworkList/NetworkList.tsx | 4 +- .../NetworkSelector/NetworkSelector.test.tsx | 8 +- .../NetworkSelector/NetworkSelector.tsx | 20 +- .../Settings/Networks/components/index.ts | 0 .../Settings/Overview/Overview.test.tsx | 2 +- .../Settings/Overview/Overview.tsx | 2 +- .../GeneralActions/GeneralActions.test.tsx | 4 +- .../GeneralActions/GeneralActions.tsx | 10 +- .../MatrixAction/MatrixAction.test.tsx | 5 +- .../components/MatrixAction/MatrixAction.tsx | 11 +- .../SocialLinks/SocialLinks.test.tsx | 2 +- .../components/SocialLinks/SocialLinks.tsx | 8 +- .../components/Version/Version.test.tsx | 2 +- .../Overview/components/Version/Version.tsx | 5 +- .../Settings/Overview/components/index.ts | 0 .../{screens => pages}/Settings/index.ts | 0 .../Staking/Operations/Bond/Bond.test.tsx | 17 +- .../Staking/Operations/Bond/Bond.tsx | 28 +-- .../Bond/InitOperation/InitOperation.test.tsx | 18 +- .../Bond/InitOperation/InitOperation.tsx | 22 +-- .../ChangeValidators.test.tsx | 17 +- .../ChangeValidators/ChangeValidators.tsx | 25 +-- .../InitOperation/InitOperation.test.tsx | 18 +- .../InitOperation/InitOperation.tsx | 20 +- .../Destination/Destination.test.tsx | 15 +- .../Operations/Destination/Destination.tsx | 23 +-- .../InitOperation/InitOperation.test.tsx | 16 +- .../InitOperation/InitOperation.tsx | 19 +- .../InitOperation/InitOperation.test.tsx | 21 +-- .../Redeem/InitOperation/InitOperation.tsx | 23 +-- .../Staking/Operations/Redeem/Redeem.tsx | 21 +-- .../InitOperation/InitOperation.test.tsx | 21 +-- .../Restake/InitOperation/InitOperation.tsx | 22 +-- .../Operations/Restake/Restake.test.tsx | 19 +- .../Staking/Operations/Restake/Restake.tsx | 21 +-- .../InitOperation/InitOperation.test.tsx | 16 +- .../StakeMore/InitOperation/InitOperation.tsx | 19 +- .../Operations/StakeMore/StakeMore.test.tsx | 17 +- .../Operations/StakeMore/StakeMore.tsx | 21 +-- .../InitOperation/InitOperation.test.tsx | 18 +- .../Unstake/InitOperation/InitOperation.tsx | 24 +-- .../Operations/Unstake/Unstake.test.tsx | 17 +- .../Staking/Operations/Unstake/Unstake.tsx | 23 +-- .../Staking/Operations/common/types.ts | 2 +- .../Staking/Operations/common/utils.tsx | 16 +- .../Confirmation/Confirmation.test.tsx | 19 +- .../components/Confirmation/Confirmation.tsx | 25 ++- .../AccountsModal/AccountsModal.test.tsx | 15 +- .../Modals/AccountsModal/AccountsModal.tsx | 13 +- .../ValidatorsModal/ValidatorsModal.test.tsx | 13 +- .../ValidatorsModal/ValidatorsModal.tsx | 11 +- .../Operations/components/NoAsset/NoAsset.tsx | 5 +- .../OperationForm/OperationFooter.tsx | 12 +- .../OperationForm/OperationForm.tsx | 19 +- .../Operations/components/Signing/Signing.tsx | 4 +- .../Operations/components/Submit/Submit.tsx | 30 +-- .../components/Validators/Validators.test.tsx | 11 +- .../components/Validators/Validators.tsx | 28 +-- .../Staking/Operations/components/index.ts | 0 .../Staking/Overview/Overview.test.tsx | 39 ++-- .../Staking/Overview/Overview.tsx | 34 ++-- .../AboutStaking/AboutStaking.test.tsx | 15 +- .../components/AboutStaking/AboutStaking.tsx | 21 +-- .../components/Actions/Actions.test.tsx | 8 +- .../Overview/components/Actions/Actions.tsx | 17 +- .../EmptyState/InactiveChain.test.tsx | 6 +- .../components/EmptyState/InactiveChain.tsx | 6 +- .../EmptyState/NoValidators.test.tsx | 4 +- .../components/EmptyState/NoValidators.tsx | 7 +- .../NetworkInfo/NetworkInfo.test.tsx | 12 +- .../components/NetworkInfo/NetworkInfo.tsx | 31 ++-- .../NominatorsList/NominatorsList.test.tsx | 4 +- .../NominatorsList/NominatorsList.tsx | 25 +-- .../components/TimeToEra/TimeToEra.tsx | 4 +- .../UnstakingDuration/UnstakingDuration.tsx | 4 +- .../ValidatorsModal/ValidatorsModal.test.tsx | 9 +- .../ValidatorsModal/ValidatorsModal.tsx | 26 ++- .../Staking/Overview/components/index.ts | 0 .../{screens => pages}/Staking/index.ts | 0 .../{screens => pages}/Transfer/Transfer.tsx | 18 +- .../Transfer/common/constants.ts | 0 .../Transfer/common/utils.tsx | 14 +- .../components/ActionSteps/Confirmation.tsx | 17 +- .../components/ActionSteps/InitOperation.tsx | 16 +- .../components/ActionSteps/Signing.tsx | 10 +- .../components/ActionSteps/Submit.tsx | 29 +-- .../Transfer/components/ActionSteps/index.ts | 0 .../Transfer/components/Details.tsx | 13 +- .../Transfer/components/TransferForm.tsx | 34 ++-- src/renderer/pages/index.ts | 8 + .../components/AssetDetails/AssetDetails.tsx | 22 --- src/renderer/screens/Onboarding/index.ts | 3 - .../components/TransactionAmount.tsx | 32 ---- src/renderer/screens/index.ts | 19 -- .../storage/__tests__/balanceStorage.test.ts | 0 .../__tests__/connectionStorage.test.ts | 2 +- .../api}/storage/__tests__/storage.test.ts | 0 .../api}/storage/accountStorage.ts | 2 +- .../api}/storage/balanceStorage.ts | 2 +- .../api}/storage/common/types.ts | 16 +- .../api}/storage/common/upgrades.ts | 0 .../api}/storage/connectionStorage.ts | 0 .../api}/storage/contactStorage.ts | 2 +- .../{services => shared/api}/storage/index.ts | 0 .../api}/storage/multisigEventStorage.ts | 2 +- .../api}/storage/notificationStorage.ts | 2 +- .../api}/storage/storage.ts | 0 .../api}/storage/transactionStorage.ts | 2 +- .../api}/storage/walletStorage.ts | 2 +- .../hooks/__tests__/useClickOutside.test.ts | 0 .../hooks/__tests__/useCountdown.test.ts | 4 +- .../hooks/__tests__/useTaskQueue.test.ts | 0 .../hooks/__tests__/useToggle.test.ts | 0 src/renderer/shared/{ => lib}/hooks/index.ts | 0 .../shared/{ => lib}/hooks/useClickOutside.ts | 0 .../shared/{ => lib}/hooks/useCountdown.ts | 3 +- .../shared/{ => lib}/hooks/useDebounce.ts | 0 .../shared/{ => lib}/hooks/usePrevious.ts | 0 .../shared/{ => lib}/hooks/useScrollTo.ts | 0 .../shared/{ => lib}/hooks/useTaskQueue.ts | 0 .../shared/{ => lib}/hooks/useToggle.ts | 0 .../{ => lib}/utils/__tests__/address.test.ts | 2 +- .../{ => lib}/utils/__tests__/balance.test.ts | 0 .../{ => lib}/utils/__tests__/strings.test.ts | 0 .../utils/__tests__/substrate.test.ts | 2 +- .../shared/{ => lib}/utils/address.ts | 2 +- src/renderer/shared/{ => lib}/utils/assets.ts | 2 +- .../shared/{ => lib}/utils/balance.ts | 4 +- .../shared/{ => lib}/utils/bignumber.ts | 0 .../shared/{ => lib}/utils/browser.ts | 0 .../shared/{ => lib}/utils/constants.ts | 0 .../shared/{ => lib}/utils/functions.ts | 0 src/renderer/shared/lib/utils/index.ts | 12 ++ .../shared/{ => lib}/utils/strings.ts | 0 .../shared/{ => lib}/utils/substrate.ts | 2 +- src/renderer/shared/{ => lib}/utils/time.ts | 0 .../shared/{ => lib}/utils/twMerge.ts | 6 +- .../shared/{ => lib}/utils/validation.ts | 0 .../ui}/Accordion/Accordion.stories.tsx | 0 .../ui}/Accordion/Accordion.tsx | 4 +- .../ui}/Alert/Alert.stories.tsx | 0 .../ui}/Alert/Alert.test.tsx | 5 +- .../ui-redesign => shared/ui}/Alert/Alert.tsx | 5 +- .../ui}/Alert/AlertItem.tsx | 2 +- .../ui}/Alert/common/constants.ts | 2 +- .../ui}/Alert/common/types.ts | 0 .../ui}/Animation/Animation.tsx | 0 .../ui}/Animation/Data.ts | 0 .../ui}/Buttons/Button/Button.stories.tsx | 2 +- .../ui}/Buttons/Button/Button.test.tsx | 0 .../ui}/Buttons/Button/Button.tsx | 4 +- .../Buttons/ButtonBack/ButtonBack.stories.tsx | 0 .../Buttons/ButtonBack/ButtonBack.test.tsx | 0 .../ui}/Buttons/ButtonBack/ButtonBack.tsx | 2 +- .../Buttons/ButtonLink/ButtonLink.stories.tsx | 2 +- .../Buttons/ButtonLink/ButtonLink.test.tsx | 0 .../ui}/Buttons/ButtonLink/ButtonLink.tsx | 2 +- .../ui}/Buttons/IconButton/IconButton.css | 0 .../Buttons/IconButton/IconButton.stories.tsx | 0 .../Buttons/IconButton/IconButton.test.tsx | 0 .../ui}/Buttons/IconButton/IconButton.tsx | 4 +- .../ui}/Buttons/common/constants.ts | 0 .../ui}/Buttons/common/types.ts | 0 .../ui}/Checkbox/Checkbox.css | 0 .../ui}/Checkbox/Checkbox.stories.tsx | 0 .../ui}/Checkbox/Checkbox.test.tsx | 0 .../ui}/Checkbox/Checkbox.tsx | 4 +- .../ui}/Counter/Counter.stories.tsx | 0 .../ui}/Counter/Counter.test.tsx | 2 +- .../ui}/Counter/Counter.tsx | 4 +- .../ui}/Counter/common/constants.ts | 0 .../ui}/Counter/common/types.ts | 0 .../ui}/DetailRow/DetailRow.tsx | 2 +- .../Dropdowns/Combobox/Combobox.stories.tsx | 2 +- .../ui}/Dropdowns/Combobox/Combobox.test.tsx | 0 .../ui}/Dropdowns/Combobox/Combobox.tsx | 7 +- .../DropdownButton/DropdownButton.stories.tsx | 0 .../DropdownButton/DropdownButton.test.tsx | 2 +- .../DropdownButton/DropdownButton.tsx | 7 +- .../MultiSelect/MultiSelect.stories.tsx | 2 +- .../MultiSelect/MultiSelect.test.tsx | 2 +- .../ui}/Dropdowns/MultiSelect/MultiSelect.tsx | 7 +- .../ui}/Dropdowns/Select/Select.stories.tsx | 2 +- .../ui}/Dropdowns/Select/Select.test.tsx | 2 +- .../ui}/Dropdowns/Select/Select.tsx | 7 +- .../ui}/Dropdowns/common/constants.ts | 2 +- .../ui}/Dropdowns/common/types.ts | 0 .../ui/Duration/Duration.stories.tsx | 0 .../ui/Duration/Duration.test.tsx | 2 +- .../ui/Duration/Duration.tsx | 4 +- .../ui/Icon/Icon.stories.tsx | 0 .../ui/Icon/Icon.test.tsx | 0 .../{components => shared}/ui/Icon/Icon.tsx | 2 +- .../ui/Icon/data/aesthetics.tsx | 0 .../ui/Icon/data/arrow.tsx | 0 .../ui/Icon/data/chevron.tsx | 0 .../ui/Icon/data/currency.tsx | 0 .../ui/Icon/data/explorer.tsx | 0 .../ui/Icon/data/flag.tsx | 0 .../ui/Icon/data/functionals.tsx | 0 .../ui/Icon/data/index.ts | 2 +- .../ui/Icon/data/misc.tsx | 0 .../ui/Icon/data/mst.tsx | 0 .../ui/Icon/data/navigation.tsx | 0 .../ui/Icon/data/social.tsx | 0 .../ui/Icon/data/staking.tsx | 0 .../ui/Icon/data/walletType.tsx | 0 .../ui/Identicon/Identicon.stories.tsx | 2 +- .../ui/Identicon/Identicon.test.tsx | 2 +- .../ui/Identicon/Identicon.tsx | 3 +- .../ui}/InfoLink/InfoLink.stories.tsx | 0 .../ui}/InfoLink/InfoLink.test.tsx | 0 .../ui}/InfoLink/InfoLink.tsx | 6 +- .../ui}/InputHint/InputHint.stories.tsx | 0 .../ui}/InputHint/InputHint.test.tsx | 0 .../ui}/InputHint/InputHint.tsx | 6 +- .../ui}/InputHint/contants.ts | 0 .../ui}/Inputs/AmountInput/AmountInput.tsx | 15 +- .../ui}/Inputs/Input/Input.stories.tsx | 2 +- .../ui}/Inputs/Input/Input.test.tsx | 0 .../ui}/Inputs/Input/Input.tsx | 2 +- .../Inputs/InputArea/InputArea.stories.tsx | 0 .../ui/Inputs/InputArea/InputArea.test.tsx | 0 .../ui}/Inputs/InputArea/InputArea.tsx | 2 +- .../Inputs/InputFile/InputFile.stories.tsx | 0 .../ui}/Inputs/InputFile/InputFile.test.tsx | 0 .../ui}/Inputs/InputFile/InputFile.tsx | 8 +- .../PasswordInput/PasswordInput.stories.tsx | 0 .../PasswordInput/PasswordInput.test.tsx | 2 +- .../Inputs/PasswordInput/PasswordInput.tsx | 6 +- .../ui}/Inputs/SearchInput/SearchInput.tsx | 5 +- .../ui}/Inputs/common/styles.ts | 0 .../ui}/Inputs/common/types.ts | 0 .../ui}/LabelHelpbox/LabelHelpBox.stories.tsx | 0 .../ui}/LabelHelpbox/LabelHelpBox.test.tsx | 0 .../ui}/LabelHelpbox/LabelHelpBox.tsx | 5 +- .../LanguageSwitcher.stories.tsx | 0 .../LanguageSwitcher.test.tsx | 0 .../ui/LanguageSwitcher/LanguageSwitcher.tsx | 2 +- .../ui/Loader/Loader.stories.tsx | 0 .../ui/Loader/Loader.test.tsx | 0 .../ui/Loader/Loader.tsx | 4 +- .../Modals/BaseModal/BaseModal.stories.tsx | 0 .../ui}/Modals/BaseModal/BaseModal.test.tsx | 0 .../ui}/Modals/BaseModal/BaseModal.tsx | 8 +- .../ConfirmModal/ConfirmModal.stories.tsx | 0 .../Modals/ConfirmModal/ConfirmModal.test.tsx | 0 .../ui}/Modals/ConfirmModal/ConfirmModal.tsx | 4 +- .../ui}/Modals/common/ModalBackdrop.tsx | 0 .../ui}/Modals/common/ModalTransition.tsx | 0 .../ui}/Modals/common/index.ts | 0 .../ui}/Plate/Plate.stories.tsx | 0 .../ui}/Plate/Plate.test.tsx | 0 .../ui-redesign => shared/ui}/Plate/Plate.tsx | 2 +- .../ui}/PopoverLink/PopoverLink.stories.tsx | 2 +- .../ui}/PopoverLink/PopoverLink.test.tsx | 0 .../ui}/PopoverLink/PopoverLink.tsx | 6 +- .../InfoPopover/InfoPopover.stories.tsx | 0 .../Popovers/InfoPopover/InfoPopover.test.tsx | 0 .../ui}/Popovers/InfoPopover/InfoPopover.tsx | 4 +- .../ui}/Popovers/MenuPopover/MenuPopover.tsx | 2 +- .../ui}/Popovers/Popover/Popover.stories.tsx | 0 .../ui}/Popovers/Popover/Popover.test.tsx | 2 +- .../ui}/Popovers/Popover/Popover.tsx | 4 +- .../ui}/Popovers/Tooltip/Tooltip.css | 0 .../ui}/Popovers/Tooltip/Tooltip.stories.tsx | 0 .../ui}/Popovers/Tooltip/Tooltip.test.tsx | 0 .../ui}/Popovers/Tooltip/Tooltip.tsx | 4 +- .../ui}/RadioGroup/RadioGroup.css | 0 .../ui}/RadioGroup/RadioGroup.stories.tsx | 0 .../ui}/RadioGroup/RadioGroup.test.tsx | 0 .../ui}/RadioGroup/RadioGroup.tsx | 2 +- .../ui}/RadioGroup/RadioOption.tsx | 4 +- .../ui}/RadioGroup/common/types.ts | 0 .../ui/Shimmering/Shimmering.css | 0 .../ui/Shimmering/Shimmering.stories.tsx | 0 .../ui/Shimmering/Shimmering.tsx | 2 +- .../ui}/StatusLabel/StatusLabel.stories.tsx | 0 .../ui}/StatusLabel/StatusLabel.test.tsx | 2 +- .../ui}/StatusLabel/StatusLabel.tsx | 6 +- .../ui}/StatusLabel/common/constants.ts | 0 .../ui}/StatusLabel/common/types.ts | 0 .../ui/Switch/Switch.stories.tsx | 0 .../ui/Switch/Switch.test.tsx | 0 .../ui/Switch/Switch.tsx | 4 +- .../ui}/Tabs/Tabs.stories.tsx | 0 .../ui}/Tabs/Tabs.test.tsx | 2 +- .../ui-redesign => shared/ui}/Tabs/Tabs.tsx | 4 +- .../ui}/Tabs/common/types.ts | 0 .../ui/Truncate/Truncate.test.tsx | 2 +- .../ui/Truncate/Truncate.tsx | 4 +- .../ui/Truncate/utils.ts | 0 .../ui}/Typography/Typography.stories.tsx | 0 .../ui}/Typography/common/TextBase.ts | 2 +- .../ui}/Typography/common/types.ts | 0 .../ui}/Typography/components/BodyText.tsx | 2 +- .../ui}/Typography/components/CaptionText.tsx | 2 +- .../Typography/components/FootnoteText.tsx | 2 +- .../Typography/components/HeaderTitleText.tsx | 2 +- .../Typography/components/HeadlineText.tsx | 2 +- .../ui}/Typography/components/HelpText.tsx | 2 +- .../ui}/Typography/components/LabelText.tsx | 2 +- .../Typography/components/LargeTitleText.tsx | 2 +- .../Typography/components/SmallTitleText.tsx | 2 +- .../ui}/Typography/components/TitleText.tsx | 2 +- .../ui}/Typography/index.ts | 2 + .../ui-redesign => shared/ui}/index.ts | 28 ++- src/renderer/shared/ui/types.ts | 5 + .../dataVerification.base.test.ts | 2 +- tests/integrations/matrix/matrix.base.test.ts | 4 +- tests/integrations/utils/matrixCreateRoom.ts | 2 +- tests/integrations/utils/matrixLogin.ts | 2 +- 677 files changed, 1952 insertions(+), 5365 deletions(-) rename src/renderer/{ => app}/App.tsx (69%) rename src/renderer/{ => app}/i18n.ts (77%) create mode 100644 src/renderer/app/index.css rename src/renderer/{ => app}/index.html (100%) rename src/renderer/{ => app}/index.tsx (96%) rename src/renderer/{ => app/providers}/context/ConfirmContext/ConfirmContext.test.tsx (93%) rename src/renderer/{ => app/providers}/context/ConfirmContext/ConfirmContext.tsx (92%) create mode 100644 src/renderer/app/providers/context/ConfirmContext/index.ts rename src/renderer/{ => app/providers}/context/GraphqlContext/GraphqlContext.test.tsx (86%) rename src/renderer/{ => app/providers}/context/GraphqlContext/GraphqlContext.tsx (94%) create mode 100644 src/renderer/app/providers/context/GraphqlContext/index.ts rename src/renderer/{ => app/providers}/context/I18nContext/I18nContext.tsx (97%) create mode 100644 src/renderer/app/providers/context/I18nContext/index.ts rename src/renderer/{ => app/providers}/context/MatrixContext/MatrixContext.test.tsx (81%) rename src/renderer/{ => app/providers}/context/MatrixContext/MatrixContext.tsx (94%) create mode 100644 src/renderer/app/providers/context/MatrixContext/index.ts rename src/renderer/{ => app/providers}/context/MultisigChainContext/MultisigChainContext.tsx (90%) create mode 100644 src/renderer/app/providers/context/MultisigChainContext/index.ts rename src/renderer/{ => app/providers}/context/NetworkContext/NetworkContext.test.tsx (91%) rename src/renderer/{ => app/providers}/context/NetworkContext/NetworkContext.tsx (91%) create mode 100644 src/renderer/app/providers/context/NetworkContext/index.ts create mode 100644 src/renderer/app/providers/context/index.ts create mode 100644 src/renderer/app/providers/index.ts create mode 100644 src/renderer/app/providers/routes/index.ts rename src/renderer/{ => app/providers}/routes/paths.ts (94%) rename src/renderer/{routes/index.tsx => app/providers/routes/routesConfig.tsx} (89%) rename src/renderer/{ => app/providers}/routes/utils.test.ts (81%) rename src/renderer/{ => app/providers}/routes/utils.ts (100%) create mode 100644 src/renderer/app/styles/fonts.css rename src/renderer/{ => app/styles}/theme/dark.css (100%) rename src/renderer/{ => app/styles}/theme/default.css (100%) rename src/renderer/{services/network/common => assets}/chains/chains.json (100%) rename src/renderer/{services/network/common => assets}/chains/chains_dev.json (100%) delete mode 100644 src/renderer/components/ui-redesign/Buttons/ButtonLink/ButtonLink.stories.tsx delete mode 100644 src/renderer/components/ui-redesign/Inputs/InputArea/InputArea.test.tsx delete mode 100644 src/renderer/components/ui/Address/ChainAddress.stories.tsx delete mode 100644 src/renderer/components/ui/Address/ChainAddress.test.tsx delete mode 100644 src/renderer/components/ui/Address/ChainAddress.tsx delete mode 100644 src/renderer/components/ui/Balance/Balance.stories.tsx delete mode 100644 src/renderer/components/ui/Balance/Balance.test.tsx delete mode 100644 src/renderer/components/ui/Balance/Balance.tsx delete mode 100644 src/renderer/components/ui/Block/Block.stories.tsx delete mode 100644 src/renderer/components/ui/Block/Block.test.tsx delete mode 100644 src/renderer/components/ui/Block/Block.tsx delete mode 100644 src/renderer/components/ui/Buttons/Button/Button.stories.tsx delete mode 100644 src/renderer/components/ui/Buttons/Button/Button.test.tsx delete mode 100644 src/renderer/components/ui/Buttons/Button/Button.tsx delete mode 100644 src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.stories.tsx delete mode 100644 src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.test.tsx delete mode 100644 src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.tsx delete mode 100644 src/renderer/components/ui/Buttons/ButtonLink/ButtonLink.test.tsx delete mode 100644 src/renderer/components/ui/Buttons/ButtonLink/ButtonLink.tsx delete mode 100644 src/renderer/components/ui/Buttons/common/constants.ts delete mode 100644 src/renderer/components/ui/Buttons/common/types.ts delete mode 100644 src/renderer/components/ui/Carousel/Carousel.stories.tsx delete mode 100644 src/renderer/components/ui/Carousel/Carousel.test.tsx delete mode 100644 src/renderer/components/ui/Carousel/Carousel.tsx delete mode 100644 src/renderer/components/ui/Checkbox/Checkbox.css delete mode 100644 src/renderer/components/ui/Checkbox/Checkbox.stories.tsx delete mode 100644 src/renderer/components/ui/Checkbox/Checkbox.test.tsx delete mode 100644 src/renderer/components/ui/Checkbox/Checkbox.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Combobox/Combobox.stories.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Combobox/Combobox.test.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Combobox/Combobox.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.stories.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.test.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Filter/Filter.stories.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Filter/Filter.test.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Filter/Filter.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Select/Select.stories.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Select/Select.test.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/Select/Select.tsx delete mode 100644 src/renderer/components/ui/Dropdowns/common/constants.ts delete mode 100644 src/renderer/components/ui/Dropdowns/common/types.ts delete mode 100644 src/renderer/components/ui/InfoLink/InfoLink.stories.tsx delete mode 100644 src/renderer/components/ui/InfoLink/InfoLink.test.tsx delete mode 100644 src/renderer/components/ui/InfoLink/InfoLink.tsx delete mode 100644 src/renderer/components/ui/InputHint/InputHint.stories.tsx delete mode 100644 src/renderer/components/ui/InputHint/InputHint.test.tsx delete mode 100644 src/renderer/components/ui/InputHint/InputHint.tsx delete mode 100644 src/renderer/components/ui/Inputs/Input/Input.stories.tsx delete mode 100644 src/renderer/components/ui/Inputs/Input/Input.test.tsx delete mode 100644 src/renderer/components/ui/Inputs/Input/Input.tsx delete mode 100644 src/renderer/components/ui/Inputs/InputArea/InputArea.stories.tsx delete mode 100644 src/renderer/components/ui/Inputs/common/types.ts delete mode 100644 src/renderer/components/ui/Plate/Plate.stories.tsx delete mode 100644 src/renderer/components/ui/Plate/Plate.test.tsx delete mode 100644 src/renderer/components/ui/Plate/Plate.tsx delete mode 100644 src/renderer/components/ui/Stepper/Stepper.stories.tsx delete mode 100644 src/renderer/components/ui/Stepper/Stepper.test.tsx delete mode 100644 src/renderer/components/ui/Stepper/Stepper.tsx delete mode 100644 src/renderer/components/ui/Table/Table.stories.tsx delete mode 100644 src/renderer/components/ui/Table/Table.tsx delete mode 100644 src/renderer/components/ui/Table/TableContext.tsx delete mode 100644 src/renderer/components/ui/Table/TableParts.tsx delete mode 100644 src/renderer/components/ui/Table/common/constants.ts delete mode 100644 src/renderer/components/ui/Table/common/types.ts delete mode 100644 src/renderer/components/ui/Table/common/utils.ts delete mode 100644 src/renderer/components/ui/index.ts delete mode 100644 src/renderer/context/ConfirmContext/index.ts delete mode 100644 src/renderer/context/GraphqlContext/index.ts delete mode 100644 src/renderer/context/I18nContext/index.ts delete mode 100644 src/renderer/context/MatrixContext/index.ts delete mode 100644 src/renderer/context/MultisigChainContext/index.ts delete mode 100644 src/renderer/context/NetworkContext/index.ts create mode 100644 src/renderer/entities/account/index.ts rename src/renderer/{services/account => entities/account/lib}/__tests__/accountService.test.ts (86%) rename src/renderer/{services/account => entities/account/lib}/accountService.ts (95%) rename src/renderer/{services/account => entities/account/lib}/common/types.ts (86%) create mode 100644 src/renderer/entities/account/lib/index.ts rename src/renderer/{components/common/AccountAddress => entities/account/lib}/useAddressInfo.tsx (67%) rename src/renderer/{domain => entities/account/model}/account.ts (89%) rename src/renderer/{components/common => entities/account/ui}/AccountAddress/AccountAddress.stories.tsx (82%) rename src/renderer/{components/common => entities/account/ui}/AccountAddress/AccountAddress.test.tsx (90%) rename src/renderer/{components/common => entities/account/ui}/AccountAddress/AccountAddress.tsx (86%) rename src/renderer/{components/common => entities/account/ui}/AccountsList/AccountsList.tsx (69%) rename src/renderer/{components/common => entities/account/ui}/AddressWithExplorers/AddressWithExplorers.tsx (56%) rename src/renderer/{components/common => entities/account/ui}/AddressWithName/AddressWithName.stories.tsx (82%) rename src/renderer/{components/common => entities/account/ui}/AddressWithName/AddressWithName.test.tsx (90%) rename src/renderer/{components/common => entities/account/ui}/AddressWithName/AddressWithName.tsx (80%) create mode 100644 src/renderer/entities/account/ui/index.ts create mode 100644 src/renderer/entities/asset/index.ts rename src/renderer/{services/balance => entities/asset/lib}/balanceService.ts (97%) rename src/renderer/{services/balance => entities/asset/lib}/common/constants.ts (100%) rename src/renderer/{services/balance => entities/asset/lib}/common/types.ts (84%) rename src/renderer/{services/matrix => entities/asset/lib}/index.ts (59%) rename src/renderer/{domain => entities/asset/model}/asset.ts (100%) rename src/renderer/{domain => entities/asset/model}/balance.ts (85%) rename src/renderer/{components/common/BalanceNew/BalanceNew.stories.tsx => entities/asset/ui/AssetBalance/AssetBalance.stories.tsx} (71%) rename src/renderer/{components/common/BalanceNew/BalanceNew.tsx => entities/asset/ui/AssetBalance/AssetBalance.tsx} (69%) rename src/renderer/{screens/Assets/components => entities/asset/ui}/AssetCard/AssetCard.test.tsx (87%) rename src/renderer/{screens/Assets/components => entities/asset/ui}/AssetCard/AssetCard.tsx (78%) create mode 100644 src/renderer/entities/asset/ui/AssetDetails/AssetDetails.tsx rename src/renderer/{components/ui-redesign => entities/asset/ui}/AssetIcon/AssetIcon.test.tsx (84%) rename src/renderer/{components/ui-redesign => entities/asset/ui}/AssetIcon/AssetIcon.tsx (87%) create mode 100644 src/renderer/entities/asset/ui/index.ts create mode 100644 src/renderer/entities/chain/index.ts rename src/renderer/{services/chainSubscription => entities/chain/lib}/chainSubscriptionService.ts (100%) rename src/renderer/{services/chainSubscription => entities/chain/lib}/common/types.ts (100%) create mode 100644 src/renderer/entities/chain/lib/index.ts rename src/renderer/{domain => entities/chain/model}/chain.ts (85%) rename src/renderer/{components/ui-redesign => entities/chain/ui}/ChainIcon/ChainIcon.test.tsx (85%) rename src/renderer/{components/ui-redesign => entities/chain/ui}/ChainIcon/ChainIcon.tsx (78%) rename src/renderer/{components/ui-redesign/Chain/Chain.stories.tsx => entities/chain/ui/ChainTitle/ChainTitle.stories.tsx} (50%) rename src/renderer/{components/ui-redesign/Chain/Chain.test.tsx => entities/chain/ui/ChainTitle/ChainTitle.test.tsx} (64%) rename src/renderer/{components/ui-redesign/Chain/Chain.tsx => entities/chain/ui/ChainTitle/ChainTitle.tsx} (68%) create mode 100644 src/renderer/entities/chain/ui/index.ts create mode 100644 src/renderer/entities/contact/index.ts rename src/renderer/{services/contact => entities/contact/lib}/common/types.ts (76%) rename src/renderer/{services/contact => entities/contact/lib}/contactService.ts (86%) create mode 100644 src/renderer/entities/contact/lib/index.ts rename src/renderer/{domain => entities/contact/model}/contact.ts (61%) rename src/renderer/{screens/AddressBook/Overview/components => entities/contact/ui}/ContactList/ContactList.tsx (85%) create mode 100644 src/renderer/entities/contact/ui/index.ts create mode 100644 src/renderer/entities/matrix/index.ts rename src/renderer/{services/matrix => entities/matrix/lib}/__tests__/credentialStorage.test.ts (100%) rename src/renderer/{services/matrix => entities/matrix/lib}/__tests__/matrix.test.ts (100%) rename src/renderer/{services/matrix => entities/matrix/lib}/__tests__/secretStorage.test.ts (100%) rename src/renderer/{services/matrix => entities/matrix/lib}/common/constants.ts (100%) rename src/renderer/{services/matrix => entities/matrix/lib}/common/errors.ts (100%) rename src/renderer/{services/matrix => entities/matrix/lib}/common/types.ts (98%) rename src/renderer/{services/matrix => entities/matrix/lib}/credentialStorage.ts (100%) create mode 100644 src/renderer/entities/matrix/lib/index.ts rename src/renderer/{services/matrix => entities/matrix/lib}/matrix.ts (99%) rename src/renderer/{services/matrix => entities/matrix/lib}/secretStorage.ts (100%) create mode 100644 src/renderer/entities/multisig/index.ts create mode 100644 src/renderer/entities/multisig/lib/index.ts rename src/renderer/{services => entities/multisig/lib}/multisigEvent/common/types.ts (89%) rename src/renderer/{services => entities/multisig/lib}/multisigEvent/multisigEventService.ts (94%) rename src/renderer/{services => entities/multisig/lib}/multisigTx/common/consts.ts (100%) rename src/renderer/{services => entities/multisig/lib}/multisigTx/common/types.ts (86%) rename src/renderer/{services => entities/multisig/lib}/multisigTx/common/utils.ts (93%) rename src/renderer/{services => entities/multisig/lib}/multisigTx/multisigTxService.ts (92%) create mode 100644 src/renderer/entities/network/index.ts rename src/renderer/{services/network => entities/network/lib}/__tests__/chainSpecService.test.ts (93%) rename src/renderer/{services/network => entities/network/lib}/__tests__/chainsService.test.ts (100%) rename src/renderer/{services/network => entities/network/lib}/chainSpecService.ts (100%) rename src/renderer/{services/network => entities/network/lib}/chainsService.ts (88%) rename src/renderer/{services/network => entities/network/lib}/common/constants.ts (100%) rename src/renderer/{services/network => entities/network/lib}/common/types.ts (94%) rename src/renderer/{services/network => entities/network/lib}/common/utils.ts (90%) create mode 100644 src/renderer/entities/network/lib/index.ts rename src/renderer/{services/network => entities/network/lib}/networkService.ts (98%) create mode 100644 src/renderer/entities/notification/index.ts rename src/renderer/{services/notification => entities/notification/lib}/common/types.ts (66%) create mode 100644 src/renderer/entities/notification/lib/index.ts rename src/renderer/{services/notification => entities/notification/lib}/notificationService.ts (84%) rename src/renderer/{domain => entities/notification/model}/notification.ts (52%) rename src/renderer/{screens/Notifications/components => entities/notification/ui/NotificationRow}/NotificationRow.tsx (84%) create mode 100644 src/renderer/entities/notification/ui/index.ts create mode 100644 src/renderer/entities/settings/index.ts rename src/renderer/{services/settings => entities/settings/lib}/__tests__/settingsStorage.test.ts (100%) rename src/renderer/{services/settings => entities/settings/lib}/common/constants.ts (100%) rename src/renderer/{services/settings => entities/settings/lib}/common/types.ts (100%) create mode 100644 src/renderer/entities/settings/lib/index.ts rename src/renderer/{services/settings => entities/settings/lib}/settingsStorage.ts (100%) create mode 100644 src/renderer/entities/signatory/index.ts rename src/renderer/{domain => entities/signatory/model}/signatory.ts (66%) rename src/renderer/{components/common => entities/signatory/ui}/SelectableSignatory/SelectableSignatory.tsx (55%) rename src/renderer/{components/common => entities/signatory/ui}/SignatoryCard/SignatoryCard.stories.tsx (80%) rename src/renderer/{components/common => entities/signatory/ui}/SignatoryCard/SignatoryCard.test.tsx (67%) rename src/renderer/{components/common => entities/signatory/ui}/SignatoryCard/SignatoryCard.tsx (73%) create mode 100644 src/renderer/entities/signatory/ui/index.ts create mode 100644 src/renderer/entities/staking/index.ts rename src/renderer/{services/staking => entities/staking/lib}/__tests__/apyCalculator.test.ts (100%) rename src/renderer/{services/staking => entities/staking/lib}/__tests__/stakingDataService.test.ts (100%) rename src/renderer/{services/staking => entities/staking/lib}/__tests__/stakingRewardsService.test.ts (100%) rename src/renderer/{services/staking => entities/staking/lib}/apyCalculator.ts (100%) rename src/renderer/{services/staking => entities/staking/lib}/common/constants.ts (100%) rename src/renderer/{services/staking => entities/staking/lib}/common/types.ts (97%) rename src/renderer/{services/staking => entities/staking/lib}/eraService.ts (100%) create mode 100644 src/renderer/entities/staking/lib/index.ts rename src/renderer/{services/staking => entities/staking/lib}/stakingDataService.ts (100%) rename src/renderer/{services/staking => entities/staking/lib}/stakingRewardsService.ts (89%) rename src/renderer/{services/staking => entities/staking/lib}/validatorsService.ts (100%) rename src/renderer/{domain => entities/staking/model}/stake.ts (100%) create mode 100644 src/renderer/entities/transaction/index.ts rename src/renderer/{services/transaction => entities/transaction/lib}/callDataDecoder.ts (98%) rename src/renderer/{services/transaction => entities/transaction/lib}/common/constants.ts (100%) rename src/renderer/{services/transaction => entities/transaction/lib}/common/types.ts (95%) rename src/renderer/{services/transaction => entities/transaction/lib}/common/utils.ts (93%) create mode 100644 src/renderer/entities/transaction/lib/index.ts rename src/renderer/{services/transaction => entities/transaction/lib}/transactionService.ts (98%) rename src/renderer/{domain => entities/transaction/model}/transaction.ts (96%) rename src/renderer/{components/common => entities/transaction/ui}/Deposit/Deposit.test.tsx (62%) rename src/renderer/{components/common => entities/transaction/ui}/Deposit/Deposit.tsx (59%) rename src/renderer/{components/common => entities/transaction/ui}/DepositWithLabel/DepositWithLabel.test.tsx (74%) rename src/renderer/{components/common => entities/transaction/ui}/DepositWithLabel/DepositWithLabel.tsx (74%) rename src/renderer/{components/common => entities/transaction/ui}/Fee/Fee.test.tsx (77%) rename src/renderer/{components/common => entities/transaction/ui}/Fee/Fee.tsx (69%) rename src/renderer/{components/common => entities/transaction/ui}/OperationResult/OperationResult.stories.tsx (100%) rename src/renderer/{components/common => entities/transaction/ui}/OperationResult/OperationResult.test.tsx (87%) rename src/renderer/{components/common => entities/transaction/ui}/OperationResult/OperationResult.tsx (85%) rename src/renderer/{components/common => entities/transaction/ui}/OperationResult/common/constants.ts (79%) rename src/renderer/{components/common => entities/transaction/ui}/OperationResult/common/types.ts (100%) create mode 100644 src/renderer/entities/transaction/ui/index.ts create mode 100644 src/renderer/entities/wallet/index.ts rename src/renderer/{services/wallet => entities/wallet/lib}/common/types.ts (76%) create mode 100644 src/renderer/entities/wallet/lib/index.ts rename src/renderer/{services/wallet => entities/wallet/lib}/walletService.ts (86%) rename src/renderer/{domain => entities/wallet/model}/wallet.ts (74%) delete mode 100644 src/renderer/index.css rename src/renderer/{screens => pages}/AddressBook/ManageContact/ManageContact.test.tsx (87%) rename src/renderer/{screens => pages}/AddressBook/ManageContact/ManageContact.tsx (77%) rename src/renderer/{screens => pages}/AddressBook/Overview/Overview.test.tsx (87%) rename src/renderer/{screens => pages}/AddressBook/Overview/Overview.tsx (83%) rename src/renderer/{screens => pages}/AddressBook/Overview/components/EmptyState/EmptyContacts.test.tsx (81%) rename src/renderer/{screens => pages}/AddressBook/Overview/components/EmptyState/EmptyContacts.tsx (80%) rename src/renderer/{screens => pages}/AddressBook/Overview/components/EmptyState/EmptySearch.test.tsx (74%) rename src/renderer/{screens => pages}/AddressBook/Overview/components/EmptyState/EmptySearch.tsx (69%) rename src/renderer/{screens => pages}/AddressBook/Overview/components/index.ts (57%) rename src/renderer/{screens => pages}/AddressBook/index.ts (100%) rename src/renderer/{screens => pages}/Assets/Assets.test.tsx (73%) rename src/renderer/{screens => pages}/Assets/Assets.tsx (88%) rename src/renderer/{screens => pages}/Assets/common/utils.ts (90%) rename src/renderer/{screens => pages}/Assets/components/AssetsFilter/AssetsFilters.tsx (85%) rename src/renderer/{screens => pages}/Assets/components/Modals/ReceiveModal/ReceiveModal.test.tsx (66%) rename src/renderer/{screens => pages}/Assets/components/Modals/ReceiveModal/ReceiveModal.tsx (80%) rename src/renderer/{screens => pages}/Assets/components/Modals/SelectShardModal/SelectShardModal.tsx (93%) rename src/renderer/{screens => pages}/Assets/components/NetworkAssets/NetworkAssets.test.tsx (87%) rename src/renderer/{screens => pages}/Assets/components/NetworkAssets/NetworkAssets.tsx (81%) rename src/renderer/{screens => pages}/Assets/components/index.ts (100%) rename src/renderer/{screens => pages}/Notifications/Notifications.tsx (82%) rename src/renderer/{screens => pages}/Notifications/common/utils.ts (100%) rename src/renderer/{screens => pages}/Notifications/components/EmptyNotifications.tsx (68%) rename src/renderer/{screens => pages}/Onboarding/FinalStep/FinalStep.test.tsx (85%) rename src/renderer/{screens => pages}/Onboarding/FinalStep/FinalStep.tsx (92%) rename src/renderer/{screens => pages}/Onboarding/Vault/KeyQrReader/KeyQrReader.test.tsx (91%) rename src/renderer/{screens => pages}/Onboarding/Vault/KeyQrReader/KeyQrReader.tsx (95%) rename src/renderer/{screens => pages}/Onboarding/Vault/ManageStep/ManageStep.tsx (93%) rename src/renderer/{screens => pages}/Onboarding/Vault/ManageStepSingle/ManageStepSingle.tsx (89%) rename src/renderer/{screens => pages}/Onboarding/Vault/ScanStep/ScanStep.tsx (94%) rename src/renderer/{screens => pages}/Onboarding/Vault/Vault.tsx (96%) rename src/renderer/{screens => pages}/Onboarding/WatchOnly/EmptyState.tsx (71%) rename src/renderer/{screens => pages}/Onboarding/WatchOnly/WatchOnly.tsx (88%) rename src/renderer/{screens => pages}/Onboarding/Welcome/PrivacyPolicy.tsx (82%) rename src/renderer/{screens => pages}/Onboarding/Welcome/Welcome.tsx (88%) rename src/renderer/{screens => pages}/Onboarding/Welcome/WelcomeCard.tsx (83%) create mode 100644 src/renderer/pages/Onboarding/index.ts rename src/renderer/{screens => pages}/Operations/Operations.test.tsx (58%) rename src/renderer/{screens => pages}/Operations/Operations.tsx (82%) rename src/renderer/{screens => pages}/Operations/common/constants.ts (100%) rename src/renderer/{screens => pages}/Operations/common/utils.ts (94%) rename src/renderer/{screens => pages}/Operations/components/ActionSteps/Confirmation.tsx (71%) rename src/renderer/{screens => pages}/Operations/components/ActionSteps/Submit.tsx (85%) rename src/renderer/{screens => pages}/Operations/components/Details.tsx (90%) rename src/renderer/{screens => pages}/Operations/components/EmptyState/EmptyOperations.test.tsx (76%) rename src/renderer/{screens => pages}/Operations/components/EmptyState/EmptyOperations.tsx (77%) rename src/renderer/{screens => pages}/Operations/components/Filters.tsx (92%) rename src/renderer/{screens => pages}/Operations/components/Log.tsx (85%) rename src/renderer/{screens => pages}/Operations/components/Operation.tsx (78%) rename src/renderer/{screens => pages}/Operations/components/OperationFullInfo.tsx (79%) rename src/renderer/{screens => pages}/Operations/components/OperationModalTitle.tsx (69%) rename src/renderer/{screens => pages}/Operations/components/OperationStatus.tsx (90%) rename src/renderer/{screens => pages}/Operations/components/ShortTransactionInfo.test.tsx (81%) create mode 100644 src/renderer/pages/Operations/components/TransactionAmount.tsx rename src/renderer/{screens => pages}/Operations/components/TransactionTitle/TransactionTitle.test.tsx (77%) rename src/renderer/{screens => pages}/Operations/components/TransactionTitle/TransactionTitle.tsx (77%) rename src/renderer/{screens => pages}/Operations/components/modals/ApproveTx.tsx (84%) rename src/renderer/{screens => pages}/Operations/components/modals/CallDataModal.tsx (85%) rename src/renderer/{screens => pages}/Operations/components/modals/RejectReasonModal.tsx (88%) rename src/renderer/{screens => pages}/Operations/components/modals/RejectTx.tsx (85%) rename src/renderer/{screens => pages}/Operations/components/modals/SignatorySelectModal.tsx (73%) rename src/renderer/{screens => pages}/Settings/Networks/Networks.test.tsx (93%) rename src/renderer/{screens => pages}/Settings/Networks/Networks.tsx (91%) rename src/renderer/{screens => pages}/Settings/Networks/components/CustomRpcModal/CustomRpcModal.test.tsx (95%) rename src/renderer/{screens => pages}/Settings/Networks/components/CustomRpcModal/CustomRpcModal.tsx (93%) rename src/renderer/{screens => pages}/Settings/Networks/components/NetworkItem/NetworkItem.css (100%) rename src/renderer/{screens => pages}/Settings/Networks/components/NetworkItem/NetworkItem.test.tsx (92%) rename src/renderer/{screens => pages}/Settings/Networks/components/NetworkItem/NetworkItem.tsx (86%) rename src/renderer/{screens => pages}/Settings/Networks/components/NetworkList/NetworkList.test.tsx (96%) rename src/renderer/{screens => pages}/Settings/Networks/components/NetworkList/NetworkList.tsx (93%) rename src/renderer/{screens => pages}/Settings/Networks/components/NetworkSelector/NetworkSelector.test.tsx (94%) rename src/renderer/{screens => pages}/Settings/Networks/components/NetworkSelector/NetworkSelector.tsx (89%) rename src/renderer/{screens => pages}/Settings/Networks/components/index.ts (100%) rename src/renderer/{screens => pages}/Settings/Overview/Overview.test.tsx (95%) rename src/renderer/{screens => pages}/Settings/Overview/Overview.tsx (92%) rename src/renderer/{screens => pages}/Settings/Overview/components/GeneralActions/GeneralActions.test.tsx (85%) rename src/renderer/{screens => pages}/Settings/Overview/components/GeneralActions/GeneralActions.tsx (85%) rename src/renderer/{screens => pages}/Settings/Overview/components/MatrixAction/MatrixAction.test.tsx (90%) rename src/renderer/{screens => pages}/Settings/Overview/components/MatrixAction/MatrixAction.tsx (79%) rename src/renderer/{screens => pages}/Settings/Overview/components/SocialLinks/SocialLinks.test.tsx (94%) rename src/renderer/{screens => pages}/Settings/Overview/components/SocialLinks/SocialLinks.tsx (86%) rename src/renderer/{screens => pages}/Settings/Overview/components/Version/Version.test.tsx (90%) rename src/renderer/{screens => pages}/Settings/Overview/components/Version/Version.tsx (64%) rename src/renderer/{screens => pages}/Settings/Overview/components/index.ts (100%) rename src/renderer/{screens => pages}/Settings/index.ts (100%) rename src/renderer/{screens => pages}/Staking/Operations/Bond/Bond.test.tsx (91%) rename src/renderer/{screens => pages}/Staking/Operations/Bond/Bond.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/Bond/InitOperation/InitOperation.test.tsx (77%) rename src/renderer/{screens => pages}/Staking/Operations/Bond/InitOperation/InitOperation.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/ChangeValidators/ChangeValidators.test.tsx (91%) rename src/renderer/{screens => pages}/Staking/Operations/ChangeValidators/ChangeValidators.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/ChangeValidators/InitOperation/InitOperation.test.tsx (75%) rename src/renderer/{screens => pages}/Staking/Operations/ChangeValidators/InitOperation/InitOperation.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/Destination/Destination.test.tsx (92%) rename src/renderer/{screens => pages}/Staking/Operations/Destination/Destination.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/Destination/InitOperation/InitOperation.test.tsx (76%) rename src/renderer/{screens => pages}/Staking/Operations/Destination/InitOperation/InitOperation.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/Redeem/InitOperation/InitOperation.test.tsx (76%) rename src/renderer/{screens => pages}/Staking/Operations/Redeem/InitOperation/InitOperation.tsx (89%) rename src/renderer/{screens => pages}/Staking/Operations/Redeem/Redeem.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/Restake/InitOperation/InitOperation.test.tsx (76%) rename src/renderer/{screens => pages}/Staking/Operations/Restake/InitOperation/InitOperation.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/Restake/Restake.test.tsx (89%) rename src/renderer/{screens => pages}/Staking/Operations/Restake/Restake.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/StakeMore/InitOperation/InitOperation.test.tsx (77%) rename src/renderer/{screens => pages}/Staking/Operations/StakeMore/InitOperation/InitOperation.tsx (91%) rename src/renderer/{screens => pages}/Staking/Operations/StakeMore/StakeMore.test.tsx (91%) rename src/renderer/{screens => pages}/Staking/Operations/StakeMore/StakeMore.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx (78%) rename src/renderer/{screens => pages}/Staking/Operations/Unstake/InitOperation/InitOperation.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/Unstake/Unstake.test.tsx (91%) rename src/renderer/{screens => pages}/Staking/Operations/Unstake/Unstake.tsx (90%) rename src/renderer/{screens => pages}/Staking/Operations/common/types.ts (65%) rename src/renderer/{screens => pages}/Staking/Operations/common/utils.tsx (91%) rename src/renderer/{screens => pages}/Staking/Operations/components/Confirmation/Confirmation.test.tsx (70%) rename src/renderer/{screens => pages}/Staking/Operations/components/Confirmation/Confirmation.tsx (88%) rename src/renderer/{screens => pages}/Staking/Operations/components/Modals/AccountsModal/AccountsModal.test.tsx (81%) rename src/renderer/{screens => pages}/Staking/Operations/components/Modals/AccountsModal/AccountsModal.tsx (72%) rename src/renderer/{screens => pages}/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.test.tsx (80%) rename src/renderer/{screens => pages}/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.tsx (73%) rename src/renderer/{screens => pages}/Staking/Operations/components/NoAsset/NoAsset.tsx (79%) rename src/renderer/{screens => pages}/Staking/Operations/components/OperationForm/OperationFooter.tsx (87%) rename src/renderer/{screens => pages}/Staking/Operations/components/OperationForm/OperationForm.tsx (92%) rename src/renderer/{screens => pages}/Staking/Operations/components/Signing/Signing.tsx (93%) rename src/renderer/{screens => pages}/Staking/Operations/components/Submit/Submit.tsx (84%) rename src/renderer/{screens => pages}/Staking/Operations/components/Validators/Validators.test.tsx (84%) rename src/renderer/{screens => pages}/Staking/Operations/components/Validators/Validators.tsx (88%) rename src/renderer/{screens => pages}/Staking/Operations/components/index.ts (100%) rename src/renderer/{screens => pages}/Staking/Overview/Overview.test.tsx (79%) rename src/renderer/{screens => pages}/Staking/Overview/Overview.tsx (87%) rename src/renderer/{screens => pages}/Staking/Overview/components/AboutStaking/AboutStaking.test.tsx (86%) rename src/renderer/{screens => pages}/Staking/Overview/components/AboutStaking/AboutStaking.tsx (88%) rename src/renderer/{screens => pages}/Staking/Overview/components/Actions/Actions.test.tsx (92%) rename src/renderer/{screens => pages}/Staking/Overview/components/Actions/Actions.tsx (92%) rename src/renderer/{screens => pages}/Staking/Overview/components/EmptyState/InactiveChain.test.tsx (82%) rename src/renderer/{screens => pages}/Staking/Overview/components/EmptyState/InactiveChain.tsx (82%) rename src/renderer/{screens => pages}/Staking/Overview/components/EmptyState/NoValidators.test.tsx (77%) rename src/renderer/{screens => pages}/Staking/Overview/components/EmptyState/NoValidators.tsx (68%) rename src/renderer/{screens => pages}/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx (90%) rename src/renderer/{screens => pages}/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx (80%) rename src/renderer/{screens => pages}/Staking/Overview/components/NominatorsList/NominatorsList.test.tsx (87%) rename src/renderer/{screens => pages}/Staking/Overview/components/NominatorsList/NominatorsList.tsx (85%) rename src/renderer/{screens => pages}/Staking/Overview/components/TimeToEra/TimeToEra.tsx (86%) rename src/renderer/{screens => pages}/Staking/Overview/components/UnstakingDuration/UnstakingDuration.tsx (81%) rename src/renderer/{screens => pages}/Staking/Overview/components/ValidatorsModal/ValidatorsModal.test.tsx (88%) rename src/renderer/{screens => pages}/Staking/Overview/components/ValidatorsModal/ValidatorsModal.tsx (87%) rename src/renderer/{screens => pages}/Staking/Overview/components/index.ts (100%) rename src/renderer/{screens => pages}/Staking/index.ts (100%) rename src/renderer/{screens => pages}/Transfer/Transfer.tsx (91%) rename src/renderer/{screens => pages}/Transfer/common/constants.ts (100%) rename src/renderer/{screens => pages}/Transfer/common/utils.tsx (81%) rename src/renderer/{screens => pages}/Transfer/components/ActionSteps/Confirmation.tsx (78%) rename src/renderer/{screens => pages}/Transfer/components/ActionSteps/InitOperation.tsx (90%) rename src/renderer/{screens => pages}/Transfer/components/ActionSteps/Signing.tsx (88%) rename src/renderer/{screens => pages}/Transfer/components/ActionSteps/Submit.tsx (84%) rename src/renderer/{screens => pages}/Transfer/components/ActionSteps/index.ts (100%) rename src/renderer/{screens => pages}/Transfer/components/Details.tsx (81%) rename src/renderer/{screens => pages}/Transfer/components/TransferForm.tsx (93%) create mode 100644 src/renderer/pages/index.ts delete mode 100644 src/renderer/screens/Assets/components/AssetDetails/AssetDetails.tsx delete mode 100644 src/renderer/screens/Onboarding/index.ts delete mode 100644 src/renderer/screens/Operations/components/TransactionAmount.tsx delete mode 100644 src/renderer/screens/index.ts rename src/renderer/{services => shared/api}/storage/__tests__/balanceStorage.test.ts (100%) rename src/renderer/{services => shared/api}/storage/__tests__/connectionStorage.test.ts (97%) rename src/renderer/{services => shared/api}/storage/__tests__/storage.test.ts (100%) rename src/renderer/{services => shared/api}/storage/accountStorage.ts (92%) rename src/renderer/{services => shared/api}/storage/balanceStorage.ts (95%) rename src/renderer/{services => shared/api}/storage/common/types.ts (91%) rename src/renderer/{services => shared/api}/storage/common/upgrades.ts (100%) rename src/renderer/{services => shared/api}/storage/connectionStorage.ts (100%) rename src/renderer/{services => shared/api}/storage/contactStorage.ts (90%) rename src/renderer/{services => shared/api}/storage/index.ts (100%) rename src/renderer/{services => shared/api}/storage/multisigEventStorage.ts (96%) rename src/renderer/{services => shared/api}/storage/notificationStorage.ts (84%) rename src/renderer/{services => shared/api}/storage/storage.ts (100%) rename src/renderer/{services => shared/api}/storage/transactionStorage.ts (95%) rename src/renderer/{services => shared/api}/storage/walletStorage.ts (90%) rename src/renderer/shared/{ => lib}/hooks/__tests__/useClickOutside.test.ts (100%) rename src/renderer/shared/{ => lib}/hooks/__tests__/useCountdown.test.ts (87%) rename src/renderer/shared/{ => lib}/hooks/__tests__/useTaskQueue.test.ts (100%) rename src/renderer/shared/{ => lib}/hooks/__tests__/useToggle.test.ts (100%) rename src/renderer/shared/{ => lib}/hooks/index.ts (100%) rename src/renderer/shared/{ => lib}/hooks/useClickOutside.ts (100%) rename src/renderer/shared/{ => lib}/hooks/useCountdown.ts (86%) rename src/renderer/shared/{ => lib}/hooks/useDebounce.ts (100%) rename src/renderer/shared/{ => lib}/hooks/usePrevious.ts (100%) rename src/renderer/shared/{ => lib}/hooks/useScrollTo.ts (100%) rename src/renderer/shared/{ => lib}/hooks/useTaskQueue.ts (100%) rename src/renderer/shared/{ => lib}/hooks/useToggle.ts (100%) rename src/renderer/shared/{ => lib}/utils/__tests__/address.test.ts (99%) rename src/renderer/shared/{ => lib}/utils/__tests__/balance.test.ts (100%) rename src/renderer/shared/{ => lib}/utils/__tests__/strings.test.ts (100%) rename src/renderer/shared/{ => lib}/utils/__tests__/substrate.test.ts (93%) rename src/renderer/shared/{ => lib}/utils/address.ts (98%) rename src/renderer/shared/{ => lib}/utils/assets.ts (96%) rename src/renderer/shared/{ => lib}/utils/balance.ts (97%) rename src/renderer/shared/{ => lib}/utils/bignumber.ts (100%) rename src/renderer/shared/{ => lib}/utils/browser.ts (100%) rename src/renderer/shared/{ => lib}/utils/constants.ts (100%) rename src/renderer/shared/{ => lib}/utils/functions.ts (100%) create mode 100644 src/renderer/shared/lib/utils/index.ts rename src/renderer/shared/{ => lib}/utils/strings.ts (100%) rename src/renderer/shared/{ => lib}/utils/substrate.ts (97%) rename src/renderer/shared/{ => lib}/utils/time.ts (100%) rename src/renderer/shared/{ => lib}/utils/twMerge.ts (76%) rename src/renderer/shared/{ => lib}/utils/validation.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Accordion/Accordion.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Accordion/Accordion.tsx (94%) rename src/renderer/{components/ui-redesign => shared/ui}/Alert/Alert.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Alert/Alert.test.tsx (90%) rename src/renderer/{components/ui-redesign => shared/ui}/Alert/Alert.tsx (86%) rename src/renderer/{components/ui-redesign => shared/ui}/Alert/AlertItem.tsx (86%) rename src/renderer/{components/ui-redesign => shared/ui}/Alert/common/constants.ts (89%) rename src/renderer/{components/ui-redesign => shared/ui}/Alert/common/types.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Animation/Animation.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Animation/Data.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/Button/Button.stories.tsx (97%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/Button/Button.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/Button/Button.tsx (94%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/ButtonBack/ButtonBack.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/ButtonBack/ButtonBack.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/ButtonBack/ButtonBack.tsx (86%) rename src/renderer/{components => shared}/ui/Buttons/ButtonLink/ButtonLink.stories.tsx (97%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/ButtonLink/ButtonLink.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/ButtonLink/ButtonLink.tsx (96%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/IconButton/IconButton.css (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/IconButton/IconButton.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/IconButton/IconButton.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/IconButton/IconButton.tsx (89%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/common/constants.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Buttons/common/types.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Checkbox/Checkbox.css (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Checkbox/Checkbox.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Checkbox/Checkbox.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Checkbox/Checkbox.tsx (94%) rename src/renderer/{components/ui-redesign => shared/ui}/Counter/Counter.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Counter/Counter.test.tsx (95%) rename src/renderer/{components/ui-redesign => shared/ui}/Counter/Counter.tsx (83%) rename src/renderer/{components/ui-redesign => shared/ui}/Counter/common/constants.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Counter/common/types.ts (100%) rename src/renderer/{components/common => shared/ui}/DetailRow/DetailRow.tsx (92%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/Combobox/Combobox.stories.tsx (96%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/Combobox/Combobox.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/Combobox/Combobox.tsx (90%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/DropdownButton/DropdownButton.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/DropdownButton/DropdownButton.test.tsx (95%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/DropdownButton/DropdownButton.tsx (91%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/MultiSelect/MultiSelect.stories.tsx (97%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/MultiSelect/MultiSelect.test.tsx (95%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/MultiSelect/MultiSelect.tsx (94%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/Select/Select.stories.tsx (98%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/Select/Select.test.tsx (95%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/Select/Select.tsx (94%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/common/constants.ts (97%) rename src/renderer/{components/ui-redesign => shared/ui}/Dropdowns/common/types.ts (100%) rename src/renderer/{components => shared}/ui/Duration/Duration.stories.tsx (100%) rename src/renderer/{components => shared}/ui/Duration/Duration.test.tsx (88%) rename src/renderer/{components => shared}/ui/Duration/Duration.tsx (88%) rename src/renderer/{components => shared}/ui/Icon/Icon.stories.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/Icon.test.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/Icon.tsx (95%) rename src/renderer/{components => shared}/ui/Icon/data/aesthetics.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/arrow.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/chevron.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/currency.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/explorer.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/flag.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/functionals.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/index.ts (94%) rename src/renderer/{components => shared}/ui/Icon/data/misc.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/mst.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/navigation.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/social.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/staking.tsx (100%) rename src/renderer/{components => shared}/ui/Icon/data/walletType.tsx (100%) rename src/renderer/{components => shared}/ui/Identicon/Identicon.stories.tsx (91%) rename src/renderer/{components => shared}/ui/Identicon/Identicon.test.tsx (92%) rename src/renderer/{components => shared}/ui/Identicon/Identicon.tsx (95%) rename src/renderer/{components/ui-redesign => shared/ui}/InfoLink/InfoLink.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/InfoLink/InfoLink.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/InfoLink/InfoLink.tsx (83%) rename src/renderer/{components/ui-redesign => shared/ui}/InputHint/InputHint.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/InputHint/InputHint.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/InputHint/InputHint.tsx (63%) rename src/renderer/{components/ui-redesign => shared/ui}/InputHint/contants.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/AmountInput/AmountInput.tsx (78%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/Input/Input.stories.tsx (95%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/Input/Input.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/Input/Input.tsx (97%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/InputArea/InputArea.stories.tsx (100%) rename src/renderer/{components => shared}/ui/Inputs/InputArea/InputArea.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/InputArea/InputArea.tsx (95%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/InputFile/InputFile.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/InputFile/InputFile.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/InputFile/InputFile.tsx (87%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/PasswordInput/PasswordInput.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/PasswordInput/PasswordInput.test.tsx (95%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/PasswordInput/PasswordInput.tsx (84%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/SearchInput/SearchInput.tsx (81%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/common/styles.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Inputs/common/types.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/LabelHelpbox/LabelHelpBox.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/LabelHelpbox/LabelHelpBox.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/LabelHelpbox/LabelHelpBox.tsx (79%) rename src/renderer/{components => shared}/ui/LanguageSwitcher/LanguageSwitcher.stories.tsx (100%) rename src/renderer/{components => shared}/ui/LanguageSwitcher/LanguageSwitcher.test.tsx (100%) rename src/renderer/{components => shared}/ui/LanguageSwitcher/LanguageSwitcher.tsx (97%) rename src/renderer/{components => shared}/ui/Loader/Loader.stories.tsx (100%) rename src/renderer/{components => shared}/ui/Loader/Loader.test.tsx (100%) rename src/renderer/{components => shared}/ui/Loader/Loader.tsx (78%) rename src/renderer/{components/ui-redesign => shared/ui}/Modals/BaseModal/BaseModal.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Modals/BaseModal/BaseModal.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Modals/BaseModal/BaseModal.tsx (88%) rename src/renderer/{components/ui-redesign => shared/ui}/Modals/ConfirmModal/ConfirmModal.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Modals/ConfirmModal/ConfirmModal.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Modals/ConfirmModal/ConfirmModal.tsx (89%) rename src/renderer/{components/ui-redesign => shared/ui}/Modals/common/ModalBackdrop.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Modals/common/ModalTransition.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Modals/common/index.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Plate/Plate.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Plate/Plate.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Plate/Plate.tsx (85%) rename src/renderer/{components/ui-redesign => shared/ui}/PopoverLink/PopoverLink.stories.tsx (87%) rename src/renderer/{components/ui-redesign => shared/ui}/PopoverLink/PopoverLink.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/PopoverLink/PopoverLink.tsx (79%) rename src/renderer/{components/ui-redesign => shared/ui}/Popovers/InfoPopover/InfoPopover.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Popovers/InfoPopover/InfoPopover.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Popovers/InfoPopover/InfoPopover.tsx (93%) rename src/renderer/{components/ui-redesign => shared/ui}/Popovers/MenuPopover/MenuPopover.tsx (96%) rename src/renderer/{components/ui-redesign => shared/ui}/Popovers/Popover/Popover.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Popovers/Popover/Popover.test.tsx (97%) rename src/renderer/{components/ui-redesign => shared/ui}/Popovers/Popover/Popover.tsx (95%) rename src/renderer/{components/ui-redesign => shared/ui}/Popovers/Tooltip/Tooltip.css (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Popovers/Tooltip/Tooltip.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Popovers/Tooltip/Tooltip.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Popovers/Tooltip/Tooltip.tsx (87%) rename src/renderer/{components/ui-redesign => shared/ui}/RadioGroup/RadioGroup.css (100%) rename src/renderer/{components/ui-redesign => shared/ui}/RadioGroup/RadioGroup.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/RadioGroup/RadioGroup.test.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/RadioGroup/RadioGroup.tsx (94%) rename src/renderer/{components/ui-redesign => shared/ui}/RadioGroup/RadioOption.tsx (93%) rename src/renderer/{components/ui-redesign => shared/ui}/RadioGroup/common/types.ts (100%) rename src/renderer/{components => shared}/ui/Shimmering/Shimmering.css (100%) rename src/renderer/{components => shared}/ui/Shimmering/Shimmering.stories.tsx (100%) rename src/renderer/{components => shared}/ui/Shimmering/Shimmering.tsx (89%) rename src/renderer/{components/ui-redesign => shared/ui}/StatusLabel/StatusLabel.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/StatusLabel/StatusLabel.test.tsx (95%) rename src/renderer/{components/ui-redesign => shared/ui}/StatusLabel/StatusLabel.tsx (79%) rename src/renderer/{components/ui-redesign => shared/ui}/StatusLabel/common/constants.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/StatusLabel/common/types.ts (100%) rename src/renderer/{components => shared}/ui/Switch/Switch.stories.tsx (100%) rename src/renderer/{components => shared}/ui/Switch/Switch.test.tsx (100%) rename src/renderer/{components => shared}/ui/Switch/Switch.tsx (93%) rename src/renderer/{components/ui-redesign => shared/ui}/Tabs/Tabs.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Tabs/Tabs.test.tsx (98%) rename src/renderer/{components/ui-redesign => shared/ui}/Tabs/Tabs.tsx (91%) rename src/renderer/{components/ui-redesign => shared/ui}/Tabs/common/types.ts (100%) rename src/renderer/{components => shared}/ui/Truncate/Truncate.test.tsx (96%) rename src/renderer/{components => shared}/ui/Truncate/Truncate.tsx (95%) rename src/renderer/{components => shared}/ui/Truncate/utils.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/Typography.stories.tsx (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/common/TextBase.ts (87%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/common/types.ts (100%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/components/BodyText.tsx (82%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/components/CaptionText.tsx (82%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/components/FootnoteText.tsx (82%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/components/HeaderTitleText.tsx (84%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/components/HeadlineText.tsx (82%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/components/HelpText.tsx (82%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/components/LabelText.tsx (90%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/components/LargeTitleText.tsx (84%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/components/SmallTitleText.tsx (84%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/components/TitleText.tsx (84%) rename src/renderer/{components/ui-redesign => shared/ui}/Typography/index.ts (93%) rename src/renderer/{components/ui-redesign => shared/ui}/index.ts (81%) create mode 100644 src/renderer/shared/ui/types.ts diff --git a/README.md b/README.md index c8211c6b7f..80e1e65168 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ The `production` run configuration is the same as [production build](#production installed in the operating system and source code hot-reload will be used. Production configuration uses: -1. [`chains.json`](/src/renderer/services/network/common/chains/chains.json) file for chains configuration +1. [`chains.json`](/src/renderer/assets/chains/chains.json) file for chains configuration 2. debug tools are disabled by default 3. errors are handled in a smooth way in order not to interrupt the user @@ -97,7 +97,7 @@ The `dev` run configuration **shouldn't be** used for production. This configura debugging errors. Development configuration uses: -1. [`chains_dev.json`](/src/renderer/services/network/common/chains/chains_dev.json) file that contains testnets in order to debug and test new features +1. [`chains_dev.json`](/src/renderer/assets/chains/chains_dev.json) file that contains testnets in order to debug and test new features 2. debug tools are enabled by default 3. error handling is turned off in order to pay developer's attention to errors diff --git a/app.config.js b/app.config.js index aec9c6c8b3..8ee7acb4d1 100644 --- a/app.config.js +++ b/app.config.js @@ -29,10 +29,10 @@ exports.APP_CONFIG = { ENTRY_POINTS: { MAIN: 'src/main/index.ts', BRIDGE: 'src/shared/bridge.ts', - RENDERER: 'src/renderer/index.tsx', + RENDERER: 'src/renderer/app/index.tsx', }, - INDEX_HTML: 'src/renderer/index.html', + INDEX_HTML: 'src/renderer/app/index.html', RESOURCES: 'src/main/resources', DEV_BUILD: 'release/build/', PROD_BUILD: 'release/dist/', diff --git a/jest.config.ts b/jest.config.ts index 4beb304273..b4336ec6ff 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -37,7 +37,6 @@ const config: Config = { '^.+\\.(t|j)sx?$': ['@swc/jest', swcConfig], '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|json)$)': '/scripts/fileTransform.js', }, - // help @swc/jest to transform node_modules esm packages (swiper.js I look at you) transformIgnorePatterns: [], moduleNameMapper: { '\\.(css|less|scss|sass)$': 'identity-obj-proxy', @@ -47,6 +46,8 @@ const config: Config = { '^@renderer(.*)$': '/src/renderer/$1', '^@images(.*)$': '/src/renderer/assets/images/$1', '^@video(.*)$': '/src/renderer/assets/video/$1', + '^dexie$': '/node_modules/dexie/dist/dexie.js', + '^lottie': 'lottie-react', }, modulePathIgnorePatterns: ['/tests'], collectCoverageFrom: [ diff --git a/src/main/main.ts b/src/main/main.ts index 47484dcde4..42b6ac3e4a 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -19,7 +19,7 @@ log.transports.console.useStyles = true; log.transports.file.fileName = 'nova-spektr.log'; log.transports.file.format = '{y}/{m}/{d} {h}:{i}:{s}.{ms} [{env}#{version}]-{processType} [{level}] > {text}'; log.transports.file.level = 'info'; -log.transports.file.maxSize = 1048576 * 5; //5mb; +log.transports.file.maxSize = 1048576 * 5; // 5 MB; log.transports.file.archiveLogFn = (oldLogFile: LogFile): void => { const file = oldLogFile.toString(); const info = path.parse(file); @@ -36,7 +36,7 @@ log.transports.file.archiveLogFn = (oldLogFile: LogFile): void => { Object.assign(console, log.functions); log.errorHandler.startCatching({ showDialog: false, - onError({ createIssue, error, processType, versions }) { + onError({ error }) { console.error('Uncaught error', error); }, }); diff --git a/src/renderer/App.tsx b/src/renderer/app/App.tsx similarity index 69% rename from src/renderer/App.tsx rename to src/renderer/app/App.tsx index bc6ed49235..f02d1363a0 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/app/App.tsx @@ -3,15 +3,17 @@ import { ErrorBoundary } from 'react-error-boundary'; import { useNavigate, useRoutes } from 'react-router-dom'; import { FallbackScreen } from '@renderer/components/common'; -import ConfirmDialogProvider from '@renderer/context/ConfirmContext'; -import GraphqlContext from '@renderer/context/GraphqlContext'; -import I18Provider from '@renderer/context/I18nContext'; -import MatrixProvider from '@renderer/context/MatrixContext'; -import NetworkProvider from '@renderer/context/NetworkContext'; -import Paths from '@renderer/routes/paths'; -import routesConfig from './routes'; -import { useAccount } from './services/account/accountService'; -import { MultisigChainProvider } from './context/MultisigChainContext/MultisigChainContext'; +import { useAccount } from '../entities/account/lib/accountService'; +import { + ConfirmDialogProvider, + I18Provider, + MatrixProvider, + NetworkProvider, + GraphqlProvider, + MultisigChainProvider, + Paths, + routesConfig, +} from './providers'; const SPLASH_SCREEN_DELAY = 450; @@ -50,7 +52,7 @@ const App = () => { - {getContent()} + {getContent()} diff --git a/src/renderer/i18n.ts b/src/renderer/app/i18n.ts similarity index 77% rename from src/renderer/i18n.ts rename to src/renderer/app/i18n.ts index 62628a52e5..4ecd843cb2 100644 --- a/src/renderer/i18n.ts +++ b/src/renderer/app/i18n.ts @@ -1,7 +1,7 @@ import i18next from 'i18next'; import { initReactI18next } from 'react-i18next'; -import { useTranslationService } from './services/translation/translationService'; +import { useTranslationService } from '../services/translation/translationService'; const { getConfig } = useTranslationService(); diff --git a/src/renderer/app/index.css b/src/renderer/app/index.css new file mode 100644 index 0000000000..87e357614c --- /dev/null +++ b/src/renderer/app/index.css @@ -0,0 +1,62 @@ +@import './styles/fonts.css'; + +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Scrollbar for Firefox */ +* { + scrollbar-width: thin; +} + +/* Scrollbar for other Browsers */ +/* Width */ +::-webkit-scrollbar { + width: 8px; + height: 8px; /* horizontal scrollbar */ +} + +/* Track */ +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-track:vertical { + margin: 2px 0; +} + +::-webkit-scrollbar-track:horizontal { + margin: 0 2px; +} + +/* Handle */ +::-webkit-scrollbar-thumb { + background-color: var(--scroll-background-default); + border-radius: 4px; + border: 2px solid transparent; + background-clip: padding-box; +} + +*:focus-visible { + outline: 2px solid var(--focus-container-border); +} + +ul:focus-visible { + outline: none; +} + +.logo-background { + background-size: cover; + background-position: center; + background-image: url('../assets/images/misc/bg.webp'); + transform: scaleY(-1); +} + +.video-cover { + position: absolute; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.27); + left: 0; + top: 0; +} diff --git a/src/renderer/index.html b/src/renderer/app/index.html similarity index 100% rename from src/renderer/index.html rename to src/renderer/app/index.html diff --git a/src/renderer/index.tsx b/src/renderer/app/index.tsx similarity index 96% rename from src/renderer/index.tsx rename to src/renderer/app/index.tsx index 5cadca9fb1..ae34b251e8 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/app/index.tsx @@ -6,7 +6,7 @@ import App from './App'; import './i18n'; import './index.css'; -import './theme/default.css'; +import './styles/theme/default.css'; log.variables.version = process.env.VERSION; log.variables.env = process.env.NODE_ENV; diff --git a/src/renderer/context/ConfirmContext/ConfirmContext.test.tsx b/src/renderer/app/providers/context/ConfirmContext/ConfirmContext.test.tsx similarity index 93% rename from src/renderer/context/ConfirmContext/ConfirmContext.test.tsx rename to src/renderer/app/providers/context/ConfirmContext/ConfirmContext.test.tsx index 1e34c9345f..15bfb0a3b3 100644 --- a/src/renderer/context/ConfirmContext/ConfirmContext.test.tsx +++ b/src/renderer/app/providers/context/ConfirmContext/ConfirmContext.test.tsx @@ -1,9 +1,9 @@ import { act, render, renderHook, screen } from '@testing-library/react'; -import { useToggle } from '@renderer/shared/hooks'; +import { useToggle } from '@renderer/shared/lib/hooks'; import { ConfirmDialogProvider, useConfirmContext } from './ConfirmContext'; -jest.mock('@renderer/shared/hooks'); +jest.mock('@renderer/shared/lib/hooks'); describe('context/ConfirmContext', () => { afterEach(() => { diff --git a/src/renderer/context/ConfirmContext/ConfirmContext.tsx b/src/renderer/app/providers/context/ConfirmContext/ConfirmContext.tsx similarity index 92% rename from src/renderer/context/ConfirmContext/ConfirmContext.tsx rename to src/renderer/app/providers/context/ConfirmContext/ConfirmContext.tsx index dc41fd1ad8..f2ccb9ea24 100644 --- a/src/renderer/context/ConfirmContext/ConfirmContext.tsx +++ b/src/renderer/app/providers/context/ConfirmContext/ConfirmContext.tsx @@ -1,8 +1,8 @@ import { createContext, PropsWithChildren, useCallback, useContext, useRef, useState, ReactNode } from 'react'; -import { ConfirmModal, SmallTitleText, FootnoteText } from '@renderer/components/ui-redesign'; -import { useToggle } from '@renderer/shared/hooks'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; +import { ConfirmModal, SmallTitleText, FootnoteText } from '@renderer/shared/ui'; +import { DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { useToggle } from '@renderer/shared/lib/hooks'; export type ConfirmDialogProps = { title: string; diff --git a/src/renderer/app/providers/context/ConfirmContext/index.ts b/src/renderer/app/providers/context/ConfirmContext/index.ts new file mode 100644 index 0000000000..e401e8e39d --- /dev/null +++ b/src/renderer/app/providers/context/ConfirmContext/index.ts @@ -0,0 +1 @@ +export { ConfirmDialogProvider, useConfirmContext } from './ConfirmContext'; diff --git a/src/renderer/context/GraphqlContext/GraphqlContext.test.tsx b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.test.tsx similarity index 86% rename from src/renderer/context/GraphqlContext/GraphqlContext.test.tsx rename to src/renderer/app/providers/context/GraphqlContext/GraphqlContext.test.tsx index bd469486cf..047f3317f7 100644 --- a/src/renderer/context/GraphqlContext/GraphqlContext.test.tsx +++ b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.test.tsx @@ -2,9 +2,9 @@ import { act, render, renderHook, screen } from '@testing-library/react'; import { GraphqlProvider, useGraphql } from './GraphqlContext'; -jest.mock('@renderer/shared/hooks'); +jest.mock('@renderer/shared/lib/hooks'); -jest.mock('@renderer/services/network/chainsService', () => ({ +jest.mock('@renderer/entities/network', () => ({ useChains: jest.fn().mockReturnValue({ getStakingChainsData: jest .fn() @@ -14,7 +14,7 @@ jest.mock('@renderer/services/network/chainsService', () => ({ }), })); -jest.mock('@renderer/services/settings/settingsStorage', () => ({ +jest.mock('@renderer/entities/settings', () => ({ useSettingsStorage: jest.fn().mockReturnValue({ getStakingNetwork: jest.fn().mockReturnValue('0x123'), }), diff --git a/src/renderer/context/GraphqlContext/GraphqlContext.tsx b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.tsx similarity index 94% rename from src/renderer/context/GraphqlContext/GraphqlContext.tsx rename to src/renderer/app/providers/context/GraphqlContext/GraphqlContext.tsx index 0e6343d0f1..19e0282d50 100644 --- a/src/renderer/context/GraphqlContext/GraphqlContext.tsx +++ b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.tsx @@ -3,8 +3,8 @@ import { onError } from '@apollo/client/link/error'; import { createContext, PropsWithChildren, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { useChains } from '@renderer/services/network/chainsService'; -import { useSettingsStorage } from '@renderer/services/settings/settingsStorage'; +import { useChains } from '@renderer/entities/network'; +import { useSettingsStorage } from '@renderer/entities/settings'; type GraphqlContextProps = { changeClient: (chainId: ChainId) => void; diff --git a/src/renderer/app/providers/context/GraphqlContext/index.ts b/src/renderer/app/providers/context/GraphqlContext/index.ts new file mode 100644 index 0000000000..d3e93c837f --- /dev/null +++ b/src/renderer/app/providers/context/GraphqlContext/index.ts @@ -0,0 +1 @@ +export { GraphqlProvider, useGraphql } from './GraphqlContext'; diff --git a/src/renderer/context/I18nContext/I18nContext.tsx b/src/renderer/app/providers/context/I18nContext/I18nContext.tsx similarity index 97% rename from src/renderer/context/I18nContext/I18nContext.tsx rename to src/renderer/app/providers/context/I18nContext/I18nContext.tsx index 4281ada7fe..1111524cb8 100644 --- a/src/renderer/context/I18nContext/I18nContext.tsx +++ b/src/renderer/app/providers/context/I18nContext/I18nContext.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { Locale } from 'date-fns'; import { enGB } from 'date-fns/locale'; -import { LanguageSwitcher } from '@renderer/components/ui'; +import { LanguageSwitcher } from '@renderer/shared/ui'; import { LanguageItem, SupportedLocale } from '@renderer/services/translation/common/types'; import { LanguageOptions } from '@renderer/services/translation/common/constants'; import { useTranslationService } from '@renderer/services/translation/translationService'; diff --git a/src/renderer/app/providers/context/I18nContext/index.ts b/src/renderer/app/providers/context/I18nContext/index.ts new file mode 100644 index 0000000000..78411e4bdb --- /dev/null +++ b/src/renderer/app/providers/context/I18nContext/index.ts @@ -0,0 +1 @@ +export { I18Provider, useI18n } from './I18nContext'; diff --git a/src/renderer/context/MatrixContext/MatrixContext.test.tsx b/src/renderer/app/providers/context/MatrixContext/MatrixContext.test.tsx similarity index 81% rename from src/renderer/context/MatrixContext/MatrixContext.test.tsx rename to src/renderer/app/providers/context/MatrixContext/MatrixContext.test.tsx index 7839dff004..867c1c28a1 100644 --- a/src/renderer/context/MatrixContext/MatrixContext.test.tsx +++ b/src/renderer/app/providers/context/MatrixContext/MatrixContext.test.tsx @@ -1,50 +1,47 @@ import { act, render, screen } from '@testing-library/react'; -import Matrix from '@renderer/services/matrix'; +import { Matrix } from '@renderer/entities/matrix'; import { MatrixProvider } from './MatrixContext'; import { ConnectionType } from '@renderer/domain/connection'; -jest.mock('@renderer/services/matrix', () => jest.fn().mockReturnValue({})); +jest.mock('@renderer/entities/matrix', () => ({ Matrix: jest.fn().mockReturnValue({}) })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getAccounts: jest.fn().mockReturnValue([]), }), })); -jest.mock('@renderer/services/multisigTx/multisigTxService', () => ({ +jest.mock('@renderer/entities/multisig', () => ({ useMultisigTx: jest.fn().mockReturnValue({ getMultisigTxs: jest.fn(), addMultisigTx: jest.fn(), updateMultisigTx: jest.fn(), updateCallData: jest.fn(), }), + useMultisigEvent: jest.fn().mockReturnValue({ + addEvent: jest.fn(), + updateEvent: jest.fn(), + getEvents: jest.fn().mockReturnValue([]), + }), })); -jest.mock('@renderer/services/contact/contactService', () => ({ +jest.mock('@renderer/entities/contact', () => ({ useContact: jest.fn().mockReturnValue({ getContacts: jest.fn().mockReturnValue([]), }), })); -jest.mock('@renderer/services/notification/notificationService', () => ({ +jest.mock('@renderer/entities/notification', () => ({ useNotification: jest.fn().mockReturnValue({ addNotification: jest.fn(), }), })); -jest.mock('@renderer/services/multisigEvent/multisigEventService', () => ({ - useMultisigEvent: jest.fn().mockReturnValue({ - addEvent: jest.fn(), - updateEvent: jest.fn(), - getEvents: jest.fn().mockReturnValue([]), - }), -})); - -jest.mock('@renderer/context/NetworkContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useNetworkContext: jest.fn(() => ({ connections: { - '0x0000000000000000000000000000000000000000': { + '0x00': { chainId: '1', assets: [{ assetId: '1', symbol: '1' }], connection: { @@ -53,6 +50,9 @@ jest.mock('@renderer/context/NetworkContext', () => ({ }, }, })), + useMultisigChainContext: jest.fn(() => ({ + addTask: () => undefined, + })), })); describe('context/MatrixContext', () => { diff --git a/src/renderer/context/MatrixContext/MatrixContext.tsx b/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx similarity index 94% rename from src/renderer/context/MatrixContext/MatrixContext.tsx rename to src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx index 5c97b7a544..a43b320797 100644 --- a/src/renderer/context/MatrixContext/MatrixContext.tsx +++ b/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx @@ -1,18 +1,24 @@ import { createContext, PropsWithChildren, useContext, useEffect, useRef, useState } from 'react'; -import { createMultisigAccount, getMultisigAccountId, MultisigAccount } from '@renderer/domain/account'; -import { useAccount } from '@renderer/services/account/accountService'; -import { toAddress } from '@renderer/shared/utils/address'; -import { useContact } from '@renderer/services/contact/contactService'; +import { createMultisigAccount, getMultisigAccountId, MultisigAccount, useAccount } from '@renderer/entities/account'; +import { toAddress, getCreatedDateFromApi, validateCallData } from '@renderer/shared/lib/utils'; +import { useContact } from '@renderer/entities/contact'; import { AccountId, Address, CallHash, ChainId, SigningType } from '@renderer/domain/shared-kernel'; -import { getCreatedDateFromApi, validateCallData } from '@renderer/shared/utils/substrate'; -import { useMultisigTx } from '@renderer/services/multisigTx/multisigTxService'; -import { Signatory } from '@renderer/domain/signatory'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { useNotification } from '@renderer/services/notification/notificationService'; -import { MultisigNotificationType } from '@renderer/domain/notification'; -import Matrix, { +import { useMultisigTx, useMultisigEvent } from '@renderer/entities/multisig'; +import { Signatory } from '@renderer/entities/signatory'; +import { useNetworkContext } from '@renderer/app/providers/context/NetworkContext/NetworkContext'; +import { + useTransaction, + MultisigEvent, + MultisigTransaction, + MultisigTxFinalStatus, + MultisigTxInitStatus, + MultisigTxStatus, + SigningStatus, +} from '@renderer/entities/transaction'; +import { useNotification, MultisigNotificationType } from '@renderer/entities/notification'; +import { + Matrix, ApprovePayload, BaseMultisigPayload, CancelPayload, @@ -22,17 +28,8 @@ import Matrix, { MultisigPayload, SpektrExtras, UpdatePayload, -} from '@renderer/services/matrix'; -import { - MultisigEvent, - MultisigTransaction, - MultisigTxFinalStatus, - MultisigTxInitStatus, - MultisigTxStatus, - SigningStatus, -} from '@renderer/domain/transaction'; -import { useMultisigEvent } from '@renderer/services/multisigEvent/multisigEventService'; -import { useMultisigChainContext } from '../MultisigChainContext'; +} from '@renderer/entities/matrix'; +import { useMultisigChainContext } from '@renderer/app/providers'; type MatrixContextProps = { matrix: ISecureMessenger; diff --git a/src/renderer/app/providers/context/MatrixContext/index.ts b/src/renderer/app/providers/context/MatrixContext/index.ts new file mode 100644 index 0000000000..0f10f75cdf --- /dev/null +++ b/src/renderer/app/providers/context/MatrixContext/index.ts @@ -0,0 +1 @@ +export { MatrixProvider, useMatrix } from './MatrixContext'; diff --git a/src/renderer/context/MultisigChainContext/MultisigChainContext.tsx b/src/renderer/app/providers/context/MultisigChainContext/MultisigChainContext.tsx similarity index 90% rename from src/renderer/context/MultisigChainContext/MultisigChainContext.tsx rename to src/renderer/app/providers/context/MultisigChainContext/MultisigChainContext.tsx index 17323368f6..d2eaeed08d 100644 --- a/src/renderer/context/MultisigChainContext/MultisigChainContext.tsx +++ b/src/renderer/app/providers/context/MultisigChainContext/MultisigChainContext.tsx @@ -2,19 +2,16 @@ import { createContext, PropsWithChildren, useContext, useEffect } from 'react'; import { VoidFn } from '@polkadot/api/types'; import { Event } from '@polkadot/types/interfaces'; -import { useChainSubscription } from '@renderer/services/chainSubscription/chainSubscriptionService'; +import { useChainSubscription } from '@renderer/entities/chain'; import { useNetworkContext } from '../NetworkContext'; -import { useMultisigTx } from '@renderer/services/multisigTx/multisigTxService'; -import { useAccount } from '@renderer/services/account/accountService'; -import { MultisigAccount } from '@renderer/domain/account'; -import { MultisigTxFinalStatus, SigningStatus } from '@renderer/domain/transaction'; -import { toAddress } from '@renderer/shared/utils/address'; +import { useMultisigTx, useMultisigEvent } from '@renderer/entities/multisig'; +import { useAccount, MultisigAccount } from '@renderer/entities/account'; +import { MultisigTxFinalStatus, SigningStatus } from '@renderer/entities/transaction'; +import { toAddress, getCreatedDateFromApi } from '@renderer/shared/lib/utils'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { useDebounce, useTaskQueue } from '@renderer/shared/hooks'; -import { useMultisigEvent } from '@renderer/services/multisigEvent/multisigEventService'; +import { useDebounce, useTaskQueue } from '@renderer/shared/lib/hooks'; import { ConnectionStatus } from '@renderer/domain/connection'; -import { getCreatedDateFromApi } from '@renderer/shared/utils/substrate'; -import { Task } from '@renderer/shared/hooks/useTaskQueue'; +import { Task } from '@renderer/shared/lib/hooks/useTaskQueue'; type MultisigChainContextProps = { addTask: (task: Task) => void; diff --git a/src/renderer/app/providers/context/MultisigChainContext/index.ts b/src/renderer/app/providers/context/MultisigChainContext/index.ts new file mode 100644 index 0000000000..a56f07ee24 --- /dev/null +++ b/src/renderer/app/providers/context/MultisigChainContext/index.ts @@ -0,0 +1 @@ +export { MultisigChainProvider, useMultisigChainContext } from './MultisigChainContext'; diff --git a/src/renderer/context/NetworkContext/NetworkContext.test.tsx b/src/renderer/app/providers/context/NetworkContext/NetworkContext.test.tsx similarity index 91% rename from src/renderer/context/NetworkContext/NetworkContext.test.tsx rename to src/renderer/app/providers/context/NetworkContext/NetworkContext.test.tsx index ef3c1dfdc8..c97fab79cc 100644 --- a/src/renderer/context/NetworkContext/NetworkContext.test.tsx +++ b/src/renderer/app/providers/context/NetworkContext/NetworkContext.test.tsx @@ -1,19 +1,19 @@ import { act, render, renderHook, screen, waitFor } from '@testing-library/react'; import { ConnectionStatus, ConnectionType } from '@renderer/domain/connection'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { useNetwork } from '@renderer/services/network/networkService'; +import { useBalance } from '@renderer/entities/asset'; +import { useNetwork } from '@renderer/entities/network'; import { NetworkProvider, useNetworkContext } from './NetworkContext'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; -jest.mock('@renderer/services/network/networkService', () => ({ +jest.mock('@renderer/entities/network', () => ({ useNetwork: jest.fn().mockReturnValue({ connections: {}, setupConnections: jest.fn(), }), })); -jest.mock('@renderer/services/balance/balanceService', () => ({ +jest.mock('@renderer/entities/asset', () => ({ useBalance: jest.fn().mockReturnValue({ subscribeBalances: jest.fn(), subscribeLockBalances: jest.fn(), @@ -29,7 +29,8 @@ jest.mock('@renderer/services/subscription/subscriptionService', () => ({ }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ + ...jest.requireActual('@renderer/entities/account'), useAccount: jest.fn().mockReturnValue({ getActiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], getLiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], diff --git a/src/renderer/context/NetworkContext/NetworkContext.tsx b/src/renderer/app/providers/context/NetworkContext/NetworkContext.tsx similarity index 91% rename from src/renderer/context/NetworkContext/NetworkContext.tsx rename to src/renderer/app/providers/context/NetworkContext/NetworkContext.tsx index 1906949e5c..d3d6d88e2a 100644 --- a/src/renderer/context/NetworkContext/NetworkContext.tsx +++ b/src/renderer/app/providers/context/NetworkContext/NetworkContext.tsx @@ -1,15 +1,13 @@ import { createContext, PropsWithChildren, useContext, useEffect, useState } from 'react'; -import { RpcNode } from '@renderer/domain/chain'; +import { RpcNode } from '@renderer/entities/chain'; import { ConnectionStatus, ConnectionType } from '@renderer/domain/connection'; import { ChainId, AccountId } from '@renderer/domain/shared-kernel'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { ConnectProps, ExtendedChain, RpcValidation } from '@renderer/services/network/common/types'; -import { useNetwork } from '@renderer/services/network/networkService'; +import { useBalance } from '@renderer/entities/asset'; +import { ConnectProps, ExtendedChain, RpcValidation, useNetwork } from '@renderer/entities/network'; import { useSubscription } from '@renderer/services/subscription/subscriptionService'; -import { useAccount } from '@renderer/services/account/accountService'; -import { usePrevious } from '@renderer/shared/hooks'; -import { isMultisig } from '@renderer/domain/account'; +import { useAccount, isMultisig } from '@renderer/entities/account'; +import { usePrevious } from '@renderer/shared/lib/hooks'; type NetworkContextProps = { connections: Record; diff --git a/src/renderer/app/providers/context/NetworkContext/index.ts b/src/renderer/app/providers/context/NetworkContext/index.ts new file mode 100644 index 0000000000..dfaaa25daf --- /dev/null +++ b/src/renderer/app/providers/context/NetworkContext/index.ts @@ -0,0 +1 @@ +export { NetworkProvider, useNetworkContext } from './NetworkContext'; diff --git a/src/renderer/app/providers/context/index.ts b/src/renderer/app/providers/context/index.ts new file mode 100644 index 0000000000..928bc39f60 --- /dev/null +++ b/src/renderer/app/providers/context/index.ts @@ -0,0 +1,6 @@ +export * from './ConfirmContext'; +export * from './GraphqlContext'; +export * from './I18nContext'; +export * from './MatrixContext'; +export * from './MultisigChainContext'; +export * from './NetworkContext'; diff --git a/src/renderer/app/providers/index.ts b/src/renderer/app/providers/index.ts new file mode 100644 index 0000000000..c4c0e86195 --- /dev/null +++ b/src/renderer/app/providers/index.ts @@ -0,0 +1,2 @@ +export * from './context'; +export * from './routes'; diff --git a/src/renderer/app/providers/routes/index.ts b/src/renderer/app/providers/routes/index.ts new file mode 100644 index 0000000000..7a6e51b2b3 --- /dev/null +++ b/src/renderer/app/providers/routes/index.ts @@ -0,0 +1,3 @@ +export * from './routesConfig'; +export * from './paths'; +export * from './utils'; diff --git a/src/renderer/routes/paths.ts b/src/renderer/app/providers/routes/paths.ts similarity index 94% rename from src/renderer/routes/paths.ts rename to src/renderer/app/providers/routes/paths.ts index 5c8d7d48de..29d8a7719e 100644 --- a/src/renderer/routes/paths.ts +++ b/src/renderer/app/providers/routes/paths.ts @@ -1,4 +1,4 @@ -const Paths = { +export const Paths = { ROOT: '/', // Onboarding @@ -29,5 +29,3 @@ const Paths = { } as const; export type PathValue = (typeof Paths)[keyof typeof Paths]; - -export default Paths; diff --git a/src/renderer/routes/index.tsx b/src/renderer/app/providers/routes/routesConfig.tsx similarity index 89% rename from src/renderer/routes/index.tsx rename to src/renderer/app/providers/routes/routesConfig.tsx index 104ce545d9..14b7cef975 100644 --- a/src/renderer/routes/index.tsx +++ b/src/renderer/app/providers/routes/routesConfig.tsx @@ -1,13 +1,13 @@ import { Navigate, RouteObject } from 'react-router-dom'; import Layouts from '@renderer/components/layout'; -import Screens from '@renderer/screens'; -import Paths from './paths'; +import { Paths } from './paths'; +import * as Screens from '@renderer/pages'; // React router v6 hint: // https://github.com/remix-run/react-router/blob/main/docs/upgrading/v5.md#use-useroutes-instead-of-react-router-config -const routesConfig: RouteObject[] = [ - { path: Paths.ONBOARDING, element: }, +export const routesConfig: RouteObject[] = [ + { path: Paths.ONBOARDING, element: }, { path: Paths.ROOT, element: , @@ -45,5 +45,3 @@ const routesConfig: RouteObject[] = [ ], }, ]; - -export default routesConfig; diff --git a/src/renderer/routes/utils.test.ts b/src/renderer/app/providers/routes/utils.test.ts similarity index 81% rename from src/renderer/routes/utils.test.ts rename to src/renderer/app/providers/routes/utils.test.ts index 189e8a5c85..cce5668d74 100644 --- a/src/renderer/routes/utils.test.ts +++ b/src/renderer/app/providers/routes/utils.test.ts @@ -1,5 +1,5 @@ -import Paths from '@renderer/routes/paths'; -import { createLink } from '@renderer/routes/utils'; +import { Paths } from './paths'; +import { createLink } from './utils'; describe('routes/utils/createLink', () => { test('parse route with params', () => { diff --git a/src/renderer/routes/utils.ts b/src/renderer/app/providers/routes/utils.ts similarity index 100% rename from src/renderer/routes/utils.ts rename to src/renderer/app/providers/routes/utils.ts diff --git a/src/renderer/app/styles/fonts.css b/src/renderer/app/styles/fonts.css new file mode 100644 index 0000000000..13efa0b36a --- /dev/null +++ b/src/renderer/app/styles/fonts.css @@ -0,0 +1,59 @@ +/* Inter (Latin) */ +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/src/renderer/assets/fonts/Inter/Inter-400.woff2') format('woff2'); +} +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('/src/renderer/assets/fonts/Inter/Inter-500.woff2') format('woff2'); +} +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url('/src/renderer/assets/fonts/Inter/Inter-600.woff2') format('woff2'); +} +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url('/src/renderer/assets/fonts/Inter/Inter-800.woff2') format('woff2'); +} + +/* Manrope (Latin) */ +@font-face { + font-family: 'Manrope'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/src/renderer/assets/fonts/Manrope/Manrope-400.woff2') format('woff2'); +} +@font-face { + font-family: 'Manrope'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('/src/renderer/assets/fonts/Manrope/Manrope-500.woff2') format('woff2'); +} +@font-face { + font-family: 'Manrope'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url('/src/renderer/assets/fonts/Manrope/Manrope-600.woff2') format('woff2'); +} +@font-face { + font-family: 'Manrope'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url('/src/renderer/assets/fonts/Manrope/Manrope-800.woff2') format('woff2'); +} diff --git a/src/renderer/theme/dark.css b/src/renderer/app/styles/theme/dark.css similarity index 100% rename from src/renderer/theme/dark.css rename to src/renderer/app/styles/theme/dark.css diff --git a/src/renderer/theme/default.css b/src/renderer/app/styles/theme/default.css similarity index 100% rename from src/renderer/theme/default.css rename to src/renderer/app/styles/theme/default.css diff --git a/src/renderer/services/network/common/chains/chains.json b/src/renderer/assets/chains/chains.json similarity index 100% rename from src/renderer/services/network/common/chains/chains.json rename to src/renderer/assets/chains/chains.json diff --git a/src/renderer/services/network/common/chains/chains_dev.json b/src/renderer/assets/chains/chains_dev.json similarity index 100% rename from src/renderer/services/network/common/chains/chains_dev.json rename to src/renderer/assets/chains/chains_dev.json diff --git a/src/renderer/components/common/ExplorerLink/ExplorerLink.tsx b/src/renderer/components/common/ExplorerLink/ExplorerLink.tsx index 50d3c18bae..149c3e74f6 100644 --- a/src/renderer/components/common/ExplorerLink/ExplorerLink.tsx +++ b/src/renderer/components/common/ExplorerLink/ExplorerLink.tsx @@ -1,11 +1,9 @@ -import cnTw from '@renderer/shared/utils/twMerge'; -import { toAddress } from '@renderer/shared/utils/address'; -import { Icon } from '@renderer/components/ui'; +import { cnTw, toAddress } from '@renderer/shared/lib/utils'; +import { Icon, FootnoteText } from '@renderer/shared/ui'; import { DefaultExplorer, ExplorerIcons } from '@renderer/components/common/ExplorerLink/constants'; -import { Explorer } from '@renderer/domain/chain'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Explorer } from '@renderer/entities/chain'; +import { useI18n } from '@renderer/app/providers'; import { AccountId, Address, HexString } from '@renderer/domain/shared-kernel'; -import { FootnoteText } from '@renderer/components/ui-redesign'; const isExtrinsic = (props: WithAccount | WithExtrinsic): props is WithExtrinsic => (props as WithExtrinsic).hash !== undefined; diff --git a/src/renderer/components/common/ExplorerLink/constants.ts b/src/renderer/components/common/ExplorerLink/constants.ts index a82f7e3267..7c9b1f7bab 100644 --- a/src/renderer/components/common/ExplorerLink/constants.ts +++ b/src/renderer/components/common/ExplorerLink/constants.ts @@ -1,4 +1,4 @@ -import { Explorer } from '@renderer/components/ui/Icon/data/explorer'; +import { Explorer } from '@renderer/shared/ui/Icon/data/explorer'; export const DefaultExplorer = 'default'; diff --git a/src/renderer/components/common/ExtrinsicExplorers/ExtrinsicExplorers.tsx b/src/renderer/components/common/ExtrinsicExplorers/ExtrinsicExplorers.tsx index 2840923b32..0cb1a1d444 100644 --- a/src/renderer/components/common/ExtrinsicExplorers/ExtrinsicExplorers.tsx +++ b/src/renderer/components/common/ExtrinsicExplorers/ExtrinsicExplorers.tsx @@ -1,6 +1,5 @@ -import { InfoPopover } from '@renderer/components/ui-redesign'; -import { Icon } from '@renderer/components/ui'; -import { Explorer } from '@renderer/domain/chain'; +import { InfoPopover, Icon } from '@renderer/shared/ui'; +import { Explorer } from '@renderer/entities/chain'; import useExtrinsicInfo from './useExtrinsicInfo'; import { HexString } from '@renderer/domain/shared-kernel'; diff --git a/src/renderer/components/common/ExtrinsicExplorers/useExtrinsicInfo.tsx b/src/renderer/components/common/ExtrinsicExplorers/useExtrinsicInfo.tsx index 0816eb30e7..3e55df87d6 100644 --- a/src/renderer/components/common/ExtrinsicExplorers/useExtrinsicInfo.tsx +++ b/src/renderer/components/common/ExtrinsicExplorers/useExtrinsicInfo.tsx @@ -1,6 +1,6 @@ -import { InfoSection } from '@renderer/components/ui-redesign/Popovers/InfoPopover/InfoPopover'; +import { InfoSection } from '@renderer/shared/ui/Popovers/InfoPopover/InfoPopover'; import { HexString } from '@renderer/domain/shared-kernel'; -import { Explorer } from '@renderer/domain/chain'; +import { Explorer } from '@renderer/entities/chain'; import { ExplorerLink } from '@renderer/components/common'; const useExtrinsicInfo = (hash: HexString, explorers?: Explorer[]): InfoSection[] => { diff --git a/src/renderer/components/common/FallbackScreen/FallbackScreen.test.tsx b/src/renderer/components/common/FallbackScreen/FallbackScreen.test.tsx index 0693585994..a421b78797 100644 --- a/src/renderer/components/common/FallbackScreen/FallbackScreen.test.tsx +++ b/src/renderer/components/common/FallbackScreen/FallbackScreen.test.tsx @@ -3,7 +3,7 @@ import { MemoryRouter } from 'react-router-dom'; import FallbackScreen from './FallbackScreen'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/components/common/FallbackScreen/FallbackScreen.tsx b/src/renderer/components/common/FallbackScreen/FallbackScreen.tsx index e5fe83d258..0e93f669d8 100644 --- a/src/renderer/components/common/FallbackScreen/FallbackScreen.tsx +++ b/src/renderer/components/common/FallbackScreen/FallbackScreen.tsx @@ -1,6 +1,5 @@ -import { Icon } from '@renderer/components/ui'; -import { BodyText, Button } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Icon, BodyText, Button } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; const FallbackScreen = () => { const { t } = useI18n(); diff --git a/src/renderer/components/common/Header/Header.tsx b/src/renderer/components/common/Header/Header.tsx index f3b7240ce3..74290ee922 100644 --- a/src/renderer/components/common/Header/Header.tsx +++ b/src/renderer/components/common/Header/Header.tsx @@ -1,7 +1,7 @@ import { PropsWithChildren } from 'react'; -import { TitleText } from '@renderer/components/ui-redesign/Typography'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { TitleText } from '@renderer/shared/ui/Typography'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { title: string; diff --git a/src/renderer/components/common/QrCode/QrGeneratorContainer/QrGeneratorContainer.tsx b/src/renderer/components/common/QrCode/QrGeneratorContainer/QrGeneratorContainer.tsx index ecc1893fb7..2152dc0636 100644 --- a/src/renderer/components/common/QrCode/QrGeneratorContainer/QrGeneratorContainer.tsx +++ b/src/renderer/components/common/QrCode/QrGeneratorContainer/QrGeneratorContainer.tsx @@ -1,10 +1,9 @@ import { PropsWithChildren } from 'react'; import cn from 'classnames'; -import { Button, CaptionText, FootnoteText, InfoLink, SmallTitleText } from '@renderer/components/ui-redesign'; -import { secondsToMinutes } from '@renderer/shared/utils/time'; -import { Icon, Shimmering } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Button, CaptionText, FootnoteText, InfoLink, SmallTitleText, Icon, Shimmering } from '@renderer/shared/ui'; +import { secondsToMinutes } from '@renderer/shared/lib/utils'; +import { useI18n } from '@renderer/app/providers'; import { ChainId } from '@renderer/domain/shared-kernel'; import { getMetadataPortalMetadataUrl, TROUBLESHOOTING_URL } from '../common/constants'; diff --git a/src/renderer/components/common/QrCode/QrReader/QrMultiframeSignatureReader.tsx b/src/renderer/components/common/QrCode/QrReader/QrMultiframeSignatureReader.tsx index a8839e58eb..e2112c651e 100644 --- a/src/renderer/components/common/QrCode/QrReader/QrMultiframeSignatureReader.tsx +++ b/src/renderer/components/common/QrCode/QrReader/QrMultiframeSignatureReader.tsx @@ -4,8 +4,8 @@ import { useEffect, useRef } from 'react'; import { u8aToHex } from '@polkadot/util'; import RaptorFrame from './RaptorFrame'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { useI18n } from '@renderer/context/I18nContext'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { useI18n } from '@renderer/app/providers'; import { HexString } from '@renderer/domain/shared-kernel'; import { QR_READER_ERRORS } from '../common/errors'; import { ErrorFields, FRAME_KEY, SIGNED_TRANSACTION_BULK } from '../common/constants'; diff --git a/src/renderer/components/common/QrCode/QrReader/QrReader.test.tsx b/src/renderer/components/common/QrCode/QrReader/QrReader.test.tsx index c4f1b4b4ed..aa8e35edc3 100644 --- a/src/renderer/components/common/QrCode/QrReader/QrReader.test.tsx +++ b/src/renderer/components/common/QrCode/QrReader/QrReader.test.tsx @@ -21,7 +21,7 @@ jest.mock('@zxing/browser', () => ({ })), })); -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/components/common/QrCode/QrReader/QrReader.tsx b/src/renderer/components/common/QrCode/QrReader/QrReader.tsx index 70dffdc95b..9175b1252e 100644 --- a/src/renderer/components/common/QrCode/QrReader/QrReader.tsx +++ b/src/renderer/components/common/QrCode/QrReader/QrReader.tsx @@ -3,10 +3,9 @@ import { BrowserCodeReader, BrowserQRCodeReader, IScannerControls } from '@zxing import init, { Decoder, EncodingPacket } from 'raptorq'; import { useEffect, useRef } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { validateSignerFormat } from '@renderer/shared/utils/strings'; +import { cnTw, validateSignerFormat } from '@renderer/shared/lib/utils'; import { CryptoTypeString } from '@renderer/domain/shared-kernel'; -import { useI18n } from '@renderer/context/I18nContext'; +import { useI18n } from '@renderer/app/providers'; import { ErrorFields, EXPORT_ADDRESS, FRAME_KEY } from '../common/constants'; import { QR_READER_ERRORS } from '../common/errors'; import { DecodeCallback, ErrorObject, Progress, QrError, SeedInfo, VideoInput } from '../common/types'; diff --git a/src/renderer/components/common/QrCode/QrReader/QrReaderWrapper.tsx b/src/renderer/components/common/QrCode/QrReader/QrReaderWrapper.tsx index 2149840379..b2d92df32c 100644 --- a/src/renderer/components/common/QrCode/QrReader/QrReaderWrapper.tsx +++ b/src/renderer/components/common/QrCode/QrReader/QrReaderWrapper.tsx @@ -1,13 +1,10 @@ import cn from 'classnames'; import React, { useEffect, useState } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { Shimmering } from '@renderer/components/ui'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui-redesign/Dropdowns/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; -import { ValidationErrors } from '@renderer/shared/utils/validation'; -import { secondsToMinutes } from '@renderer/shared/utils/time'; -import { Button, CaptionText, FootnoteText, Select, SmallTitleText } from '@renderer/components/ui-redesign'; +import { cnTw, ValidationErrors, secondsToMinutes } from '@renderer/shared/lib/utils'; +import { Shimmering, Button, CaptionText, FootnoteText, Select, SmallTitleText } from '@renderer/shared/ui'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { useI18n } from '@renderer/app/providers'; import SignatureReaderError from './SignatureReaderError'; import QrMultiframeSignatureReader from './QrMultiframeSignatureReader'; import { HexString } from '@renderer/domain/shared-kernel'; diff --git a/src/renderer/components/common/QrCode/QrReader/QrSignatureReader.tsx b/src/renderer/components/common/QrCode/QrReader/QrSignatureReader.tsx index 1b949930f0..9650e62f5c 100644 --- a/src/renderer/components/common/QrCode/QrReader/QrSignatureReader.tsx +++ b/src/renderer/components/common/QrCode/QrReader/QrSignatureReader.tsx @@ -3,8 +3,8 @@ import { BrowserCodeReader, BrowserQRCodeReader, IScannerControls } from '@zxing import { useEffect, useRef } from 'react'; import init from 'raptorq'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { useI18n } from '@renderer/context/I18nContext'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { useI18n } from '@renderer/app/providers'; import { ErrorFields } from '../common/constants'; import { QR_READER_ERRORS } from '../common/errors'; import { DecodeCallback, ErrorObject, QrError, VideoInput } from '../common/types'; diff --git a/src/renderer/components/common/QrCode/QrReader/SignatureReaderError.tsx b/src/renderer/components/common/QrCode/QrReader/SignatureReaderError.tsx index 1920ba1354..83ae7304ea 100644 --- a/src/renderer/components/common/QrCode/QrReader/SignatureReaderError.tsx +++ b/src/renderer/components/common/QrCode/QrReader/SignatureReaderError.tsx @@ -1,5 +1,5 @@ -import { Button, FootnoteText } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Button, FootnoteText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; import { CameraError, CameraErrorText } from '../common/constants'; type Props = { diff --git a/src/renderer/components/common/Scanning/ScanMultiframeQr.tsx b/src/renderer/components/common/Scanning/ScanMultiframeQr.tsx index bd8f70c6d1..38f3883bbf 100644 --- a/src/renderer/components/common/Scanning/ScanMultiframeQr.tsx +++ b/src/renderer/components/common/Scanning/ScanMultiframeQr.tsx @@ -4,16 +4,15 @@ import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import init, { Encoder } from 'raptorq'; import { useEffect, useState } from 'react'; +import { AccountDS } from '@renderer/shared/api/storage'; import { Command } from '@renderer/components/common/QrCode/QrGenerator/common/constants'; import QrMultiframeGenerator from '@renderer/components/common/QrCode/QrGenerator/QrMultiframeTxGenerator'; import { TRANSACTION_BULK } from '@renderer/components/common/QrCode/common/constants'; -import { useI18n } from '@renderer/context/I18nContext'; +import { useI18n } from '@renderer/app/providers'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { Transaction } from '@renderer/domain/transaction'; -import { AccountDS } from '@renderer/services/storage'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { toAddress } from '@renderer/shared/utils/address'; -import { Button } from '@renderer/components/ui-redesign'; +import { Transaction, useTransaction } from '@renderer/entities/transaction'; +import { toAddress } from '@renderer/shared/lib/utils'; +import { Button } from '@renderer/shared/ui'; import { QrGeneratorContainer } from '@renderer/components/common'; import { createMultipleSignPayload, diff --git a/src/renderer/components/common/Scanning/ScanSingleframeQr.tsx b/src/renderer/components/common/Scanning/ScanSingleframeQr.tsx index eb299106c2..0bcb0c0ea2 100644 --- a/src/renderer/components/common/Scanning/ScanSingleframeQr.tsx +++ b/src/renderer/components/common/Scanning/ScanSingleframeQr.tsx @@ -3,14 +3,12 @@ import { useEffect, useState } from 'react'; import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { QrTxGenerator, QrGeneratorContainer } from '@renderer/components/common'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Transaction } from '@renderer/domain/transaction'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; +import { useI18n } from '@renderer/app/providers'; +import { Transaction, useTransaction } from '@renderer/entities/transaction'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { Explorer } from '@renderer/domain/chain'; -import { Account } from '@renderer/domain/account'; -import { Button, FootnoteText } from '@renderer/components/ui-redesign'; -import AddressWithExplorers from '@renderer/components/common/AddressWithExplorers/AddressWithExplorers'; +import { Explorer } from '@renderer/entities/chain'; +import { Account, AddressWithExplorers } from '@renderer/entities/account'; +import { Button, FootnoteText } from '@renderer/shared/ui'; type Props = { api: ApiPromise; diff --git a/src/renderer/components/common/index.ts b/src/renderer/components/common/index.ts index 587ec14c58..f7f1b03488 100644 --- a/src/renderer/components/common/index.ts +++ b/src/renderer/components/common/index.ts @@ -4,17 +4,10 @@ import QrSignatureReader from './QrCode/QrReader/QrSignatureReader'; import QrTextGenerator from './QrCode/QrGenerator/QrTextGenerator'; import QrTxGenerator from './QrCode/QrGenerator/QrTxGenerator'; import Message from './Message/Message'; -import Deposit from './Deposit/Deposit'; -import Fee from './Fee/Fee'; import Header from './Header/Header'; import ExplorerLink from './ExplorerLink/ExplorerLink'; -import BalanceNew from './BalanceNew/BalanceNew'; -import AccountAddress from './AccountAddress/AccountAddress'; -import AddressWithName from './AddressWithName/AddressWithName'; import QrGeneratorContainer from './QrCode/QrGeneratorContainer/QrGeneratorContainer'; -import DetailRow from '@renderer/components/common/DetailRow/DetailRow'; import ExtrinsicExplorers from './ExtrinsicExplorers/ExtrinsicExplorers'; -import { DepositWithLabel } from './DepositWithLabel/DepositWithLabel'; // FIXME: SignatoryCard, AddressWithExplorers, ScanMultiframeQr and ScanSingleframeQr exported separately. // Adding them to this file causes to crash all tests which use anything from that file @@ -28,15 +21,8 @@ export { QrTextGenerator, QrGeneratorContainer, QrTxGenerator, - Deposit, - DepositWithLabel, Message, - Fee, Header, ExplorerLink, - BalanceNew, - AccountAddress, - AddressWithName, - DetailRow, ExtrinsicExplorers, }; diff --git a/src/renderer/components/forms/ContactForm/ContactForm.test.tsx b/src/renderer/components/forms/ContactForm/ContactForm.test.tsx index a4e0e5d160..fca41751cd 100644 --- a/src/renderer/components/forms/ContactForm/ContactForm.test.tsx +++ b/src/renderer/components/forms/ContactForm/ContactForm.test.tsx @@ -2,22 +2,19 @@ import { render, screen } from '@testing-library/react'; import noop from 'lodash/noop'; import { ContactForm } from './ContactForm'; -import { Contact } from '@renderer/domain/contact'; -import { TEST_ADDRESS, TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { Contact } from '@renderer/entities/contact'; +import { TEST_ADDRESS, TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), -})); - -jest.mock('@renderer/context/MatrixContext', () => ({ useMatrix: jest.fn().mockReturnValue({ matrix: { validateFullUserName: jest.fn().mockReturnValue(true) }, }), })); -jest.mock('@renderer/services/contact/contactService', () => ({ +jest.mock('@renderer/entities/contact', () => ({ useContact: jest.fn().mockReturnValue({ addContact: jest.fn(), updateContact: jest.fn(), @@ -25,7 +22,7 @@ jest.mock('@renderer/services/contact/contactService', () => ({ }), })); -describe('screens/AddressBook/ContactForm', () => { +describe('pages/AddressBook/ContactForm', () => { const contact: Contact = { name: 'Contact', address: TEST_ADDRESS, diff --git a/src/renderer/components/forms/ContactForm/ContactForm.tsx b/src/renderer/components/forms/ContactForm/ContactForm.tsx index 693f3c5ffc..f4e30476bc 100644 --- a/src/renderer/components/forms/ContactForm/ContactForm.tsx +++ b/src/renderer/components/forms/ContactForm/ContactForm.tsx @@ -1,14 +1,11 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import { useEffect, useState } from 'react'; -import { Icon, Identicon } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Icon, Identicon, Button, Input, InputHint } from '@renderer/shared/ui'; +import { useI18n, useMatrix } from '@renderer/app/providers'; import { Address, ErrorType } from '@renderer/domain/shared-kernel'; -import { useContact } from '@renderer/services/contact/contactService'; -import { toAccountId, validateAddress } from '@renderer/shared/utils/address'; -import { Button, Input, InputHint } from '@renderer/components/ui-redesign'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { Contact } from '@renderer/domain/contact'; +import { useContact, Contact } from '@renderer/entities/contact'; +import { toAccountId, validateAddress } from '@renderer/shared/lib/utils'; type ContactFormData = { name: string; diff --git a/src/renderer/components/layout/PrimaryLayout/NavItem/NavItem.tsx b/src/renderer/components/layout/PrimaryLayout/NavItem/NavItem.tsx index 137c510122..a7cf68ee9a 100644 --- a/src/renderer/components/layout/PrimaryLayout/NavItem/NavItem.tsx +++ b/src/renderer/components/layout/PrimaryLayout/NavItem/NavItem.tsx @@ -1,11 +1,10 @@ import { ReactNode } from 'react'; import { NavLink } from 'react-router-dom'; -import { Icon } from '@renderer/components/ui'; -import { BodyText } from '@renderer/components/ui-redesign'; -import { IconNames } from '@renderer/components/ui/Icon/data'; -import { useI18n } from '@renderer/context/I18nContext'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { Icon, BodyText } from '@renderer/shared/ui'; +import { IconNames } from '@renderer/shared/ui/Icon/data'; +import { useI18n } from '@renderer/app/providers'; +import { cnTw } from '@renderer/shared/lib/utils'; export type Props = { title: string; diff --git a/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.test.tsx b/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.test.tsx index 21f7eefa9d..759c1e9813 100644 --- a/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.test.tsx +++ b/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.test.tsx @@ -2,28 +2,28 @@ import { render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import Navigation from './Navigation'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ LocaleComponent: () =>
localeComponent
, t: (key: string) => key, }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getActiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/services/multisigTx/multisigTxService', () => ({ +jest.mock('@renderer/entities/multisig', () => ({ useMultisigTx: jest.fn().mockReturnValue({ getLiveAccountMultisigTxs: jest.fn().mockReturnValue([]), }), })); -jest.mock('@renderer/services/wallet/walletService', () => ({ +jest.mock('@renderer/entities/wallet', () => ({ useWallet: jest.fn().mockReturnValue({ getLiveWallets: jest.fn().mockReturnValue([]), }), diff --git a/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx b/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx index 6fb1a97f70..d31a2e1bab 100644 --- a/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx +++ b/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx @@ -2,18 +2,18 @@ import { useEffect, useState } from 'react'; import { keyBy } from 'lodash'; import cn from 'classnames'; -import { useAccount } from '@renderer/services/account/accountService'; -import { useMultisigTx } from '@renderer/services/multisigTx/multisigTxService'; +import { useAccount } from '@renderer/entities/account'; +import { useMultisigTx } from '@renderer/entities/multisig'; import './Navigation.css'; -import { MultisigTxInitStatus } from '@renderer/domain/transaction'; +import { MultisigTxInitStatus } from '@renderer/entities/transaction'; import WalletMenu from '@renderer/components/layout/PrimaryLayout/Wallets/WalletMenu'; import ActiveAccountCard from '@renderer/components/layout/PrimaryLayout/Wallets/ActiveAccountCard'; import NavItem, { Props as NavItemProps } from '../NavItem/NavItem'; -import { useChains } from '@renderer/services/network/chainsService'; +import { useChains } from '@renderer/entities/network'; import { ChainsRecord } from '@renderer/components/layout/PrimaryLayout/Wallets/common/types'; -import Paths from '@renderer/routes/paths'; -import { useWallet } from '@renderer/services/wallet/walletService'; -import { Shimmering } from '@renderer/components/ui'; +import { Paths } from '../../../../app/providers/routes/paths'; +import { useWallet } from '@renderer/entities/wallet'; +import { Shimmering } from '@renderer/shared/ui'; const Navigation = () => { const { getActiveAccounts } = useAccount(); diff --git a/src/renderer/components/layout/PrimaryLayout/Wallets/ActiveAccountCard.tsx b/src/renderer/components/layout/PrimaryLayout/Wallets/ActiveAccountCard.tsx index 63ec11e16d..0271abedba 100644 --- a/src/renderer/components/layout/PrimaryLayout/Wallets/ActiveAccountCard.tsx +++ b/src/renderer/components/layout/PrimaryLayout/Wallets/ActiveAccountCard.tsx @@ -1,13 +1,11 @@ -import { BodyText, CaptionText, FootnoteText } from '@renderer/components/ui-redesign'; -import { Icon, Identicon } from '@renderer/components/ui'; +import { BodyText, CaptionText, FootnoteText, Icon, Identicon } from '@renderer/shared/ui'; import { WalletType } from '@renderer/domain/shared-kernel'; import { GroupIcons, GroupLabels } from '@renderer/components/layout/PrimaryLayout/Wallets/common/constants'; -import { toAddress } from '@renderer/shared/utils/address'; -import { SS58_DEFAULT_PREFIX } from '@renderer/shared/utils/constants'; -import { useI18n } from '@renderer/context/I18nContext'; -import { WalletDS } from '@renderer/services/storage'; +import { toAddress, SS58_DEFAULT_PREFIX } from '@renderer/shared/lib/utils'; +import { useI18n } from '@renderer/app/providers'; +import { WalletDS } from '@renderer/shared/api/storage'; import { ChainsRecord } from './common/types'; -import { Account, getActiveWalletType } from '@renderer/domain/account'; +import { Account, getActiveWalletType } from '@renderer/entities/account'; type Props = { activeAccounts: Account[]; diff --git a/src/renderer/components/layout/PrimaryLayout/Wallets/WalletGroup.tsx b/src/renderer/components/layout/PrimaryLayout/Wallets/WalletGroup.tsx index fa3f55c157..ffa4e0a4a0 100644 --- a/src/renderer/components/layout/PrimaryLayout/Wallets/WalletGroup.tsx +++ b/src/renderer/components/layout/PrimaryLayout/Wallets/WalletGroup.tsx @@ -2,16 +2,13 @@ import { Disclosure } from '@headlessui/react'; import cn from 'classnames'; import { WalletGroupItem, MultishardWallet } from '@renderer/components/layout/PrimaryLayout/Wallets/common/types'; -import { Icon } from '@renderer/components/ui'; +import { Icon, HelpText, BodyText, CaptionText } from '@renderer/shared/ui'; import { WalletType } from '@renderer/domain/shared-kernel'; import { GroupIcons, GroupLabels } from '@renderer/components/layout/PrimaryLayout/Wallets/common/constants'; -import { BodyText, CaptionText } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; -import { AccountAddress } from '@renderer/components/common'; -import { Account } from '@renderer/domain/account'; +import { useI18n } from '@renderer/app/providers'; +import { Account, AccountAddress } from '@renderer/entities/account'; import { isMultishardWalletItem } from '@renderer/components/layout/PrimaryLayout/Wallets/common/utils'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { type: WalletType; diff --git a/src/renderer/components/layout/PrimaryLayout/Wallets/WalletMenu.tsx b/src/renderer/components/layout/PrimaryLayout/Wallets/WalletMenu.tsx index 14e4c6ba8d..82d60d6c0e 100644 --- a/src/renderer/components/layout/PrimaryLayout/Wallets/WalletMenu.tsx +++ b/src/renderer/components/layout/PrimaryLayout/Wallets/WalletMenu.tsx @@ -3,21 +3,20 @@ import { Fragment, PropsWithChildren, useState } from 'react'; import cn from 'classnames'; import { useNavigate } from 'react-router-dom'; -import { DropdownButton, SearchInput, SmallTitleText } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; +import { DropdownButton, SearchInput, SmallTitleText } from '@renderer/shared/ui'; +import { useI18n, Paths } from '@renderer/app/providers'; import { WalletType } from '@renderer/domain/shared-kernel'; -import { useAccount } from '@renderer/services/account/accountService'; +import { useAccount } from '@renderer/entities/account'; import WalletGroup from '@renderer/components/layout/PrimaryLayout/Wallets/WalletGroup'; import { useGroupedWallets } from './common/useGroupedWallets'; -import { ID, WalletDS } from '@renderer/services/storage'; -import WatchOnly from '@renderer/screens/Onboarding/WatchOnly/WatchOnly'; -import Vault from '@renderer/screens/Onboarding/Vault/Vault'; -import { useToggle } from '@renderer/shared/hooks'; -import { ButtonDropdownOption } from '@renderer/components/ui-redesign/Dropdowns/DropdownButton/DropdownButton'; +import { ID, WalletDS } from '@renderer/shared/api/storage'; +import WatchOnly from '@renderer/pages/Onboarding/WatchOnly/WatchOnly'; +import Vault from '@renderer/pages/Onboarding/Vault/Vault'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { ButtonDropdownOption } from '@renderer/shared/ui/types'; import { CreateMultisigAccount } from '@renderer/components/modals'; import { isMultishardWalletItem } from '@renderer/components/layout/PrimaryLayout/Wallets/common/utils'; -import Paths from '@renderer/routes/paths'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; +import { DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; import { ChainsRecord, WalletGroupItem, diff --git a/src/renderer/components/layout/PrimaryLayout/Wallets/common/constants.ts b/src/renderer/components/layout/PrimaryLayout/Wallets/common/constants.ts index b7592656b8..ecc3d28b4c 100644 --- a/src/renderer/components/layout/PrimaryLayout/Wallets/common/constants.ts +++ b/src/renderer/components/layout/PrimaryLayout/Wallets/common/constants.ts @@ -1,5 +1,5 @@ import { WalletType } from '@renderer/domain/shared-kernel'; -import { IconNames } from '@renderer/components/ui/Icon/data'; +import { IconNames } from '@renderer/shared/ui/Icon/data'; export const GroupLabels: Record = { [WalletType.MULTISIG]: 'wallets.multisigLabel', diff --git a/src/renderer/components/layout/PrimaryLayout/Wallets/common/types.ts b/src/renderer/components/layout/PrimaryLayout/Wallets/common/types.ts index 07a5c7687d..bc643d7c02 100644 --- a/src/renderer/components/layout/PrimaryLayout/Wallets/common/types.ts +++ b/src/renderer/components/layout/PrimaryLayout/Wallets/common/types.ts @@ -1,6 +1,6 @@ -import { Chain } from '@renderer/domain/chain'; +import { AccountDS, WalletDS } from '@renderer/shared/api/storage'; +import { Chain } from '@renderer/entities/chain/model/chain'; import { ChainId, WalletType } from '@renderer/domain/shared-kernel'; -import { AccountDS, WalletDS } from '@renderer/services/storage'; export type ChainWithAccounts = Chain & { accounts: AccountDS[] }; export type RootAccount = AccountDS & { chains: ChainWithAccounts[]; amount: number }; diff --git a/src/renderer/components/layout/PrimaryLayout/Wallets/common/useGroupedWallets.ts b/src/renderer/components/layout/PrimaryLayout/Wallets/common/useGroupedWallets.ts index cc70c1a03e..f6e41efaad 100644 --- a/src/renderer/components/layout/PrimaryLayout/Wallets/common/useGroupedWallets.ts +++ b/src/renderer/components/layout/PrimaryLayout/Wallets/common/useGroupedWallets.ts @@ -1,14 +1,13 @@ import { useEffect, useState } from 'react'; import { uniq } from 'lodash'; -import { WalletDS } from '@renderer/services/storage'; +import { WalletDS } from '@renderer/shared/api/storage'; import { ChainsRecord, GroupedWallets } from './types'; import { getMultishardStructure } from '@renderer/components/layout/PrimaryLayout/Wallets/common/utils'; import { SigningType, WalletType } from '@renderer/domain/shared-kernel'; -import { includes } from '@renderer/shared/utils/strings'; -import { useAccount } from '@renderer/services/account/accountService'; -import { Account } from '@renderer/domain/account'; -import { toAddress } from '@renderer/shared/utils/address'; +import { includes, toAddress } from '@renderer/shared/lib/utils'; +import { useAccount } from '@renderer/entities/account/lib/accountService'; +import { Account } from '@renderer/entities/account/model/account'; export const useGroupedWallets = ( liveWallets: WalletDS[], diff --git a/src/renderer/components/layout/PrimaryLayout/Wallets/common/utils.ts b/src/renderer/components/layout/PrimaryLayout/Wallets/common/utils.ts index 1d2ed82e6c..1dfd1785c4 100644 --- a/src/renderer/components/layout/PrimaryLayout/Wallets/common/utils.ts +++ b/src/renderer/components/layout/PrimaryLayout/Wallets/common/utils.ts @@ -1,6 +1,6 @@ import { groupBy } from 'lodash'; -import { AccountDS } from '@renderer/services/storage'; +import { AccountDS } from '@renderer/shared/api/storage'; import { ChainsRecord, ChainWithAccounts, @@ -10,7 +10,7 @@ import { SelectableShards, WalletGroupItem, } from '@renderer/components/layout/PrimaryLayout/Wallets/common/types'; -import { includes } from '@renderer/shared/utils/strings'; +import { includes } from '@renderer/shared/lib/utils'; const getRootAccount = (accounts: AccountDS[], chains: ChainsRecord, root: AccountDS): RootAccount => { const accountsByChain = groupBy( diff --git a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx index 045ac1d0f0..e6762c8515 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx @@ -2,20 +2,27 @@ import { render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import noop from 'lodash/noop'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { CreateMultisigAccount } from './CreateMultisigAccount'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), + useMatrix: jest.fn().mockReturnValue({ + isLoggedIn: true, + matrix: { + createRoom: jest.fn(), + userId: 'userId', + }, + }), })); jest.mock('react-router-dom', () => ({ useNavigate: jest.fn(), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ addAccount: jest.fn(), setActiveAccount: jest.fn(), @@ -23,39 +30,29 @@ jest.mock('@renderer/services/account/accountService', () => ({ }), })); -jest.mock('@renderer/services/contact/contactService', () => ({ +jest.mock('@renderer/entities/contact', () => ({ useContact: jest.fn().mockReturnValue({ getLiveContacts: jest.fn().mockReturnValue([]), }), })); -jest.mock('@renderer/services/wallet/walletService', () => ({ +jest.mock('@renderer/entities/wallet', () => ({ useWallet: jest.fn().mockReturnValue({ getWallets: jest.fn().mockResolvedValue([]), }), })); -jest.mock('@renderer/services/network/chainsService', () => ({ +jest.mock('@renderer/entities/network', () => ({ useChains: jest.fn().mockReturnValue({ getChainsData: jest.fn().mockResolvedValue([]), }), })); -jest.mock('@renderer/context/MatrixContext', () => ({ - useMatrix: jest.fn().mockReturnValue({ - isLoggedIn: true, - matrix: { - createRoom: jest.fn(), - userId: 'userId', - }, - }), -})); - jest.mock('@renderer/components/modals/MatrixModal/MatrixModal', () => ({ MatrixModal: () => matrixModal, })); -jest.mock('@renderer/components/common/OperationResult/OperationResult', () => ({ +jest.mock('@renderer/entities/transaction', () => ({ OperationResult: () => operationResult, })); diff --git a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx index 7a0b9b53c0..f346a2e13f 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx @@ -1,21 +1,23 @@ import { ComponentProps, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { BaseModal, HeaderTitleText, StatusLabel, Button } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { useAccount } from '@renderer/services/account/accountService'; -import { createMultisigAccount, MultisigAccount, Account, getMultisigAccountId } from '@renderer/domain/account'; -import { useToggle } from '@renderer/shared/hooks'; -import { OperationResult } from '@renderer/components/common/OperationResult/OperationResult'; +import { BaseModal, HeaderTitleText, StatusLabel, Button } from '@renderer/shared/ui'; +import { useI18n, useMatrix, Paths } from '@renderer/app/providers'; +import { + useAccount, + createMultisigAccount, + MultisigAccount, + Account, + getMultisigAccountId, +} from '@renderer/entities/account'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { OperationResult } from '@renderer/entities/transaction'; import { MatrixModal } from '../MatrixModal/MatrixModal'; -import { Wallet } from '@renderer/domain/wallet'; -import { useWallet } from '@renderer/services/wallet/walletService'; -import { useContact } from '@renderer/services/contact/contactService'; +import { Wallet, useWallet } from '@renderer/entities/wallet'; +import { useContact } from '@renderer/entities/contact'; import { ExtendedContact, ExtendedWallet } from './common/types'; import { SelectSignatories, ConfirmSignatories, WalletForm } from './components'; import { AccountId } from '@renderer/domain/shared-kernel'; -import Paths from '@renderer/routes/paths'; type OperationResultProps = Pick, 'variant' | 'description'>; diff --git a/src/renderer/components/modals/CreateMultisigAccount/common/types.ts b/src/renderer/components/modals/CreateMultisigAccount/common/types.ts index 36476c34a1..bcfc71b6e3 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/common/types.ts +++ b/src/renderer/components/modals/CreateMultisigAccount/common/types.ts @@ -1,4 +1,4 @@ -import { Contact } from '@renderer/domain/contact'; +import { Contact } from '@renderer/entities/contact/model/contact'; import { ChainId, AccountId } from '@renderer/domain/shared-kernel'; export type ExtendedContact = Contact & { index: string }; diff --git a/src/renderer/components/modals/CreateMultisigAccount/components/ConfirmSignatories.tsx b/src/renderer/components/modals/CreateMultisigAccount/components/ConfirmSignatories.tsx index 6bc73f5049..2892861930 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/components/ConfirmSignatories.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/components/ConfirmSignatories.tsx @@ -1,7 +1,6 @@ -import cnTw from '@renderer/shared/utils/twMerge'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { FootnoteText, SmallTitleText } from '@renderer/components/ui-redesign'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { useI18n, useNetworkContext } from '@renderer/app/providers'; +import { FootnoteText, SmallTitleText } from '@renderer/shared/ui'; import { WalletsTabItem } from './WalletsTabItem'; import { ExtendedWallet, ExtendedContact } from '../common/types'; diff --git a/src/renderer/components/modals/CreateMultisigAccount/components/SelectSignatories.tsx b/src/renderer/components/modals/CreateMultisigAccount/components/SelectSignatories.tsx index bd2addac57..201285aea4 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/components/SelectSignatories.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/components/SelectSignatories.tsx @@ -1,25 +1,13 @@ import { useState, useEffect } from 'react'; import { keyBy } from 'lodash'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { includes } from '@renderer/shared/utils/strings'; -import { toAddress } from '@renderer/shared/utils/address'; +import { cnTw, includes, toAddress } from '@renderer/shared/lib/utils'; +import { useI18n, useMatrix, useNetworkContext } from '@renderer/app/providers'; import { AccountId } from '@renderer/domain/shared-kernel'; -import { useToggle } from '@renderer/shared/hooks'; +import { useToggle } from '@renderer/shared/lib/hooks'; import { WalletsTabItem } from './WalletsTabItem'; -import { Icon } from '@renderer/components/ui'; -import { EmptyContacts } from '@renderer/screens/AddressBook/Overview/components'; -import { isWalletContact, Account, MultisigAccount } from '@renderer/domain/account'; -import { ContactForm } from '@renderer/components/forms'; -import { TabItem } from '@renderer/components/ui-redesign/Tabs/common/types'; -import { Contact } from '@renderer/domain/contact'; -import { WalletDS } from '@renderer/services/storage'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { getSelectedLength } from '../common/utils'; -import { ExtendedWallet, ExtendedContact, SelectedMap } from '../common/types'; import { + Icon, FootnoteText, SearchInput, SmallTitleText, @@ -27,7 +15,15 @@ import { Button, BaseModal, Tabs, -} from '@renderer/components/ui-redesign'; +} from '@renderer/shared/ui'; +import { EmptyContacts } from '@renderer/pages/AddressBook/Overview/components'; +import { isWalletContact, Account, MultisigAccount } from '@renderer/entities/account'; +import { ContactForm } from '@renderer/components/forms'; +import { TabItem } from '@renderer/shared/ui/types'; +import { Contact } from '@renderer/entities/contact'; +import { WalletDS } from '@renderer/shared/api/storage'; +import { getSelectedLength } from '../common/utils'; +import { ExtendedWallet, ExtendedContact, SelectedMap } from '../common/types'; const enum SignatoryTabs { WALLETS = 'wallets', diff --git a/src/renderer/components/modals/CreateMultisigAccount/components/WalletForm.tsx b/src/renderer/components/modals/CreateMultisigAccount/components/WalletForm.tsx index 65c2749bfe..bc9dc68b81 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/components/WalletForm.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/components/WalletForm.tsx @@ -1,12 +1,17 @@ import { Controller, useForm, SubmitHandler } from 'react-hook-form'; -import { Alert, Button, Input, InputHint, Select, SmallTitleText } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui/Dropdowns/common/types'; -import { Signatory } from '@renderer/domain/signatory'; -import { getMultisigAccountId, isMultisig, isWalletContact, Account, MultisigAccount } from '@renderer/domain/account'; +import { Alert, Button, Input, InputHint, Select, SmallTitleText } from '@renderer/shared/ui'; +import { useI18n, useMatrix } from '@renderer/app/providers'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { Signatory } from '@renderer/entities/signatory'; +import { + getMultisigAccountId, + isMultisig, + isWalletContact, + Account, + MultisigAccount, +} from '@renderer/entities/account'; import { SigningType, AccountId } from '@renderer/domain/shared-kernel'; -import { useMatrix } from '@renderer/context/MatrixContext'; type MultisigAccountForm = { name: string; diff --git a/src/renderer/components/modals/CreateMultisigAccount/components/WalletsTabItem.tsx b/src/renderer/components/modals/CreateMultisigAccount/components/WalletsTabItem.tsx index 0fea4b292d..8682d21e41 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/components/WalletsTabItem.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/components/WalletsTabItem.tsx @@ -1,9 +1,7 @@ -import { Explorer } from '@renderer/domain/chain'; -import { toAddress } from '@renderer/shared/utils/address'; -import useAddressInfo from '@renderer/components/common/AccountAddress/useAddressInfo'; -import { Icon, Identicon } from '@renderer/components/ui'; -import { BodyText, InfoPopover } from '@renderer/components/ui-redesign'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; +import { useAddressInfo } from '@renderer/entities/account'; +import { Explorer } from '@renderer/entities/chain'; +import { toAddress } from '@renderer/shared/lib/utils'; +import { Icon, Identicon, BodyText, InfoPopover, HelpText } from '@renderer/shared/ui'; import { ExtendedWallet } from '../common/types'; type Props = Pick & { explorers?: Explorer[] }; diff --git a/src/renderer/components/modals/MatrixModal/Matrix.test.tsx b/src/renderer/components/modals/MatrixModal/Matrix.test.tsx index 2ae90948f6..52b1a102d0 100644 --- a/src/renderer/components/modals/MatrixModal/Matrix.test.tsx +++ b/src/renderer/components/modals/MatrixModal/Matrix.test.tsx @@ -3,15 +3,12 @@ import { MemoryRouter } from 'react-router-dom'; import noop from 'lodash/noop'; import { MatrixModal } from './MatrixModal'; -import { useMatrix } from '@renderer/context/MatrixContext'; +import { useMatrix } from '@renderer/app/providers'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), -})); - -jest.mock('@renderer/context/MatrixContext', () => ({ useMatrix: jest.fn().mockReturnValue({ isLoggedIn: false, matrix: { diff --git a/src/renderer/components/modals/MatrixModal/MatrixModal.tsx b/src/renderer/components/modals/MatrixModal/MatrixModal.tsx index 25a4223adc..d73f4fc1fb 100644 --- a/src/renderer/components/modals/MatrixModal/MatrixModal.tsx +++ b/src/renderer/components/modals/MatrixModal/MatrixModal.tsx @@ -1,6 +1,5 @@ -import { useI18n } from '@renderer/context/I18nContext'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { BaseModal } from '@renderer/components/ui-redesign'; +import { useI18n, useMatrix } from '@renderer/app/providers'; +import { BaseModal } from '@renderer/shared/ui'; import MatrixInfoPopover from './components/MatrixInfoPopover/MatrixInfoPopover'; import Credentials from './components/Credentials/Credentials'; import Verification from './components/Verification/Verification'; diff --git a/src/renderer/components/modals/MatrixModal/components/Credentials/Credentials.test.tsx b/src/renderer/components/modals/MatrixModal/components/Credentials/Credentials.test.tsx index 0d3d9d5501..6c8891fec8 100644 --- a/src/renderer/components/modals/MatrixModal/components/Credentials/Credentials.test.tsx +++ b/src/renderer/components/modals/MatrixModal/components/Credentials/Credentials.test.tsx @@ -2,21 +2,18 @@ import { render, screen, act } from '@testing-library/react'; import noop from 'lodash/noop'; import Credentials from './Credentials'; -import { useMatrix } from '@renderer/context/MatrixContext'; +import { useMatrix } from '@renderer/app/providers'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), -})); - -jest.mock('@renderer/context/MatrixContext', () => ({ useMatrix: jest.fn().mockReturnValue({ matrix: { logout: jest.fn() }, }), })); -describe('screens/Settings/Matrix/Credentials', () => { +describe('pages/Settings/Matrix/Credentials', () => { afterEach(() => { jest.clearAllMocks(); }); diff --git a/src/renderer/components/modals/MatrixModal/components/Credentials/Credentials.tsx b/src/renderer/components/modals/MatrixModal/components/Credentials/Credentials.tsx index 697190c9dd..1d451d9b71 100644 --- a/src/renderer/components/modals/MatrixModal/components/Credentials/Credentials.tsx +++ b/src/renderer/components/modals/MatrixModal/components/Credentials/Credentials.tsx @@ -1,7 +1,6 @@ -import { useI18n } from '@renderer/context/I18nContext'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { useToggle } from '@renderer/shared/hooks'; -import { Button, FootnoteText, StatusLabel } from '@renderer/components/ui-redesign'; +import { useI18n, useMatrix } from '@renderer/app/providers'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { Button, FootnoteText, StatusLabel } from '@renderer/shared/ui'; type Props = { onLogOut: () => void; diff --git a/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.test.tsx b/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.test.tsx index 78d69e7dbb..2bc99df669 100644 --- a/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.test.tsx +++ b/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.test.tsx @@ -5,7 +5,7 @@ import LoginForm from './LoginForm'; jest.mock('react-i18next', () => ({ Trans: (props: any) => props.i18nKey })); -jest.mock('@renderer/context/MatrixContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useMatrix: jest.fn().mockReturnValue({ matrix: { setHomeserver: jest.fn(), @@ -13,9 +13,6 @@ jest.mock('@renderer/context/MatrixContext', () => ({ loginWithCreds: jest.fn(), }, }), -})); - -jest.mock('@renderer/context/I18nContext', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx b/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx index 1836c170a7..ec2bb3081e 100644 --- a/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx +++ b/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx @@ -2,9 +2,8 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import { Trans } from 'react-i18next'; import { useEffect, useState } from 'react'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { useI18n } from '@renderer/context/I18nContext'; -import { WELL_KNOWN_SERVERS } from '@renderer/services/matrix'; +import { useMatrix, useI18n } from '@renderer/app/providers'; +import { WELL_KNOWN_SERVERS } from '@renderer/entities/matrix'; import { Alert, Button, @@ -14,9 +13,10 @@ import { Input, InputHint, PasswordInput, -} from '@renderer/components/ui-redesign'; -import { Icon, Loader } from '@renderer/components/ui'; -import { ComboboxOption } from '@renderer/components/ui-redesign/Dropdowns/common/types'; + Icon, + Loader, +} from '@renderer/shared/ui'; +import { ComboboxOption } from '@renderer/shared/ui/Dropdowns/common/types'; const HOME_SERVERS = WELL_KNOWN_SERVERS.map((server) => ({ id: server.domain, diff --git a/src/renderer/components/modals/MatrixModal/components/MatrixInfoPopover/MatrixInfoPopover.test.tsx b/src/renderer/components/modals/MatrixModal/components/MatrixInfoPopover/MatrixInfoPopover.test.tsx index 4f80578a9b..f5f59dee45 100644 --- a/src/renderer/components/modals/MatrixModal/components/MatrixInfoPopover/MatrixInfoPopover.test.tsx +++ b/src/renderer/components/modals/MatrixModal/components/MatrixInfoPopover/MatrixInfoPopover.test.tsx @@ -5,13 +5,13 @@ import MatrixInfoPopover from './MatrixInfoPopover'; jest.mock('react-i18next', () => ({ Trans: (props: any) => props.i18nKey })); -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -describe('screens/Settings/Matrix/MatrixInfoPopover', () => { +describe('pages/Settings/Matrix/MatrixInfoPopover', () => { test('should render component', async () => { const user = userEvent.setup(); diff --git a/src/renderer/components/modals/MatrixModal/components/MatrixInfoPopover/MatrixInfoPopover.tsx b/src/renderer/components/modals/MatrixModal/components/MatrixInfoPopover/MatrixInfoPopover.tsx index b6b87f78be..790b9f5f5f 100644 --- a/src/renderer/components/modals/MatrixModal/components/MatrixInfoPopover/MatrixInfoPopover.tsx +++ b/src/renderer/components/modals/MatrixModal/components/MatrixInfoPopover/MatrixInfoPopover.tsx @@ -1,7 +1,7 @@ import { Trans } from 'react-i18next'; -import { FootnoteText, InfoLink, LabelHelpBox, Popover, SmallTitleText } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; +import { FootnoteText, InfoLink, LabelHelpBox, Popover, SmallTitleText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; const MATRIX = 'https://matrix.org/'; const SMP = 'https://docs.novaspektr.io/multisig-accounts/spektr-matrix-protocol'; diff --git a/src/renderer/components/modals/MatrixModal/components/Verification/Verification.test.tsx b/src/renderer/components/modals/MatrixModal/components/Verification/Verification.test.tsx index 232ba4775a..fe1f5454d4 100644 --- a/src/renderer/components/modals/MatrixModal/components/Verification/Verification.test.tsx +++ b/src/renderer/components/modals/MatrixModal/components/Verification/Verification.test.tsx @@ -2,19 +2,12 @@ import { render, screen, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import Verification from './Verification'; -import { useMatrix } from '@renderer/context/MatrixContext'; +import { useMatrix } from '@renderer/app/providers'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), -})); - -jest.mock('@renderer/shared/utils/browser', () => ({ - getOperatingSystem: jest.fn().mockReturnValue('macOS'), -})); - -jest.mock('@renderer/context/MatrixContext', () => ({ useMatrix: jest.fn().mockReturnValue({ matrix: { sessionIsVerified: false, @@ -24,7 +17,12 @@ jest.mock('@renderer/context/MatrixContext', () => ({ }), })); -describe('screens/Settings/Matrix/Verification', () => { +jest.mock('@renderer/shared/lib/utils', () => ({ + ...jest.requireActual('@renderer/shared/lib/utils'), + getOperatingSystem: jest.fn().mockReturnValue('macOS'), +})); + +describe('pages/Settings/Matrix/Verification', () => { const submitFormWithString = async () => { const user = userEvent.setup({ delay: null }); render(); diff --git a/src/renderer/components/modals/MatrixModal/components/Verification/Verification.tsx b/src/renderer/components/modals/MatrixModal/components/Verification/Verification.tsx index cb65e7cd5d..d189dbba97 100644 --- a/src/renderer/components/modals/MatrixModal/components/Verification/Verification.tsx +++ b/src/renderer/components/modals/MatrixModal/components/Verification/Verification.tsx @@ -2,11 +2,9 @@ import { useState } from 'react'; import { TFunction } from 'react-i18next'; import { useForm, SubmitHandler, Controller } from 'react-hook-form'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Button, Input, InputHint, FootnoteText, InputFile, Tabs } from '@renderer/components/ui-redesign'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { TabItem } from '@renderer/components/ui-redesign/Tabs/common/types'; -import { Icon } from '@renderer/components/ui'; +import { useI18n, useMatrix } from '@renderer/app/providers'; +import { Button, Input, InputHint, FootnoteText, InputFile, Tabs, Icon } from '@renderer/shared/ui'; +import { TabItem } from '@renderer/shared/ui/Tabs/common/types'; type VerifyForm = { secretKey: string; diff --git a/src/renderer/components/ui-redesign/Buttons/ButtonLink/ButtonLink.stories.tsx b/src/renderer/components/ui-redesign/Buttons/ButtonLink/ButtonLink.stories.tsx deleted file mode 100644 index dd29710b00..0000000000 --- a/src/renderer/components/ui-redesign/Buttons/ButtonLink/ButtonLink.stories.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { MemoryRouter } from 'react-router-dom'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; - -import { Icon } from '@renderer/components/ui'; -import ButtonLink from './ButtonLink'; - -export default { - title: 'ButtonLink', - component: ButtonLink, - parameters: { actions: { argTypesRegex: '^on.*' } }, - decorators: [ - (Story) => ( - - - - ), - ], -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Primary = Template.bind({}); -Primary.args = { - to: 'test_path', - variant: 'fill', - pallet: 'primary', - children: 'Link', - className: 'w-[200px]', - disabled: false, -}; - -export const Prefix = Template.bind({}); - -Prefix.args = { - to: 'test_path', - variant: 'fill', - pallet: 'primary', - children: 'Link', - className: 'w-[200px]', - disabled: false, - prefixElement: , -}; -export const Suffix = Template.bind({}); - -Suffix.args = { - to: 'test_path', - variant: 'fill', - pallet: 'primary', - children: 'Link', - className: 'w-[200px]', - disabled: false, - suffixElement: , -}; -export const Both = Template.bind({}); - -Both.args = { - to: 'test_path', - variant: 'fill', - pallet: 'primary', - children: 'Link', - className: 'w-[200px]', - disabled: false, - prefixElement: , - suffixElement: , -}; - -export const OnlyIcon = Template.bind({}); -OnlyIcon.args = { - to: 'test_path', - variant: 'fill', - pallet: 'primary', - className: 'w-max', - children: , - disabled: false, -}; diff --git a/src/renderer/components/ui-redesign/Inputs/InputArea/InputArea.test.tsx b/src/renderer/components/ui-redesign/Inputs/InputArea/InputArea.test.tsx deleted file mode 100644 index d99e02c55a..0000000000 --- a/src/renderer/components/ui-redesign/Inputs/InputArea/InputArea.test.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import InputArea from './InputArea'; - -describe('ui-redesign/Inputs/InputArea', () => { - test('should render component', () => { - render(); - - const input = screen.getByDisplayValue('test input'); - expect(input).toBeInTheDocument(); - }); - - test('should call onChange', async () => { - const user = userEvent.setup(); - const spyChange = jest.fn(); - render(); - - const input = screen.getByRole('textbox'); - await user.type(input, 'x'); - - expect(spyChange).toBeCalledWith('x'); - }); - - test('should respect maxLength', async () => { - const user = userEvent.setup({ delay: null }); - render(); - - const input = screen.getByRole('textbox'); - await user.type(input, 'this is my long text'); - - expect(input).toHaveDisplayValue('this is my'); - expect(input).not.toHaveDisplayValue('this is my long text'); - }); -}); diff --git a/src/renderer/components/ui/Address/ChainAddress.stories.tsx b/src/renderer/components/ui/Address/ChainAddress.stories.tsx deleted file mode 100644 index 02a84ca421..0000000000 --- a/src/renderer/components/ui/Address/ChainAddress.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentStory, ComponentMeta } from '@storybook/react'; - -import ChainAddress from './ChainAddress'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; - -export default { - title: 'Address', - component: ChainAddress, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Primary = Template.bind({}); -Primary.args = { - accountId: TEST_ACCOUNT_ID, -}; - -export const Full = Template.bind({}); -Full.args = { - accountId: TEST_ACCOUNT_ID, - type: 'full', -}; diff --git a/src/renderer/components/ui/Address/ChainAddress.test.tsx b/src/renderer/components/ui/Address/ChainAddress.test.tsx deleted file mode 100644 index e131a91813..0000000000 --- a/src/renderer/components/ui/Address/ChainAddress.test.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import ChainAddress from './ChainAddress'; -import { TEST_ACCOUNT_ID, TEST_ADDRESS } from '@renderer/shared/utils/constants'; - -describe('ui/Address', () => { - test('should render component', () => { - render(); - - const addressValue = screen.getByText(TEST_ADDRESS); - expect(addressValue).toBeInTheDocument(); - }); - - test('should render short component', () => { - render(); - - const elipsis = screen.getByText('5CGQ7B...VbXyr9'); - expect(elipsis).toBeInTheDocument(); - }); -}); diff --git a/src/renderer/components/ui/Address/ChainAddress.tsx b/src/renderer/components/ui/Address/ChainAddress.tsx deleted file mode 100644 index cac9c7e603..0000000000 --- a/src/renderer/components/ui/Address/ChainAddress.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import cnTw from '@renderer/shared/utils/twMerge'; -import { Identicon } from '@renderer/components/ui'; -import { SigningType, AccountId, Address } from '@renderer/domain/shared-kernel'; -import { toShortAddress, toAddress } from '@renderer/shared/utils/address'; -import Truncate from '../Truncate/Truncate'; -import { FootnoteText } from '@renderer/components/ui-redesign'; - -type AddressType = 'full' | 'short' | 'adaptive'; -type AddressStyle = 'small' | 'normal' | 'large'; - -const Styles: Record = { - small: 'text-2xs text-neutral-variant font-normal', - normal: 'text-xs leading-4 text-neutral-variant font-normal', - large: 'text-base text-neutral', -}; - -type WithAccountId = { - accountId: AccountId; - addressPrefix?: number; -}; - -type WithAddress = { - address: Address; -}; - -type Props = { - className?: string; - type?: AddressType; - addressStyle?: AddressStyle; - signType?: SigningType; - name?: string; - subName?: string; - size?: number; - symbols?: number; - canCopy?: boolean; - showIcon?: boolean; -} & (WithAccountId | WithAddress); - -const getAddress = (props: WithAccountId | WithAddress): Address => { - if ((props as WithAddress).address) return (props as WithAddress).address; - - const { accountId, addressPrefix } = props as WithAccountId; - - return toAddress(accountId, { prefix: addressPrefix }); -}; - -const ChainAddress = ({ - className, - symbols, - signType, - name, - subName, - size = 16, - addressStyle = 'normal', - type = 'full', - canCopy = true, - showIcon = true, - ...props -}: Props) => { - const currentAddress = getAddress(props); - const typeIsAdaptive = type === 'adaptive'; - const addressToShow = type === 'short' ? toShortAddress(currentAddress, symbols) : currentAddress; - - const nameContent = (name || subName) && ( -
- {name} - {subName &&

{subName}

} -
- ); - - const addressContent = typeIsAdaptive ? ( - - ) : ( -

{addressToShow}

- ); - - return ( -
- {showIcon && ( - - )} - {nameContent || addressContent} -
- ); -}; - -export default ChainAddress; diff --git a/src/renderer/components/ui/Balance/Balance.stories.tsx b/src/renderer/components/ui/Balance/Balance.stories.tsx deleted file mode 100644 index 4a66b19bf2..0000000000 --- a/src/renderer/components/ui/Balance/Balance.stories.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentStory, ComponentMeta } from '@storybook/react'; - -import Balance from './Balance'; - -export default { - title: 'Balance', - component: Balance, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Primary = Template.bind({}); -Primary.args = { - value: '10000000000000000000', - precision: 10, -}; - -export const WithSymbol = Template.bind({}); -WithSymbol.args = { - value: '10000000000000000000', - precision: 10, - symbol: 'KSM', -}; diff --git a/src/renderer/components/ui/Balance/Balance.test.tsx b/src/renderer/components/ui/Balance/Balance.test.tsx deleted file mode 100644 index d03e9b4cae..0000000000 --- a/src/renderer/components/ui/Balance/Balance.test.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import Balance from './Balance'; - -jest.mock('@renderer/context/I18nContext', () => ({ - useI18n: jest.fn().mockReturnValue({ - t: (key: string) => key, - }), -})); - -describe('ui/Balance', () => { - test('should render component', () => { - render(); - - const balance = screen.getByText('assetBalance.number'); - expect(balance).toBeInTheDocument(); - }); - - test('should render component with symbol', () => { - render(); - - const balance = screen.getByText('assetBalance.number'); - const symbol = screen.getByText('KSM'); - expect(balance).toBeInTheDocument(); - expect(symbol).toBeInTheDocument(); - }); -}); diff --git a/src/renderer/components/ui/Balance/Balance.tsx b/src/renderer/components/ui/Balance/Balance.tsx deleted file mode 100644 index 15ed5f4f9f..0000000000 --- a/src/renderer/components/ui/Balance/Balance.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { useI18n } from '@renderer/context/I18nContext'; -import { formatBalance } from '@renderer/shared/utils/balance'; - -interface Props { - value: string; - precision: number; - symbol?: string; - className?: string; -} - -const Balance = ({ value, precision, symbol, className }: Props) => { - const { t } = useI18n(); - const { value: formattedValue, decimalPlaces, suffix } = formatBalance(value, precision); - - const balanceValue = t('assetBalance.number', { - value: formattedValue, - maximumFractionDigits: decimalPlaces, - }); - - return ( - - {balanceValue} - {suffix} - {symbol && {symbol}} - - ); -}; - -export default Balance; diff --git a/src/renderer/components/ui/Block/Block.stories.tsx b/src/renderer/components/ui/Block/Block.stories.tsx deleted file mode 100644 index 9412c6ba33..0000000000 --- a/src/renderer/components/ui/Block/Block.stories.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { ComponentStory, ComponentMeta } from '@storybook/react'; - -import Block from './Block'; - -export default { - title: 'Block', - component: Block, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Primary = Template.bind({}); -Primary.args = { - children: 'This is simple content', -}; diff --git a/src/renderer/components/ui/Block/Block.test.tsx b/src/renderer/components/ui/Block/Block.test.tsx deleted file mode 100644 index 40b0489313..0000000000 --- a/src/renderer/components/ui/Block/Block.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import Block from './Block'; - -describe('ui/Block', () => { - test('should render component', () => { - const content = 'This is simple content'; - render({content}); - - const children = screen.getByText(content); - expect(children).toBeInTheDocument(); - }); -}); diff --git a/src/renderer/components/ui/Block/Block.tsx b/src/renderer/components/ui/Block/Block.tsx deleted file mode 100644 index 889f15932a..0000000000 --- a/src/renderer/components/ui/Block/Block.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { PropsWithChildren } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; - -type Props = { - className?: string; -}; - -const Block = ({ className = 'p-5', children }: PropsWithChildren) => ( -
{children}
-); - -export default Block; diff --git a/src/renderer/components/ui/Buttons/Button/Button.stories.tsx b/src/renderer/components/ui/Buttons/Button/Button.stories.tsx deleted file mode 100644 index bfe1a96df5..0000000000 --- a/src/renderer/components/ui/Buttons/Button/Button.stories.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; - -import { Icon } from '@renderer/components/ui'; -import Button from './Button'; - -export default { - title: 'Button', - component: Button, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => , - ); - - const button = screen.getByRole('button', { name: 'Hello button' }); - expect(button).toBeInTheDocument(); - }); - - test('should render prefix', () => { - render( - , - ); - - const prefix = screen.getByTestId('prefix'); - expect(prefix).toBeInTheDocument(); - }); - - test('should render suffix', () => { - render( - , - ); - - const suffix = screen.getByTestId('suffix'); - expect(suffix).toBeInTheDocument(); - }); - - test('should call onClick', () => { - const spyClick = jest.fn(); - render( - , - ); - - const button = screen.getByRole('button', { name: 'Hello button' }); - button.click(); - - expect(spyClick).toBeCalledTimes(1); - }); -}); diff --git a/src/renderer/components/ui/Buttons/Button/Button.tsx b/src/renderer/components/ui/Buttons/Button/Button.tsx deleted file mode 100644 index 4392ef4676..0000000000 --- a/src/renderer/components/ui/Buttons/Button/Button.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import noop from 'lodash/noop'; -import { MouseEvent, PropsWithChildren, ReactNode } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; -import { ViewClass, WeightClass } from '../common/constants'; -import { Pallet, Variant } from '../common/types'; - -type Props = { - className?: string; - type?: 'button' | 'submit'; - form?: string; - variant: Variant; - pallet: Pallet; - weight?: keyof typeof WeightClass; - disabled?: boolean; - prefixElement?: ReactNode; - suffixElement?: ReactNode; - onClick?: (event: MouseEvent) => void; -}; - -const Button = ({ - variant, - pallet, - type = 'button', - weight = 'md', - form, - className, - disabled, - children, - prefixElement, - suffixElement, - onClick = noop, -}: PropsWithChildren) => ( - -); - -export default Button; diff --git a/src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.stories.tsx b/src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.stories.tsx deleted file mode 100644 index dcf7492bb7..0000000000 --- a/src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.stories.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { MemoryRouter } from 'react-router-dom'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; - -import ButtonBack from './ButtonBack'; - -export default { - title: 'ButtonBack', - component: ButtonBack, - parameters: { actions: { argTypesRegex: '^on.*' } }, - decorators: [ - (Story) => ( - - - - ), - ], -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Primary = Template.bind({}); -Primary.args = { - path: 'test_path', - children:

Back home

, -}; diff --git a/src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.test.tsx b/src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.test.tsx deleted file mode 100644 index e2a9eb1742..0000000000 --- a/src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.test.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import { MemoryRouter, useNavigate } from 'react-router-dom'; - -import ButtonBack from './ButtonBack'; - -jest.mock('react-router-dom', () => ({ - useNavigate: jest.fn(), -})); - -describe('ui/Buttons/ButtonBack', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - test('should render component', () => { - render(Back home, { wrapper: MemoryRouter }); - - const children = screen.getByText('Back home'); - expect(children).toBeInTheDocument(); - }); - - test('should call inner navigate', () => { - const spyNavigate = jest.fn(); - (useNavigate as jest.Mock).mockReturnValue(spyNavigate); - - render(Back home, { wrapper: MemoryRouter }); - - const button = screen.getByRole('button'); - button.click(); - - expect(spyNavigate).toBeCalled(); - }); - - test('should call custom navigate', () => { - const spyReturn = jest.fn(); - render(Back home, { wrapper: MemoryRouter }); - - const button = screen.getByRole('button'); - button.click(); - - expect(spyReturn).toBeCalled(); - }); -}); diff --git a/src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.tsx b/src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.tsx deleted file mode 100644 index dfb8496c51..0000000000 --- a/src/renderer/components/ui/Buttons/ButtonBack/ButtonBack.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { PropsWithChildren } from 'react'; -import { useNavigate } from 'react-router-dom'; - -import Icon from '../../Icon/Icon'; - -type Props = { - path?: string; - onCustomReturn?: () => void; -}; - -const ButtonBack = ({ path, children, onCustomReturn }: PropsWithChildren) => { - const navigate = useNavigate(); - - const onClick = () => { - if (path) { - navigate(path, { replace: true }); - } else if (onCustomReturn) { - onCustomReturn(); - } else { - navigate(-1); - } - }; - - return ( - - ); -}; - -export default ButtonBack; diff --git a/src/renderer/components/ui/Buttons/ButtonLink/ButtonLink.test.tsx b/src/renderer/components/ui/Buttons/ButtonLink/ButtonLink.test.tsx deleted file mode 100644 index 43f9623495..0000000000 --- a/src/renderer/components/ui/Buttons/ButtonLink/ButtonLink.test.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { act, render, screen } from '@testing-library/react'; -import { BrowserRouter, MemoryRouter } from 'react-router-dom'; - -import ButtonLink from './ButtonLink'; - -describe('ui/Buttons/ButtonLink', () => { - test('should render component', () => { - render(, { wrapper: MemoryRouter }); - - const buttonLink = screen.getByRole('link'); - expect(buttonLink).toBeInTheDocument(); - }); - - test('should navigate on click', () => { - window.history.pushState({}, '', '/init_page'); - - render(, { wrapper: BrowserRouter }); - - expect(window.location.href).toEqual('http://localhost/init_page'); - - const buttonLink = screen.getByRole('link'); - act(() => buttonLink.click()); - - expect(window.location.href).toEqual('http://localhost/test_page'); - }); - - test('should render disabled', () => { - render( - - Disabled - , - { wrapper: MemoryRouter }, - ); - - const buttonLink = screen.queryByRole('link'); - expect(buttonLink).not.toBeInTheDocument(); - - const disabledContainer = screen.getByText('Disabled'); - expect(disabledContainer).toBeInTheDocument(); - }); - - test('should call callback on page transition', async () => { - const spyCallback = jest.fn(); - render(, { - wrapper: MemoryRouter, - }); - - const buttonLink = screen.getByRole('link'); - await act(() => buttonLink.click()); - - expect(spyCallback).toBeCalled(); - }); -}); diff --git a/src/renderer/components/ui/Buttons/ButtonLink/ButtonLink.tsx b/src/renderer/components/ui/Buttons/ButtonLink/ButtonLink.tsx deleted file mode 100644 index 251c187efa..0000000000 --- a/src/renderer/components/ui/Buttons/ButtonLink/ButtonLink.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { PropsWithChildren, ReactNode } from 'react'; -import { Link } from 'react-router-dom'; - -import cnTw from '@renderer/shared/utils/twMerge'; -import { ViewClass, WeightClass } from '../common/constants'; -import { Pallet, Variant } from '../common/types'; - -type Props = { - to: string; - className?: string; - variant: Variant; - pallet: Pallet; - weight?: keyof typeof WeightClass; - disabled?: boolean; - prefixElement?: ReactNode; - suffixElement?: ReactNode; - callback?: () => void; -}; - -const ButtonLink = ({ - to, - variant, - pallet, - weight = 'md', - className, - disabled, - children, - prefixElement, - suffixElement, - callback, -}: PropsWithChildren) => { - const classes = cnTw( - 'flex items-center justify-center gap-x-2.5 border font-semibold select-none', - WeightClass[weight], - ViewClass[`${variant}_${disabled ? 'shade' : pallet}`], - className, - ); - - const content = ( - <> - {prefixElement &&
{prefixElement}
} -
{children}
- {suffixElement &&
{suffixElement}
} - - ); - - return disabled ? ( -
{content}
- ) : ( - - {content} - - ); -}; - -export default ButtonLink; diff --git a/src/renderer/components/ui/Buttons/common/constants.ts b/src/renderer/components/ui/Buttons/common/constants.ts deleted file mode 100644 index 65a6b90164..0000000000 --- a/src/renderer/components/ui/Buttons/common/constants.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Pallet, Variant } from './types'; - -export const ViewClass: Record<`${Variant}_${Pallet}`, string> = { - text_primary: 'text-primary border-transparent bg-transparent', - text_secondary: 'text-secondary border-transparent bg-transparent', - text_error: 'text-error border-transparent bg-transparent', - text_shade: 'text-shade-40 border-transparent bg-transparent', - text_alert: 'text-alert border-transparent bg-transparent', - text_dark: 'text-neutral border-transparent bg-transparent', - fill_primary: 'text-white border-primary bg-primary', - fill_secondary: 'text-white border-secondary bg-secondary', - fill_error: 'text-white border-error bg-error', - fill_shade: 'text-shade-40 border-shade-20 bg-shade-20', - fill_alert: 'text-white border-alert bg-alert', - fill_dark: 'text-white border-neutral bg-neutral', - outline_primary: 'text-primary border-current', - outline_secondary: 'text-secondary border-current', - outline_error: 'text-error border-current', - outline_shade: 'text-shade-40 border-shade-20', - outline_alert: 'text-alert border-alert', - outline_dark: 'text-neutral border-neutral', - dashed_primary: 'text-primary border-current', - dashed_secondary: 'text-secondary border-dashed border-current', - dashed_error: 'text-error border-dashed border-current', - dashed_shade: 'text-shade-40 border-dashed border-shade-20', - dashed_alert: 'text-alert border-dashed border-alert', - dashed_dark: 'text-neutral border-dashed border-neutral', -}; - -export const WeightClass = { - xs: 'text-xs leading-3.5 font-semibold h-5 px-2 rounded-md', - sm: 'text-xs leading-3.5 font-semibold h-6 px-2 rounded-md', - md: 'text-sm leading-3.5 font-semibold h-7.5 px-2 rounded-lg', - lg: 'text-base leading-5 font-semibold h-10 px-3 rounded-2lg', -}; diff --git a/src/renderer/components/ui/Buttons/common/types.ts b/src/renderer/components/ui/Buttons/common/types.ts deleted file mode 100644 index c9e0f8673d..0000000000 --- a/src/renderer/components/ui/Buttons/common/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type Variant = 'text' | 'fill' | 'outline' | 'dashed'; - -export type Pallet = 'primary' | 'secondary' | 'error' | 'alert' | 'shade' | 'dark'; diff --git a/src/renderer/components/ui/Carousel/Carousel.stories.tsx b/src/renderer/components/ui/Carousel/Carousel.stories.tsx deleted file mode 100644 index 06f9e27b3a..0000000000 --- a/src/renderer/components/ui/Carousel/Carousel.stories.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; - -import SlideOne from '@images/misc/onboarding/slide-1.svg'; -import SlideTwo from '@images/misc/onboarding/slide-2.svg'; -import SlideThree from '@images/misc/onboarding/slide-3.svg'; -import Carousel from './Carousel'; - -export default { - title: 'Carousel', - component: Carousel, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -const slides = [SlideOne, SlideTwo, SlideThree].map((slide, index) => ({ - id: index, - node: ( -
- -

Description {index}

-
- ), -})); - -export const Primary = Template.bind({}); -Primary.args = { - animationDuration: 500, - slides, -}; diff --git a/src/renderer/components/ui/Carousel/Carousel.test.tsx b/src/renderer/components/ui/Carousel/Carousel.test.tsx deleted file mode 100644 index 9d5f62e547..0000000000 --- a/src/renderer/components/ui/Carousel/Carousel.test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import Carousel from './Carousel'; - -describe('ui/Carousel', () => { - const slides = [ - { id: '1', node:
slide 1
}, - { id: '2', node:
slide 2
}, - ]; - - test('should render component', () => { - render(); - - const slideEl = screen.getAllByText(/slide \d/); - expect(slideEl).toHaveLength(2); - }); - - test('should render controls', () => { - render(); - - const prevBtn = screen.getByRole('button', { name: /left.svg/ }); - const nextBtn = screen.getByRole('button', { name: /right.svg/ }); - const dots = screen.getAllByRole('button', { name: /^$/ }); - expect(prevBtn).toBeInTheDocument(); - expect(nextBtn).toBeInTheDocument(); - expect(dots).toHaveLength(2); - }); -}); diff --git a/src/renderer/components/ui/Carousel/Carousel.tsx b/src/renderer/components/ui/Carousel/Carousel.tsx deleted file mode 100644 index 2248cfd6e4..0000000000 --- a/src/renderer/components/ui/Carousel/Carousel.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { ReactNode, useState } from 'react'; -import { EffectFade, Autoplay } from 'swiper'; -import { Swiper as SwiperRoot, SwiperSlide } from 'swiper/react'; -import { AutoplayOptions, Swiper } from 'swiper/types'; -import 'swiper/css'; -import 'swiper/css/effect-fade'; - -import { Icon } from '@renderer/components/ui'; -import cnTw from '@renderer/shared/utils/twMerge'; - -type Props = { - animationDuration?: number; - loop?: boolean; - autoplay?: boolean | AutoplayOptions; - slides: SlideNode[]; -}; - -export type SlideNode = { - id: string | number; - node: ReactNode; -}; - -const Carousel = ({ loop, autoplay, animationDuration = 300, slides }: Props) => { - const [swiper, setSwiper] = useState(); - const [activeIndex, setActiveIndex] = useState(0); - - return ( - setActiveIndex(swiper.realIndex)} - > - {slides.map((slide) => ( - - {slide.node} - - ))} - -
- -
- {slides.map((slide, index) => ( -
- -
-
- ); -}; - -export default Carousel; diff --git a/src/renderer/components/ui/Checkbox/Checkbox.css b/src/renderer/components/ui/Checkbox/Checkbox.css deleted file mode 100644 index 2be2ac3426..0000000000 --- a/src/renderer/components/ui/Checkbox/Checkbox.css +++ /dev/null @@ -1,16 +0,0 @@ -.old-checkbox:checked::before { - content: '✓'; - color: white; - display: block; - width: 100%; - line-height: 20px; - font-weight: bold; - font-size: 14px; - text-align: center; -} - -.old-checkbox::-webkit-outer-spin-button, -.old-checkbox::-webkit-inner-spin-button { - appearance: none; - margin: 0; -} diff --git a/src/renderer/components/ui/Checkbox/Checkbox.stories.tsx b/src/renderer/components/ui/Checkbox/Checkbox.stories.tsx deleted file mode 100644 index c1b9cebbcc..0000000000 --- a/src/renderer/components/ui/Checkbox/Checkbox.stories.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { ComponentStory, ComponentMeta } from '@storybook/react'; - -import Checkbox from './Checkbox'; - -export default { - title: 'Checkbox', - component: Checkbox, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Primary = Template.bind({}); -Primary.args = { - children: 'Checkbox', -}; - -export const Left = Template.bind({}); -Left.args = { - position: 'left', - children: 'Checkbox', -}; - -export const Disabled = Template.bind({}); -Disabled.args = { - children: 'Checkbox', - disabled: true, -}; diff --git a/src/renderer/components/ui/Checkbox/Checkbox.test.tsx b/src/renderer/components/ui/Checkbox/Checkbox.test.tsx deleted file mode 100644 index de0232690d..0000000000 --- a/src/renderer/components/ui/Checkbox/Checkbox.test.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import Checkbox from './Checkbox'; - -describe('ui/Checkbox', () => { - test('should render component', () => { - render(test label); - - const label = screen.getByText('test label'); - expect(label).toBeInTheDocument(); - }); - - test('should toggle check state', () => { - let isChecked = false; - const toggleCheck = jest.fn().mockImplementation(() => { - isChecked = !isChecked; - }); - - const { rerender } = render( - - test label - , - ); - - let input = screen.getByRole('checkbox'); - expect(input).not.toBeChecked(); - - input.click(); - - rerender(test label); - - input = screen.getByRole('checkbox'); - expect(input).toBeChecked(); - expect(toggleCheck).toBeCalled(); - expect(isChecked).toEqual(true); - }); -}); diff --git a/src/renderer/components/ui/Checkbox/Checkbox.tsx b/src/renderer/components/ui/Checkbox/Checkbox.tsx deleted file mode 100644 index 6afb992891..0000000000 --- a/src/renderer/components/ui/Checkbox/Checkbox.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { ChangeEvent, PropsWithChildren } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; -import './Checkbox.css'; - -type Props = { - defaultChecked?: boolean; - position?: 'right' | 'left'; - checked?: boolean; - disabled?: boolean; - readOnly?: boolean; - value?: any; - className?: string; - onChange?: (event: ChangeEvent) => void; -}; - -const Checkbox = ({ - checked, - defaultChecked, - position = 'right', - disabled, - readOnly, - value, - className, - children, - onChange, -}: PropsWithChildren) => { - const content = typeof children === 'string' ?

{children}

: children; - - return ( - - ); -}; - -export default Checkbox; diff --git a/src/renderer/components/ui/Dropdowns/Combobox/Combobox.stories.tsx b/src/renderer/components/ui/Dropdowns/Combobox/Combobox.stories.tsx deleted file mode 100644 index e8d0c1b568..0000000000 --- a/src/renderer/components/ui/Dropdowns/Combobox/Combobox.stories.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; - -import { Icon, Identicon } from '@renderer/components/ui'; -import Combobox from './Combobox'; - -export default { - title: 'Combobox', - component: Combobox, - parameters: { actions: { argTypesRegex: '^on.*' } }, - decorators: [ - (Story) => ( -
- -
- ), - ], -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -const data = [ - { value: 'Durward Reynolds', address: '13mK8AssyPekT5cFuYQ7ijKNXcjHPq8Gnx6TxF5eFCAwoLQ' }, - { value: 'Kenton Towne', address: '1A2ATy1FEu5yQ9ZzghPLsRckPQ7XLmq5MJQYcTvGnxGvCho' }, - { value: 'Therese Wunsch', address: '1bAVKRsNUbq1Qmvj7Cemkncjo17WgyWAusCFZQdUfeHSTYj' }, -]; - -const options = data.map((d, index) => ({ - id: index.toString(), - value: d.value, - element: d.value, -})); - -const customOptions = data.map((d, index) => ({ - id: index.toString(), - value: d.value, - element: ( -
- -

{d.value}

-
- ), -})); - -export const Primary = Template.bind({}); -Primary.args = { - placeholder: 'Select an option', - options, - onChange: () => {}, -}; - -export const Selected = Template.bind({}); -Selected.args = { - placeholder: 'Select an option', - value: options[1].id, - options, - onChange: () => {}, -}; - -export const Large = Template.bind({}); -Large.args = { - placeholder: 'Select an option', - value: options[1].id, - options, - weight: 'lg', - onChange: () => {}, -}; - -export const WithLabel = Template.bind({}); -WithLabel.args = { - placeholder: 'Select an option', - label: 'Payout account', - options, - weight: 'lg', - onChange: () => {}, -}; - -export const Custom = Template.bind({}); -Custom.args = { - placeholder: 'Select an option', - label: 'Payout account', - value: customOptions[2].id, - options: customOptions, - suffixElement: , - weight: 'md', - onChange: () => {}, -}; diff --git a/src/renderer/components/ui/Dropdowns/Combobox/Combobox.test.tsx b/src/renderer/components/ui/Dropdowns/Combobox/Combobox.test.tsx deleted file mode 100644 index 2baf8e6bc7..0000000000 --- a/src/renderer/components/ui/Dropdowns/Combobox/Combobox.test.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import Combobox from './Combobox'; - -describe('ui/Combobox/Combobox', () => { - const options = [ - { id: '0', element: 'label_0', value: '0' }, - { id: '1', element: 'label_1', value: '1' }, - ]; - const defaultProps = { - activeId: undefined, - placeholder: 'Select option', - options, - onChange: () => {}, - }; - - test('should render component', () => { - render(); - - const input = screen.getByRole('combobox'); - expect(input).toBeInTheDocument(); - }); -}); diff --git a/src/renderer/components/ui/Dropdowns/Combobox/Combobox.tsx b/src/renderer/components/ui/Dropdowns/Combobox/Combobox.tsx deleted file mode 100644 index 75a436cf24..0000000000 --- a/src/renderer/components/ui/Dropdowns/Combobox/Combobox.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { ComponentPropsWithoutRef, Fragment, ReactNode, useState } from 'react'; -import { Transition, Combobox as HeadlessCombobox } from '@headlessui/react'; - -import cnTw from '@renderer/shared/utils/twMerge'; -import { Input } from '@renderer/components/ui'; -import { ViewClass, DropdownClass } from '../common/constants'; -import { DropdownOption, DropdownResult, HTMLComboboxProps, Variant } from '../common/types'; -import { includes } from '@renderer/shared/utils/strings'; - -interface Props extends Pick, HTMLComboboxProps> { - label?: ReactNode; - invalid?: boolean; - options: DropdownOption[]; - value?: DropdownOption['value']; - filterBy?: string; - suffixElement?: ReactNode; - prefixElement?: ReactNode; - variant?: Variant; - weight?: keyof typeof DropdownClass; - onChange: (data: DropdownResult) => void; -} - -const Combobox = ({ - className, - placeholder, - label, - value, - options, - disabled, - suffixElement, - prefixElement, - variant = 'down', - weight = 'md', - invalid, - onChange, - ...props -}: Props) => { - const style = DropdownClass[weight]; - - const [query, setQuery] = useState(''); - - const filteredOptions = query - ? options.filter((option) => includes(option.value, query) || includes(JSON.stringify(option.element), query)) - : options; - - return ( - -
- option.value} - prefixElement={prefixElement} - suffixElement={suffixElement} - // @ts-ignore onChange doesn't respect custom onChange type - onChange={setQuery} - {...props} - /> - - - - {query.length > 0 && ( - - cnTw( - 'flex items-center cursor-pointer select-none px-2.5 rounded-2lg mb-[2px] last:mb-0', - active && 'bg-shade-5', - style.option, - ) - } - > -
-

{query}

-
-
- )} - - {filteredOptions.map((option) => ( - - cnTw( - 'flex items-center cursor-pointer select-none px-2.5 rounded-2lg mb-[2px] last:mb-0', - active && 'bg-shade-5', - style.option, - ) - } - > -
- {typeof option.element === 'string' ? ( -

{option.element}

- ) : ( - option.element - )} -
-
- ))} -
-
-
-
- ); -}; - -export default Combobox; diff --git a/src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.stories.tsx b/src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.stories.tsx deleted file mode 100644 index 71964b35fe..0000000000 --- a/src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.stories.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; - -import { Icon, Identicon } from '@renderer/components/ui'; -import Dropdown from './Dropdown'; - -export default { - title: 'Dropdown', - component: Dropdown, - parameters: { actions: { argTypesRegex: '^on.*' } }, - decorators: [ - (Story) => ( -
- -
- ), - ], -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -const data = [ - { value: 'Durward Reynolds', address: '13mK8AssyPekT5cFuYQ7ijKNXcjHPq8Gnx6TxF5eFCAwoLQ' }, - { value: 'Kenton Towne', address: '1A2ATy1FEu5yQ9ZzghPLsRckPQ7XLmq5MJQYcTvGnxGvCho' }, - { value: 'Therese Wunsch', address: '1bAVKRsNUbq1Qmvj7Cemkncjo17WgyWAusCFZQdUfeHSTYj' }, -]; - -const options = data.map((d, index) => ({ - id: index.toString(), - value: d.value, - element: d.value, -})); - -const customOptions = data.map((d, index) => ({ - id: index.toString(), - value: d.value, - element: ( -
- -

{d.value}

-
- ), -})); - -export const Primary = Template.bind({}); -Primary.args = { - placeholder: 'Select an option', - options, - onChange: () => {}, -}; - -export const Selected = Template.bind({}); -Selected.args = { - placeholder: 'Select an option', - activeId: options[1].id, - options, - onChange: () => {}, -}; - -export const Large = Template.bind({}); -Large.args = { - placeholder: 'Select an option', - activeId: options[1].id, - options, - weight: 'lg', - onChange: () => {}, -}; - -export const WithLabel = Template.bind({}); -WithLabel.args = { - placeholder: 'Select an option', - label: 'Payout account', - options, - weight: 'lg', - onChange: () => {}, -}; - -export const Custom = Template.bind({}); -Custom.args = { - placeholder: 'Select an option', - label: 'Payout account', - activeId: customOptions[2].id, - options: customOptions, - suffix: , - weight: 'md', - onChange: () => {}, -}; diff --git a/src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.test.tsx b/src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.test.tsx deleted file mode 100644 index c03a37e0ed..0000000000 --- a/src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.test.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { act, render, screen } from '@testing-library/react'; - -import Dropdown from './Dropdown'; - -describe('ui/Dropdowns/Dropdown', () => { - const options = [ - { id: '0', element: 'label_0', value: '0' }, - { id: '1', element: 'label_1', value: '1' }, - ]; - const defaultProps = { - activeId: undefined, - placeholder: 'Select option', - onChange: () => {}, - options, - }; - - test('should render component', () => { - render(); - - const button = screen.getByRole('button'); - const placeholder = screen.getByText('Select option'); - expect(button).toBeInTheDocument(); - expect(placeholder).toBeInTheDocument(); - }); - - test('should call onSelected', async () => { - const spySelected = jest.fn(); - render(); - - const button = screen.getByRole('button'); - await act(() => button.click()); - - const option = screen.getByRole('option', { name: options[0].element }); - await act(() => option.click()); - - expect(spySelected).toBeCalledWith({ id: options[0].id, value: options[0].value }); - }); - - test('should render selected option', async () => { - const { rerender } = render(); - - const button = screen.getByRole('button'); - await act(() => button.click()); - - let selectedOptions = screen.getAllByRole('option'); - selectedOptions.forEach((option) => { - expect(option).not.toHaveClass('bg-shade-5'); - }); - - rerender(); - - selectedOptions = screen.getAllByRole('option'); - expect(selectedOptions[1]).toHaveClass('bg-shade-5'); - }); -}); diff --git a/src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.tsx b/src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.tsx deleted file mode 100644 index aaf0e2237c..0000000000 --- a/src/renderer/components/ui/Dropdowns/Dropdown/Dropdown.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { Listbox, Transition } from '@headlessui/react'; -import { Fragment, ReactNode } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; -import { Icon } from '@renderer/components/ui'; -import { ViewClass, DropdownClass } from '../common/constants'; -import { DropdownOption, DropdownResult, Variant } from '../common/types'; - -type Props = { - className?: string; - placeholder: string; - label?: string; - disabled?: boolean; - activeId?: DropdownOption['id']; - options: DropdownOption[]; - suffix?: ReactNode; - variant?: Variant; - weight?: keyof typeof DropdownClass; - onChange: (data: DropdownResult) => void; -}; - -const Dropdown = ({ - className, - placeholder, - label, - disabled, - activeId, - options, - suffix, - variant = 'down', - weight = 'md', - onChange, -}: Props) => { - const style = DropdownClass[weight]; - - const activeOption = options.find((option) => option.id === activeId); - - return ( - - {({ open, disabled }) => ( -
- - {label && ( -

- {label} -

- )} - -
- {activeOption && - (typeof activeOption.element === 'string' ? ( -

- {activeOption.element} -

- ) : ( - activeOption.element - ))} - - {!activeOption && ( -

- {placeholder} -

- )} - - - - {suffix} -
-
- - - {options.map(({ id, value, element }) => ( - - cnTw( - 'flex items-center cursor-pointer select-none px-2.5 rounded mb-0.5 last:mb-0', - (active || selected) && 'bg-shade-5', - style.option, - ) - } - > -
- {typeof element === 'string' ? ( -

{element}

- ) : ( - element - )} -
-
- ))} -
-
-
- )} -
- ); -}; - -export default Dropdown; diff --git a/src/renderer/components/ui/Dropdowns/Filter/Filter.stories.tsx b/src/renderer/components/ui/Dropdowns/Filter/Filter.stories.tsx deleted file mode 100644 index 0c08dfdac2..0000000000 --- a/src/renderer/components/ui/Dropdowns/Filter/Filter.stories.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { ComponentStory, ComponentMeta } from '@storybook/react'; - -import Filter from './Filter'; - -export default { - title: 'Filter', - component: Filter, - parameters: { actions: { argTypesRegex: '^on.*' } }, - decorators: [ - (Story) => ( -
- -
- ), - ], -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -const options = [ - { id: '0', value: 'option_1', element: 'label_1' }, - { id: '1', value: 'option_2', element: 'label_2' }, - { id: '2', value: 'option_3', element: 'label_3' }, - { id: '3', value: 'option_4', element: 'label_4' }, -]; - -export const Primary = Template.bind({}); -Primary.args = { - activeIds: [options[1].id, options[3].id], - placeholder: 'Filter', - position: 'right', - options, -}; - -export const WithoutText = Template.bind({}); -WithoutText.args = { - activeIds: [options[1].id, options[3].id], - position: 'right', - options, -}; diff --git a/src/renderer/components/ui/Dropdowns/Filter/Filter.test.tsx b/src/renderer/components/ui/Dropdowns/Filter/Filter.test.tsx deleted file mode 100644 index 6dff60fd38..0000000000 --- a/src/renderer/components/ui/Dropdowns/Filter/Filter.test.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { act, render, screen } from '@testing-library/react'; - -import Filter from './Filter'; - -describe('ui/Dropdowns/Filter', () => { - const options = [ - { id: '0', value: 'test_1', element: 'el_test_1' }, - { id: '1', value: 'test_2', element: 'el_test_2' }, - { id: '2', value: 'test_3', element: 'el_test_3' }, - ]; - - const defaultProps = { - activeIds: [], - options, - placeholder: 'Filters', - onChange: () => {}, - }; - - test('should render component', () => { - render(); - - const button = screen.getByRole('button'); - const placeholder = screen.getByText('Filters'); - expect(button).toBeInTheDocument(); - expect(placeholder).toBeInTheDocument(); - }); - - test('should call onChange', async () => { - const spyChange = jest.fn(); - render(); - - const button = screen.getByRole('button'); - await act(() => button.click()); - - const optionOne = screen.getByRole('option', { name: options[0].element }); - await act(() => optionOne.click()); - expect(spyChange).toBeCalledWith([{ id: options[0].id, value: options[0].value }]); - - const optionTwo = screen.getByRole('option', { name: options[1].element }); - await act(() => optionTwo.click()); - expect(spyChange).toBeCalledWith([{ id: options[1].id, value: options[1].value }]); - }); - - test('should render list', async () => { - render(); - - const button = screen.getByRole('button'); - await act(() => button.click()); - - const selectedOptions = screen.getAllByRole('option'); - expect(selectedOptions).toHaveLength(3); - }); -}); diff --git a/src/renderer/components/ui/Dropdowns/Filter/Filter.tsx b/src/renderer/components/ui/Dropdowns/Filter/Filter.tsx deleted file mode 100644 index 1c15490f5b..0000000000 --- a/src/renderer/components/ui/Dropdowns/Filter/Filter.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { Listbox, Transition } from '@headlessui/react'; -import { Fragment } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; -import { Icon, Switch } from '@renderer/components/ui'; -import { DropdownOption, DropdownResult } from '../common/types'; - -type Props = { - activeIds: DropdownOption['id'][]; - placeholder?: string; - className?: string; - position?: 'left' | 'right'; - options: DropdownOption[]; - onChange: (data: DropdownResult[]) => void; -}; - -const Filter = ({ activeIds, placeholder, position = 'right', options, className, onChange }: Props) => { - const activeOptions = options.filter((option) => activeIds.includes(option.id)); - - return ( - - {({ open }) => ( -
- -
- - {placeholder} -
-
- - - {options.map(({ id, value, element }) => ( - - cnTw('h-10 flex items-center px-2.5 cursor-pointer rounded-2lg', active && 'bg-shade-10') - } - > - {({ selected }) => ( - - {element} - - )} - - ))} - - -
- )} -
- ); -}; - -export default Filter; diff --git a/src/renderer/components/ui/Dropdowns/Select/Select.stories.tsx b/src/renderer/components/ui/Dropdowns/Select/Select.stories.tsx deleted file mode 100644 index 7a0808f49f..0000000000 --- a/src/renderer/components/ui/Dropdowns/Select/Select.stories.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; - -import { Icon, Identicon } from '@renderer/components/ui'; -import Select from './Select'; - -export default { - title: 'Select', - component: Select, - parameters: { actions: { argTypesRegex: '^on.*' } }, - decorators: [ - (Story) => ( -
- -
- ), - ], -} as ComponentMeta; - -const Template: ComponentStory = (args) => ); - - const button = screen.getByRole('button'); - const placeholder = screen.getByText('Select option'); - expect(button).toBeInTheDocument(); - expect(placeholder).toBeInTheDocument(); - }); - - test('should call onChange', async () => { - const spyChange = jest.fn(); - render(); - - const summary = screen.getByRole('button'); - expect(summary).toHaveTextContent('el_test_2'); - }); - - test('should render selected options', async () => { - const { rerender } = render(); - - selectedOption = screen.getByRole('checkbox', { checked: true }); - expect(selectedOption).toBeInTheDocument(); - }); -}); diff --git a/src/renderer/components/ui/Dropdowns/Select/Select.tsx b/src/renderer/components/ui/Dropdowns/Select/Select.tsx deleted file mode 100644 index 8a73f6c8dc..0000000000 --- a/src/renderer/components/ui/Dropdowns/Select/Select.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { Listbox, Transition } from '@headlessui/react'; -import { Fragment, ReactNode } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; -import { Checkbox, Icon } from '@renderer/components/ui'; -import { ViewClass, SelectClass } from '../common/constants'; -import { DropdownResult, DropdownOption, Variant } from '../common/types'; - -type Props = { - summary: ReactNode; - className?: string; - placeholder?: string; - activeIds: DropdownOption['id'][]; - options: DropdownOption[]; - variant?: Variant; - suffix?: ReactNode; - weight?: keyof typeof SelectClass; - position?: 'left' | 'right'; - onChange: (data: DropdownResult[]) => void; -}; - -const Select = ({ - className, - suffix, - activeIds = [], - summary, - placeholder, - options, - variant = 'down', - weight = 'md', - position = 'right', - onChange, -}: Props) => { - const weightStyle = SelectClass[weight]; - - const activeOptions = options.filter((option) => activeIds.includes(option.id)); - - return ( - - {({ open }) => ( -
- -
- {activeOptions.length === 0 && ( -

- {placeholder} -

- )} - {activeOptions.length === 1 && activeOptions[0].element} - {activeOptions.length > 1 && ( -
-

- {activeOptions.length} -

-

{summary}

-
- )} -
- - - - {suffix} -
- - - {options.map(({ id, value, element }) => ( - - cnTw( - 'flex items-center cursor-pointer select-none px-2.5 rounded-2lg', - active && 'bg-shade-5', - weightStyle.option, - ) - } - > - {({ selected }) => ( - -
{element}
-
- )} -
- ))} -
-
-
- )} -
- ); -}; - -export default Select; diff --git a/src/renderer/components/ui/Dropdowns/common/constants.ts b/src/renderer/components/ui/Dropdowns/common/constants.ts deleted file mode 100644 index d3ad1750ee..0000000000 --- a/src/renderer/components/ui/Dropdowns/common/constants.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Variant } from './types'; - -export const ViewClass: Record, string> = { - up: 'bottom-full mb-2.5', - down: 'top-full mt-2.5', -}; - -const BaseWeightClass = { - md: { - placeholder: 'text-sm', - height: 'h-12.5', - option: 'h-10', - arrows: 16, - }, - lg: { - placeholder: 'text-lg', - height: 'h-15', - option: 'h-12', - arrows: 20, - }, -}; - -export const DropdownClass = { - md: { - ...BaseWeightClass.md, - text: 'text-sm', - label: { - height: 'h-[82px]', - text: 'text-2xs', - }, - }, - lg: { - ...BaseWeightClass.lg, - text: 'text-lg', - label: { - height: 'h-[94px]', - text: 'text-xs', - }, - }, -}; - -export const SelectClass = { - md: { - ...BaseWeightClass.md, - count: 'w-6.5 h-6.5', - summary: 'test-sm', - }, - lg: { - ...BaseWeightClass.lg, - count: 'w-6.5 h-6.5', - summary: 'test-sm', - }, -}; diff --git a/src/renderer/components/ui/Dropdowns/common/types.ts b/src/renderer/components/ui/Dropdowns/common/types.ts deleted file mode 100644 index e07c620888..0000000000 --- a/src/renderer/components/ui/Dropdowns/common/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ReactNode } from 'react'; - -export type Variant = 'up' | 'down' | 'auto'; - -export type DropdownOption = { - id: string; - element: ReactNode; - value: T; -}; - -export type DropdownResult = { - id: string; - value: T; -}; - -export type HTMLComboboxProps = 'value' | 'type' | 'required' | 'disabled' | 'placeholder' | 'name' | 'className'; diff --git a/src/renderer/components/ui/InfoLink/InfoLink.stories.tsx b/src/renderer/components/ui/InfoLink/InfoLink.stories.tsx deleted file mode 100644 index 54396dd2da..0000000000 --- a/src/renderer/components/ui/InfoLink/InfoLink.stories.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { ComponentStory, ComponentMeta } from '@storybook/react'; - -import InfoLink from './InfoLink'; - -export default { - title: 'InfoLink', - component: InfoLink, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Primary = Template.bind({}); -Primary.args = { - url: 'https://test.com', - children: 'This is my link', -}; diff --git a/src/renderer/components/ui/InfoLink/InfoLink.test.tsx b/src/renderer/components/ui/InfoLink/InfoLink.test.tsx deleted file mode 100644 index 37d8fa0c0a..0000000000 --- a/src/renderer/components/ui/InfoLink/InfoLink.test.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import InfoLink from './InfoLink'; - -describe('screen/Settings/InfoLink', () => { - test('should render component', () => { - render(My link); - - const children = screen.getByRole('link', { name: 'globe.svg My link' }); - const icon = screen.queryByRole('img'); - expect(children).toBeInTheDocument(); - expect(icon).toBeInTheDocument(); - }); - - test('should render without icon', () => { - render( - - My link - , - ); - - const children = screen.getByRole('link', { name: 'My link' }); - const icon = screen.queryByRole('img'); - expect(children).toBeInTheDocument(); - expect(icon).not.toBeInTheDocument(); - }); -}); diff --git a/src/renderer/components/ui/InfoLink/InfoLink.tsx b/src/renderer/components/ui/InfoLink/InfoLink.tsx deleted file mode 100644 index ccb64af6df..0000000000 --- a/src/renderer/components/ui/InfoLink/InfoLink.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { PropsWithChildren } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; -import { Icon } from '@renderer/components/ui'; - -type Props = { - url: string; - showIcon?: boolean; -}; -const InfoLink = ({ url, showIcon = true, children }: PropsWithChildren) => ( - - {showIcon && } - {children} - -); - -export default InfoLink; diff --git a/src/renderer/components/ui/InputHint/InputHint.stories.tsx b/src/renderer/components/ui/InputHint/InputHint.stories.tsx deleted file mode 100644 index fabd1b9dba..0000000000 --- a/src/renderer/components/ui/InputHint/InputHint.stories.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; - -import InputHint from './InputHint'; - -export default { - title: 'InputHint', - component: InputHint, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Primary = Template.bind({}); -Primary.args = { - active: true, - variant: 'hint', - children: 'Test hint text', -}; - -export const Error = Template.bind({}); -Error.args = { - active: true, - variant: 'error', - children: 'Test error text', -}; diff --git a/src/renderer/components/ui/InputHint/InputHint.test.tsx b/src/renderer/components/ui/InputHint/InputHint.test.tsx deleted file mode 100644 index 803402ad7e..0000000000 --- a/src/renderer/components/ui/InputHint/InputHint.test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import InputHint from './InputHint'; - -describe('ui/InputHint', () => { - test('should render component', () => { - render( - - test hint - , - ); - - const hint = screen.getByText('test hint'); - expect(hint).toBeInTheDocument(); - expect(hint).toHaveClass('text-shade-40'); - }); - - test('should not render component', () => { - render( - - test hint - , - ); - - const hint = screen.queryByText('test hint'); - expect(hint).not.toBeInTheDocument(); - }); -}); diff --git a/src/renderer/components/ui/InputHint/InputHint.tsx b/src/renderer/components/ui/InputHint/InputHint.tsx deleted file mode 100644 index 70ffdbecad..0000000000 --- a/src/renderer/components/ui/InputHint/InputHint.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { PropsWithChildren } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; - -type Props = { - active: boolean; - variant: 'hint' | 'alert' | 'error' | 'success'; - className?: string; -}; - -const InputHint = ({ variant, active, className, children }: PropsWithChildren) => { - if (!active) return null; - - return ( -

- {children} -

- ); -}; - -export default InputHint; diff --git a/src/renderer/components/ui/Inputs/Input/Input.stories.tsx b/src/renderer/components/ui/Inputs/Input/Input.stories.tsx deleted file mode 100644 index df164773bf..0000000000 --- a/src/renderer/components/ui/Inputs/Input/Input.stories.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; - -import Input from './Input'; - -export default { - title: 'Input', - component: Input, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Primary = Template.bind({}); -Primary.args = { - placeholder: 'Test input', -}; - -export const Filled = Template.bind({}); -Filled.args = { - value: 'This is value', -}; - -export const Label = Template.bind({}); -Label.args = { - label: 'With label', - value: 'This is value', -}; - -export const Invalid = Template.bind({}); -Invalid.args = { - label: 'With invalid', - value: 'This is value', - invalid: true, -}; - -export const Disabled = Template.bind({}); -Disabled.args = { - label: 'With disabled label', - value: 'This is value', - disabled: true, -}; diff --git a/src/renderer/components/ui/Inputs/Input/Input.test.tsx b/src/renderer/components/ui/Inputs/Input/Input.test.tsx deleted file mode 100644 index 28050b67f7..0000000000 --- a/src/renderer/components/ui/Inputs/Input/Input.test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import Input from './Input'; - -describe('ui/Inputs/Input', () => { - test('should render component', () => { - render( {}} />); - - const input = screen.getByDisplayValue('test input'); - expect(input).toBeInTheDocument(); - }); - - test('should call onChange', async () => { - const user = userEvent.setup(); - const spyChange = jest.fn(); - render(); - - const input = screen.getByRole('textbox'); - await user.type(input, 'x'); - - expect(spyChange).toBeCalledWith('x'); - }); -}); diff --git a/src/renderer/components/ui/Inputs/Input/Input.tsx b/src/renderer/components/ui/Inputs/Input/Input.tsx deleted file mode 100644 index 94db2b7d4e..0000000000 --- a/src/renderer/components/ui/Inputs/Input/Input.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { ReactNode, ComponentPropsWithoutRef, forwardRef } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; -import { HTMLInputProps } from '../common/types'; - -interface Props extends Pick, HTMLInputProps> { - label?: ReactNode; - disabledStyle?: boolean; - invalid?: boolean; - wrapperClass?: string; - prefixElement?: ReactNode; - suffixElement?: ReactNode; - onChange?: (value: string) => void; -} - -const Input = forwardRef( - ( - { - type = 'text', - label = '', - disabledStyle, - className, - wrapperClass, - invalid = false, - prefixElement, - suffixElement, - onChange, - ...props - }, - ref, - ) => ( - - ), -); - -export default Input; diff --git a/src/renderer/components/ui/Inputs/InputArea/InputArea.stories.tsx b/src/renderer/components/ui/Inputs/InputArea/InputArea.stories.tsx deleted file mode 100644 index 27600e4177..0000000000 --- a/src/renderer/components/ui/Inputs/InputArea/InputArea.stories.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; - -import InputArea from './InputArea'; - -export default { - title: 'InputArea', - component: InputArea, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Primary = Template.bind({}); -Primary.args = { - rows: 3, - maxLength: 120, - placeholder: 'Max length is 120', -}; - -export const Filled = Template.bind({}); -Filled.args = { - rows: 2, - value: - 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Culpa doloribus iusto possimus praesentium ratione temporibus. Aperiam autem cumque esse eum fugit laborum quas! Architecto at, cupiditate dignissimos eveniet sunt voluptatibus.', -}; - -export const Invalid = Template.bind({}); -Invalid.args = { - rows: 1, - value: 'This is value', - invalid: true, -}; - -export const Disabled = Template.bind({}); -Disabled.args = { - rows: 1, - value: 'This is value', - disabled: true, -}; diff --git a/src/renderer/components/ui/Inputs/InputArea/InputArea.tsx b/src/renderer/components/ui/Inputs/InputArea/InputArea.tsx index 491c9bb5aa..a6918274b7 100644 --- a/src/renderer/components/ui/Inputs/InputArea/InputArea.tsx +++ b/src/renderer/components/ui/Inputs/InputArea/InputArea.tsx @@ -1,9 +1,8 @@ import { ComponentPropsWithoutRef, forwardRef } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { CommonInputStyles, CommonInputStylesTheme } from '@renderer/components/ui-redesign/Inputs/common/styles'; -import { HTMLTextAreaProps } from '../common/types'; -import { Theme } from '@renderer/components/ui-redesign/Dropdowns/common/types'; +import { cnTw } from '@renderer/shared/lib/utils'; +import type { HTMLTextAreaProps, Theme } from '@renderer/shared/ui/types'; +import { CommonInputStyles, CommonInputStylesTheme } from '@renderer/shared/ui/Inputs/common/styles'; interface Props extends Pick, HTMLTextAreaProps> { invalid?: boolean; diff --git a/src/renderer/components/ui/Inputs/common/types.ts b/src/renderer/components/ui/Inputs/common/types.ts deleted file mode 100644 index bada24118e..0000000000 --- a/src/renderer/components/ui/Inputs/common/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type HTMLInputProps = 'value' | 'type' | 'required' | 'disabled' | 'placeholder' | 'name' | 'className'; -export type HTMLTextAreaProps = - | 'value' - | 'rows' - | 'maxLength' - | 'required' - | 'disabled' - | 'placeholder' - | 'name' - | 'className'; diff --git a/src/renderer/components/ui/Plate/Plate.stories.tsx b/src/renderer/components/ui/Plate/Plate.stories.tsx deleted file mode 100644 index 40b9e37f66..0000000000 --- a/src/renderer/components/ui/Plate/Plate.stories.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { ComponentStory, ComponentMeta } from '@storybook/react'; - -import Plate from './Plate'; - -export default { - title: 'Plate', - component: Plate, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Primary = Template.bind({}); -Primary.args = { - children: 'This is simple content', -}; diff --git a/src/renderer/components/ui/Plate/Plate.test.tsx b/src/renderer/components/ui/Plate/Plate.test.tsx deleted file mode 100644 index cea96133f3..0000000000 --- a/src/renderer/components/ui/Plate/Plate.test.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import Plate from './Plate'; - -describe('ui/Plate', () => { - test('should render component', () => { - const content = 'This is simple content'; - render({content}); - - const children = screen.getByText(content); - expect(children).toBeInTheDocument(); - }); - - test('should render as custom tag', () => { - const content = 'Custom tag'; - render({content}); - - const children = screen.getByText(content); - expect(children).toBeInTheDocument(); - expect(children.nodeName.toLowerCase()).toEqual('section'); - }); -}); diff --git a/src/renderer/components/ui/Plate/Plate.tsx b/src/renderer/components/ui/Plate/Plate.tsx deleted file mode 100644 index 65a0e004e5..0000000000 --- a/src/renderer/components/ui/Plate/Plate.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { ElementType, PropsWithChildren } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; - -type Props = { - as?: ElementType; - className?: string; -}; - -const Plate = ({ as: Tag = 'div', className, children }: PropsWithChildren) => ( - {children} -); - -export default Plate; diff --git a/src/renderer/components/ui/Stepper/Stepper.stories.tsx b/src/renderer/components/ui/Stepper/Stepper.stories.tsx deleted file mode 100644 index 700eec74af..0000000000 --- a/src/renderer/components/ui/Stepper/Stepper.stories.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; - -import Stepper from './Stepper'; - -export default { - title: 'Stepper', - component: Stepper, - parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -export const Start = Template.bind({}); -Start.args = { - steps: [ - { title: 'Prepare the QR code' }, - { title: 'Scan the QR code' }, - { title: 'Check the result' }, - { title: 'Finish' }, - ], - active: 0, -}; - -export const Progress = Template.bind({}); -Progress.args = { - steps: [ - { title: 'Prepare the QR code' }, - { title: 'Scan the QR code' }, - { title: 'Check the result' }, - { title: 'Finish' }, - ], - active: 2, -}; - -export const Finish = Template.bind({}); -Finish.args = { - steps: [ - { title: 'Prepare the QR code' }, - { title: 'Scan the QR code' }, - { title: 'Check the result' }, - { title: 'Finish' }, - ], - active: 4, -}; diff --git a/src/renderer/components/ui/Stepper/Stepper.test.tsx b/src/renderer/components/ui/Stepper/Stepper.test.tsx deleted file mode 100644 index 548deb44a2..0000000000 --- a/src/renderer/components/ui/Stepper/Stepper.test.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import Stepper from './Stepper'; - -describe('ui/Stepper', () => { - test('should render correct steps', () => { - const steps = [{ title: 'Step_1' }, { title: 'Step_2' }]; - - render(); - - const active = screen.getByTestId('active-step'); - const inactive = screen.getByTestId('inactive-step'); - expect(active).toBeInTheDocument(); - expect(inactive).toBeInTheDocument(); - }); - - test('should render fully complete stepper', () => { - const steps = [{ title: 'Step_1' }, { title: 'Step_2' }, { title: 'Step_3' }]; - - render(); - - const complete = screen.getAllByTestId('complete-step'); - expect(complete).toHaveLength(3); - }); - - test('should update step', () => { - const steps = [{ title: 'Step_1' }, { title: 'Step_2' }]; - - const { rerender } = render(); - - let complete = screen.queryByTestId('complete-step'); - let active = screen.getByTestId('active-step'); - let inactive = screen.queryByTestId('inactive-step'); - expect(complete).not.toBeInTheDocument(); - expect(active).toBeInTheDocument(); - expect(inactive).toBeInTheDocument(); - - rerender(); - - complete = screen.getByTestId('active-step'); - active = screen.getByTestId('active-step'); - inactive = screen.queryByTestId('inactive-step'); - expect(complete).toBeInTheDocument(); - expect(active).toBeInTheDocument(); - expect(inactive).not.toBeInTheDocument(); - }); -}); diff --git a/src/renderer/components/ui/Stepper/Stepper.tsx b/src/renderer/components/ui/Stepper/Stepper.tsx deleted file mode 100644 index c3e8bca87b..0000000000 --- a/src/renderer/components/ui/Stepper/Stepper.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import cnTw from '@renderer/shared/utils/twMerge'; -import { ReactComponent as CheckSvg } from '@images/functionals/checkmark.svg'; - -const getTitleClass = (isStart: boolean, isEnd: boolean): string => { - const defaultClass = 'mt-2.5 absolute top-full w-max '; - - if (isEnd) return defaultClass + 'translate-x-[calc(-100%+40px)]'; - if (isStart) return defaultClass; - - return defaultClass + 'translate-x-[calc(-50%+20px)]'; -}; - -type StepProps = { - index: number; - title: string; - isStart: boolean; - isEnd: boolean; -}; - -const InactiveStep = ({ index, title, isStart, isEnd }: StepProps) => ( -
-
-
- {index + 1} -
-

{title}

-
- {!isEnd &&
} -
-); - -const ActiveStep = ({ index, title, isStart, isEnd }: StepProps) => ( -
-
-
{index + 1}
-

{title}

-
- {!isEnd &&
} -
-); - -const CompleteStep = ({ title, isStart, isEnd }: StepProps) => ( -
-
-
- -
-

{title}

-
- {!isEnd &&
} -
-); - -type Props = { - steps: Record<'title', string>[]; - active: number; -}; - -const Stepper = ({ steps, active }: Props) => ( -
    - {steps.map(({ title }, index) => { - const isStart = index === 0; - const isEnd = index === steps.length - 1; - - let variant = CompleteStep; - if (index === active) variant = ActiveStep; - else if (index > active) variant = InactiveStep; - - return ( -
  • - {variant({ index, title, isStart, isEnd })} -
  • - ); - })} -
-); - -export default Stepper; diff --git a/src/renderer/components/ui/Table/Table.stories.tsx b/src/renderer/components/ui/Table/Table.stories.tsx deleted file mode 100644 index 791db74836..0000000000 --- a/src/renderer/components/ui/Table/Table.stories.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { ComponentStory, ComponentMeta } from '@storybook/react'; - -import Table from './Table'; - -export default { - title: 'Table', - component: Table, - parameters: { actions: { argTypesRegex: '^on.*' } }, - decorators: [ - (Story) => ( -
- -
- ), - ], -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -type Struct = { - name: string; - age: number; - address: string; -}; -const dataSource: Struct[] = [ - { name: 'Mike', age: 32, address: '20 Downing Street' }, - { name: 'John', age: 42, address: '10 Downing Street' }, -]; - -export const Primary = Template.bind({}); -Primary.args = { - by: 'name', - dataSource, - selectedKeys: ['1'], - onSelect: console.log, - children: ( - <> - - - Name - - - Age - - - Address - - - - > - {(source) => ( - - {source.name} - {source.age} - {source.address} - ••• - - )} - - - ), -}; diff --git a/src/renderer/components/ui/Table/Table.tsx b/src/renderer/components/ui/Table/Table.tsx deleted file mode 100644 index 3bb7439154..0000000000 --- a/src/renderer/components/ui/Table/Table.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import { PropsWithChildren, useCallback, useMemo, useState } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; -import { SortConfig, SortType, AnyRecord, IndexKey, ColumnConfig } from './common/types'; -import { getUpdatedConfig, getSortedData } from './common/utils'; -import { TableBody, TableCell, TableColumn, TableHeader, TableRow } from './TableParts'; -import { TableContext } from './TableContext'; - -type Props = { - by: IndexKey; - dataSource: T[]; - loading?: boolean; - className?: string; - selectedKeys?: IndexKey[]; - onSelect?: (keys: IndexKey[]) => void; -}; - -// TODO: think about nice TS support -// type TableComposition = { -// Header: Parameters; -// Column: Parameters; -// Body: Parameters; -// Row: Parameters; -// Cell: Parameters; -// }; - -const Table = ({ - by, - dataSource, - loading, - className, - selectedKeys, - onSelect, - children, -}: PropsWithChildren>) => { - const [sortConfig, setSortConfig] = useState({}); - const [excludedKeys, setExcludedKeys] = useState([]); - - const allRowsSelected = dataSource.length - excludedKeys.length === selectedKeys?.length; - - const addSortingConfig = useCallback( - ({ dataKey, align, sortable, sortType }: ColumnConfig) => { - const payload = { - dataKey, - align, - sortable, - sortType: sortType || SortType.NONE, - }; - - setSortConfig((prev) => ({ ...prev, [dataKey]: payload })); - }, - [setSortConfig], - ); - - const updateSortingOrder = useCallback( - (column: string) => { - if (sortConfig[column]) { - setSortConfig((prev) => getUpdatedConfig(column, prev)); - } else { - console.warn(`${column} is absent`); - } - }, - [sortConfig, setSortConfig], - ); - - const excludeKey = useCallback( - (key: IndexKey) => { - setExcludedKeys((prev) => prev.concat(key)); - }, - [setExcludedKeys], - ); - - const selectAll = useCallback(() => { - if (!selectedKeys || !onSelect) return; - - if (allRowsSelected) { - onSelect?.([]); - } else { - const allSelectedKeys = dataSource.reduce((acc, source) => { - if (!excludedKeys.includes(source[by])) { - acc.push(source[by]); - } - - return acc; - }, []); - - onSelect(allSelectedKeys); - } - }, [dataSource, excludedKeys, allRowsSelected, onSelect]); - - const selectRow = useCallback( - (key: IndexKey) => { - if (!selectedKeys || !onSelect) return; - - if (selectedKeys.includes(key)) { - onSelect(selectedKeys.filter((k) => k !== key)); - } else { - onSelect(selectedKeys.concat(key)); - } - }, - [selectedKeys, onSelect], - ); - - const sortedData = useMemo(() => { - return getSortedData(dataSource, sortConfig); - }, [dataSource, sortConfig]); - - const value = { - by, - dataSource: sortedData, - loading, - sortConfig, - selectedKeys, - allRowsSelected, - addSortingConfig, - updateSortingOrder, - excludeKey, - selectAll, - selectRow, - }; - - return ( - -
{children}
- - ); -}; - -Table.Header = TableHeader; -Table.Column = TableColumn; -Table.Body = TableBody; -Table.Cell = TableCell; -Table.Row = TableRow; - -export default Table; diff --git a/src/renderer/components/ui/Table/TableContext.tsx b/src/renderer/components/ui/Table/TableContext.tsx deleted file mode 100644 index 809db175b5..0000000000 --- a/src/renderer/components/ui/Table/TableContext.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Context, createContext, useContext } from 'react'; - -import { AnyRecord, ColumnConfig, IndexKey, SortConfig } from './common/types'; - -type TableContextProps = { - by: IndexKey; - dataSource: T[]; - loading?: boolean; - sortConfig: SortConfig; - selectedKeys?: IndexKey[]; - allRowsSelected: boolean; - addSortingConfig: (params: ColumnConfig) => void; - updateSortingOrder: (column: string) => void; - excludeKey: (key: IndexKey) => void; - selectAll: () => void; - selectRow: (key: IndexKey) => void; -}; - -export const TableContext = createContext>({} as TableContextProps); - -export const useTableContext = () => { - const context = useContext>(TableContext as unknown as Context>); - if (!context) { - throw new Error('Table compound components cannot be rendered outside the Table component'); - } - - return context; -}; diff --git a/src/renderer/components/ui/Table/TableParts.tsx b/src/renderer/components/ui/Table/TableParts.tsx deleted file mode 100644 index 489df93458..0000000000 --- a/src/renderer/components/ui/Table/TableParts.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import { Children, cloneElement, PropsWithChildren, ReactElement, ReactNode, useEffect } from 'react'; - -import cnTw from '@renderer/shared/utils/twMerge'; -import { AlignmentClass, HeightClass } from '@renderer/components/ui/Table/common/constants'; -import { Checkbox, Icon } from '@renderer/components/ui'; -import { Alignment, AnyRecord, IndexKey, SortType } from './common/types'; -import { useTableContext } from './TableContext'; -import { FootnoteText } from '@renderer/components/ui-redesign'; - -type HeaderProps = { - hidden?: boolean; - className?: string; -}; -export const TableHeader = ({ hidden, className, children }: PropsWithChildren) => { - const { allRowsSelected, selectedKeys, loading, selectAll } = useTableContext(); - - return ( - - {selectedKeys ? ( - - - - - {children} - - ) : ( - {children} - )} - - ); -}; - -export type ColumnProps = { - dataKey: string; - align?: Alignment; - sortable?: boolean | ((a: any, b: any) => number); - defaultSort?: 'asc' | 'desc'; - width?: number; - classname?: string; -}; -export const TableColumn = ({ - dataKey, - align = 'left', - sortable = false, - defaultSort, - width, - classname, - children, -}: PropsWithChildren) => { - const { sortConfig, addSortingConfig, updateSortingOrder } = useTableContext(); - - useEffect(() => { - addSortingConfig({ dataKey, align, sortable, sortType: defaultSort as SortType }); - }, []); - - if (!sortable) { - return ( - -
- {children} -
- - ); - } - - const sortIcon = [SortType.DESC, SortType.NONE].includes(sortConfig[dataKey]?.sortType) ? 'down' : 'up'; - const columnIsSorted = sortConfig[dataKey]?.sortType !== SortType.NONE; - - return ( - -
- -
- - ); -}; - -type BodyProps = { - children: (data: T & { rowIndex: number }) => ReactNode; -}; -export const TableBody = ({ children }: BodyProps) => { - const { by, dataSource } = useTableContext(); - - return ( - - {dataSource.map((source, index) => { - const item = children({ rowIndex: index, ...source }) as ReactElement>; - - return cloneElement(item, { dataKey: source[by] }); - })} - - ); -}; - -type RowProps = { - className?: string; - height?: keyof typeof HeightClass; - selectable?: boolean; -}; -type _RowProps = { - dataKey: IndexKey; -}; -export const TableRow = ({ - className, - height = 'md', - selectable = true, - children, - ...props -}: PropsWithChildren) => { - const { sortConfig, selectedKeys, loading, selectRow, excludeKey } = useTableContext(); - - // eslint-disable-next-line react/prop-types - const { dataKey } = props as _RowProps; - const alignments = Object.values(sortConfig).map((config) => config.align); - - useEffect(() => { - if (selectable) return; - - excludeKey(dataKey); - }, []); - - return ( - - {selectedKeys && ( - - selectRow(dataKey)} - /> - - )} - {Children.map(children, (child, index) => { - const item = child as ReactElement>; - if (!item) return null; - - return cloneElement(item, { align: alignments[index] }); - })} - - ); -}; - -type CellProps = { - className?: string; - colSpan?: number; - cellAlign?: Alignment; -}; - -type _CellProps = { - align: Alignment; -}; - -export const TableCell = ({ className, children, colSpan, cellAlign, ...props }: PropsWithChildren) => { - // eslint-disable-next-line react/prop-types - const { align } = props as _CellProps; - - return ( - -
{children}
- - ); -}; diff --git a/src/renderer/components/ui/Table/common/constants.ts b/src/renderer/components/ui/Table/common/constants.ts deleted file mode 100644 index 30396f9f52..0000000000 --- a/src/renderer/components/ui/Table/common/constants.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Alignment } from './types'; - -export const HeightClass = { - md: 'h-10', - lg: 'h-15', -}; - -export const AlignmentClass: Record = { - left: 'w-max mr-auto', - right: 'w-max ml-auto', - center: 'w-max mx-auto', - width: 'w-full', -}; diff --git a/src/renderer/components/ui/Table/common/types.ts b/src/renderer/components/ui/Table/common/types.ts deleted file mode 100644 index bd83c8a47a..0000000000 --- a/src/renderer/components/ui/Table/common/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -export type IndexKey = string; - -export type AnyRecord = { - [key: string]: any; -}; - -export const enum SortType { - ASC = 'asc', - DESC = 'desc', - NONE = 'none', -} - -export type Alignment = 'left' | 'right' | 'center' | 'width'; - -export type ColumnConfig = { - dataKey: string; - align: Alignment; - sortType: SortType; - sortable: boolean | ((a: any, b: any) => number); -}; - -export type SortConfig = Record; diff --git a/src/renderer/components/ui/Table/common/utils.ts b/src/renderer/components/ui/Table/common/utils.ts deleted file mode 100644 index ba7e6749bf..0000000000 --- a/src/renderer/components/ui/Table/common/utils.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { orderBy } from 'lodash'; - -import { AnyRecord, SortConfig, SortType } from './types'; - -/** - * Get sorted data based on current config - * @param dataSource table data to be sorted - * @param config columns' config with sorting params - * @return {Array} - */ -export const getSortedData = (dataSource: T[], config: SortConfig): T[] => { - const activeSorting = Object.values(config).find((sort) => sort.sortType !== SortType.NONE); - - if (!dataSource.length || !activeSorting || activeSorting.sortType === SortType.NONE) { - return dataSource; - } - - if (typeof activeSorting.sortable === 'function') { - const sortedData = dataSource.slice().sort(activeSorting.sortable); - - return activeSorting.sortType === SortType.ASC ? sortedData : sortedData.reverse(); - } - - return orderBy(dataSource, [activeSorting.dataKey], [activeSorting.sortType]); -}; - -/** - * Get updated config with only one sorted column - * @param column name of the column to be sorted - * @param config columns' config with sorting params - * @return {Object} - */ -export const getUpdatedConfig = (column: string, config: SortConfig): SortConfig => { - return Object.entries(config).reduce((acc, [key, value]) => { - const payload = { ...value, sortType: SortType.NONE }; - - if (key === column) { - payload.sortType = [SortType.DESC, SortType.NONE].includes(value.sortType) ? SortType.ASC : SortType.DESC; - } - acc[key] = payload; - - return acc; - }, {}); -}; diff --git a/src/renderer/components/ui/index.ts b/src/renderer/components/ui/index.ts deleted file mode 100644 index 06a8afee35..0000000000 --- a/src/renderer/components/ui/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import ChainAddress from './Address/ChainAddress'; -import Checkbox from './Checkbox/Checkbox'; -import Switch from './Switch/Switch'; -import Icon from './Icon/Icon'; -import Identicon from './Identicon/Identicon'; -import Input from './Inputs/Input/Input'; -import InputArea from './Inputs/InputArea/InputArea'; -import Block from './Block/Block'; -import Plate from './Plate/Plate'; -import InputHint from './InputHint/InputHint'; -import Button from './Buttons/Button/Button'; -import ButtonLink from './Buttons/ButtonLink/ButtonLink'; -import ButtonBack from './Buttons/ButtonBack/ButtonBack'; -import Stepper from './Stepper/Stepper'; -import LanguageSwitcher from './LanguageSwitcher/LanguageSwitcher'; -import Carousel from './Carousel/Carousel'; -import Dropdown from './Dropdowns/Dropdown/Dropdown'; -import Select from './Dropdowns/Select/Select'; -import Balance from './Balance/Balance'; -import Filter from './Dropdowns/Filter/Filter'; -import Table from './Table/Table'; -import Shimmering from './Shimmering/Shimmering'; -import Combobox from './Dropdowns/Combobox/Combobox'; -import Duration from './Duration/Duration'; -import InfoLink from './InfoLink/InfoLink'; -import Loader from './Loader/Loader'; - -export { - ChainAddress, - Checkbox, - Switch, - Icon, - Identicon, - Input, - InputArea, - Block, - Plate, - InputHint, - Button, - ButtonLink, - ButtonBack, - Stepper, - LanguageSwitcher, - Dropdown, - Select, - Carousel, - Balance, - Filter, - Table, - Shimmering, - Combobox, - InfoLink, - Duration, - Loader, -}; diff --git a/src/renderer/context/ConfirmContext/index.ts b/src/renderer/context/ConfirmContext/index.ts deleted file mode 100644 index 3379bcc0d1..0000000000 --- a/src/renderer/context/ConfirmContext/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { ConfirmDialogProvider as default } from './ConfirmContext'; -export { useConfirmContext } from './ConfirmContext'; diff --git a/src/renderer/context/GraphqlContext/index.ts b/src/renderer/context/GraphqlContext/index.ts deleted file mode 100644 index 613dd30910..0000000000 --- a/src/renderer/context/GraphqlContext/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { GraphqlProvider as default } from './GraphqlContext'; -export { useGraphql } from './GraphqlContext'; diff --git a/src/renderer/context/I18nContext/index.ts b/src/renderer/context/I18nContext/index.ts deleted file mode 100644 index 70b596ab82..0000000000 --- a/src/renderer/context/I18nContext/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { I18Provider as default } from './I18nContext'; -export { useI18n } from './I18nContext'; diff --git a/src/renderer/context/MatrixContext/index.ts b/src/renderer/context/MatrixContext/index.ts deleted file mode 100644 index f01f5ea2e8..0000000000 --- a/src/renderer/context/MatrixContext/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { MatrixProvider as default } from './MatrixContext'; -export { useMatrix } from './MatrixContext'; diff --git a/src/renderer/context/MultisigChainContext/index.ts b/src/renderer/context/MultisigChainContext/index.ts deleted file mode 100644 index 25de10373f..0000000000 --- a/src/renderer/context/MultisigChainContext/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { MultisigChainProvider as default } from './MultisigChainContext'; -export { useMultisigChainContext } from './MultisigChainContext'; diff --git a/src/renderer/context/NetworkContext/index.ts b/src/renderer/context/NetworkContext/index.ts deleted file mode 100644 index 83b21d5d3d..0000000000 --- a/src/renderer/context/NetworkContext/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { NetworkProvider as default } from './NetworkContext'; -export { useNetworkContext } from './NetworkContext'; diff --git a/src/renderer/domain/connection.ts b/src/renderer/domain/connection.ts index 2d9f38b40a..a832f69be5 100644 --- a/src/renderer/domain/connection.ts +++ b/src/renderer/domain/connection.ts @@ -1,4 +1,4 @@ -import { RpcNode } from './chain'; +import { RpcNode } from '../entities/chain/model/chain'; import { ChainId } from './shared-kernel'; export type Connection = { diff --git a/src/renderer/domain/utility.ts b/src/renderer/domain/utility.ts index 04d3be2252..6aee2cdeb5 100644 --- a/src/renderer/domain/utility.ts +++ b/src/renderer/domain/utility.ts @@ -1 +1,3 @@ export type PartialBy = Omit & Pick, K>; + +export type ObjectValues = T[keyof T]; diff --git a/src/renderer/entities/account/index.ts b/src/renderer/entities/account/index.ts new file mode 100644 index 0000000000..74f5e47c62 --- /dev/null +++ b/src/renderer/entities/account/index.ts @@ -0,0 +1,3 @@ +export * from './ui'; +export * from './lib'; +export * from './model/account'; diff --git a/src/renderer/services/account/__tests__/accountService.test.ts b/src/renderer/entities/account/lib/__tests__/accountService.test.ts similarity index 86% rename from src/renderer/services/account/__tests__/accountService.test.ts rename to src/renderer/entities/account/lib/__tests__/accountService.test.ts index 3ba61c9093..7d2d18eb09 100644 --- a/src/renderer/services/account/__tests__/accountService.test.ts +++ b/src/renderer/entities/account/lib/__tests__/accountService.test.ts @@ -1,9 +1,9 @@ import { waitFor } from '@testing-library/react'; -import storage from '@renderer/services/storage'; -import { useAccount } from '@renderer/services/account/accountService'; +import storage from '@renderer/shared/api/storage'; +import { useAccount } from '@renderer/entities/account/lib/accountService'; -jest.mock('@renderer/services/storage', () => jest.fn()); +jest.mock('@renderer/shared/api/storage', () => jest.fn()); jest.mock('dexie-react-hooks', () => ({ useLiveQuery: (handler: () => any) => handler(), diff --git a/src/renderer/services/account/accountService.ts b/src/renderer/entities/account/lib/accountService.ts similarity index 95% rename from src/renderer/services/account/accountService.ts rename to src/renderer/entities/account/lib/accountService.ts index 3125404616..4114dbb0b0 100644 --- a/src/renderer/services/account/accountService.ts +++ b/src/renderer/entities/account/lib/accountService.ts @@ -1,8 +1,8 @@ import { useLiveQuery } from 'dexie-react-hooks'; -import storage, { AccountDS, ID } from '@renderer/services/storage'; +import storage, { AccountDS, ID } from '@renderer/shared/api/storage'; import { IAccountService } from './common/types'; -import { MultisigAccount, Account } from '@renderer/domain/account'; +import { MultisigAccount, Account } from '@renderer/entities/account/model/account'; export const useAccount = (): IAccountService => { const accountStorage = storage.connectTo('accounts'); diff --git a/src/renderer/services/account/common/types.ts b/src/renderer/entities/account/lib/common/types.ts similarity index 86% rename from src/renderer/services/account/common/types.ts rename to src/renderer/entities/account/lib/common/types.ts index 66c9b814a7..6cc86dd871 100644 --- a/src/renderer/services/account/common/types.ts +++ b/src/renderer/entities/account/lib/common/types.ts @@ -1,6 +1,6 @@ -import { Account } from '@renderer/domain/account'; +import { AccountDS, ID } from '@renderer/shared/api/storage'; +import { Account } from '@renderer/entities/account/model/account'; import { Address } from '@renderer/domain/shared-kernel'; -import { AccountDS, ID } from '@renderer/services/storage'; export interface IAccountService { getAccount: (accountId: Address) => Promise; diff --git a/src/renderer/entities/account/lib/index.ts b/src/renderer/entities/account/lib/index.ts new file mode 100644 index 0000000000..56b253994e --- /dev/null +++ b/src/renderer/entities/account/lib/index.ts @@ -0,0 +1,3 @@ +export * from './accountService'; +export * from './common/types'; +export * from './useAddressInfo'; diff --git a/src/renderer/components/common/AccountAddress/useAddressInfo.tsx b/src/renderer/entities/account/lib/useAddressInfo.tsx similarity index 67% rename from src/renderer/components/common/AccountAddress/useAddressInfo.tsx rename to src/renderer/entities/account/lib/useAddressInfo.tsx index cc22626822..c64d7efb63 100644 --- a/src/renderer/components/common/AccountAddress/useAddressInfo.tsx +++ b/src/renderer/entities/account/lib/useAddressInfo.tsx @@ -1,13 +1,13 @@ -import { InfoSection } from '@renderer/components/ui-redesign/Popovers/InfoPopover/InfoPopover'; +import { InfoSection } from '@renderer/shared/ui/Popovers/InfoPopover/InfoPopover'; import { Address } from '@renderer/domain/shared-kernel'; -import { Explorer } from '@renderer/domain/chain'; -import { useContact } from '@renderer/services/contact/contactService'; -import { useAccount } from '@renderer/services/account/accountService'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { toAccountId } from '@renderer/shared/utils/address'; +import { Explorer } from '@renderer/entities/chain'; +import { useContact } from '@renderer/entities/contact'; +import { useAccount } from '@renderer/entities/account'; +import { useMatrix } from '@renderer/app/providers'; +import { toAccountId } from '@renderer/shared/lib/utils'; import { ExplorerLink } from '@renderer/components/common'; -const useAddressInfo = (address: Address, explorers?: Explorer[], showMatrix = false): InfoSection[] => { +export const useAddressInfo = (address: Address, explorers?: Explorer[], showMatrix = false): InfoSection[] => { const { matrix } = useMatrix(); const { getLiveContacts } = useContact(); const { getLiveAccounts } = useAccount(); @@ -39,5 +39,3 @@ const useAddressInfo = (address: Address, explorers?: Explorer[], showMatrix = f return popoverItems; }; - -export default useAddressInfo; diff --git a/src/renderer/domain/account.ts b/src/renderer/entities/account/model/account.ts similarity index 89% rename from src/renderer/domain/account.ts rename to src/renderer/entities/account/model/account.ts index 3311beb278..14024a628f 100644 --- a/src/renderer/domain/account.ts +++ b/src/renderer/entities/account/model/account.ts @@ -1,9 +1,17 @@ import { createKeyMulti } from '@polkadot/util-crypto'; import { u8aToHex } from '@polkadot/util'; -import { ChainId, CryptoType, AccountId, ChainType, SigningType, Threshold, WalletType } from './shared-kernel'; -import { Signatory } from '@renderer/domain/signatory'; -import { AccountDS, ID } from '@renderer/services/storage'; +import { AccountDS, ID } from '@renderer/shared/api/storage'; +import { + ChainId, + CryptoType, + AccountId, + ChainType, + SigningType, + Threshold, + WalletType, +} from '../../../domain/shared-kernel'; +import { Signatory } from '@renderer/entities/signatory/model/signatory'; export type Account = { walletId?: ID; @@ -89,14 +97,14 @@ export function createMultisigAccount({ } as MultisigAccount; } -export function isMultisig(account?: Account | MultisigAccount): account is MultisigAccount { +export const isMultisig = (account?: Account | MultisigAccount): account is MultisigAccount => { if (!account) return false; const hasSignatories = 'signatories' in (account as MultisigAccount); const hasThreshold = 'threshold' in (account as MultisigAccount); return hasSignatories && hasThreshold; -} +}; export function isMultishard(account?: Account | MultisigAccount): boolean { if (!account) return false; diff --git a/src/renderer/components/common/AccountAddress/AccountAddress.stories.tsx b/src/renderer/entities/account/ui/AccountAddress/AccountAddress.stories.tsx similarity index 82% rename from src/renderer/components/common/AccountAddress/AccountAddress.stories.tsx rename to src/renderer/entities/account/ui/AccountAddress/AccountAddress.stories.tsx index c78689b942..2ce1fe9773 100644 --- a/src/renderer/components/common/AccountAddress/AccountAddress.stories.tsx +++ b/src/renderer/entities/account/ui/AccountAddress/AccountAddress.stories.tsx @@ -1,7 +1,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; -import AccountAddress from './AccountAddress'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { AccountAddress } from './AccountAddress'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; export default { title: 'Redesign/Address', diff --git a/src/renderer/components/common/AccountAddress/AccountAddress.test.tsx b/src/renderer/entities/account/ui/AccountAddress/AccountAddress.test.tsx similarity index 90% rename from src/renderer/components/common/AccountAddress/AccountAddress.test.tsx rename to src/renderer/entities/account/ui/AccountAddress/AccountAddress.test.tsx index 0f35ab58ff..78a24a0c8f 100644 --- a/src/renderer/components/common/AccountAddress/AccountAddress.test.tsx +++ b/src/renderer/entities/account/ui/AccountAddress/AccountAddress.test.tsx @@ -1,7 +1,7 @@ import { render, screen } from '@testing-library/react'; -import AccountAddress from './AccountAddress'; -import { TEST_ACCOUNT_ID, TEST_ADDRESS } from '@renderer/shared/utils/constants'; +import { AccountAddress } from './AccountAddress'; +import { TEST_ACCOUNT_ID, TEST_ADDRESS } from '@renderer/shared/lib/utils'; describe('ui/AccountAddress', () => { test('should render component', () => { diff --git a/src/renderer/components/common/AccountAddress/AccountAddress.tsx b/src/renderer/entities/account/ui/AccountAddress/AccountAddress.tsx similarity index 86% rename from src/renderer/components/common/AccountAddress/AccountAddress.tsx rename to src/renderer/entities/account/ui/AccountAddress/AccountAddress.tsx index 2dd2b99588..d2e0d5bb09 100644 --- a/src/renderer/components/common/AccountAddress/AccountAddress.tsx +++ b/src/renderer/entities/account/ui/AccountAddress/AccountAddress.tsx @@ -1,8 +1,6 @@ -import cnTw from '@renderer/shared/utils/twMerge'; -import { Identicon } from '@renderer/components/ui'; +import { cnTw, toShortAddress, toAddress } from '@renderer/shared/lib/utils'; +import { Identicon, Truncate } from '@renderer/shared/ui'; import { SigningType, AccountId, Address } from '@renderer/domain/shared-kernel'; -import { toShortAddress, toAddress } from '@renderer/shared/utils/address'; -import Truncate from '@renderer/components/ui/Truncate/Truncate'; type AddressType = 'full' | 'short' | 'adaptive'; @@ -35,7 +33,7 @@ export const getAddress = (props: WithAccountId | WithAddress): Address => { return toAddress(accountId, { prefix: addressPrefix }); }; -const AccountAddress = ({ +export const AccountAddress = ({ className, symbols, signType, @@ -83,5 +81,3 @@ const AccountAddress = ({ ); }; - -export default AccountAddress; diff --git a/src/renderer/components/common/AccountsList/AccountsList.tsx b/src/renderer/entities/account/ui/AccountsList/AccountsList.tsx similarity index 69% rename from src/renderer/components/common/AccountsList/AccountsList.tsx rename to src/renderer/entities/account/ui/AccountsList/AccountsList.tsx index 2e84883c27..36a0a9c7a2 100644 --- a/src/renderer/components/common/AccountsList/AccountsList.tsx +++ b/src/renderer/entities/account/ui/AccountsList/AccountsList.tsx @@ -1,17 +1,17 @@ -import cnTw from '@renderer/shared/utils/twMerge'; -import { Chain as ChainType } from '@renderer/domain/chain'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { Chain, ChainTitle } from '@renderer/entities/chain'; import { AccountId } from '@renderer/domain/shared-kernel'; -import AddressWithExplorers from '../AddressWithExplorers/AddressWithExplorers'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Chain, FootnoteText } from '@renderer/components/ui-redesign'; +import { AddressWithExplorers } from '@renderer/entities/account'; +import { useI18n } from '@renderer/app/providers'; +import { FootnoteText } from '@renderer/shared/ui'; type Props = { accountId: AccountId; - chains: ChainType[]; + chains: Chain[]; className?: string; }; -const AccountsList = ({ accountId, chains, className }: Props) => { +export const AccountsList = ({ accountId, chains, className }: Props) => { const { t } = useI18n(); return ( @@ -29,7 +29,7 @@ const AccountsList = ({ accountId, chains, className }: Props) => { return (
  • - +
    { ); }; - -export default AccountsList; diff --git a/src/renderer/components/common/AddressWithExplorers/AddressWithExplorers.tsx b/src/renderer/entities/account/ui/AddressWithExplorers/AddressWithExplorers.tsx similarity index 56% rename from src/renderer/components/common/AddressWithExplorers/AddressWithExplorers.tsx rename to src/renderer/entities/account/ui/AddressWithExplorers/AddressWithExplorers.tsx index d7a951ceaf..50525f42e9 100644 --- a/src/renderer/components/common/AddressWithExplorers/AddressWithExplorers.tsx +++ b/src/renderer/entities/account/ui/AddressWithExplorers/AddressWithExplorers.tsx @@ -1,12 +1,7 @@ -import AccountAddress, { - getAddress, - Props as AccountAddressProps, -} from '@renderer/components/common/AccountAddress/AccountAddress'; -import { InfoPopover } from '@renderer/components/ui-redesign'; -import { Icon } from '@renderer/components/ui'; -import { Explorer } from '@renderer/domain/chain'; -import useAddressInfo from '@renderer/components/common/AccountAddress/useAddressInfo'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { AccountAddress, AccountAddressProps, getAddress, useAddressInfo } from '@renderer/entities/account'; +import { InfoPopover, Icon } from '@renderer/shared/ui'; +import { Explorer } from '@renderer/entities/chain'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { showMatrix?: boolean; @@ -15,7 +10,13 @@ type Props = { wrapperClassName?: string; } & AccountAddressProps; -const AddressWithExplorers = ({ explorers = [], showMatrix, position, wrapperClassName, ...addressProps }: Props) => { +export const AddressWithExplorers = ({ + explorers = [], + showMatrix, + position, + wrapperClassName, + ...addressProps +}: Props) => { const address = getAddress(addressProps); const popoverItems = useAddressInfo(address, explorers, showMatrix); @@ -33,5 +34,3 @@ const AddressWithExplorers = ({ explorers = [], showMatrix, position, wrapperCla ); }; - -export default AddressWithExplorers; diff --git a/src/renderer/components/common/AddressWithName/AddressWithName.stories.tsx b/src/renderer/entities/account/ui/AddressWithName/AddressWithName.stories.tsx similarity index 82% rename from src/renderer/components/common/AddressWithName/AddressWithName.stories.tsx rename to src/renderer/entities/account/ui/AddressWithName/AddressWithName.stories.tsx index 93568a6a2d..1921e645d6 100644 --- a/src/renderer/components/common/AddressWithName/AddressWithName.stories.tsx +++ b/src/renderer/entities/account/ui/AddressWithName/AddressWithName.stories.tsx @@ -1,7 +1,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; -import AddressWithName from './AddressWithName'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { AddressWithName } from './AddressWithName'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; export default { title: 'AddressWithName', diff --git a/src/renderer/components/common/AddressWithName/AddressWithName.test.tsx b/src/renderer/entities/account/ui/AddressWithName/AddressWithName.test.tsx similarity index 90% rename from src/renderer/components/common/AddressWithName/AddressWithName.test.tsx rename to src/renderer/entities/account/ui/AddressWithName/AddressWithName.test.tsx index daa00962ee..27fb0d9c18 100644 --- a/src/renderer/components/common/AddressWithName/AddressWithName.test.tsx +++ b/src/renderer/entities/account/ui/AddressWithName/AddressWithName.test.tsx @@ -1,7 +1,7 @@ import { render, screen } from '@testing-library/react'; -import AddressWithName from './AddressWithName'; -import { TEST_ACCOUNT_ID, TEST_ADDRESS } from '@renderer/shared/utils/constants'; +import { AddressWithName } from './AddressWithName'; +import { TEST_ACCOUNT_ID, TEST_ADDRESS } from '@renderer/shared/lib/utils'; describe('ui/Address', () => { test('should render component', () => { diff --git a/src/renderer/components/common/AddressWithName/AddressWithName.tsx b/src/renderer/entities/account/ui/AddressWithName/AddressWithName.tsx similarity index 80% rename from src/renderer/components/common/AddressWithName/AddressWithName.tsx rename to src/renderer/entities/account/ui/AddressWithName/AddressWithName.tsx index 0681519af5..e9304799b2 100644 --- a/src/renderer/components/common/AddressWithName/AddressWithName.tsx +++ b/src/renderer/entities/account/ui/AddressWithName/AddressWithName.tsx @@ -1,11 +1,7 @@ -import cnTw from '@renderer/shared/utils/twMerge'; -import { Identicon } from '@renderer/components/ui'; +import { cnTw, toShortAddress, copyToClipboard } from '@renderer/shared/lib/utils'; +import { Identicon, IconButton, Truncate } from '@renderer/shared/ui'; import { SigningType, AccountId, Address } from '@renderer/domain/shared-kernel'; -import { toShortAddress } from '@renderer/shared/utils/address'; -import Truncate from '@renderer/components/ui/Truncate/Truncate'; -import { getAddress } from '../AccountAddress/AccountAddress'; -import { IconButton } from '@renderer/components/ui-redesign'; -import { copyToClipboard } from '@renderer/shared/utils/strings'; +import { getAddress } from '@renderer/entities/account'; type AddressType = 'full' | 'short' | 'adaptive'; @@ -18,7 +14,7 @@ type WithAddress = { address: Address; }; -export type Props = { +type Props = { className?: string; type?: AddressType; addressFont?: string; @@ -31,7 +27,7 @@ export type Props = { canCopySubName?: boolean; } & (WithAccountId | WithAddress); -const AddressWithName = ({ +export const AddressWithName = ({ className, symbols, signType, @@ -87,5 +83,3 @@ const AddressWithName = ({
    ); }; - -export default AddressWithName; diff --git a/src/renderer/entities/account/ui/index.ts b/src/renderer/entities/account/ui/index.ts new file mode 100644 index 0000000000..78e306e09b --- /dev/null +++ b/src/renderer/entities/account/ui/index.ts @@ -0,0 +1,5 @@ +export { AccountAddress, getAddress } from './AccountAddress/AccountAddress'; +export type { Props as AccountAddressProps } from './AccountAddress/AccountAddress'; +export * from './AccountsList/AccountsList'; +export * from './AddressWithExplorers/AddressWithExplorers'; +export * from './AddressWithName/AddressWithName'; diff --git a/src/renderer/entities/asset/index.ts b/src/renderer/entities/asset/index.ts new file mode 100644 index 0000000000..7ae4b5e28b --- /dev/null +++ b/src/renderer/entities/asset/index.ts @@ -0,0 +1,4 @@ +export * from './ui'; +export * from './lib'; +export * from './model/asset'; +export * from './model/balance'; diff --git a/src/renderer/services/balance/balanceService.ts b/src/renderer/entities/asset/lib/balanceService.ts similarity index 97% rename from src/renderer/services/balance/balanceService.ts rename to src/renderer/entities/asset/lib/balanceService.ts index e698101ef5..7ec4f7736b 100644 --- a/src/renderer/services/balance/balanceService.ts +++ b/src/renderer/entities/asset/lib/balanceService.ts @@ -6,16 +6,16 @@ import { Codec } from '@polkadot/types/types'; import { Option } from '@polkadot/types'; import _ from 'lodash'; -import { Asset, AssetType, OrmlExtras, StatemineExtras } from '@renderer/domain/asset'; +import { Asset, AssetType, OrmlExtras, StatemineExtras } from '@renderer/entities/asset/model/asset'; import { ChainId, AccountId } from '@renderer/domain/shared-kernel'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { isLightClient } from '@renderer/services/network/common/utils'; -import { validate } from '../dataVerification/dataVerification'; -import storage, { BalanceDS } from '../storage'; +import { ExtendedChain } from '@renderer/entities/network/lib/common/types'; +import { isLightClient } from '@renderer/entities/network/lib/common/utils'; +import { validate } from '../../../services/dataVerification/dataVerification'; +import storage, { BalanceDS } from '../../../shared/api/storage'; import { IBalanceService } from './common/types'; import { VERIFY_TIMEOUT } from './common/constants'; import { useSubscription } from '@renderer/services/subscription/subscriptionService'; -import { toAddress } from '@renderer/shared/utils/address'; +import { toAddress } from '@renderer/shared/lib/utils'; export const useBalance = (): IBalanceService => { const balanceStorage = storage.connectTo('balances'); diff --git a/src/renderer/services/balance/common/constants.ts b/src/renderer/entities/asset/lib/common/constants.ts similarity index 100% rename from src/renderer/services/balance/common/constants.ts rename to src/renderer/entities/asset/lib/common/constants.ts diff --git a/src/renderer/services/balance/common/types.ts b/src/renderer/entities/asset/lib/common/types.ts similarity index 84% rename from src/renderer/services/balance/common/types.ts rename to src/renderer/entities/asset/lib/common/types.ts index 3de7b95352..1b1126f2eb 100644 --- a/src/renderer/services/balance/common/types.ts +++ b/src/renderer/entities/asset/lib/common/types.ts @@ -1,7 +1,7 @@ import { ChainId, AccountId } from '@renderer/domain/shared-kernel'; -import { BalanceDS } from '@renderer/services/storage/common/types'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { BalanceKey } from '@renderer/domain/balance'; +import { BalanceDS } from '@renderer/shared/api/storage/common/types'; +import { ExtendedChain } from '@renderer/entities/network/lib/common/types'; +import { BalanceKey } from '@renderer/entities/asset/model/balance'; export interface IBalanceService { getBalance: (accountId: AccountId, chainId: ChainId, assetId: string) => Promise; diff --git a/src/renderer/services/matrix/index.ts b/src/renderer/entities/asset/lib/index.ts similarity index 59% rename from src/renderer/services/matrix/index.ts rename to src/renderer/entities/asset/lib/index.ts index 5932559950..2131d19588 100644 --- a/src/renderer/services/matrix/index.ts +++ b/src/renderer/entities/asset/lib/index.ts @@ -1,3 +1,3 @@ -export { Matrix as default } from './matrix'; +export * from './balanceService'; export * from './common/types'; export * from './common/constants'; diff --git a/src/renderer/domain/asset.ts b/src/renderer/entities/asset/model/asset.ts similarity index 100% rename from src/renderer/domain/asset.ts rename to src/renderer/entities/asset/model/asset.ts diff --git a/src/renderer/domain/balance.ts b/src/renderer/entities/asset/model/balance.ts similarity index 85% rename from src/renderer/domain/balance.ts rename to src/renderer/entities/asset/model/balance.ts index 85fcfdb536..75b47e86a3 100644 --- a/src/renderer/domain/balance.ts +++ b/src/renderer/entities/asset/model/balance.ts @@ -1,4 +1,4 @@ -import { ChainId, AccountId } from './shared-kernel'; +import { ChainId, AccountId } from '../../../domain/shared-kernel'; export const enum LockTypes { STAKING = '0x7374616b696e6720', diff --git a/src/renderer/components/common/BalanceNew/BalanceNew.stories.tsx b/src/renderer/entities/asset/ui/AssetBalance/AssetBalance.stories.tsx similarity index 71% rename from src/renderer/components/common/BalanceNew/BalanceNew.stories.tsx rename to src/renderer/entities/asset/ui/AssetBalance/AssetBalance.stories.tsx index 8bc8f6c370..86d38770d0 100644 --- a/src/renderer/components/common/BalanceNew/BalanceNew.stories.tsx +++ b/src/renderer/entities/asset/ui/AssetBalance/AssetBalance.stories.tsx @@ -1,12 +1,12 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; -import BalanceNew from './BalanceNew'; +import { AssetBalance } from './AssetBalance'; export default { title: 'Redesign/Token balance', - component: BalanceNew, + component: AssetBalance, parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; +} as ComponentMeta; const assetDot = { assetId: 3, @@ -17,7 +17,7 @@ const assetDot = { name: 'Polkadot', }; -const Template: ComponentStory = (args) => ; +const Template: ComponentStory = (args) => ; export const Default = Template.bind({}); Default.args = { diff --git a/src/renderer/components/common/BalanceNew/BalanceNew.tsx b/src/renderer/entities/asset/ui/AssetBalance/AssetBalance.tsx similarity index 69% rename from src/renderer/components/common/BalanceNew/BalanceNew.tsx rename to src/renderer/entities/asset/ui/AssetBalance/AssetBalance.tsx index b4844077e5..890a251d39 100644 --- a/src/renderer/components/common/BalanceNew/BalanceNew.tsx +++ b/src/renderer/entities/asset/ui/AssetBalance/AssetBalance.tsx @@ -1,8 +1,6 @@ -import cnTw from '@renderer/shared/utils/twMerge'; -import { formatBalance } from '@renderer/shared/utils/balance'; -import { Asset } from '@renderer/domain/asset'; -import { useI18n } from '@renderer/context/I18nContext'; -import { AssetIcon } from '@renderer/components/ui-redesign'; +import { cnTw, formatBalance } from '@renderer/shared/lib/utils'; +import { Asset, AssetIcon } from '@renderer/entities/asset'; +import { useI18n } from '@renderer/app/providers'; type Props = { value: string; @@ -13,7 +11,7 @@ type Props = { wrapperClassName?: string; }; -const BalanceNew = ({ value, asset, className, showIcon, imgClassName, wrapperClassName }: Props) => { +export const AssetBalance = ({ value, asset, className, showIcon, imgClassName, wrapperClassName }: Props) => { const { t } = useI18n(); const { precision, symbol, icon, name } = asset; const { value: formattedValue, decimalPlaces, suffix } = formatBalance(value, precision); @@ -40,5 +38,3 @@ const BalanceNew = ({ value, asset, className, showIcon, imgClassName, wrapperCl ); }; - -export default BalanceNew; diff --git a/src/renderer/screens/Assets/components/AssetCard/AssetCard.test.tsx b/src/renderer/entities/asset/ui/AssetCard/AssetCard.test.tsx similarity index 87% rename from src/renderer/screens/Assets/components/AssetCard/AssetCard.test.tsx rename to src/renderer/entities/asset/ui/AssetCard/AssetCard.test.tsx index 6fc0c74f46..478167d81b 100644 --- a/src/renderer/screens/Assets/components/AssetCard/AssetCard.test.tsx +++ b/src/renderer/entities/asset/ui/AssetCard/AssetCard.test.tsx @@ -1,14 +1,13 @@ import { act, render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; -import { Chain } from '@renderer/domain/chain'; -import { Asset } from '@renderer/domain/asset'; -import chains from '@renderer/services/network/common/chains/chains.json'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; -import { Balance } from '@renderer/domain/balance'; +import { Chain } from '@renderer/entities/chain'; +import { Asset, Balance } from '@renderer/entities/asset'; +import chains from '@renderer/assets/chains/chains.json'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { AssetCard } from './AssetCard'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/screens/Assets/components/AssetCard/AssetCard.tsx b/src/renderer/entities/asset/ui/AssetCard/AssetCard.tsx similarity index 78% rename from src/renderer/screens/Assets/components/AssetCard/AssetCard.tsx rename to src/renderer/entities/asset/ui/AssetCard/AssetCard.tsx index d1eafd9c61..b05df687f1 100644 --- a/src/renderer/screens/Assets/components/AssetCard/AssetCard.tsx +++ b/src/renderer/entities/asset/ui/AssetCard/AssetCard.tsx @@ -1,16 +1,11 @@ import cn from 'classnames'; import { KeyboardEvent, MouseEvent } from 'react'; -import { Shimmering } from '@renderer/components/ui'; -import { Asset } from '@renderer/domain/asset'; -import { Balance } from '@renderer/domain/balance'; -import { useToggle } from '@renderer/shared/hooks'; -import { totalAmount, transferableAmount } from '@renderer/shared/utils/balance'; -import { KeyboardKey } from '@renderer/shared/utils/constants'; -import { AssetIcon, BodyText, IconButton } from '@renderer/components/ui-redesign'; -import { BalanceNew } from '@renderer/components/common'; -import { AssetDetails } from '../AssetDetails/AssetDetails'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Shimmering, BodyText, IconButton } from '@renderer/shared/ui'; +import { Asset, Balance, AssetBalance, AssetDetails, AssetIcon } from '@renderer/entities/asset'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { totalAmount, transferableAmount, KeyboardKey } from '@renderer/shared/lib/utils'; +import { useI18n } from '@renderer/app/providers'; type Props = { asset: Asset; @@ -64,7 +59,7 @@ export const AssetCard = ({ asset, balance, canMakeActions, onReceiveClick, onTr {asset.name} {balance?.free ? ( - + ) : ( )} diff --git a/src/renderer/entities/asset/ui/AssetDetails/AssetDetails.tsx b/src/renderer/entities/asset/ui/AssetDetails/AssetDetails.tsx new file mode 100644 index 0000000000..59f787afcf --- /dev/null +++ b/src/renderer/entities/asset/ui/AssetDetails/AssetDetails.tsx @@ -0,0 +1,21 @@ +import { Asset } from '@renderer/entities/asset'; +import { AssetBalance } from '../index'; +import { Shimmering, HelpText } from '@renderer/shared/ui'; + +type Props = { + asset: Asset; + value?: string; + label: string; + showShimmer?: boolean; +}; + +export const AssetDetails = ({ asset, value, label }: Props) => { + return ( +
    + + {label} + +
    {value ? : }
    +
    + ); +}; diff --git a/src/renderer/components/ui-redesign/AssetIcon/AssetIcon.test.tsx b/src/renderer/entities/asset/ui/AssetIcon/AssetIcon.test.tsx similarity index 84% rename from src/renderer/components/ui-redesign/AssetIcon/AssetIcon.test.tsx rename to src/renderer/entities/asset/ui/AssetIcon/AssetIcon.test.tsx index b03858f156..fb3ae4278b 100644 --- a/src/renderer/components/ui-redesign/AssetIcon/AssetIcon.test.tsx +++ b/src/renderer/entities/asset/ui/AssetIcon/AssetIcon.test.tsx @@ -1,9 +1,9 @@ import { act, render, screen } from '@testing-library/react'; import { AssetIcon } from './AssetIcon'; -import { TEST_CHAIN_ICON } from '@renderer/shared/utils/constants'; +import { TEST_CHAIN_ICON } from '@renderer/shared/lib/utils'; -describe('ui-redesign/ChainIcon', () => { +describe('ui/ChainIcon', () => { test('should render component', async () => { await act(async () => { render(); diff --git a/src/renderer/components/ui-redesign/AssetIcon/AssetIcon.tsx b/src/renderer/entities/asset/ui/AssetIcon/AssetIcon.tsx similarity index 87% rename from src/renderer/components/ui-redesign/AssetIcon/AssetIcon.tsx rename to src/renderer/entities/asset/ui/AssetIcon/AssetIcon.tsx index c060537b47..e41e0ada3f 100644 --- a/src/renderer/components/ui-redesign/AssetIcon/AssetIcon.tsx +++ b/src/renderer/entities/asset/ui/AssetIcon/AssetIcon.tsx @@ -1,5 +1,5 @@ -import { useToggle } from '@renderer/shared/hooks'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { src?: string; diff --git a/src/renderer/entities/asset/ui/index.ts b/src/renderer/entities/asset/ui/index.ts new file mode 100644 index 0000000000..f0e30e8c7c --- /dev/null +++ b/src/renderer/entities/asset/ui/index.ts @@ -0,0 +1,4 @@ +export { AssetBalance } from './AssetBalance/AssetBalance'; +export { AssetDetails } from './AssetDetails/AssetDetails'; +export { AssetCard } from './AssetCard/AssetCard'; +export { AssetIcon } from './AssetIcon/AssetIcon'; diff --git a/src/renderer/entities/chain/index.ts b/src/renderer/entities/chain/index.ts new file mode 100644 index 0000000000..f92e403c7c --- /dev/null +++ b/src/renderer/entities/chain/index.ts @@ -0,0 +1,3 @@ +export * from './ui'; +export * from './lib'; +export * from './model/chain'; diff --git a/src/renderer/services/chainSubscription/chainSubscriptionService.ts b/src/renderer/entities/chain/lib/chainSubscriptionService.ts similarity index 100% rename from src/renderer/services/chainSubscription/chainSubscriptionService.ts rename to src/renderer/entities/chain/lib/chainSubscriptionService.ts diff --git a/src/renderer/services/chainSubscription/common/types.ts b/src/renderer/entities/chain/lib/common/types.ts similarity index 100% rename from src/renderer/services/chainSubscription/common/types.ts rename to src/renderer/entities/chain/lib/common/types.ts diff --git a/src/renderer/entities/chain/lib/index.ts b/src/renderer/entities/chain/lib/index.ts new file mode 100644 index 0000000000..bbd9eefe02 --- /dev/null +++ b/src/renderer/entities/chain/lib/index.ts @@ -0,0 +1,2 @@ +export * from './chainSubscriptionService'; +export * from './common/types'; diff --git a/src/renderer/domain/chain.ts b/src/renderer/entities/chain/model/chain.ts similarity index 85% rename from src/renderer/domain/chain.ts rename to src/renderer/entities/chain/model/chain.ts index b222e6422f..7da8856071 100644 --- a/src/renderer/domain/chain.ts +++ b/src/renderer/entities/chain/model/chain.ts @@ -1,5 +1,5 @@ -import { Asset } from './asset'; -import { ChainId, HexString } from './shared-kernel'; +import { Asset } from '../../asset/model/asset'; +import { ChainId, HexString } from '../../../domain/shared-kernel'; export type Chain = { chainId: ChainId; diff --git a/src/renderer/components/ui-redesign/ChainIcon/ChainIcon.test.tsx b/src/renderer/entities/chain/ui/ChainIcon/ChainIcon.test.tsx similarity index 85% rename from src/renderer/components/ui-redesign/ChainIcon/ChainIcon.test.tsx rename to src/renderer/entities/chain/ui/ChainIcon/ChainIcon.test.tsx index 80e40c911f..4e23e762f7 100644 --- a/src/renderer/components/ui-redesign/ChainIcon/ChainIcon.test.tsx +++ b/src/renderer/entities/chain/ui/ChainIcon/ChainIcon.test.tsx @@ -1,9 +1,9 @@ import { act, render, screen } from '@testing-library/react'; import { ChainIcon } from './ChainIcon'; -import { TEST_CHAIN_ICON } from '@renderer/shared/utils/constants'; +import { TEST_CHAIN_ICON } from '@renderer/shared/lib/utils'; -describe('ui-redesign/ChainIcon', () => { +describe('ui/ChainIcon', () => { test('should render component', async () => { await act(async () => { render(); diff --git a/src/renderer/components/ui-redesign/ChainIcon/ChainIcon.tsx b/src/renderer/entities/chain/ui/ChainIcon/ChainIcon.tsx similarity index 78% rename from src/renderer/components/ui-redesign/ChainIcon/ChainIcon.tsx rename to src/renderer/entities/chain/ui/ChainIcon/ChainIcon.tsx index 481a14a1eb..fe5b74e2ac 100644 --- a/src/renderer/components/ui-redesign/ChainIcon/ChainIcon.tsx +++ b/src/renderer/entities/chain/ui/ChainIcon/ChainIcon.tsx @@ -1,6 +1,6 @@ -import { useToggle } from '@renderer/shared/hooks'; -import { Shimmering } from '@renderer/components/ui'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { Shimmering } from '@renderer/shared/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { src?: string; diff --git a/src/renderer/components/ui-redesign/Chain/Chain.stories.tsx b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.stories.tsx similarity index 50% rename from src/renderer/components/ui-redesign/Chain/Chain.stories.tsx rename to src/renderer/entities/chain/ui/ChainTitle/ChainTitle.stories.tsx index f286ef5443..6bd6f9e66a 100644 --- a/src/renderer/components/ui-redesign/Chain/Chain.stories.tsx +++ b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.stories.tsx @@ -1,15 +1,15 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Chain } from './Chain'; -import { TEST_CHAIN_ID } from '@renderer/shared/utils/constants'; +import { ChainTitle } from './ChainTitle'; +import { TEST_CHAIN_ID } from '@renderer/shared/lib/utils'; export default { title: 'Redesign/Chain', - component: Chain, + component: ChainTitle, parameters: { actions: { argTypesRegex: '^on.*' } }, -} as ComponentMeta; +} as ComponentMeta; -const Template: ComponentStory = (args) => ; +const Template: ComponentStory = (args) => ; export const Primary = Template.bind({}); Primary.args = { diff --git a/src/renderer/components/ui-redesign/Chain/Chain.test.tsx b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.test.tsx similarity index 64% rename from src/renderer/components/ui-redesign/Chain/Chain.test.tsx rename to src/renderer/entities/chain/ui/ChainTitle/ChainTitle.test.tsx index 9a5a25dc6e..636deb7727 100644 --- a/src/renderer/components/ui-redesign/Chain/Chain.test.tsx +++ b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.test.tsx @@ -1,12 +1,12 @@ import { act, render, screen } from '@testing-library/react'; -import { Chain } from './Chain'; -import { TEST_CHAIN_ID } from '@renderer/shared/utils/constants'; +import { ChainTitle } from './ChainTitle'; +import { TEST_CHAIN_ID } from '@renderer/shared/lib/utils'; -describe('ui-redesign/Chain', () => { +describe('ui/Chain', () => { test('should render component', async () => { await act(async () => { - render(); + render(); }); const title = screen.getByText('Polkadot'); diff --git a/src/renderer/components/ui-redesign/Chain/Chain.tsx b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.tsx similarity index 68% rename from src/renderer/components/ui-redesign/Chain/Chain.tsx rename to src/renderer/entities/chain/ui/ChainTitle/ChainTitle.tsx index 8120ea4d91..47da63e3ad 100644 --- a/src/renderer/components/ui-redesign/Chain/Chain.tsx +++ b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.tsx @@ -1,11 +1,10 @@ import { ElementType, useEffect, useState } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { useChains } from '@renderer/services/network/chainsService'; -import { Chain as ChainType } from '@renderer/domain/chain'; -import TextBase from '@renderer/components/ui-redesign/Typography/common/TextBase'; -import { ChainIcon } from '../ChainIcon/ChainIcon'; +import { useChains } from '@renderer/entities/network'; +import { Chain as ChainType, ChainIcon } from '@renderer/entities/chain'; +import TextBase from '@renderer/shared/ui/Typography/common/TextBase'; type WithChain = { chain: ChainType }; type WithChainId = { chainId: ChainId }; @@ -17,7 +16,7 @@ type Props = { iconSize?: number; } & (WithChain | WithChainId); -export const Chain = ({ as: Tag = 'div', fontClass, className, iconSize = 16, ...chainProps }: Props) => { +export const ChainTitle = ({ as: Tag = 'div', fontClass, className, iconSize = 16, ...chainProps }: Props) => { const { getChainById } = useChains(); const [chainObj, setChainObj] = useState(); diff --git a/src/renderer/entities/chain/ui/index.ts b/src/renderer/entities/chain/ui/index.ts new file mode 100644 index 0000000000..ca7d89d3b8 --- /dev/null +++ b/src/renderer/entities/chain/ui/index.ts @@ -0,0 +1,2 @@ +export { ChainTitle } from './ChainTitle/ChainTitle'; +export { ChainIcon } from './ChainIcon/ChainIcon'; diff --git a/src/renderer/entities/contact/index.ts b/src/renderer/entities/contact/index.ts new file mode 100644 index 0000000000..08251fd531 --- /dev/null +++ b/src/renderer/entities/contact/index.ts @@ -0,0 +1,3 @@ +export * from './ui'; +export * from './lib'; +export * from './model/contact'; diff --git a/src/renderer/services/contact/common/types.ts b/src/renderer/entities/contact/lib/common/types.ts similarity index 76% rename from src/renderer/services/contact/common/types.ts rename to src/renderer/entities/contact/lib/common/types.ts index 1294e13619..ae0a8171e2 100644 --- a/src/renderer/services/contact/common/types.ts +++ b/src/renderer/entities/contact/lib/common/types.ts @@ -1,5 +1,5 @@ -import { Contact } from '@renderer/domain/contact'; -import { ContactDS, ID } from '@renderer/services/storage'; +import { ContactDS, ID } from '@renderer/shared/api/storage'; +import { Contact } from '@renderer/entities/contact/model/contact'; export interface IContactService { getContact: (contactId: ID) => Promise; diff --git a/src/renderer/services/contact/contactService.ts b/src/renderer/entities/contact/lib/contactService.ts similarity index 86% rename from src/renderer/services/contact/contactService.ts rename to src/renderer/entities/contact/lib/contactService.ts index f3e3ea7dfc..6b3dce3509 100644 --- a/src/renderer/services/contact/contactService.ts +++ b/src/renderer/entities/contact/lib/contactService.ts @@ -1,8 +1,8 @@ import { useLiveQuery } from 'dexie-react-hooks'; -import storage, { ContactDS } from '@renderer/services/storage'; +import storage, { ContactDS } from '@renderer/shared/api/storage'; import { IContactService } from './common/types'; -import { Contact } from '@renderer/domain/contact'; +import { Contact } from '@renderer/entities/contact/model/contact'; export const useContact = (): IContactService => { const contactStorage = storage.connectTo('contacts'); diff --git a/src/renderer/entities/contact/lib/index.ts b/src/renderer/entities/contact/lib/index.ts new file mode 100644 index 0000000000..06430b1da5 --- /dev/null +++ b/src/renderer/entities/contact/lib/index.ts @@ -0,0 +1,2 @@ +export * from './contactService'; +export * from './common/types'; diff --git a/src/renderer/domain/contact.ts b/src/renderer/entities/contact/model/contact.ts similarity index 61% rename from src/renderer/domain/contact.ts rename to src/renderer/entities/contact/model/contact.ts index f035ac3f0b..27d3297a42 100644 --- a/src/renderer/domain/contact.ts +++ b/src/renderer/entities/contact/model/contact.ts @@ -1,4 +1,4 @@ -import { AccountId, Address } from './shared-kernel'; +import { AccountId, Address } from '../../../domain/shared-kernel'; export type Contact = { name: string; diff --git a/src/renderer/screens/AddressBook/Overview/components/ContactList/ContactList.tsx b/src/renderer/entities/contact/ui/ContactList/ContactList.tsx similarity index 85% rename from src/renderer/screens/AddressBook/Overview/components/ContactList/ContactList.tsx rename to src/renderer/entities/contact/ui/ContactList/ContactList.tsx index 29f059c7ec..3132c79f53 100644 --- a/src/renderer/screens/AddressBook/Overview/components/ContactList/ContactList.tsx +++ b/src/renderer/entities/contact/ui/ContactList/ContactList.tsx @@ -1,11 +1,11 @@ import { useState, useEffect } from 'react'; -import { useI18n } from '@renderer/context/I18nContext'; -import { EmptySearch } from '../EmptyState/EmptySearch'; -import { includes } from '@renderer/shared/utils/strings'; -import { AddressWithName } from '@renderer/components/common'; -import { BodyText, FootnoteText, IconButton, Plate } from '@renderer/components/ui-redesign'; -import { Contact } from '@renderer/domain/contact'; +import { useI18n } from '@renderer/app/providers'; +import { EmptySearch } from '@renderer/pages/AddressBook/Overview/components'; +import { includes } from '@renderer/shared/lib/utils'; +import { AddressWithName } from '@renderer/entities/account'; +import { BodyText, FootnoteText, IconButton, Plate } from '@renderer/shared/ui'; +import { Contact } from '@renderer/entities/contact'; type Props = { query?: string; diff --git a/src/renderer/entities/contact/ui/index.ts b/src/renderer/entities/contact/ui/index.ts new file mode 100644 index 0000000000..4baed554f9 --- /dev/null +++ b/src/renderer/entities/contact/ui/index.ts @@ -0,0 +1 @@ +export * from './ContactList/ContactList'; diff --git a/src/renderer/entities/matrix/index.ts b/src/renderer/entities/matrix/index.ts new file mode 100644 index 0000000000..f41a696fd2 --- /dev/null +++ b/src/renderer/entities/matrix/index.ts @@ -0,0 +1 @@ +export * from './lib'; diff --git a/src/renderer/services/matrix/__tests__/credentialStorage.test.ts b/src/renderer/entities/matrix/lib/__tests__/credentialStorage.test.ts similarity index 100% rename from src/renderer/services/matrix/__tests__/credentialStorage.test.ts rename to src/renderer/entities/matrix/lib/__tests__/credentialStorage.test.ts diff --git a/src/renderer/services/matrix/__tests__/matrix.test.ts b/src/renderer/entities/matrix/lib/__tests__/matrix.test.ts similarity index 100% rename from src/renderer/services/matrix/__tests__/matrix.test.ts rename to src/renderer/entities/matrix/lib/__tests__/matrix.test.ts diff --git a/src/renderer/services/matrix/__tests__/secretStorage.test.ts b/src/renderer/entities/matrix/lib/__tests__/secretStorage.test.ts similarity index 100% rename from src/renderer/services/matrix/__tests__/secretStorage.test.ts rename to src/renderer/entities/matrix/lib/__tests__/secretStorage.test.ts diff --git a/src/renderer/services/matrix/common/constants.ts b/src/renderer/entities/matrix/lib/common/constants.ts similarity index 100% rename from src/renderer/services/matrix/common/constants.ts rename to src/renderer/entities/matrix/lib/common/constants.ts diff --git a/src/renderer/services/matrix/common/errors.ts b/src/renderer/entities/matrix/lib/common/errors.ts similarity index 100% rename from src/renderer/services/matrix/common/errors.ts rename to src/renderer/entities/matrix/lib/common/errors.ts diff --git a/src/renderer/services/matrix/common/types.ts b/src/renderer/entities/matrix/lib/common/types.ts similarity index 98% rename from src/renderer/services/matrix/common/types.ts rename to src/renderer/entities/matrix/lib/common/types.ts index cb455bc359..09b86d4ac4 100644 --- a/src/renderer/services/matrix/common/types.ts +++ b/src/renderer/entities/matrix/lib/common/types.ts @@ -9,7 +9,7 @@ import { CallData, ChainId, } from '@renderer/domain/shared-kernel'; -import { MultisigTxStatus } from '@renderer/domain/transaction'; +import { MultisigTxStatus } from '@renderer/entities/transaction/model/transaction'; // ===================================================== // ============ ISecureMessenger interface ============= diff --git a/src/renderer/services/matrix/credentialStorage.ts b/src/renderer/entities/matrix/lib/credentialStorage.ts similarity index 100% rename from src/renderer/services/matrix/credentialStorage.ts rename to src/renderer/entities/matrix/lib/credentialStorage.ts diff --git a/src/renderer/entities/matrix/lib/index.ts b/src/renderer/entities/matrix/lib/index.ts new file mode 100644 index 0000000000..41624369d2 --- /dev/null +++ b/src/renderer/entities/matrix/lib/index.ts @@ -0,0 +1,3 @@ +export * from './matrix'; +export * from './common/types'; +export * from './common/constants'; diff --git a/src/renderer/services/matrix/matrix.ts b/src/renderer/entities/matrix/lib/matrix.ts similarity index 99% rename from src/renderer/services/matrix/matrix.ts rename to src/renderer/entities/matrix/lib/matrix.ts index e43668f41e..418b2cd03b 100644 --- a/src/renderer/services/matrix/matrix.ts +++ b/src/renderer/entities/matrix/lib/matrix.ts @@ -56,7 +56,7 @@ import { } from './common/types'; import CredentialStorage from './credentialStorage'; import SecretStorage from './secretStorage'; -import { nonNullable } from '@renderer/shared/utils/functions'; +import { nonNullable } from '@renderer/shared/lib/utils'; global.Olm = Olm; logger.disableAll(); diff --git a/src/renderer/services/matrix/secretStorage.ts b/src/renderer/entities/matrix/lib/secretStorage.ts similarity index 100% rename from src/renderer/services/matrix/secretStorage.ts rename to src/renderer/entities/matrix/lib/secretStorage.ts diff --git a/src/renderer/entities/multisig/index.ts b/src/renderer/entities/multisig/index.ts new file mode 100644 index 0000000000..f41a696fd2 --- /dev/null +++ b/src/renderer/entities/multisig/index.ts @@ -0,0 +1 @@ +export * from './lib'; diff --git a/src/renderer/entities/multisig/lib/index.ts b/src/renderer/entities/multisig/lib/index.ts new file mode 100644 index 0000000000..0c62a0573b --- /dev/null +++ b/src/renderer/entities/multisig/lib/index.ts @@ -0,0 +1,6 @@ +export * from './multisigEvent/multisigEventService'; +export * from './multisigEvent/common/types'; +export * from './multisigTx/multisigTxService'; +export * from './multisigTx/common/types'; +export * from './multisigTx/common/consts'; +export * from './multisigTx/common/utils'; diff --git a/src/renderer/services/multisigEvent/common/types.ts b/src/renderer/entities/multisig/lib/multisigEvent/common/types.ts similarity index 89% rename from src/renderer/services/multisigEvent/common/types.ts rename to src/renderer/entities/multisig/lib/multisigEvent/common/types.ts index 6a5323593e..f125b8c75d 100644 --- a/src/renderer/services/multisigEvent/common/types.ts +++ b/src/renderer/entities/multisig/lib/multisigEvent/common/types.ts @@ -1,6 +1,6 @@ +import { ID, MultisigEventDS } from '@renderer/shared/api/storage'; import { AccountId, CallHash, ChainId } from '@renderer/domain/shared-kernel'; -import { MultisigEvent, MultisigTransactionKey, SigningStatus } from '@renderer/domain/transaction'; -import { ID, MultisigEventDS } from '@renderer/services/storage'; +import { MultisigEvent, MultisigTransactionKey, SigningStatus } from '@renderer/entities/transaction/model/transaction'; export interface IMultisigEventService { getEvent: (eventId: ID) => Promise; diff --git a/src/renderer/services/multisigEvent/multisigEventService.ts b/src/renderer/entities/multisig/lib/multisigEvent/multisigEventService.ts similarity index 94% rename from src/renderer/services/multisigEvent/multisigEventService.ts rename to src/renderer/entities/multisig/lib/multisigEvent/multisigEventService.ts index 55f1cb70da..c590f5888c 100644 --- a/src/renderer/services/multisigEvent/multisigEventService.ts +++ b/src/renderer/entities/multisig/lib/multisigEvent/multisigEventService.ts @@ -1,10 +1,10 @@ import { useLiveQuery } from 'dexie-react-hooks'; -import storage, { MultisigEventDS } from '@renderer/services/storage'; +import storage, { MultisigEventDS } from '@renderer/shared/api/storage'; import { IMultisigEventService } from './common/types'; import { AccountId, CallHash, ChainId } from '@renderer/domain/shared-kernel'; -import { MultisigEvent, MultisigTransactionKey, SigningStatus } from '@renderer/domain/transaction'; -import { Task } from '@renderer/shared/hooks/useTaskQueue'; +import { MultisigEvent, MultisigTransactionKey, SigningStatus } from '@renderer/entities/transaction/model/transaction'; +import { Task } from '@renderer/shared/lib/hooks/useTaskQueue'; type Props = { addTask?: (task: Task) => void; diff --git a/src/renderer/services/multisigTx/common/consts.ts b/src/renderer/entities/multisig/lib/multisigTx/common/consts.ts similarity index 100% rename from src/renderer/services/multisigTx/common/consts.ts rename to src/renderer/entities/multisig/lib/multisigTx/common/consts.ts diff --git a/src/renderer/services/multisigTx/common/types.ts b/src/renderer/entities/multisig/lib/multisigTx/common/types.ts similarity index 86% rename from src/renderer/services/multisigTx/common/types.ts rename to src/renderer/entities/multisig/lib/multisigTx/common/types.ts index 9f58d66901..c0eca32e93 100644 --- a/src/renderer/services/multisigTx/common/types.ts +++ b/src/renderer/entities/multisig/lib/multisigTx/common/types.ts @@ -2,9 +2,9 @@ import { ApiPromise } from '@polkadot/api'; import { U8aFixed } from '@polkadot/types'; import { PalletMultisigMultisig } from '@polkadot/types/lookup'; -import { MultisigAccount } from '@renderer/domain/account'; -import { MultisigTransactionDS } from '@renderer/services/storage'; -import { MultisigTransaction } from '@renderer/domain/transaction'; +import { MultisigTransactionDS } from '@renderer/shared/api/storage'; +import { MultisigAccount } from '@renderer/entities/account/model/account'; +import { MultisigTransaction } from '@renderer/entities/transaction/model/transaction'; import { CallData, AccountId, ChainId, CallHash } from '@renderer/domain/shared-kernel'; export interface IMultisigTxService { diff --git a/src/renderer/services/multisigTx/common/utils.ts b/src/renderer/entities/multisig/lib/multisigTx/common/utils.ts similarity index 93% rename from src/renderer/services/multisigTx/common/utils.ts rename to src/renderer/entities/multisig/lib/multisigTx/common/utils.ts index 8f11e07899..19e3fa28cf 100644 --- a/src/renderer/services/multisigTx/common/utils.ts +++ b/src/renderer/entities/multisig/lib/multisigTx/common/utils.ts @@ -2,11 +2,15 @@ import { ApiPromise } from '@polkadot/api'; import { Vec } from '@polkadot/types'; import { AccountId32 } from '@polkadot/types/interfaces'; -import { MultisigAccount } from '@renderer/domain/account'; +import { MultisigAccount } from '@renderer/entities/account/model/account'; import { Address, ChainId } from '@renderer/domain/shared-kernel'; -import { MultisigEvent, MultisigTransaction, MultisigTxInitStatus } from '@renderer/domain/transaction'; +import { + MultisigEvent, + MultisigTransaction, + MultisigTxInitStatus, +} from '@renderer/entities/transaction/model/transaction'; import { PendingMultisigTransaction } from './types'; -import { getCreatedDate } from '@renderer/shared/utils/substrate'; +import { getCreatedDate } from '@renderer/shared/lib/utils'; export const getPendingMultisigTxs = async ( api: ApiPromise, diff --git a/src/renderer/services/multisigTx/multisigTxService.ts b/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts similarity index 92% rename from src/renderer/services/multisigTx/multisigTxService.ts rename to src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts index 63cc8d1717..b4068f7214 100644 --- a/src/renderer/services/multisigTx/multisigTxService.ts +++ b/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts @@ -1,9 +1,13 @@ import { ApiPromise } from '@polkadot/api'; import { useLiveQuery } from 'dexie-react-hooks'; -import { MultisigAccount } from '@renderer/domain/account'; -import { MultisigTransaction, MultisigTxFinalStatus, MultisigTxInitStatus } from '@renderer/domain/transaction'; -import storage, { MultisigTransactionDS } from '../storage'; +import { MultisigAccount } from '@renderer/entities/account/model/account'; +import { + MultisigTransaction, + MultisigTxFinalStatus, + MultisigTxInitStatus, +} from '@renderer/entities/transaction/model/transaction'; +import storage, { MultisigTransactionDS } from '../../../../shared/api/storage'; import { QUERY_INTERVAL } from './common/consts'; import { IMultisigTxService } from './common/types'; import { @@ -14,13 +18,12 @@ import { createNewEventsPayload, updateOldEventsPayload, } from './common/utils'; -import { useChains } from '../network/chainsService'; -import { useTransaction } from '../transaction/transactionService'; +import { useChains } from '../../../network/lib/chainsService'; +import { useTransaction } from '../../../transaction/lib/transactionService'; import { CallData, AccountId } from '@renderer/domain/shared-kernel'; -import { toAddress } from '@renderer/shared/utils/address'; -import { getCurrentBlockNumber, getExpectedBlockTime } from '@renderer/shared/utils/substrate'; +import { toAddress, getCurrentBlockNumber, getExpectedBlockTime } from '@renderer/shared/lib/utils'; import { useMultisigEvent } from '../multisigEvent/multisigEventService'; -import { Task } from '@renderer/shared/hooks/useTaskQueue'; +import { Task } from '@renderer/shared/lib/hooks/useTaskQueue'; type Props = { addTask?: (task: Task) => void; diff --git a/src/renderer/entities/network/index.ts b/src/renderer/entities/network/index.ts new file mode 100644 index 0000000000..f41a696fd2 --- /dev/null +++ b/src/renderer/entities/network/index.ts @@ -0,0 +1 @@ +export * from './lib'; diff --git a/src/renderer/services/network/__tests__/chainSpecService.test.ts b/src/renderer/entities/network/lib/__tests__/chainSpecService.test.ts similarity index 93% rename from src/renderer/services/network/__tests__/chainSpecService.test.ts rename to src/renderer/entities/network/lib/__tests__/chainSpecService.test.ts index 1b91aedcfe..17811c777c 100644 --- a/src/renderer/services/network/__tests__/chainSpecService.test.ts +++ b/src/renderer/entities/network/lib/__tests__/chainSpecService.test.ts @@ -1,4 +1,4 @@ -import { Chains } from '@renderer/services/network/common/constants'; +import { Chains } from '@renderer/entities/network/lib/common/constants'; import { useChainSpec } from '../chainSpecService'; jest.mock('@polkadot/rpc-provider/substrate-connect', () => ({ diff --git a/src/renderer/services/network/__tests__/chainsService.test.ts b/src/renderer/entities/network/lib/__tests__/chainsService.test.ts similarity index 100% rename from src/renderer/services/network/__tests__/chainsService.test.ts rename to src/renderer/entities/network/lib/__tests__/chainsService.test.ts diff --git a/src/renderer/services/network/chainSpecService.ts b/src/renderer/entities/network/lib/chainSpecService.ts similarity index 100% rename from src/renderer/services/network/chainSpecService.ts rename to src/renderer/entities/network/lib/chainSpecService.ts diff --git a/src/renderer/services/network/chainsService.ts b/src/renderer/entities/network/lib/chainsService.ts similarity index 88% rename from src/renderer/services/network/chainsService.ts rename to src/renderer/entities/network/lib/chainsService.ts index 012c3cb655..303d9f021d 100644 --- a/src/renderer/services/network/chainsService.ts +++ b/src/renderer/entities/network/lib/chainsService.ts @@ -3,15 +3,12 @@ import concat from 'lodash/concat'; import keyBy from 'lodash/keyBy'; import orderBy from 'lodash/orderBy'; -import { Chain } from '@renderer/domain/chain'; -import chainsProd from './common/chains/chains.json'; -import chainsDev from './common/chains/chains_dev.json'; +import { Chain } from '@renderer/entities/chain/model/chain'; +import chainsProd from '@renderer/assets/chains/chains.json'; +import chainsDev from '@renderer/assets/chains/chains_dev.json'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { getRelaychainAsset } from '@renderer/shared/utils/assets'; -import { nonNullable } from '@renderer/shared/utils/functions'; -import { Balance } from '@renderer/domain/balance'; -import { totalAmount } from '@renderer/shared/utils/balance'; -import { ZERO_BALANCE } from '@renderer/shared/utils/constants'; +import { getRelaychainAsset, nonNullable, totalAmount, ZERO_BALANCE } from '@renderer/shared/lib/utils'; +import { Balance } from '@renderer/entities/asset/model/balance'; import { ChainLike, IChainService } from './common/types'; import { isKusama, isPolkadot, isTestnet, isNameWithNumber } from './common/utils'; diff --git a/src/renderer/services/network/common/constants.ts b/src/renderer/entities/network/lib/common/constants.ts similarity index 100% rename from src/renderer/services/network/common/constants.ts rename to src/renderer/entities/network/lib/common/constants.ts diff --git a/src/renderer/services/network/common/types.ts b/src/renderer/entities/network/lib/common/types.ts similarity index 94% rename from src/renderer/services/network/common/types.ts rename to src/renderer/entities/network/lib/common/types.ts index f43caa6c5f..12f85c918f 100644 --- a/src/renderer/services/network/common/types.ts +++ b/src/renderer/entities/network/lib/common/types.ts @@ -1,10 +1,10 @@ import { ApiPromise } from '@polkadot/api'; import { ProviderInterface } from '@polkadot/rpc-provider/types'; -import { Chain, RpcNode } from '@renderer/domain/chain'; +import { Chain, RpcNode } from '@renderer/entities/chain/model/chain'; import { Connection, ConnectionType } from '@renderer/domain/connection'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { Balance } from '@renderer/domain/balance'; +import { Balance } from '@renderer/entities/asset/model/balance'; // ===================================================== // ================ Service interface ================== diff --git a/src/renderer/services/network/common/utils.ts b/src/renderer/entities/network/lib/common/utils.ts similarity index 90% rename from src/renderer/services/network/common/utils.ts rename to src/renderer/entities/network/lib/common/utils.ts index e39818a4e8..133ac49050 100644 --- a/src/renderer/services/network/common/utils.ts +++ b/src/renderer/entities/network/lib/common/utils.ts @@ -1,4 +1,4 @@ -import { ChainOptions } from '@renderer/domain/chain'; +import { ChainOptions } from '@renderer/entities/chain/model/chain'; import { ConnectionType } from '@renderer/domain/connection'; import { ExtendedChain } from './types'; diff --git a/src/renderer/entities/network/lib/index.ts b/src/renderer/entities/network/lib/index.ts new file mode 100644 index 0000000000..c94417b95a --- /dev/null +++ b/src/renderer/entities/network/lib/index.ts @@ -0,0 +1,6 @@ +export * from './chainSpecService'; +export * from './chainsService'; +export * from './networkService'; +export * from './common/types'; +export * from './common/utils'; +export * from './common/constants'; diff --git a/src/renderer/services/network/networkService.ts b/src/renderer/entities/network/lib/networkService.ts similarity index 98% rename from src/renderer/services/network/networkService.ts rename to src/renderer/entities/network/lib/networkService.ts index ea58acaa5b..a164ea3e1c 100644 --- a/src/renderer/services/network/networkService.ts +++ b/src/renderer/entities/network/lib/networkService.ts @@ -4,11 +4,11 @@ import { ProviderInterface } from '@polkadot/rpc-provider/types'; import keyBy from 'lodash/keyBy'; import { useRef, useState } from 'react'; -import { Chain, RpcNode } from '@renderer/domain/chain'; +import storage from '@renderer/shared/api/storage'; +import { Chain, RpcNode } from '@renderer/entities/chain/model/chain'; import { Connection, ConnectionStatus, ConnectionType } from '@renderer/domain/connection'; import { ChainId } from '@renderer/domain/shared-kernel'; -import storage from '@renderer/services/storage'; -import { ISubscriptionService } from '../subscription/common/types'; +import { ISubscriptionService } from '../../../services/subscription/common/types'; import { useChainSpec } from './chainSpecService'; import { useChains } from './chainsService'; import { AUTO_BALANCE_TIMEOUT, MAX_ATTEMPTS, PROGRESSION_BASE } from './common/constants'; diff --git a/src/renderer/entities/notification/index.ts b/src/renderer/entities/notification/index.ts new file mode 100644 index 0000000000..5a4e4eaf50 --- /dev/null +++ b/src/renderer/entities/notification/index.ts @@ -0,0 +1,3 @@ +export * from './ui'; +export * from './lib'; +export * from './model/notification'; diff --git a/src/renderer/services/notification/common/types.ts b/src/renderer/entities/notification/lib/common/types.ts similarity index 66% rename from src/renderer/services/notification/common/types.ts rename to src/renderer/entities/notification/lib/common/types.ts index ca912e3e63..e6d7a1c7f8 100644 --- a/src/renderer/services/notification/common/types.ts +++ b/src/renderer/entities/notification/lib/common/types.ts @@ -1,5 +1,5 @@ -import { Notification } from '@renderer/domain/notification'; -import { ID, NotificationDS } from '@renderer/services/storage'; +import { ID, NotificationDS } from '@renderer/shared/api/storage'; +import { Notification } from '@renderer/entities/notification/model/notification'; export interface INotificationService { getNotifications: (where?: Partial) => Promise; diff --git a/src/renderer/entities/notification/lib/index.ts b/src/renderer/entities/notification/lib/index.ts new file mode 100644 index 0000000000..d3e11b7280 --- /dev/null +++ b/src/renderer/entities/notification/lib/index.ts @@ -0,0 +1,2 @@ +export * from './notificationService'; +export * from './common/types'; diff --git a/src/renderer/services/notification/notificationService.ts b/src/renderer/entities/notification/lib/notificationService.ts similarity index 84% rename from src/renderer/services/notification/notificationService.ts rename to src/renderer/entities/notification/lib/notificationService.ts index 76be28faac..db8b3a410d 100644 --- a/src/renderer/services/notification/notificationService.ts +++ b/src/renderer/entities/notification/lib/notificationService.ts @@ -1,8 +1,8 @@ import { useLiveQuery } from 'dexie-react-hooks'; -import storage, { NotificationDS } from '@renderer/services/storage'; +import storage, { NotificationDS } from '@renderer/shared/api/storage'; import { INotificationService } from './common/types'; -import { Notification } from '@renderer/domain/notification'; +import { Notification } from '@renderer/entities/notification/model/notification'; export const useNotification = (): INotificationService => { const notificationStorage = storage.connectTo('notifications'); diff --git a/src/renderer/domain/notification.ts b/src/renderer/entities/notification/model/notification.ts similarity index 52% rename from src/renderer/domain/notification.ts rename to src/renderer/entities/notification/model/notification.ts index 8c91b39f77..99e4f546bc 100644 --- a/src/renderer/domain/notification.ts +++ b/src/renderer/entities/notification/model/notification.ts @@ -1,12 +1,13 @@ -import { AccountId, CallHash, ChainId, Timepoint } from './shared-kernel'; +import { AccountId, CallHash, ChainId, Timepoint } from '../../../domain/shared-kernel'; +import { ObjectValues } from '@renderer/domain/utility'; -export const enum MultisigNotificationType { - ACCOUNT_INVITED = 'MultisigAccountInvitedNotification', - MST_CREATED = 'MultisigCreatedNotification', - MST_APPROVED = 'MultisigApprovedNotification', - MST_EXECUTED = 'MultisigExecutedNotification', - MST_CANCELLED = 'MultisigCancelledNotification', -} +export const MultisigNotificationType = { + ACCOUNT_INVITED: 'MultisigAccountInvitedNotification', + MST_CREATED: 'MultisigCreatedNotification', + MST_APPROVED: 'MultisigApprovedNotification', + MST_EXECUTED: 'MultisigExecutedNotification', + MST_CANCELLED: 'MultisigCancelledNotification', +} as const; export type MultisigAccountInvitedNotification = { signatories: AccountId[]; @@ -29,5 +30,5 @@ export type MultisigNotification = { export type Notification = { read: boolean; dateCreated: number; - type: MultisigNotificationType; + type: ObjectValues; } & MultisigNotification; diff --git a/src/renderer/screens/Notifications/components/NotificationRow.tsx b/src/renderer/entities/notification/ui/NotificationRow/NotificationRow.tsx similarity index 84% rename from src/renderer/screens/Notifications/components/NotificationRow.tsx rename to src/renderer/entities/notification/ui/NotificationRow/NotificationRow.tsx index ab42700638..84891373d1 100644 --- a/src/renderer/screens/Notifications/components/NotificationRow.tsx +++ b/src/renderer/entities/notification/ui/NotificationRow/NotificationRow.tsx @@ -1,16 +1,15 @@ import { format } from 'date-fns'; import { TFunction, Trans } from 'react-i18next'; -import { BodyText, FootnoteText } from '@renderer/components/ui-redesign'; +import { BodyText, FootnoteText, Identicon } from '@renderer/shared/ui'; import { MultisigAccountInvitedNotification, MultisigNotification, MultisigNotificationType, Notification, -} from '@renderer/domain/notification'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Identicon } from '@renderer/components/ui'; -import { toAddress } from '@renderer/shared/utils/address'; +} from '../../model/notification'; +import { useI18n } from '@renderer/app/providers'; +import { toAddress } from '@renderer/shared/lib/utils'; const NotificationBody = { [MultisigNotificationType.ACCOUNT_INVITED]: (n: Notification, t: TFunction) => { @@ -52,7 +51,7 @@ type Props = { notification: Notification; }; -const NotificationRow = ({ notification }: Props) => { +export const NotificationRow = ({ notification }: Props) => { const { t, dateLocale } = useI18n(); const { dateCreated, type } = notification; @@ -68,5 +67,3 @@ const NotificationRow = ({ notification }: Props) => {
  • ); }; - -export default NotificationRow; diff --git a/src/renderer/entities/notification/ui/index.ts b/src/renderer/entities/notification/ui/index.ts new file mode 100644 index 0000000000..b0bb2b6ae7 --- /dev/null +++ b/src/renderer/entities/notification/ui/index.ts @@ -0,0 +1 @@ +export * from './NotificationRow/NotificationRow'; diff --git a/src/renderer/entities/settings/index.ts b/src/renderer/entities/settings/index.ts new file mode 100644 index 0000000000..f41a696fd2 --- /dev/null +++ b/src/renderer/entities/settings/index.ts @@ -0,0 +1 @@ +export * from './lib'; diff --git a/src/renderer/services/settings/__tests__/settingsStorage.test.ts b/src/renderer/entities/settings/lib/__tests__/settingsStorage.test.ts similarity index 100% rename from src/renderer/services/settings/__tests__/settingsStorage.test.ts rename to src/renderer/entities/settings/lib/__tests__/settingsStorage.test.ts diff --git a/src/renderer/services/settings/common/constants.ts b/src/renderer/entities/settings/lib/common/constants.ts similarity index 100% rename from src/renderer/services/settings/common/constants.ts rename to src/renderer/entities/settings/lib/common/constants.ts diff --git a/src/renderer/services/settings/common/types.ts b/src/renderer/entities/settings/lib/common/types.ts similarity index 100% rename from src/renderer/services/settings/common/types.ts rename to src/renderer/entities/settings/lib/common/types.ts diff --git a/src/renderer/entities/settings/lib/index.ts b/src/renderer/entities/settings/lib/index.ts new file mode 100644 index 0000000000..b46ca846de --- /dev/null +++ b/src/renderer/entities/settings/lib/index.ts @@ -0,0 +1,3 @@ +export * from './settingsStorage'; +export * from './common/types'; +export * from './common/constants'; diff --git a/src/renderer/services/settings/settingsStorage.ts b/src/renderer/entities/settings/lib/settingsStorage.ts similarity index 100% rename from src/renderer/services/settings/settingsStorage.ts rename to src/renderer/entities/settings/lib/settingsStorage.ts diff --git a/src/renderer/entities/signatory/index.ts b/src/renderer/entities/signatory/index.ts new file mode 100644 index 0000000000..afcf88750b --- /dev/null +++ b/src/renderer/entities/signatory/index.ts @@ -0,0 +1,2 @@ +export * from './ui'; +export * from './model/signatory'; diff --git a/src/renderer/domain/signatory.ts b/src/renderer/entities/signatory/model/signatory.ts similarity index 66% rename from src/renderer/domain/signatory.ts rename to src/renderer/entities/signatory/model/signatory.ts index 5975410909..beb708928e 100644 --- a/src/renderer/domain/signatory.ts +++ b/src/renderer/entities/signatory/model/signatory.ts @@ -1,4 +1,4 @@ -import { Contact } from './contact'; +import { Contact } from '../../contact/model/contact'; import { PartialBy } from '@renderer/domain/utility'; export type Signatory = PartialBy; diff --git a/src/renderer/components/common/SelectableSignatory/SelectableSignatory.tsx b/src/renderer/entities/signatory/ui/SelectableSignatory/SelectableSignatory.tsx similarity index 55% rename from src/renderer/components/common/SelectableSignatory/SelectableSignatory.tsx rename to src/renderer/entities/signatory/ui/SelectableSignatory/SelectableSignatory.tsx index e1f9a24a93..db60e24b1d 100644 --- a/src/renderer/components/common/SelectableSignatory/SelectableSignatory.tsx +++ b/src/renderer/entities/signatory/ui/SelectableSignatory/SelectableSignatory.tsx @@ -1,18 +1,9 @@ -import AccountAddress, { - getAddress, - Props as AccountAddressProps, -} from '@renderer/components/common/AccountAddress/AccountAddress'; -import { InfoPopover } from '@renderer/components/ui-redesign'; -import { Icon } from '@renderer/components/ui'; -import { Explorer } from '@renderer/domain/chain'; -import useAddressInfo from '@renderer/components/common/AccountAddress/useAddressInfo'; -import { toAccountId } from '@renderer/shared/utils/address'; -import { useBalance } from '@renderer/services/balance/balanceService'; +import { AccountAddress, getAddress, AccountAddressProps, useAddressInfo } from '@renderer/entities/account'; +import { InfoPopover, Icon } from '@renderer/shared/ui'; +import { Explorer } from '@renderer/entities/chain'; +import { toAccountId, transferableAmount, cnTw } from '@renderer/shared/lib/utils'; +import { useBalance, Asset, AssetBalance } from '@renderer/entities/asset'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { Asset } from '@renderer/domain/asset'; -import BalanceNew from '../BalanceNew/BalanceNew'; -import { transferableAmount } from '@renderer/shared/utils/balance'; -import cnTw from '@renderer/shared/utils/twMerge'; type Props = { explorers?: Explorer[]; @@ -22,7 +13,7 @@ type Props = { asset: Asset; } & AccountAddressProps; -const SelectableSignatory = ({ +export const SelectableSignatory = ({ explorers, size = 20, value, @@ -49,11 +40,13 @@ const SelectableSignatory = ({ {balance && asset && ( - + )} ); }; - -export default SelectableSignatory; diff --git a/src/renderer/components/common/SignatoryCard/SignatoryCard.stories.tsx b/src/renderer/entities/signatory/ui/SignatoryCard/SignatoryCard.stories.tsx similarity index 80% rename from src/renderer/components/common/SignatoryCard/SignatoryCard.stories.tsx rename to src/renderer/entities/signatory/ui/SignatoryCard/SignatoryCard.stories.tsx index 3b8e77aa9a..c4038902bc 100644 --- a/src/renderer/components/common/SignatoryCard/SignatoryCard.stories.tsx +++ b/src/renderer/entities/signatory/ui/SignatoryCard/SignatoryCard.stories.tsx @@ -1,7 +1,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; -import SignatoryCard from './SignatoryCard'; -import { TEST_ADDRESS } from '@renderer/shared/utils/constants'; +import { SignatoryCard } from './SignatoryCard'; +import { TEST_ADDRESS } from '@renderer/shared/lib/utils'; export default { title: 'Redesign/Signatory', diff --git a/src/renderer/components/common/SignatoryCard/SignatoryCard.test.tsx b/src/renderer/entities/signatory/ui/SignatoryCard/SignatoryCard.test.tsx similarity index 67% rename from src/renderer/components/common/SignatoryCard/SignatoryCard.test.tsx rename to src/renderer/entities/signatory/ui/SignatoryCard/SignatoryCard.test.tsx index 81da0b0f10..e570d20880 100644 --- a/src/renderer/components/common/SignatoryCard/SignatoryCard.test.tsx +++ b/src/renderer/entities/signatory/ui/SignatoryCard/SignatoryCard.test.tsx @@ -1,21 +1,22 @@ import { render, screen } from '@testing-library/react'; -import { TEST_ADDRESS } from '@renderer/shared/utils/constants'; -import SignatoryCard from './SignatoryCard'; +import { TEST_ADDRESS } from '@renderer/shared/lib/utils'; +import { SignatoryCard } from './SignatoryCard'; -jest.mock('@renderer/services/contact/contactService', () => ({ +jest.mock('@renderer/entities/contact', () => ({ useContact: jest.fn().mockReturnValue({ getLiveContacts: jest.fn().mockReturnValue([]), }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ + ...jest.requireActual('@renderer/entities/account'), useAccount: jest.fn().mockReturnValue({ getLiveAccounts: jest.fn().mockReturnValue([]), }), })); -jest.mock('@renderer/context/MatrixContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useMatrix: jest.fn().mockReturnValue({ matrix: { userId: 'some_id' } }), })); diff --git a/src/renderer/components/common/SignatoryCard/SignatoryCard.tsx b/src/renderer/entities/signatory/ui/SignatoryCard/SignatoryCard.tsx similarity index 73% rename from src/renderer/components/common/SignatoryCard/SignatoryCard.tsx rename to src/renderer/entities/signatory/ui/SignatoryCard/SignatoryCard.tsx index c23daddee0..f20552766a 100644 --- a/src/renderer/components/common/SignatoryCard/SignatoryCard.tsx +++ b/src/renderer/entities/signatory/ui/SignatoryCard/SignatoryCard.tsx @@ -1,13 +1,8 @@ -import AccountAddress, { - getAddress, - Props as AccountAddressProps, -} from '@renderer/components/common/AccountAddress/AccountAddress'; -import { InfoPopover } from '@renderer/components/ui-redesign'; -import { Icon } from '@renderer/components/ui'; -import { Explorer } from '@renderer/domain/chain'; -import { SigningStatus } from '@renderer/domain/transaction'; -import useAddressInfo from '@renderer/components/common/AccountAddress/useAddressInfo'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { AccountAddress, getAddress, AccountAddressProps, useAddressInfo } from '@renderer/entities/account'; +import { InfoPopover, Icon } from '@renderer/shared/ui'; +import { Explorer } from '@renderer/entities/chain'; +import { SigningStatus } from '@renderer/entities/transaction'; +import { cnTw } from '@renderer/shared/lib/utils'; const IconProps = { SIGNED: { className: 'group-hover:hidden text-text-positive', name: 'checkLineRedesign' }, @@ -21,7 +16,7 @@ type Props = { wrapperClassName?: string; } & AccountAddressProps; -const SignatoryCard = ({ +export const SignatoryCard = ({ explorers, status, addressFont = 'text-body text-inherit', @@ -57,5 +52,3 @@ const SignatoryCard = ({ ); }; - -export default SignatoryCard; diff --git a/src/renderer/entities/signatory/ui/index.ts b/src/renderer/entities/signatory/ui/index.ts new file mode 100644 index 0000000000..9ef09d6812 --- /dev/null +++ b/src/renderer/entities/signatory/ui/index.ts @@ -0,0 +1,2 @@ +export * from './SelectableSignatory/SelectableSignatory'; +export * from './SignatoryCard/SignatoryCard'; diff --git a/src/renderer/entities/staking/index.ts b/src/renderer/entities/staking/index.ts new file mode 100644 index 0000000000..e39fecb146 --- /dev/null +++ b/src/renderer/entities/staking/index.ts @@ -0,0 +1,3 @@ +// export * from './ui'; +export * from './lib'; +export * from './model/stake'; diff --git a/src/renderer/services/staking/__tests__/apyCalculator.test.ts b/src/renderer/entities/staking/lib/__tests__/apyCalculator.test.ts similarity index 100% rename from src/renderer/services/staking/__tests__/apyCalculator.test.ts rename to src/renderer/entities/staking/lib/__tests__/apyCalculator.test.ts diff --git a/src/renderer/services/staking/__tests__/stakingDataService.test.ts b/src/renderer/entities/staking/lib/__tests__/stakingDataService.test.ts similarity index 100% rename from src/renderer/services/staking/__tests__/stakingDataService.test.ts rename to src/renderer/entities/staking/lib/__tests__/stakingDataService.test.ts diff --git a/src/renderer/services/staking/__tests__/stakingRewardsService.test.ts b/src/renderer/entities/staking/lib/__tests__/stakingRewardsService.test.ts similarity index 100% rename from src/renderer/services/staking/__tests__/stakingRewardsService.test.ts rename to src/renderer/entities/staking/lib/__tests__/stakingRewardsService.test.ts diff --git a/src/renderer/services/staking/apyCalculator.ts b/src/renderer/entities/staking/lib/apyCalculator.ts similarity index 100% rename from src/renderer/services/staking/apyCalculator.ts rename to src/renderer/entities/staking/lib/apyCalculator.ts diff --git a/src/renderer/services/staking/common/constants.ts b/src/renderer/entities/staking/lib/common/constants.ts similarity index 100% rename from src/renderer/services/staking/common/constants.ts rename to src/renderer/entities/staking/lib/common/constants.ts diff --git a/src/renderer/services/staking/common/types.ts b/src/renderer/entities/staking/lib/common/types.ts similarity index 97% rename from src/renderer/services/staking/common/types.ts rename to src/renderer/entities/staking/lib/common/types.ts index 5e2128b293..6bacda13cd 100644 --- a/src/renderer/services/staking/common/types.ts +++ b/src/renderer/entities/staking/lib/common/types.ts @@ -1,7 +1,7 @@ import { ApiPromise } from '@polkadot/api'; import { Address, ChainId, EraIndex } from '@renderer/domain/shared-kernel'; -import { Stake } from '@renderer/domain/stake'; +import { Stake } from '@renderer/entities/staking/model/stake'; import { Validator } from '@renderer/domain/validator'; // ===================================================== diff --git a/src/renderer/services/staking/eraService.ts b/src/renderer/entities/staking/lib/eraService.ts similarity index 100% rename from src/renderer/services/staking/eraService.ts rename to src/renderer/entities/staking/lib/eraService.ts diff --git a/src/renderer/entities/staking/lib/index.ts b/src/renderer/entities/staking/lib/index.ts new file mode 100644 index 0000000000..24db56fc0e --- /dev/null +++ b/src/renderer/entities/staking/lib/index.ts @@ -0,0 +1,7 @@ +export * from './apyCalculator'; +export * from './eraService'; +export * from './stakingDataService'; +export * from './stakingRewardsService'; +export * from './validatorsService'; +export * from './common/types'; +export * from './common/constants'; diff --git a/src/renderer/services/staking/stakingDataService.ts b/src/renderer/entities/staking/lib/stakingDataService.ts similarity index 100% rename from src/renderer/services/staking/stakingDataService.ts rename to src/renderer/entities/staking/lib/stakingDataService.ts diff --git a/src/renderer/services/staking/stakingRewardsService.ts b/src/renderer/entities/staking/lib/stakingRewardsService.ts similarity index 89% rename from src/renderer/services/staking/stakingRewardsService.ts rename to src/renderer/entities/staking/lib/stakingRewardsService.ts index 9f07c474eb..82ff687eee 100644 --- a/src/renderer/services/staking/stakingRewardsService.ts +++ b/src/renderer/entities/staking/lib/stakingRewardsService.ts @@ -3,7 +3,7 @@ import { useQuery } from '@apollo/client'; import { Address } from '@renderer/domain/shared-kernel'; import { GET_TOTAL_REWARDS } from '@renderer/graphql/queries/stakingRewards'; import { RewardsQuery } from '@renderer/graphql/types/stakingRewards'; -import { IStakingRewardsService, RewardsMap } from '@renderer/services/staking/common/types'; +import { IStakingRewardsService, RewardsMap } from '@renderer/entities/staking/lib/common/types'; export const useStakingRewards = (addresses: Address[]): IStakingRewardsService => { const { data, loading } = useQuery(GET_TOTAL_REWARDS, { diff --git a/src/renderer/services/staking/validatorsService.ts b/src/renderer/entities/staking/lib/validatorsService.ts similarity index 100% rename from src/renderer/services/staking/validatorsService.ts rename to src/renderer/entities/staking/lib/validatorsService.ts diff --git a/src/renderer/domain/stake.ts b/src/renderer/entities/staking/model/stake.ts similarity index 100% rename from src/renderer/domain/stake.ts rename to src/renderer/entities/staking/model/stake.ts diff --git a/src/renderer/entities/transaction/index.ts b/src/renderer/entities/transaction/index.ts new file mode 100644 index 0000000000..c7baeb12de --- /dev/null +++ b/src/renderer/entities/transaction/index.ts @@ -0,0 +1,3 @@ +export * from './ui'; +export * from './lib'; +export * from './model/transaction'; diff --git a/src/renderer/services/transaction/callDataDecoder.ts b/src/renderer/entities/transaction/lib/callDataDecoder.ts similarity index 98% rename from src/renderer/services/transaction/callDataDecoder.ts rename to src/renderer/entities/transaction/lib/callDataDecoder.ts index 61d2cb01da..cb3bf0dd43 100644 --- a/src/renderer/services/transaction/callDataDecoder.ts +++ b/src/renderer/entities/transaction/lib/callDataDecoder.ts @@ -5,7 +5,7 @@ import { HexString } from '@polkadot/util/types'; import { Type } from '@polkadot/types'; import { Address, CallData } from '@renderer/domain/shared-kernel'; -import { DecodedTransaction, TransactionType } from '@renderer/domain/transaction'; +import { DecodedTransaction, TransactionType } from '@renderer/entities/transaction/model/transaction'; import { BOND_WITH_CONTROLLER_ARGS_AMOUNT, OLD_MULTISIG_ARGS_AMOUNT } from './common/constants'; import { ICallDataDecoder } from './common/types'; diff --git a/src/renderer/services/transaction/common/constants.ts b/src/renderer/entities/transaction/lib/common/constants.ts similarity index 100% rename from src/renderer/services/transaction/common/constants.ts rename to src/renderer/entities/transaction/lib/common/constants.ts diff --git a/src/renderer/services/transaction/common/types.ts b/src/renderer/entities/transaction/lib/common/types.ts similarity index 95% rename from src/renderer/services/transaction/common/types.ts rename to src/renderer/entities/transaction/lib/common/types.ts index 58bb6e63bb..8b46d93c2f 100644 --- a/src/renderer/services/transaction/common/types.ts +++ b/src/renderer/entities/transaction/lib/common/types.ts @@ -4,7 +4,7 @@ import { Weight } from '@polkadot/types/interfaces'; import { SubmittableExtrinsic } from '@polkadot/api/types'; import { Address, CallData, HexString, Timepoint, Threshold, AccountId } from '@renderer/domain/shared-kernel'; -import { DecodedTransaction, Transaction } from '@renderer/domain/transaction'; +import { DecodedTransaction, Transaction } from '@renderer/entities/transaction/model/transaction'; // ===================================================== // =========== ITransactionService interface =========== diff --git a/src/renderer/services/transaction/common/utils.ts b/src/renderer/entities/transaction/lib/common/utils.ts similarity index 93% rename from src/renderer/services/transaction/common/utils.ts rename to src/renderer/entities/transaction/lib/common/utils.ts index 186f6568bd..23b1e468d7 100644 --- a/src/renderer/services/transaction/common/utils.ts +++ b/src/renderer/entities/transaction/lib/common/utils.ts @@ -1,7 +1,7 @@ import { ApiPromise } from '@polkadot/api'; import { SpRuntimeDispatchError } from '@polkadot/types/lookup'; -import { Transaction } from '@renderer/domain/transaction'; +import { Transaction } from '@renderer/entities/transaction/model/transaction'; import { MAX_WEIGHT, OLD_MULTISIG_ARGS_AMOUNT, CONTROLLER_ARG_NAME } from './constants'; export const decodeDispatchError = (error: SpRuntimeDispatchError, api: ApiPromise): string => { diff --git a/src/renderer/entities/transaction/lib/index.ts b/src/renderer/entities/transaction/lib/index.ts new file mode 100644 index 0000000000..d7fdc340e8 --- /dev/null +++ b/src/renderer/entities/transaction/lib/index.ts @@ -0,0 +1,5 @@ +export * from './callDataDecoder'; +export * from './transactionService'; +export * from './common/utils'; +export * from './common/types'; +export * from './common/constants'; diff --git a/src/renderer/services/transaction/transactionService.ts b/src/renderer/entities/transaction/lib/transactionService.ts similarity index 98% rename from src/renderer/services/transaction/transactionService.ts rename to src/renderer/entities/transaction/lib/transactionService.ts index 1707be12a9..a988f9bf1e 100644 --- a/src/renderer/services/transaction/transactionService.ts +++ b/src/renderer/entities/transaction/lib/transactionService.ts @@ -15,9 +15,8 @@ import { Weight } from '@polkadot/types/interfaces'; import { blake2AsU8a, signatureVerify } from '@polkadot/util-crypto'; import { AccountId, HexString, Threshold } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { createTxMetadata } from '@renderer/shared/utils/substrate'; -import { toAccountId } from '@renderer/shared/utils/address'; +import { Transaction, TransactionType } from '@renderer/entities/transaction/model/transaction'; +import { createTxMetadata, toAccountId } from '@renderer/shared/lib/utils'; import { ITransactionService, HashData, ExtrinsicResultParams } from './common/types'; import { decodeDispatchError, getMaxWeight, isControllerMissing, isOldMultisigPallet } from './common/utils'; import { useCallDataDecoder } from './callDataDecoder'; diff --git a/src/renderer/domain/transaction.ts b/src/renderer/entities/transaction/model/transaction.ts similarity index 96% rename from src/renderer/domain/transaction.ts rename to src/renderer/entities/transaction/model/transaction.ts index 44cdb21c10..07985f0b0a 100644 --- a/src/renderer/domain/transaction.ts +++ b/src/renderer/entities/transaction/model/transaction.ts @@ -1,5 +1,5 @@ -import { Address, ChainId, HexString, AccountId, CallData, CallHash } from './shared-kernel'; -import { Signatory } from './signatory'; +import { Address, ChainId, HexString, AccountId, CallData, CallHash } from '../../../domain/shared-kernel'; +import { Signatory } from '../../signatory/model/signatory'; import { PartialBy } from '@renderer/domain/utility'; export const enum TransactionType { diff --git a/src/renderer/components/common/Deposit/Deposit.test.tsx b/src/renderer/entities/transaction/ui/Deposit/Deposit.test.tsx similarity index 62% rename from src/renderer/components/common/Deposit/Deposit.test.tsx rename to src/renderer/entities/transaction/ui/Deposit/Deposit.test.tsx index a3ada976b1..0fce6f6716 100644 --- a/src/renderer/components/common/Deposit/Deposit.test.tsx +++ b/src/renderer/entities/transaction/ui/Deposit/Deposit.test.tsx @@ -1,33 +1,29 @@ import { ApiPromise } from '@polkadot/api'; import { act, render, screen } from '@testing-library/react'; -import { Asset } from '@renderer/domain/asset'; -import { BalanceNew } from '@renderer/components/common'; -import Deposit from './Deposit'; +import { Asset } from '@renderer/entities/asset'; +import { Deposit } from './Deposit'; jest.mock('@renderer/components/common'); -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/transaction/transactionService', () => ({ +jest.mock('@renderer/entities/transaction', () => ({ useTransaction: jest.fn().mockReturnValue({ getTransactionDeposit: jest.fn().mockReturnValue('46'), }), })); -describe('components/common/Deposit', () => { - beforeAll(() => { - (BalanceNew as jest.Mock).mockImplementation(({ value }: any) =>

    {value}

    ); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); +jest.mock('@renderer/entities/asset', () => ({ + ...jest.requireActual('@renderer/entities/asset'), + AssetBalance: ({ value }: any) =>
    {value}
    , +})); +describe('components/common/Deposit', () => { test('should render component', async () => { const asset = { symbol: 'DOT', precision: 10 } as Asset; diff --git a/src/renderer/components/common/Deposit/Deposit.tsx b/src/renderer/entities/transaction/ui/Deposit/Deposit.tsx similarity index 59% rename from src/renderer/components/common/Deposit/Deposit.tsx rename to src/renderer/entities/transaction/ui/Deposit/Deposit.tsx index 7203d46e91..1397834ca5 100644 --- a/src/renderer/components/common/Deposit/Deposit.tsx +++ b/src/renderer/entities/transaction/ui/Deposit/Deposit.tsx @@ -1,10 +1,9 @@ import { ApiPromise } from '@polkadot/api'; import { useEffect, useState, memo } from 'react'; -import { Asset } from '@renderer/domain/asset'; +import { Asset, AssetBalance } from '@renderer/entities/asset'; import { Threshold } from '@renderer/domain/shared-kernel'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { BalanceNew } from '@renderer/components/common'; +import { useTransaction } from '@renderer/entities/transaction'; type Props = { api: ApiPromise; @@ -14,7 +13,7 @@ type Props = { onDepositChange?: (deposit: string) => void; }; -const Deposit = ({ api, asset, threshold, className, onDepositChange }: Props) => { +export const Deposit = memo(({ api, asset, threshold, className, onDepositChange }: Props) => { const { getTransactionDeposit } = useTransaction(); const [deposit, setDeposit] = useState(''); @@ -26,7 +25,5 @@ const Deposit = ({ api, asset, threshold, className, onDepositChange }: Props) = onDepositChange?.(txDeposit); }, [threshold, api]); - return ; -}; - -export default memo(Deposit); + return ; +}); diff --git a/src/renderer/components/common/DepositWithLabel/DepositWithLabel.test.tsx b/src/renderer/entities/transaction/ui/DepositWithLabel/DepositWithLabel.test.tsx similarity index 74% rename from src/renderer/components/common/DepositWithLabel/DepositWithLabel.test.tsx rename to src/renderer/entities/transaction/ui/DepositWithLabel/DepositWithLabel.test.tsx index 33880b7b39..167419923d 100644 --- a/src/renderer/components/common/DepositWithLabel/DepositWithLabel.test.tsx +++ b/src/renderer/entities/transaction/ui/DepositWithLabel/DepositWithLabel.test.tsx @@ -1,22 +1,22 @@ import { ApiPromise } from '@polkadot/api'; import { render, screen } from '@testing-library/react'; -import { Asset } from '@renderer/domain/asset'; +import { Asset } from '@renderer/entities/asset'; import { DepositWithLabel } from './DepositWithLabel'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/transaction/transactionService', () => ({ +jest.mock('@renderer/entities/transaction', () => ({ useTransaction: jest.fn().mockReturnValue({ getTransactionDeposit: jest.fn().mockReturnValue('46'), }), })); -jest.mock('@renderer/components/common/BalanceNew/BalanceNew', () => () =>
    deposit_value
    ); +jest.mock('@renderer/entities/asset', () => ({ AssetBalance: () =>
    deposit_value
    })); describe('components/common/DepositWithLabel', () => { test('should render component', () => { diff --git a/src/renderer/components/common/DepositWithLabel/DepositWithLabel.tsx b/src/renderer/entities/transaction/ui/DepositWithLabel/DepositWithLabel.tsx similarity index 74% rename from src/renderer/components/common/DepositWithLabel/DepositWithLabel.tsx rename to src/renderer/entities/transaction/ui/DepositWithLabel/DepositWithLabel.tsx index 9388baa24b..350f1ce32e 100644 --- a/src/renderer/components/common/DepositWithLabel/DepositWithLabel.tsx +++ b/src/renderer/entities/transaction/ui/DepositWithLabel/DepositWithLabel.tsx @@ -1,9 +1,8 @@ import { ComponentProps } from 'react'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Icon } from '@renderer/components/ui'; -import { FootnoteText, Tooltip } from '@renderer/components/ui-redesign'; -import { Deposit, DetailRow } from '@renderer/components/common'; +import { useI18n } from '@renderer/app/providers'; +import { Icon, FootnoteText, Tooltip, DetailRow } from '@renderer/shared/ui'; +import { Deposit } from '../Deposit/Deposit'; export const DepositWithLabel = ({ ...depositProps }: ComponentProps) => { const { t } = useI18n(); diff --git a/src/renderer/components/common/Fee/Fee.test.tsx b/src/renderer/entities/transaction/ui/Fee/Fee.test.tsx similarity index 77% rename from src/renderer/components/common/Fee/Fee.test.tsx rename to src/renderer/entities/transaction/ui/Fee/Fee.test.tsx index 1d89bbee01..845e76636f 100644 --- a/src/renderer/components/common/Fee/Fee.test.tsx +++ b/src/renderer/entities/transaction/ui/Fee/Fee.test.tsx @@ -1,34 +1,30 @@ import { ApiPromise } from '@polkadot/api'; import { act, render, screen } from '@testing-library/react'; -import { Asset } from '@renderer/domain/asset'; -import { Transaction } from '@renderer/domain/transaction'; -import Fee from './Fee'; -import { BalanceNew } from '@renderer/components/common'; +import { Asset } from '@renderer/entities/asset'; +import { Transaction } from '@renderer/entities/transaction'; +import { Fee } from './Fee'; jest.mock('@renderer/components/common'); -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/transaction/transactionService', () => ({ +jest.mock('@renderer/entities/transaction', () => ({ useTransaction: jest.fn().mockReturnValue({ getTransactionFee: jest.fn().mockResolvedValue('12'), }), })); -describe('components/common/Fee', () => { - beforeAll(() => { - (BalanceNew as jest.Mock).mockImplementation(({ value }: any) =>

    {value}

    ); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); +jest.mock('@renderer/entities/asset', () => ({ + ...jest.requireActual('@renderer/entities/asset'), + AssetBalance: ({ value }: any) =>
    {value}
    , +})); +describe('components/common/Fee', () => { test('should render component', async () => { const asset = { symbol: 'DOT', precision: 10 } as Asset; const tx = { address: '0x123', args: {} } as Transaction; diff --git a/src/renderer/components/common/Fee/Fee.tsx b/src/renderer/entities/transaction/ui/Fee/Fee.tsx similarity index 69% rename from src/renderer/components/common/Fee/Fee.tsx rename to src/renderer/entities/transaction/ui/Fee/Fee.tsx index 48aeff02dd..94fdb45e58 100644 --- a/src/renderer/components/common/Fee/Fee.tsx +++ b/src/renderer/entities/transaction/ui/Fee/Fee.tsx @@ -2,11 +2,9 @@ import { ApiPromise } from '@polkadot/api'; import { BN } from '@polkadot/util'; import { useEffect, useState, memo } from 'react'; -import { Asset } from '@renderer/domain/asset'; -import { Transaction } from '@renderer/domain/transaction'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { BalanceNew } from '@renderer/components/common'; -import { Shimmering } from '@renderer/components/ui'; +import { Asset, AssetBalance } from '@renderer/entities/asset'; +import { Transaction, useTransaction } from '@renderer/entities/transaction'; +import { Shimmering } from '@renderer/shared/ui'; type Props = { api: ApiPromise; @@ -18,7 +16,7 @@ type Props = { onFeeLoading?: (loading: boolean) => void; }; -const Fee = ({ api, multiply = 1, asset, transaction, className, onFeeChange, onFeeLoading }: Props) => { +export const Fee = memo(({ api, multiply = 1, asset, transaction, className, onFeeChange, onFeeLoading }: Props) => { const { getTransactionFee } = useTransaction(); const [fee, setFee] = useState(''); @@ -56,7 +54,5 @@ const Fee = ({ api, multiply = 1, asset, transaction, className, onFeeChange, on const totalFee = new BN(fee).muln(multiply).toString(); - return ; -}; - -export default memo(Fee); + return ; +}); diff --git a/src/renderer/components/common/OperationResult/OperationResult.stories.tsx b/src/renderer/entities/transaction/ui/OperationResult/OperationResult.stories.tsx similarity index 100% rename from src/renderer/components/common/OperationResult/OperationResult.stories.tsx rename to src/renderer/entities/transaction/ui/OperationResult/OperationResult.stories.tsx diff --git a/src/renderer/components/common/OperationResult/OperationResult.test.tsx b/src/renderer/entities/transaction/ui/OperationResult/OperationResult.test.tsx similarity index 87% rename from src/renderer/components/common/OperationResult/OperationResult.test.tsx rename to src/renderer/entities/transaction/ui/OperationResult/OperationResult.test.tsx index 619146da32..a1aa367d80 100644 --- a/src/renderer/components/common/OperationResult/OperationResult.test.tsx +++ b/src/renderer/entities/transaction/ui/OperationResult/OperationResult.test.tsx @@ -3,7 +3,7 @@ import noop from 'lodash/noop'; import { OperationResult } from './OperationResult'; -jest.mock('@renderer/components/ui-redesign/Animation/Animation', () => ({ +jest.mock('@renderer/shared/ui/Animation/Animation', () => ({ Animation: () => animation, })); diff --git a/src/renderer/components/common/OperationResult/OperationResult.tsx b/src/renderer/entities/transaction/ui/OperationResult/OperationResult.tsx similarity index 85% rename from src/renderer/components/common/OperationResult/OperationResult.tsx rename to src/renderer/entities/transaction/ui/OperationResult/OperationResult.tsx index 240cc8505c..d6925d878e 100644 --- a/src/renderer/components/common/OperationResult/OperationResult.tsx +++ b/src/renderer/entities/transaction/ui/OperationResult/OperationResult.tsx @@ -1,12 +1,12 @@ import { Fragment, PropsWithChildren, useEffect, useState } from 'react'; import { Dialog, Transition } from '@headlessui/react'; -import { ModalBackdrop, ModalTransition } from '@renderer/components/ui-redesign/Modals/common'; -import { FootnoteText, SmallTitleText } from '@renderer/components/ui-redesign'; -import { Animation } from '@renderer/components/ui-redesign/Animation/Animation'; +import { ModalBackdrop, ModalTransition } from '@renderer/shared/ui/Modals/common'; +import { FootnoteText, SmallTitleText } from '@renderer/shared/ui'; +import { Animation } from '@renderer/shared/ui/Animation/Animation'; import { VariantAnimationProps } from './common/constants'; import { Variant } from './common/types'; -import Animations from '@renderer/components/ui-redesign/Animation/Data'; +import Animations from '@renderer/shared/ui/Animation/Data'; type Props = { title: string; diff --git a/src/renderer/components/common/OperationResult/common/constants.ts b/src/renderer/entities/transaction/ui/OperationResult/common/constants.ts similarity index 79% rename from src/renderer/components/common/OperationResult/common/constants.ts rename to src/renderer/entities/transaction/ui/OperationResult/common/constants.ts index 35b55b5cf6..4d57410e16 100644 --- a/src/renderer/components/common/OperationResult/common/constants.ts +++ b/src/renderer/entities/transaction/ui/OperationResult/common/constants.ts @@ -1,6 +1,6 @@ -import { IconNames } from '@renderer/components/ui/Icon/data'; +import { IconNames } from '@renderer/shared/ui/Icon/data'; import { Variant } from './types'; -import { AnimationNames, Props } from '@renderer/components/ui-redesign/Animation/Animation'; +import { AnimationNames, Props } from '@renderer/shared/ui/Animation/Animation'; export const VariantIcons: Record = { success: 'checkLineRedesign', diff --git a/src/renderer/components/common/OperationResult/common/types.ts b/src/renderer/entities/transaction/ui/OperationResult/common/types.ts similarity index 100% rename from src/renderer/components/common/OperationResult/common/types.ts rename to src/renderer/entities/transaction/ui/OperationResult/common/types.ts diff --git a/src/renderer/entities/transaction/ui/index.ts b/src/renderer/entities/transaction/ui/index.ts new file mode 100644 index 0000000000..2c3a334f65 --- /dev/null +++ b/src/renderer/entities/transaction/ui/index.ts @@ -0,0 +1,9 @@ +import { Deposit } from './Deposit/Deposit'; +import { DepositWithLabel } from './DepositWithLabel/DepositWithLabel'; +import { Fee } from './Fee/Fee'; +import { OperationResult } from './OperationResult/OperationResult'; + +export * from './OperationResult/common/constants'; +export type { Variant } from './OperationResult/common/types'; + +export { Fee, DepositWithLabel, OperationResult, Deposit }; diff --git a/src/renderer/entities/wallet/index.ts b/src/renderer/entities/wallet/index.ts new file mode 100644 index 0000000000..b79d946e99 --- /dev/null +++ b/src/renderer/entities/wallet/index.ts @@ -0,0 +1,2 @@ +export * from './lib'; +export * from './model/wallet'; diff --git a/src/renderer/services/wallet/common/types.ts b/src/renderer/entities/wallet/lib/common/types.ts similarity index 76% rename from src/renderer/services/wallet/common/types.ts rename to src/renderer/entities/wallet/lib/common/types.ts index de19f921a3..6c54050280 100644 --- a/src/renderer/services/wallet/common/types.ts +++ b/src/renderer/entities/wallet/lib/common/types.ts @@ -1,5 +1,5 @@ -import { Wallet } from '@renderer/domain/wallet'; -import { WalletDS, ID } from '@renderer/services/storage'; +import { WalletDS, ID } from '@renderer/shared/api/storage'; +import { Wallet } from '@renderer/entities/wallet/model/wallet'; export interface IWalletService { getWallet: (walletId: string) => Promise; diff --git a/src/renderer/entities/wallet/lib/index.ts b/src/renderer/entities/wallet/lib/index.ts new file mode 100644 index 0000000000..142a1e1667 --- /dev/null +++ b/src/renderer/entities/wallet/lib/index.ts @@ -0,0 +1,2 @@ +export * from './walletService'; +export * from './common/types'; diff --git a/src/renderer/services/wallet/walletService.ts b/src/renderer/entities/wallet/lib/walletService.ts similarity index 86% rename from src/renderer/services/wallet/walletService.ts rename to src/renderer/entities/wallet/lib/walletService.ts index e1a22cb324..60b47e3f66 100644 --- a/src/renderer/services/wallet/walletService.ts +++ b/src/renderer/entities/wallet/lib/walletService.ts @@ -1,8 +1,8 @@ import { useLiveQuery } from 'dexie-react-hooks'; -import storage, { WalletDS } from '@renderer/services/storage'; +import storage, { WalletDS } from '@renderer/shared/api/storage'; import { IWalletService } from './common/types'; -import { Wallet } from '@renderer/domain/wallet'; +import { Wallet } from '@renderer/entities/wallet/model/wallet'; export const useWallet = (): IWalletService => { const walletStorage = storage.connectTo('wallets'); diff --git a/src/renderer/domain/wallet.ts b/src/renderer/entities/wallet/model/wallet.ts similarity index 74% rename from src/renderer/domain/wallet.ts rename to src/renderer/entities/wallet/model/wallet.ts index 6e85afa327..4dbc2535d4 100644 --- a/src/renderer/domain/wallet.ts +++ b/src/renderer/entities/wallet/model/wallet.ts @@ -1,4 +1,4 @@ -import { WalletType } from './shared-kernel'; +import { WalletType } from '../../../domain/shared-kernel'; export type Wallet = { name: string; diff --git a/src/renderer/index.css b/src/renderer/index.css deleted file mode 100644 index 05295847b9..0000000000 --- a/src/renderer/index.css +++ /dev/null @@ -1,120 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -/* Inter (Latin) */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('assets/fonts/Inter/Inter-400.woff2') format('woff2'); -} -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('assets/fonts/Inter/Inter-500.woff2') format('woff2'); -} -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('assets/fonts/Inter/Inter-600.woff2') format('woff2'); -} -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url('assets/fonts/Inter/Inter-800.woff2') format('woff2'); -} - -/* Manrope (Latin) */ -@font-face { - font-family: 'Manrope'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('assets/fonts/Manrope/Manrope-400.woff2') format('woff2'); -} -@font-face { - font-family: 'Manrope'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('assets/fonts/Manrope/Manrope-500.woff2') format('woff2'); -} -@font-face { - font-family: 'Manrope'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('assets/fonts/Manrope/Manrope-600.woff2') format('woff2'); -} -@font-face { - font-family: 'Manrope'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url('assets/fonts/Manrope/Manrope-800.woff2') format('woff2'); -} - -/* Scrollbar for Firefox */ -* { - scrollbar-width: thin; -} - -/* Scrollbar for other Browsers */ -/* Width */ -::-webkit-scrollbar { - width: 8px; - height: 8px; /* horizontal scrollbar */ -} - -/* Track */ -::-webkit-scrollbar-track { - background: transparent; -} - -::-webkit-scrollbar-track:vertical { - margin: 2px 0; -} - -::-webkit-scrollbar-track:horizontal { - margin: 0 2px; -} - -/* Handle */ -::-webkit-scrollbar-thumb { - background-color: var(--scroll-background-default); - border-radius: 4px; - border: 2px solid transparent; - background-clip: padding-box; -} - -*:focus-visible { - outline: 2px solid var(--focus-container-border); -} - -ul:focus-visible { - outline: none; -} - -.logo-background { - background-size: cover; - background-position: center; - background-image: url('assets/images/misc/bg.webp'); - transform: scaleY(-1); -} - -.video-cover { - position: absolute; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.27); - left: 0; - top: 0; -} diff --git a/src/renderer/screens/AddressBook/ManageContact/ManageContact.test.tsx b/src/renderer/pages/AddressBook/ManageContact/ManageContact.test.tsx similarity index 87% rename from src/renderer/screens/AddressBook/ManageContact/ManageContact.test.tsx rename to src/renderer/pages/AddressBook/ManageContact/ManageContact.test.tsx index 704a0bf595..8a0e6adedc 100644 --- a/src/renderer/screens/AddressBook/ManageContact/ManageContact.test.tsx +++ b/src/renderer/pages/AddressBook/ManageContact/ManageContact.test.tsx @@ -3,7 +3,7 @@ import { MemoryRouter } from 'react-router-dom'; import { ManageContact } from './ManageContact'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), @@ -14,7 +14,7 @@ jest.mock('react-router-dom', () => ({ useSearchParams: jest.fn().mockReturnValue([new URLSearchParams('id=7')]), })); -jest.mock('@renderer/services/contact/contactService', () => ({ +jest.mock('@renderer/entities/contact', () => ({ useContact: jest.fn().mockReturnValue({ getContact: jest.fn().mockResolvedValue({ name: 'Contact', @@ -29,7 +29,7 @@ jest.mock('@renderer/components/forms', () => ({ ContactForm: () => contactForm, })); -describe('screens/AddressBook/ContactForm', () => { +describe('pages/AddressBook/ContactForm', () => { afterEach(() => { jest.clearAllMocks(); }); diff --git a/src/renderer/screens/AddressBook/ManageContact/ManageContact.tsx b/src/renderer/pages/AddressBook/ManageContact/ManageContact.tsx similarity index 77% rename from src/renderer/screens/AddressBook/ManageContact/ManageContact.tsx rename to src/renderer/pages/AddressBook/ManageContact/ManageContact.tsx index 0d9cb9a79f..2465d30aa3 100644 --- a/src/renderer/screens/AddressBook/ManageContact/ManageContact.tsx +++ b/src/renderer/pages/AddressBook/ManageContact/ManageContact.tsx @@ -1,15 +1,12 @@ import { useEffect, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; -import { useContact } from '@renderer/services/contact/contactService'; -import { Contact } from '@renderer/domain/contact'; +import { useContact, Contact } from '@renderer/entities/contact'; import { ContactForm } from '@renderer/components/forms'; -import Paths from '@renderer/routes/paths'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; -import { useToggle } from '@renderer/shared/hooks'; -import { BaseModal } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Loader } from '@renderer/components/ui'; +import { Paths, useI18n } from '@renderer/app/providers'; +import { DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { BaseModal, Loader } from '@renderer/shared/ui'; export const ManageContact = () => { const { t } = useI18n(); diff --git a/src/renderer/screens/AddressBook/Overview/Overview.test.tsx b/src/renderer/pages/AddressBook/Overview/Overview.test.tsx similarity index 87% rename from src/renderer/screens/AddressBook/Overview/Overview.test.tsx rename to src/renderer/pages/AddressBook/Overview/Overview.test.tsx index a7a1776aba..63bd8c2b7c 100644 --- a/src/renderer/screens/AddressBook/Overview/Overview.test.tsx +++ b/src/renderer/pages/AddressBook/Overview/Overview.test.tsx @@ -3,7 +3,7 @@ import { MemoryRouter } from 'react-router-dom'; import { Overview } from './Overview'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), @@ -14,7 +14,7 @@ jest.mock('react-router-dom', () => ({ Outlet: () => 'outlet', })); -jest.mock('@renderer/services/contact/contactService', () => ({ +jest.mock('@renderer/entities/contact', () => ({ useContact: jest.fn().mockReturnValue({ getLiveContacts: jest.fn().mockReturnValue([]), }), diff --git a/src/renderer/screens/AddressBook/Overview/Overview.tsx b/src/renderer/pages/AddressBook/Overview/Overview.tsx similarity index 83% rename from src/renderer/screens/AddressBook/Overview/Overview.tsx rename to src/renderer/pages/AddressBook/Overview/Overview.tsx index 76049c1b4c..8942c13c16 100644 --- a/src/renderer/screens/AddressBook/Overview/Overview.tsx +++ b/src/renderer/pages/AddressBook/Overview/Overview.tsx @@ -1,14 +1,12 @@ import { useState } from 'react'; import { Outlet, useNavigate } from 'react-router-dom'; -import { useI18n } from '@renderer/context/I18nContext'; +import { useI18n, createLink, Paths } from '@renderer/app/providers'; import { Header } from '@renderer/components/common'; -import { useContact } from '@renderer/services/contact/contactService'; -import { Button, SearchInput } from '@renderer/components/ui-redesign'; -import { createLink } from '@renderer/routes/utils'; -import { ContactDS } from '@renderer/services/storage'; +import { useContact } from '@renderer/entities/contact'; +import { Button, SearchInput } from '@renderer/shared/ui'; +import { ContactDS } from '@renderer/shared/api/storage'; import { EmptyContacts, ContactList } from './components'; -import Paths from '@renderer/routes/paths'; export const Overview = () => { const { t } = useI18n(); diff --git a/src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptyContacts.test.tsx b/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.test.tsx similarity index 81% rename from src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptyContacts.test.tsx rename to src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.test.tsx index d5be09cfec..2e42cdea21 100644 --- a/src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptyContacts.test.tsx +++ b/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.test.tsx @@ -2,13 +2,13 @@ import { render, screen } from '@testing-library/react'; import { EmptyContacts } from './EmptyContacts'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -describe('screens/AddressBook/components/EmptyState/EmptyContacts.tsx', () => { +describe('pages/AddressBook/components/EmptyState/EmptyContacts.tsx', () => { test('should render component', () => { const onAddContactSpy = jest.fn(); diff --git a/src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptyContacts.tsx b/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.tsx similarity index 80% rename from src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptyContacts.tsx rename to src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.tsx index 856896ca51..e18a78ecf4 100644 --- a/src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptyContacts.tsx +++ b/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.tsx @@ -1,6 +1,5 @@ -import { Icon } from '@renderer/components/ui'; -import { BodyText, Button } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Icon, BodyText, Button } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; type Props = { description?: string; diff --git a/src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptySearch.test.tsx b/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.test.tsx similarity index 74% rename from src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptySearch.test.tsx rename to src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.test.tsx index fa549e3d5e..a5fec042ea 100644 --- a/src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptySearch.test.tsx +++ b/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.test.tsx @@ -2,13 +2,13 @@ import { render, screen } from '@testing-library/react'; import { EmptySearch } from './EmptySearch'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -describe('screens/AddressBook/components/EmptyState/EmptySearch.tsx', () => { +describe('pages/AddressBook/components/EmptyState/EmptySearch.tsx', () => { test('should render component', () => { render(); diff --git a/src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptySearch.tsx b/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.tsx similarity index 69% rename from src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptySearch.tsx rename to src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.tsx index 5ad825f4f0..7fb0dbfb7f 100644 --- a/src/renderer/screens/AddressBook/Overview/components/EmptyState/EmptySearch.tsx +++ b/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.tsx @@ -1,6 +1,5 @@ -import { Icon } from '@renderer/components/ui'; -import { BodyText } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Icon, BodyText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; export const EmptySearch = () => { const { t } = useI18n(); diff --git a/src/renderer/screens/AddressBook/Overview/components/index.ts b/src/renderer/pages/AddressBook/Overview/components/index.ts similarity index 57% rename from src/renderer/screens/AddressBook/Overview/components/index.ts rename to src/renderer/pages/AddressBook/Overview/components/index.ts index e8b998fc49..a4592a762f 100644 --- a/src/renderer/screens/AddressBook/Overview/components/index.ts +++ b/src/renderer/pages/AddressBook/Overview/components/index.ts @@ -1,3 +1,3 @@ -export { ContactList } from './ContactList/ContactList'; +export { ContactList } from '@renderer/entities/contact/ui/ContactList/ContactList'; export { EmptyContacts } from './EmptyState/EmptyContacts'; export { EmptySearch } from './EmptyState/EmptySearch'; diff --git a/src/renderer/screens/AddressBook/index.ts b/src/renderer/pages/AddressBook/index.ts similarity index 100% rename from src/renderer/screens/AddressBook/index.ts rename to src/renderer/pages/AddressBook/index.ts diff --git a/src/renderer/screens/Assets/Assets.test.tsx b/src/renderer/pages/Assets/Assets.test.tsx similarity index 73% rename from src/renderer/screens/Assets/Assets.test.tsx rename to src/renderer/pages/Assets/Assets.test.tsx index 846543a50d..2bdfafe543 100644 --- a/src/renderer/screens/Assets/Assets.test.tsx +++ b/src/renderer/pages/Assets/Assets.test.tsx @@ -1,21 +1,29 @@ import { render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { ConnectionType } from '@renderer/domain/connection'; -import { useAccount } from '@renderer/services/account/accountService'; +import { useAccount } from '@renderer/entities/account'; import { Assets } from './Assets'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), + useNetworkContext: jest.fn(() => ({ + connections: { + '0x00': CHAINS[0], + '0x01': CHAINS[1], + }, + })), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getActiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), + AddressWithExplorers: ({ address }: { address: string }) => {address}, + isMultisig: () => true, })); const CHAINS = [ @@ -34,33 +42,19 @@ const CHAINS = [ }, ]; -jest.mock('@renderer/services/network/chainsService', () => ({ +jest.mock('@renderer/entities/network', () => ({ useChains: jest.fn().mockReturnValue({ sortChainsByBalance: () => CHAINS, }), })); -jest.mock('@renderer/services/balance/balanceService', () => ({ +jest.mock('@renderer/entities/asset', () => ({ useBalance: jest.fn().mockReturnValue({ getLiveBalances: jest.fn().mockReturnValue([]), }), })); -jest.mock('@renderer/context/NetworkContext', () => ({ - useNetworkContext: jest.fn(() => ({ - connections: { - '0x00': CHAINS[0], - '0x01': CHAINS[1], - }, - })), -})); - -jest.mock( - '@renderer/components/common/AddressWithExplorers/AddressWithExplorers', - jest.fn().mockReturnValue(({ address }: { address: string }) => {address}), -); - -jest.mock('@renderer/screens/Transfer/Transfer', () => TransferButton); +jest.mock('@renderer/pages/Transfer/Transfer', () => TransferButton); jest.mock('./components/NetworkAssets/NetworkAssets', () => ({ NetworkAssets: () => NetworkAssets, diff --git a/src/renderer/screens/Assets/Assets.tsx b/src/renderer/pages/Assets/Assets.tsx similarity index 88% rename from src/renderer/screens/Assets/Assets.tsx rename to src/renderer/pages/Assets/Assets.tsx index 3dfd80a5b9..9a9d394d10 100644 --- a/src/renderer/screens/Assets/Assets.tsx +++ b/src/renderer/pages/Assets/Assets.tsx @@ -1,22 +1,18 @@ import { useEffect, useState } from 'react'; -import { Icon } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { Asset } from '@renderer/domain/asset'; -import { Chain } from '@renderer/domain/chain'; +import { Icon, BodyText, Button, SmallTitleText } from '@renderer/shared/ui'; +import { useI18n, useNetworkContext } from '@renderer/app/providers'; +import { Asset, useBalance } from '@renderer/entities/asset'; +import { Chain } from '@renderer/entities/chain'; import { ConnectionType } from '@renderer/domain/connection'; import { SigningType } from '@renderer/domain/shared-kernel'; -import { useToggle } from '@renderer/shared/hooks'; -import { useChains } from '@renderer/services/network/chainsService'; -import { useSettingsStorage } from '@renderer/services/settings/settingsStorage'; -import { useAccount } from '@renderer/services/account/accountService'; -import { isMultisig, Account } from '@renderer/domain/account'; -import { BodyText, Button, SmallTitleText } from '@renderer/components/ui-redesign'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { useChains } from '@renderer/entities/network'; +import { useSettingsStorage } from '@renderer/entities/settings'; +import { useAccount, isMultisig, Account } from '@renderer/entities/account'; import { AssetsFilters, NetworkAssets, ReceiveModal, SelectShardModal } from './components'; import { Header } from '@renderer/components/common'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import Transfer from '@renderer/screens/Transfer/Transfer'; +import { Transfer } from '@renderer/pages'; export const Assets = () => { const { t } = useI18n(); diff --git a/src/renderer/screens/Assets/common/utils.ts b/src/renderer/pages/Assets/common/utils.ts similarity index 90% rename from src/renderer/screens/Assets/common/utils.ts rename to src/renderer/pages/Assets/common/utils.ts index 6985f384e4..36f2bba58e 100644 --- a/src/renderer/screens/Assets/common/utils.ts +++ b/src/renderer/pages/Assets/common/utils.ts @@ -1,9 +1,9 @@ import { BN } from '@polkadot/util'; import BigNumber from 'bignumber.js'; -import { Balance } from '@renderer/domain/balance'; -import { Decimal, totalAmount } from '@renderer/shared/utils/balance'; -import { Asset } from '@renderer/domain/asset'; +import { Balance } from '@renderer/entities/asset/model/balance'; +import { Decimal, totalAmount } from '@renderer/shared/lib/utils'; +import { Asset } from '@renderer/entities/asset/model/asset'; export const sumBalances = (firstBalance: Balance, secondBalance?: Balance): Balance => { if (!secondBalance) return firstBalance; diff --git a/src/renderer/screens/Assets/components/AssetsFilter/AssetsFilters.tsx b/src/renderer/pages/Assets/components/AssetsFilter/AssetsFilters.tsx similarity index 85% rename from src/renderer/screens/Assets/components/AssetsFilter/AssetsFilters.tsx rename to src/renderer/pages/Assets/components/AssetsFilter/AssetsFilters.tsx index 0234c5a845..3a4ba0791c 100644 --- a/src/renderer/screens/Assets/components/AssetsFilter/AssetsFilters.tsx +++ b/src/renderer/pages/Assets/components/AssetsFilter/AssetsFilters.tsx @@ -1,6 +1,5 @@ -import { Switch } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; -import { IconButton, MenuPopover, SearchInput } from '@renderer/components/ui-redesign'; +import { Switch, IconButton, MenuPopover, SearchInput } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; type Props = { searchQuery: string; diff --git a/src/renderer/screens/Assets/components/Modals/ReceiveModal/ReceiveModal.test.tsx b/src/renderer/pages/Assets/components/Modals/ReceiveModal/ReceiveModal.test.tsx similarity index 66% rename from src/renderer/screens/Assets/components/Modals/ReceiveModal/ReceiveModal.test.tsx rename to src/renderer/pages/Assets/components/Modals/ReceiveModal/ReceiveModal.test.tsx index 66db57c739..2a245725cb 100644 --- a/src/renderer/screens/Assets/components/Modals/ReceiveModal/ReceiveModal.test.tsx +++ b/src/renderer/pages/Assets/components/Modals/ReceiveModal/ReceiveModal.test.tsx @@ -1,32 +1,29 @@ import { render, screen, act } from '@testing-library/react'; -import { Asset } from '@renderer/domain/asset'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; -import chains from '@renderer/services/network/common/chains/chains.json'; -import { useAccount } from '@renderer/services/account/accountService'; +import { Asset } from '@renderer/entities/asset'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; +import chains from '@renderer/assets/chains/chains.json'; +import { useAccount } from '@renderer/entities/account'; import { SigningType } from '@renderer/domain/shared-kernel'; -import { Chain } from '@renderer/domain/chain'; +import { Chain } from '@renderer/entities/chain'; import { ReceiveModal } from './ReceiveModal'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getActiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID, signingType: SigningType.WATCH_ONLY }], }), + AccountAddress: ({ name, address }: { name?: string; address?: string }) => {name || address}, })); -jest.mock('@renderer/components/ui/Truncate/Truncate', () => () => ( - 5CGQ7BPJZZKNirQgVhzbX9wdkgbnUHtJ5V7FkMXdZeVbXyr9 -)); - const westendExplorers = chains.find((chain) => chain.name === 'Westend')?.explorers || []; -describe('screens/Assets/ReceiveModal', () => { +describe('pages/Assets/ReceiveModal', () => { afterEach(() => { jest.clearAllMocks(); }); @@ -42,17 +39,6 @@ describe('screens/Assets/ReceiveModal', () => { } as Chain, }); - test('should render component', async () => { - await act(async () => { - render(); - }); - - const title = screen.getByRole('dialog', { name: 'receive.title' }); - const address = screen.getByText('5CGQ7BPJZZKNirQgVhzbX9wdkgbnUHtJ5V7FkMXdZeVbXyr9'); - expect(title).toBeInTheDocument(); - expect(address).toBeInTheDocument(); - }); - test('should not render empty explorer links', async () => { await act(async () => { render(); diff --git a/src/renderer/screens/Assets/components/Modals/ReceiveModal/ReceiveModal.tsx b/src/renderer/pages/Assets/components/Modals/ReceiveModal/ReceiveModal.tsx similarity index 80% rename from src/renderer/screens/Assets/components/Modals/ReceiveModal/ReceiveModal.tsx rename to src/renderer/pages/Assets/components/Modals/ReceiveModal/ReceiveModal.tsx index f9fe4408bb..3b06d3afaa 100644 --- a/src/renderer/screens/Assets/components/Modals/ReceiveModal/ReceiveModal.tsx +++ b/src/renderer/pages/Assets/components/Modals/ReceiveModal/ReceiveModal.tsx @@ -1,22 +1,18 @@ import cn from 'classnames'; import { useEffect, useState } from 'react'; -import { AccountAddress, QrTextGenerator } from '@renderer/components/common'; +import { QrTextGenerator } from '@renderer/components/common'; import { DefaultExplorer, ExplorerIcons } from '@renderer/components/common/ExplorerLink/constants'; -import { Icon } from '@renderer/components/ui'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui/Dropdowns/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; -import { Chain } from '@renderer/domain/chain'; +import { Icon, BaseModal, Button, FootnoteText, Select, HelpText } from '@renderer/shared/ui'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/types'; +import { useI18n } from '@renderer/app/providers'; +import { Asset } from '@renderer/entities/asset'; +import { Chain } from '@renderer/entities/chain'; import { SigningType } from '@renderer/domain/shared-kernel'; -import { copyToClipboard } from '@renderer/shared/utils/strings'; -import { useAccount } from '@renderer/services/account/accountService'; -import { toAddress } from '@renderer/shared/utils/address'; -import { BaseModal, Button, FootnoteText, Select } from '@renderer/components/ui-redesign'; -import OperationModalTitle from '@renderer/screens/Operations/components/OperationModalTitle'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { IconButtonStyle } from '@renderer/components/ui-redesign/Buttons/IconButton/IconButton'; +import { copyToClipboard, toAddress, cnTw } from '@renderer/shared/lib/utils'; +import { useAccount, AccountAddress } from '@renderer/entities/account'; +import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; +import { IconButtonStyle } from '@renderer/shared/ui/Buttons/IconButton/IconButton'; type Props = { chain: Chain; diff --git a/src/renderer/screens/Assets/components/Modals/SelectShardModal/SelectShardModal.tsx b/src/renderer/pages/Assets/components/Modals/SelectShardModal/SelectShardModal.tsx similarity index 93% rename from src/renderer/screens/Assets/components/Modals/SelectShardModal/SelectShardModal.tsx rename to src/renderer/pages/Assets/components/Modals/SelectShardModal/SelectShardModal.tsx index 20c5dd8ff4..dcd75eceeb 100644 --- a/src/renderer/screens/Assets/components/Modals/SelectShardModal/SelectShardModal.tsx +++ b/src/renderer/pages/Assets/components/Modals/SelectShardModal/SelectShardModal.tsx @@ -2,11 +2,10 @@ import { useEffect, useState } from 'react'; import { keyBy } from 'lodash'; import { AccountId, ChainId } from '@renderer/domain/shared-kernel'; -import { BaseModal, Button, Checkbox, FootnoteText, SearchInput, Chain } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { AccountDS } from '@renderer/services/storage'; -import { useChains } from '@renderer/services/network/chainsService'; -import AddressWithExplorers from '@renderer/components/common/AddressWithExplorers/AddressWithExplorers'; +import { BaseModal, Button, Checkbox, FootnoteText, SearchInput } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { AccountDS } from '@renderer/shared/api/storage'; +import { useChains } from '@renderer/entities/network'; import { getMultishardStructure, getSelectableShards, @@ -17,6 +16,8 @@ import { SelectableAccount, SelectableShards, } from '@renderer/components/layout/PrimaryLayout/Wallets/common/types'; +import { AddressWithExplorers } from '@renderer/entities/account'; +import { ChainTitle } from '@renderer/entities/chain'; type Props = { accounts: AccountDS[]; @@ -173,7 +174,7 @@ export const SelectShardModal = ({ isOpen, onClose, activeAccounts, accounts }: semiChecked={chain.selectedAmount > 0 && chain.selectedAmount < chain.accounts.length} onChange={(event) => selectChain(event.target?.checked, chain.chainId, root.accountId)} > - + {chain.selectedAmount}/{chain.accounts.length} diff --git a/src/renderer/screens/Assets/components/NetworkAssets/NetworkAssets.test.tsx b/src/renderer/pages/Assets/components/NetworkAssets/NetworkAssets.test.tsx similarity index 87% rename from src/renderer/screens/Assets/components/NetworkAssets/NetworkAssets.test.tsx rename to src/renderer/pages/Assets/components/NetworkAssets/NetworkAssets.test.tsx index 2ba8dd2497..b11eebcdc9 100644 --- a/src/renderer/screens/Assets/components/NetworkAssets/NetworkAssets.test.tsx +++ b/src/renderer/pages/Assets/components/NetworkAssets/NetworkAssets.test.tsx @@ -1,17 +1,17 @@ import { act, render, screen } from '@testing-library/react'; -import { Chain } from '@renderer/domain/chain'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; -import chains from '@renderer/services/network/common/chains/chains.json'; +import { Chain } from '@renderer/entities/chain'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; +import chains from '@renderer/assets/chains/chains.json'; import { NetworkAssets } from './NetworkAssets'; -import { Account } from '@renderer/domain/account'; +import { Account } from '@renderer/entities/account'; import { ChainType, CryptoType, SigningType } from '@renderer/domain/shared-kernel'; const testChain = chains.find((chain) => chain.assets.length > 1) as Chain; const testAsset = testChain.assets[0]; const testAsset2 = testChain.assets[1]; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), @@ -35,13 +35,10 @@ const testBalances = [ }, ]; -jest.mock('@renderer/services/balance/balanceService', () => ({ +jest.mock('@renderer/entities/asset', () => ({ useBalance: jest.fn().mockReturnValue({ getLiveNetworkBalances: () => testBalances, }), -})); - -jest.mock('../AssetCard/AssetCard', () => ({ AssetCard: ({ asset }: any) => {asset.name}, })); diff --git a/src/renderer/screens/Assets/components/NetworkAssets/NetworkAssets.tsx b/src/renderer/pages/Assets/components/NetworkAssets/NetworkAssets.tsx similarity index 81% rename from src/renderer/screens/Assets/components/NetworkAssets/NetworkAssets.tsx rename to src/renderer/pages/Assets/components/NetworkAssets/NetworkAssets.tsx index 0f6a223c49..675476ec12 100644 --- a/src/renderer/screens/Assets/components/NetworkAssets/NetworkAssets.tsx +++ b/src/renderer/pages/Assets/components/NetworkAssets/NetworkAssets.tsx @@ -1,28 +1,21 @@ import { useEffect, useMemo, useState } from 'react'; import { groupBy } from 'lodash'; -import { Icon } from '@renderer/components/ui'; -import { Asset } from '@renderer/domain/asset'; -import { Chain as ChainType } from '@renderer/domain/chain'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { ZERO_BALANCE } from '@renderer/shared/utils/constants'; -import { totalAmount } from '@renderer/shared/utils/balance'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Balance } from '@renderer/domain/balance'; -import { includes } from '@renderer/shared/utils/strings'; -import { CaptionText, Chain, Tooltip, Accordion } from '@renderer/components/ui-redesign'; -import { AssetCard } from '../AssetCard/AssetCard'; +import { Icon, CaptionText, Tooltip, Accordion } from '@renderer/shared/ui'; +import { Asset, useBalance, Balance, AssetCard } from '@renderer/entities/asset'; +import { Chain, ChainTitle } from '@renderer/entities/chain'; +import { ZERO_BALANCE, totalAmount, includes, cnTw } from '@renderer/shared/lib/utils'; +import { ExtendedChain } from '@renderer/entities/network'; +import { useI18n } from '@renderer/app/providers'; import { balanceSorter, sumBalances } from '../../common/utils'; -import { Account } from '@renderer/domain/account'; +import { Account } from '@renderer/entities/account'; import { AccountId } from '@renderer/domain/shared-kernel'; -import cnTw from '@renderer/shared/utils/twMerge'; type Props = { hideZeroBalance?: boolean; searchSymbolOnly?: boolean; query?: string; - chain: ChainType | ExtendedChain; + chain: Chain | ExtendedChain; accounts: Account[]; canMakeActions?: boolean; onReceiveClick?: (asset: Asset) => void; @@ -110,7 +103,7 @@ export const NetworkAssets = ({ )} >
    - + {hasFailedVerification && (
    diff --git a/src/renderer/screens/Assets/components/index.ts b/src/renderer/pages/Assets/components/index.ts similarity index 100% rename from src/renderer/screens/Assets/components/index.ts rename to src/renderer/pages/Assets/components/index.ts diff --git a/src/renderer/screens/Notifications/Notifications.tsx b/src/renderer/pages/Notifications/Notifications.tsx similarity index 82% rename from src/renderer/screens/Notifications/Notifications.tsx rename to src/renderer/pages/Notifications/Notifications.tsx index 9a936a5bed..d93e80354a 100644 --- a/src/renderer/screens/Notifications/Notifications.tsx +++ b/src/renderer/pages/Notifications/Notifications.tsx @@ -1,15 +1,14 @@ import { groupBy } from 'lodash'; import { format } from 'date-fns'; -import { useI18n } from '@renderer/context/I18nContext'; +import { useI18n } from '@renderer/app/providers'; import EmptyNotifications from './components/EmptyNotifications'; -import NotificationRow from './components/NotificationRow'; import { sortByDate } from './common/utils'; -import { FootnoteText } from '@renderer/components/ui-redesign'; -import { useNotification } from '@renderer/services/notification/notificationService'; +import { FootnoteText } from '@renderer/shared/ui'; +import { useNotification, NotificationRow } from '@renderer/entities/notification'; import { Header } from '@renderer/components/common'; -const Notifications = () => { +export const Notifications = () => { const { t, dateLocale } = useI18n(); const { getLiveNotifications } = useNotification(); @@ -46,5 +45,3 @@ const Notifications = () => {
    ); }; - -export default Notifications; diff --git a/src/renderer/screens/Notifications/common/utils.ts b/src/renderer/pages/Notifications/common/utils.ts similarity index 100% rename from src/renderer/screens/Notifications/common/utils.ts rename to src/renderer/pages/Notifications/common/utils.ts diff --git a/src/renderer/screens/Notifications/components/EmptyNotifications.tsx b/src/renderer/pages/Notifications/components/EmptyNotifications.tsx similarity index 68% rename from src/renderer/screens/Notifications/components/EmptyNotifications.tsx rename to src/renderer/pages/Notifications/components/EmptyNotifications.tsx index 51a483721c..e19a1cb704 100644 --- a/src/renderer/screens/Notifications/components/EmptyNotifications.tsx +++ b/src/renderer/pages/Notifications/components/EmptyNotifications.tsx @@ -1,6 +1,5 @@ -import { Icon } from '@renderer/components/ui'; -import { BodyText } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Icon, BodyText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; const EmptyNotifications = () => { const { t } = useI18n(); diff --git a/src/renderer/screens/Onboarding/FinalStep/FinalStep.test.tsx b/src/renderer/pages/Onboarding/FinalStep/FinalStep.test.tsx similarity index 85% rename from src/renderer/screens/Onboarding/FinalStep/FinalStep.test.tsx rename to src/renderer/pages/Onboarding/FinalStep/FinalStep.test.tsx index 1c9f894926..11696006ff 100644 --- a/src/renderer/screens/Onboarding/FinalStep/FinalStep.test.tsx +++ b/src/renderer/pages/Onboarding/FinalStep/FinalStep.test.tsx @@ -7,14 +7,14 @@ jest.mock('react-router-dom', () => ({ useNavigate: jest.fn(), })); -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); // TODO: add more tests -describe('screens/Onboard/FinalStep', () => { +describe('pages/Onboard/FinalStep', () => { test('should render Watch Only component', () => { render(); diff --git a/src/renderer/screens/Onboarding/FinalStep/FinalStep.tsx b/src/renderer/pages/Onboarding/FinalStep/FinalStep.tsx similarity index 92% rename from src/renderer/screens/Onboarding/FinalStep/FinalStep.tsx rename to src/renderer/pages/Onboarding/FinalStep/FinalStep.tsx index a6214a1762..8052dd54d1 100644 --- a/src/renderer/screens/Onboarding/FinalStep/FinalStep.tsx +++ b/src/renderer/pages/Onboarding/FinalStep/FinalStep.tsx @@ -5,8 +5,7 @@ import LaptopImg from '@images/misc/onboarding/laptop.png'; // import CardImg from '@images/misc/onboarding/default-card.png'; import ParityImg from '@images/misc/onboarding/parity-card.png'; import WatchImg from '@images/misc/onboarding/watch-card.png'; -import Paths from '@renderer/routes/paths'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Paths, useI18n } from '@renderer/app/providers'; import { SigningType } from '@renderer/domain/shared-kernel'; type Props = { diff --git a/src/renderer/screens/Onboarding/Vault/KeyQrReader/KeyQrReader.test.tsx b/src/renderer/pages/Onboarding/Vault/KeyQrReader/KeyQrReader.test.tsx similarity index 91% rename from src/renderer/screens/Onboarding/Vault/KeyQrReader/KeyQrReader.test.tsx rename to src/renderer/pages/Onboarding/Vault/KeyQrReader/KeyQrReader.test.tsx index 488825383d..706b3518a0 100644 --- a/src/renderer/screens/Onboarding/Vault/KeyQrReader/KeyQrReader.test.tsx +++ b/src/renderer/pages/Onboarding/Vault/KeyQrReader/KeyQrReader.test.tsx @@ -2,19 +2,26 @@ import { act, render, screen, waitFor } from '@testing-library/react'; import { QrReader } from '@renderer/components/common'; import { QrError } from '@renderer/components/common/QrCode/common/types'; -import { Button, Dropdown } from '@renderer/components/ui'; import KeyQrReader from './KeyQrReader'; jest.mock('@renderer/components/common'); -jest.mock('@renderer/components/ui'); -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -describe('screens/Onboarding/Vault/KeyQrReader', () => { +jest.mock('@renderer/shared/ui', () => ({ + Select: ({ options }: any) => options.map((o: any) => {o.element}), + Button: ({ children }: any) => , + Loader: () => '', + Icon: () => '', + FootnoteText: () => '', + CaptionText: () => '', +})); + +describe('pages/Onboarding/Vault/KeyQrReader', () => { describe('success state', () => { afterEach(() => { jest.clearAllMocks(); @@ -41,9 +48,6 @@ describe('screens/Onboarding/Vault/KeyQrReader', () => { qrReader )); - (Dropdown as jest.Mock).mockImplementation(({ options }: any) => - options.map((o: any) => {o.element}), - ); render( {}} />); @@ -109,10 +113,6 @@ describe('screens/Onboarding/Vault/KeyQrReader', () => { act(() => qrButton.click()); }; - beforeAll(() => { - (Button as jest.Mock).mockImplementation(({ children }: any) => ); - }); - afterEach(() => { jest.clearAllMocks(); }); diff --git a/src/renderer/screens/Onboarding/Vault/KeyQrReader/KeyQrReader.tsx b/src/renderer/pages/Onboarding/Vault/KeyQrReader/KeyQrReader.tsx similarity index 95% rename from src/renderer/screens/Onboarding/Vault/KeyQrReader/KeyQrReader.tsx rename to src/renderer/pages/Onboarding/Vault/KeyQrReader/KeyQrReader.tsx index 21aff6df39..92828470b4 100644 --- a/src/renderer/screens/Onboarding/Vault/KeyQrReader/KeyQrReader.tsx +++ b/src/renderer/pages/Onboarding/Vault/KeyQrReader/KeyQrReader.tsx @@ -5,11 +5,10 @@ import { useState } from 'react'; import { QrReader } from '@renderer/components/common'; import { ErrorObject, QrError, SeedInfo, VideoInput } from '@renderer/components/common/QrCode/common/types'; -import { Icon, Loader } from '@renderer/components/ui'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui/Dropdowns/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Button, CaptionText, FootnoteText, Select } from '@renderer/components/ui-redesign'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { Icon, Loader, Button, CaptionText, FootnoteText, Select } from '@renderer/shared/ui'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { useI18n } from '@renderer/app/providers'; +import { cnTw } from '@renderer/shared/lib/utils'; const enum CameraState { ACTIVE, diff --git a/src/renderer/screens/Onboarding/Vault/ManageStep/ManageStep.tsx b/src/renderer/pages/Onboarding/Vault/ManageStep/ManageStep.tsx similarity index 93% rename from src/renderer/screens/Onboarding/Vault/ManageStep/ManageStep.tsx rename to src/renderer/pages/Onboarding/Vault/ManageStep/ManageStep.tsx index 3461782740..838a1a09a2 100644 --- a/src/renderer/screens/Onboarding/Vault/ManageStep/ManageStep.tsx +++ b/src/renderer/pages/Onboarding/Vault/ManageStep/ManageStep.tsx @@ -4,10 +4,11 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import { u8aToHex } from '@polkadot/util'; import { keyBy } from 'lodash'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Chain as ChainType, Explorer } from '@renderer/domain/chain'; +import { useChains } from '@renderer/entities/network'; +import { ID } from '@renderer/shared/api/storage'; +import { useI18n } from '@renderer/app/providers'; +import { Chain, Explorer, ChainTitle } from '@renderer/entities/chain'; import { Address, ChainId, ErrorType, HexString, SigningType, WalletType } from '@renderer/domain/shared-kernel'; -import { useChains } from '@renderer/services/network/chainsService'; import { Button, Input, @@ -16,18 +17,12 @@ import { SmallTitleText, IconButton, FootnoteText, - Chain, -} from '@renderer/components/ui-redesign'; + Icon, +} from '@renderer/shared/ui'; import { AddressInfo, CompactSeedInfo, SeedInfo } from '@renderer/components/common/QrCode/common/types'; -import { useWallet } from '@renderer/services/wallet/walletService'; -import { useAccount } from '@renderer/services/account/accountService'; -import { toAccountId, toAddress } from '@renderer/shared/utils/address'; -import { Account, createAccount } from '@renderer/domain/account'; -import { ID } from '@renderer/services/storage'; -import { createWallet } from '@renderer/domain/wallet'; -import AddressWithExplorers from '@renderer/components/common/AddressWithExplorers/AddressWithExplorers'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { Icon } from '@renderer/components/ui'; +import { useWallet, createWallet } from '@renderer/entities/wallet'; +import { useAccount, Account, createAccount, AddressWithExplorers } from '@renderer/entities/account'; +import { toAccountId, toAddress, cnTw } from '@renderer/shared/lib/utils'; const RootExplorers: Explorer[] = [ { name: 'Subscan', account: 'https://subscan.io/account/{address}' }, @@ -62,7 +57,7 @@ const ManageStep = ({ seedInfo, onBack, onComplete }: Props) => { const { addWallet } = useWallet(); const { addAccount, setActiveAccounts } = useAccount(); - const [chainsObject, setChainsObject] = useState>({}); + const [chainsObject, setChainsObject] = useState>({}); const [inactiveAccounts, setInactiveAccounts] = useState>({}); const [accountNames, setAccountNames] = useState>({}); @@ -82,7 +77,7 @@ const ManageStep = ({ seedInfo, onBack, onComplete }: Props) => { }); }, []); - const filterByExistingChains = (seedInfo: SeedInfo, chainsMap: Record): SeedInfo => { + const filterByExistingChains = (seedInfo: SeedInfo, chainsMap: Record): SeedInfo => { const derivedKeysForChsains = seedInfo.derivedKeys.filter((key) => Boolean(chainsMap[u8aToHex(key.genesisHash)])); return { ...seedInfo, derivedKeys: derivedKeysForChsains }; @@ -325,7 +320,7 @@ const ManageStep = ({ seedInfo, onBack, onComplete }: Props) => {
    - +
    {derivedKeys.map(({ address }, derivedKeyIndex) => (
    { const { t } = useI18n(); diff --git a/src/renderer/screens/Onboarding/WatchOnly/WatchOnly.tsx b/src/renderer/pages/Onboarding/WatchOnly/WatchOnly.tsx similarity index 88% rename from src/renderer/screens/Onboarding/WatchOnly/WatchOnly.tsx rename to src/renderer/pages/Onboarding/WatchOnly/WatchOnly.tsx index f1e8bf3ea4..a0c3befa7d 100644 --- a/src/renderer/screens/Onboarding/WatchOnly/WatchOnly.tsx +++ b/src/renderer/pages/Onboarding/WatchOnly/WatchOnly.tsx @@ -2,17 +2,23 @@ import cn from 'classnames'; import { useEffect, useState } from 'react'; import { Controller, SubmitHandler, useForm } from 'react-hook-form'; -import { Icon, Identicon } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Chain } from '@renderer/domain/chain'; +import { + Icon, + Identicon, + BaseModal, + Button, + Input, + InputHint, + HeaderTitleText, + SmallTitleText, +} from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { Chain } from '@renderer/entities/chain'; import { ErrorType, AccountId, SigningType } from '@renderer/domain/shared-kernel'; -import { useChains } from '@renderer/services/network/chainsService'; -import { toAccountId, validateAddress } from '@renderer/shared/utils/address'; -import { BaseModal, Button, Input, InputHint, HeaderTitleText, SmallTitleText } from '@renderer/components/ui-redesign'; +import { useChains } from '@renderer/entities/network'; +import { toAccountId, validateAddress } from '@renderer/shared/lib/utils'; import EmptyState from './EmptyState'; -import { createAccount } from '@renderer/domain/account'; -import { useAccount } from '@renderer/services/account/accountService'; -import AccountsList from '@renderer/components/common/AccountsList/AccountsList'; +import { createAccount, useAccount, AccountsList } from '@renderer/entities/account'; type WalletForm = { walletName: string; diff --git a/src/renderer/screens/Onboarding/Welcome/PrivacyPolicy.tsx b/src/renderer/pages/Onboarding/Welcome/PrivacyPolicy.tsx similarity index 82% rename from src/renderer/screens/Onboarding/Welcome/PrivacyPolicy.tsx rename to src/renderer/pages/Onboarding/Welcome/PrivacyPolicy.tsx index de78657030..08c1ad81a2 100644 --- a/src/renderer/screens/Onboarding/Welcome/PrivacyPolicy.tsx +++ b/src/renderer/pages/Onboarding/Welcome/PrivacyPolicy.tsx @@ -1,7 +1,7 @@ import { Trans } from 'react-i18next'; -import { useI18n } from '@renderer/context/I18nContext'; -import { FootnoteText, InfoLink } from '@renderer/components/ui-redesign'; +import { useI18n } from '@renderer/app/providers'; +import { FootnoteText, InfoLink } from '@renderer/shared/ui'; const TERMS_AND_CONDITIONS = 'https://novaspektr.io/terms'; const PRIVACY_POLICY = 'https://novaspektr.io/privacy'; diff --git a/src/renderer/screens/Onboarding/Welcome/Welcome.tsx b/src/renderer/pages/Onboarding/Welcome/Welcome.tsx similarity index 88% rename from src/renderer/screens/Onboarding/Welcome/Welcome.tsx rename to src/renderer/pages/Onboarding/Welcome/Welcome.tsx index 9876178f85..7ff4e2624a 100644 --- a/src/renderer/screens/Onboarding/Welcome/Welcome.tsx +++ b/src/renderer/pages/Onboarding/Welcome/Welcome.tsx @@ -2,22 +2,19 @@ import { useNavigate } from 'react-router-dom'; import { useLayoutEffect, useRef, useState } from 'react'; import { throttle } from 'lodash'; -import { Icon } from '@renderer/components/ui'; -import { TitleText } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useToggle } from '@renderer/shared/hooks'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { Icon, TitleText } from '@renderer/shared/ui'; +import { useI18n, Paths } from '@renderer/app/providers'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { cnTw, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; import WatchOnly from '../WatchOnly/WatchOnly'; -import Paths from '@renderer/routes/paths'; import Vault from '../Vault/Vault'; import PrivacyPolicy from './PrivacyPolicy'; import { WelcomeCard } from './WelcomeCard'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; const LOGO_WIDTH = 232; const RIGHT_PADDING = 225; -const Welcome = () => { +export const Welcome = () => { const { t } = useI18n(); const navigate = useNavigate(); @@ -118,5 +115,3 @@ const Welcome = () => {
    ); }; - -export default Welcome; diff --git a/src/renderer/screens/Onboarding/Welcome/WelcomeCard.tsx b/src/renderer/pages/Onboarding/Welcome/WelcomeCard.tsx similarity index 83% rename from src/renderer/screens/Onboarding/Welcome/WelcomeCard.tsx rename to src/renderer/pages/Onboarding/Welcome/WelcomeCard.tsx index 953fac8c26..8347a074a4 100644 --- a/src/renderer/screens/Onboarding/Welcome/WelcomeCard.tsx +++ b/src/renderer/pages/Onboarding/Welcome/WelcomeCard.tsx @@ -1,8 +1,7 @@ -import cnTw from '@renderer/shared/utils/twMerge'; -import { Icon } from '@renderer/components/ui'; -import { BodyText, CaptionText, FootnoteText } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { IconNames } from '@renderer/components/ui/Icon/data'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { Icon, BodyText, CaptionText, FootnoteText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { IconNames } from '@renderer/shared/ui/Icon/data'; type Props = { title: string; diff --git a/src/renderer/pages/Onboarding/index.ts b/src/renderer/pages/Onboarding/index.ts new file mode 100644 index 0000000000..0fa6229dd4 --- /dev/null +++ b/src/renderer/pages/Onboarding/index.ts @@ -0,0 +1 @@ +export { Welcome as Onboarding } from './Welcome/Welcome'; diff --git a/src/renderer/screens/Operations/Operations.test.tsx b/src/renderer/pages/Operations/Operations.test.tsx similarity index 58% rename from src/renderer/screens/Operations/Operations.test.tsx rename to src/renderer/pages/Operations/Operations.test.tsx index bb42e05d1f..288244b3bd 100644 --- a/src/renderer/screens/Operations/Operations.test.tsx +++ b/src/renderer/pages/Operations/Operations.test.tsx @@ -1,55 +1,44 @@ import { render, screen } from '@testing-library/react'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { ConnectionType } from '@renderer/domain/connection'; -import Operations from './Operations'; +import { Operations } from './Operations'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), + useNetworkContext: jest.fn(() => ({ + connections: { + '0x00': { + chainId: '1', + assets: [{ assetId: '1', symbol: '1' }], + connection: { + connectionType: ConnectionType.RPC_NODE, + }, + }, + }, + })), })); const mockTxs = [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }]; const mockAccounts = [{ name: 'Test Account', accountId: TEST_ACCOUNT_ID }]; -jest.mock('@renderer/services/multisigTx/multisigTxService', () => ({ +jest.mock('@renderer/entities/multisig', () => ({ useMultisigTx: jest.fn().mockReturnValue({ getLiveAccountMultisigTxs: () => mockTxs, }), -})); - -jest.mock('@renderer/services/multisigEvent/multisigEventService', () => ({ useMultisigEvent: jest.fn().mockReturnValue({ getLiveEventsByKeys: jest.fn().mockResolvedValue([]), }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getActiveMultisigAccount: () => mockAccounts, }), })); -jest.mock('@renderer/context/NetworkContext', () => ({ - useNetworkContext: jest.fn(() => ({ - connections: { - '0x0000000000000000000000000000000000000000': { - chainId: '1', - assets: [{ assetId: '1', symbol: '1' }], - connection: { - connectionType: ConnectionType.RPC_NODE, - }, - }, - }, - })), -})); - -// TODO add test for Operation component and move it there -// jest.mock('./components/Chain/Chain', () => () => 'Chain'); -// jest.mock('./components/TransactionAmount', () => () => 'TransactionAmount'); -// jest.mock('./components/TransactionTitle/TransactionTitle', () => () => 'TransactionTitle'); -// jest.mock('./components/EmptyState/EmptyOperations', () => () => 'EmptyState/EmptyOperations'); jest.mock('./components/Operation', () => () => 'Operation'); jest.mock('./components/Filters', () => () => 'Filters'); diff --git a/src/renderer/screens/Operations/Operations.tsx b/src/renderer/pages/Operations/Operations.tsx similarity index 82% rename from src/renderer/screens/Operations/Operations.tsx rename to src/renderer/pages/Operations/Operations.tsx index 431646d155..728a02d494 100644 --- a/src/renderer/screens/Operations/Operations.tsx +++ b/src/renderer/pages/Operations/Operations.tsx @@ -2,22 +2,19 @@ import { useEffect, useState } from 'react'; import { groupBy } from 'lodash'; import { format } from 'date-fns'; -import { useI18n } from '@renderer/context/I18nContext'; +import { useI18n, useNetworkContext } from '@renderer/app/providers'; import EmptyOperations from './components/EmptyState/EmptyOperations'; -import { useAccount } from '@renderer/services/account/accountService'; -import { MultisigAccount } from '@renderer/domain/account'; +import { useAccount, MultisigAccount } from '@renderer/entities/account'; import Operation from './components/Operation'; import { sortByDateDesc } from './common/utils'; -import { FootnoteText } from '@renderer/components/ui-redesign'; +import { FootnoteText } from '@renderer/shared/ui'; import Filters from './components/Filters'; -import { MultisigTransactionDS } from '@renderer/services/storage'; -import { useMultisigTx } from '@renderer/services/multisigTx/multisigTxService'; +import { MultisigTransactionDS } from '@renderer/shared/api/storage'; +import { useMultisigTx, useMultisigEvent } from '@renderer/entities/multisig'; import { Header } from '@renderer/components/common'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { MultisigEvent, MultisigTransactionKey } from '@renderer/domain/transaction'; -import { useMultisigEvent } from '@renderer/services/multisigEvent/multisigEventService'; +import { MultisigEvent, MultisigTransactionKey } from '@renderer/entities/transaction'; -const Operations = () => { +export const Operations = () => { const { t, dateLocale } = useI18n(); const { getActiveMultisigAccount } = useAccount(); const { getLiveAccountMultisigTxs } = useMultisigTx({}); @@ -91,5 +88,3 @@ const Operations = () => {
    ); }; - -export default Operations; diff --git a/src/renderer/screens/Operations/common/constants.ts b/src/renderer/pages/Operations/common/constants.ts similarity index 100% rename from src/renderer/screens/Operations/common/constants.ts rename to src/renderer/pages/Operations/common/constants.ts diff --git a/src/renderer/screens/Operations/common/utils.ts b/src/renderer/pages/Operations/common/utils.ts similarity index 94% rename from src/renderer/screens/Operations/common/utils.ts rename to src/renderer/pages/Operations/common/utils.ts index 6d42019976..13400485b8 100644 --- a/src/renderer/screens/Operations/common/utils.ts +++ b/src/renderer/pages/Operations/common/utils.ts @@ -1,7 +1,7 @@ import { TFunction } from 'react-i18next'; -import { IconNames } from '@renderer/components/ui/Icon/data'; -import { Explorer } from '@renderer/domain/chain'; +import { IconNames } from '@renderer/shared/ui/Icon/data'; +import { Explorer } from '@renderer/entities/chain/model/chain'; import { AccountId, HexString } from '@renderer/domain/shared-kernel'; import { DecodedTransaction, @@ -9,12 +9,11 @@ import { MultisigTxInitStatus, Transaction, TransactionType, -} from '@renderer/domain/transaction'; -import { toAddress } from '@renderer/shared/utils/address'; -import { Contact } from '@renderer/domain/contact'; -import { Account } from '@renderer/domain/account'; -import { Signatory } from '@renderer/domain/signatory'; -import { formatSectionAndMethod } from '@renderer/shared/utils/strings'; +} from '@renderer/entities/transaction/model/transaction'; +import { toAddress, formatSectionAndMethod } from '@renderer/shared/lib/utils'; +import { Contact } from '@renderer/entities/contact/model/contact'; +import { Account } from '@renderer/entities/account/model/account'; +import { Signatory } from '@renderer/entities/signatory/model/signatory'; export const UNKNOWN_TYPE = 'UNKNOWN_TYPE'; export const TRANSACTION_UNKNOWN = 'operations.titles.unknown'; diff --git a/src/renderer/screens/Operations/components/ActionSteps/Confirmation.tsx b/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx similarity index 71% rename from src/renderer/screens/Operations/components/ActionSteps/Confirmation.tsx rename to src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx index ad6f136f4a..e7a2e14e54 100644 --- a/src/renderer/screens/Operations/components/ActionSteps/Confirmation.tsx +++ b/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx @@ -1,10 +1,9 @@ -import { MultisigTransaction, Transaction } from '@renderer/domain/transaction'; -import TransactionAmount from '@renderer/screens/Operations/components/TransactionAmount'; -import { FootnoteText } from '@renderer/components/ui-redesign'; -import { Fee, DetailRow } from '@renderer/components/common'; -import { MultisigAccount } from '@renderer/domain/account'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; +import { MultisigTransaction, Transaction, Fee } from '@renderer/entities/transaction'; +import TransactionAmount from '@renderer/pages/Operations/components/TransactionAmount'; +import { DetailRow, FootnoteText } from '@renderer/shared/ui'; +import { MultisigAccount } from '@renderer/entities/account'; +import { ExtendedChain } from '@renderer/entities/network'; +import { useI18n } from '@renderer/app/providers'; import Details from '../Details'; const AmountFontStyle = 'font-manrope text-text-primary text-[32px] leading-[36px] font-bold'; diff --git a/src/renderer/screens/Operations/components/ActionSteps/Submit.tsx b/src/renderer/pages/Operations/components/ActionSteps/Submit.tsx similarity index 85% rename from src/renderer/screens/Operations/components/ActionSteps/Submit.tsx rename to src/renderer/pages/Operations/components/ActionSteps/Submit.tsx index e191afd416..622cbe849d 100644 --- a/src/renderer/screens/Operations/components/ActionSteps/Submit.tsx +++ b/src/renderer/pages/Operations/components/ActionSteps/Submit.tsx @@ -2,7 +2,7 @@ import { ApiPromise } from '@polkadot/api'; import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { useEffect, useState, ComponentProps } from 'react'; -import { useI18n } from '@renderer/context/I18nContext'; +import { useI18n, useMatrix, useMultisigChainContext } from '@renderer/app/providers'; import { MultisigEvent, MultisigTxFinalStatus, @@ -10,19 +10,16 @@ import { Transaction, TransactionType, MultisigTransaction, -} from '@renderer/domain/transaction'; + useTransaction, + ExtrinsicResultParams, + OperationResult, +} from '@renderer/entities/transaction'; import { HexString } from '@renderer/domain/shared-kernel'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { Account } from '@renderer/domain/account'; -import { ExtrinsicResultParams } from '@renderer/services/transaction/common/types'; -import { useMultisigTx } from '@renderer/services/multisigTx/multisigTxService'; -import { toAccountId } from '@renderer/shared/utils/address'; -import { useToggle } from '@renderer/shared/hooks'; -import { Button } from '@renderer/components/ui-redesign'; -import { OperationResult } from '@renderer/components/common/OperationResult/OperationResult'; -import { useMultisigEvent } from '@renderer/services/multisigEvent/multisigEventService'; -import { useMultisigChainContext } from '@renderer/context/MultisigChainContext'; +import { Account } from '@renderer/entities/account'; +import { useMultisigTx, useMultisigEvent } from '@renderer/entities/multisig'; +import { toAccountId } from '@renderer/shared/lib/utils'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { Button } from '@renderer/shared/ui'; type ResultProps = Pick, 'title' | 'description' | 'variant'>; diff --git a/src/renderer/screens/Operations/components/Details.tsx b/src/renderer/pages/Operations/components/Details.tsx similarity index 90% rename from src/renderer/screens/Operations/components/Details.tsx rename to src/renderer/pages/Operations/components/Details.tsx index 8c50bc45fa..0345b417d9 100644 --- a/src/renderer/screens/Operations/components/Details.tsx +++ b/src/renderer/pages/Operations/components/Details.tsx @@ -1,19 +1,16 @@ import cn from 'classnames'; -import { useI18n } from '@renderer/context/I18nContext'; -import { MultisigAccount } from '@renderer/domain/account'; -import { Icon } from '@renderer/components/ui'; -import { copyToClipboard, truncate } from '@renderer/shared/utils/strings'; -import { useToggle } from '@renderer/shared/hooks'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { MultisigTransaction, Transaction, TransactionType } from '@renderer/domain/transaction'; -import { Button, FootnoteText } from '@renderer/components/ui-redesign'; -import ValidatorsModal from '@renderer/screens/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal'; -import { BalanceNew, DetailRow } from '@renderer/components/common'; -import AddressWithExplorers from '@renderer/components/common/AddressWithExplorers/AddressWithExplorers'; +import { useI18n } from '@renderer/app/providers'; +import { MultisigAccount, AddressWithExplorers } from '@renderer/entities/account'; +import { Icon, Button, FootnoteText, DetailRow } from '@renderer/shared/ui'; +import { copyToClipboard, truncate, cnTw } from '@renderer/shared/lib/utils'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { ExtendedChain } from '@renderer/entities/network'; +import { MultisigTransaction, Transaction, TransactionType } from '@renderer/entities/transaction'; +import ValidatorsModal from '@renderer/pages/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal'; import { AddressStyle, DescriptionBlockStyle, InteractionStyle } from '../common/constants'; import { getMultisigExtrinsicLink } from '../common/utils'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { AssetBalance } from '@renderer/entities/asset'; type Props = { tx: MultisigTransaction; @@ -199,7 +196,7 @@ const Details = ({ tx, account, connection, isCardDetails = true }: Props) => { {deposit && defaultAsset && ( - ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -describe('screens/Operations/components/EmptyState/EmptyOperations.tsx', () => { +describe('pages/Operations/components/EmptyState/EmptyOperations.tsx', () => { test('should render component', () => { render(); diff --git a/src/renderer/screens/Operations/components/EmptyState/EmptyOperations.tsx b/src/renderer/pages/Operations/components/EmptyState/EmptyOperations.tsx similarity index 77% rename from src/renderer/screens/Operations/components/EmptyState/EmptyOperations.tsx rename to src/renderer/pages/Operations/components/EmptyState/EmptyOperations.tsx index e177238248..29357c72d3 100644 --- a/src/renderer/screens/Operations/components/EmptyState/EmptyOperations.tsx +++ b/src/renderer/pages/Operations/components/EmptyState/EmptyOperations.tsx @@ -1,7 +1,6 @@ -import { Icon } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; -import { BodyText } from '@renderer/components/ui-redesign'; -import { Account } from '@renderer/domain/account'; +import { Icon, BodyText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { Account } from '@renderer/entities/account'; type Props = { multisigAccount: Account | null; diff --git a/src/renderer/screens/Operations/components/Filters.tsx b/src/renderer/pages/Operations/components/Filters.tsx similarity index 92% rename from src/renderer/screens/Operations/components/Filters.tsx rename to src/renderer/pages/Operations/components/Filters.tsx index 129316bf8b..733d83d366 100644 --- a/src/renderer/screens/Operations/components/Filters.tsx +++ b/src/renderer/pages/Operations/components/Filters.tsx @@ -1,12 +1,11 @@ import { useEffect, useState } from 'react'; -import { useI18n } from '@renderer/context/I18nContext'; -import { MultisigTransactionDS } from '@renderer/services/storage'; +import { useI18n, useNetworkContext } from '@renderer/app/providers'; +import { MultisigTransactionDS } from '@renderer/shared/api/storage'; import { UNKNOWN_TYPE, getStatusOptions, getTransactionOptions, TransferTypes } from '../common/utils'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui-redesign/Dropdowns/common/types'; -import { MultisigTransaction, Transaction, TransactionType } from '@renderer/domain/transaction'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { Button, MultiSelect } from '@renderer/components/ui-redesign'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/types'; +import { MultisigTransaction, Transaction, TransactionType } from '@renderer/entities/transaction'; +import { Button, MultiSelect } from '@renderer/shared/ui'; type FilterName = 'status' | 'network' | 'type'; diff --git a/src/renderer/screens/Operations/components/Log.tsx b/src/renderer/pages/Operations/components/Log.tsx similarity index 85% rename from src/renderer/screens/Operations/components/Log.tsx rename to src/renderer/pages/Operations/components/Log.tsx index 391c3a5ced..e9e3dbc8c3 100644 --- a/src/renderer/screens/Operations/components/Log.tsx +++ b/src/renderer/pages/Operations/components/Log.tsx @@ -1,22 +1,20 @@ import { groupBy } from 'lodash'; import { format } from 'date-fns'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Account, MultisigAccount } from '@renderer/domain/account'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { MultisigEvent, SigningStatus } from '@renderer/domain/transaction'; +import { useI18n } from '@renderer/app/providers'; +import { Account, MultisigAccount } from '@renderer/entities/account'; +import { ExtendedChain } from '@renderer/entities/network'; +import { MultisigEvent, SigningStatus } from '@renderer/entities/transaction'; import TransactionTitle from './TransactionTitle/TransactionTitle'; import OperationStatus from './OperationStatus'; import { getSignatoryName, getTransactionAmount, sortByDateAsc } from '../common/utils'; -import { AssetIcon, BaseModal, BodyText, FootnoteText } from '@renderer/components/ui-redesign'; -import { getAssetById } from '@renderer/shared/utils/assets'; -import { Identicon } from '@renderer/components/ui'; -import { toAddress } from '@renderer/shared/utils/address'; -import { SS58_DEFAULT_PREFIX } from '@renderer/shared/utils/constants'; +import { BaseModal, BodyText, FootnoteText, Identicon } from '@renderer/shared/ui'; +import { getAssetById, toAddress, SS58_DEFAULT_PREFIX } from '@renderer/shared/lib/utils'; import { ExtrinsicExplorers } from '@renderer/components/common'; -import { Contact } from '@renderer/domain/contact'; -import { useMultisigEvent } from '@renderer/services/multisigEvent/multisigEventService'; -import { MultisigTransactionDS } from '@renderer/services/storage'; +import { Contact } from '@renderer/entities/contact'; +import { useMultisigEvent } from '@renderer/entities/multisig'; +import { MultisigTransactionDS } from '@renderer/shared/api/storage'; +import { AssetIcon } from '@renderer/entities/asset'; type Props = { tx: MultisigTransactionDS; diff --git a/src/renderer/screens/Operations/components/Operation.tsx b/src/renderer/pages/Operations/components/Operation.tsx similarity index 78% rename from src/renderer/screens/Operations/components/Operation.tsx rename to src/renderer/pages/Operations/components/Operation.tsx index 2ee951cbbc..9342266e0c 100644 --- a/src/renderer/screens/Operations/components/Operation.tsx +++ b/src/renderer/pages/Operations/components/Operation.tsx @@ -1,15 +1,16 @@ import { format } from 'date-fns'; -import { useI18n } from '@renderer/context/I18nContext'; +import { useI18n } from '@renderer/app/providers'; import TransactionTitle from './TransactionTitle/TransactionTitle'; -import { MultisigAccount } from '@renderer/domain/account'; -import { FootnoteText, Chain, Accordion } from '@renderer/components/ui-redesign'; +import { MultisigAccount } from '@renderer/entities/account'; +import { FootnoteText, Accordion } from '@renderer/shared/ui'; import TransactionAmount from './TransactionAmount'; import OperationStatus from './OperationStatus'; import OperationFullInfo from './OperationFullInfo'; -import { getTransactionAmount } from '@renderer/screens/Operations/common/utils'; -import { MultisigTransactionDS } from '@renderer/services/storage'; -import { useMultisigEvent } from '@renderer/services/multisigEvent/multisigEventService'; +import { getTransactionAmount } from '@renderer/pages/Operations/common/utils'; +import { MultisigTransactionDS } from '@renderer/shared/api/storage'; +import { useMultisigEvent } from '@renderer/entities/multisig'; +import { ChainTitle } from '@renderer/entities/chain'; type Props = { tx: MultisigTransactionDS; @@ -38,7 +39,7 @@ const Operation = ({ tx, account }: Props) => { ) : ( )} - +
    diff --git a/src/renderer/screens/Operations/components/OperationFullInfo.tsx b/src/renderer/pages/Operations/components/OperationFullInfo.tsx similarity index 79% rename from src/renderer/screens/Operations/components/OperationFullInfo.tsx rename to src/renderer/pages/Operations/components/OperationFullInfo.tsx index b49a18bb63..5cc9c8e2a2 100644 --- a/src/renderer/screens/Operations/components/OperationFullInfo.tsx +++ b/src/renderer/pages/Operations/components/OperationFullInfo.tsx @@ -1,29 +1,22 @@ import { useEffect, useState } from 'react'; -import { MultisigEvent, SigningStatus } from '@renderer/domain/transaction'; -import { MultisigAccount } from '@renderer/domain/account'; -import { Icon } from '@renderer/components/ui'; -import Details from '@renderer/screens/Operations/components/Details'; -import RejectTx from '@renderer/screens/Operations/components/modals/RejectTx'; -import ApproveTx from '@renderer/screens/Operations/components/modals/ApproveTx'; -import { getMultisigExtrinsicLink, getSignatoryName } from '@renderer/screens/Operations/common/utils'; -import { Signatory } from '@renderer/domain/signatory'; -import CallDataModal from '@renderer/screens/Operations/components/modals/CallDataModal'; +import { MultisigEvent, SigningStatus } from '@renderer/entities/transaction'; +import { MultisigAccount, useAccount } from '@renderer/entities/account'; +import { Icon, Button, CaptionText, InfoLink, SmallTitleText } from '@renderer/shared/ui'; +import Details from '@renderer/pages/Operations/components/Details'; +import RejectTx from '@renderer/pages/Operations/components/modals/RejectTx'; +import ApproveTx from '@renderer/pages/Operations/components/modals/ApproveTx'; +import { getMultisigExtrinsicLink, getSignatoryName } from '@renderer/pages/Operations/common/utils'; +import { Signatory, SignatoryCard } from '@renderer/entities/signatory'; +import CallDataModal from '@renderer/pages/Operations/components/modals/CallDataModal'; import { AccountId, CallData, ChainId } from '@renderer/domain/shared-kernel'; -import { nonNullable } from '@renderer/shared/utils/functions'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { useMultisigTx } from '@renderer/services/multisigTx/multisigTxService'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { useToggle } from '@renderer/shared/hooks'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Button, CaptionText, InfoLink, SmallTitleText } from '@renderer/components/ui-redesign'; -import SignatoryCard from '@renderer/components/common/SignatoryCard/SignatoryCard'; +import { nonNullable } from '@renderer/shared/lib/utils'; +import { useMatrix, useNetworkContext, useI18n, useMultisigChainContext } from '@renderer/app/providers'; +import { useMultisigTx, useMultisigEvent } from '@renderer/entities/multisig'; +import { useToggle } from '@renderer/shared/lib/hooks'; import LogModal from './Log'; -import { useContact } from '@renderer/services/contact/contactService'; -import { useAccount } from '@renderer/services/account/accountService'; -import { MultisigTransactionDS } from '@renderer/services/storage'; -import { useMultisigEvent } from '@renderer/services/multisigEvent/multisigEventService'; -import { useMultisigChainContext } from '@renderer/context/MultisigChainContext'; +import { useContact } from '@renderer/entities/contact'; +import { MultisigTransactionDS } from '@renderer/shared/api/storage'; type Props = { tx: MultisigTransactionDS; diff --git a/src/renderer/screens/Operations/components/OperationModalTitle.tsx b/src/renderer/pages/Operations/components/OperationModalTitle.tsx similarity index 69% rename from src/renderer/screens/Operations/components/OperationModalTitle.tsx rename to src/renderer/pages/Operations/components/OperationModalTitle.tsx index 05d6f23c6f..f2d0be7840 100644 --- a/src/renderer/screens/Operations/components/OperationModalTitle.tsx +++ b/src/renderer/pages/Operations/components/OperationModalTitle.tsx @@ -1,5 +1,5 @@ -import { Chain } from '@renderer/components/ui-redesign'; import { ChainId } from '@renderer/domain/shared-kernel'; +import { ChainTitle } from '@renderer/entities/chain'; type Props = { title: string; @@ -11,7 +11,7 @@ const ChainFontStyle = 'font-manrope text-header-title text-text-primary truncat const OperationModalTitle = ({ title, chainId }: Props) => (
    {title} - +
    ); diff --git a/src/renderer/screens/Operations/components/OperationStatus.tsx b/src/renderer/pages/Operations/components/OperationStatus.tsx similarity index 90% rename from src/renderer/screens/Operations/components/OperationStatus.tsx rename to src/renderer/pages/Operations/components/OperationStatus.tsx index 6bd9127b5c..496b6b09c0 100644 --- a/src/renderer/screens/Operations/components/OperationStatus.tsx +++ b/src/renderer/pages/Operations/components/OperationStatus.tsx @@ -5,9 +5,9 @@ import { MultisigTxFinalStatus, MultisigTxInitStatus, MultisigTxStatus, -} from '@renderer/domain/transaction'; -import { CaptionText } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; +} from '@renderer/entities/transaction'; +import { CaptionText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; const StatusTitle: Record = { [MultisigTxInitStatus.SIGNING]: 'operation.status.signing', diff --git a/src/renderer/screens/Operations/components/ShortTransactionInfo.test.tsx b/src/renderer/pages/Operations/components/ShortTransactionInfo.test.tsx similarity index 81% rename from src/renderer/screens/Operations/components/ShortTransactionInfo.test.tsx rename to src/renderer/pages/Operations/components/ShortTransactionInfo.test.tsx index f20bfe9f09..08f0249a65 100644 --- a/src/renderer/screens/Operations/components/ShortTransactionInfo.test.tsx +++ b/src/renderer/pages/Operations/components/ShortTransactionInfo.test.tsx @@ -1,10 +1,10 @@ import { act, render, screen } from '@testing-library/react'; import TransactionAmount from './TransactionAmount'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { TEST_ADDRESS, TEST_CHAIN_ID } from '@renderer/shared/utils/constants'; +import { Transaction, TransactionType } from '@renderer/entities/transaction'; +import { TEST_ADDRESS, TEST_CHAIN_ID } from '@renderer/shared/lib/utils'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/pages/Operations/components/TransactionAmount.tsx b/src/renderer/pages/Operations/components/TransactionAmount.tsx new file mode 100644 index 0000000000..aea4ea1a0c --- /dev/null +++ b/src/renderer/pages/Operations/components/TransactionAmount.tsx @@ -0,0 +1,31 @@ +import { useEffect, useState, ComponentProps } from 'react'; + +import { DecodedTransaction, Transaction } from '@renderer/entities/transaction'; +import { useChains } from '@renderer/entities/network'; +import { Asset, AssetBalance } from '@renderer/entities/asset'; +import { getAssetById } from '@renderer/shared/lib/utils'; +import { getTransactionAmount } from '@renderer/pages/Operations/common/utils'; + +type Props = { + tx: Transaction | DecodedTransaction; +}; + +type BalanceProps = Pick, 'className' | 'showIcon' | 'wrapperClassName'>; + +const TransactionAmount = ({ tx, ...balanceProps }: Props & BalanceProps) => { + const { getChainById } = useChains(); + const [assets, setAssets] = useState([]); + + useEffect(() => { + getChainById(tx.chainId).then((chain) => setAssets(chain?.assets || [])); + }, []); + + const asset = getAssetById(tx.args.assetId, assets); + const value = getTransactionAmount(tx); + + if (!asset || !value) return null; + + return ; +}; + +export default TransactionAmount; diff --git a/src/renderer/screens/Operations/components/TransactionTitle/TransactionTitle.test.tsx b/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.test.tsx similarity index 77% rename from src/renderer/screens/Operations/components/TransactionTitle/TransactionTitle.test.tsx rename to src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.test.tsx index ed33bf9fd0..5333dd15a4 100644 --- a/src/renderer/screens/Operations/components/TransactionTitle/TransactionTitle.test.tsx +++ b/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.test.tsx @@ -1,10 +1,10 @@ import { act, render, screen } from '@testing-library/react'; import TransactionTitle from './TransactionTitle'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { TEST_ADDRESS, TEST_CHAIN_ID } from '@renderer/shared/utils/constants'; +import { Transaction, TransactionType } from '@renderer/entities/transaction'; +import { TEST_ADDRESS, TEST_CHAIN_ID } from '@renderer/shared/lib/utils'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/screens/Operations/components/TransactionTitle/TransactionTitle.tsx b/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.tsx similarity index 77% rename from src/renderer/screens/Operations/components/TransactionTitle/TransactionTitle.tsx rename to src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.tsx index d805191448..d8a494ab13 100644 --- a/src/renderer/screens/Operations/components/TransactionTitle/TransactionTitle.tsx +++ b/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.tsx @@ -1,9 +1,8 @@ -import { Icon } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; -import { DecodedTransaction, Transaction } from '@renderer/domain/transaction'; +import { Icon, BodyText, FootnoteText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { DecodedTransaction, Transaction } from '@renderer/entities/transaction'; import { getIconName, getTransactionTitle } from '../../common/utils'; -import { BodyText, FootnoteText } from '@renderer/components/ui-redesign'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { tx?: Transaction | DecodedTransaction; diff --git a/src/renderer/screens/Operations/components/modals/ApproveTx.tsx b/src/renderer/pages/Operations/components/modals/ApproveTx.tsx similarity index 84% rename from src/renderer/screens/Operations/components/modals/ApproveTx.tsx rename to src/renderer/pages/Operations/components/modals/ApproveTx.tsx index 8d36bbfaed..0d26cad0b9 100644 --- a/src/renderer/screens/Operations/components/modals/ApproveTx.tsx +++ b/src/renderer/pages/Operations/components/modals/ApproveTx.tsx @@ -3,31 +3,30 @@ import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { Weight } from '@polkadot/types/interfaces'; import { BN } from '@polkadot/util'; -import { Icon } from '@renderer/components/ui'; -import { BaseModal, Button } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { AccountDS, MultisigTransactionDS } from '@renderer/services/storage'; -import { useCountdown, useToggle } from '@renderer/shared/hooks'; -import { Account, MultisigAccount } from '@renderer/domain/account'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; +import { Icon, BaseModal, Button } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { AccountDS, MultisigTransactionDS } from '@renderer/shared/api/storage'; +import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { Account, MultisigAccount, useAccount } from '@renderer/entities/account'; +import { ExtendedChain } from '@renderer/entities/network'; +import { + Transaction, + TransactionType, + useTransaction, + OperationResult, + useCallDataDecoder, +} from '@renderer/entities/transaction'; import { Address, HexString, SigningType, Timepoint } from '@renderer/domain/shared-kernel'; -import { toAddress } from '@renderer/shared/utils/address'; -import { useAccount } from '@renderer/services/account/accountService'; +import { toAddress, transferableAmount, TEST_ADDRESS } from '@renderer/shared/lib/utils'; import { getTransactionTitle } from '../../common/utils'; import { Submit } from '../ActionSteps/Submit'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { transferableAmount } from '@renderer/shared/utils/balance'; -import { TEST_ADDRESS } from '@renderer/shared/utils/constants'; -import Confirmation from '@renderer/screens/Operations/components/ActionSteps/Confirmation'; -import SignatorySelectModal from '@renderer/screens/Operations/components/modals/SignatorySelectModal'; -import { OperationResult } from '@renderer/components/common/OperationResult/OperationResult'; -import OperationModalTitle from '@renderer/screens/Operations/components/OperationModalTitle'; -import { Signing } from '@renderer/screens/Transfer/components/ActionSteps'; +import { useBalance } from '@renderer/entities/asset'; +import Confirmation from '@renderer/pages/Operations/components/ActionSteps/Confirmation'; +import SignatorySelectModal from '@renderer/pages/Operations/components/modals/SignatorySelectModal'; +import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; +import { Signing } from '@renderer/pages/Transfer/components/ActionSteps'; import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; -import { useMultisigEvent } from '@renderer/services/multisigEvent/multisigEventService'; -import { useCallDataDecoder } from '@renderer/services/transaction/callDataDecoder'; +import { useMultisigEvent } from '@renderer/entities/multisig'; type Props = { tx: MultisigTransactionDS; diff --git a/src/renderer/screens/Operations/components/modals/CallDataModal.tsx b/src/renderer/pages/Operations/components/modals/CallDataModal.tsx similarity index 85% rename from src/renderer/screens/Operations/components/modals/CallDataModal.tsx rename to src/renderer/pages/Operations/components/modals/CallDataModal.tsx index d2777d6534..b643d17327 100644 --- a/src/renderer/screens/Operations/components/modals/CallDataModal.tsx +++ b/src/renderer/pages/Operations/components/modals/CallDataModal.tsx @@ -1,11 +1,10 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form'; -import { BaseModal, Button, InputHint } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { MultisigTransactionDS } from '@renderer/services/storage'; +import { BaseModal, Button, InputHint, InputArea } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { MultisigTransactionDS } from '@renderer/shared/api/storage'; import { CallData } from '@renderer/domain/shared-kernel'; -import { validateCallData } from '@renderer/shared/utils/substrate'; -import { InputArea } from '@renderer/components/ui'; +import { validateCallData } from '@renderer/shared/lib/utils'; type CallDataForm = { callData: string; @@ -62,7 +61,6 @@ const CallDataModal = ({ isOpen, tx, onClose, onSubmit }: Props) => { rules={{ required: true, validate: validateCallDataValue }} render={({ field: { value, onChange }, fieldState: { error } }) => ( <> - {/* TODO: use InputArea from ui-redesign */} { rules={{ required: true }} render={({ field: { value, onChange }, fieldState: { error } }) => ( <> - {/* TODO: use InputArea from ui-redesign */} ({ - useConfirmContext: jest.fn(() => ({ - confirm: confirmSpy, - })), -})); -jest.mock('@renderer/context/I18nContext', () => ({ - useI18n: jest.fn().mockReturnValue({ - t: (key: string) => key, - }), -})); - -jest.mock('@renderer/context/NetworkContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useNetworkContext: jest.fn(() => ({ connections: { '0x111': { @@ -46,15 +35,21 @@ jest.mock('@renderer/context/NetworkContext', () => ({ removeRpcNode: jest.fn(), getParachains: jest.fn().mockReturnValue([]), })), + useI18n: jest.fn().mockReturnValue({ + t: (key: string) => key, + }), + useConfirmContext: jest.fn(() => ({ + confirm: confirmSpy, + })), })); -jest.mock('@renderer/services/balance/balanceService', () => ({ +jest.mock('@renderer/entities/asset', () => ({ useBalance: jest.fn().mockReturnValue({ setBalanceIsValid: jest.fn(), }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getAccounts: jest.fn().mockReturnValue([]), }), diff --git a/src/renderer/screens/Settings/Networks/Networks.tsx b/src/renderer/pages/Settings/Networks/Networks.tsx similarity index 91% rename from src/renderer/screens/Settings/Networks/Networks.tsx rename to src/renderer/pages/Settings/Networks/Networks.tsx index f44ad21a75..463ee8400d 100644 --- a/src/renderer/screens/Settings/Networks/Networks.tsx +++ b/src/renderer/pages/Settings/Networks/Networks.tsx @@ -3,23 +3,17 @@ import { useNavigate } from 'react-router-dom'; import { Trans } from 'react-i18next'; import uniqBy from 'lodash/uniqBy'; -import { useI18n } from '@renderer/context/I18nContext'; -import { BaseModal, SearchInput, BodyText, InfoLink } from '@renderer/components/ui-redesign'; -import { useToggle } from '@renderer/shared/hooks'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { includes } from '@renderer/shared/utils/strings'; +import { useI18n, useNetworkContext, useConfirmContext, Paths } from '@renderer/app/providers'; +import { BaseModal, SearchInput, BodyText, InfoLink, Icon } from '@renderer/shared/ui'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { ExtendedChain, useChains } from '@renderer/entities/network'; +import { includes, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; import { ConnectionType, ConnectionStatus } from '@renderer/domain/connection'; -import { useChains } from '@renderer/services/network/chainsService'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; -import { Icon } from '@renderer/components/ui'; -import { RpcNode } from '@renderer/domain/chain'; -import { useConfirmContext } from '@renderer/context/ConfirmContext'; +import { RpcNode } from '@renderer/entities/chain'; import { ChainId } from '@renderer/domain/shared-kernel'; import { NetworkList, NetworkItem, CustomRpcModal } from './components'; -import Paths from '@renderer/routes/paths'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { useAccount } from '@renderer/services/account/accountService'; +import { useBalance } from '@renderer/entities/asset'; +import { useAccount } from '@renderer/entities/account'; const MAX_LIGHT_CLIENTS = 3; diff --git a/src/renderer/screens/Settings/Networks/components/CustomRpcModal/CustomRpcModal.test.tsx b/src/renderer/pages/Settings/Networks/components/CustomRpcModal/CustomRpcModal.test.tsx similarity index 95% rename from src/renderer/screens/Settings/Networks/components/CustomRpcModal/CustomRpcModal.test.tsx rename to src/renderer/pages/Settings/Networks/components/CustomRpcModal/CustomRpcModal.test.tsx index d4fb145caa..dd3fd77eec 100644 --- a/src/renderer/screens/Settings/Networks/components/CustomRpcModal/CustomRpcModal.test.tsx +++ b/src/renderer/pages/Settings/Networks/components/CustomRpcModal/CustomRpcModal.test.tsx @@ -1,17 +1,14 @@ import { act, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { RpcValidation, ExtendedChain } from '@renderer/services/network/common/types'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; +import { RpcValidation, ExtendedChain } from '@renderer/entities/network'; +import { useNetworkContext } from '@renderer/app/providers'; import { CustomRpcModal } from './CustomRpcModal'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), -})); - -jest.mock('@renderer/context/NetworkContext', () => ({ useNetworkContext: jest.fn(() => ({ validateRpcNode: jest.fn(), addRpcNode: jest.fn(), diff --git a/src/renderer/screens/Settings/Networks/components/CustomRpcModal/CustomRpcModal.tsx b/src/renderer/pages/Settings/Networks/components/CustomRpcModal/CustomRpcModal.tsx similarity index 93% rename from src/renderer/screens/Settings/Networks/components/CustomRpcModal/CustomRpcModal.tsx rename to src/renderer/pages/Settings/Networks/components/CustomRpcModal/CustomRpcModal.tsx index e5baaa4690..1a1dfbf0b5 100644 --- a/src/renderer/screens/Settings/Networks/components/CustomRpcModal/CustomRpcModal.tsx +++ b/src/renderer/pages/Settings/Networks/components/CustomRpcModal/CustomRpcModal.tsx @@ -1,13 +1,12 @@ import { useEffect, useState } from 'react'; import { Controller, SubmitHandler, useForm } from 'react-hook-form'; -import { BaseModal, Button, Input, InputHint, Alert } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { RpcNode } from '@renderer/domain/chain'; -import { RpcValidation, ExtendedChain } from '@renderer/services/network/common/types'; -import { validateWsAddress } from '@renderer/shared/utils/strings'; -import OperationModalTitle from '@renderer/screens/Operations/components/OperationModalTitle'; +import { BaseModal, Button, Input, InputHint, Alert } from '@renderer/shared/ui'; +import { useI18n, useNetworkContext } from '@renderer/app/providers'; +import { RpcNode } from '@renderer/entities/chain'; +import { RpcValidation, ExtendedChain } from '@renderer/entities/network'; +import { validateWsAddress } from '@renderer/shared/lib/utils'; +import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; const MODAL_ANIMATION = 300; diff --git a/src/renderer/screens/Settings/Networks/components/NetworkItem/NetworkItem.css b/src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.css similarity index 100% rename from src/renderer/screens/Settings/Networks/components/NetworkItem/NetworkItem.css rename to src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.css diff --git a/src/renderer/screens/Settings/Networks/components/NetworkItem/NetworkItem.test.tsx b/src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.test.tsx similarity index 92% rename from src/renderer/screens/Settings/Networks/components/NetworkItem/NetworkItem.test.tsx rename to src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.test.tsx index a20e752800..bdc0b5c57b 100644 --- a/src/renderer/screens/Settings/Networks/components/NetworkItem/NetworkItem.test.tsx +++ b/src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.test.tsx @@ -2,10 +2,10 @@ import { render, screen } from '@testing-library/react'; import noop from 'lodash/noop'; import { ConnectionStatus, ConnectionType } from '@renderer/domain/connection'; -import { ExtendedChain } from '@renderer/services/network/common/types'; +import { ExtendedChain } from '@renderer/entities/network'; import { NetworkItem } from './NetworkItem'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/screens/Settings/Networks/components/NetworkItem/NetworkItem.tsx b/src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.tsx similarity index 86% rename from src/renderer/screens/Settings/Networks/components/NetworkItem/NetworkItem.tsx rename to src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.tsx index a9e03a466b..1de920baf7 100644 --- a/src/renderer/screens/Settings/Networks/components/NetworkItem/NetworkItem.tsx +++ b/src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.tsx @@ -1,12 +1,11 @@ import { TFunction } from 'react-i18next'; import { ConnectionStatus, ConnectionType } from '@renderer/domain/connection'; -import { ExtendedChain } from '@renderer/services/network/common/types'; +import { ExtendedChain } from '@renderer/entities/network'; import { NetworkSelector } from '../NetworkSelector/NetworkSelector'; -import { BodyText, StatusLabel, FootnoteText, ChainIcon } from '@renderer/components/ui-redesign'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; -import { useI18n } from '@renderer/context/I18nContext'; -import { RpcNode } from '@renderer/domain/chain'; +import { BodyText, StatusLabel, FootnoteText, HelpText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { RpcNode, ChainIcon } from '@renderer/entities/chain'; import './NetworkItem.css'; const Status = { diff --git a/src/renderer/screens/Settings/Networks/components/NetworkList/NetworkList.test.tsx b/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.test.tsx similarity index 96% rename from src/renderer/screens/Settings/Networks/components/NetworkList/NetworkList.test.tsx rename to src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.test.tsx index 07be0f91c8..341ad097e6 100644 --- a/src/renderer/screens/Settings/Networks/components/NetworkList/NetworkList.test.tsx +++ b/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.test.tsx @@ -1,7 +1,7 @@ import { act, render, screen, waitFor } from '@testing-library/react'; import { ConnectionStatus, ConnectionType } from '@renderer/domain/connection'; -import { ExtendedChain } from '@renderer/services/network/common/types'; +import { ExtendedChain } from '@renderer/entities/network'; import { NetworkList } from './NetworkList'; describe('screen/Settings/Networks/NetworkList', () => { diff --git a/src/renderer/screens/Settings/Networks/components/NetworkList/NetworkList.tsx b/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.tsx similarity index 93% rename from src/renderer/screens/Settings/Networks/components/NetworkList/NetworkList.tsx rename to src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.tsx index 52b1f12cfd..73f19b3c7b 100644 --- a/src/renderer/screens/Settings/Networks/components/NetworkList/NetworkList.tsx +++ b/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.tsx @@ -1,7 +1,7 @@ import { useEffect, useState, ReactNode } from 'react'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { CaptionText, Counter, Accordion } from '@renderer/components/ui-redesign'; +import { ExtendedChain } from '@renderer/entities/network'; +import { CaptionText, Counter, Accordion } from '@renderer/shared/ui'; import { ConnectionType, ConnectionStatus } from '@renderer/domain/connection'; type Props = { diff --git a/src/renderer/screens/Settings/Networks/components/NetworkSelector/NetworkSelector.test.tsx b/src/renderer/pages/Settings/Networks/components/NetworkSelector/NetworkSelector.test.tsx similarity index 94% rename from src/renderer/screens/Settings/Networks/components/NetworkSelector/NetworkSelector.test.tsx rename to src/renderer/pages/Settings/Networks/components/NetworkSelector/NetworkSelector.test.tsx index a394d963d9..bf11ca46a3 100644 --- a/src/renderer/screens/Settings/Networks/components/NetworkSelector/NetworkSelector.test.tsx +++ b/src/renderer/pages/Settings/Networks/components/NetworkSelector/NetworkSelector.test.tsx @@ -2,17 +2,17 @@ import { act, render, screen } from '@testing-library/react'; import noop from 'lodash/noop'; import { ConnectionStatus, ConnectionType } from '@renderer/domain/connection'; -import { ExtendedChain } from '@renderer/services/network/common/types'; +import { ExtendedChain } from '@renderer/entities/network'; import { NetworkSelector } from './NetworkSelector'; -import { useScrollTo } from '@renderer/shared/hooks'; +import { useScrollTo } from '@renderer/shared/lib/hooks'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/shared/hooks'); +jest.mock('@renderer/shared/lib/hooks'); describe('screen/Settings/Networks/NetworkSelector', () => { beforeAll(() => { diff --git a/src/renderer/screens/Settings/Networks/components/NetworkSelector/NetworkSelector.tsx b/src/renderer/pages/Settings/Networks/components/NetworkSelector/NetworkSelector.tsx similarity index 89% rename from src/renderer/screens/Settings/Networks/components/NetworkSelector/NetworkSelector.tsx rename to src/renderer/pages/Settings/Networks/components/NetworkSelector/NetworkSelector.tsx index 443a9d2688..5435f986bf 100644 --- a/src/renderer/screens/Settings/Networks/components/NetworkSelector/NetworkSelector.tsx +++ b/src/renderer/pages/Settings/Networks/components/NetworkSelector/NetworkSelector.tsx @@ -1,18 +1,16 @@ import { Listbox, Transition } from '@headlessui/react'; import { useState, Fragment, useEffect } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { Icon } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; -import { RpcNode } from '@renderer/domain/chain'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { Icon, FootnoteText, IconButton, Button, HelpText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { RpcNode } from '@renderer/entities/chain'; import { ConnectionType } from '@renderer/domain/connection'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { SelectButtonStyle, OptionStyle } from '@renderer/components/ui-redesign/Dropdowns/common/constants'; -import { FootnoteText, IconButton, Button } from '@renderer/components/ui-redesign'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; -import { useScrollTo } from '@renderer/shared/hooks'; -import { CommonInputStyles, CommonInputStylesTheme } from '@renderer/components/ui-redesign/Inputs/common/styles'; -import { Theme } from '@renderer/components/ui-redesign/Dropdowns/common/types'; +import { ExtendedChain } from '@renderer/entities/network'; +import { SelectButtonStyle, OptionStyle } from '@renderer/shared/ui/Dropdowns/common/constants'; +import { useScrollTo } from '@renderer/shared/lib/hooks'; +import { CommonInputStyles, CommonInputStylesTheme } from '@renderer/shared/ui/Inputs/common/styles'; +import type { Theme } from '@renderer/shared/ui/types'; export const OptionsContainerStyle = 'mt-1 absolute z-20 py-1 px-1 w-full border border-token-container-border rounded bg-input-background shadow-card-shadow'; diff --git a/src/renderer/screens/Settings/Networks/components/index.ts b/src/renderer/pages/Settings/Networks/components/index.ts similarity index 100% rename from src/renderer/screens/Settings/Networks/components/index.ts rename to src/renderer/pages/Settings/Networks/components/index.ts diff --git a/src/renderer/screens/Settings/Overview/Overview.test.tsx b/src/renderer/pages/Settings/Overview/Overview.test.tsx similarity index 95% rename from src/renderer/screens/Settings/Overview/Overview.test.tsx rename to src/renderer/pages/Settings/Overview/Overview.test.tsx index cb29fc4638..90fa97fe90 100644 --- a/src/renderer/screens/Settings/Overview/Overview.test.tsx +++ b/src/renderer/pages/Settings/Overview/Overview.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'; import { Overview } from './Overview'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/screens/Settings/Overview/Overview.tsx b/src/renderer/pages/Settings/Overview/Overview.tsx similarity index 92% rename from src/renderer/screens/Settings/Overview/Overview.tsx rename to src/renderer/pages/Settings/Overview/Overview.tsx index cb256f2659..fac960bd18 100644 --- a/src/renderer/screens/Settings/Overview/Overview.tsx +++ b/src/renderer/pages/Settings/Overview/Overview.tsx @@ -1,6 +1,6 @@ import { Outlet } from 'react-router-dom'; -import { useI18n } from '@renderer/context/I18nContext'; +import { useI18n } from '@renderer/app/providers'; import { Header } from '@renderer/components/common'; import { GeneralActions, MatrixAction, SocialLinks, Version } from './components'; diff --git a/src/renderer/screens/Settings/Overview/components/GeneralActions/GeneralActions.test.tsx b/src/renderer/pages/Settings/Overview/components/GeneralActions/GeneralActions.test.tsx similarity index 85% rename from src/renderer/screens/Settings/Overview/components/GeneralActions/GeneralActions.test.tsx rename to src/renderer/pages/Settings/Overview/components/GeneralActions/GeneralActions.test.tsx index bb4ae6b552..81414b0f9d 100644 --- a/src/renderer/screens/Settings/Overview/components/GeneralActions/GeneralActions.test.tsx +++ b/src/renderer/pages/Settings/Overview/components/GeneralActions/GeneralActions.test.tsx @@ -2,9 +2,9 @@ import { render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { GeneralActions } from './GeneralActions'; -import Paths from '@renderer/routes/paths'; +import { Paths } from '../../../../../app/providers/routes/paths'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/screens/Settings/Overview/components/GeneralActions/GeneralActions.tsx b/src/renderer/pages/Settings/Overview/components/GeneralActions/GeneralActions.tsx similarity index 85% rename from src/renderer/screens/Settings/Overview/components/GeneralActions/GeneralActions.tsx rename to src/renderer/pages/Settings/Overview/components/GeneralActions/GeneralActions.tsx index 27d05cffd8..f389fff3a6 100644 --- a/src/renderer/screens/Settings/Overview/components/GeneralActions/GeneralActions.tsx +++ b/src/renderer/pages/Settings/Overview/components/GeneralActions/GeneralActions.tsx @@ -1,11 +1,9 @@ import { Link } from 'react-router-dom'; -import { Icon } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; -import { BodyText, Plate, FootnoteText } from '@renderer/components/ui-redesign'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; -import Paths from '@renderer/routes/paths'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { Icon, BodyText, Plate, FootnoteText, HelpText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { Paths } from '../../../../../app/providers/routes/paths'; +import { cnTw } from '@renderer/shared/lib/utils'; // TODO: Language switcher temporary removed export const GeneralActions = () => { diff --git a/src/renderer/screens/Settings/Overview/components/MatrixAction/MatrixAction.test.tsx b/src/renderer/pages/Settings/Overview/components/MatrixAction/MatrixAction.test.tsx similarity index 90% rename from src/renderer/screens/Settings/Overview/components/MatrixAction/MatrixAction.test.tsx rename to src/renderer/pages/Settings/Overview/components/MatrixAction/MatrixAction.test.tsx index 4c8f325b32..cce8b711da 100644 --- a/src/renderer/screens/Settings/Overview/components/MatrixAction/MatrixAction.test.tsx +++ b/src/renderer/pages/Settings/Overview/components/MatrixAction/MatrixAction.test.tsx @@ -3,13 +3,10 @@ import { MemoryRouter } from 'react-router-dom'; import { MatrixAction } from './MatrixAction'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), -})); - -jest.mock('@renderer/context/MatrixContext', () => ({ useMatrix: jest.fn().mockReturnValue({ matrix: { userId: '@some_id:matrix.com' }, isLoggedIn: true, diff --git a/src/renderer/screens/Settings/Overview/components/MatrixAction/MatrixAction.tsx b/src/renderer/pages/Settings/Overview/components/MatrixAction/MatrixAction.tsx similarity index 79% rename from src/renderer/screens/Settings/Overview/components/MatrixAction/MatrixAction.tsx rename to src/renderer/pages/Settings/Overview/components/MatrixAction/MatrixAction.tsx index 9c4f2c8c53..cbe7410a67 100644 --- a/src/renderer/screens/Settings/Overview/components/MatrixAction/MatrixAction.tsx +++ b/src/renderer/pages/Settings/Overview/components/MatrixAction/MatrixAction.tsx @@ -1,11 +1,8 @@ -import { Icon } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; -import { FootnoteText, Plate, BodyText, StatusLabel } from '@renderer/components/ui-redesign'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { useToggle } from '@renderer/shared/hooks'; +import { Icon, FootnoteText, Plate, BodyText, StatusLabel, HelpText } from '@renderer/shared/ui'; +import { useI18n, useMatrix } from '@renderer/app/providers'; +import { useToggle } from '@renderer/shared/lib/hooks'; import { MatrixModal } from '@renderer/components/modals'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; export const MatrixAction = () => { const { t } = useI18n(); diff --git a/src/renderer/screens/Settings/Overview/components/SocialLinks/SocialLinks.test.tsx b/src/renderer/pages/Settings/Overview/components/SocialLinks/SocialLinks.test.tsx similarity index 94% rename from src/renderer/screens/Settings/Overview/components/SocialLinks/SocialLinks.test.tsx rename to src/renderer/pages/Settings/Overview/components/SocialLinks/SocialLinks.test.tsx index c9aa386025..2dc45d9235 100644 --- a/src/renderer/screens/Settings/Overview/components/SocialLinks/SocialLinks.test.tsx +++ b/src/renderer/pages/Settings/Overview/components/SocialLinks/SocialLinks.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'; import { SocialLinks } from './SocialLinks'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/screens/Settings/Overview/components/SocialLinks/SocialLinks.tsx b/src/renderer/pages/Settings/Overview/components/SocialLinks/SocialLinks.tsx similarity index 86% rename from src/renderer/screens/Settings/Overview/components/SocialLinks/SocialLinks.tsx rename to src/renderer/pages/Settings/Overview/components/SocialLinks/SocialLinks.tsx index c01286aa3a..fc90efdebe 100644 --- a/src/renderer/screens/Settings/Overview/components/SocialLinks/SocialLinks.tsx +++ b/src/renderer/pages/Settings/Overview/components/SocialLinks/SocialLinks.tsx @@ -1,8 +1,6 @@ -import { Icon } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; -import { FootnoteText, Plate, BodyText } from '@renderer/components/ui-redesign'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { Icon, FootnoteText, Plate, BodyText, HelpText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { cnTw } from '@renderer/shared/lib/utils'; const Links = [ { diff --git a/src/renderer/screens/Settings/Overview/components/Version/Version.test.tsx b/src/renderer/pages/Settings/Overview/components/Version/Version.test.tsx similarity index 90% rename from src/renderer/screens/Settings/Overview/components/Version/Version.test.tsx rename to src/renderer/pages/Settings/Overview/components/Version/Version.test.tsx index be16b9b3f8..9f6d55137d 100644 --- a/src/renderer/screens/Settings/Overview/components/Version/Version.test.tsx +++ b/src/renderer/pages/Settings/Overview/components/Version/Version.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'; import { Version } from './Version'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/screens/Settings/Overview/components/Version/Version.tsx b/src/renderer/pages/Settings/Overview/components/Version/Version.tsx similarity index 64% rename from src/renderer/screens/Settings/Overview/components/Version/Version.tsx rename to src/renderer/pages/Settings/Overview/components/Version/Version.tsx index 9b05d81ea2..4612bfeab3 100644 --- a/src/renderer/screens/Settings/Overview/components/Version/Version.tsx +++ b/src/renderer/pages/Settings/Overview/components/Version/Version.tsx @@ -1,6 +1,5 @@ -import { Icon } from '@renderer/components/ui'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Icon, HelpText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; export const Version = () => { const { t } = useI18n(); diff --git a/src/renderer/screens/Settings/Overview/components/index.ts b/src/renderer/pages/Settings/Overview/components/index.ts similarity index 100% rename from src/renderer/screens/Settings/Overview/components/index.ts rename to src/renderer/pages/Settings/Overview/components/index.ts diff --git a/src/renderer/screens/Settings/index.ts b/src/renderer/pages/Settings/index.ts similarity index 100% rename from src/renderer/screens/Settings/index.ts rename to src/renderer/pages/Settings/index.ts diff --git a/src/renderer/screens/Staking/Operations/Bond/Bond.test.tsx b/src/renderer/pages/Staking/Operations/Bond/Bond.test.tsx similarity index 91% rename from src/renderer/screens/Staking/Operations/Bond/Bond.test.tsx rename to src/renderer/pages/Staking/Operations/Bond/Bond.test.tsx index 6ff4118e62..cc8c6cee33 100644 --- a/src/renderer/screens/Staking/Operations/Bond/Bond.test.tsx +++ b/src/renderer/pages/Staking/Operations/Bond/Bond.test.tsx @@ -2,28 +2,22 @@ import { act, render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { ConnectionStatus } from '@renderer/domain/connection'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { Bond } from './Bond'; -jest.mock('@renderer/context/I18nContext', () => ({ - useI18n: jest.fn().mockReturnValue({ - t: (key: string) => key, - }), -})); - jest.mock('react-router-dom', () => ({ useSearchParams: jest.fn().mockReturnValue([new URLSearchParams('id=1,2,3')]), useParams: jest.fn().mockReturnValue({ chainId: '0x123' }), useNavigate: jest.fn(), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getActiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/context/NetworkContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useNetworkContext: jest.fn(() => ({ connections: { '0x123': { @@ -45,6 +39,9 @@ jest.mock('@renderer/context/NetworkContext', () => ({ }, }, })), + useI18n: jest.fn().mockReturnValue({ + t: (key: string) => key, + }), })); const mockButton = (text: string, callback: () => void) => ( @@ -79,7 +76,7 @@ jest.mock( mockButton('to sign', onResult), ); -describe('screens/Staking/Bond', () => { +describe('pages/Staking/Bond', () => { test('should render component', async () => { await act(async () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/screens/Staking/Operations/Bond/Bond.tsx b/src/renderer/pages/Staking/Operations/Bond/Bond.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/Bond/Bond.tsx rename to src/renderer/pages/Staking/Operations/Bond/Bond.tsx index acf847fdaa..94ee8dc462 100644 --- a/src/renderer/screens/Staking/Operations/Bond/Bond.tsx +++ b/src/renderer/pages/Staking/Operations/Bond/Bond.tsx @@ -2,30 +2,22 @@ import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { useState, useEffect } from 'react'; import { Navigate, useParams, useSearchParams, useNavigate } from 'react-router-dom'; -import { toAddress } from '@renderer/shared/utils/address'; -import { getRelaychainAsset } from '@renderer/shared/utils/assets'; -import { RewardsDestination } from '@renderer/domain/stake'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; +import { toAddress, getRelaychainAsset, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { RewardsDestination, ValidatorMap } from '@renderer/entities/staking'; +import { useI18n, useNetworkContext, Paths } from '@renderer/app/providers'; import { Address, ChainId, HexString, AccountId } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { ValidatorMap } from '@renderer/services/staking/common/types'; +import { Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; import { Validators, Confirmation, Signing, Submit, NoAsset } from '../components'; -import { useCountdown, useToggle } from '@renderer/shared/hooks'; -import { Account, MultisigAccount, isMultisig } from '@renderer/domain/account'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { BaseModal, Alert, Button } from '@renderer/components/ui-redesign'; +import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { Account, MultisigAccount, isMultisig, useAccount } from '@renderer/entities/account'; +import { BaseModal, Alert, Button, Loader } from '@renderer/shared/ui'; import InitOperation, { BondResult } from './InitOperation/InitOperation'; -import Paths from '@renderer/routes/paths'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; -import OperationModalTitle from '@renderer/screens/Operations/components/OperationModalTitle'; -import { useAccount } from '@renderer/services/account/accountService'; +import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; import { DestinationType } from '../common/types'; import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; -import { UnstakingDuration } from '@renderer/screens/Staking/Overview/components'; -import { isLightClient } from '@renderer/services/network/common/utils'; -import { Loader } from '@renderer/components/ui'; +import { UnstakingDuration } from '@renderer/pages/Staking/Overview/components'; +import { isLightClient } from '@renderer/entities/network'; const enum Step { INIT, diff --git a/src/renderer/screens/Staking/Operations/Bond/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.test.tsx similarity index 77% rename from src/renderer/screens/Staking/Operations/Bond/InitOperation/InitOperation.test.tsx rename to src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.test.tsx index c2e198dd85..5c87a11304 100644 --- a/src/renderer/screens/Staking/Operations/Bond/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.test.tsx @@ -2,31 +2,32 @@ import { ApiPromise } from '@polkadot/api'; import { act, render, screen } from '@testing-library/react'; import noop from 'lodash/noop'; -import { Asset } from '@renderer/domain/asset'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { Asset } from '@renderer/entities/asset'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import InitOperation from './InitOperation'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { Account } from '@renderer/domain/account'; +import { Account } from '@renderer/entities/account'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ + ...jest.requireActual('@renderer/entities/account'), useAccount: jest.fn().mockReturnValue({ getLiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/services/staking/validatorsService', () => ({ +jest.mock('@renderer/entities/staking', () => ({ useValidators: jest.fn().mockReturnValue({ getMaxValidators: () => 4, }), })); -jest.mock('@renderer/services/balance/balanceService', () => ({ +jest.mock('@renderer/entities/asset', () => ({ useBalance: jest.fn().mockReturnValue({ getLiveAssetBalances: jest.fn().mockReturnValue([ { @@ -38,6 +39,7 @@ jest.mock('@renderer/services/balance/balanceService', () => ({ }, ]), }), + AssetBalance: () => balance, })); jest.mock('../../components', () => ({ @@ -51,7 +53,7 @@ jest.mock('../../components', () => ({ }, })); -describe('screens/Staking/Bond/InitOperation', () => { +describe('pages/Staking/Bond/InitOperation', () => { const defaultProps = { api: {} as ApiPromise, chainId: '0x123' as ChainId, diff --git a/src/renderer/screens/Staking/Operations/Bond/InitOperation/InitOperation.tsx b/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/Bond/InitOperation/InitOperation.tsx rename to src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.tsx index 925451cdd1..7d36420aec 100644 --- a/src/renderer/screens/Staking/Operations/Bond/InitOperation/InitOperation.tsx +++ b/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.tsx @@ -2,21 +2,15 @@ import { ApiPromise } from '@polkadot/api'; import { BN } from '@polkadot/util'; import { useEffect, useState } from 'react'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui-redesign/Dropdowns/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; -import { Balance as AccountBalance } from '@renderer/domain/balance'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, Balance as AccountBalance, useBalance } from '@renderer/entities/asset'; import { AccountId, Address, ChainId, SigningType } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { useAccount } from '@renderer/services/account/accountService'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { formatAmount, stakeableAmount } from '@renderer/shared/utils/balance'; -import { useValidators } from '@renderer/services/staking/validatorsService'; -import { Account, isMultisig, MultisigAccount } from '@renderer/domain/account'; -import { toAddress } from '@renderer/shared/utils/address'; -import { nonNullable } from '@renderer/shared/utils/functions'; -import { InputHint, MultiSelect, Select } from '@renderer/components/ui-redesign'; -import { TEST_ADDRESS } from '@renderer/shared/utils/constants'; +import { Transaction, TransactionType } from '@renderer/entities/transaction'; +import { useAccount, Account, isMultisig, MultisigAccount } from '@renderer/entities/account'; +import { formatAmount, stakeableAmount, toAddress, nonNullable, TEST_ADDRESS } from '@renderer/shared/lib/utils'; +import { useValidators } from '@renderer/entities/staking'; +import { InputHint, MultiSelect, Select } from '@renderer/shared/ui'; import { OperationForm } from '../../components'; import { getSignatoryOptions, diff --git a/src/renderer/screens/Staking/Operations/ChangeValidators/ChangeValidators.test.tsx b/src/renderer/pages/Staking/Operations/ChangeValidators/ChangeValidators.test.tsx similarity index 91% rename from src/renderer/screens/Staking/Operations/ChangeValidators/ChangeValidators.test.tsx rename to src/renderer/pages/Staking/Operations/ChangeValidators/ChangeValidators.test.tsx index 607f2739a5..ce8f3e46d2 100644 --- a/src/renderer/screens/Staking/Operations/ChangeValidators/ChangeValidators.test.tsx +++ b/src/renderer/pages/Staking/Operations/ChangeValidators/ChangeValidators.test.tsx @@ -2,28 +2,25 @@ import { act, render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { ConnectionStatus } from '@renderer/domain/connection'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { ChangeValidators } from './ChangeValidators'; -jest.mock('@renderer/context/I18nContext', () => ({ - useI18n: jest.fn().mockReturnValue({ - t: (key: string) => key, - }), -})); - jest.mock('react-router-dom', () => ({ useSearchParams: jest.fn().mockReturnValue([new URLSearchParams('id=1,2,3')]), useParams: jest.fn().mockReturnValue({ chainId: '0x123' }), useNavigate: jest.fn(), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getActiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/context/NetworkContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ + useI18n: jest.fn().mockReturnValue({ + t: (key: string) => key, + }), useNetworkContext: jest.fn(() => ({ connections: { '0x123': { @@ -79,7 +76,7 @@ jest.mock( mockButton('to sign', onResult), ); -describe('screens/Staking/ChangeValidators', () => { +describe('pages/Staking/ChangeValidators', () => { test('should render component', async () => { await act(async () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/screens/Staking/Operations/ChangeValidators/ChangeValidators.tsx b/src/renderer/pages/Staking/Operations/ChangeValidators/ChangeValidators.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/ChangeValidators/ChangeValidators.tsx rename to src/renderer/pages/Staking/Operations/ChangeValidators/ChangeValidators.tsx index 134db3c24f..ba7ee6f4ea 100644 --- a/src/renderer/screens/Staking/Operations/ChangeValidators/ChangeValidators.tsx +++ b/src/renderer/pages/Staking/Operations/ChangeValidators/ChangeValidators.tsx @@ -2,27 +2,20 @@ import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { useState, useEffect } from 'react'; import { Navigate, useNavigate, useParams, useSearchParams } from 'react-router-dom'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; +import { useI18n, useNetworkContext, Paths } from '@renderer/app/providers'; import { ChainId, HexString, AccountId, Address } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { useAccount } from '@renderer/services/account/accountService'; -import { ValidatorMap } from '@renderer/services/staking/common/types'; -import { toAddress } from '@renderer/shared/utils/address'; -import { getRelaychainAsset } from '@renderer/shared/utils/assets'; +import { Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; +import { useAccount, Account, isMultisig, MultisigAccount } from '@renderer/entities/account'; +import { ValidatorMap } from '@renderer/entities/staking'; +import { toAddress, getRelaychainAsset, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; import { Confirmation, Signing, Submit, Validators, NoAsset } from '../components'; -import { useCountdown, useToggle } from '@renderer/shared/hooks'; -import { Alert, BaseModal, Button } from '@renderer/components/ui-redesign'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; -import { Account, isMultisig, MultisigAccount } from '@renderer/domain/account'; +import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { Alert, BaseModal, Button, Loader } from '@renderer/shared/ui'; import InitOperation, { ValidatorsResult } from './InitOperation/InitOperation'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; -import { isLightClient } from '@renderer/services/network/common/utils'; -import Paths from '@renderer/routes/paths'; -import { Loader } from '@renderer/components/ui'; -import OperationModalTitle from '@renderer/screens/Operations/components/OperationModalTitle'; +import { isLightClient } from '@renderer/entities/network'; +import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; const enum Step { INIT, diff --git a/src/renderer/screens/Staking/Operations/ChangeValidators/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/ChangeValidators/InitOperation/InitOperation.test.tsx similarity index 75% rename from src/renderer/screens/Staking/Operations/ChangeValidators/InitOperation/InitOperation.test.tsx rename to src/renderer/pages/Staking/Operations/ChangeValidators/InitOperation/InitOperation.test.tsx index b18e588819..c8adc2456f 100644 --- a/src/renderer/screens/Staking/Operations/ChangeValidators/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/ChangeValidators/InitOperation/InitOperation.test.tsx @@ -2,31 +2,32 @@ import { ApiPromise } from '@polkadot/api'; import { act, render, screen } from '@testing-library/react'; import noop from 'lodash/noop'; -import { Asset } from '@renderer/domain/asset'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { Asset } from '@renderer/entities/asset'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import InitOperation from './InitOperation'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { Account } from '@renderer/domain/account'; +import { Account } from '@renderer/entities/account'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/staking/validatorsService', () => ({ +jest.mock('@renderer/entities/staking', () => ({ useValidators: jest.fn().mockReturnValue({ getMaxValidators: () => 4, }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ + ...jest.requireActual('@renderer/entities/account'), useAccount: jest.fn().mockReturnValue({ getLiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/services/balance/balanceService', () => ({ +jest.mock('@renderer/entities/asset', () => ({ useBalance: jest.fn().mockReturnValue({ getLiveAssetBalances: jest.fn().mockReturnValue([ { @@ -38,6 +39,7 @@ jest.mock('@renderer/services/balance/balanceService', () => ({ }, ]), }), + AssetBalance: () => balance, })); jest.mock('../../components', () => ({ @@ -51,7 +53,7 @@ jest.mock('../../components', () => ({ }, })); -describe('screens/Staking/ChangeValidators/InitOperation', () => { +describe('pages/Staking/ChangeValidators/InitOperation', () => { const defaultProps = { api: {} as ApiPromise, chainId: '0x123' as ChainId, diff --git a/src/renderer/screens/Staking/Operations/ChangeValidators/InitOperation/InitOperation.tsx b/src/renderer/pages/Staking/Operations/ChangeValidators/InitOperation/InitOperation.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/ChangeValidators/InitOperation/InitOperation.tsx rename to src/renderer/pages/Staking/Operations/ChangeValidators/InitOperation/InitOperation.tsx index ca4365b97c..05e70ec5b0 100644 --- a/src/renderer/screens/Staking/Operations/ChangeValidators/InitOperation/InitOperation.tsx +++ b/src/renderer/pages/Staking/Operations/ChangeValidators/InitOperation/InitOperation.tsx @@ -1,19 +1,15 @@ import { ApiPromise } from '@polkadot/api'; import { useEffect, useState } from 'react'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui-redesign/Dropdowns/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; -import { Balance as AccountBalance } from '@renderer/domain/balance'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, Balance as AccountBalance, useBalance } from '@renderer/entities/asset'; import { ChainId, AccountId, SigningType } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { useAccount } from '@renderer/services/account/accountService'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { useValidators } from '@renderer/services/staking/validatorsService'; -import { Account, isMultisig, MultisigAccount } from '@renderer/domain/account'; -import { toAddress } from '@renderer/shared/utils/address'; -import { nonNullable } from '@renderer/shared/utils/functions'; -import { MultiSelect, Select, InputHint } from '@renderer/components/ui-redesign'; +import { Transaction, TransactionType } from '@renderer/entities/transaction'; +import { useAccount, Account, isMultisig, MultisigAccount } from '@renderer/entities/account'; +import { useValidators } from '@renderer/entities/staking'; +import { toAddress, nonNullable } from '@renderer/shared/lib/utils'; +import { MultiSelect, Select, InputHint } from '@renderer/shared/ui'; import { OperationForm } from '../../components'; import { getSignatoryOptions, diff --git a/src/renderer/screens/Staking/Operations/Destination/Destination.test.tsx b/src/renderer/pages/Staking/Operations/Destination/Destination.test.tsx similarity index 92% rename from src/renderer/screens/Staking/Operations/Destination/Destination.test.tsx rename to src/renderer/pages/Staking/Operations/Destination/Destination.test.tsx index 5389297945..e6b82b81ad 100644 --- a/src/renderer/screens/Staking/Operations/Destination/Destination.test.tsx +++ b/src/renderer/pages/Staking/Operations/Destination/Destination.test.tsx @@ -4,25 +4,22 @@ import { MemoryRouter } from 'react-router-dom'; import { ConnectionStatus } from '@renderer/domain/connection'; import { Destination } from './Destination'; -jest.mock('@renderer/context/I18nContext', () => ({ - useI18n: jest.fn().mockReturnValue({ - t: (key: string) => key, - }), -})); - jest.mock('react-router-dom', () => ({ useSearchParams: jest.fn().mockReturnValue([new URLSearchParams('id=1,2,3')]), useParams: jest.fn().mockReturnValue({ chainId: '0x123' }), useNavigate: jest.fn(), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getActiveAccounts: jest.fn().mockReturnValue([]), }), })); -jest.mock('@renderer/context/NetworkContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ + useI18n: jest.fn().mockReturnValue({ + t: (key: string) => key, + }), useNetworkContext: jest.fn(() => ({ connections: { '0x123': { @@ -77,7 +74,7 @@ jest.mock( mockButton('to sign', onResult), ); -describe('screens/Staking/Destination', () => { +describe('pages/Staking/Destination', () => { test('should render component', async () => { await act(async () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/screens/Staking/Operations/Destination/Destination.tsx b/src/renderer/pages/Staking/Operations/Destination/Destination.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/Destination/Destination.tsx rename to src/renderer/pages/Staking/Operations/Destination/Destination.tsx index ed4c1b59f0..06727310bd 100644 --- a/src/renderer/screens/Staking/Operations/Destination/Destination.tsx +++ b/src/renderer/pages/Staking/Operations/Destination/Destination.tsx @@ -2,27 +2,20 @@ import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { useState, useEffect } from 'react'; import { Navigate, useNavigate, useParams, useSearchParams } from 'react-router-dom'; -import { toAddress } from '@renderer/shared/utils/address'; -import { getRelaychainAsset } from '@renderer/shared/utils/assets'; -import { RewardsDestination } from '@renderer/domain/stake'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; +import { toAddress, getRelaychainAsset, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { RewardsDestination } from '@renderer/entities/staking'; +import { useI18n, useNetworkContext, Paths } from '@renderer/app/providers'; import { Address, ChainId, HexString, AccountId } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; +import { Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; import { Confirmation, Signing, Submit, NoAsset } from '../components'; import InitOperation, { DestinationResult } from './InitOperation/InitOperation'; -import { useCountdown, useToggle } from '@renderer/shared/hooks'; -import { MultisigAccount, isMultisig, Account } from '@renderer/domain/account'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; +import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { MultisigAccount, isMultisig, Account, useAccount } from '@renderer/entities/account'; import { DestinationType } from '../common/types'; -import { BaseModal, Button } from '@renderer/components/ui-redesign'; -import Paths from '@renderer/routes/paths'; -import { useAccount } from '@renderer/services/account/accountService'; +import { BaseModal, Button, Loader } from '@renderer/shared/ui'; import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; -import { Loader } from '@renderer/components/ui'; -import OperationModalTitle from '@renderer/screens/Operations/components/OperationModalTitle'; +import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; const enum Step { INIT, diff --git a/src/renderer/screens/Staking/Operations/Destination/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.test.tsx similarity index 76% rename from src/renderer/screens/Staking/Operations/Destination/InitOperation/InitOperation.test.tsx rename to src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.test.tsx index 4cb7c5d6ae..23256cebdf 100644 --- a/src/renderer/screens/Staking/Operations/Destination/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.test.tsx @@ -2,25 +2,26 @@ import { ApiPromise } from '@polkadot/api'; import { act, render, screen } from '@testing-library/react'; import noop from 'lodash/noop'; -import { Asset } from '@renderer/domain/asset'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { Asset } from '@renderer/entities/asset'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import InitOperation from './InitOperation'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { Account } from '@renderer/domain/account'; +import { Account } from '@renderer/entities/account'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ + ...jest.requireActual('@renderer/entities/account'), useAccount: jest.fn().mockReturnValue({ getLiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/services/balance/balanceService', () => ({ +jest.mock('@renderer/entities/asset', () => ({ useBalance: jest.fn().mockReturnValue({ getLiveAssetBalances: jest.fn().mockReturnValue([ { @@ -32,6 +33,7 @@ jest.mock('@renderer/services/balance/balanceService', () => ({ }, ]), }), + AssetBalance: () => balance, })); jest.mock('../../components', () => ({ @@ -45,7 +47,7 @@ jest.mock('../../components', () => ({ }, })); -describe('screens/Staking/Destination/InitOperation', () => { +describe('pages/Staking/Destination/InitOperation', () => { const defaultProps = { api: {} as ApiPromise, chainId: '0x123' as ChainId, diff --git a/src/renderer/screens/Staking/Operations/Destination/InitOperation/InitOperation.tsx b/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/Destination/InitOperation/InitOperation.tsx rename to src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.tsx index 122778ee4a..6dc2b7f4c0 100644 --- a/src/renderer/screens/Staking/Operations/Destination/InitOperation/InitOperation.tsx +++ b/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.tsx @@ -1,19 +1,14 @@ import { ApiPromise } from '@polkadot/api'; import { useEffect, useState } from 'react'; -import { Select, MultiSelect, InputHint } from '@renderer/components/ui-redesign'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui/Dropdowns/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; +import { Select, MultiSelect, InputHint } from '@renderer/shared/ui'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, useBalance, Balance as AccountBalance } from '@renderer/entities/asset'; import { Address, ChainId, AccountId, SigningType } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { useAccount } from '@renderer/services/account/accountService'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { Balance as AccountBalance } from '@renderer/domain/balance'; -import { Account, isMultisig } from '@renderer/domain/account'; -import { toAddress } from '@renderer/shared/utils/address'; -import { nonNullable } from '@renderer/shared/utils/functions'; -import { TEST_ADDRESS } from '@renderer/shared/utils/constants'; +import { Transaction, TransactionType } from '@renderer/entities/transaction'; +import { useAccount, Account, isMultisig } from '@renderer/entities/account'; +import { toAddress, nonNullable, TEST_ADDRESS } from '@renderer/shared/lib/utils'; import { OperationForm } from '../../components'; import { getSignatoryOptions, diff --git a/src/renderer/screens/Staking/Operations/Redeem/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/Redeem/InitOperation/InitOperation.test.tsx similarity index 76% rename from src/renderer/screens/Staking/Operations/Redeem/InitOperation/InitOperation.test.tsx rename to src/renderer/pages/Staking/Operations/Redeem/InitOperation/InitOperation.test.tsx index 0dac5255bf..574ceeb003 100644 --- a/src/renderer/screens/Staking/Operations/Redeem/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/Redeem/InitOperation/InitOperation.test.tsx @@ -2,37 +2,35 @@ import { ApiPromise } from '@polkadot/api'; import { act, render, screen } from '@testing-library/react'; import noop from 'lodash/noop'; -import { Asset } from '@renderer/domain/asset'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { Asset } from '@renderer/entities/asset'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import InitOperation from './InitOperation'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { Account } from '@renderer/domain/account'; +import { Account } from '@renderer/entities/account'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/staking/stakingDataService', () => ({ +jest.mock('@renderer/entities/staking', () => ({ useStakingData: jest.fn().mockReturnValue({ subscribeStaking: jest.fn(), }), -})); - -jest.mock('@renderer/services/staking/eraService', () => ({ useEra: jest.fn().mockReturnValue({ subscribeActiveEra: jest.fn(), }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ + ...jest.requireActual('@renderer/entities/account'), useAccount: jest.fn().mockReturnValue({ getLiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/services/balance/balanceService', () => ({ +jest.mock('@renderer/entities/asset', () => ({ useBalance: jest.fn().mockReturnValue({ getLiveAssetBalances: jest.fn().mockReturnValue([ { @@ -44,6 +42,7 @@ jest.mock('@renderer/services/balance/balanceService', () => ({ }, ]), }), + AssetBalance: () => balance, })); jest.mock('../../components', () => ({ @@ -57,7 +56,7 @@ jest.mock('../../components', () => ({ }, })); -describe('screens/Staking/Redeem/InitOperation', () => { +describe('pages/Staking/Redeem/InitOperation', () => { const defaultProps = { api: {} as ApiPromise, chainId: '0x123' as ChainId, diff --git a/src/renderer/screens/Staking/Operations/Redeem/InitOperation/InitOperation.tsx b/src/renderer/pages/Staking/Operations/Redeem/InitOperation/InitOperation.tsx similarity index 89% rename from src/renderer/screens/Staking/Operations/Redeem/InitOperation/InitOperation.tsx rename to src/renderer/pages/Staking/Operations/Redeem/InitOperation/InitOperation.tsx index 6e3f9a1f0f..cf5dba1037 100644 --- a/src/renderer/screens/Staking/Operations/Redeem/InitOperation/InitOperation.tsx +++ b/src/renderer/pages/Staking/Operations/Redeem/InitOperation/InitOperation.tsx @@ -2,22 +2,15 @@ import { ApiPromise } from '@polkadot/api'; import { BN, BN_ZERO } from '@polkadot/util'; import { useEffect, useState } from 'react'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui/Dropdowns/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; -import { Balance as AccountBalance } from '@renderer/domain/balance'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, Balance as AccountBalance, useBalance } from '@renderer/entities/asset'; import { ChainId, AccountId, SigningType } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { useAccount } from '@renderer/services/account/accountService'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { redeemableAmount, formatBalance } from '@renderer/shared/utils/balance'; -import { nonNullable } from '@renderer/shared/utils/functions'; -import { toAddress } from '@renderer/shared/utils/address'; -import { Account, isMultisig } from '@renderer/domain/account'; -import { StakingMap } from '@renderer/services/staking/common/types'; -import { MultiSelect, Select, InputHint } from '@renderer/components/ui-redesign'; -import { useStakingData } from '@renderer/services/staking/stakingDataService'; -import { useEra } from '@renderer/services/staking/eraService'; +import { Transaction, TransactionType } from '@renderer/entities/transaction'; +import { useAccount, Account, isMultisig } from '@renderer/entities/account'; +import { redeemableAmount, formatBalance, nonNullable, toAddress } from '@renderer/shared/lib/utils'; +import { StakingMap, useStakingData, useEra } from '@renderer/entities/staking'; +import { MultiSelect, Select, InputHint } from '@renderer/shared/ui'; import { OperationForm } from '../../components'; import { validateBalanceForFee, diff --git a/src/renderer/screens/Staking/Operations/Redeem/Redeem.tsx b/src/renderer/pages/Staking/Operations/Redeem/Redeem.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/Redeem/Redeem.tsx rename to src/renderer/pages/Staking/Operations/Redeem/Redeem.tsx index 238dec12ad..1a54f66cec 100644 --- a/src/renderer/screens/Staking/Operations/Redeem/Redeem.tsx +++ b/src/renderer/pages/Staking/Operations/Redeem/Redeem.tsx @@ -2,25 +2,18 @@ import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { useEffect, useState } from 'react'; import { Navigate, useNavigate, useParams, useSearchParams } from 'react-router-dom'; -import Paths from '@renderer/routes/paths'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; +import { Paths, useI18n, useNetworkContext } from '@renderer/app/providers'; import { ChainId, HexString, AccountId, Address } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { useAccount } from '@renderer/services/account/accountService'; +import { Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; +import { useAccount, Account, MultisigAccount, isMultisig } from '@renderer/entities/account'; import InitOperation, { RedeemResult } from './InitOperation/InitOperation'; import { Confirmation, Signing, Submit, NoAsset } from '../components'; -import { getRelaychainAsset } from '@renderer/shared/utils/assets'; -import { useCountdown, useToggle } from '@renderer/shared/hooks'; -import { Account, MultisigAccount, isMultisig } from '@renderer/domain/account'; -import { toAddress } from '@renderer/shared/utils/address'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; -import OperationModalTitle from '@renderer/screens/Operations/components/OperationModalTitle'; -import { BaseModal, Button } from '@renderer/components/ui-redesign'; +import { getRelaychainAsset, toAddress, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; +import { BaseModal, Button, Loader } from '@renderer/shared/ui'; import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; -import { Loader } from '@renderer/components/ui'; const enum Step { INIT, diff --git a/src/renderer/screens/Staking/Operations/Restake/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.test.tsx similarity index 76% rename from src/renderer/screens/Staking/Operations/Restake/InitOperation/InitOperation.test.tsx rename to src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.test.tsx index 8be1bc858f..83d9781739 100644 --- a/src/renderer/screens/Staking/Operations/Restake/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.test.tsx @@ -2,37 +2,35 @@ import { ApiPromise } from '@polkadot/api'; import { act, render, screen } from '@testing-library/react'; import noop from 'lodash/noop'; -import { Asset } from '@renderer/domain/asset'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { Asset } from '@renderer/entities/asset'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import InitOperation from './InitOperation'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { Account } from '@renderer/domain/account'; +import { Account } from '@renderer/entities/account'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/staking/stakingDataService', () => ({ +jest.mock('@renderer/entities/staking', () => ({ useStakingData: jest.fn().mockReturnValue({ subscribeStaking: jest.fn(), }), -})); - -jest.mock('@renderer/services/staking/eraService', () => ({ useEra: jest.fn().mockReturnValue({ subscribeActiveEra: jest.fn(), }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ + ...jest.requireActual('@renderer/entities/account'), useAccount: jest.fn().mockReturnValue({ getLiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/services/balance/balanceService', () => ({ +jest.mock('@renderer/entities/asset', () => ({ useBalance: jest.fn().mockReturnValue({ getLiveAssetBalances: jest.fn().mockReturnValue([ { @@ -44,6 +42,7 @@ jest.mock('@renderer/services/balance/balanceService', () => ({ }, ]), }), + AssetBalance: () => balance, })); jest.mock('../../components', () => ({ @@ -57,7 +56,7 @@ jest.mock('../../components', () => ({ }, })); -describe('screens/Staking/Restake/InitOperation', () => { +describe('pages/Staking/Restake/InitOperation', () => { const defaultProps = { api: {} as ApiPromise, chainId: '0x123' as ChainId, diff --git a/src/renderer/screens/Staking/Operations/Restake/InitOperation/InitOperation.tsx b/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/Restake/InitOperation/InitOperation.tsx rename to src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.tsx index 599dcd7f8c..33c9cc10d1 100644 --- a/src/renderer/screens/Staking/Operations/Restake/InitOperation/InitOperation.tsx +++ b/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.tsx @@ -2,22 +2,16 @@ import { ApiPromise } from '@polkadot/api'; import { BN } from '@polkadot/util'; import { useEffect, useState } from 'react'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui/Dropdowns/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, useBalance, Balance as AccountBalance } from '@renderer/entities/asset'; import { ChainId, AccountId, SigningType } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { useAccount } from '@renderer/services/account/accountService'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { formatAmount, unlockingAmount } from '@renderer/shared/utils/balance'; -import { StakingMap } from '@renderer/services/staking/common/types'; -import { toAddress } from '@renderer/shared/utils/address'; -import { Account, isMultisig } from '@renderer/domain/account'; -import { Balance as AccountBalance } from '@renderer/domain/balance'; +import { Transaction, TransactionType } from '@renderer/entities/transaction'; +import { useAccount, Account, isMultisig } from '@renderer/entities/account'; +import { formatAmount, unlockingAmount, toAddress, nonNullable } from '@renderer/shared/lib/utils'; +import { StakingMap, useStakingData } from '@renderer/entities/staking'; import { OperationForm } from '../../components'; -import { nonNullable } from '@renderer/shared/utils/functions'; -import { Select, MultiSelect, InputHint } from '@renderer/components/ui-redesign'; -import { useStakingData } from '@renderer/services/staking/stakingDataService'; +import { Select, MultiSelect, InputHint } from '@renderer/shared/ui'; import { getRestakeAccountOption, validateRestake, diff --git a/src/renderer/screens/Staking/Operations/Restake/Restake.test.tsx b/src/renderer/pages/Staking/Operations/Restake/Restake.test.tsx similarity index 89% rename from src/renderer/screens/Staking/Operations/Restake/Restake.test.tsx rename to src/renderer/pages/Staking/Operations/Restake/Restake.test.tsx index ab81b7bca0..7ac5aba9ab 100644 --- a/src/renderer/screens/Staking/Operations/Restake/Restake.test.tsx +++ b/src/renderer/pages/Staking/Operations/Restake/Restake.test.tsx @@ -2,28 +2,25 @@ import { act, render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { ConnectionStatus } from '@renderer/domain/connection'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { Restake } from './Restake'; -jest.mock('@renderer/context/I18nContext', () => ({ - useI18n: jest.fn().mockReturnValue({ - t: (key: string) => key, - }), -})); - jest.mock('react-router-dom', () => ({ useSearchParams: jest.fn().mockReturnValue([new URLSearchParams('id=1,2,3')]), useParams: jest.fn().mockReturnValue({ chainId: '0x123' }), useNavigate: jest.fn(), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getActiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/context/NetworkContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ + useI18n: jest.fn().mockReturnValue({ + t: (key: string) => key, + }), useNetworkContext: jest.fn(() => ({ connections: { '0x123': { @@ -47,7 +44,7 @@ jest.mock('@renderer/context/NetworkContext', () => ({ })), })); -jest.mock('@renderer/services/staking/stakingDataService', () => ({ +jest.mock('@renderer/entities/staking', () => ({ useStakingData: jest.fn().mockReturnValue({ subscribeStaking: jest.fn(), }), @@ -84,7 +81,7 @@ jest.mock( mockButton('to sign', onResult), ); -describe('screens/Staking/Restake', () => { +describe('pages/Staking/Restake', () => { test('should render component', async () => { await act(async () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/screens/Staking/Operations/Restake/Restake.tsx b/src/renderer/pages/Staking/Operations/Restake/Restake.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/Restake/Restake.tsx rename to src/renderer/pages/Staking/Operations/Restake/Restake.tsx index 5f728416ae..1b67c81f09 100644 --- a/src/renderer/screens/Staking/Operations/Restake/Restake.tsx +++ b/src/renderer/pages/Staking/Operations/Restake/Restake.tsx @@ -2,25 +2,18 @@ import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { useEffect, useState } from 'react'; import { Navigate, useNavigate, useParams, useSearchParams } from 'react-router-dom'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; +import { useI18n, useNetworkContext, Paths } from '@renderer/app/providers'; import { Address, ChainId, HexString, AccountId } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { useAccount } from '@renderer/services/account/accountService'; +import { Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; +import { useAccount, Account, MultisigAccount, isMultisig } from '@renderer/entities/account'; import InitOperation, { RestakeResult } from './InitOperation/InitOperation'; import { Confirmation, Signing, Submit, NoAsset } from '../components'; -import { getRelaychainAsset } from '@renderer/shared/utils/assets'; -import { useCountdown, useToggle } from '@renderer/shared/hooks'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { Account, MultisigAccount, isMultisig } from '@renderer/domain/account'; -import { toAddress } from '@renderer/shared/utils/address'; -import { Alert, BaseModal, Button } from '@renderer/components/ui-redesign'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; -import Paths from '@renderer/routes/paths'; +import { getRelaychainAsset, toAddress, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { Alert, BaseModal, Button, Loader } from '@renderer/shared/ui'; import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; -import { Loader } from '@renderer/components/ui'; -import OperationModalTitle from '@renderer/screens/Operations/components/OperationModalTitle'; +import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; const enum Step { INIT, diff --git a/src/renderer/screens/Staking/Operations/StakeMore/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/StakeMore/InitOperation/InitOperation.test.tsx similarity index 77% rename from src/renderer/screens/Staking/Operations/StakeMore/InitOperation/InitOperation.test.tsx rename to src/renderer/pages/Staking/Operations/StakeMore/InitOperation/InitOperation.test.tsx index b2c1d315b8..b420ea35d7 100644 --- a/src/renderer/screens/Staking/Operations/StakeMore/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/StakeMore/InitOperation/InitOperation.test.tsx @@ -2,25 +2,26 @@ import { ApiPromise } from '@polkadot/api'; import { act, render, screen } from '@testing-library/react'; import noop from 'lodash/noop'; -import { Asset } from '@renderer/domain/asset'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { Asset } from '@renderer/entities/asset'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import InitOperation from './InitOperation'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { Account } from '@renderer/domain/account'; +import { Account } from '@renderer/entities/account'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ + ...jest.requireActual('@renderer/entities/account'), useAccount: jest.fn().mockReturnValue({ getLiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/services/balance/balanceService', () => ({ +jest.mock('@renderer/entities/asset', () => ({ useBalance: jest.fn().mockReturnValue({ getLiveAssetBalances: jest.fn().mockReturnValue([ { @@ -32,6 +33,7 @@ jest.mock('@renderer/services/balance/balanceService', () => ({ }, ]), }), + AssetBalance: () => balance, })); jest.mock('../../components', () => ({ @@ -45,7 +47,7 @@ jest.mock('../../components', () => ({ }, })); -describe('screens/Staking/StakeMore/InitOperation', () => { +describe('pages/Staking/StakeMore/InitOperation', () => { const defaultProps = { api: {} as ApiPromise, chainId: '0x123' as ChainId, diff --git a/src/renderer/screens/Staking/Operations/StakeMore/InitOperation/InitOperation.tsx b/src/renderer/pages/Staking/Operations/StakeMore/InitOperation/InitOperation.tsx similarity index 91% rename from src/renderer/screens/Staking/Operations/StakeMore/InitOperation/InitOperation.tsx rename to src/renderer/pages/Staking/Operations/StakeMore/InitOperation/InitOperation.tsx index bdb3bbc8d1..fbece4ef47 100644 --- a/src/renderer/screens/Staking/Operations/StakeMore/InitOperation/InitOperation.tsx +++ b/src/renderer/pages/Staking/Operations/StakeMore/InitOperation/InitOperation.tsx @@ -2,20 +2,15 @@ import { ApiPromise } from '@polkadot/api'; import { BN } from '@polkadot/util'; import { useEffect, useState } from 'react'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui/Dropdowns/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; -import { Balance, Balance as AccountBalance } from '@renderer/domain/balance'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, Balance, Balance as AccountBalance, useBalance } from '@renderer/entities/asset'; import { ChainId, AccountId, SigningType } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { useAccount } from '@renderer/services/account/accountService'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { formatAmount, stakeableAmount } from '@renderer/shared/utils/balance'; -import { nonNullable } from '@renderer/shared/utils/functions'; +import { Transaction, TransactionType } from '@renderer/entities/transaction'; +import { useAccount, Account, isMultisig } from '@renderer/entities/account'; +import { formatAmount, stakeableAmount, nonNullable, toAddress } from '@renderer/shared/lib/utils'; import { OperationForm } from '../../components'; -import { toAddress } from '@renderer/shared/utils/address'; -import { Account, isMultisig } from '@renderer/domain/account'; -import { MultiSelect, Select, InputHint } from '@renderer/components/ui-redesign'; +import { MultiSelect, Select, InputHint } from '@renderer/shared/ui'; import { getStakeAccountOption, validateBalanceForFee, diff --git a/src/renderer/screens/Staking/Operations/StakeMore/StakeMore.test.tsx b/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.test.tsx similarity index 91% rename from src/renderer/screens/Staking/Operations/StakeMore/StakeMore.test.tsx rename to src/renderer/pages/Staking/Operations/StakeMore/StakeMore.test.tsx index 4ba16b00a6..9d60f303f2 100644 --- a/src/renderer/screens/Staking/Operations/StakeMore/StakeMore.test.tsx +++ b/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.test.tsx @@ -2,28 +2,25 @@ import { act, render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { ConnectionStatus } from '@renderer/domain/connection'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { StakeMore } from './StakeMore'; -jest.mock('@renderer/context/I18nContext', () => ({ - useI18n: jest.fn().mockReturnValue({ - t: (key: string) => key, - }), -})); - jest.mock('react-router-dom', () => ({ useSearchParams: jest.fn().mockReturnValue([new URLSearchParams('id=1,2,3')]), useParams: jest.fn().mockReturnValue({ chainId: '0x123' }), useNavigate: jest.fn(), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getActiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/context/NetworkContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ + useI18n: jest.fn().mockReturnValue({ + t: (key: string) => key, + }), useNetworkContext: jest.fn(() => ({ connections: { '0x123': { @@ -78,7 +75,7 @@ jest.mock( mockButton('to sign', onResult), ); -describe('screens/Staking/StakeMore', () => { +describe('pages/Staking/StakeMore', () => { test('should render component', async () => { await act(async () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/screens/Staking/Operations/StakeMore/StakeMore.tsx b/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/StakeMore/StakeMore.tsx rename to src/renderer/pages/Staking/Operations/StakeMore/StakeMore.tsx index a10e732dba..4817f17ceb 100644 --- a/src/renderer/screens/Staking/Operations/StakeMore/StakeMore.tsx +++ b/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.tsx @@ -2,25 +2,18 @@ import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { useState, useEffect } from 'react'; import { Navigate, useNavigate, useParams, useSearchParams } from 'react-router-dom'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; +import { useI18n, useNetworkContext, Paths } from '@renderer/app/providers'; import { ChainId, HexString, Address, AccountId } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; +import { Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; import InitOperation, { StakeMoreResult } from './InitOperation/InitOperation'; import { Confirmation, Signing, Submit, NoAsset } from '../components'; -import { getRelaychainAsset } from '@renderer/shared/utils/assets'; -import { useCountdown, useToggle } from '@renderer/shared/hooks'; -import { isMultisig, MultisigAccount, Account } from '@renderer/domain/account'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { toAddress } from '@renderer/shared/utils/address'; -import { Alert, BaseModal, Button } from '@renderer/components/ui-redesign'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; -import Paths from '@renderer/routes/paths'; -import OperationModalTitle from '@renderer/screens/Operations/components/OperationModalTitle'; -import { useAccount } from '@renderer/services/account/accountService'; +import { getRelaychainAsset, toAddress, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { isMultisig, MultisigAccount, Account, useAccount } from '@renderer/entities/account'; +import { Alert, BaseModal, Button, Loader } from '@renderer/shared/ui'; +import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; -import { Loader } from '@renderer/components/ui'; const enum Step { INIT, diff --git a/src/renderer/screens/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx similarity index 78% rename from src/renderer/screens/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx rename to src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx index f399a59ee7..91fbbb2511 100644 --- a/src/renderer/screens/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx @@ -2,32 +2,33 @@ import { ApiPromise } from '@polkadot/api'; import { act, render, screen } from '@testing-library/react'; import noop from 'lodash/noop'; -import { Asset } from '@renderer/domain/asset'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { Asset } from '@renderer/entities/asset'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import InitOperation from './InitOperation'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { Account } from '@renderer/domain/account'; +import { Account } from '@renderer/entities/account'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/staking/stakingDataService', () => ({ +jest.mock('@renderer/entities/staking', () => ({ useStakingData: jest.fn().mockReturnValue({ subscribeStaking: jest.fn(), getMinNominatorBond: jest.fn().mockResolvedValue('1000000000000'), }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ + ...jest.requireActual('@renderer/entities/account'), useAccount: jest.fn().mockReturnValue({ getLiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/services/balance/balanceService', () => ({ +jest.mock('@renderer/entities/asset', () => ({ useBalance: jest.fn().mockReturnValue({ getLiveAssetBalances: jest.fn().mockReturnValue([ { @@ -39,6 +40,7 @@ jest.mock('@renderer/services/balance/balanceService', () => ({ }, ]), }), + AssetBalance: () => balance, })); jest.mock('../../../Overview/components', () => ({ UnstakingDuration: () => 'unstaking_duration' })); @@ -53,7 +55,7 @@ jest.mock('../../components', () => ({ }, })); -describe('screens/Staking/Unstake/InitOperation', () => { +describe('pages/Staking/Unstake/InitOperation', () => { const defaultProps = { api: {} as ApiPromise, chainId: '0x123' as ChainId, diff --git a/src/renderer/screens/Staking/Operations/Unstake/InitOperation/InitOperation.tsx b/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/Unstake/InitOperation/InitOperation.tsx rename to src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.tsx index be914a1a9b..4d845eabc7 100644 --- a/src/renderer/screens/Staking/Operations/Unstake/InitOperation/InitOperation.tsx +++ b/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.tsx @@ -2,22 +2,16 @@ import { ApiPromise } from '@polkadot/api'; import { BN } from '@polkadot/util'; import { useEffect, useState } from 'react'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui/Dropdowns/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, useBalance, Balance as AccountBalance } from '@renderer/entities/asset'; import { ChainId, AccountId, SigningType } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import { useAccount } from '@renderer/services/account/accountService'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { formatAmount } from '@renderer/shared/utils/balance'; -import { StakingMap } from '@renderer/services/staking/common/types'; -import { nonNullable } from '@renderer/shared/utils/functions'; -import { Balance as AccountBalance } from '@renderer/domain/balance'; -import { OperationForm } from '@renderer/screens/Staking/Operations/components'; -import { isMultisig, Account } from '@renderer/domain/account'; -import { toAddress } from '@renderer/shared/utils/address'; -import { useStakingData } from '@renderer/services/staking/stakingDataService'; -import { Select, MultiSelect, InputHint } from '@renderer/components/ui-redesign'; +import { Transaction, TransactionType } from '@renderer/entities/transaction'; +import { useAccount, isMultisig, Account } from '@renderer/entities/account'; +import { formatAmount, nonNullable, toAddress } from '@renderer/shared/lib/utils'; +import { StakingMap, useStakingData } from '@renderer/entities/staking'; +import { OperationForm } from '@renderer/pages/Staking/Operations/components'; +import { Select, MultiSelect, InputHint } from '@renderer/shared/ui'; import { getUnstakeAccountOption, validateBalanceForFee, diff --git a/src/renderer/screens/Staking/Operations/Unstake/Unstake.test.tsx b/src/renderer/pages/Staking/Operations/Unstake/Unstake.test.tsx similarity index 91% rename from src/renderer/screens/Staking/Operations/Unstake/Unstake.test.tsx rename to src/renderer/pages/Staking/Operations/Unstake/Unstake.test.tsx index 863d4efc9c..37dec57c90 100644 --- a/src/renderer/screens/Staking/Operations/Unstake/Unstake.test.tsx +++ b/src/renderer/pages/Staking/Operations/Unstake/Unstake.test.tsx @@ -2,28 +2,25 @@ import { act, render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { ConnectionStatus } from '@renderer/domain/connection'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { Unstake } from './Unstake'; -jest.mock('@renderer/context/I18nContext', () => ({ - useI18n: jest.fn().mockReturnValue({ - t: (key: string) => key, - }), -})); - jest.mock('react-router-dom', () => ({ useSearchParams: jest.fn().mockReturnValue([new URLSearchParams('id=1,2,3')]), useParams: jest.fn().mockReturnValue({ chainId: '0x123' }), useNavigate: jest.fn(), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getActiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/context/NetworkContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ + useI18n: jest.fn().mockReturnValue({ + t: (key: string) => key, + }), useNetworkContext: jest.fn(() => ({ connections: { '0x123': { @@ -78,7 +75,7 @@ jest.mock( mockButton('to sign', onResult), ); -describe('screens/Staking/Unstake', () => { +describe('pages/Staking/Unstake', () => { test('should render component', async () => { await act(async () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/screens/Staking/Operations/Unstake/Unstake.tsx b/src/renderer/pages/Staking/Operations/Unstake/Unstake.tsx similarity index 90% rename from src/renderer/screens/Staking/Operations/Unstake/Unstake.tsx rename to src/renderer/pages/Staking/Operations/Unstake/Unstake.tsx index 3e42db24d7..8bb3462b24 100644 --- a/src/renderer/screens/Staking/Operations/Unstake/Unstake.tsx +++ b/src/renderer/pages/Staking/Operations/Unstake/Unstake.tsx @@ -2,26 +2,19 @@ import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { useEffect, useState } from 'react'; import { Navigate, useNavigate, useParams, useSearchParams } from 'react-router-dom'; -import { UnstakingDuration } from '@renderer/screens/Staking/Overview/components'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; +import { UnstakingDuration } from '@renderer/pages/Staking/Overview/components'; +import { useI18n, useNetworkContext, Paths } from '@renderer/app/providers'; import { Address, ChainId, HexString, AccountId } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/domain/transaction'; -import Paths from '@renderer/routes/paths'; -import { useAccount } from '@renderer/services/account/accountService'; +import { Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; +import { useAccount, Account, MultisigAccount, isMultisig } from '@renderer/entities/account'; import InitOperation, { UnstakeResult } from './InitOperation/InitOperation'; import { Confirmation, Signing, Submit, NoAsset } from '../components'; -import { toAddress } from '@renderer/shared/utils/address'; -import { getRelaychainAsset } from '@renderer/shared/utils/assets'; -import { useCountdown, useToggle } from '@renderer/shared/hooks'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { Account, MultisigAccount, isMultisig } from '@renderer/domain/account'; -import { Alert, BaseModal, Button } from '@renderer/components/ui-redesign'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; -import OperationModalTitle from '@renderer/screens/Operations/components/OperationModalTitle'; +import { toAddress, getRelaychainAsset, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { Alert, BaseModal, Button, Loader } from '@renderer/shared/ui'; +import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; -import { Loader } from '@renderer/components/ui'; const enum Step { INIT, diff --git a/src/renderer/screens/Staking/Operations/common/types.ts b/src/renderer/pages/Staking/Operations/common/types.ts similarity index 65% rename from src/renderer/screens/Staking/Operations/common/types.ts rename to src/renderer/pages/Staking/Operations/common/types.ts index c0f91798b0..3c33eb76db 100644 --- a/src/renderer/screens/Staking/Operations/common/types.ts +++ b/src/renderer/pages/Staking/Operations/common/types.ts @@ -1,5 +1,5 @@ import { Address } from '@renderer/domain/shared-kernel'; -import { RewardsDestination } from '@renderer/domain/stake'; +import { RewardsDestination } from '@renderer/entities/staking/model/stake'; export type DestinationType = { address?: Address; diff --git a/src/renderer/screens/Staking/Operations/common/utils.tsx b/src/renderer/pages/Staking/Operations/common/utils.tsx similarity index 91% rename from src/renderer/screens/Staking/Operations/common/utils.tsx rename to src/renderer/pages/Staking/Operations/common/utils.tsx index 8ff95c0169..46a36a0531 100644 --- a/src/renderer/screens/Staking/Operations/common/utils.tsx +++ b/src/renderer/pages/Staking/Operations/common/utils.tsx @@ -2,21 +2,19 @@ import { BN } from '@polkadot/util'; import cn from 'classnames'; import { ReactNode } from 'react'; -import { Account, MultisigAccount } from '@renderer/domain/account'; +import { Account, MultisigAccount, AccountAddress } from '@renderer/entities/account'; import { Address } from '@renderer/domain/shared-kernel'; -import { DropdownOption } from '@renderer/components/ui/Dropdowns/common/types'; -import { Balance as AccountBalance } from '@renderer/domain/balance'; -import { Asset } from '@renderer/domain/asset'; -import { toAddress } from '@renderer/shared/utils/address'; -import { Stake } from '@renderer/domain/stake'; -import { AccountAddress, BalanceNew } from '@renderer/components/common'; +import { DropdownOption } from '@renderer/shared/ui/Dropdowns/common/types'; +import { Balance as AccountBalance, Asset, AssetBalance } from '@renderer/entities/asset'; import { + toAddress, stakeableAmount, formatAmount, transferableAmount, unlockingAmount, redeemableAmount, -} from '@renderer/shared/utils/balance'; +} from '@renderer/shared/lib/utils'; +import { Stake } from '@renderer/entities/staking'; export const validateBalanceForFee = (balance: AccountBalance | string, fee: string): boolean => { const transferableBalance = typeof balance === 'string' ? balance : transferableAmount(balance); @@ -74,7 +72,7 @@ const getElement = (address: Address, accountName: string, content?: ReactNode): const getBalance = (balance: string, asset: Asset, isCorrect = true): ReactNode => { if (!balance) return null; - return ; + return ; }; type Params = { diff --git a/src/renderer/screens/Staking/Operations/components/Confirmation/Confirmation.test.tsx b/src/renderer/pages/Staking/Operations/components/Confirmation/Confirmation.test.tsx similarity index 70% rename from src/renderer/screens/Staking/Operations/components/Confirmation/Confirmation.test.tsx rename to src/renderer/pages/Staking/Operations/components/Confirmation/Confirmation.test.tsx index be3c3300e3..5e4c07b83a 100644 --- a/src/renderer/screens/Staking/Operations/components/Confirmation/Confirmation.test.tsx +++ b/src/renderer/pages/Staking/Operations/components/Confirmation/Confirmation.test.tsx @@ -1,29 +1,28 @@ import { ApiPromise } from '@polkadot/api'; import { render, screen } from '@testing-library/react'; -import { Asset } from '@renderer/domain/asset'; -import { Transaction } from '@renderer/domain/transaction'; -import { AccountDS } from '@renderer/services/storage'; +import { Asset } from '@renderer/entities/asset'; +import { Transaction } from '@renderer/entities/transaction'; +import { AccountDS } from '@renderer/shared/api/storage'; import { Confirmation } from './Confirmation'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/multisigTx/multisigTxService', () => ({ +jest.mock('@renderer/entities/multisig', () => ({ useMultisigTx: jest.fn().mockReturnValue({ getMultisigTxs: jest.fn().mockReturnValue([]), }), })); -jest.mock( - '@renderer/components/common/AddressWithExplorers/AddressWithExplorers', - jest.fn().mockReturnValue(({ address }: any) => {address}), -); +jest.mock('@renderer/entities/account', () => ({ + AddressWithExplorers: ({ address }: any) => {address}, +})); -describe('screens/Staking/components/Confirmation', () => { +describe('pages/Staking/components/Confirmation', () => { const spyResult = jest.fn(); const spyGoBack = jest.fn(); diff --git a/src/renderer/screens/Staking/Operations/components/Confirmation/Confirmation.tsx b/src/renderer/pages/Staking/Operations/components/Confirmation/Confirmation.tsx similarity index 88% rename from src/renderer/screens/Staking/Operations/components/Confirmation/Confirmation.tsx rename to src/renderer/pages/Staking/Operations/components/Confirmation/Confirmation.tsx index e28de3879e..21273de843 100644 --- a/src/renderer/screens/Staking/Operations/components/Confirmation/Confirmation.tsx +++ b/src/renderer/pages/Staking/Operations/components/Confirmation/Confirmation.tsx @@ -2,23 +2,20 @@ import { BN, BN_ZERO } from '@polkadot/util'; import { ApiPromise } from '@polkadot/api'; import { PropsWithChildren, useState, useEffect } from 'react'; -import { Icon } from '@renderer/components/ui'; -import { Button, FootnoteText, CaptionText, InputHint } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useToggle } from '@renderer/shared/hooks'; -import { BalanceNew, Fee, DepositWithLabel } from '@renderer/components/common'; -import { RewardsDestination } from '@renderer/domain/stake'; +import { Icon, Button, FootnoteText, CaptionText, InputHint } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { RewardsDestination } from '@renderer/entities/staking'; import { Validator } from '@renderer/domain/validator'; -import { Account } from '@renderer/domain/account'; -import { Asset } from '@renderer/domain/asset'; -import { Explorer } from '@renderer/domain/chain'; -import { Transaction, MultisigTxInitStatus } from '@renderer/domain/transaction'; -import AddressWithExplorers from '@renderer/components/common/AddressWithExplorers/AddressWithExplorers'; +import { Account, AddressWithExplorers } from '@renderer/entities/account'; +import { Asset, AssetBalance } from '@renderer/entities/asset'; +import { Explorer } from '@renderer/entities/chain'; +import { Transaction, MultisigTxInitStatus, DepositWithLabel, Fee } from '@renderer/entities/transaction'; import AccountsModal from '../Modals/AccountsModal/AccountsModal'; import ValidatorsModal from '../Modals/ValidatorsModal/ValidatorsModal'; import { DestinationType } from '../../common/types'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { useMultisigTx } from '@renderer/services/multisigTx/multisigTxService'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { useMultisigTx } from '@renderer/entities/multisig'; const ActionStyle = 'group hover:bg-action-background-hover px-2 py-1 rounded'; @@ -91,7 +88,7 @@ export const Confirmation = ({
    {amounts.length > 0 && ( - + )} {description && ( diff --git a/src/renderer/screens/Staking/Operations/components/Modals/AccountsModal/AccountsModal.test.tsx b/src/renderer/pages/Staking/Operations/components/Modals/AccountsModal/AccountsModal.test.tsx similarity index 81% rename from src/renderer/screens/Staking/Operations/components/Modals/AccountsModal/AccountsModal.test.tsx rename to src/renderer/pages/Staking/Operations/components/Modals/AccountsModal/AccountsModal.test.tsx index af4785bc58..83a04f308f 100644 --- a/src/renderer/screens/Staking/Operations/components/Modals/AccountsModal/AccountsModal.test.tsx +++ b/src/renderer/pages/Staking/Operations/components/Modals/AccountsModal/AccountsModal.test.tsx @@ -1,22 +1,21 @@ import { render, screen } from '@testing-library/react'; -import { Asset } from '@renderer/domain/asset'; +import { Asset } from '@renderer/entities/asset'; import { SigningType, ChainType, CryptoType } from '@renderer/domain/shared-kernel'; -import { Account } from '@renderer/domain/account'; +import { Account } from '@renderer/entities/account'; import AccountsModal from './AccountsModal'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock( - '@renderer/components/common/AddressWithExplorers/AddressWithExplorers', - jest.fn().mockReturnValue(({ address }: { address: string }) => {address}), -); +jest.mock('@renderer/entities/account', () => ({ + AddressWithExplorers: ({ address }: { address: string }) => {address}, +})); -describe('screens/Staking/components/AccountsModal', () => { +describe('pages/Staking/components/AccountsModal', () => { const defaultProps = { isOpen: true, amounts: ['1000000000000', '2000000000000', '3000000000000'], diff --git a/src/renderer/screens/Staking/Operations/components/Modals/AccountsModal/AccountsModal.tsx b/src/renderer/pages/Staking/Operations/components/Modals/AccountsModal/AccountsModal.tsx similarity index 72% rename from src/renderer/screens/Staking/Operations/components/Modals/AccountsModal/AccountsModal.tsx rename to src/renderer/pages/Staking/Operations/components/Modals/AccountsModal/AccountsModal.tsx index bb19fcf8a8..d743eb0ec8 100644 --- a/src/renderer/screens/Staking/Operations/components/Modals/AccountsModal/AccountsModal.tsx +++ b/src/renderer/pages/Staking/Operations/components/Modals/AccountsModal/AccountsModal.tsx @@ -1,10 +1,9 @@ -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; -import { Explorer } from '@renderer/domain/chain'; -import { Account } from '@renderer/domain/account'; -import { BaseModal } from '@renderer/components/ui-redesign'; -import AddressWithExplorers from '@renderer/components/common/AddressWithExplorers/AddressWithExplorers'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { useI18n } from '@renderer/app/providers'; +import { Asset } from '@renderer/entities/asset'; +import { Explorer } from '@renderer/entities/chain'; +import { Account, AddressWithExplorers } from '@renderer/entities/account'; +import { BaseModal } from '@renderer/shared/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { isOpen: boolean; diff --git a/src/renderer/screens/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.test.tsx b/src/renderer/pages/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.test.tsx similarity index 80% rename from src/renderer/screens/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.test.tsx rename to src/renderer/pages/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.test.tsx index 53eadf2a94..af910c7353 100644 --- a/src/renderer/screens/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.test.tsx +++ b/src/renderer/pages/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.test.tsx @@ -1,21 +1,20 @@ import { render, screen } from '@testing-library/react'; import { Validator } from '@renderer/domain/validator'; -import { Asset } from '@renderer/domain/asset'; +import { Asset } from '@renderer/entities/asset'; import ValidatorsModal from './ValidatorsModal'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock( - '@renderer/components/common/AddressWithExplorers/AddressWithExplorers', - jest.fn().mockReturnValue(({ address }: { address: string }) => {address}), -); +jest.mock('@renderer/entities/account', () => ({ + AddressWithExplorers: ({ address }: { address: string }) => {address}, +})); -describe('screens/Staking/components/ValidatorsModal', () => { +describe('pages/Staking/components/ValidatorsModal', () => { const defaultProps = { isOpen: true, amount: '1000000000000', diff --git a/src/renderer/screens/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.tsx b/src/renderer/pages/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.tsx similarity index 73% rename from src/renderer/screens/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.tsx rename to src/renderer/pages/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.tsx index b80b6ef653..2d4c000fe8 100644 --- a/src/renderer/screens/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.tsx +++ b/src/renderer/pages/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal.tsx @@ -1,10 +1,9 @@ -import { BaseModal } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Explorer } from '@renderer/domain/chain'; +import { BaseModal } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { Explorer } from '@renderer/entities/chain'; import { Validator } from '@renderer/domain/validator'; -import AddressWithExplorers from '@renderer/components/common/AddressWithExplorers/AddressWithExplorers'; -import { getComposedIdentity } from '@renderer/shared/utils/strings'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { getComposedIdentity, cnTw } from '@renderer/shared/lib/utils'; +import { AddressWithExplorers } from '@renderer/entities/account'; type Props = { isOpen: boolean; diff --git a/src/renderer/screens/Staking/Operations/components/NoAsset/NoAsset.tsx b/src/renderer/pages/Staking/Operations/components/NoAsset/NoAsset.tsx similarity index 79% rename from src/renderer/screens/Staking/Operations/components/NoAsset/NoAsset.tsx rename to src/renderer/pages/Staking/Operations/components/NoAsset/NoAsset.tsx index a302efb812..6c6098801b 100644 --- a/src/renderer/screens/Staking/Operations/components/NoAsset/NoAsset.tsx +++ b/src/renderer/pages/Staking/Operations/components/NoAsset/NoAsset.tsx @@ -1,6 +1,5 @@ -import { Icon } from '@renderer/components/ui'; -import { TitleText, BodyText, Button } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Icon, TitleText, BodyText, Button } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; type Props = { chainName: string; diff --git a/src/renderer/screens/Staking/Operations/components/OperationForm/OperationFooter.tsx b/src/renderer/pages/Staking/Operations/components/OperationForm/OperationFooter.tsx similarity index 87% rename from src/renderer/screens/Staking/Operations/components/OperationForm/OperationFooter.tsx rename to src/renderer/pages/Staking/Operations/components/OperationForm/OperationFooter.tsx index bf396d7b69..b53805896a 100644 --- a/src/renderer/screens/Staking/Operations/components/OperationForm/OperationFooter.tsx +++ b/src/renderer/pages/Staking/Operations/components/OperationForm/OperationFooter.tsx @@ -1,12 +1,10 @@ import { ApiPromise } from '@polkadot/api'; -import { Icon } from '@renderer/components/ui'; -import { FootnoteText, Tooltip } from '@renderer/components/ui-redesign'; -import { Deposit, Fee } from '@renderer/components/common'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; -import { Transaction } from '@renderer/domain/transaction'; -import { MultisigAccount, Account, isMultisig } from '@renderer/domain/account'; +import { Icon, FootnoteText, Tooltip } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { Asset } from '@renderer/entities/asset'; +import { Transaction, Deposit, Fee } from '@renderer/entities/transaction'; +import { MultisigAccount, Account, isMultisig } from '@renderer/entities/account'; type Props = { api: ApiPromise; diff --git a/src/renderer/screens/Staking/Operations/components/OperationForm/OperationForm.tsx b/src/renderer/pages/Staking/Operations/components/OperationForm/OperationForm.tsx similarity index 92% rename from src/renderer/screens/Staking/Operations/components/OperationForm/OperationForm.tsx rename to src/renderer/pages/Staking/Operations/components/OperationForm/OperationForm.tsx index 9c56ed1526..10b28aacb9 100644 --- a/src/renderer/screens/Staking/Operations/components/OperationForm/OperationForm.tsx +++ b/src/renderer/pages/Staking/Operations/components/OperationForm/OperationForm.tsx @@ -2,18 +2,15 @@ import { useForm, Controller, SubmitHandler } from 'react-hook-form'; import { useState, useEffect, ReactNode } from 'react'; import { Trans, TFunction } from 'react-i18next'; -import { Identicon } from '@renderer/components/ui'; -import { Button, AmountInput, InputHint, Combobox, RadioGroup, Input } from '@renderer/components/ui-redesign'; -import { useI18n } from '@renderer/context/I18nContext'; -import { RewardsDestination } from '@renderer/domain/stake'; -import { validateAddress } from '@renderer/shared/utils/address'; -import { Asset } from '@renderer/domain/asset'; +import { Identicon, Button, AmountInput, InputHint, Combobox, RadioGroup, Input } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { RewardsDestination } from '@renderer/entities/staking'; +import { validateAddress } from '@renderer/shared/lib/utils'; +import { Asset, useBalance } from '@renderer/entities/asset'; import { Address, ChainId, AccountId } from '@renderer/domain/shared-kernel'; -import { RadioOption } from '@renderer/components/ui-redesign/RadioGroup/common/types'; -import { DropdownOption } from '@renderer/components/ui/Dropdowns/common/types'; -import { useAccount } from '@renderer/services/account/accountService'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { ComboboxOption } from '@renderer/components/ui-redesign/Dropdowns/common/types'; +import { RadioOption } from '@renderer/shared/ui/RadioGroup/common/types'; +import { DropdownOption, ComboboxOption } from '@renderer/shared/ui/Dropdowns/common/types'; +import { useAccount } from '@renderer/entities/account'; import { getPayoutAccountOption } from '../../common/utils'; import OperationFooter from './OperationFooter'; diff --git a/src/renderer/screens/Staking/Operations/components/Signing/Signing.tsx b/src/renderer/pages/Staking/Operations/components/Signing/Signing.tsx similarity index 93% rename from src/renderer/screens/Staking/Operations/components/Signing/Signing.tsx rename to src/renderer/pages/Staking/Operations/components/Signing/Signing.tsx index af054aa069..c28dc93786 100644 --- a/src/renderer/screens/Staking/Operations/components/Signing/Signing.tsx +++ b/src/renderer/pages/Staking/Operations/components/Signing/Signing.tsx @@ -2,8 +2,8 @@ import { useEffect, useState } from 'react'; import QrReaderWrapper from '@renderer/components/common/QrCode/QrReader/QrReaderWrapper'; import { AccountId, HexString } from '@renderer/domain/shared-kernel'; -import { ValidationErrors } from '@renderer/shared/utils/validation'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; +import { ValidationErrors } from '@renderer/shared/lib/utils'; +import { useTransaction } from '@renderer/entities/transaction'; type Props = { multiQr: boolean; diff --git a/src/renderer/screens/Staking/Operations/components/Submit/Submit.tsx b/src/renderer/pages/Staking/Operations/components/Submit/Submit.tsx similarity index 84% rename from src/renderer/screens/Staking/Operations/components/Submit/Submit.tsx rename to src/renderer/pages/Staking/Operations/components/Submit/Submit.tsx index 9793d9840c..ba8c5727a7 100644 --- a/src/renderer/screens/Staking/Operations/components/Submit/Submit.tsx +++ b/src/renderer/pages/Staking/Operations/components/Submit/Submit.tsx @@ -3,22 +3,22 @@ import { useEffect, useState, ComponentProps } from 'react'; import { ApiPromise } from '@polkadot/api'; import { useNavigate } from 'react-router-dom'; -import { useI18n } from '@renderer/context/I18nContext'; +import { useI18n, useMatrix, useMultisigChainContext, Paths } from '@renderer/app/providers'; import { HexString } from '@renderer/domain/shared-kernel'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { ExtrinsicResultParams } from '@renderer/services/transaction/common/types'; -import { isMultisig, Account, MultisigAccount } from '@renderer/domain/account'; -import { toAccountId } from '@renderer/shared/utils/address'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { Button } from '@renderer/components/ui-redesign'; -import { OperationResult } from '@renderer/components/common/OperationResult/OperationResult'; -import { useToggle } from '@renderer/shared/hooks'; -import { useMultisigTx } from '@renderer/services/multisigTx/multisigTxService'; -import { DEFAULT_TRANSITION } from '@renderer/shared/utils/constants'; -import Paths from '@renderer/routes/paths'; -import { Transaction, MultisigEvent, MultisigTransaction, MultisigTxInitStatus } from '@renderer/domain/transaction'; -import { useMultisigEvent } from '@renderer/services/multisigEvent/multisigEventService'; -import { useMultisigChainContext } from '@renderer/context/MultisigChainContext'; +import { + useTransaction, + ExtrinsicResultParams, + OperationResult, + Transaction, + MultisigEvent, + MultisigTransaction, + MultisigTxInitStatus, +} from '@renderer/entities/transaction'; +import { isMultisig, Account, MultisigAccount } from '@renderer/entities/account'; +import { toAccountId, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { Button } from '@renderer/shared/ui'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { useMultisigTx, useMultisigEvent } from '@renderer/entities/multisig'; type ResultProps = Pick, 'title' | 'description' | 'variant'>; diff --git a/src/renderer/screens/Staking/Operations/components/Validators/Validators.test.tsx b/src/renderer/pages/Staking/Operations/components/Validators/Validators.test.tsx similarity index 84% rename from src/renderer/screens/Staking/Operations/components/Validators/Validators.test.tsx rename to src/renderer/pages/Staking/Operations/components/Validators/Validators.test.tsx index cd876a9f89..5dd0b5eb2f 100644 --- a/src/renderer/screens/Staking/Operations/components/Validators/Validators.test.tsx +++ b/src/renderer/pages/Staking/Operations/components/Validators/Validators.test.tsx @@ -2,18 +2,18 @@ import { ApiPromise } from '@polkadot/api'; import { act, render, screen } from '@testing-library/react'; import noop from 'lodash/noop'; -import { Asset } from '@renderer/domain/asset'; +import { Asset } from '@renderer/entities/asset'; import { Validators } from './Validators'; jest.mock('@renderer/components/common'); -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/staking/validatorsService', () => ({ +jest.mock('@renderer/entities/staking', () => ({ useValidators: jest.fn().mockReturnValue({ getMaxValidators: jest.fn().mockReturnValue(6), getValidatorsWithInfo: jest.fn().mockResolvedValue({ @@ -25,15 +25,12 @@ jest.mock('@renderer/services/staking/validatorsService', () => ({ }, }), }), -})); - -jest.mock('@renderer/services/staking/eraService', () => ({ useEra: jest.fn().mockReturnValue({ subscribeActiveEra: jest.fn().mockImplementation((api: any, eraCallback: any) => eraCallback(1)), }), })); -describe('screens/Staking/components/Validators', () => { +describe('pages/Staking/components/Validators', () => { const api = { isConnected: true } as ApiPromise; const asset = { assetId: 0, diff --git a/src/renderer/screens/Staking/Operations/components/Validators/Validators.tsx b/src/renderer/pages/Staking/Operations/components/Validators/Validators.tsx similarity index 88% rename from src/renderer/screens/Staking/Operations/components/Validators/Validators.tsx rename to src/renderer/pages/Staking/Operations/components/Validators/Validators.tsx index 421bcccd42..7a63f6cb4e 100644 --- a/src/renderer/screens/Staking/Operations/components/Validators/Validators.tsx +++ b/src/renderer/pages/Staking/Operations/components/Validators/Validators.tsx @@ -2,18 +2,11 @@ import { ApiPromise } from '@polkadot/api'; import { useEffect, useState } from 'react'; import { mapValues } from 'lodash'; -import { Icon, Identicon, Shimmering, Loader } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; -import { Explorer } from '@renderer/domain/chain'; -import { Address, ChainId } from '@renderer/domain/shared-kernel'; -import { ValidatorMap } from '@renderer/services/staking/common/types'; -import { useEra } from '@renderer/services/staking/eraService'; -import { useValidators } from '@renderer/services/staking/validatorsService'; -import { includes, getComposedIdentity } from '@renderer/shared/utils/strings'; -import { toShortAddress } from '@renderer/shared/utils/address'; -import { ExplorerLink, BalanceNew } from '@renderer/components/common'; import { + Icon, + Identicon, + Shimmering, + Loader, BodyText, InfoPopover, FootnoteText, @@ -21,7 +14,14 @@ import { SearchInput, SmallTitleText, Checkbox, -} from '@renderer/components/ui-redesign'; +} from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, AssetBalance } from '@renderer/entities/asset'; +import { Explorer } from '@renderer/entities/chain'; +import { Address, ChainId } from '@renderer/domain/shared-kernel'; +import { ValidatorMap, useEra, useValidators } from '@renderer/entities/staking'; +import { includes, getComposedIdentity, toShortAddress } from '@renderer/shared/lib/utils'; +import { ExplorerLink } from '@renderer/components/common'; type Props = { api: ApiPromise; @@ -166,10 +166,10 @@ export const Validators = ({ api, chainId, asset, explorers, isLightClient, onGo
    - + - + diff --git a/src/renderer/screens/Staking/Operations/components/index.ts b/src/renderer/pages/Staking/Operations/components/index.ts similarity index 100% rename from src/renderer/screens/Staking/Operations/components/index.ts rename to src/renderer/pages/Staking/Operations/components/index.ts diff --git a/src/renderer/screens/Staking/Overview/Overview.test.tsx b/src/renderer/pages/Staking/Overview/Overview.test.tsx similarity index 79% rename from src/renderer/screens/Staking/Overview/Overview.test.tsx rename to src/renderer/pages/Staking/Overview/Overview.test.tsx index 680334be86..c46161390e 100644 --- a/src/renderer/screens/Staking/Overview/Overview.test.tsx +++ b/src/renderer/pages/Staking/Overview/Overview.test.tsx @@ -1,31 +1,25 @@ import { act, render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { Chain } from '@renderer/domain/chain'; +import { useNetworkContext } from '@renderer/app/providers'; +import { Chain } from '@renderer/entities/chain'; import { ConnectionType } from '@renderer/domain/connection'; -import { TEST_ACCOUNT_ID } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { Overview } from './Overview'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), -})); - -jest.mock('@renderer/context/NetworkContext', () => ({ useNetworkContext: jest.fn(() => ({ connections: {}, })), -})); - -jest.mock('@renderer/context/GraphqlContext', () => ({ useGraphql: jest.fn(() => ({ changeClient: jest.fn(), })), })); -jest.mock('@renderer/services/network/chainsService', () => ({ +jest.mock('@renderer/entities/network', () => ({ useChains: jest.fn().mockReturnValue({ sortChains: (value: Chain[]) => value, getChainsData: jest.fn().mockResolvedValue([ @@ -39,36 +33,27 @@ jest.mock('@renderer/services/network/chainsService', () => ({ }), })); -jest.mock('@renderer/services/account/accountService', () => ({ +jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ getActiveAccounts: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }], }), })); -jest.mock('@renderer/services/staking/stakingDataService', () => ({ - useStakingData: jest.fn().mockReturnValue({ - staking: {}, - subscribeLedger: jest.fn(), - }), -})); - -jest.mock('@renderer/services/staking/validatorsService', () => ({ +jest.mock('@renderer/entities/staking', () => ({ useValidators: jest.fn().mockReturnValue({ getValidatorsWithInfo: jest.fn(), }), -})); - -jest.mock('@renderer/services/staking/eraService', () => ({ useEra: jest.fn().mockReturnValue({ subscribeActiveEra: jest.fn(), }), -})); - -jest.mock('@renderer/services/staking/stakingRewardsService', () => ({ useStakingRewards: jest.fn().mockReturnValue({ rewards: {}, isLoading: false, }), + useStakingData: jest.fn().mockReturnValue({ + staking: {}, + subscribeLedger: jest.fn(), + }), })); jest.mock('./components', () => ({ @@ -84,7 +69,7 @@ jest.mock('./components', () => ({ ), })); -describe('screens/Staking/Overview', () => { +describe('pages/Staking/Overview', () => { beforeEach(() => { (useNetworkContext as jest.Mock).mockImplementation(() => ({ connections: { '0x00': { connection: { connectionType: ConnectionType.LIGHT_CLIENT } } }, diff --git a/src/renderer/screens/Staking/Overview/Overview.tsx b/src/renderer/pages/Staking/Overview/Overview.tsx similarity index 87% rename from src/renderer/screens/Staking/Overview/Overview.tsx rename to src/renderer/pages/Staking/Overview/Overview.tsx index 22e629e319..17b1731afc 100644 --- a/src/renderer/screens/Staking/Overview/Overview.tsx +++ b/src/renderer/pages/Staking/Overview/Overview.tsx @@ -2,25 +2,23 @@ import { useState, useEffect } from 'react'; import { useNavigate, Outlet } from 'react-router-dom'; import { Header } from '@renderer/components/common'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Chain } from '@renderer/domain/chain'; -import { getRelaychainAsset } from '@renderer/shared/utils/assets'; -import { useGraphql } from '@renderer/context/GraphqlContext'; +import { Chain } from '@renderer/entities/chain'; +import { getRelaychainAsset, toAddress } from '@renderer/shared/lib/utils'; +import { useGraphql, useI18n, useNetworkContext, PathValue, createLink } from '@renderer/app/providers'; import { ChainId, Address, SigningType } from '@renderer/domain/shared-kernel'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { useEra } from '@renderer/services/staking/eraService'; -import { useStakingData } from '@renderer/services/staking/stakingDataService'; -import { toAddress } from '@renderer/shared/utils/address'; -import { useAccount } from '@renderer/services/account/accountService'; -import { StakingMap, ValidatorMap } from '@renderer/services/staking/common/types'; -import { useToggle } from '@renderer/shared/hooks'; -import { useValidators } from '@renderer/services/staking/validatorsService'; -import { useStakingRewards } from '@renderer/services/staking/stakingRewardsService'; -import { NominatorInfo } from '@renderer/screens/Staking/Overview/components/NominatorsList/NominatorsList'; -import { Stake } from '@renderer/domain/stake'; -import { PathValue } from '@renderer/routes/paths'; -import { createLink } from '@renderer/routes/utils'; -import { AccountDS } from '@renderer/services/storage'; +import { + useEra, + useStakingData, + StakingMap, + ValidatorMap, + useValidators, + useStakingRewards, + Stake, +} from '@renderer/entities/staking'; +import { useAccount } from '@renderer/entities/account'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { NominatorInfo } from '@renderer/pages/Staking/Overview/components/NominatorsList/NominatorsList'; +import { AccountDS } from '@renderer/shared/api/storage'; import { ConnectionType, ConnectionStatus } from '@renderer/domain/connection'; import { AboutStaking, NetworkInfo, NominatorsList, Actions, ValidatorsModal, InactiveChain } from './components'; diff --git a/src/renderer/screens/Staking/Overview/components/AboutStaking/AboutStaking.test.tsx b/src/renderer/pages/Staking/Overview/components/AboutStaking/AboutStaking.test.tsx similarity index 86% rename from src/renderer/screens/Staking/Overview/components/AboutStaking/AboutStaking.test.tsx rename to src/renderer/pages/Staking/Overview/components/AboutStaking/AboutStaking.test.tsx index 97572a7396..73e88a8090 100644 --- a/src/renderer/screens/Staking/Overview/components/AboutStaking/AboutStaking.test.tsx +++ b/src/renderer/pages/Staking/Overview/components/AboutStaking/AboutStaking.test.tsx @@ -1,23 +1,20 @@ import { render, screen, act } from '@testing-library/react'; import { ApiPromise } from '@polkadot/api'; -import { Asset } from '@renderer/domain/asset'; +import { Asset } from '@renderer/entities/asset'; import { Validator } from '@renderer/domain/validator'; import { AboutStaking } from './AboutStaking'; jest.mock('react-i18next', () => ({ Trans: (props: any) => props.i18nKey })); -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string, params?: any) => `${key} ${params?.value || ''}`, }), })); -jest.mock('@renderer/services/staking/apyCalculator', () => ({ +jest.mock('@renderer/entities/staking', () => ({ getAvgApy: jest.fn().mockResolvedValue('3'), -})); - -jest.mock('@renderer/services/staking/stakingDataService', () => ({ useStakingData: jest.fn().mockReturnValue({ getMinNominatorBond: jest.fn().mockResolvedValue('1000000000000'), getUnbondingPeriod: jest.fn().mockReturnValue('43200'), @@ -25,7 +22,7 @@ jest.mock('@renderer/services/staking/stakingDataService', () => ({ }), })); -describe('screens/Staking/Overview/AboutStaking', () => { +describe('pages/Staking/Overview/AboutStaking', () => { const api = { isConnected: true } as ApiPromise; const asset = { symbol: 'WND', precision: 12 } as Asset; const validators = [ @@ -65,8 +62,8 @@ describe('screens/Staking/Overview/AboutStaking', () => { render(); }); - const totalStaked = screen.getByText(/assetBalance.number 1.42M/); - const minimumStake = screen.getByText('assetBalance.number 1'); + const totalStaked = screen.getByText(/assetBalance.number 1.42\sM/); + const minimumStake = screen.getAllByText(/assetBalance.number 1/)[0]; // const activeNominators = screen.getByText('2'); const stakingPeriod = screen.getByText('staking.about.unlimitedLabel'); const unstakingPeriod = screen.getByText('time.hours'); diff --git a/src/renderer/screens/Staking/Overview/components/AboutStaking/AboutStaking.tsx b/src/renderer/pages/Staking/Overview/components/AboutStaking/AboutStaking.tsx similarity index 88% rename from src/renderer/screens/Staking/Overview/components/AboutStaking/AboutStaking.tsx rename to src/renderer/pages/Staking/Overview/components/AboutStaking/AboutStaking.tsx index 93d846e0c6..1f1778f20c 100644 --- a/src/renderer/screens/Staking/Overview/components/AboutStaking/AboutStaking.tsx +++ b/src/renderer/pages/Staking/Overview/components/AboutStaking/AboutStaking.tsx @@ -2,13 +2,12 @@ import { ApiPromise } from '@polkadot/api'; import { useEffect, useState } from 'react'; import { Trans } from 'react-i18next'; -import { Balance, Duration, Shimmering } from '@renderer/components/ui'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; +import { Duration, Shimmering, FootnoteText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, AssetBalance } from '@renderer/entities/asset'; import { EraIndex } from '@renderer/domain/shared-kernel'; import { Validator } from '@renderer/domain/validator'; -import { useStakingData } from '@renderer/services/staking/stakingDataService'; -import { FootnoteText } from '@renderer/components/ui-redesign'; +import { useStakingData } from '@renderer/entities/staking'; type Props = { api?: ApiPromise; @@ -105,10 +104,8 @@ export const AboutStaking = ({ api, era, asset, validators }: Props) => {
    {t('staking.about.totalStakedLabel')} - {totalStaked ? ( - - - + {totalStaked && asset ? ( + ) : ( )} @@ -116,10 +113,8 @@ export const AboutStaking = ({ api, era, asset, validators }: Props) => {
    {t('staking.about.minimumStakeLabel')} - {minimumStake ? ( - - - + {minimumStake && asset ? ( + ) : ( )} diff --git a/src/renderer/screens/Staking/Overview/components/Actions/Actions.test.tsx b/src/renderer/pages/Staking/Overview/components/Actions/Actions.test.tsx similarity index 92% rename from src/renderer/screens/Staking/Overview/components/Actions/Actions.test.tsx rename to src/renderer/pages/Staking/Overview/components/Actions/Actions.test.tsx index ea9505a1ed..e09cdc56a3 100644 --- a/src/renderer/screens/Staking/Overview/components/Actions/Actions.test.tsx +++ b/src/renderer/pages/Staking/Overview/components/Actions/Actions.test.tsx @@ -1,17 +1,17 @@ import { render, screen, act } from '@testing-library/react'; import noop from 'lodash/noop'; -import { Stake } from '@renderer/domain/stake'; -import { TEST_ADDRESS } from '@renderer/shared/utils/constants'; +import { Stake } from '@renderer/entities/staking'; +import { TEST_ADDRESS } from '@renderer/shared/lib/utils'; import { Actions } from './Actions'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -describe('screens/Staking/Overview/Actions', () => { +describe('pages/Staking/Overview/Actions', () => { const stakes: Stake[] = [ { active: '1660129379444', diff --git a/src/renderer/screens/Staking/Overview/components/Actions/Actions.tsx b/src/renderer/pages/Staking/Overview/components/Actions/Actions.tsx similarity index 92% rename from src/renderer/screens/Staking/Overview/components/Actions/Actions.tsx rename to src/renderer/pages/Staking/Overview/components/Actions/Actions.tsx index 65472c54b6..9a3ebecf5e 100644 --- a/src/renderer/screens/Staking/Overview/components/Actions/Actions.tsx +++ b/src/renderer/pages/Staking/Overview/components/Actions/Actions.tsx @@ -1,16 +1,15 @@ import { useState } from 'react'; import { Trans } from 'react-i18next'; -import Paths, { PathValue } from '@renderer/routes/paths'; -import { useI18n } from '@renderer/context/I18nContext'; -import { SmallTitleText, DropdownButton, Button, BaseModal } from '@renderer/components/ui-redesign'; -import { Stake } from '@renderer/domain/stake'; -import { toAccountId } from '@renderer/shared/utils/address'; -import { useToggle } from '@renderer/shared/hooks'; -import { Icon } from '@renderer/components/ui'; -import { ButtonDropdownOption } from '@renderer/components/ui-redesign/Dropdowns/DropdownButton/DropdownButton'; +import { useI18n, PathValue } from '@renderer/app/providers'; +import { Paths } from '../../../../../app/providers/routes/paths'; +import { SmallTitleText, DropdownButton, Button, BaseModal, Icon } from '@renderer/shared/ui'; +import { Stake } from '@renderer/entities/staking'; +import { toAccountId } from '@renderer/shared/lib/utils'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { ButtonDropdownOption } from '@renderer/shared/ui/types'; import { Address } from '@renderer/domain/shared-kernel'; -import { IconNames } from '@renderer/components/ui/Icon/data'; +import { IconNames } from '@renderer/shared/ui/Icon/data'; const enum AccountTypes { STASH = 'stash', diff --git a/src/renderer/screens/Staking/Overview/components/EmptyState/InactiveChain.test.tsx b/src/renderer/pages/Staking/Overview/components/EmptyState/InactiveChain.test.tsx similarity index 82% rename from src/renderer/screens/Staking/Overview/components/EmptyState/InactiveChain.test.tsx rename to src/renderer/pages/Staking/Overview/components/EmptyState/InactiveChain.test.tsx index 1702a10bef..4116a30fd7 100644 --- a/src/renderer/screens/Staking/Overview/components/EmptyState/InactiveChain.test.tsx +++ b/src/renderer/pages/Staking/Overview/components/EmptyState/InactiveChain.test.tsx @@ -1,16 +1,16 @@ import { render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; -import Paths from '@renderer/routes/paths'; +import { Paths } from '../../../../../app/providers/routes/paths'; import { InactiveChain } from './InactiveChain'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -describe('screens/Staking/Overview/InactiveChain', () => { +describe('pages/Staking/Overview/InactiveChain', () => { test('should render component', () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/screens/Staking/Overview/components/EmptyState/InactiveChain.tsx b/src/renderer/pages/Staking/Overview/components/EmptyState/InactiveChain.tsx similarity index 82% rename from src/renderer/screens/Staking/Overview/components/EmptyState/InactiveChain.tsx rename to src/renderer/pages/Staking/Overview/components/EmptyState/InactiveChain.tsx index 6f4f0c18c9..2787e376b8 100644 --- a/src/renderer/screens/Staking/Overview/components/EmptyState/InactiveChain.tsx +++ b/src/renderer/pages/Staking/Overview/components/EmptyState/InactiveChain.tsx @@ -1,9 +1,9 @@ import cn from 'classnames'; import NoConnection from '@images/misc/no-connection.webp'; -import { useI18n } from '@renderer/context/I18nContext'; -import { FootnoteText, ButtonLink } from '@renderer/components/ui-redesign'; -import Paths from '@renderer/routes/paths'; +import { useI18n } from '@renderer/app/providers'; +import { Paths } from '../../../../../app/providers/routes/paths'; +import { FootnoteText, ButtonLink } from '@renderer/shared/ui'; type Props = { className?: string; diff --git a/src/renderer/screens/Staking/Overview/components/EmptyState/NoValidators.test.tsx b/src/renderer/pages/Staking/Overview/components/EmptyState/NoValidators.test.tsx similarity index 77% rename from src/renderer/screens/Staking/Overview/components/EmptyState/NoValidators.test.tsx rename to src/renderer/pages/Staking/Overview/components/EmptyState/NoValidators.test.tsx index 550841d5d6..8dbb039015 100644 --- a/src/renderer/screens/Staking/Overview/components/EmptyState/NoValidators.test.tsx +++ b/src/renderer/pages/Staking/Overview/components/EmptyState/NoValidators.test.tsx @@ -2,13 +2,13 @@ import { render, screen } from '@testing-library/react'; import { NoValidators } from './NoValidators'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -describe('screens/Staking/Overview/NoValidators', () => { +describe('pages/Staking/Overview/NoValidators', () => { test('should render component', () => { render(); diff --git a/src/renderer/screens/Staking/Overview/components/EmptyState/NoValidators.tsx b/src/renderer/pages/Staking/Overview/components/EmptyState/NoValidators.tsx similarity index 68% rename from src/renderer/screens/Staking/Overview/components/EmptyState/NoValidators.tsx rename to src/renderer/pages/Staking/Overview/components/EmptyState/NoValidators.tsx index 8a7998a25f..033d3d8718 100644 --- a/src/renderer/screens/Staking/Overview/components/EmptyState/NoValidators.tsx +++ b/src/renderer/pages/Staking/Overview/components/EmptyState/NoValidators.tsx @@ -1,7 +1,6 @@ -import { useI18n } from '@renderer/context/I18nContext'; -import { BodyText } from '@renderer/components/ui-redesign'; -import { Icon } from '@renderer/components/ui'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { useI18n } from '@renderer/app/providers'; +import { BodyText, Icon } from '@renderer/shared/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { className?: string; diff --git a/src/renderer/screens/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx similarity index 90% rename from src/renderer/screens/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx rename to src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx index 1c14934fbf..748d145d39 100644 --- a/src/renderer/screens/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx +++ b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx @@ -1,17 +1,17 @@ import { render, screen, act } from '@testing-library/react'; import noop from 'lodash/noop'; -import { Chain } from '@renderer/domain/chain'; +import { Chain } from '@renderer/entities/chain'; import { NetworkInfo } from './NetworkInfo'; -import { useSettingsStorage } from '@renderer/services/settings/settingsStorage'; +import { useSettingsStorage } from '@renderer/entities/settings'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string, params?: any) => `${key} ${params?.value || ''}`, }), })); -jest.mock('@renderer/services/network/chainsService', () => ({ +jest.mock('@renderer/entities/network', () => ({ useChains: jest.fn().mockReturnValue({ sortChains: jest.fn((value: Chain[]) => value), getChainsData: jest.fn().mockResolvedValue([ @@ -31,14 +31,14 @@ jest.mock('@renderer/services/network/chainsService', () => ({ }), })); -jest.mock('@renderer/services/settings/settingsStorage', () => ({ +jest.mock('@renderer/entities/settings', () => ({ useSettingsStorage: jest.fn().mockReturnValue({ getStakingNetwork: jest.fn().mockReturnValue('0x123'), setStakingNetwork: jest.fn(), }), })); -describe('screens/Staking/Overview/NetworkInfo', () => { +describe('pages/Staking/Overview/NetworkInfo', () => { const defaultProps = { isRewardsLoading: false, isStakingLoading: false, diff --git a/src/renderer/screens/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx similarity index 80% rename from src/renderer/screens/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx rename to src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx index c4a4010f29..31c3deca7e 100644 --- a/src/renderer/screens/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx +++ b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx @@ -1,22 +1,15 @@ import { PropsWithChildren, useEffect, useState } from 'react'; import { BN, BN_ZERO } from '@polkadot/util'; -import { - Select, - FootnoteText, - Plate, - IconButton, - SmallTitleText, - Chain as ChainComponent, -} from '@renderer/components/ui-redesign'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui-redesign/Dropdowns/common/types'; -import { getRelaychainAsset } from '@renderer/shared/utils/assets'; -import { useChains } from '@renderer/services/network/chainsService'; -import { useSettingsStorage } from '@renderer/services/settings/settingsStorage'; -import { Chain } from '@renderer/domain/chain'; -import { useToggle } from '@renderer/shared/hooks'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Shimmering, Balance } from '@renderer/components/ui'; +import { Select, FootnoteText, Plate, IconButton, Shimmering } from '@renderer/shared/ui'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/types'; +import { getRelaychainAsset } from '@renderer/shared/lib/utils'; +import { useChains } from '@renderer/entities/network'; +import { useSettingsStorage } from '@renderer/entities/settings'; +import { Chain, ChainTitle } from '@renderer/entities/chain'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { useI18n } from '@renderer/app/providers'; +import { AssetBalance } from '@renderer/entities/asset'; const getTotal = (values: string[]): BN => { return values.reduce((acc, value) => acc.add(new BN(value || 0)), BN_ZERO); @@ -55,7 +48,7 @@ export const NetworkInfo = ({ // without key dropdown doesn't show changes (thought functionally everything works fine) // TODO look into it const element = ( - {title} - - - +
    ), )} diff --git a/src/renderer/screens/Staking/Overview/components/NominatorsList/NominatorsList.test.tsx b/src/renderer/pages/Staking/Overview/components/NominatorsList/NominatorsList.test.tsx similarity index 87% rename from src/renderer/screens/Staking/Overview/components/NominatorsList/NominatorsList.test.tsx rename to src/renderer/pages/Staking/Overview/components/NominatorsList/NominatorsList.test.tsx index d11559855d..939e67d5ef 100644 --- a/src/renderer/screens/Staking/Overview/components/NominatorsList/NominatorsList.test.tsx +++ b/src/renderer/pages/Staking/Overview/components/NominatorsList/NominatorsList.test.tsx @@ -3,13 +3,13 @@ import noop from 'lodash/noop'; import { NominatorsList } from './NominatorsList'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -describe('screens/Staking/Overview/NominatorsList', () => { +describe('pages/Staking/Overview/NominatorsList', () => { test('should render component', () => { render( , diff --git a/src/renderer/screens/Staking/Overview/components/NominatorsList/NominatorsList.tsx b/src/renderer/pages/Staking/Overview/components/NominatorsList/NominatorsList.tsx similarity index 85% rename from src/renderer/screens/Staking/Overview/components/NominatorsList/NominatorsList.tsx rename to src/renderer/pages/Staking/Overview/components/NominatorsList/NominatorsList.tsx index 645e9acd33..dafe54ff49 100644 --- a/src/renderer/screens/Staking/Overview/components/NominatorsList/NominatorsList.tsx +++ b/src/renderer/pages/Staking/Overview/components/NominatorsList/NominatorsList.tsx @@ -2,16 +2,15 @@ import { ApiPromise } from '@polkadot/api'; import { Trans } from 'react-i18next'; import { Address, SigningType, EraIndex } from '@renderer/domain/shared-kernel'; -import { Unlocking } from '@renderer/domain/stake'; -import { useI18n } from '@renderer/context/I18nContext'; -import { FootnoteText, Plate, BodyText, Checkbox, InfoPopover, Tooltip } from '@renderer/components/ui-redesign'; -import { AccountAddress, ExplorerLink } from '@renderer/components/common'; -import { Icon, Shimmering, Balance } from '@renderer/components/ui'; -import { Explorer } from '@renderer/domain/chain'; -import { Asset } from '@renderer/domain/asset'; +import { Unlocking } from '@renderer/entities/staking'; +import { useI18n } from '@renderer/app/providers'; +import { FootnoteText, Plate, Checkbox, InfoPopover, Tooltip, Icon, Shimmering, HelpText } from '@renderer/shared/ui'; +import { ExplorerLink } from '@renderer/components/common'; +import { Explorer } from '@renderer/entities/chain'; +import { Asset, AssetBalance } from '@renderer/entities/asset'; import { TimeToEra } from '../TimeToEra/TimeToEra'; -import { redeemableAmount } from '@renderer/shared/utils/balance'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; +import { redeemableAmount } from '@renderer/shared/lib/utils'; +import { AccountAddress } from '@renderer/entities/account'; const getNextUnstakingEra = (unlocking: Unlocking[] = [], era?: number): EraIndex | undefined => { if (!era) return undefined; @@ -139,16 +138,12 @@ export const NominatorsList = ({ {!stake.totalStake || !asset ? ( ) : ( - - - + )} {!stake.totalReward || !asset ? ( ) : ( - - - + )} diff --git a/src/renderer/screens/Staking/Overview/components/TimeToEra/TimeToEra.tsx b/src/renderer/pages/Staking/Overview/components/TimeToEra/TimeToEra.tsx similarity index 86% rename from src/renderer/screens/Staking/Overview/components/TimeToEra/TimeToEra.tsx rename to src/renderer/pages/Staking/Overview/components/TimeToEra/TimeToEra.tsx index b71038aeca..2887631475 100644 --- a/src/renderer/screens/Staking/Overview/components/TimeToEra/TimeToEra.tsx +++ b/src/renderer/pages/Staking/Overview/components/TimeToEra/TimeToEra.tsx @@ -1,8 +1,8 @@ import { ApiPromise } from '@polkadot/api'; import { useEffect, useState } from 'react'; -import { Duration, Shimmering } from '@renderer/components/ui'; -import { useEra } from '@renderer/services/staking/eraService'; +import { Duration, Shimmering } from '@renderer/shared/ui'; +import { useEra } from '@renderer/entities/staking'; interface Props { api?: ApiPromise; diff --git a/src/renderer/screens/Staking/Overview/components/UnstakingDuration/UnstakingDuration.tsx b/src/renderer/pages/Staking/Overview/components/UnstakingDuration/UnstakingDuration.tsx similarity index 81% rename from src/renderer/screens/Staking/Overview/components/UnstakingDuration/UnstakingDuration.tsx rename to src/renderer/pages/Staking/Overview/components/UnstakingDuration/UnstakingDuration.tsx index c3597673a7..6780074f82 100644 --- a/src/renderer/screens/Staking/Overview/components/UnstakingDuration/UnstakingDuration.tsx +++ b/src/renderer/pages/Staking/Overview/components/UnstakingDuration/UnstakingDuration.tsx @@ -1,8 +1,8 @@ import { ApiPromise } from '@polkadot/api'; import { useEffect, useState } from 'react'; -import { Duration } from '@renderer/components/ui'; -import { useStakingData } from '@renderer/services/staking/stakingDataService'; +import { Duration } from '@renderer/shared/ui'; +import { useStakingData } from '@renderer/entities/staking'; type Props = { api?: ApiPromise; diff --git a/src/renderer/screens/Staking/Overview/components/ValidatorsModal/ValidatorsModal.test.tsx b/src/renderer/pages/Staking/Overview/components/ValidatorsModal/ValidatorsModal.test.tsx similarity index 88% rename from src/renderer/screens/Staking/Overview/components/ValidatorsModal/ValidatorsModal.test.tsx rename to src/renderer/pages/Staking/Overview/components/ValidatorsModal/ValidatorsModal.test.tsx index 275bd08767..3dd0e476bf 100644 --- a/src/renderer/screens/Staking/Overview/components/ValidatorsModal/ValidatorsModal.test.tsx +++ b/src/renderer/pages/Staking/Overview/components/ValidatorsModal/ValidatorsModal.test.tsx @@ -2,25 +2,24 @@ import { render, screen, act } from '@testing-library/react'; import noop from 'lodash/noop'; import { ApiPromise } from '@polkadot/api'; -import { ValidatorMap } from '@renderer/services/staking/common/types'; -import { useValidators } from '@renderer/services/staking/validatorsService'; +import { ValidatorMap, useValidators } from '@renderer/entities/staking'; import { ValidatorsModal } from './ValidatorsModal'; jest.mock('@renderer/components/common'); -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), })); -jest.mock('@renderer/services/staking/validatorsService', () => ({ +jest.mock('@renderer/entities/staking', () => ({ useValidators: jest.fn().mockReturnValue({ getNominators: jest.fn().mockResolvedValue({}), }), })); -describe('screens/Staking/Overview/ValidatorsModal', () => { +describe('pages/Staking/Overview/ValidatorsModal', () => { const api = { isConnected: true } as ApiPromise; const elected = { diff --git a/src/renderer/screens/Staking/Overview/components/ValidatorsModal/ValidatorsModal.tsx b/src/renderer/pages/Staking/Overview/components/ValidatorsModal/ValidatorsModal.tsx similarity index 87% rename from src/renderer/screens/Staking/Overview/components/ValidatorsModal/ValidatorsModal.tsx rename to src/renderer/pages/Staking/Overview/components/ValidatorsModal/ValidatorsModal.tsx index 7a11a21611..b0d712dd4c 100644 --- a/src/renderer/screens/Staking/Overview/components/ValidatorsModal/ValidatorsModal.tsx +++ b/src/renderer/pages/Staking/Overview/components/ValidatorsModal/ValidatorsModal.tsx @@ -2,25 +2,25 @@ import { ApiPromise } from '@polkadot/api'; import { useEffect, useState, ReactNode } from 'react'; import { ExplorerLink } from '@renderer/components/common'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; -import { Explorer } from '@renderer/domain/chain'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, AssetBalance } from '@renderer/entities/asset'; +import { Explorer } from '@renderer/entities/chain'; import { Address } from '@renderer/domain/shared-kernel'; import { Validator } from '@renderer/domain/validator'; -import { ValidatorMap } from '@renderer/services/staking/common/types'; -import { useValidators } from '@renderer/services/staking/validatorsService'; -import { getComposedIdentity } from '@renderer/shared/utils/strings'; -import { toShortAddress } from '@renderer/shared/utils/address'; +import { ValidatorMap, useValidators } from '@renderer/entities/staking'; +import { getComposedIdentity, toShortAddress } from '@renderer/shared/lib/utils'; import { NoValidators } from '../EmptyState/NoValidators'; -import { Icon, Balance, Identicon, Loader } from '@renderer/components/ui'; import { + Icon, + Identicon, + Loader, BaseModal, SmallTitleText, FootnoteText, InfoPopover, BodyText, Accordion, -} from '@renderer/components/ui-redesign'; +} from '@renderer/shared/ui'; type Props = { api?: ApiPromise; @@ -103,12 +103,8 @@ export const ValidatorsModal = ({ {toShortAddress(validator.address, 11)} )}
    - - - - - - + +
    diff --git a/src/renderer/screens/Staking/Overview/components/index.ts b/src/renderer/pages/Staking/Overview/components/index.ts similarity index 100% rename from src/renderer/screens/Staking/Overview/components/index.ts rename to src/renderer/pages/Staking/Overview/components/index.ts diff --git a/src/renderer/screens/Staking/index.ts b/src/renderer/pages/Staking/index.ts similarity index 100% rename from src/renderer/screens/Staking/index.ts rename to src/renderer/pages/Staking/index.ts diff --git a/src/renderer/screens/Transfer/Transfer.tsx b/src/renderer/pages/Transfer/Transfer.tsx similarity index 91% rename from src/renderer/screens/Transfer/Transfer.tsx rename to src/renderer/pages/Transfer/Transfer.tsx index d994e1acb0..b84b8d5aac 100644 --- a/src/renderer/screens/Transfer/Transfer.tsx +++ b/src/renderer/pages/Transfer/Transfer.tsx @@ -1,18 +1,16 @@ import { useState, useEffect } from 'react'; import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; -import { useI18n } from '@renderer/context/I18nContext'; +import { useI18n, useNetworkContext } from '@renderer/app/providers'; import { ChainId, HexString } from '@renderer/domain/shared-kernel'; -import { useNetworkContext } from '@renderer/context/NetworkContext'; -import { useChains } from '@renderer/services/network/chainsService'; -import { Transaction } from '@renderer/domain/transaction'; -import { Account, MultisigAccount, isMultisig, isMultishard } from '@renderer/domain/account'; -import { useCountdown } from '@renderer/shared/hooks'; -import { BaseModal, Button } from '@renderer/components/ui-redesign'; +import { useChains } from '@renderer/entities/network'; +import { Transaction } from '@renderer/entities/transaction'; +import { Account, MultisigAccount, isMultisig, isMultishard } from '@renderer/entities/account'; +import { useCountdown } from '@renderer/shared/lib/hooks'; +import { BaseModal, Button, Loader } from '@renderer/shared/ui'; import OperationModalTitle from '../Operations/components/OperationModalTitle'; import { InitOperation, Confirmation, Signing, Submit } from './components/ActionSteps'; import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; -import { Loader } from '@renderer/components/ui'; const enum Step { INIT, @@ -29,7 +27,7 @@ type Props = { onClose?: () => void; }; -const Transfer = ({ assetId, chainId, isOpen, onClose }: Props) => { +export const Transfer = ({ assetId, chainId, isOpen, onClose }: Props) => { const { t } = useI18n(); const { getChainById } = useChains(); const { connections } = useNetworkContext(); @@ -201,5 +199,3 @@ const Transfer = ({ assetId, chainId, isOpen, onClose }: Props) => { ); }; - -export default Transfer; diff --git a/src/renderer/screens/Transfer/common/constants.ts b/src/renderer/pages/Transfer/common/constants.ts similarity index 100% rename from src/renderer/screens/Transfer/common/constants.ts rename to src/renderer/pages/Transfer/common/constants.ts diff --git a/src/renderer/screens/Transfer/common/utils.tsx b/src/renderer/pages/Transfer/common/utils.tsx similarity index 81% rename from src/renderer/screens/Transfer/common/utils.tsx rename to src/renderer/pages/Transfer/common/utils.tsx index a41a1d303d..06936d3034 100644 --- a/src/renderer/screens/Transfer/common/utils.tsx +++ b/src/renderer/pages/Transfer/common/utils.tsx @@ -1,15 +1,11 @@ import { ReactNode } from 'react'; import { BN } from '@polkadot/util'; -import { Account, MultisigAccount } from '@renderer/domain/account'; +import { Account, MultisigAccount, AccountAddress } from '@renderer/entities/account'; import { Address } from '@renderer/domain/shared-kernel'; -import { DropdownOption } from '@renderer/components/ui/Dropdowns/common/types'; -import { toAddress } from '@renderer/shared/utils/address'; -import { AccountAddress, BalanceNew } from '@renderer/components/common'; -import { Balance } from '@renderer/domain/balance'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { Asset } from '@renderer/domain/asset'; -import { transferableAmount } from '@renderer/shared/utils/balance'; +import { DropdownOption } from '@renderer/shared/ui/Dropdowns/common/types'; +import { toAddress, cnTw, transferableAmount } from '@renderer/shared/lib/utils'; +import { Balance, Asset, AssetBalance } from '@renderer/entities/asset'; type Params = { asset: Asset; @@ -24,7 +20,7 @@ type Params = { const getBalance = (balance: string, asset: Asset, isCorrect = true): ReactNode => { if (!balance) return null; - return ; + return ; }; const getElement = (address: Address, accountName: string, content?: ReactNode): ReactNode => { diff --git a/src/renderer/screens/Transfer/components/ActionSteps/Confirmation.tsx b/src/renderer/pages/Transfer/components/ActionSteps/Confirmation.tsx similarity index 78% rename from src/renderer/screens/Transfer/components/ActionSteps/Confirmation.tsx rename to src/renderer/pages/Transfer/components/ActionSteps/Confirmation.tsx index a0f545d75a..94c34bf262 100644 --- a/src/renderer/screens/Transfer/components/ActionSteps/Confirmation.tsx +++ b/src/renderer/pages/Transfer/components/ActionSteps/Confirmation.tsx @@ -1,16 +1,13 @@ import { useEffect, useState } from 'react'; -import { Transaction } from '@renderer/domain/transaction'; -import TransactionAmount from '@renderer/screens/Operations/components/TransactionAmount'; -import { Button, FootnoteText } from '@renderer/components/ui-redesign'; -import { Fee, DetailRow, DepositWithLabel } from '@renderer/components/common'; -import { Account, MultisigAccount } from '@renderer/domain/account'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { useI18n } from '@renderer/context/I18nContext'; +import { Transaction, DepositWithLabel, Fee } from '@renderer/entities/transaction'; +import TransactionAmount from '@renderer/pages/Operations/components/TransactionAmount'; +import { Button, DetailRow, FootnoteText, Icon } from '@renderer/shared/ui'; +import { Account, MultisigAccount } from '@renderer/entities/account'; +import { ExtendedChain } from '@renderer/entities/network'; +import { useI18n } from '@renderer/app/providers'; import Details from '../Details'; -import { Icon } from '@renderer/components/ui'; -import { Wallet } from '@renderer/domain/wallet'; -import { useWallet } from '@renderer/services/wallet/walletService'; +import { Wallet, useWallet } from '@renderer/entities/wallet'; const AmountFontStyle = 'font-manrope text-text-primary text-[32px] leading-[36px] font-bold'; diff --git a/src/renderer/screens/Transfer/components/ActionSteps/InitOperation.tsx b/src/renderer/pages/Transfer/components/ActionSteps/InitOperation.tsx similarity index 90% rename from src/renderer/screens/Transfer/components/ActionSteps/InitOperation.tsx rename to src/renderer/pages/Transfer/components/ActionSteps/InitOperation.tsx index a4ea871fe6..fb2dcd4ac8 100644 --- a/src/renderer/screens/Transfer/components/ActionSteps/InitOperation.tsx +++ b/src/renderer/pages/Transfer/components/ActionSteps/InitOperation.tsx @@ -1,18 +1,16 @@ import { ApiPromise } from '@polkadot/api'; import { useEffect, useState } from 'react'; -import { useI18n } from '@renderer/context/I18nContext'; -import { DropdownOption, DropdownResult } from '@renderer/components/ui/Dropdowns/common/types'; +import { useI18n } from '@renderer/app/providers'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; import { ChainId, SigningType } from '@renderer/domain/shared-kernel'; -import { useAccount } from '@renderer/services/account/accountService'; -import { Explorer } from '@renderer/domain/chain'; -import { Asset } from '@renderer/domain/asset'; -import { Transaction } from '@renderer/domain/transaction'; +import { useAccount, Account, isMultisig, MultisigAccount } from '@renderer/entities/account'; +import { Explorer } from '@renderer/entities/chain'; +import { Asset, useBalance } from '@renderer/entities/asset'; +import { Transaction } from '@renderer/entities/transaction'; import { TransferForm } from '../TransferForm'; -import { Account, isMultisig, MultisigAccount } from '@renderer/domain/account'; import { getAccountOption, getSignatoryOption } from '../../common/utils'; -import { InputHint, Select } from '@renderer/components/ui-redesign'; -import { useBalance } from '@renderer/services/balance/balanceService'; +import { InputHint, Select } from '@renderer/shared/ui'; type Props = { api: ApiPromise; diff --git a/src/renderer/screens/Transfer/components/ActionSteps/Signing.tsx b/src/renderer/pages/Transfer/components/ActionSteps/Signing.tsx similarity index 88% rename from src/renderer/screens/Transfer/components/ActionSteps/Signing.tsx rename to src/renderer/pages/Transfer/components/ActionSteps/Signing.tsx index 458c02a619..25e3bca1d9 100644 --- a/src/renderer/screens/Transfer/components/ActionSteps/Signing.tsx +++ b/src/renderer/pages/Transfer/components/ActionSteps/Signing.tsx @@ -3,14 +3,10 @@ import { BN } from '@polkadot/util'; import { ApiPromise } from '@polkadot/api'; import QrReaderWrapper from '@renderer/components/common/QrCode/QrReader/QrReaderWrapper'; -import { ValidationErrors } from '@renderer/shared/utils/validation'; -import { toAccountId } from '@renderer/shared/utils/address'; -import { transferableAmount } from '@renderer/shared/utils/balance'; -import { Transaction } from '@renderer/domain/transaction'; -import { useBalance } from '@renderer/services/balance/balanceService'; +import { toAccountId, transferableAmount, ValidationErrors } from '@renderer/shared/lib/utils'; +import { Transaction, useTransaction } from '@renderer/entities/transaction'; +import { Balance, useBalance } from '@renderer/entities/asset'; import { AccountId, ChainId, HexString } from '@renderer/domain/shared-kernel'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { Balance } from '@renderer/domain/balance'; type Props = { api: ApiPromise; diff --git a/src/renderer/screens/Transfer/components/ActionSteps/Submit.tsx b/src/renderer/pages/Transfer/components/ActionSteps/Submit.tsx similarity index 84% rename from src/renderer/screens/Transfer/components/ActionSteps/Submit.tsx rename to src/renderer/pages/Transfer/components/ActionSteps/Submit.tsx index f99b752f1c..354f9c74d0 100644 --- a/src/renderer/screens/Transfer/components/ActionSteps/Submit.tsx +++ b/src/renderer/pages/Transfer/components/ActionSteps/Submit.tsx @@ -3,21 +3,22 @@ import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { useEffect, useState, ComponentProps } from 'react'; import { useNavigate } from 'react-router-dom'; -import { useI18n } from '@renderer/context/I18nContext'; -import { MultisigEvent, Transaction, MultisigTransaction, MultisigTxInitStatus } from '@renderer/domain/transaction'; +import { useI18n, useMatrix, useMultisigChainContext, Paths } from '@renderer/app/providers'; +import { + MultisigEvent, + Transaction, + MultisigTransaction, + MultisigTxInitStatus, + useTransaction, + ExtrinsicResultParams, + OperationResult, +} from '@renderer/entities/transaction'; import { HexString } from '@renderer/domain/shared-kernel'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { useMatrix } from '@renderer/context/MatrixContext'; -import { Account, MultisigAccount, isMultisig } from '@renderer/domain/account'; -import { ExtrinsicResultParams } from '@renderer/services/transaction/common/types'; -import { useMultisigTx } from '@renderer/services/multisigTx/multisigTxService'; -import { toAccountId } from '@renderer/shared/utils/address'; -import { useToggle } from '@renderer/shared/hooks'; -import { Button } from '@renderer/components/ui-redesign'; -import { OperationResult } from '@renderer/components/common/OperationResult/OperationResult'; -import Paths from '@renderer/routes/paths'; -import { useMultisigEvent } from '@renderer/services/multisigEvent/multisigEventService'; -import { useMultisigChainContext } from '@renderer/context/MultisigChainContext'; +import { Account, MultisigAccount, isMultisig } from '@renderer/entities/account'; +import { useMultisigTx, useMultisigEvent } from '@renderer/entities/multisig'; +import { toAccountId } from '@renderer/shared/lib/utils'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { Button } from '@renderer/shared/ui'; type ResultProps = Pick, 'title' | 'description' | 'variant'>; diff --git a/src/renderer/screens/Transfer/components/ActionSteps/index.ts b/src/renderer/pages/Transfer/components/ActionSteps/index.ts similarity index 100% rename from src/renderer/screens/Transfer/components/ActionSteps/index.ts rename to src/renderer/pages/Transfer/components/ActionSteps/index.ts diff --git a/src/renderer/screens/Transfer/components/Details.tsx b/src/renderer/pages/Transfer/components/Details.tsx similarity index 81% rename from src/renderer/screens/Transfer/components/Details.tsx rename to src/renderer/pages/Transfer/components/Details.tsx index 5b10c452a1..4adfb71ffc 100644 --- a/src/renderer/screens/Transfer/components/Details.tsx +++ b/src/renderer/pages/Transfer/components/Details.tsx @@ -1,11 +1,10 @@ -import { useI18n } from '@renderer/context/I18nContext'; -import { Account, MultisigAccount } from '@renderer/domain/account'; -import { ExtendedChain } from '@renderer/services/network/common/types'; -import { DetailRow } from '@renderer/components/common'; -import { Transaction } from '@renderer/domain/transaction'; -import AddressWithExplorers from '@renderer/components/common/AddressWithExplorers/AddressWithExplorers'; +import { useI18n } from '@renderer/app/providers'; +import { Account, MultisigAccount, AddressWithExplorers } from '@renderer/entities/account'; +import { ExtendedChain } from '@renderer/entities/network'; +import { Transaction } from '@renderer/entities/transaction'; import { AddressStyle } from '../common/constants'; -import { Wallet } from '@renderer/domain/wallet'; +import { Wallet } from '@renderer/entities/wallet'; +import { DetailRow } from '@renderer/shared/ui'; type Props = { transaction: Transaction; diff --git a/src/renderer/screens/Transfer/components/TransferForm.tsx b/src/renderer/pages/Transfer/components/TransferForm.tsx similarity index 93% rename from src/renderer/screens/Transfer/components/TransferForm.tsx rename to src/renderer/pages/Transfer/components/TransferForm.tsx index ac3f25f0fe..b5bd501e43 100644 --- a/src/renderer/screens/Transfer/components/TransferForm.tsx +++ b/src/renderer/pages/Transfer/components/TransferForm.tsx @@ -4,20 +4,28 @@ import { Controller, useForm, SubmitHandler } from 'react-hook-form'; import { ApiPromise } from '@polkadot/api'; import { Trans } from 'react-i18next'; -import { toAccountId, validateAddress, toAddress } from '@renderer/shared/utils/address'; -import { Icon, Identicon } from '@renderer/components/ui'; -import { Fee, DetailRow, DepositWithLabel } from '@renderer/components/common'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset, AssetType } from '@renderer/domain/asset'; -import { Transaction, MultisigTxInitStatus, TransactionType } from '@renderer/domain/transaction'; -import { useBalance } from '@renderer/services/balance/balanceService'; -import { formatAmount, transferableAmount } from '@renderer/shared/utils/balance'; +import { + toAccountId, + validateAddress, + toAddress, + formatAmount, + transferableAmount, + getAssetId, +} from '@renderer/shared/lib/utils'; +import { Icon, Identicon, Button, AmountInput, Input, InputHint, DetailRow } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, AssetType, useBalance } from '@renderer/entities/asset'; +import { + Transaction, + MultisigTxInitStatus, + TransactionType, + useTransaction, + DepositWithLabel, + Fee, +} from '@renderer/entities/transaction'; import { Address, ChainId, AccountId } from '@renderer/domain/shared-kernel'; -import { useTransaction } from '@renderer/services/transaction/transactionService'; -import { useMultisigTx } from '@renderer/services/multisigTx/multisigTxService'; -import { getAssetId } from '@renderer/shared/utils/assets'; -import { MultisigAccount, Account, isMultisig } from '@renderer/domain/account'; -import { Button, AmountInput, Input, InputHint } from '@renderer/components/ui-redesign'; +import { useMultisigTx } from '@renderer/entities/multisig'; +import { MultisigAccount, Account, isMultisig } from '@renderer/entities/account'; const DESCRIPTION_MAX_LENGTH = 120; diff --git a/src/renderer/pages/index.ts b/src/renderer/pages/index.ts new file mode 100644 index 0000000000..aeb3444a74 --- /dev/null +++ b/src/renderer/pages/index.ts @@ -0,0 +1,8 @@ +export * as Settings from './Settings'; +export * as Staking from './Staking'; +export * as AddressBook from './AddressBook'; +export * as Assets from './Assets/Assets'; +export { Onboarding } from './Onboarding'; +export { Operations } from './Operations/Operations'; +export { Transfer } from './Transfer/Transfer'; +export { Notifications } from './Notifications/Notifications'; diff --git a/src/renderer/screens/Assets/components/AssetDetails/AssetDetails.tsx b/src/renderer/screens/Assets/components/AssetDetails/AssetDetails.tsx deleted file mode 100644 index c24012460c..0000000000 --- a/src/renderer/screens/Assets/components/AssetDetails/AssetDetails.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Asset } from '@renderer/domain/asset'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; -import { BalanceNew } from '@renderer/components/common'; -import { Shimmering } from '@renderer/components/ui'; - -type Props = { - asset: Asset; - value?: string; - label: string; - showShimmer?: boolean; -}; - -export const AssetDetails = ({ asset, value, label }: Props) => { - return ( -
    - - {label} - -
    {value ? : }
    -
    - ); -}; diff --git a/src/renderer/screens/Onboarding/index.ts b/src/renderer/screens/Onboarding/index.ts deleted file mode 100644 index 4876d4703a..0000000000 --- a/src/renderer/screens/Onboarding/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Welcome from './Welcome/Welcome'; - -export default { Welcome }; diff --git a/src/renderer/screens/Operations/components/TransactionAmount.tsx b/src/renderer/screens/Operations/components/TransactionAmount.tsx deleted file mode 100644 index dd24544f38..0000000000 --- a/src/renderer/screens/Operations/components/TransactionAmount.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useEffect, useState, ComponentProps } from 'react'; - -import { DecodedTransaction, Transaction } from '@renderer/domain/transaction'; -import { useChains } from '@renderer/services/network/chainsService'; -import { Asset } from '@renderer/domain/asset'; -import { getAssetById } from '@renderer/shared/utils/assets'; -import { BalanceNew } from '@renderer/components/common'; -import { getTransactionAmount } from '@renderer/screens/Operations/common/utils'; - -type Props = { - tx: Transaction | DecodedTransaction; -}; - -type BalanceProps = Pick, 'className' | 'showIcon' | 'wrapperClassName'>; - -const TransactionAmount = ({ tx, ...balanceProps }: Props & BalanceProps) => { - const { getChainById } = useChains(); - const [assets, setAssets] = useState([]); - - useEffect(() => { - getChainById(tx.chainId).then((chain) => setAssets(chain?.assets || [])); - }, []); - - const asset = getAssetById(tx.args.assetId, assets); - const value = getTransactionAmount(tx); - - if (!asset || !value) return null; - - return ; -}; - -export default TransactionAmount; diff --git a/src/renderer/screens/index.ts b/src/renderer/screens/index.ts deleted file mode 100644 index 8d3f064cdf..0000000000 --- a/src/renderer/screens/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import * as Settings from './Settings'; -import * as Staking from './Staking'; -import * as AddressBook from './AddressBook'; -import * as Assets from './Assets/Assets'; -import Onboarding from './Onboarding'; -import Operations from './Operations/Operations'; -import Transfer from './Transfer/Transfer'; -import Notifications from './Notifications/Notifications'; - -export default { - Settings, - Assets, - AddressBook, - Operations, - Onboarding, - Staking, - Transfer, - Notifications, -}; diff --git a/src/renderer/services/storage/__tests__/balanceStorage.test.ts b/src/renderer/shared/api/storage/__tests__/balanceStorage.test.ts similarity index 100% rename from src/renderer/services/storage/__tests__/balanceStorage.test.ts rename to src/renderer/shared/api/storage/__tests__/balanceStorage.test.ts diff --git a/src/renderer/services/storage/__tests__/connectionStorage.test.ts b/src/renderer/shared/api/storage/__tests__/connectionStorage.test.ts similarity index 97% rename from src/renderer/services/storage/__tests__/connectionStorage.test.ts rename to src/renderer/shared/api/storage/__tests__/connectionStorage.test.ts index 90760f4220..f38a140e1c 100644 --- a/src/renderer/services/storage/__tests__/connectionStorage.test.ts +++ b/src/renderer/shared/api/storage/__tests__/connectionStorage.test.ts @@ -1,6 +1,6 @@ import { ConnectionStatus, ConnectionType } from '@renderer/domain/connection'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { useConnectionStorage } from '@renderer/services/storage/connectionStorage'; +import { useConnectionStorage } from '@renderer/shared/api/storage/connectionStorage'; describe('service/storage/connectionStorage', () => { const setupDbMock = jest.fn((methodsToMock: any = {}) => { diff --git a/src/renderer/services/storage/__tests__/storage.test.ts b/src/renderer/shared/api/storage/__tests__/storage.test.ts similarity index 100% rename from src/renderer/services/storage/__tests__/storage.test.ts rename to src/renderer/shared/api/storage/__tests__/storage.test.ts diff --git a/src/renderer/services/storage/accountStorage.ts b/src/renderer/shared/api/storage/accountStorage.ts similarity index 92% rename from src/renderer/services/storage/accountStorage.ts rename to src/renderer/shared/api/storage/accountStorage.ts index 3268e70fa1..d16ad6c09a 100644 --- a/src/renderer/services/storage/accountStorage.ts +++ b/src/renderer/shared/api/storage/accountStorage.ts @@ -1,4 +1,4 @@ -import { Account } from '@renderer/domain/account'; +import { Account } from '@renderer/entities/account/model/account'; import { Address } from '@renderer/domain/shared-kernel'; import { AccountDS, IAccountStorage, TAccount, ID } from './common/types'; diff --git a/src/renderer/services/storage/balanceStorage.ts b/src/renderer/shared/api/storage/balanceStorage.ts similarity index 95% rename from src/renderer/services/storage/balanceStorage.ts rename to src/renderer/shared/api/storage/balanceStorage.ts index 929cd8adbb..00a3e64e4e 100644 --- a/src/renderer/services/storage/balanceStorage.ts +++ b/src/renderer/shared/api/storage/balanceStorage.ts @@ -1,4 +1,4 @@ -import { Balance, BalanceKey } from '@renderer/domain/balance'; +import { Balance, BalanceKey } from '@renderer/entities/asset/model/balance'; import { ChainId, AccountId } from '@renderer/domain/shared-kernel'; import { BalanceDS, IBalanceStorage, TBalance } from './common/types'; diff --git a/src/renderer/services/storage/common/types.ts b/src/renderer/shared/api/storage/common/types.ts similarity index 91% rename from src/renderer/services/storage/common/types.ts rename to src/renderer/shared/api/storage/common/types.ts index b522174f94..4811c32da3 100644 --- a/src/renderer/services/storage/common/types.ts +++ b/src/renderer/shared/api/storage/common/types.ts @@ -1,13 +1,17 @@ import { Table } from 'dexie'; -import { Balance, BalanceKey } from '@renderer/domain/balance'; +import { Balance, BalanceKey } from '@renderer/entities/asset/model/balance'; import { Connection, ConnectionType } from '@renderer/domain/connection'; -import { Contact } from '@renderer/domain/contact'; +import { Contact } from '@renderer/entities/contact/model/contact'; import { Address, ChainId, AccountId, CallHash } from '@renderer/domain/shared-kernel'; -import { Wallet } from '@renderer/domain/wallet'; -import { MultisigEvent, MultisigTransaction, MultisigTransactionKey } from '@renderer/domain/transaction'; -import { Account, MultisigAccount } from '@renderer/domain/account'; -import { Notification } from '@renderer/domain/notification'; +import { Wallet } from '@renderer/entities/wallet/model/wallet'; +import { + MultisigEvent, + MultisigTransaction, + MultisigTransactionKey, +} from '@renderer/entities/transaction/model/transaction'; +import { Account, MultisigAccount } from '@renderer/entities/account/model/account'; +import { Notification } from '@renderer/entities/notification/model/notification'; // ===================================================== // ================ Storage interface ================== diff --git a/src/renderer/services/storage/common/upgrades.ts b/src/renderer/shared/api/storage/common/upgrades.ts similarity index 100% rename from src/renderer/services/storage/common/upgrades.ts rename to src/renderer/shared/api/storage/common/upgrades.ts diff --git a/src/renderer/services/storage/connectionStorage.ts b/src/renderer/shared/api/storage/connectionStorage.ts similarity index 100% rename from src/renderer/services/storage/connectionStorage.ts rename to src/renderer/shared/api/storage/connectionStorage.ts diff --git a/src/renderer/services/storage/contactStorage.ts b/src/renderer/shared/api/storage/contactStorage.ts similarity index 90% rename from src/renderer/services/storage/contactStorage.ts rename to src/renderer/shared/api/storage/contactStorage.ts index 506a424d44..9586d91c61 100644 --- a/src/renderer/services/storage/contactStorage.ts +++ b/src/renderer/shared/api/storage/contactStorage.ts @@ -1,4 +1,4 @@ -import { Contact } from '@renderer/domain/contact'; +import { Contact } from '@renderer/entities/contact/model/contact'; import { ContactDS, IContactStorage, TContact, ID } from './common/types'; export const useContactStorage = (db: TContact): IContactStorage => ({ diff --git a/src/renderer/services/storage/index.ts b/src/renderer/shared/api/storage/index.ts similarity index 100% rename from src/renderer/services/storage/index.ts rename to src/renderer/shared/api/storage/index.ts diff --git a/src/renderer/services/storage/multisigEventStorage.ts b/src/renderer/shared/api/storage/multisigEventStorage.ts similarity index 96% rename from src/renderer/services/storage/multisigEventStorage.ts rename to src/renderer/shared/api/storage/multisigEventStorage.ts index 49979c4ce1..6058613c83 100644 --- a/src/renderer/services/storage/multisigEventStorage.ts +++ b/src/renderer/shared/api/storage/multisigEventStorage.ts @@ -1,4 +1,4 @@ -import { MultisigEvent, MultisigTransactionKey } from '@renderer/domain/transaction'; +import { MultisigEvent, MultisigTransactionKey } from '@renderer/entities/transaction/model/transaction'; import { TMultisigEvent, IMultisigEventStorage, MultisigEventDS, ID } from './common/types'; export const useMultisigEventStorage = (db: TMultisigEvent): IMultisigEventStorage => ({ diff --git a/src/renderer/services/storage/notificationStorage.ts b/src/renderer/shared/api/storage/notificationStorage.ts similarity index 84% rename from src/renderer/services/storage/notificationStorage.ts rename to src/renderer/shared/api/storage/notificationStorage.ts index 2058f420ba..5e541b894a 100644 --- a/src/renderer/services/storage/notificationStorage.ts +++ b/src/renderer/shared/api/storage/notificationStorage.ts @@ -1,4 +1,4 @@ -import { Notification } from '@renderer/domain/notification'; +import { Notification } from '@renderer/entities/notification/model/notification'; import { ID, INotificationStorage, NotificationDS, TNotification } from './common/types'; export const useNotificationStorage = (db: TNotification): INotificationStorage => ({ diff --git a/src/renderer/services/storage/storage.ts b/src/renderer/shared/api/storage/storage.ts similarity index 100% rename from src/renderer/services/storage/storage.ts rename to src/renderer/shared/api/storage/storage.ts diff --git a/src/renderer/services/storage/transactionStorage.ts b/src/renderer/shared/api/storage/transactionStorage.ts similarity index 95% rename from src/renderer/services/storage/transactionStorage.ts rename to src/renderer/shared/api/storage/transactionStorage.ts index d728f86588..40603a16e8 100644 --- a/src/renderer/services/storage/transactionStorage.ts +++ b/src/renderer/shared/api/storage/transactionStorage.ts @@ -1,4 +1,4 @@ -import { MultisigTransaction } from '@renderer/domain/transaction'; +import { MultisigTransaction } from '@renderer/entities/transaction/model/transaction'; import { MultisigTransactionDS, IMultisigTransactionStorage, TMultisigTransaction } from './common/types'; import { AccountId, CallHash, ChainId } from '@renderer/domain/shared-kernel'; diff --git a/src/renderer/services/storage/walletStorage.ts b/src/renderer/shared/api/storage/walletStorage.ts similarity index 90% rename from src/renderer/services/storage/walletStorage.ts rename to src/renderer/shared/api/storage/walletStorage.ts index 51d2a9d4d8..4606294fa0 100644 --- a/src/renderer/services/storage/walletStorage.ts +++ b/src/renderer/shared/api/storage/walletStorage.ts @@ -1,4 +1,4 @@ -import { Wallet } from '@renderer/domain/wallet'; +import { Wallet } from '@renderer/entities/wallet/model/wallet'; import { IWalletStorage, WalletDS, TWallet, ID } from './common/types'; export const useWalletStorage = (db: TWallet): IWalletStorage => ({ diff --git a/src/renderer/shared/hooks/__tests__/useClickOutside.test.ts b/src/renderer/shared/lib/hooks/__tests__/useClickOutside.test.ts similarity index 100% rename from src/renderer/shared/hooks/__tests__/useClickOutside.test.ts rename to src/renderer/shared/lib/hooks/__tests__/useClickOutside.test.ts diff --git a/src/renderer/shared/hooks/__tests__/useCountdown.test.ts b/src/renderer/shared/lib/hooks/__tests__/useCountdown.test.ts similarity index 87% rename from src/renderer/shared/hooks/__tests__/useCountdown.test.ts rename to src/renderer/shared/lib/hooks/__tests__/useCountdown.test.ts index c5cf123cb0..79b83927f2 100644 --- a/src/renderer/shared/hooks/__tests__/useCountdown.test.ts +++ b/src/renderer/shared/lib/hooks/__tests__/useCountdown.test.ts @@ -2,10 +2,10 @@ import { renderHook, act, waitFor } from '@testing-library/react'; import { BN_MILLION } from '@polkadot/util'; import { ApiPromise } from '@polkadot/api'; -import { DEFAULT_QR_LIFETIME } from '@renderer/shared/utils/constants'; +import { DEFAULT_QR_LIFETIME } from '@renderer/shared/lib/utils'; import { useCountdown } from '../useCountdown'; -jest.mock('@renderer/shared/utils/substrate', () => ({ +jest.mock('@renderer/shared/lib/utils', () => ({ getExpectedBlockTime: jest.fn().mockReturnValue(BN_MILLION.muln(2)), })); diff --git a/src/renderer/shared/hooks/__tests__/useTaskQueue.test.ts b/src/renderer/shared/lib/hooks/__tests__/useTaskQueue.test.ts similarity index 100% rename from src/renderer/shared/hooks/__tests__/useTaskQueue.test.ts rename to src/renderer/shared/lib/hooks/__tests__/useTaskQueue.test.ts diff --git a/src/renderer/shared/hooks/__tests__/useToggle.test.ts b/src/renderer/shared/lib/hooks/__tests__/useToggle.test.ts similarity index 100% rename from src/renderer/shared/hooks/__tests__/useToggle.test.ts rename to src/renderer/shared/lib/hooks/__tests__/useToggle.test.ts diff --git a/src/renderer/shared/hooks/index.ts b/src/renderer/shared/lib/hooks/index.ts similarity index 100% rename from src/renderer/shared/hooks/index.ts rename to src/renderer/shared/lib/hooks/index.ts diff --git a/src/renderer/shared/hooks/useClickOutside.ts b/src/renderer/shared/lib/hooks/useClickOutside.ts similarity index 100% rename from src/renderer/shared/hooks/useClickOutside.ts rename to src/renderer/shared/lib/hooks/useClickOutside.ts diff --git a/src/renderer/shared/hooks/useCountdown.ts b/src/renderer/shared/lib/hooks/useCountdown.ts similarity index 86% rename from src/renderer/shared/hooks/useCountdown.ts rename to src/renderer/shared/lib/hooks/useCountdown.ts index d42f5bacc1..58edf537db 100644 --- a/src/renderer/shared/hooks/useCountdown.ts +++ b/src/renderer/shared/lib/hooks/useCountdown.ts @@ -2,8 +2,7 @@ import { useEffect, useState, useCallback } from 'react'; import { BN, BN_THOUSAND } from '@polkadot/util'; import { ApiPromise } from '@polkadot/api'; -import { DEFAULT_QR_LIFETIME } from '@renderer/shared/utils/constants'; -import { getExpectedBlockTime } from '@renderer/shared/utils/substrate'; +import { DEFAULT_QR_LIFETIME, getExpectedBlockTime } from '@renderer/shared/lib/utils'; /** * Start countdown based on Expected block time diff --git a/src/renderer/shared/hooks/useDebounce.ts b/src/renderer/shared/lib/hooks/useDebounce.ts similarity index 100% rename from src/renderer/shared/hooks/useDebounce.ts rename to src/renderer/shared/lib/hooks/useDebounce.ts diff --git a/src/renderer/shared/hooks/usePrevious.ts b/src/renderer/shared/lib/hooks/usePrevious.ts similarity index 100% rename from src/renderer/shared/hooks/usePrevious.ts rename to src/renderer/shared/lib/hooks/usePrevious.ts diff --git a/src/renderer/shared/hooks/useScrollTo.ts b/src/renderer/shared/lib/hooks/useScrollTo.ts similarity index 100% rename from src/renderer/shared/hooks/useScrollTo.ts rename to src/renderer/shared/lib/hooks/useScrollTo.ts diff --git a/src/renderer/shared/hooks/useTaskQueue.ts b/src/renderer/shared/lib/hooks/useTaskQueue.ts similarity index 100% rename from src/renderer/shared/hooks/useTaskQueue.ts rename to src/renderer/shared/lib/hooks/useTaskQueue.ts diff --git a/src/renderer/shared/hooks/useToggle.ts b/src/renderer/shared/lib/hooks/useToggle.ts similarity index 100% rename from src/renderer/shared/hooks/useToggle.ts rename to src/renderer/shared/lib/hooks/useToggle.ts diff --git a/src/renderer/shared/utils/__tests__/address.test.ts b/src/renderer/shared/lib/utils/__tests__/address.test.ts similarity index 99% rename from src/renderer/shared/utils/__tests__/address.test.ts rename to src/renderer/shared/lib/utils/__tests__/address.test.ts index 14400732cf..493b3e7289 100644 --- a/src/renderer/shared/utils/__tests__/address.test.ts +++ b/src/renderer/shared/lib/utils/__tests__/address.test.ts @@ -1,4 +1,4 @@ -import { TEST_ACCOUNT_ID, TEST_ADDRESS } from '@renderer/shared/utils/constants'; +import { TEST_ACCOUNT_ID, TEST_ADDRESS } from '@renderer/shared/lib/utils'; import { toAddress, validateAddress } from '../address'; describe('shared/utils/address', () => { diff --git a/src/renderer/shared/utils/__tests__/balance.test.ts b/src/renderer/shared/lib/utils/__tests__/balance.test.ts similarity index 100% rename from src/renderer/shared/utils/__tests__/balance.test.ts rename to src/renderer/shared/lib/utils/__tests__/balance.test.ts diff --git a/src/renderer/shared/utils/__tests__/strings.test.ts b/src/renderer/shared/lib/utils/__tests__/strings.test.ts similarity index 100% rename from src/renderer/shared/utils/__tests__/strings.test.ts rename to src/renderer/shared/lib/utils/__tests__/strings.test.ts diff --git a/src/renderer/shared/utils/__tests__/substrate.test.ts b/src/renderer/shared/lib/utils/__tests__/substrate.test.ts similarity index 93% rename from src/renderer/shared/utils/__tests__/substrate.test.ts rename to src/renderer/shared/lib/utils/__tests__/substrate.test.ts index 476bb0e6ef..10806ff321 100644 --- a/src/renderer/shared/utils/__tests__/substrate.test.ts +++ b/src/renderer/shared/lib/utils/__tests__/substrate.test.ts @@ -2,7 +2,7 @@ import { ApiPromise } from '@polkadot/api'; import { BN, BN_TWO } from '@polkadot/util'; import { getExpectedBlockTime } from '../substrate'; -import { DEFAULT_TIME, THRESHOLD } from '@renderer/services/network/common/constants'; +import { DEFAULT_TIME, THRESHOLD } from '@renderer/entities/network/lib/common/constants'; describe('shared/utils/substrate', () => { const blockTime = new BN(10_000); diff --git a/src/renderer/shared/utils/address.ts b/src/renderer/shared/lib/utils/address.ts similarity index 98% rename from src/renderer/shared/utils/address.ts rename to src/renderer/shared/lib/utils/address.ts index fd9c1c6860..8fb367b20f 100644 --- a/src/renderer/shared/utils/address.ts +++ b/src/renderer/shared/lib/utils/address.ts @@ -8,7 +8,7 @@ import { PUBLIC_KEY_LENGTH_BYTES, SS58_DEFAULT_PREFIX, } from './constants'; -import { truncate } from '@renderer/shared/utils/strings'; +import { truncate } from '@renderer/shared/lib/utils'; /** * Format address or accountId with prefix and chunk size diff --git a/src/renderer/shared/utils/assets.ts b/src/renderer/shared/lib/utils/assets.ts similarity index 96% rename from src/renderer/shared/utils/assets.ts rename to src/renderer/shared/lib/utils/assets.ts index 9e69fa1c4e..230de1cb4d 100644 --- a/src/renderer/shared/utils/assets.ts +++ b/src/renderer/shared/lib/utils/assets.ts @@ -1,4 +1,4 @@ -import { Asset, AssetType, OrmlExtras, StatemineExtras, StakingType } from '@renderer/domain/asset'; +import { Asset, AssetType, OrmlExtras, StatemineExtras, StakingType } from '@renderer/entities/asset/model/asset'; /** * Get ID of the asset by type diff --git a/src/renderer/shared/utils/balance.ts b/src/renderer/shared/lib/utils/balance.ts similarity index 97% rename from src/renderer/shared/utils/balance.ts rename to src/renderer/shared/lib/utils/balance.ts index 51ce45542c..6acc3914bd 100644 --- a/src/renderer/shared/utils/balance.ts +++ b/src/renderer/shared/lib/utils/balance.ts @@ -1,8 +1,8 @@ import { BN, BN_TEN, BN_ZERO } from '@polkadot/util'; import BigNumber from 'bignumber.js'; -import { Balance, LockTypes } from '@renderer/domain/balance'; -import { Unlocking } from '@renderer/domain/stake'; +import { Balance, LockTypes } from '@renderer/entities/asset/model/balance'; +import { Unlocking } from '@renderer/entities/staking/model/stake'; import { ZERO_BALANCE } from './constants'; const MAX_INTEGER = 15; diff --git a/src/renderer/shared/utils/bignumber.ts b/src/renderer/shared/lib/utils/bignumber.ts similarity index 100% rename from src/renderer/shared/utils/bignumber.ts rename to src/renderer/shared/lib/utils/bignumber.ts diff --git a/src/renderer/shared/utils/browser.ts b/src/renderer/shared/lib/utils/browser.ts similarity index 100% rename from src/renderer/shared/utils/browser.ts rename to src/renderer/shared/lib/utils/browser.ts diff --git a/src/renderer/shared/utils/constants.ts b/src/renderer/shared/lib/utils/constants.ts similarity index 100% rename from src/renderer/shared/utils/constants.ts rename to src/renderer/shared/lib/utils/constants.ts diff --git a/src/renderer/shared/utils/functions.ts b/src/renderer/shared/lib/utils/functions.ts similarity index 100% rename from src/renderer/shared/utils/functions.ts rename to src/renderer/shared/lib/utils/functions.ts diff --git a/src/renderer/shared/lib/utils/index.ts b/src/renderer/shared/lib/utils/index.ts new file mode 100644 index 0000000000..e4f87f8483 --- /dev/null +++ b/src/renderer/shared/lib/utils/index.ts @@ -0,0 +1,12 @@ +export * from './address'; +export * from './assets'; +export * from './balance'; +export * from './bignumber'; +export * from './browser'; +export * from './constants'; +export * from './functions'; +export * from './strings'; +export * from './substrate'; +export * from './time'; +export * from './twMerge'; +export * from './validation'; diff --git a/src/renderer/shared/utils/strings.ts b/src/renderer/shared/lib/utils/strings.ts similarity index 100% rename from src/renderer/shared/utils/strings.ts rename to src/renderer/shared/lib/utils/strings.ts diff --git a/src/renderer/shared/utils/substrate.ts b/src/renderer/shared/lib/utils/substrate.ts similarity index 97% rename from src/renderer/shared/utils/substrate.ts rename to src/renderer/shared/lib/utils/substrate.ts index f0adcb4989..fa3a549153 100644 --- a/src/renderer/shared/utils/substrate.ts +++ b/src/renderer/shared/lib/utils/substrate.ts @@ -4,7 +4,7 @@ import { isHex, hexToU8a, bnMin, BN_TWO, BN } from '@polkadot/util'; import { blake2AsHex } from '@polkadot/util-crypto'; import { Address, CallData, CallHash } from '@renderer/domain/shared-kernel'; -import { DEFAULT_TIME, ONE_DAY, THRESHOLD } from '@renderer/services/network/common/constants'; +import { DEFAULT_TIME, ONE_DAY, THRESHOLD } from '@renderer/entities/network/lib/common/constants'; /** * Compose and return all the data needed for @substrate/txwrapper-polkadot signing diff --git a/src/renderer/shared/utils/time.ts b/src/renderer/shared/lib/utils/time.ts similarity index 100% rename from src/renderer/shared/utils/time.ts rename to src/renderer/shared/lib/utils/time.ts diff --git a/src/renderer/shared/utils/twMerge.ts b/src/renderer/shared/lib/utils/twMerge.ts similarity index 76% rename from src/renderer/shared/utils/twMerge.ts rename to src/renderer/shared/lib/utils/twMerge.ts index 206db528d4..675c1b1241 100644 --- a/src/renderer/shared/utils/twMerge.ts +++ b/src/renderer/shared/lib/utils/twMerge.ts @@ -1,7 +1,7 @@ import cn from 'classnames'; import { extendTailwindMerge } from 'tailwind-merge'; -import fontSizes from '../../../../tw-config-consts/font-sizes'; +import fontSizes from '../../../../../tw-config-consts/font-sizes'; type CnArgs = Parameters; @@ -23,6 +23,4 @@ const twMerge = extendTailwindMerge({ * @param args list of arguments for cn * @return {String} */ -const cnTw = (...args: CnArgs): string => twMerge(cn(args)); - -export default cnTw; +export const cnTw = (...args: CnArgs): string => twMerge(cn(args)); diff --git a/src/renderer/shared/utils/validation.ts b/src/renderer/shared/lib/utils/validation.ts similarity index 100% rename from src/renderer/shared/utils/validation.ts rename to src/renderer/shared/lib/utils/validation.ts diff --git a/src/renderer/components/ui-redesign/Accordion/Accordion.stories.tsx b/src/renderer/shared/ui/Accordion/Accordion.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Accordion/Accordion.stories.tsx rename to src/renderer/shared/ui/Accordion/Accordion.stories.tsx diff --git a/src/renderer/components/ui-redesign/Accordion/Accordion.tsx b/src/renderer/shared/ui/Accordion/Accordion.tsx similarity index 94% rename from src/renderer/components/ui-redesign/Accordion/Accordion.tsx rename to src/renderer/shared/ui/Accordion/Accordion.tsx index ffb9825edc..699001d8b8 100644 --- a/src/renderer/components/ui-redesign/Accordion/Accordion.tsx +++ b/src/renderer/shared/ui/Accordion/Accordion.tsx @@ -1,8 +1,8 @@ import { PropsWithChildren } from 'react'; import { Disclosure, Transition } from '@headlessui/react'; -import { Icon } from '@renderer/components/ui'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { Icon } from '@renderer/shared/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { className?: string; diff --git a/src/renderer/components/ui-redesign/Alert/Alert.stories.tsx b/src/renderer/shared/ui/Alert/Alert.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Alert/Alert.stories.tsx rename to src/renderer/shared/ui/Alert/Alert.stories.tsx diff --git a/src/renderer/components/ui-redesign/Alert/Alert.test.tsx b/src/renderer/shared/ui/Alert/Alert.test.tsx similarity index 90% rename from src/renderer/components/ui-redesign/Alert/Alert.test.tsx rename to src/renderer/shared/ui/Alert/Alert.test.tsx index c8fdd46442..790fda79c8 100644 --- a/src/renderer/components/ui-redesign/Alert/Alert.test.tsx +++ b/src/renderer/shared/ui/Alert/Alert.test.tsx @@ -3,7 +3,10 @@ import noop from 'lodash/noop'; import Alert from './Alert'; -describe('ui-redesign/Alert', () => { +// jest.mock('dexie-react-hooks'); +// jest.mock('dexie'); + +describe('ui/Alert', () => { test('should render title and items', () => { render( diff --git a/src/renderer/components/ui-redesign/Alert/Alert.tsx b/src/renderer/shared/ui/Alert/Alert.tsx similarity index 86% rename from src/renderer/components/ui-redesign/Alert/Alert.tsx rename to src/renderer/shared/ui/Alert/Alert.tsx index 7ceb1bd55a..b767a79bb0 100644 --- a/src/renderer/components/ui-redesign/Alert/Alert.tsx +++ b/src/renderer/shared/ui/Alert/Alert.tsx @@ -1,8 +1,7 @@ import { PropsWithChildren, Children } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { IconButton, HeadlineText } from '@renderer/components/ui-redesign'; -import { Icon } from '@renderer/components/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { IconButton, HeadlineText, Icon } from '@renderer/shared/ui'; import { Variant } from './common/types'; import { ViewStyle, IconStyle, IconName } from './common/constants'; import AlertItem from './AlertItem'; diff --git a/src/renderer/components/ui-redesign/Alert/AlertItem.tsx b/src/renderer/shared/ui/Alert/AlertItem.tsx similarity index 86% rename from src/renderer/components/ui-redesign/Alert/AlertItem.tsx rename to src/renderer/shared/ui/Alert/AlertItem.tsx index a3997593e8..d3f0259255 100644 --- a/src/renderer/components/ui-redesign/Alert/AlertItem.tsx +++ b/src/renderer/shared/ui/Alert/AlertItem.tsx @@ -1,6 +1,6 @@ import { PropsWithChildren } from 'react'; -import { FootnoteText } from '@renderer/components/ui-redesign'; +import { FootnoteText } from '@renderer/shared/ui'; type Props = { withDot?: boolean; diff --git a/src/renderer/components/ui-redesign/Alert/common/constants.ts b/src/renderer/shared/ui/Alert/common/constants.ts similarity index 89% rename from src/renderer/components/ui-redesign/Alert/common/constants.ts rename to src/renderer/shared/ui/Alert/common/constants.ts index c2af135188..f3da9181de 100644 --- a/src/renderer/components/ui-redesign/Alert/common/constants.ts +++ b/src/renderer/shared/ui/Alert/common/constants.ts @@ -1,5 +1,5 @@ import { Variant } from './types'; -import { IconNames } from '@renderer/components/ui/Icon/data'; +import { IconNames } from '@renderer/shared/ui/Icon/data'; export const IconName: Record = { info: 'info', diff --git a/src/renderer/components/ui-redesign/Alert/common/types.ts b/src/renderer/shared/ui/Alert/common/types.ts similarity index 100% rename from src/renderer/components/ui-redesign/Alert/common/types.ts rename to src/renderer/shared/ui/Alert/common/types.ts diff --git a/src/renderer/components/ui-redesign/Animation/Animation.tsx b/src/renderer/shared/ui/Animation/Animation.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Animation/Animation.tsx rename to src/renderer/shared/ui/Animation/Animation.tsx diff --git a/src/renderer/components/ui-redesign/Animation/Data.ts b/src/renderer/shared/ui/Animation/Data.ts similarity index 100% rename from src/renderer/components/ui-redesign/Animation/Data.ts rename to src/renderer/shared/ui/Animation/Data.ts diff --git a/src/renderer/components/ui-redesign/Buttons/Button/Button.stories.tsx b/src/renderer/shared/ui/Buttons/Button/Button.stories.tsx similarity index 97% rename from src/renderer/components/ui-redesign/Buttons/Button/Button.stories.tsx rename to src/renderer/shared/ui/Buttons/Button/Button.stories.tsx index 51d8166234..ee8a1634c2 100644 --- a/src/renderer/components/ui-redesign/Buttons/Button/Button.stories.tsx +++ b/src/renderer/shared/ui/Buttons/Button/Button.stories.tsx @@ -1,6 +1,6 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Icon } from '@renderer/components/ui'; +import { Icon } from '@renderer/shared/ui'; import Button from './Button'; export default { title: 'Redesign/Button', diff --git a/src/renderer/components/ui-redesign/Buttons/Button/Button.test.tsx b/src/renderer/shared/ui/Buttons/Button/Button.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Buttons/Button/Button.test.tsx rename to src/renderer/shared/ui/Buttons/Button/Button.test.tsx diff --git a/src/renderer/components/ui-redesign/Buttons/Button/Button.tsx b/src/renderer/shared/ui/Buttons/Button/Button.tsx similarity index 94% rename from src/renderer/components/ui-redesign/Buttons/Button/Button.tsx rename to src/renderer/shared/ui/Buttons/Button/Button.tsx index 8db0fae9ef..3534ceaea4 100644 --- a/src/renderer/components/ui-redesign/Buttons/Button/Button.tsx +++ b/src/renderer/shared/ui/Buttons/Button/Button.tsx @@ -1,10 +1,10 @@ import noop from 'lodash/noop'; import { MouseEvent, PropsWithChildren, ReactNode, forwardRef } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { ViewClass, SizeClass, Padding } from '../common/constants'; import { Pallet, Variant } from '../common/types'; -import { Loader } from '@renderer/components/ui'; +import { Loader } from '@renderer/shared/ui'; type Props = { className?: string; diff --git a/src/renderer/components/ui-redesign/Buttons/ButtonBack/ButtonBack.stories.tsx b/src/renderer/shared/ui/Buttons/ButtonBack/ButtonBack.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Buttons/ButtonBack/ButtonBack.stories.tsx rename to src/renderer/shared/ui/Buttons/ButtonBack/ButtonBack.stories.tsx diff --git a/src/renderer/components/ui-redesign/Buttons/ButtonBack/ButtonBack.test.tsx b/src/renderer/shared/ui/Buttons/ButtonBack/ButtonBack.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Buttons/ButtonBack/ButtonBack.test.tsx rename to src/renderer/shared/ui/Buttons/ButtonBack/ButtonBack.test.tsx diff --git a/src/renderer/components/ui-redesign/Buttons/ButtonBack/ButtonBack.tsx b/src/renderer/shared/ui/Buttons/ButtonBack/ButtonBack.tsx similarity index 86% rename from src/renderer/components/ui-redesign/Buttons/ButtonBack/ButtonBack.tsx rename to src/renderer/shared/ui/Buttons/ButtonBack/ButtonBack.tsx index 3535bf0c51..5c86961cb2 100644 --- a/src/renderer/components/ui-redesign/Buttons/ButtonBack/ButtonBack.tsx +++ b/src/renderer/shared/ui/Buttons/ButtonBack/ButtonBack.tsx @@ -1,7 +1,7 @@ import { PropsWithChildren } from 'react'; import { useNavigate } from 'react-router-dom'; -import IconButton from '@renderer/components/ui-redesign/Buttons/IconButton/IconButton'; +import IconButton from '@renderer/shared/ui/Buttons/IconButton/IconButton'; type Props = { path?: string; diff --git a/src/renderer/components/ui/Buttons/ButtonLink/ButtonLink.stories.tsx b/src/renderer/shared/ui/Buttons/ButtonLink/ButtonLink.stories.tsx similarity index 97% rename from src/renderer/components/ui/Buttons/ButtonLink/ButtonLink.stories.tsx rename to src/renderer/shared/ui/Buttons/ButtonLink/ButtonLink.stories.tsx index dd29710b00..dfee3ea6a0 100644 --- a/src/renderer/components/ui/Buttons/ButtonLink/ButtonLink.stories.tsx +++ b/src/renderer/shared/ui/Buttons/ButtonLink/ButtonLink.stories.tsx @@ -1,7 +1,7 @@ import { MemoryRouter } from 'react-router-dom'; import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Icon } from '@renderer/components/ui'; +import { Icon } from '@renderer/shared/ui'; import ButtonLink from './ButtonLink'; export default { diff --git a/src/renderer/components/ui-redesign/Buttons/ButtonLink/ButtonLink.test.tsx b/src/renderer/shared/ui/Buttons/ButtonLink/ButtonLink.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Buttons/ButtonLink/ButtonLink.test.tsx rename to src/renderer/shared/ui/Buttons/ButtonLink/ButtonLink.test.tsx diff --git a/src/renderer/components/ui-redesign/Buttons/ButtonLink/ButtonLink.tsx b/src/renderer/shared/ui/Buttons/ButtonLink/ButtonLink.tsx similarity index 96% rename from src/renderer/components/ui-redesign/Buttons/ButtonLink/ButtonLink.tsx rename to src/renderer/shared/ui/Buttons/ButtonLink/ButtonLink.tsx index 7e3da232b1..4977824090 100644 --- a/src/renderer/components/ui-redesign/Buttons/ButtonLink/ButtonLink.tsx +++ b/src/renderer/shared/ui/Buttons/ButtonLink/ButtonLink.tsx @@ -1,7 +1,7 @@ import { PropsWithChildren, ReactNode } from 'react'; import { Link } from 'react-router-dom'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { ViewClass, SizeClass, Padding } from '../common/constants'; import { Pallet, Variant } from '../common/types'; diff --git a/src/renderer/components/ui-redesign/Buttons/IconButton/IconButton.css b/src/renderer/shared/ui/Buttons/IconButton/IconButton.css similarity index 100% rename from src/renderer/components/ui-redesign/Buttons/IconButton/IconButton.css rename to src/renderer/shared/ui/Buttons/IconButton/IconButton.css diff --git a/src/renderer/components/ui-redesign/Buttons/IconButton/IconButton.stories.tsx b/src/renderer/shared/ui/Buttons/IconButton/IconButton.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Buttons/IconButton/IconButton.stories.tsx rename to src/renderer/shared/ui/Buttons/IconButton/IconButton.stories.tsx diff --git a/src/renderer/components/ui-redesign/Buttons/IconButton/IconButton.test.tsx b/src/renderer/shared/ui/Buttons/IconButton/IconButton.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Buttons/IconButton/IconButton.test.tsx rename to src/renderer/shared/ui/Buttons/IconButton/IconButton.test.tsx diff --git a/src/renderer/components/ui-redesign/Buttons/IconButton/IconButton.tsx b/src/renderer/shared/ui/Buttons/IconButton/IconButton.tsx similarity index 89% rename from src/renderer/components/ui-redesign/Buttons/IconButton/IconButton.tsx rename to src/renderer/shared/ui/Buttons/IconButton/IconButton.tsx index 7c8d3f027d..225d4202e1 100644 --- a/src/renderer/components/ui-redesign/Buttons/IconButton/IconButton.tsx +++ b/src/renderer/shared/ui/Buttons/IconButton/IconButton.tsx @@ -1,8 +1,8 @@ import { ComponentProps, MouseEvent } from 'react'; import cn from 'classnames'; -import cnTw from '@renderer/shared/utils/twMerge'; -import Icon from '../../../ui/Icon/Icon'; +import { cnTw } from '@renderer/shared/lib/utils'; +import Icon from '@renderer/shared/ui/Icon/Icon'; import './IconButton.css'; type IconProps = ComponentProps; diff --git a/src/renderer/components/ui-redesign/Buttons/common/constants.ts b/src/renderer/shared/ui/Buttons/common/constants.ts similarity index 100% rename from src/renderer/components/ui-redesign/Buttons/common/constants.ts rename to src/renderer/shared/ui/Buttons/common/constants.ts diff --git a/src/renderer/components/ui-redesign/Buttons/common/types.ts b/src/renderer/shared/ui/Buttons/common/types.ts similarity index 100% rename from src/renderer/components/ui-redesign/Buttons/common/types.ts rename to src/renderer/shared/ui/Buttons/common/types.ts diff --git a/src/renderer/components/ui-redesign/Checkbox/Checkbox.css b/src/renderer/shared/ui/Checkbox/Checkbox.css similarity index 100% rename from src/renderer/components/ui-redesign/Checkbox/Checkbox.css rename to src/renderer/shared/ui/Checkbox/Checkbox.css diff --git a/src/renderer/components/ui-redesign/Checkbox/Checkbox.stories.tsx b/src/renderer/shared/ui/Checkbox/Checkbox.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Checkbox/Checkbox.stories.tsx rename to src/renderer/shared/ui/Checkbox/Checkbox.stories.tsx diff --git a/src/renderer/components/ui-redesign/Checkbox/Checkbox.test.tsx b/src/renderer/shared/ui/Checkbox/Checkbox.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Checkbox/Checkbox.test.tsx rename to src/renderer/shared/ui/Checkbox/Checkbox.test.tsx diff --git a/src/renderer/components/ui-redesign/Checkbox/Checkbox.tsx b/src/renderer/shared/ui/Checkbox/Checkbox.tsx similarity index 94% rename from src/renderer/components/ui-redesign/Checkbox/Checkbox.tsx rename to src/renderer/shared/ui/Checkbox/Checkbox.tsx index 2820c4e0ba..f848dd83fa 100644 --- a/src/renderer/components/ui-redesign/Checkbox/Checkbox.tsx +++ b/src/renderer/shared/ui/Checkbox/Checkbox.tsx @@ -1,7 +1,7 @@ import { ChangeEvent, PropsWithChildren } from 'react'; -import { LabelText } from '@renderer/components/ui-redesign'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { LabelText } from '@renderer/shared/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; import './Checkbox.css'; type Props = { diff --git a/src/renderer/components/ui-redesign/Counter/Counter.stories.tsx b/src/renderer/shared/ui/Counter/Counter.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Counter/Counter.stories.tsx rename to src/renderer/shared/ui/Counter/Counter.stories.tsx diff --git a/src/renderer/components/ui-redesign/Counter/Counter.test.tsx b/src/renderer/shared/ui/Counter/Counter.test.tsx similarity index 95% rename from src/renderer/components/ui-redesign/Counter/Counter.test.tsx rename to src/renderer/shared/ui/Counter/Counter.test.tsx index f4d9c01503..8a1d5313d5 100644 --- a/src/renderer/components/ui-redesign/Counter/Counter.test.tsx +++ b/src/renderer/shared/ui/Counter/Counter.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'; import Counter from './Counter'; -describe('ui-redesign/Counter', () => { +describe('ui/Counter', () => { test('should render component', () => { render(25); diff --git a/src/renderer/components/ui-redesign/Counter/Counter.tsx b/src/renderer/shared/ui/Counter/Counter.tsx similarity index 83% rename from src/renderer/components/ui-redesign/Counter/Counter.tsx rename to src/renderer/shared/ui/Counter/Counter.tsx index 0c353855b2..12cb5c0cd5 100644 --- a/src/renderer/components/ui-redesign/Counter/Counter.tsx +++ b/src/renderer/shared/ui/Counter/Counter.tsx @@ -2,8 +2,8 @@ import { PropsWithChildren } from 'react'; import { Variant } from './common/types'; import { BadgeStyles } from './common/constants'; -import { CaptionText } from '@renderer/components/ui-redesign'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { CaptionText } from '@renderer/shared/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { variant: Variant; diff --git a/src/renderer/components/ui-redesign/Counter/common/constants.ts b/src/renderer/shared/ui/Counter/common/constants.ts similarity index 100% rename from src/renderer/components/ui-redesign/Counter/common/constants.ts rename to src/renderer/shared/ui/Counter/common/constants.ts diff --git a/src/renderer/components/ui-redesign/Counter/common/types.ts b/src/renderer/shared/ui/Counter/common/types.ts similarity index 100% rename from src/renderer/components/ui-redesign/Counter/common/types.ts rename to src/renderer/shared/ui/Counter/common/types.ts diff --git a/src/renderer/components/common/DetailRow/DetailRow.tsx b/src/renderer/shared/ui/DetailRow/DetailRow.tsx similarity index 92% rename from src/renderer/components/common/DetailRow/DetailRow.tsx rename to src/renderer/shared/ui/DetailRow/DetailRow.tsx index 7838c3f813..ae2eda6232 100644 --- a/src/renderer/components/common/DetailRow/DetailRow.tsx +++ b/src/renderer/shared/ui/DetailRow/DetailRow.tsx @@ -1,7 +1,7 @@ import { PropsWithChildren, ReactNode } from 'react'; import cn from 'classnames'; -import { FootnoteText } from '@renderer/components/ui-redesign'; +import { FootnoteText } from '@renderer/shared/ui'; type Props = { label: ReactNode; diff --git a/src/renderer/components/ui-redesign/Dropdowns/Combobox/Combobox.stories.tsx b/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.stories.tsx similarity index 96% rename from src/renderer/components/ui-redesign/Dropdowns/Combobox/Combobox.stories.tsx rename to src/renderer/shared/ui/Dropdowns/Combobox/Combobox.stories.tsx index abbfcdb748..400a9e88e0 100644 --- a/src/renderer/components/ui-redesign/Dropdowns/Combobox/Combobox.stories.tsx +++ b/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.stories.tsx @@ -1,6 +1,6 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Icon, Identicon } from '@renderer/components/ui'; +import { Icon, Identicon } from '@renderer/shared/ui'; import Combobox from './Combobox'; export default { diff --git a/src/renderer/components/ui-redesign/Dropdowns/Combobox/Combobox.test.tsx b/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Dropdowns/Combobox/Combobox.test.tsx rename to src/renderer/shared/ui/Dropdowns/Combobox/Combobox.test.tsx diff --git a/src/renderer/components/ui-redesign/Dropdowns/Combobox/Combobox.tsx b/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.tsx similarity index 90% rename from src/renderer/components/ui-redesign/Dropdowns/Combobox/Combobox.tsx rename to src/renderer/shared/ui/Dropdowns/Combobox/Combobox.tsx index 49dc17205c..7eef9a438a 100644 --- a/src/renderer/components/ui-redesign/Dropdowns/Combobox/Combobox.tsx +++ b/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.tsx @@ -1,12 +1,11 @@ import { Fragment, useState } from 'react'; import { Transition, Combobox as HeadlessCombobox } from '@headlessui/react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { Props as InputProps } from '@renderer/components/ui-redesign/Inputs/Input/Input'; +import { cnTw, includes } from '@renderer/shared/lib/utils'; +import { Props as InputProps } from '@renderer/shared/ui/Inputs/Input/Input'; import { OptionsContainerStyle, OptionStyle, OptionStyleTheme, ViewClass } from '../common/constants'; import { Position, ComboboxOption, Theme } from '../common/types'; -import { includes } from '@renderer/shared/utils/strings'; -import { FootnoteText, Input } from '@renderer/components/ui-redesign'; +import { FootnoteText, Input } from '@renderer/shared/ui'; type Props = Omit & { options: ComboboxOption[]; diff --git a/src/renderer/components/ui-redesign/Dropdowns/DropdownButton/DropdownButton.stories.tsx b/src/renderer/shared/ui/Dropdowns/DropdownButton/DropdownButton.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Dropdowns/DropdownButton/DropdownButton.stories.tsx rename to src/renderer/shared/ui/Dropdowns/DropdownButton/DropdownButton.stories.tsx diff --git a/src/renderer/components/ui-redesign/Dropdowns/DropdownButton/DropdownButton.test.tsx b/src/renderer/shared/ui/Dropdowns/DropdownButton/DropdownButton.test.tsx similarity index 95% rename from src/renderer/components/ui-redesign/Dropdowns/DropdownButton/DropdownButton.test.tsx rename to src/renderer/shared/ui/Dropdowns/DropdownButton/DropdownButton.test.tsx index d9c99d06d3..6bebe23ff5 100644 --- a/src/renderer/components/ui-redesign/Dropdowns/DropdownButton/DropdownButton.test.tsx +++ b/src/renderer/shared/ui/Dropdowns/DropdownButton/DropdownButton.test.tsx @@ -3,7 +3,7 @@ import noop from 'lodash/noop'; import DropdownButton, { ButtonDropdownOption } from './DropdownButton'; -describe('ui-redesign/Dropdowns/DropdownButton', () => { +describe('ui/Dropdowns/DropdownButton', () => { const options: ButtonDropdownOption[] = [ { id: '0', title: 'label_0', iconName: 'globe', onClick: noop }, { id: '1', title: 'label_1', iconName: 'globe', onClick: noop }, diff --git a/src/renderer/components/ui-redesign/Dropdowns/DropdownButton/DropdownButton.tsx b/src/renderer/shared/ui/Dropdowns/DropdownButton/DropdownButton.tsx similarity index 91% rename from src/renderer/components/ui-redesign/Dropdowns/DropdownButton/DropdownButton.tsx rename to src/renderer/shared/ui/Dropdowns/DropdownButton/DropdownButton.tsx index f5c3b9c333..9081d539a4 100644 --- a/src/renderer/components/ui-redesign/Dropdowns/DropdownButton/DropdownButton.tsx +++ b/src/renderer/shared/ui/Dropdowns/DropdownButton/DropdownButton.tsx @@ -3,10 +3,9 @@ import { Menu } from '@headlessui/react'; import { ComponentProps } from 'react'; import { Link } from 'react-router-dom'; -import { IconNames } from '@renderer/components/ui/Icon/data'; -import { Button, FootnoteText } from '@renderer/components/ui-redesign'; -import { Icon } from '@renderer/components/ui'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { IconNames } from '@renderer/shared/ui/Icon/data'; +import { Button, FootnoteText, Icon } from '@renderer/shared/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; type ButtonProps = ComponentProps; diff --git a/src/renderer/components/ui-redesign/Dropdowns/MultiSelect/MultiSelect.stories.tsx b/src/renderer/shared/ui/Dropdowns/MultiSelect/MultiSelect.stories.tsx similarity index 97% rename from src/renderer/components/ui-redesign/Dropdowns/MultiSelect/MultiSelect.stories.tsx rename to src/renderer/shared/ui/Dropdowns/MultiSelect/MultiSelect.stories.tsx index 30ba7b2b75..845d5bfa6f 100644 --- a/src/renderer/components/ui-redesign/Dropdowns/MultiSelect/MultiSelect.stories.tsx +++ b/src/renderer/shared/ui/Dropdowns/MultiSelect/MultiSelect.stories.tsx @@ -1,6 +1,6 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Identicon } from '@renderer/components/ui'; +import { Identicon } from '@renderer/shared/ui'; import MultiSelect from './MultiSelect'; export default { diff --git a/src/renderer/components/ui-redesign/Dropdowns/MultiSelect/MultiSelect.test.tsx b/src/renderer/shared/ui/Dropdowns/MultiSelect/MultiSelect.test.tsx similarity index 95% rename from src/renderer/components/ui-redesign/Dropdowns/MultiSelect/MultiSelect.test.tsx rename to src/renderer/shared/ui/Dropdowns/MultiSelect/MultiSelect.test.tsx index aea6ee6bf5..c04e04d3b0 100644 --- a/src/renderer/components/ui-redesign/Dropdowns/MultiSelect/MultiSelect.test.tsx +++ b/src/renderer/shared/ui/Dropdowns/MultiSelect/MultiSelect.test.tsx @@ -2,7 +2,7 @@ import { act, render, screen } from '@testing-library/react'; import MultiSelect from './MultiSelect'; -describe('ui-redesign/Dropdowns/MultiSelect', () => { +describe('ui/Dropdowns/MultiSelect', () => { const options = [ { id: '0', element: 'label_0', value: '0' }, { id: '1', element: 'label_1', value: '1' }, diff --git a/src/renderer/components/ui-redesign/Dropdowns/MultiSelect/MultiSelect.tsx b/src/renderer/shared/ui/Dropdowns/MultiSelect/MultiSelect.tsx similarity index 94% rename from src/renderer/components/ui-redesign/Dropdowns/MultiSelect/MultiSelect.tsx rename to src/renderer/shared/ui/Dropdowns/MultiSelect/MultiSelect.tsx index e71fa977e7..4f58b6993e 100644 --- a/src/renderer/components/ui-redesign/Dropdowns/MultiSelect/MultiSelect.tsx +++ b/src/renderer/shared/ui/Dropdowns/MultiSelect/MultiSelect.tsx @@ -1,11 +1,10 @@ import { Listbox, Transition } from '@headlessui/react'; import { Fragment, useId } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { Icon } from '@renderer/components/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { Icon, Checkbox, FootnoteText, LabelText, CaptionText } from '@renderer/shared/ui'; import { DropdownOption, DropdownResult, Position, Theme } from '../common/types'; -import { CommonInputStyles, CommonInputStylesTheme } from '@renderer/components/ui-redesign/Inputs/common/styles'; -import { Checkbox, FootnoteText, LabelText, CaptionText } from '@renderer/components/ui-redesign'; +import { CommonInputStyles, CommonInputStylesTheme } from '@renderer/shared/ui/Inputs/common/styles'; import { OptionsContainerStyle, OptionStyle, diff --git a/src/renderer/components/ui-redesign/Dropdowns/Select/Select.stories.tsx b/src/renderer/shared/ui/Dropdowns/Select/Select.stories.tsx similarity index 98% rename from src/renderer/components/ui-redesign/Dropdowns/Select/Select.stories.tsx rename to src/renderer/shared/ui/Dropdowns/Select/Select.stories.tsx index 3612394f49..11c9420558 100644 --- a/src/renderer/components/ui-redesign/Dropdowns/Select/Select.stories.tsx +++ b/src/renderer/shared/ui/Dropdowns/Select/Select.stories.tsx @@ -1,6 +1,6 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Identicon } from '@renderer/components/ui'; +import { Identicon } from '@renderer/shared/ui'; import Select from './Select'; export default { diff --git a/src/renderer/components/ui-redesign/Dropdowns/Select/Select.test.tsx b/src/renderer/shared/ui/Dropdowns/Select/Select.test.tsx similarity index 95% rename from src/renderer/components/ui-redesign/Dropdowns/Select/Select.test.tsx rename to src/renderer/shared/ui/Dropdowns/Select/Select.test.tsx index 6ac9e33307..5f81f04d84 100644 --- a/src/renderer/components/ui-redesign/Dropdowns/Select/Select.test.tsx +++ b/src/renderer/shared/ui/Dropdowns/Select/Select.test.tsx @@ -2,7 +2,7 @@ import { act, render, screen } from '@testing-library/react'; import Select from './Select'; -describe('ui-redesign/Dropdowns/Select', () => { +describe('ui/Dropdowns/Select', () => { const options = [ { id: '0', element: 'label_0', value: '0' }, { id: '1', element: 'label_1', value: '1' }, diff --git a/src/renderer/components/ui-redesign/Dropdowns/Select/Select.tsx b/src/renderer/shared/ui/Dropdowns/Select/Select.tsx similarity index 94% rename from src/renderer/components/ui-redesign/Dropdowns/Select/Select.tsx rename to src/renderer/shared/ui/Dropdowns/Select/Select.tsx index 165a4c90c7..cf64e5d4b0 100644 --- a/src/renderer/components/ui-redesign/Dropdowns/Select/Select.tsx +++ b/src/renderer/shared/ui/Dropdowns/Select/Select.tsx @@ -1,11 +1,10 @@ import { Listbox, Transition } from '@headlessui/react'; import { Fragment, useId } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { Icon } from '@renderer/components/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { Icon, FootnoteText, LabelText } from '@renderer/shared/ui'; import { DropdownOption, DropdownResult, Position, Theme } from '../common/types'; -import { CommonInputStyles, CommonInputStylesTheme } from '@renderer/components/ui-redesign/Inputs/common/styles'; -import { FootnoteText, LabelText } from '@renderer/components/ui-redesign'; +import { CommonInputStyles, CommonInputStylesTheme } from '@renderer/shared/ui/Inputs/common/styles'; import { ButtonTextFilledStyle, ButtonTextEmptyStyle, diff --git a/src/renderer/components/ui-redesign/Dropdowns/common/constants.ts b/src/renderer/shared/ui/Dropdowns/common/constants.ts similarity index 97% rename from src/renderer/components/ui-redesign/Dropdowns/common/constants.ts rename to src/renderer/shared/ui/Dropdowns/common/constants.ts index e3ffb54a47..0dda34f0f8 100644 --- a/src/renderer/components/ui-redesign/Dropdowns/common/constants.ts +++ b/src/renderer/shared/ui/Dropdowns/common/constants.ts @@ -1,5 +1,5 @@ -import cnTw from '@renderer/shared/utils/twMerge'; import { Position, Theme } from './types'; +import { cnTw } from '@renderer/shared/lib/utils'; export const ViewClass: Record, string> = { up: 'bottom-full mb-2.5', diff --git a/src/renderer/components/ui-redesign/Dropdowns/common/types.ts b/src/renderer/shared/ui/Dropdowns/common/types.ts similarity index 100% rename from src/renderer/components/ui-redesign/Dropdowns/common/types.ts rename to src/renderer/shared/ui/Dropdowns/common/types.ts diff --git a/src/renderer/components/ui/Duration/Duration.stories.tsx b/src/renderer/shared/ui/Duration/Duration.stories.tsx similarity index 100% rename from src/renderer/components/ui/Duration/Duration.stories.tsx rename to src/renderer/shared/ui/Duration/Duration.stories.tsx diff --git a/src/renderer/components/ui/Duration/Duration.test.tsx b/src/renderer/shared/ui/Duration/Duration.test.tsx similarity index 88% rename from src/renderer/components/ui/Duration/Duration.test.tsx rename to src/renderer/shared/ui/Duration/Duration.test.tsx index 97a4f0b17e..a780e8edd3 100644 --- a/src/renderer/components/ui/Duration/Duration.test.tsx +++ b/src/renderer/shared/ui/Duration/Duration.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'; import Duration from './Duration'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/components/ui/Duration/Duration.tsx b/src/renderer/shared/ui/Duration/Duration.tsx similarity index 88% rename from src/renderer/components/ui/Duration/Duration.tsx rename to src/renderer/shared/ui/Duration/Duration.tsx index 98983a15a3..9226059e83 100644 --- a/src/renderer/components/ui/Duration/Duration.tsx +++ b/src/renderer/shared/ui/Duration/Duration.tsx @@ -1,6 +1,6 @@ /* eslint-disable i18next/no-literal-string */ -import { useI18n } from '@renderer/context/I18nContext'; -import { secondsToDuration, getDurationFormat, getDurationParams, DurationFormat } from '@renderer/shared/utils/time'; +import { useI18n } from '@renderer/app/providers'; +import { secondsToDuration, getDurationFormat, getDurationParams, DurationFormat } from '@renderer/shared/lib/utils'; interface Props { seconds: string; diff --git a/src/renderer/components/ui/Icon/Icon.stories.tsx b/src/renderer/shared/ui/Icon/Icon.stories.tsx similarity index 100% rename from src/renderer/components/ui/Icon/Icon.stories.tsx rename to src/renderer/shared/ui/Icon/Icon.stories.tsx diff --git a/src/renderer/components/ui/Icon/Icon.test.tsx b/src/renderer/shared/ui/Icon/Icon.test.tsx similarity index 100% rename from src/renderer/components/ui/Icon/Icon.test.tsx rename to src/renderer/shared/ui/Icon/Icon.test.tsx diff --git a/src/renderer/components/ui/Icon/Icon.tsx b/src/renderer/shared/ui/Icon/Icon.tsx similarity index 95% rename from src/renderer/components/ui/Icon/Icon.tsx rename to src/renderer/shared/ui/Icon/Icon.tsx index ac9a747553..f698ffbb99 100644 --- a/src/renderer/components/ui/Icon/Icon.tsx +++ b/src/renderer/shared/ui/Icon/Icon.tsx @@ -1,5 +1,5 @@ import AllIcons, { IconNames } from './data'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { as?: 'img' | 'svg'; diff --git a/src/renderer/components/ui/Icon/data/aesthetics.tsx b/src/renderer/shared/ui/Icon/data/aesthetics.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/aesthetics.tsx rename to src/renderer/shared/ui/Icon/data/aesthetics.tsx diff --git a/src/renderer/components/ui/Icon/data/arrow.tsx b/src/renderer/shared/ui/Icon/data/arrow.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/arrow.tsx rename to src/renderer/shared/ui/Icon/data/arrow.tsx diff --git a/src/renderer/components/ui/Icon/data/chevron.tsx b/src/renderer/shared/ui/Icon/data/chevron.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/chevron.tsx rename to src/renderer/shared/ui/Icon/data/chevron.tsx diff --git a/src/renderer/components/ui/Icon/data/currency.tsx b/src/renderer/shared/ui/Icon/data/currency.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/currency.tsx rename to src/renderer/shared/ui/Icon/data/currency.tsx diff --git a/src/renderer/components/ui/Icon/data/explorer.tsx b/src/renderer/shared/ui/Icon/data/explorer.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/explorer.tsx rename to src/renderer/shared/ui/Icon/data/explorer.tsx diff --git a/src/renderer/components/ui/Icon/data/flag.tsx b/src/renderer/shared/ui/Icon/data/flag.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/flag.tsx rename to src/renderer/shared/ui/Icon/data/flag.tsx diff --git a/src/renderer/components/ui/Icon/data/functionals.tsx b/src/renderer/shared/ui/Icon/data/functionals.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/functionals.tsx rename to src/renderer/shared/ui/Icon/data/functionals.tsx diff --git a/src/renderer/components/ui/Icon/data/index.ts b/src/renderer/shared/ui/Icon/data/index.ts similarity index 94% rename from src/renderer/components/ui/Icon/data/index.ts rename to src/renderer/shared/ui/Icon/data/index.ts index 5db9d298e2..c17337c06d 100644 --- a/src/renderer/components/ui/Icon/data/index.ts +++ b/src/renderer/shared/ui/Icon/data/index.ts @@ -10,7 +10,7 @@ import ArrowImages, { Arrow } from './arrow'; import WalletTypeImages, { WalletImages } from './walletType'; import StakingImages, { Staking } from './staking'; import SocialImages, { Social } from './social'; -import MstImages, { Mst } from '@renderer/components/ui/Icon/data/mst'; +import MstImages, { Mst } from '@renderer/shared/ui/Icon/data/mst'; const AllIcons = { ...CurrencyImages, diff --git a/src/renderer/components/ui/Icon/data/misc.tsx b/src/renderer/shared/ui/Icon/data/misc.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/misc.tsx rename to src/renderer/shared/ui/Icon/data/misc.tsx diff --git a/src/renderer/components/ui/Icon/data/mst.tsx b/src/renderer/shared/ui/Icon/data/mst.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/mst.tsx rename to src/renderer/shared/ui/Icon/data/mst.tsx diff --git a/src/renderer/components/ui/Icon/data/navigation.tsx b/src/renderer/shared/ui/Icon/data/navigation.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/navigation.tsx rename to src/renderer/shared/ui/Icon/data/navigation.tsx diff --git a/src/renderer/components/ui/Icon/data/social.tsx b/src/renderer/shared/ui/Icon/data/social.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/social.tsx rename to src/renderer/shared/ui/Icon/data/social.tsx diff --git a/src/renderer/components/ui/Icon/data/staking.tsx b/src/renderer/shared/ui/Icon/data/staking.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/staking.tsx rename to src/renderer/shared/ui/Icon/data/staking.tsx diff --git a/src/renderer/components/ui/Icon/data/walletType.tsx b/src/renderer/shared/ui/Icon/data/walletType.tsx similarity index 100% rename from src/renderer/components/ui/Icon/data/walletType.tsx rename to src/renderer/shared/ui/Icon/data/walletType.tsx diff --git a/src/renderer/components/ui/Identicon/Identicon.stories.tsx b/src/renderer/shared/ui/Identicon/Identicon.stories.tsx similarity index 91% rename from src/renderer/components/ui/Identicon/Identicon.stories.tsx rename to src/renderer/shared/ui/Identicon/Identicon.stories.tsx index 5ce91fc154..baad131390 100644 --- a/src/renderer/components/ui/Identicon/Identicon.stories.tsx +++ b/src/renderer/shared/ui/Identicon/Identicon.stories.tsx @@ -1,7 +1,7 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; import { SigningType } from '@renderer/domain/shared-kernel'; -import { TEST_ADDRESS } from '@renderer/shared/utils/constants'; +import { TEST_ADDRESS } from '@renderer/shared/lib/utils'; import Identicon from './Identicon'; export default { diff --git a/src/renderer/components/ui/Identicon/Identicon.test.tsx b/src/renderer/shared/ui/Identicon/Identicon.test.tsx similarity index 92% rename from src/renderer/components/ui/Identicon/Identicon.test.tsx rename to src/renderer/shared/ui/Identicon/Identicon.test.tsx index c33b8fa289..a5b380dc61 100644 --- a/src/renderer/components/ui/Identicon/Identicon.test.tsx +++ b/src/renderer/shared/ui/Identicon/Identicon.test.tsx @@ -1,7 +1,7 @@ import { render, screen } from '@testing-library/react'; import { SigningType } from '@renderer/domain/shared-kernel'; -import { TEST_ADDRESS } from '@renderer/shared/utils/constants'; +import { TEST_ADDRESS } from '@renderer/shared/lib/utils'; import Identicon from './Identicon'; describe('ui/Identicon', () => { diff --git a/src/renderer/components/ui/Identicon/Identicon.tsx b/src/renderer/shared/ui/Identicon/Identicon.tsx similarity index 95% rename from src/renderer/components/ui/Identicon/Identicon.tsx rename to src/renderer/shared/ui/Identicon/Identicon.tsx index 6144c36ff9..40df6e8885 100644 --- a/src/renderer/components/ui/Identicon/Identicon.tsx +++ b/src/renderer/shared/ui/Identicon/Identicon.tsx @@ -2,9 +2,8 @@ import { Identicon as PolkadotIdenticon } from '@polkadot/react-identicon'; import { IconTheme } from '@polkadot/react-identicon/types'; import { ReactNode, useLayoutEffect, useRef, memo, SyntheticEvent } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw, copyToClipboard } from '@renderer/shared/lib/utils'; import { SigningType, Address } from '@renderer/domain/shared-kernel'; -import { copyToClipboard } from '@renderer/shared/utils/strings'; import Icon from '../Icon/Icon'; const Badges: Record ReactNode> = { diff --git a/src/renderer/components/ui-redesign/InfoLink/InfoLink.stories.tsx b/src/renderer/shared/ui/InfoLink/InfoLink.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/InfoLink/InfoLink.stories.tsx rename to src/renderer/shared/ui/InfoLink/InfoLink.stories.tsx diff --git a/src/renderer/components/ui-redesign/InfoLink/InfoLink.test.tsx b/src/renderer/shared/ui/InfoLink/InfoLink.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/InfoLink/InfoLink.test.tsx rename to src/renderer/shared/ui/InfoLink/InfoLink.test.tsx diff --git a/src/renderer/components/ui-redesign/InfoLink/InfoLink.tsx b/src/renderer/shared/ui/InfoLink/InfoLink.tsx similarity index 83% rename from src/renderer/components/ui-redesign/InfoLink/InfoLink.tsx rename to src/renderer/shared/ui/InfoLink/InfoLink.tsx index fc784efeba..0763f75b56 100644 --- a/src/renderer/components/ui-redesign/InfoLink/InfoLink.tsx +++ b/src/renderer/shared/ui/InfoLink/InfoLink.tsx @@ -1,8 +1,8 @@ import { PropsWithChildren } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { IconNames } from '@renderer/components/ui/Icon/data'; -import { Icon } from '@renderer/components/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { IconNames } from '@renderer/shared/ui/Icon/data'; +import { Icon } from '@renderer/shared/ui'; type Props = { url: string; diff --git a/src/renderer/components/ui-redesign/InputHint/InputHint.stories.tsx b/src/renderer/shared/ui/InputHint/InputHint.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/InputHint/InputHint.stories.tsx rename to src/renderer/shared/ui/InputHint/InputHint.stories.tsx diff --git a/src/renderer/components/ui-redesign/InputHint/InputHint.test.tsx b/src/renderer/shared/ui/InputHint/InputHint.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/InputHint/InputHint.test.tsx rename to src/renderer/shared/ui/InputHint/InputHint.test.tsx diff --git a/src/renderer/components/ui-redesign/InputHint/InputHint.tsx b/src/renderer/shared/ui/InputHint/InputHint.tsx similarity index 63% rename from src/renderer/components/ui-redesign/InputHint/InputHint.tsx rename to src/renderer/shared/ui/InputHint/InputHint.tsx index 68badfdb61..e94e322e7a 100644 --- a/src/renderer/components/ui-redesign/InputHint/InputHint.tsx +++ b/src/renderer/shared/ui/InputHint/InputHint.tsx @@ -1,6 +1,6 @@ -import cnTw from '@renderer/shared/utils/twMerge'; -import { TypographyProps } from '@renderer/components/ui-redesign/Typography/common/types'; -import { HintVariant, HintStyles } from '@renderer/components/ui-redesign/InputHint/contants'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { TypographyProps } from '@renderer/shared/ui/Typography/common/types'; +import { HintVariant, HintStyles } from '@renderer/shared/ui/InputHint/contants'; import { FootnoteText } from '../Typography'; type Props = { diff --git a/src/renderer/components/ui-redesign/InputHint/contants.ts b/src/renderer/shared/ui/InputHint/contants.ts similarity index 100% rename from src/renderer/components/ui-redesign/InputHint/contants.ts rename to src/renderer/shared/ui/InputHint/contants.ts diff --git a/src/renderer/components/ui-redesign/Inputs/AmountInput/AmountInput.tsx b/src/renderer/shared/ui/Inputs/AmountInput/AmountInput.tsx similarity index 78% rename from src/renderer/components/ui-redesign/Inputs/AmountInput/AmountInput.tsx rename to src/renderer/shared/ui/Inputs/AmountInput/AmountInput.tsx index 005623211b..0c608635fe 100644 --- a/src/renderer/components/ui-redesign/Inputs/AmountInput/AmountInput.tsx +++ b/src/renderer/shared/ui/Inputs/AmountInput/AmountInput.tsx @@ -1,12 +1,11 @@ import { useCallback } from 'react'; -import { useI18n } from '@renderer/context/I18nContext'; -import { Asset } from '@renderer/domain/asset'; +import { useI18n } from '@renderer/app/providers'; import { FootnoteText, TitleText } from '../../Typography'; -import { BalanceNew } from '@renderer/components/common'; +// FIXME components in shared shouldn't use components from entity so we need to move it to entity +import { AssetBalance, AssetIcon, Asset } from '@renderer/entities/asset'; import Input from '../Input/Input'; -import { cleanAmount, formatGroups, validatePrecision, validateSymbols } from '@renderer/shared/utils/balance'; -import { AssetIcon } from '@renderer/components/ui-redesign'; +import { cleanAmount, formatGroups, validatePrecision, validateSymbols } from '@renderer/shared/lib/utils'; type Props = { name?: string; @@ -49,15 +48,15 @@ const AmountInput = ({ if (Array.isArray(balance)) { return ( - + - - + ); } return ( - + ); }, [balance]); diff --git a/src/renderer/components/ui-redesign/Inputs/Input/Input.stories.tsx b/src/renderer/shared/ui/Inputs/Input/Input.stories.tsx similarity index 95% rename from src/renderer/components/ui-redesign/Inputs/Input/Input.stories.tsx rename to src/renderer/shared/ui/Inputs/Input/Input.stories.tsx index 47cba9581e..385bc5f617 100644 --- a/src/renderer/components/ui-redesign/Inputs/Input/Input.stories.tsx +++ b/src/renderer/shared/ui/Inputs/Input/Input.stories.tsx @@ -1,7 +1,7 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; import Input from './Input'; -import { Icon } from '@renderer/components/ui'; +import { Icon } from '@renderer/shared/ui'; export default { title: 'Redesign/Input', diff --git a/src/renderer/components/ui-redesign/Inputs/Input/Input.test.tsx b/src/renderer/shared/ui/Inputs/Input/Input.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Inputs/Input/Input.test.tsx rename to src/renderer/shared/ui/Inputs/Input/Input.test.tsx diff --git a/src/renderer/components/ui-redesign/Inputs/Input/Input.tsx b/src/renderer/shared/ui/Inputs/Input/Input.tsx similarity index 97% rename from src/renderer/components/ui-redesign/Inputs/Input/Input.tsx rename to src/renderer/shared/ui/Inputs/Input/Input.tsx index ab945b2ffe..2235f168b3 100644 --- a/src/renderer/components/ui-redesign/Inputs/Input/Input.tsx +++ b/src/renderer/shared/ui/Inputs/Input/Input.tsx @@ -1,6 +1,6 @@ import { ReactNode, ComponentPropsWithoutRef, forwardRef, useId } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { LabelText } from '../../Typography'; import { HTMLInputProps } from '../common/types'; import { CommonInputStyles, CommonInputStylesTheme } from '../common/styles'; diff --git a/src/renderer/components/ui-redesign/Inputs/InputArea/InputArea.stories.tsx b/src/renderer/shared/ui/Inputs/InputArea/InputArea.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Inputs/InputArea/InputArea.stories.tsx rename to src/renderer/shared/ui/Inputs/InputArea/InputArea.stories.tsx diff --git a/src/renderer/components/ui/Inputs/InputArea/InputArea.test.tsx b/src/renderer/shared/ui/Inputs/InputArea/InputArea.test.tsx similarity index 100% rename from src/renderer/components/ui/Inputs/InputArea/InputArea.test.tsx rename to src/renderer/shared/ui/Inputs/InputArea/InputArea.test.tsx diff --git a/src/renderer/components/ui-redesign/Inputs/InputArea/InputArea.tsx b/src/renderer/shared/ui/Inputs/InputArea/InputArea.tsx similarity index 95% rename from src/renderer/components/ui-redesign/Inputs/InputArea/InputArea.tsx rename to src/renderer/shared/ui/Inputs/InputArea/InputArea.tsx index 00de88c25d..13beddade6 100644 --- a/src/renderer/components/ui-redesign/Inputs/InputArea/InputArea.tsx +++ b/src/renderer/shared/ui/Inputs/InputArea/InputArea.tsx @@ -1,6 +1,6 @@ import { ComponentPropsWithoutRef, forwardRef } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { HTMLTextAreaProps } from '../common/types'; import { CommonInputStyles, CommonInputStylesTheme } from '../common/styles'; import { Theme } from '../../Dropdowns/common/types'; diff --git a/src/renderer/components/ui-redesign/Inputs/InputFile/InputFile.stories.tsx b/src/renderer/shared/ui/Inputs/InputFile/InputFile.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Inputs/InputFile/InputFile.stories.tsx rename to src/renderer/shared/ui/Inputs/InputFile/InputFile.stories.tsx diff --git a/src/renderer/components/ui-redesign/Inputs/InputFile/InputFile.test.tsx b/src/renderer/shared/ui/Inputs/InputFile/InputFile.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Inputs/InputFile/InputFile.test.tsx rename to src/renderer/shared/ui/Inputs/InputFile/InputFile.test.tsx diff --git a/src/renderer/components/ui-redesign/Inputs/InputFile/InputFile.tsx b/src/renderer/shared/ui/Inputs/InputFile/InputFile.tsx similarity index 87% rename from src/renderer/components/ui-redesign/Inputs/InputFile/InputFile.tsx rename to src/renderer/shared/ui/Inputs/InputFile/InputFile.tsx index b5a4eea576..4b1431d1eb 100644 --- a/src/renderer/components/ui-redesign/Inputs/InputFile/InputFile.tsx +++ b/src/renderer/shared/ui/Inputs/InputFile/InputFile.tsx @@ -1,10 +1,10 @@ import { useState, ChangeEvent, ComponentPropsWithoutRef, forwardRef } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { HTMLInputFileProps } from '../common/types'; -import { Icon } from '@renderer/components/ui'; -import TextBase from '@renderer/components/ui-redesign/Typography/common/TextBase'; -import { FootnoteText } from '@renderer/components/ui-redesign'; +import TextBase from '@renderer/shared/ui/Typography/common/TextBase'; +import Icon from '../../Icon/Icon'; +import { FootnoteText } from '../../Typography'; interface Props extends Pick, HTMLInputFileProps> { invalid?: boolean; diff --git a/src/renderer/components/ui-redesign/Inputs/PasswordInput/PasswordInput.stories.tsx b/src/renderer/shared/ui/Inputs/PasswordInput/PasswordInput.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Inputs/PasswordInput/PasswordInput.stories.tsx rename to src/renderer/shared/ui/Inputs/PasswordInput/PasswordInput.stories.tsx diff --git a/src/renderer/components/ui-redesign/Inputs/PasswordInput/PasswordInput.test.tsx b/src/renderer/shared/ui/Inputs/PasswordInput/PasswordInput.test.tsx similarity index 95% rename from src/renderer/components/ui-redesign/Inputs/PasswordInput/PasswordInput.test.tsx rename to src/renderer/shared/ui/Inputs/PasswordInput/PasswordInput.test.tsx index e093d86e12..46d98d9787 100644 --- a/src/renderer/components/ui-redesign/Inputs/PasswordInput/PasswordInput.test.tsx +++ b/src/renderer/shared/ui/Inputs/PasswordInput/PasswordInput.test.tsx @@ -2,7 +2,7 @@ import { render, screen, act } from '@testing-library/react'; import PasswordInput from './PasswordInput'; -jest.mock('@renderer/context/I18nContext', () => ({ +jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ t: (key: string) => key, }), diff --git a/src/renderer/components/ui-redesign/Inputs/PasswordInput/PasswordInput.tsx b/src/renderer/shared/ui/Inputs/PasswordInput/PasswordInput.tsx similarity index 84% rename from src/renderer/components/ui-redesign/Inputs/PasswordInput/PasswordInput.tsx rename to src/renderer/shared/ui/Inputs/PasswordInput/PasswordInput.tsx index cf6229d786..45f47222ea 100644 --- a/src/renderer/components/ui-redesign/Inputs/PasswordInput/PasswordInput.tsx +++ b/src/renderer/shared/ui/Inputs/PasswordInput/PasswordInput.tsx @@ -1,9 +1,9 @@ import { forwardRef } from 'react'; import Input, { Props as InputProps } from '../Input/Input'; -import Icon from '@renderer/components/ui/Icon/Icon'; -import { useI18n } from '@renderer/context/I18nContext'; -import { useToggle } from '@renderer/shared/hooks'; +import Icon from '@renderer/shared/ui/Icon/Icon'; +import { useI18n } from '@renderer/app/providers'; +import { useToggle } from '@renderer/shared/lib/hooks'; type Props = Omit; diff --git a/src/renderer/components/ui-redesign/Inputs/SearchInput/SearchInput.tsx b/src/renderer/shared/ui/Inputs/SearchInput/SearchInput.tsx similarity index 81% rename from src/renderer/components/ui-redesign/Inputs/SearchInput/SearchInput.tsx rename to src/renderer/shared/ui/Inputs/SearchInput/SearchInput.tsx index a5f9de27d6..4301568262 100644 --- a/src/renderer/components/ui-redesign/Inputs/SearchInput/SearchInput.tsx +++ b/src/renderer/shared/ui/Inputs/SearchInput/SearchInput.tsx @@ -1,10 +1,9 @@ import { forwardRef } from 'react'; import cn from 'classnames'; -import { Icon } from '@renderer/components/ui'; -import { IconButton, Input } from '@renderer/components/ui-redesign'; +import { Icon, IconButton, Input } from '@renderer/shared/ui'; import { Props as InputProps } from '../Input/Input'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = Omit; diff --git a/src/renderer/components/ui-redesign/Inputs/common/styles.ts b/src/renderer/shared/ui/Inputs/common/styles.ts similarity index 100% rename from src/renderer/components/ui-redesign/Inputs/common/styles.ts rename to src/renderer/shared/ui/Inputs/common/styles.ts diff --git a/src/renderer/components/ui-redesign/Inputs/common/types.ts b/src/renderer/shared/ui/Inputs/common/types.ts similarity index 100% rename from src/renderer/components/ui-redesign/Inputs/common/types.ts rename to src/renderer/shared/ui/Inputs/common/types.ts diff --git a/src/renderer/components/ui-redesign/LabelHelpbox/LabelHelpBox.stories.tsx b/src/renderer/shared/ui/LabelHelpbox/LabelHelpBox.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/LabelHelpbox/LabelHelpBox.stories.tsx rename to src/renderer/shared/ui/LabelHelpbox/LabelHelpBox.stories.tsx diff --git a/src/renderer/components/ui-redesign/LabelHelpbox/LabelHelpBox.test.tsx b/src/renderer/shared/ui/LabelHelpbox/LabelHelpBox.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/LabelHelpbox/LabelHelpBox.test.tsx rename to src/renderer/shared/ui/LabelHelpbox/LabelHelpBox.test.tsx diff --git a/src/renderer/components/ui-redesign/LabelHelpbox/LabelHelpBox.tsx b/src/renderer/shared/ui/LabelHelpbox/LabelHelpBox.tsx similarity index 79% rename from src/renderer/components/ui-redesign/LabelHelpbox/LabelHelpBox.tsx rename to src/renderer/shared/ui/LabelHelpbox/LabelHelpBox.tsx index c069025dbc..382ffe9845 100644 --- a/src/renderer/components/ui-redesign/LabelHelpbox/LabelHelpBox.tsx +++ b/src/renderer/shared/ui/LabelHelpbox/LabelHelpBox.tsx @@ -1,8 +1,7 @@ import { PropsWithChildren } from 'react'; -import { BodyText } from '@renderer/components/ui-redesign'; -import { Icon } from '@renderer/components/ui'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { BodyText, Icon } from '@renderer/shared/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { className?: string; diff --git a/src/renderer/components/ui/LanguageSwitcher/LanguageSwitcher.stories.tsx b/src/renderer/shared/ui/LanguageSwitcher/LanguageSwitcher.stories.tsx similarity index 100% rename from src/renderer/components/ui/LanguageSwitcher/LanguageSwitcher.stories.tsx rename to src/renderer/shared/ui/LanguageSwitcher/LanguageSwitcher.stories.tsx diff --git a/src/renderer/components/ui/LanguageSwitcher/LanguageSwitcher.test.tsx b/src/renderer/shared/ui/LanguageSwitcher/LanguageSwitcher.test.tsx similarity index 100% rename from src/renderer/components/ui/LanguageSwitcher/LanguageSwitcher.test.tsx rename to src/renderer/shared/ui/LanguageSwitcher/LanguageSwitcher.test.tsx diff --git a/src/renderer/components/ui/LanguageSwitcher/LanguageSwitcher.tsx b/src/renderer/shared/ui/LanguageSwitcher/LanguageSwitcher.tsx similarity index 97% rename from src/renderer/components/ui/LanguageSwitcher/LanguageSwitcher.tsx rename to src/renderer/shared/ui/LanguageSwitcher/LanguageSwitcher.tsx index d7ced8a022..7b0e23111e 100644 --- a/src/renderer/components/ui/LanguageSwitcher/LanguageSwitcher.tsx +++ b/src/renderer/shared/ui/LanguageSwitcher/LanguageSwitcher.tsx @@ -1,6 +1,6 @@ import { Listbox } from '@headlessui/react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { LanguageItem, SupportedLocale } from '@renderer/services/translation/common/types'; import Icon from '../Icon/Icon'; diff --git a/src/renderer/components/ui/Loader/Loader.stories.tsx b/src/renderer/shared/ui/Loader/Loader.stories.tsx similarity index 100% rename from src/renderer/components/ui/Loader/Loader.stories.tsx rename to src/renderer/shared/ui/Loader/Loader.stories.tsx diff --git a/src/renderer/components/ui/Loader/Loader.test.tsx b/src/renderer/shared/ui/Loader/Loader.test.tsx similarity index 100% rename from src/renderer/components/ui/Loader/Loader.test.tsx rename to src/renderer/shared/ui/Loader/Loader.test.tsx diff --git a/src/renderer/components/ui/Loader/Loader.tsx b/src/renderer/shared/ui/Loader/Loader.tsx similarity index 78% rename from src/renderer/components/ui/Loader/Loader.tsx rename to src/renderer/shared/ui/Loader/Loader.tsx index cc09c6392c..613b77118f 100644 --- a/src/renderer/components/ui/Loader/Loader.tsx +++ b/src/renderer/shared/ui/Loader/Loader.tsx @@ -1,5 +1,5 @@ -import { Icon } from '@renderer/components/ui'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { Icon } from '@renderer/shared/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { size?: number; diff --git a/src/renderer/components/ui-redesign/Modals/BaseModal/BaseModal.stories.tsx b/src/renderer/shared/ui/Modals/BaseModal/BaseModal.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Modals/BaseModal/BaseModal.stories.tsx rename to src/renderer/shared/ui/Modals/BaseModal/BaseModal.stories.tsx diff --git a/src/renderer/components/ui-redesign/Modals/BaseModal/BaseModal.test.tsx b/src/renderer/shared/ui/Modals/BaseModal/BaseModal.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Modals/BaseModal/BaseModal.test.tsx rename to src/renderer/shared/ui/Modals/BaseModal/BaseModal.test.tsx diff --git a/src/renderer/components/ui-redesign/Modals/BaseModal/BaseModal.tsx b/src/renderer/shared/ui/Modals/BaseModal/BaseModal.tsx similarity index 88% rename from src/renderer/components/ui-redesign/Modals/BaseModal/BaseModal.tsx rename to src/renderer/shared/ui/Modals/BaseModal/BaseModal.tsx index 303ffc401c..279d3500d4 100644 --- a/src/renderer/components/ui-redesign/Modals/BaseModal/BaseModal.tsx +++ b/src/renderer/shared/ui/Modals/BaseModal/BaseModal.tsx @@ -1,10 +1,10 @@ import { Fragment, PropsWithChildren, ReactNode } from 'react'; import { Dialog, Transition } from '@headlessui/react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { ModalBackdrop, ModalTransition } from '@renderer/components/ui-redesign/Modals/common'; -import { useI18n } from '@renderer/context/I18nContext'; -import { HeaderTitleText, IconButton } from '@renderer/components/ui-redesign'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { ModalBackdrop, ModalTransition } from '@renderer/shared/ui/Modals/common'; +import { useI18n } from '@renderer/app/providers'; +import { HeaderTitleText, IconButton } from '@renderer/shared/ui'; // HINT: There are no modals with description right now // HeadlessUI provides description and title with some a11y features diff --git a/src/renderer/components/ui-redesign/Modals/ConfirmModal/ConfirmModal.stories.tsx b/src/renderer/shared/ui/Modals/ConfirmModal/ConfirmModal.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Modals/ConfirmModal/ConfirmModal.stories.tsx rename to src/renderer/shared/ui/Modals/ConfirmModal/ConfirmModal.stories.tsx diff --git a/src/renderer/components/ui-redesign/Modals/ConfirmModal/ConfirmModal.test.tsx b/src/renderer/shared/ui/Modals/ConfirmModal/ConfirmModal.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Modals/ConfirmModal/ConfirmModal.test.tsx rename to src/renderer/shared/ui/Modals/ConfirmModal/ConfirmModal.test.tsx diff --git a/src/renderer/components/ui-redesign/Modals/ConfirmModal/ConfirmModal.tsx b/src/renderer/shared/ui/Modals/ConfirmModal/ConfirmModal.tsx similarity index 89% rename from src/renderer/components/ui-redesign/Modals/ConfirmModal/ConfirmModal.tsx rename to src/renderer/shared/ui/Modals/ConfirmModal/ConfirmModal.tsx index 7153be2ba9..b73aeb2908 100644 --- a/src/renderer/components/ui-redesign/Modals/ConfirmModal/ConfirmModal.tsx +++ b/src/renderer/shared/ui/Modals/ConfirmModal/ConfirmModal.tsx @@ -1,7 +1,7 @@ import { PropsWithChildren } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { Button } from '@renderer/components/ui-redesign'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { Button } from '@renderer/shared/ui'; import BaseModal from '../BaseModal/BaseModal'; type Props = { diff --git a/src/renderer/components/ui-redesign/Modals/common/ModalBackdrop.tsx b/src/renderer/shared/ui/Modals/common/ModalBackdrop.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Modals/common/ModalBackdrop.tsx rename to src/renderer/shared/ui/Modals/common/ModalBackdrop.tsx diff --git a/src/renderer/components/ui-redesign/Modals/common/ModalTransition.tsx b/src/renderer/shared/ui/Modals/common/ModalTransition.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Modals/common/ModalTransition.tsx rename to src/renderer/shared/ui/Modals/common/ModalTransition.tsx diff --git a/src/renderer/components/ui-redesign/Modals/common/index.ts b/src/renderer/shared/ui/Modals/common/index.ts similarity index 100% rename from src/renderer/components/ui-redesign/Modals/common/index.ts rename to src/renderer/shared/ui/Modals/common/index.ts diff --git a/src/renderer/components/ui-redesign/Plate/Plate.stories.tsx b/src/renderer/shared/ui/Plate/Plate.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Plate/Plate.stories.tsx rename to src/renderer/shared/ui/Plate/Plate.stories.tsx diff --git a/src/renderer/components/ui-redesign/Plate/Plate.test.tsx b/src/renderer/shared/ui/Plate/Plate.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Plate/Plate.test.tsx rename to src/renderer/shared/ui/Plate/Plate.test.tsx diff --git a/src/renderer/components/ui-redesign/Plate/Plate.tsx b/src/renderer/shared/ui/Plate/Plate.tsx similarity index 85% rename from src/renderer/components/ui-redesign/Plate/Plate.tsx rename to src/renderer/shared/ui/Plate/Plate.tsx index 9e8fe892bb..3b8f309ccb 100644 --- a/src/renderer/components/ui-redesign/Plate/Plate.tsx +++ b/src/renderer/shared/ui/Plate/Plate.tsx @@ -1,6 +1,6 @@ import { ElementType, PropsWithChildren } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { as?: ElementType; diff --git a/src/renderer/components/ui-redesign/PopoverLink/PopoverLink.stories.tsx b/src/renderer/shared/ui/PopoverLink/PopoverLink.stories.tsx similarity index 87% rename from src/renderer/components/ui-redesign/PopoverLink/PopoverLink.stories.tsx rename to src/renderer/shared/ui/PopoverLink/PopoverLink.stories.tsx index c97f6b048a..c6bb04dfff 100644 --- a/src/renderer/components/ui-redesign/PopoverLink/PopoverLink.stories.tsx +++ b/src/renderer/shared/ui/PopoverLink/PopoverLink.stories.tsx @@ -1,7 +1,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import PopoverLink from './PopoverLink'; -import { Popover } from '@renderer/components/ui-redesign/Popovers/Popover/Popover'; +import { Popover } from '@renderer/shared/ui/Popovers/Popover/Popover'; export default { title: 'Redesign/Popover Link', diff --git a/src/renderer/components/ui-redesign/PopoverLink/PopoverLink.test.tsx b/src/renderer/shared/ui/PopoverLink/PopoverLink.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/PopoverLink/PopoverLink.test.tsx rename to src/renderer/shared/ui/PopoverLink/PopoverLink.test.tsx diff --git a/src/renderer/components/ui-redesign/PopoverLink/PopoverLink.tsx b/src/renderer/shared/ui/PopoverLink/PopoverLink.tsx similarity index 79% rename from src/renderer/components/ui-redesign/PopoverLink/PopoverLink.tsx rename to src/renderer/shared/ui/PopoverLink/PopoverLink.tsx index 8a04380d7b..ca4dfde785 100644 --- a/src/renderer/components/ui-redesign/PopoverLink/PopoverLink.tsx +++ b/src/renderer/shared/ui/PopoverLink/PopoverLink.tsx @@ -1,8 +1,8 @@ import { PropsWithChildren } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { IconNames } from '@renderer/components/ui/Icon/data'; -import { Icon } from '@renderer/components/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { IconNames } from '@renderer/shared/ui/Icon/data'; +import { Icon } from '@renderer/shared/ui'; type Props = { showIcon?: boolean; diff --git a/src/renderer/components/ui-redesign/Popovers/InfoPopover/InfoPopover.stories.tsx b/src/renderer/shared/ui/Popovers/InfoPopover/InfoPopover.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Popovers/InfoPopover/InfoPopover.stories.tsx rename to src/renderer/shared/ui/Popovers/InfoPopover/InfoPopover.stories.tsx diff --git a/src/renderer/components/ui-redesign/Popovers/InfoPopover/InfoPopover.test.tsx b/src/renderer/shared/ui/Popovers/InfoPopover/InfoPopover.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Popovers/InfoPopover/InfoPopover.test.tsx rename to src/renderer/shared/ui/Popovers/InfoPopover/InfoPopover.test.tsx diff --git a/src/renderer/components/ui-redesign/Popovers/InfoPopover/InfoPopover.tsx b/src/renderer/shared/ui/Popovers/InfoPopover/InfoPopover.tsx similarity index 93% rename from src/renderer/components/ui-redesign/Popovers/InfoPopover/InfoPopover.tsx rename to src/renderer/shared/ui/Popovers/InfoPopover/InfoPopover.tsx index 4b33ba81e0..cb56625310 100644 --- a/src/renderer/components/ui-redesign/Popovers/InfoPopover/InfoPopover.tsx +++ b/src/renderer/shared/ui/Popovers/InfoPopover/InfoPopover.tsx @@ -1,9 +1,9 @@ import { PropsWithChildren, ReactNode } from 'react'; import { Menu } from '@headlessui/react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import MenuPopover, { Props as MenuPopoverProps } from '../MenuPopover/MenuPopover'; -import { FootnoteText } from '@renderer/components/ui-redesign'; +import { FootnoteText } from '@renderer/shared/ui'; type Props = { data: InfoSection[]; diff --git a/src/renderer/components/ui-redesign/Popovers/MenuPopover/MenuPopover.tsx b/src/renderer/shared/ui/Popovers/MenuPopover/MenuPopover.tsx similarity index 96% rename from src/renderer/components/ui-redesign/Popovers/MenuPopover/MenuPopover.tsx rename to src/renderer/shared/ui/Popovers/MenuPopover/MenuPopover.tsx index 9dad25d24f..08912ce389 100644 --- a/src/renderer/components/ui-redesign/Popovers/MenuPopover/MenuPopover.tsx +++ b/src/renderer/shared/ui/Popovers/MenuPopover/MenuPopover.tsx @@ -1,7 +1,7 @@ import { Menu } from '@headlessui/react'; import { PropsWithChildren, ReactNode, useRef, MouseEvent } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; export type Props = { content: ReactNode; // for a11y features support use this popover with Menu.Item elements from headless ui diff --git a/src/renderer/components/ui-redesign/Popovers/Popover/Popover.stories.tsx b/src/renderer/shared/ui/Popovers/Popover/Popover.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Popovers/Popover/Popover.stories.tsx rename to src/renderer/shared/ui/Popovers/Popover/Popover.stories.tsx diff --git a/src/renderer/components/ui-redesign/Popovers/Popover/Popover.test.tsx b/src/renderer/shared/ui/Popovers/Popover/Popover.test.tsx similarity index 97% rename from src/renderer/components/ui-redesign/Popovers/Popover/Popover.test.tsx rename to src/renderer/shared/ui/Popovers/Popover/Popover.test.tsx index 002f3fb3ed..7ba61a99de 100644 --- a/src/renderer/components/ui-redesign/Popovers/Popover/Popover.test.tsx +++ b/src/renderer/shared/ui/Popovers/Popover/Popover.test.tsx @@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event'; import { Popover } from './Popover'; -describe('ui-redesign/Popover', () => { +describe('ui/Popover', () => { test('should render component', () => { render(Hover me); diff --git a/src/renderer/components/ui-redesign/Popovers/Popover/Popover.tsx b/src/renderer/shared/ui/Popovers/Popover/Popover.tsx similarity index 95% rename from src/renderer/components/ui-redesign/Popovers/Popover/Popover.tsx rename to src/renderer/shared/ui/Popovers/Popover/Popover.tsx index 0702400168..350e95d374 100644 --- a/src/renderer/components/ui-redesign/Popovers/Popover/Popover.tsx +++ b/src/renderer/shared/ui/Popovers/Popover/Popover.tsx @@ -1,8 +1,8 @@ import { Popover as Popup, Transition } from '@headlessui/react'; import { AriaRole, Fragment, PropsWithChildren, ReactNode, useId, useRef, useState } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { useDebounce } from '@renderer/shared/hooks'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { useDebounce } from '@renderer/shared/lib/hooks'; type Props = { content: ReactNode; diff --git a/src/renderer/components/ui-redesign/Popovers/Tooltip/Tooltip.css b/src/renderer/shared/ui/Popovers/Tooltip/Tooltip.css similarity index 100% rename from src/renderer/components/ui-redesign/Popovers/Tooltip/Tooltip.css rename to src/renderer/shared/ui/Popovers/Tooltip/Tooltip.css diff --git a/src/renderer/components/ui-redesign/Popovers/Tooltip/Tooltip.stories.tsx b/src/renderer/shared/ui/Popovers/Tooltip/Tooltip.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Popovers/Tooltip/Tooltip.stories.tsx rename to src/renderer/shared/ui/Popovers/Tooltip/Tooltip.stories.tsx diff --git a/src/renderer/components/ui-redesign/Popovers/Tooltip/Tooltip.test.tsx b/src/renderer/shared/ui/Popovers/Tooltip/Tooltip.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Popovers/Tooltip/Tooltip.test.tsx rename to src/renderer/shared/ui/Popovers/Tooltip/Tooltip.test.tsx diff --git a/src/renderer/components/ui-redesign/Popovers/Tooltip/Tooltip.tsx b/src/renderer/shared/ui/Popovers/Tooltip/Tooltip.tsx similarity index 87% rename from src/renderer/components/ui-redesign/Popovers/Tooltip/Tooltip.tsx rename to src/renderer/shared/ui/Popovers/Tooltip/Tooltip.tsx index f5263d6871..6904723d56 100644 --- a/src/renderer/components/ui-redesign/Popovers/Tooltip/Tooltip.tsx +++ b/src/renderer/shared/ui/Popovers/Tooltip/Tooltip.tsx @@ -1,7 +1,7 @@ import { ComponentProps } from 'react'; -import { Popover } from '@renderer/components/ui-redesign'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { Popover } from '@renderer/shared/ui'; +import { cnTw } from '@renderer/shared/lib/utils'; import { HelpText } from '../../Typography'; import './Tooltip.css'; diff --git a/src/renderer/components/ui-redesign/RadioGroup/RadioGroup.css b/src/renderer/shared/ui/RadioGroup/RadioGroup.css similarity index 100% rename from src/renderer/components/ui-redesign/RadioGroup/RadioGroup.css rename to src/renderer/shared/ui/RadioGroup/RadioGroup.css diff --git a/src/renderer/components/ui-redesign/RadioGroup/RadioGroup.stories.tsx b/src/renderer/shared/ui/RadioGroup/RadioGroup.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/RadioGroup/RadioGroup.stories.tsx rename to src/renderer/shared/ui/RadioGroup/RadioGroup.stories.tsx diff --git a/src/renderer/components/ui-redesign/RadioGroup/RadioGroup.test.tsx b/src/renderer/shared/ui/RadioGroup/RadioGroup.test.tsx similarity index 100% rename from src/renderer/components/ui-redesign/RadioGroup/RadioGroup.test.tsx rename to src/renderer/shared/ui/RadioGroup/RadioGroup.test.tsx diff --git a/src/renderer/components/ui-redesign/RadioGroup/RadioGroup.tsx b/src/renderer/shared/ui/RadioGroup/RadioGroup.tsx similarity index 94% rename from src/renderer/components/ui-redesign/RadioGroup/RadioGroup.tsx rename to src/renderer/shared/ui/RadioGroup/RadioGroup.tsx index a1d6a58785..e38860bf49 100644 --- a/src/renderer/components/ui-redesign/RadioGroup/RadioGroup.tsx +++ b/src/renderer/shared/ui/RadioGroup/RadioGroup.tsx @@ -3,7 +3,7 @@ import { RadioGroup as HeadlessRadioGroup } from '@headlessui/react'; import Option from './RadioOption'; import { RadioOption, RadioResult } from './common/types'; -import { LabelText } from '@renderer/components/ui-redesign'; +import { LabelText } from '@renderer/shared/ui'; import './RadioGroup.css'; type Props = { diff --git a/src/renderer/components/ui-redesign/RadioGroup/RadioOption.tsx b/src/renderer/shared/ui/RadioGroup/RadioOption.tsx similarity index 93% rename from src/renderer/components/ui-redesign/RadioGroup/RadioOption.tsx rename to src/renderer/shared/ui/RadioGroup/RadioOption.tsx index 3dbc6f4a25..33da0edb92 100644 --- a/src/renderer/components/ui-redesign/RadioGroup/RadioOption.tsx +++ b/src/renderer/shared/ui/RadioGroup/RadioOption.tsx @@ -1,9 +1,9 @@ import { Fragment, PropsWithChildren } from 'react'; import { RadioGroup as HeadlessRadioGroup } from '@headlessui/react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { RadioOption } from './common/types'; -import { SmallTitleText } from '@renderer/components/ui-redesign'; +import { SmallTitleText } from '@renderer/shared/ui'; import './RadioGroup.css'; type Props = { diff --git a/src/renderer/components/ui-redesign/RadioGroup/common/types.ts b/src/renderer/shared/ui/RadioGroup/common/types.ts similarity index 100% rename from src/renderer/components/ui-redesign/RadioGroup/common/types.ts rename to src/renderer/shared/ui/RadioGroup/common/types.ts diff --git a/src/renderer/components/ui/Shimmering/Shimmering.css b/src/renderer/shared/ui/Shimmering/Shimmering.css similarity index 100% rename from src/renderer/components/ui/Shimmering/Shimmering.css rename to src/renderer/shared/ui/Shimmering/Shimmering.css diff --git a/src/renderer/components/ui/Shimmering/Shimmering.stories.tsx b/src/renderer/shared/ui/Shimmering/Shimmering.stories.tsx similarity index 100% rename from src/renderer/components/ui/Shimmering/Shimmering.stories.tsx rename to src/renderer/shared/ui/Shimmering/Shimmering.stories.tsx diff --git a/src/renderer/components/ui/Shimmering/Shimmering.tsx b/src/renderer/shared/ui/Shimmering/Shimmering.tsx similarity index 89% rename from src/renderer/components/ui/Shimmering/Shimmering.tsx rename to src/renderer/shared/ui/Shimmering/Shimmering.tsx index 165681d60f..e6c8b7a311 100644 --- a/src/renderer/components/ui/Shimmering/Shimmering.tsx +++ b/src/renderer/shared/ui/Shimmering/Shimmering.tsx @@ -1,4 +1,4 @@ -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import './Shimmering.css'; diff --git a/src/renderer/components/ui-redesign/StatusLabel/StatusLabel.stories.tsx b/src/renderer/shared/ui/StatusLabel/StatusLabel.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/StatusLabel/StatusLabel.stories.tsx rename to src/renderer/shared/ui/StatusLabel/StatusLabel.stories.tsx diff --git a/src/renderer/components/ui-redesign/StatusLabel/StatusLabel.test.tsx b/src/renderer/shared/ui/StatusLabel/StatusLabel.test.tsx similarity index 95% rename from src/renderer/components/ui-redesign/StatusLabel/StatusLabel.test.tsx rename to src/renderer/shared/ui/StatusLabel/StatusLabel.test.tsx index 8a3683fcc0..115b38f976 100644 --- a/src/renderer/components/ui-redesign/StatusLabel/StatusLabel.test.tsx +++ b/src/renderer/shared/ui/StatusLabel/StatusLabel.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'; import StatusLabel from './StatusLabel'; -describe('ui-redesign/StatusLabel', () => { +describe('ui/StatusLabel', () => { test('should render component', () => { render(); diff --git a/src/renderer/components/ui-redesign/StatusLabel/StatusLabel.tsx b/src/renderer/shared/ui/StatusLabel/StatusLabel.tsx similarity index 79% rename from src/renderer/components/ui-redesign/StatusLabel/StatusLabel.tsx rename to src/renderer/shared/ui/StatusLabel/StatusLabel.tsx index 7b5888facf..0a0b2c3102 100644 --- a/src/renderer/components/ui-redesign/StatusLabel/StatusLabel.tsx +++ b/src/renderer/shared/ui/StatusLabel/StatusLabel.tsx @@ -1,8 +1,8 @@ import { ReactNode } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { FootnoteText } from '@renderer/components/ui-redesign'; -import { HelpText } from '@renderer/components/ui-redesign/Typography'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { FootnoteText } from '@renderer/shared/ui'; +import { HelpText } from '@renderer/shared/ui/Typography'; import { DotStyles, TitleStyles } from './common/constants'; import { Variant } from './common/types'; diff --git a/src/renderer/components/ui-redesign/StatusLabel/common/constants.ts b/src/renderer/shared/ui/StatusLabel/common/constants.ts similarity index 100% rename from src/renderer/components/ui-redesign/StatusLabel/common/constants.ts rename to src/renderer/shared/ui/StatusLabel/common/constants.ts diff --git a/src/renderer/components/ui-redesign/StatusLabel/common/types.ts b/src/renderer/shared/ui/StatusLabel/common/types.ts similarity index 100% rename from src/renderer/components/ui-redesign/StatusLabel/common/types.ts rename to src/renderer/shared/ui/StatusLabel/common/types.ts diff --git a/src/renderer/components/ui/Switch/Switch.stories.tsx b/src/renderer/shared/ui/Switch/Switch.stories.tsx similarity index 100% rename from src/renderer/components/ui/Switch/Switch.stories.tsx rename to src/renderer/shared/ui/Switch/Switch.stories.tsx diff --git a/src/renderer/components/ui/Switch/Switch.test.tsx b/src/renderer/shared/ui/Switch/Switch.test.tsx similarity index 100% rename from src/renderer/components/ui/Switch/Switch.test.tsx rename to src/renderer/shared/ui/Switch/Switch.test.tsx diff --git a/src/renderer/components/ui/Switch/Switch.tsx b/src/renderer/shared/ui/Switch/Switch.tsx similarity index 93% rename from src/renderer/components/ui/Switch/Switch.tsx rename to src/renderer/shared/ui/Switch/Switch.tsx index d561d66efd..0f7ffa5232 100644 --- a/src/renderer/components/ui/Switch/Switch.tsx +++ b/src/renderer/shared/ui/Switch/Switch.tsx @@ -1,8 +1,8 @@ import { Switch as HeadlessSwitch } from '@headlessui/react'; import { PropsWithChildren } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { LabelText } from '@renderer/components/ui-redesign'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { LabelText } from '@renderer/shared/ui'; interface Props { checked?: boolean; diff --git a/src/renderer/components/ui-redesign/Tabs/Tabs.stories.tsx b/src/renderer/shared/ui/Tabs/Tabs.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Tabs/Tabs.stories.tsx rename to src/renderer/shared/ui/Tabs/Tabs.stories.tsx diff --git a/src/renderer/components/ui-redesign/Tabs/Tabs.test.tsx b/src/renderer/shared/ui/Tabs/Tabs.test.tsx similarity index 98% rename from src/renderer/components/ui-redesign/Tabs/Tabs.test.tsx rename to src/renderer/shared/ui/Tabs/Tabs.test.tsx index f6968bef9c..c5d4837d13 100644 --- a/src/renderer/components/ui-redesign/Tabs/Tabs.test.tsx +++ b/src/renderer/shared/ui/Tabs/Tabs.test.tsx @@ -10,7 +10,7 @@ const tabItems: TabItem[] = [ { id: '3', title: 'Tab 3 title', panel:
    tab 3 content
    }, ]; -describe('ui-redesign/Tabs', () => { +describe('ui/Tabs', () => { test('should render component', () => { render(); diff --git a/src/renderer/components/ui-redesign/Tabs/Tabs.tsx b/src/renderer/shared/ui/Tabs/Tabs.tsx similarity index 91% rename from src/renderer/components/ui-redesign/Tabs/Tabs.tsx rename to src/renderer/shared/ui/Tabs/Tabs.tsx index 0f09341879..323b0cdf34 100644 --- a/src/renderer/components/ui-redesign/Tabs/Tabs.tsx +++ b/src/renderer/shared/ui/Tabs/Tabs.tsx @@ -1,7 +1,7 @@ import { Tab } from '@headlessui/react'; -import cnTw from '@renderer/shared/utils/twMerge'; -import { FootnoteText } from '@renderer/components/ui-redesign'; +import { cnTw } from '@renderer/shared/lib/utils'; +import { FootnoteText } from '@renderer/shared/ui'; import { TabItem } from './common/types'; type Props = { diff --git a/src/renderer/components/ui-redesign/Tabs/common/types.ts b/src/renderer/shared/ui/Tabs/common/types.ts similarity index 100% rename from src/renderer/components/ui-redesign/Tabs/common/types.ts rename to src/renderer/shared/ui/Tabs/common/types.ts diff --git a/src/renderer/components/ui/Truncate/Truncate.test.tsx b/src/renderer/shared/ui/Truncate/Truncate.test.tsx similarity index 96% rename from src/renderer/components/ui/Truncate/Truncate.test.tsx rename to src/renderer/shared/ui/Truncate/Truncate.test.tsx index 2529526dba..81c27cc20e 100644 --- a/src/renderer/components/ui/Truncate/Truncate.test.tsx +++ b/src/renderer/shared/ui/Truncate/Truncate.test.tsx @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react'; -import Truncate from './Truncate'; +import { Truncate } from './Truncate'; describe('ui/Truncate', () => { test('should render component', () => { diff --git a/src/renderer/components/ui/Truncate/Truncate.tsx b/src/renderer/shared/ui/Truncate/Truncate.tsx similarity index 95% rename from src/renderer/components/ui/Truncate/Truncate.tsx rename to src/renderer/shared/ui/Truncate/Truncate.tsx index bc41562bb0..fb2bf2a878 100644 --- a/src/renderer/components/ui/Truncate/Truncate.tsx +++ b/src/renderer/shared/ui/Truncate/Truncate.tsx @@ -12,7 +12,7 @@ type Props = { style?: CSSProperties; }; -const Truncate = ({ text, ellipsis = '...', end = 5, start = 5, className = '', style = {} }: Props) => { +export const Truncate = ({ text, ellipsis = '...', end = 5, start = 5, className = '', style = {} }: Props) => { const containerRef = useRef(null); const textRef = useRef(null); const ellipsisRef = useRef(null); @@ -97,5 +97,3 @@ const Truncate = ({ text, ellipsis = '...', end = 5, start = 5, className = '',
    ); }; - -export default Truncate; diff --git a/src/renderer/components/ui/Truncate/utils.ts b/src/renderer/shared/ui/Truncate/utils.ts similarity index 100% rename from src/renderer/components/ui/Truncate/utils.ts rename to src/renderer/shared/ui/Truncate/utils.ts diff --git a/src/renderer/components/ui-redesign/Typography/Typography.stories.tsx b/src/renderer/shared/ui/Typography/Typography.stories.tsx similarity index 100% rename from src/renderer/components/ui-redesign/Typography/Typography.stories.tsx rename to src/renderer/shared/ui/Typography/Typography.stories.tsx diff --git a/src/renderer/components/ui-redesign/Typography/common/TextBase.ts b/src/renderer/shared/ui/Typography/common/TextBase.ts similarity index 87% rename from src/renderer/components/ui-redesign/Typography/common/TextBase.ts rename to src/renderer/shared/ui/Typography/common/TextBase.ts index ae94614c63..e5ad028c3c 100644 --- a/src/renderer/components/ui-redesign/Typography/common/TextBase.ts +++ b/src/renderer/shared/ui/Typography/common/TextBase.ts @@ -1,6 +1,6 @@ import { createElement } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { TypographyProps } from './types'; const TextBase = ({ as = 'p', align = 'left', className, children }: TypographyProps) => { diff --git a/src/renderer/components/ui-redesign/Typography/common/types.ts b/src/renderer/shared/ui/Typography/common/types.ts similarity index 100% rename from src/renderer/components/ui-redesign/Typography/common/types.ts rename to src/renderer/shared/ui/Typography/common/types.ts diff --git a/src/renderer/components/ui-redesign/Typography/components/BodyText.tsx b/src/renderer/shared/ui/Typography/components/BodyText.tsx similarity index 82% rename from src/renderer/components/ui-redesign/Typography/components/BodyText.tsx rename to src/renderer/shared/ui/Typography/components/BodyText.tsx index 5db2e365fe..f205b2a7ba 100644 --- a/src/renderer/components/ui-redesign/Typography/components/BodyText.tsx +++ b/src/renderer/shared/ui/Typography/components/BodyText.tsx @@ -1,4 +1,4 @@ -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { TypographyProps } from '../common/types'; import TextBase from '../common/TextBase'; diff --git a/src/renderer/components/ui-redesign/Typography/components/CaptionText.tsx b/src/renderer/shared/ui/Typography/components/CaptionText.tsx similarity index 82% rename from src/renderer/components/ui-redesign/Typography/components/CaptionText.tsx rename to src/renderer/shared/ui/Typography/components/CaptionText.tsx index 856e961068..b5c8a80d2c 100644 --- a/src/renderer/components/ui-redesign/Typography/components/CaptionText.tsx +++ b/src/renderer/shared/ui/Typography/components/CaptionText.tsx @@ -1,4 +1,4 @@ -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { TypographyProps } from '../common/types'; import TextBase from '../common/TextBase'; diff --git a/src/renderer/components/ui-redesign/Typography/components/FootnoteText.tsx b/src/renderer/shared/ui/Typography/components/FootnoteText.tsx similarity index 82% rename from src/renderer/components/ui-redesign/Typography/components/FootnoteText.tsx rename to src/renderer/shared/ui/Typography/components/FootnoteText.tsx index bab38bc149..7c4272f1ed 100644 --- a/src/renderer/components/ui-redesign/Typography/components/FootnoteText.tsx +++ b/src/renderer/shared/ui/Typography/components/FootnoteText.tsx @@ -1,4 +1,4 @@ -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { TypographyProps } from '../common/types'; import TextBase from '../common/TextBase'; diff --git a/src/renderer/components/ui-redesign/Typography/components/HeaderTitleText.tsx b/src/renderer/shared/ui/Typography/components/HeaderTitleText.tsx similarity index 84% rename from src/renderer/components/ui-redesign/Typography/components/HeaderTitleText.tsx rename to src/renderer/shared/ui/Typography/components/HeaderTitleText.tsx index 8864f7a271..ae08f64487 100644 --- a/src/renderer/components/ui-redesign/Typography/components/HeaderTitleText.tsx +++ b/src/renderer/shared/ui/Typography/components/HeaderTitleText.tsx @@ -1,4 +1,4 @@ -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { TypographyProps } from '../common/types'; import TextBase from '../common/TextBase'; diff --git a/src/renderer/components/ui-redesign/Typography/components/HeadlineText.tsx b/src/renderer/shared/ui/Typography/components/HeadlineText.tsx similarity index 82% rename from src/renderer/components/ui-redesign/Typography/components/HeadlineText.tsx rename to src/renderer/shared/ui/Typography/components/HeadlineText.tsx index ab25b779e1..daa02448a1 100644 --- a/src/renderer/components/ui-redesign/Typography/components/HeadlineText.tsx +++ b/src/renderer/shared/ui/Typography/components/HeadlineText.tsx @@ -1,4 +1,4 @@ -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { TypographyProps } from '../common/types'; import TextBase from '../common/TextBase'; diff --git a/src/renderer/components/ui-redesign/Typography/components/HelpText.tsx b/src/renderer/shared/ui/Typography/components/HelpText.tsx similarity index 82% rename from src/renderer/components/ui-redesign/Typography/components/HelpText.tsx rename to src/renderer/shared/ui/Typography/components/HelpText.tsx index 9a46897cfc..9db2ebe10e 100644 --- a/src/renderer/components/ui-redesign/Typography/components/HelpText.tsx +++ b/src/renderer/shared/ui/Typography/components/HelpText.tsx @@ -1,4 +1,4 @@ -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { TypographyProps } from '../common/types'; import TextBase from '../common/TextBase'; diff --git a/src/renderer/components/ui-redesign/Typography/components/LabelText.tsx b/src/renderer/shared/ui/Typography/components/LabelText.tsx similarity index 90% rename from src/renderer/components/ui-redesign/Typography/components/LabelText.tsx rename to src/renderer/shared/ui/Typography/components/LabelText.tsx index cc37a0f038..c0df7dbd3b 100644 --- a/src/renderer/components/ui-redesign/Typography/components/LabelText.tsx +++ b/src/renderer/shared/ui/Typography/components/LabelText.tsx @@ -1,6 +1,6 @@ import { DetailedHTMLProps, LabelHTMLAttributes } from 'react'; -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; // eslint-plugin-react has problems with DetailedHTMLProps so this workaround needed // https://github.com/jsx-eslint/eslint-plugin-react/issues/3284 diff --git a/src/renderer/components/ui-redesign/Typography/components/LargeTitleText.tsx b/src/renderer/shared/ui/Typography/components/LargeTitleText.tsx similarity index 84% rename from src/renderer/components/ui-redesign/Typography/components/LargeTitleText.tsx rename to src/renderer/shared/ui/Typography/components/LargeTitleText.tsx index cea4d2c1f3..ba3a282c80 100644 --- a/src/renderer/components/ui-redesign/Typography/components/LargeTitleText.tsx +++ b/src/renderer/shared/ui/Typography/components/LargeTitleText.tsx @@ -1,4 +1,4 @@ -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import TextBase from '../common/TextBase'; import { TypographyProps } from '../common/types'; diff --git a/src/renderer/components/ui-redesign/Typography/components/SmallTitleText.tsx b/src/renderer/shared/ui/Typography/components/SmallTitleText.tsx similarity index 84% rename from src/renderer/components/ui-redesign/Typography/components/SmallTitleText.tsx rename to src/renderer/shared/ui/Typography/components/SmallTitleText.tsx index 0aa8927695..28b5e015af 100644 --- a/src/renderer/components/ui-redesign/Typography/components/SmallTitleText.tsx +++ b/src/renderer/shared/ui/Typography/components/SmallTitleText.tsx @@ -1,4 +1,4 @@ -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import { TypographyProps } from '../common/types'; import TextBase from '../common/TextBase'; diff --git a/src/renderer/components/ui-redesign/Typography/components/TitleText.tsx b/src/renderer/shared/ui/Typography/components/TitleText.tsx similarity index 84% rename from src/renderer/components/ui-redesign/Typography/components/TitleText.tsx rename to src/renderer/shared/ui/Typography/components/TitleText.tsx index 01f606deaa..3f1138063b 100644 --- a/src/renderer/components/ui-redesign/Typography/components/TitleText.tsx +++ b/src/renderer/shared/ui/Typography/components/TitleText.tsx @@ -1,4 +1,4 @@ -import cnTw from '@renderer/shared/utils/twMerge'; +import { cnTw } from '@renderer/shared/lib/utils'; import TextBase from '../common/TextBase'; import { TypographyProps } from '../common/types'; diff --git a/src/renderer/components/ui-redesign/Typography/index.ts b/src/renderer/shared/ui/Typography/index.ts similarity index 93% rename from src/renderer/components/ui-redesign/Typography/index.ts rename to src/renderer/shared/ui/Typography/index.ts index cf29d4cbe5..794f5d14b9 100644 --- a/src/renderer/components/ui-redesign/Typography/index.ts +++ b/src/renderer/shared/ui/Typography/index.ts @@ -8,8 +8,10 @@ import { LargeTitleText } from './components/LargeTitleText'; import { HeaderTitleText } from './components/HeaderTitleText'; import { LabelText } from './components/LabelText'; import { HelpText } from './components/HelpText'; +import TextBase from './common/TextBase'; export { + TextBase, FootnoteText, BodyText, SmallTitleText, diff --git a/src/renderer/components/ui-redesign/index.ts b/src/renderer/shared/ui/index.ts similarity index 81% rename from src/renderer/components/ui-redesign/index.ts rename to src/renderer/shared/ui/index.ts index 1acc31367f..5577d2262c 100644 --- a/src/renderer/components/ui-redesign/index.ts +++ b/src/renderer/shared/ui/index.ts @@ -28,9 +28,6 @@ import StatusLabel from './StatusLabel/StatusLabel'; import InputFile from './Inputs/InputFile/InputFile'; import { Tooltip } from './Popovers/Tooltip/Tooltip'; import { LabelHelpBox } from './LabelHelpbox/LabelHelpBox'; -import { Chain } from './Chain/Chain'; -import { ChainIcon } from './ChainIcon/ChainIcon'; -import { AssetIcon } from './AssetIcon/AssetIcon'; import { Tabs } from './Tabs/Tabs'; import { LargeTitleText, @@ -42,7 +39,18 @@ import { FootnoteText, LabelText, HeaderTitleText, + HelpText, } from './Typography'; +import InputArea from './Inputs/InputArea/InputArea'; +import Switch from './Switch/Switch'; +import Icon from './Icon/Icon'; +import Identicon from './Identicon/Identicon'; +import LanguageSwitcher from './LanguageSwitcher/LanguageSwitcher'; +import Shimmering from './Shimmering/Shimmering'; +import Duration from './Duration/Duration'; +import Loader from './Loader/Loader'; +import DetailRow from './DetailRow/DetailRow'; +import { Truncate } from './Truncate/Truncate'; // FIXME: Animation component exported separately. // Adding them to this file causes to crash all tests which use anything from that file @@ -89,8 +97,16 @@ export { InputFile, Tooltip, LabelHelpBox, - Chain, - ChainIcon, - AssetIcon, Tabs, + InputArea, + Switch, + Icon, + Identicon, + LanguageSwitcher, + Shimmering, + Duration, + Loader, + HelpText, + DetailRow, + Truncate, }; diff --git a/src/renderer/shared/ui/types.ts b/src/renderer/shared/ui/types.ts new file mode 100644 index 0000000000..4f69fee5f3 --- /dev/null +++ b/src/renderer/shared/ui/types.ts @@ -0,0 +1,5 @@ +export * from './Buttons/common/types'; +export * from './Inputs/common/types'; +export * from './Tabs/common/types'; +export * from './Dropdowns/common/types'; +export type { ButtonDropdownOption } from './Dropdowns/DropdownButton/DropdownButton'; diff --git a/tests/integrations/dataVerification/dataVerification.base.test.ts b/tests/integrations/dataVerification/dataVerification.base.test.ts index ecd4b1f5b4..aacc6799d7 100644 --- a/tests/integrations/dataVerification/dataVerification.base.test.ts +++ b/tests/integrations/dataVerification/dataVerification.base.test.ts @@ -2,7 +2,7 @@ import { ApiPromise } from '@polkadot/api'; import { AccountInfo } from '@polkadot/types/interfaces'; import { Codec } from '@polkadot/types/types'; -import chains from '@renderer/services/network/common/chains/chains.json'; +import chains from '../../../src/renderer/assets/chains/chains.json'; import { validate } from '@renderer/services/dataVerification/dataVerification'; import { getTestAccounts, diff --git a/tests/integrations/matrix/matrix.base.test.ts b/tests/integrations/matrix/matrix.base.test.ts index b90972c1aa..5bfb53eed4 100644 --- a/tests/integrations/matrix/matrix.base.test.ts +++ b/tests/integrations/matrix/matrix.base.test.ts @@ -1,5 +1,5 @@ -import { BASE_MATRIX_URL } from '@renderer/services/matrix/common/constants'; -import Matrix, { Membership, Signatory } from '@renderer/services/matrix'; +import { BASE_MATRIX_URL } from '../../../src/renderer/entities/matrix/lib/common/constants'; +import Matrix, { Membership, Signatory } from '../../../src/renderer/entities/matrix/lib'; import { createRoom } from '../utils/matrixCreateRoom'; import { matrixLoginAndSync } from '../utils/matrixLogin'; import test_data from './matrix_data.json'; diff --git a/tests/integrations/utils/matrixCreateRoom.ts b/tests/integrations/utils/matrixCreateRoom.ts index fec4e27bdc..9e32d23358 100644 --- a/tests/integrations/utils/matrixCreateRoom.ts +++ b/tests/integrations/utils/matrixCreateRoom.ts @@ -1,4 +1,4 @@ -import Matrix, { RoomParams, Signatory } from '@renderer/services/matrix'; +import Matrix, { RoomParams, Signatory } from '../../../src/renderer/entities/matrix/lib'; export async function createRoom( matrix: Matrix, diff --git a/tests/integrations/utils/matrixLogin.ts b/tests/integrations/utils/matrixLogin.ts index 92471de181..750049aa8b 100644 --- a/tests/integrations/utils/matrixLogin.ts +++ b/tests/integrations/utils/matrixLogin.ts @@ -1,4 +1,4 @@ -import Matrix from '@renderer/services/matrix'; +import Matrix from '../../../src/renderer/entities/matrix/lib'; export async function matrixLoginAndSync(matrix: Matrix, login: string, password: string): Promise { await matrix.loginWithCreds(login, password); From be34cb3eef8529566a29673a6201beb0f9e378c3 Mon Sep 17 00:00:00 2001 From: Aleksandr Makhnev Date: Mon, 7 Aug 2023 16:37:32 +0500 Subject: [PATCH 02/34] fix: options container style (#1010) --- .../shared/ui/Dropdowns/Combobox/Combobox.tsx | 16 ++++++++++++++-- .../ui/Dropdowns/MultiSelect/MultiSelect.tsx | 9 ++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.tsx b/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.tsx index 7eef9a438a..b4d5289448 100644 --- a/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.tsx +++ b/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.tsx @@ -3,7 +3,13 @@ import { Transition, Combobox as HeadlessCombobox } from '@headlessui/react'; import { cnTw, includes } from '@renderer/shared/lib/utils'; import { Props as InputProps } from '@renderer/shared/ui/Inputs/Input/Input'; -import { OptionsContainerStyle, OptionStyle, OptionStyleTheme, ViewClass } from '../common/constants'; +import { + OptionsContainerStyle, + OptionsContainerStyleTheme, + OptionStyle, + OptionStyleTheme, + ViewClass, +} from '../common/constants'; import { Position, ComboboxOption, Theme } from '../common/types'; import { FootnoteText, Input } from '@renderer/shared/ui'; @@ -46,7 +52,13 @@ const Combobox = ({ /> - + {nothingFound && ( - + {options.map(({ id, value, element }) => ( Date: Mon, 14 Aug 2023 12:11:09 +0300 Subject: [PATCH 03/34] Feat: Address book FSD + Effector (#1011) --- package.json | 4 + pnpm-lock.yaml | 101 ++++++++-- src/renderer/app/App.tsx | 7 +- .../MatrixContext/MatrixContext.test.tsx | 6 +- .../context/MatrixContext/MatrixContext.tsx | 34 ++-- src/renderer/app/providers/routes/paths.ts | 3 +- .../app/providers/routes/routesConfig.tsx | 7 +- .../forms/ContactForm/ContactForm.test.tsx | 52 ------ .../forms/ContactForm/ContactForm.tsx | 175 ------------------ src/renderer/components/forms/index.ts | 1 - .../CreateMultisigAccount/common/types.ts | 4 +- .../components/SelectSignatories.tsx | 38 +--- .../components/LoginForm/LoginForm.tsx | 12 +- src/renderer/entities/contact/index.ts | 3 +- .../entities/contact/lib/common/types.ts | 2 +- .../entities/contact/lib/contactService.ts | 2 +- .../entities/contact/model/contact.ts | 65 ++++++- src/renderer/entities/contact/model/types.ts | 8 + .../entities/contact/ui/ContactList.tsx | 19 ++ .../contact/ui/ContactList/ContactList.tsx | 60 ------ .../entities/contact/ui/ContactRow.tsx | 28 +++ .../entities/contact/ui/EmptyContactList.tsx | 37 ++++ .../contact/ui/EmptyFilteredContacts.tsx} | 2 +- .../ui/__tests__/EmptyContactList.test.tsx} | 11 +- .../__tests__/EmptyFilteredContacts.test.tsx} | 6 +- src/renderer/entities/contact/ui/index.ts | 5 +- src/renderer/entities/matrix/index.ts | 1 - .../matrix/lib/__tests__/matrix.test.ts | 7 - .../entities/signatory/model/signatory.ts | 10 +- .../entities/transaction/lib/common/types.ts | 2 +- .../transaction/lib/transactionService.ts | 10 +- .../features/contacts/ContactFilter/index.ts | 2 + .../ContactFilter/model/contact-filter.ts | 25 +++ .../ContactFilter/ui/ContactFilter.tsx | 15 ++ .../contacts/CreateContactForm/index.ts | 3 + .../CreateContactForm/model/contact-form.ts | 98 ++++++++++ .../ui/CreateContactForm.tsx | 99 ++++++++++ .../ui/CreateContactNavigation.tsx | 15 ++ .../contacts/EditContactForm/index.ts | 3 + .../EditContactForm/model/contact-form.ts | 136 ++++++++++++++ .../EditContactForm/ui/EditContactForm.tsx | 105 +++++++++++ .../ui/EditContactNavigation.tsx | 17 ++ .../features/contacts/EditRouteGuard/index.ts | 2 + .../EditRouteGuard/model/edit-guard.ts | 55 ++++++ .../EditRouteGuard/ui/EditRouteGuard.tsx | 26 +++ src/renderer/features/contacts/index.ts | 4 + .../pages/AddressBook/Contacts/Contacts.tsx | 60 ++++++ .../CreateContact/CreateContact.tsx | 10 + .../AddressBook/EditContact/EditContact.tsx | 15 ++ .../ManageContact/ManageContact.test.tsx | 52 ------ .../ManageContact/ManageContact.tsx | 58 ------ .../AddressBook/Overview/Overview.test.tsx | 35 ---- .../pages/AddressBook/Overview/Overview.tsx | 61 ------ .../components/EmptyState/EmptyContacts.tsx | 24 --- .../AddressBook/Overview/components/index.ts | 3 - src/renderer/pages/AddressBook/index.ts | 5 +- src/renderer/pages/Operations/common/utils.ts | 2 +- .../components/modals/ApproveTx.tsx | 16 +- .../InitOperation/InitOperation.test.tsx | 1 - .../services/dataVerification/index.ts | 2 +- .../__tests__/credentialStorage.test.ts | 2 +- .../matrix}/__tests__/secretStorage.test.ts | 2 +- .../api/matrix}/common/constants.ts | 0 .../api/matrix}/common/errors.ts | 0 .../lib => shared/api/matrix}/common/types.ts | 5 - .../shared/api/matrix/common/utils.ts | 21 +++ .../matrix/lib => shared/api/matrix}/index.ts | 3 +- .../api/matrix/service}/credentialStorage.ts | 2 +- .../api/matrix/service}/matrix.ts | 77 ++------ .../api/matrix/service}/secretStorage.ts | 2 +- .../shared/api/storage/common/types.ts | 8 +- .../shared/api/storage/contactStorage.ts | 4 +- .../lib/utils/__tests__/address.test.ts | 2 +- .../shared/lib/utils/__tests__/arrays.test.ts | 26 +++ .../lib/utils/__tests__/balance.test.ts | 2 +- .../lib/utils/__tests__/strings.test.ts | 2 +- .../lib/utils/__tests__/substrate.test.ts | 2 +- src/renderer/shared/lib/utils/arrays.ts | 10 + src/renderer/shared/lib/utils/index.ts | 1 + .../shared/ui/Dropdowns/Combobox/Combobox.tsx | 4 +- .../widgets/ManageContactModal/index.ts | 2 + .../CreateContactModal/CreateContactModal.tsx | 45 +++++ .../ui/EditContactModal/EditContactModal.tsx | 47 +++++ src/renderer/widgets/index.ts | 1 + src/shared/locale/en.json | 37 ++-- src/shared/locale/ru.json | 39 ++-- .../dataVerification.base.test.ts | 2 +- tests/integrations/matrix/matrix.base.test.ts | 4 +- tests/integrations/utils/matrixCreateRoom.ts | 2 +- tests/integrations/utils/matrixLogin.ts | 2 +- 90 files changed, 1266 insertions(+), 761 deletions(-) delete mode 100644 src/renderer/components/forms/ContactForm/ContactForm.test.tsx delete mode 100644 src/renderer/components/forms/ContactForm/ContactForm.tsx delete mode 100644 src/renderer/components/forms/index.ts create mode 100644 src/renderer/entities/contact/model/types.ts create mode 100644 src/renderer/entities/contact/ui/ContactList.tsx delete mode 100644 src/renderer/entities/contact/ui/ContactList/ContactList.tsx create mode 100644 src/renderer/entities/contact/ui/ContactRow.tsx create mode 100644 src/renderer/entities/contact/ui/EmptyContactList.tsx rename src/renderer/{pages/AddressBook/Overview/components/EmptyState/EmptySearch.tsx => entities/contact/ui/EmptyFilteredContacts.tsx} (90%) rename src/renderer/{pages/AddressBook/Overview/components/EmptyState/EmptyContacts.test.tsx => entities/contact/ui/__tests__/EmptyContactList.test.tsx} (56%) rename src/renderer/{pages/AddressBook/Overview/components/EmptyState/EmptySearch.test.tsx => entities/contact/ui/__tests__/EmptyFilteredContacts.test.tsx} (68%) delete mode 100644 src/renderer/entities/matrix/index.ts delete mode 100644 src/renderer/entities/matrix/lib/__tests__/matrix.test.ts create mode 100644 src/renderer/features/contacts/ContactFilter/index.ts create mode 100644 src/renderer/features/contacts/ContactFilter/model/contact-filter.ts create mode 100644 src/renderer/features/contacts/ContactFilter/ui/ContactFilter.tsx create mode 100644 src/renderer/features/contacts/CreateContactForm/index.ts create mode 100644 src/renderer/features/contacts/CreateContactForm/model/contact-form.ts create mode 100644 src/renderer/features/contacts/CreateContactForm/ui/CreateContactForm.tsx create mode 100644 src/renderer/features/contacts/CreateContactForm/ui/CreateContactNavigation.tsx create mode 100644 src/renderer/features/contacts/EditContactForm/index.ts create mode 100644 src/renderer/features/contacts/EditContactForm/model/contact-form.ts create mode 100644 src/renderer/features/contacts/EditContactForm/ui/EditContactForm.tsx create mode 100644 src/renderer/features/contacts/EditContactForm/ui/EditContactNavigation.tsx create mode 100644 src/renderer/features/contacts/EditRouteGuard/index.ts create mode 100644 src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts create mode 100644 src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx create mode 100644 src/renderer/features/contacts/index.ts create mode 100644 src/renderer/pages/AddressBook/Contacts/Contacts.tsx create mode 100644 src/renderer/pages/AddressBook/CreateContact/CreateContact.tsx create mode 100644 src/renderer/pages/AddressBook/EditContact/EditContact.tsx delete mode 100644 src/renderer/pages/AddressBook/ManageContact/ManageContact.test.tsx delete mode 100644 src/renderer/pages/AddressBook/ManageContact/ManageContact.tsx delete mode 100644 src/renderer/pages/AddressBook/Overview/Overview.test.tsx delete mode 100644 src/renderer/pages/AddressBook/Overview/Overview.tsx delete mode 100644 src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.tsx delete mode 100644 src/renderer/pages/AddressBook/Overview/components/index.ts rename src/renderer/{entities/matrix/lib => shared/api/matrix}/__tests__/credentialStorage.test.ts (95%) rename src/renderer/{entities/matrix/lib => shared/api/matrix}/__tests__/secretStorage.test.ts (96%) rename src/renderer/{entities/matrix/lib => shared/api/matrix}/common/constants.ts (100%) rename src/renderer/{entities/matrix/lib => shared/api/matrix}/common/errors.ts (100%) rename src/renderer/{entities/matrix/lib => shared/api/matrix}/common/types.ts (97%) create mode 100644 src/renderer/shared/api/matrix/common/utils.ts rename src/renderer/{entities/matrix/lib => shared/api/matrix}/index.ts (50%) rename src/renderer/{entities/matrix/lib => shared/api/matrix/service}/credentialStorage.ts (96%) rename src/renderer/{entities/matrix/lib => shared/api/matrix/service}/matrix.ts (95%) rename src/renderer/{entities/matrix/lib => shared/api/matrix/service}/secretStorage.ts (98%) create mode 100644 src/renderer/shared/lib/utils/__tests__/arrays.test.ts create mode 100644 src/renderer/shared/lib/utils/arrays.ts create mode 100644 src/renderer/widgets/ManageContactModal/index.ts create mode 100644 src/renderer/widgets/ManageContactModal/ui/CreateContactModal/CreateContactModal.tsx create mode 100644 src/renderer/widgets/ManageContactModal/ui/EditContactModal/EditContactModal.tsx create mode 100644 src/renderer/widgets/index.ts diff --git a/package.json b/package.json index 00d9669047..2c338ce07a 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,9 @@ "date-fns": "^2.29.3", "dexie": "^3.2.2", "dexie-react-hooks": "^1.1.1", + "effector": "^22.8.6", + "effector-forms": "^1.3.4", + "effector-react": "^22.5.3", "electron-log": "5.0.0-beta.23", "electron-window-state": "^5.0.3", "graphql": "^16.6.0", @@ -87,6 +90,7 @@ "lodash": "^4.17.21", "lottie-react": "^2.4.0", "parity-scale-codec": "^0.6.1", + "patronum": "^1.19.1", "qrcode-generator": "^1.4.4", "raptorq": "^1.7.22", "react": "^18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2116aee15f..3b9138c5e3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,7 +22,7 @@ dependencies: version: 12.2.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) '@polkadot/react-identicon': specifier: ^3.4.1 - version: 3.4.1(@polkadot/keyring@12.2.1)(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1)(react-dom@18.2.0)(react@18.2.0) + version: 3.4.1(@polkadot/keyring@12.2.1)(@polkadot/networks@12.2.2)(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) '@polkadot/rpc-provider': specifier: ^10.7.1 version: 10.8.1 @@ -74,6 +74,15 @@ dependencies: dexie-react-hooks: specifier: ^1.1.1 version: 1.1.3(@types/react@18.2.6)(dexie@3.2.3)(react@18.2.0) + effector: + specifier: ^22.8.6 + version: 22.8.6 + effector-forms: + specifier: ^1.3.4 + version: 1.3.4(effector-react@22.5.3)(effector@22.8.6) + effector-react: + specifier: ^22.5.3 + version: 22.5.3(effector@22.8.6)(react@18.2.0) electron-log: specifier: 5.0.0-beta.23 version: 5.0.0-beta.23 @@ -95,6 +104,9 @@ dependencies: parity-scale-codec: specifier: ^0.6.1 version: 0.6.2 + patronum: + specifier: ^1.19.1 + version: 1.19.1(effector@22.8.6) qrcode-generator: specifier: ^1.4.4 version: 1.4.4 @@ -124,7 +136,7 @@ dependencies: version: 3.0.0 styled-components: specifier: ^5.3.6 - version: 5.3.10(react-dom@18.2.0)(react@18.2.0) + version: 5.3.10(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) swiper: specifier: ^8.3.2 version: 8.4.7 @@ -180,7 +192,7 @@ devDependencies: version: 6.5.16(@swc/core@1.3.58)(eslint@8.40.0)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5)(webpack-cli@5.1.1) '@storybook/react': specifier: ^6.5.9 - version: 6.5.16(@babel/core@7.21.8)(@storybook/builder-webpack5@6.5.16)(@storybook/manager-webpack5@6.5.16)(@swc/core@1.3.58)(eslint@8.40.0)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5)(webpack-cli@5.1.1)(webpack-dev-server@4.15.0) + version: 6.5.16(@babel/core@7.21.8)(@storybook/builder-webpack5@6.5.16)(@storybook/manager-webpack5@6.5.16)(@swc/core@1.3.58)(eslint@8.40.0)(react-dom@18.2.0)(react@18.2.0)(require-from-string@2.0.2)(typescript@4.9.5)(webpack-cli@5.1.1)(webpack-dev-server@4.15.0) '@svgr/webpack': specifier: ^6.2.1 version: 6.5.1 @@ -204,7 +216,7 @@ devDependencies: version: 13.4.0(react-dom@18.2.0)(react@18.2.0) '@testing-library/user-event': specifier: ^14.2.1 - version: 14.4.3 + version: 14.4.3(@testing-library/dom@8.20.0) '@types/jest': specifier: ^28.1.4 version: 28.1.8 @@ -336,7 +348,7 @@ devDependencies: version: 15.0.0 jest-runner-groups: specifier: ^2.2.0 - version: 2.2.0 + version: 2.2.0(jest-docblock@29.4.3)(jest-runner@29.5.0) lint-staged: specifier: ^13.0.3 version: 13.2.2 @@ -3089,7 +3101,7 @@ packages: tslib: 2.5.3 dev: false - /@polkadot/react-identicon@3.4.1(@polkadot/keyring@12.2.1)(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1)(react-dom@18.2.0)(react@18.2.0): + /@polkadot/react-identicon@3.4.1(@polkadot/keyring@12.2.1)(@polkadot/networks@12.2.2)(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0): resolution: {integrity: sha512-0S8fofxKus3IORdUSSnIaiF0HKA6F+G//3+KdwiAmAlQWyfs94njQKP4IgouFSbueVyYMh4fbthQ8civ65Bkgg==} engines: {node: '>=16'} peerDependencies: @@ -3101,7 +3113,7 @@ packages: react-is: '*' dependencies: '@polkadot/keyring': 12.2.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) - '@polkadot/ui-settings': 3.4.1(@polkadot/util@12.2.1) + '@polkadot/ui-settings': 3.4.1(@polkadot/networks@12.2.2)(@polkadot/util@12.2.1) '@polkadot/ui-shared': 3.4.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) '@polkadot/util': 12.2.1 '@polkadot/util-crypto': 12.2.1(@polkadot/util@12.2.1) @@ -3110,8 +3122,11 @@ packages: react: 18.2.0 react-copy-to-clipboard: 5.1.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0) - styled-components: 5.3.10(react-dom@18.2.0)(react@18.2.0) + react-is: 18.2.0 + styled-components: 5.3.10(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) tslib: 2.5.0 + transitivePeerDependencies: + - '@polkadot/networks' dev: false /@polkadot/rpc-augment@10.8.1: @@ -3231,10 +3246,11 @@ packages: tslib: 2.5.3 dev: false - /@polkadot/ui-settings@3.4.1(@polkadot/util@12.2.1): + /@polkadot/ui-settings@3.4.1(@polkadot/networks@12.2.2)(@polkadot/util@12.2.1): resolution: {integrity: sha512-2ym8ipRl14dedExABx/+NBLxh/8W8yMukY72db+weguJBC8/AAgNAzSX4tub9IGivArSTgi2T2/zLNXEKYtA+Q==} engines: {node: '>=16'} peerDependencies: + '@polkadot/networks': '*' '@polkadot/util': '*' dependencies: '@polkadot/networks': 12.2.2 @@ -4821,7 +4837,7 @@ packages: - supports-color dev: true - /@storybook/react@6.5.16(@babel/core@7.21.8)(@storybook/builder-webpack5@6.5.16)(@storybook/manager-webpack5@6.5.16)(@swc/core@1.3.58)(eslint@8.40.0)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5)(webpack-cli@5.1.1)(webpack-dev-server@4.15.0): + /@storybook/react@6.5.16(@babel/core@7.21.8)(@storybook/builder-webpack5@6.5.16)(@storybook/manager-webpack5@6.5.16)(@swc/core@1.3.58)(eslint@8.40.0)(react-dom@18.2.0)(react@18.2.0)(require-from-string@2.0.2)(typescript@4.9.5)(webpack-cli@5.1.1)(webpack-dev-server@4.15.0): resolution: {integrity: sha512-cBtNlOzf/MySpNLBK22lJ8wFU22HnfTB2xJyBk7W7Zi71Lm7Uxkhv1Pz8HdiQndJ0SlsAAQOWjQYsSZsGkZIaA==} engines: {node: '>=10.13.0'} hasBin: true @@ -4886,6 +4902,7 @@ packages: react-refresh: 0.11.0 read-pkg-up: 7.0.1 regenerator-runtime: 0.13.11 + require-from-string: 2.0.2 ts-dedent: 2.2.0 typescript: 4.9.5 util-deprecate: 1.0.2 @@ -5465,11 +5482,13 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@testing-library/user-event@14.4.3: + /@testing-library/user-event@14.4.3(@testing-library/dom@8.20.0): resolution: {integrity: sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' + dependencies: + '@testing-library/dom': 8.20.0 dev: true /@tokenizer/token@0.3.0: @@ -6565,8 +6584,10 @@ packages: ajv: 6.12.6 dev: true - /ajv-formats@2.1.1: + /ajv-formats@2.1.1(ajv@8.12.0): resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 peerDependenciesMeta: ajv: optional: true @@ -7242,7 +7263,7 @@ packages: babel-plugin-syntax-jsx: 6.18.0 lodash: 4.17.21 picomatch: 2.3.1 - styled-components: 5.3.10(react-dom@18.2.0)(react@18.2.0) + styled-components: 5.3.10(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) dev: false /babel-plugin-syntax-jsx@6.18.0: @@ -9434,6 +9455,33 @@ packages: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: true + /effector-forms@1.3.4(effector-react@22.5.3)(effector@22.8.6): + resolution: {integrity: sha512-m/Swvhf6eDyEgYINULCXLZf9uhMMicYQs4Te3bg5Hx2WdUeq5Cts18QwHU+YxoTTJh06rptWbHk0QHHmI4uPuA==} + peerDependencies: + effector: '>=22.0.0 <23.0.0' + effector-react: '>=22.2.0 <23.0.0' + dependencies: + effector: 22.8.6 + effector-react: 22.5.3(effector@22.8.6)(react@18.2.0) + dev: false + + /effector-react@22.5.3(effector@22.8.6)(react@18.2.0): + resolution: {integrity: sha512-JEFh+eERSYTu99h+Zba9g4a7JFHq0AKo4u9+ZvbceFiijSzzQl5ChPuRsI8Y9Ly2uobFANzPRvD+U3UXiDEcrQ==} + engines: {node: '>=11.0.0'} + peerDependencies: + effector: ^22.0.2 + react: '>=16.8.0 <19.0.0' + dependencies: + effector: 22.8.6 + react: 18.2.0 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false + + /effector@22.8.6: + resolution: {integrity: sha512-Bkg/rlPs7J4dmOFGPLLcL7X0SZeA8WdRnyC2X4tGVo7i6M8oxYwtNOkEUact/zIT/MgqTGm9A8T40dPnr8oXmA==} + engines: {node: '>=11.0.0'} + dev: false + /ejs@3.1.9: resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==} engines: {node: '>=0.10.0'} @@ -12918,12 +12966,15 @@ packages: slash: 3.0.0 dev: true - /jest-runner-groups@2.2.0: + /jest-runner-groups@2.2.0(jest-docblock@29.4.3)(jest-runner@29.5.0): resolution: {integrity: sha512-Sp/B9ZX0CDAKa9dIkgH0sGyl2eDuScV4SVvOxqhBMxqWpsNAkmol/C58aTFmPWZj+C0ZTW1r1BSu66MTCN+voA==} engines: {node: '>= 10.14.2'} peerDependencies: jest-docblock: '>= 24' jest-runner: '>= 24' + dependencies: + jest-docblock: 29.4.3 + jest-runner: 29.5.0 dev: true /jest-runner@29.5.0: @@ -15069,6 +15120,14 @@ packages: engines: {node: '>=8'} dev: true + /patronum@1.19.1(effector@22.8.6): + resolution: {integrity: sha512-WnxrEczrg8MiEjp6hSqOOcwyOVfns9/kLey92RX1V7rJN8jRN4FI4JIJFbi+7aoFIRvOur5NsD/1Ooiimul8aQ==} + peerDependencies: + effector: ^22.1.2 + dependencies: + effector: 22.8.6 + dev: false + /pbkdf2@3.1.2: resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} engines: {node: '>=0.12'} @@ -16170,7 +16229,6 @@ packages: /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} - dev: true /react-merge-refs@1.1.0: resolution: {integrity: sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==} @@ -16853,7 +16911,7 @@ packages: dependencies: '@types/json-schema': 7.0.11 ajv: 8.12.0 - ajv-formats: 2.1.1 + ajv-formats: 2.1.1(ajv@8.12.0) ajv-keywords: 5.1.0(ajv@8.12.0) dev: true @@ -17720,7 +17778,7 @@ packages: inline-style-parser: 0.1.1 dev: true - /styled-components@5.3.10(react-dom@18.2.0)(react@18.2.0): + /styled-components@5.3.10(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0): resolution: {integrity: sha512-3kSzSBN0TiCnGJM04UwO1HklIQQSXW7rCARUk+VyMR7clz8XVlA3jijtf5ypqoDIdNMKx3la4VvaPFR855SFcg==} engines: {node: '>=10'} peerDependencies: @@ -17738,6 +17796,7 @@ packages: hoist-non-react-statics: 3.3.2 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) + react-is: 18.2.0 shallowequal: 1.1.0 supports-color: 5.5.0 dev: false @@ -18786,6 +18845,14 @@ packages: querystring: 0.2.0 dev: true + /use-sync-external-store@1.2.0(react@18.2.0): + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /use@3.1.1: resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} engines: {node: '>=0.10.0'} diff --git a/src/renderer/app/App.tsx b/src/renderer/app/App.tsx index f02d1363a0..0066916728 100644 --- a/src/renderer/app/App.tsx +++ b/src/renderer/app/App.tsx @@ -3,7 +3,8 @@ import { ErrorBoundary } from 'react-error-boundary'; import { useNavigate, useRoutes } from 'react-router-dom'; import { FallbackScreen } from '@renderer/components/common'; -import { useAccount } from '../entities/account/lib/accountService'; +import { useAccount } from '@renderer/entities/account'; +import { contactModel } from '@renderer/entities/contact'; import { ConfirmDialogProvider, I18Provider, @@ -25,6 +26,10 @@ const App = () => { const [showSplashScreen, setShowSplashScreen] = useState(true); const [isAccountsLoading, setIsAccountsLoading] = useState(true); + useEffect(() => { + contactModel.events.appStarted(); + }, []); + useEffect(() => { setTimeout(() => setShowSplashScreen(false), SPLASH_SCREEN_DELAY); diff --git a/src/renderer/app/providers/context/MatrixContext/MatrixContext.test.tsx b/src/renderer/app/providers/context/MatrixContext/MatrixContext.test.tsx index 867c1c28a1..801821526a 100644 --- a/src/renderer/app/providers/context/MatrixContext/MatrixContext.test.tsx +++ b/src/renderer/app/providers/context/MatrixContext/MatrixContext.test.tsx @@ -1,10 +1,10 @@ import { act, render, screen } from '@testing-library/react'; -import { Matrix } from '@renderer/entities/matrix'; -import { MatrixProvider } from './MatrixContext'; +import { Matrix } from '@renderer/shared/api/matrix'; import { ConnectionType } from '@renderer/domain/connection'; +import { MatrixProvider } from './MatrixContext'; -jest.mock('@renderer/entities/matrix', () => ({ Matrix: jest.fn().mockReturnValue({}) })); +jest.mock('@renderer/shared/api/matrix', () => ({ Matrix: jest.fn().mockReturnValue({}) })); jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ diff --git a/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx b/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx index a43b320797..bcf6976bfa 100644 --- a/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx +++ b/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx @@ -1,35 +1,35 @@ import { createContext, PropsWithChildren, useContext, useEffect, useRef, useState } from 'react'; import { createMultisigAccount, getMultisigAccountId, MultisigAccount, useAccount } from '@renderer/entities/account'; -import { toAddress, getCreatedDateFromApi, validateCallData } from '@renderer/shared/lib/utils'; -import { useContact } from '@renderer/entities/contact'; +import { getCreatedDateFromApi, toAddress, validateCallData } from '@renderer/shared/lib/utils'; import { AccountId, Address, CallHash, ChainId, SigningType } from '@renderer/domain/shared-kernel'; -import { useMultisigTx, useMultisigEvent } from '@renderer/entities/multisig'; +import { useMultisigEvent, useMultisigTx } from '@renderer/entities/multisig'; import { Signatory } from '@renderer/entities/signatory'; -import { useNetworkContext } from '@renderer/app/providers/context/NetworkContext/NetworkContext'; -import { - useTransaction, - MultisigEvent, - MultisigTransaction, - MultisigTxFinalStatus, - MultisigTxInitStatus, - MultisigTxStatus, - SigningStatus, -} from '@renderer/entities/transaction'; -import { useNotification, MultisigNotificationType } from '@renderer/entities/notification'; +import { useContact } from '@renderer/entities/contact'; +import { MultisigNotificationType, useNotification } from '@renderer/entities/notification'; +import { useMultisigChainContext } from '@renderer/app/providers'; +import { useNetworkContext } from '../NetworkContext'; import { - Matrix, ApprovePayload, BaseMultisigPayload, CancelPayload, FinalApprovePayload, InvitePayload, ISecureMessenger, + Matrix, MultisigPayload, SpektrExtras, UpdatePayload, -} from '@renderer/entities/matrix'; -import { useMultisigChainContext } from '@renderer/app/providers'; +} from '@renderer/shared/api/matrix'; +import { + MultisigEvent, + MultisigTransaction, + MultisigTxFinalStatus, + MultisigTxInitStatus, + MultisigTxStatus, + SigningStatus, + useTransaction, +} from '@renderer/entities/transaction'; type MatrixContextProps = { matrix: ISecureMessenger; diff --git a/src/renderer/app/providers/routes/paths.ts b/src/renderer/app/providers/routes/paths.ts index 29d8a7719e..b6a0dc6916 100644 --- a/src/renderer/app/providers/routes/paths.ts +++ b/src/renderer/app/providers/routes/paths.ts @@ -11,7 +11,8 @@ export const Paths = { // Address book ADDRESS_BOOK: '/address-book', - MANAGE_CONTACT: '/address-book/contact', + CREATE_CONTACT: '/address-book/create-contact', + EDIT_CONTACT: '/address-book/edit-contact', // Settings SETTINGS: '/settings', diff --git a/src/renderer/app/providers/routes/routesConfig.tsx b/src/renderer/app/providers/routes/routesConfig.tsx index 14b7cef975..c8148cccdf 100644 --- a/src/renderer/app/providers/routes/routesConfig.tsx +++ b/src/renderer/app/providers/routes/routesConfig.tsx @@ -18,8 +18,11 @@ export const routesConfig: RouteObject[] = [ { path: Paths.NOTIFICATIONS, element: }, { path: Paths.ADDRESS_BOOK, - element: , - children: [{ path: Paths.MANAGE_CONTACT, element: }], + element: , + children: [ + { path: Paths.CREATE_CONTACT, element: }, + { path: Paths.EDIT_CONTACT, element: }, + ], }, { path: Paths.SETTINGS, diff --git a/src/renderer/components/forms/ContactForm/ContactForm.test.tsx b/src/renderer/components/forms/ContactForm/ContactForm.test.tsx deleted file mode 100644 index fca41751cd..0000000000 --- a/src/renderer/components/forms/ContactForm/ContactForm.test.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import noop from 'lodash/noop'; - -import { ContactForm } from './ContactForm'; -import { Contact } from '@renderer/entities/contact'; -import { TEST_ADDRESS, TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; - -jest.mock('@renderer/app/providers', () => ({ - useI18n: jest.fn().mockReturnValue({ - t: (key: string) => key, - }), - useMatrix: jest.fn().mockReturnValue({ - matrix: { validateFullUserName: jest.fn().mockReturnValue(true) }, - }), -})); - -jest.mock('@renderer/entities/contact', () => ({ - useContact: jest.fn().mockReturnValue({ - addContact: jest.fn(), - updateContact: jest.fn(), - getContacts: jest.fn().mockResolvedValue([]), - }), -})); - -describe('pages/AddressBook/ContactForm', () => { - const contact: Contact = { - name: 'Contact', - address: TEST_ADDRESS, - accountId: TEST_ACCOUNT_ID, - matrixId: '@bob:matrix.com', - }; - - test('should render component', () => { - render(); - - const inputs = screen.getAllByRole('textbox'); - const button = screen.getByRole('button'); - - expect(inputs).toHaveLength(3); - expect(button).toBeInTheDocument(); - }); - - test('should fill all inputs', () => { - render(); - - const inputs = screen.getAllByRole('textbox'); - - expect(inputs[0]).toHaveValue(contact.name); - expect(inputs[1]).toHaveValue(contact.address); - expect(inputs[2]).toHaveValue(contact.matrixId); - }); -}); diff --git a/src/renderer/components/forms/ContactForm/ContactForm.tsx b/src/renderer/components/forms/ContactForm/ContactForm.tsx deleted file mode 100644 index f4e30476bc..0000000000 --- a/src/renderer/components/forms/ContactForm/ContactForm.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import { Controller, SubmitHandler, useForm } from 'react-hook-form'; -import { useEffect, useState } from 'react'; - -import { Icon, Identicon, Button, Input, InputHint } from '@renderer/shared/ui'; -import { useI18n, useMatrix } from '@renderer/app/providers'; -import { Address, ErrorType } from '@renderer/domain/shared-kernel'; -import { useContact, Contact } from '@renderer/entities/contact'; -import { toAccountId, validateAddress } from '@renderer/shared/lib/utils'; - -type ContactFormData = { - name: string; - matrixId?: string; - address: Address; -}; - -type Props = { - contact?: Contact; - onFormSubmit: () => void; -}; - -export const ContactForm = ({ contact, onFormSubmit }: Props) => { - const { t } = useI18n(); - const { matrix } = useMatrix(); - const { addContact, updateContact, getContacts } = useContact(); - - const [contacts, setContacts] = useState([]); - - const isEdit = contact !== undefined; - - useEffect(() => { - getContacts().then(setContacts); - }, []); - - const { - control, - handleSubmit, - formState: { isValid, isSubmitting }, - } = useForm({ - mode: 'onChange', - defaultValues: { - name: contact?.name || '', - matrixId: contact?.matrixId || '', - address: contact?.address || '', - }, - }); - - const onSubmit: SubmitHandler = async (newContact) => { - const updatedContact = { - ...contact, - ...newContact, - accountId: toAccountId(newContact.address), - }; - - if (isEdit) { - await updateContact(updatedContact); - } else { - await addContact(updatedContact); - } - - onFormSubmit(); - }; - - const validateMatrixLogin = (value?: string): boolean => { - return !value || matrix.validateFullUserName(value); - }; - - const validateAddressExists = (value?: Address): boolean => { - if (!value) return true; - - const accountId = toAccountId(value); - const isSameAddress = value.toLowerCase() === contact?.address.toLowerCase(); - const isUnique = contacts.every((contact) => contact.accountId !== accountId); - - return isSameAddress || isUnique; - }; - - const validateNameExists = (value?: string): boolean => { - if (!value) return true; - - const isSameName = value.toLowerCase() === contact?.name.toLowerCase(); - const isUnique = contacts.every((contact) => contact.name.toLowerCase() !== value.toLowerCase()); - - return isSameName || isUnique; - }; - - return ( -
    - ( -
    - - - {t('addressBook.addContact.nameRequiredError')} - - - {t('addressBook.addContact.nameExistsError')} - -
    - )} - /> - ( -
    - : - } - wrapperClass="h-[42px]" - className="w-full ml-2" - label={t('addressBook.addContact.accountIdLabel')} - placeholder={t('addressBook.addContact.accountIdPlaceholder')} - invalid={Boolean(error)} - value={value} - onChange={onChange} - /> - - {t('addressBook.editContact.editWarning')} - - - - {t('addressBook.addContact.accountIdRequiredError')} - - - {t('addressBook.addContact.accountIdIncorrectError')} - - - {t('addressBook.addContact.accountIdExistsError')} - -
    - )} - /> - - ( -
    - - {t('addressBook.addContact.matrixIdHint')} - - {t('addressBook.addContact.matrixIdError')} - -
    - )} - /> - - - - ); -}; diff --git a/src/renderer/components/forms/index.ts b/src/renderer/components/forms/index.ts deleted file mode 100644 index a0caeb8efd..0000000000 --- a/src/renderer/components/forms/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ContactForm } from './ContactForm/ContactForm'; diff --git a/src/renderer/components/modals/CreateMultisigAccount/common/types.ts b/src/renderer/components/modals/CreateMultisigAccount/common/types.ts index bcfc71b6e3..d34edeb78a 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/common/types.ts +++ b/src/renderer/components/modals/CreateMultisigAccount/common/types.ts @@ -1,5 +1,5 @@ -import { Contact } from '@renderer/entities/contact/model/contact'; -import { ChainId, AccountId } from '@renderer/domain/shared-kernel'; +import { AccountId, ChainId } from '@renderer/domain/shared-kernel'; +import type { Contact } from '@renderer/entities/contact'; export type ExtendedContact = Contact & { index: string }; export type ExtendedWallet = ExtendedContact & { walletName?: string; chainId?: ChainId }; diff --git a/src/renderer/components/modals/CreateMultisigAccount/components/SelectSignatories.tsx b/src/renderer/components/modals/CreateMultisigAccount/components/SelectSignatories.tsx index 201285aea4..82002e89c0 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/components/SelectSignatories.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/components/SelectSignatories.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { keyBy } from 'lodash'; import { cnTw, includes, toAddress } from '@renderer/shared/lib/utils'; @@ -6,24 +6,15 @@ import { useI18n, useMatrix, useNetworkContext } from '@renderer/app/providers'; import { AccountId } from '@renderer/domain/shared-kernel'; import { useToggle } from '@renderer/shared/lib/hooks'; import { WalletsTabItem } from './WalletsTabItem'; -import { - Icon, - FootnoteText, - SearchInput, - SmallTitleText, - Checkbox, - Button, - BaseModal, - Tabs, -} from '@renderer/shared/ui'; -import { EmptyContacts } from '@renderer/pages/AddressBook/Overview/components'; -import { isWalletContact, Account, MultisigAccount } from '@renderer/entities/account'; -import { ContactForm } from '@renderer/components/forms'; +import { Button, Checkbox, FootnoteText, Icon, SearchInput, SmallTitleText, Tabs } from '@renderer/shared/ui'; +import { Account, isWalletContact, MultisigAccount } from '@renderer/entities/account'; import { TabItem } from '@renderer/shared/ui/types'; -import { Contact } from '@renderer/entities/contact'; import { WalletDS } from '@renderer/shared/api/storage'; +import { CreateContactModal } from '@renderer/widgets'; import { getSelectedLength } from '../common/utils'; -import { ExtendedWallet, ExtendedContact, SelectedMap } from '../common/types'; +import { ExtendedContact, ExtendedWallet, SelectedMap } from '../common/types'; +import { EmptyContactList } from '@renderer/entities/contact'; +import type { Contact } from '@renderer/entities/contact'; const enum SignatoryTabs { WALLETS = 'wallets', @@ -141,7 +132,7 @@ export const SelectSignatories = ({ isActive, wallets, accounts, contacts, onSel ))} ) : ( - + ); const ContactsTab = ( @@ -175,7 +166,7 @@ export const SelectSignatories = ({ isActive, wallets, accounts, contacts, onSel ))} ) : ( - + )}
    ); @@ -226,16 +217,7 @@ export const SelectSignatories = ({ isActive, wallets, accounts, contacts, onSel /> - - - + ); }; diff --git a/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx b/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx index ec2bb3081e..b82a0910dd 100644 --- a/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx +++ b/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx @@ -2,21 +2,21 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import { Trans } from 'react-i18next'; import { useEffect, useState } from 'react'; -import { useMatrix, useI18n } from '@renderer/app/providers'; -import { WELL_KNOWN_SERVERS } from '@renderer/entities/matrix'; +import { useI18n, useMatrix } from '@renderer/app/providers'; +import { validateShortUserName, WELL_KNOWN_SERVERS } from '@renderer/shared/api/matrix'; import { Alert, Button, Combobox, FootnoteText, + Icon, InfoLink, Input, InputHint, - PasswordInput, - Icon, Loader, + PasswordInput, } from '@renderer/shared/ui'; -import { ComboboxOption } from '@renderer/shared/ui/Dropdowns/common/types'; +import type { ComboboxOption } from '@renderer/shared/ui/types'; const HOME_SERVERS = WELL_KNOWN_SERVERS.map((server) => ({ id: server.domain, @@ -174,7 +174,7 @@ const LoginForm = () => { ( <> Promise; diff --git a/src/renderer/entities/contact/lib/contactService.ts b/src/renderer/entities/contact/lib/contactService.ts index 6b3dce3509..6c2756b87a 100644 --- a/src/renderer/entities/contact/lib/contactService.ts +++ b/src/renderer/entities/contact/lib/contactService.ts @@ -2,7 +2,7 @@ import { useLiveQuery } from 'dexie-react-hooks'; import storage, { ContactDS } from '@renderer/shared/api/storage'; import { IContactService } from './common/types'; -import { Contact } from '@renderer/entities/contact/model/contact'; +import type { Contact } from '@renderer/entities/contact'; export const useContact = (): IContactService => { const contactStorage = storage.connectTo('contacts'); diff --git a/src/renderer/entities/contact/model/contact.ts b/src/renderer/entities/contact/model/contact.ts index 27d3297a42..f9679c2280 100644 --- a/src/renderer/entities/contact/model/contact.ts +++ b/src/renderer/entities/contact/model/contact.ts @@ -1,8 +1,61 @@ -import { AccountId, Address } from '../../../domain/shared-kernel'; +import { createEffect, createEvent, createStore, forward } from 'effector'; -export type Contact = { - name: string; - address: Address; - accountId: AccountId; - matrixId?: string; +import { ContactDS } from '@renderer/shared/api/storage'; +import { splice } from '@renderer/shared/lib/utils'; +import { useContact } from '../lib'; +import type { Contact } from './types'; + +const contactService = useContact(); + +export const $contacts = createStore([]); +const appStarted = createEvent(); + +const populateContactsFx = createEffect(() => { + return contactService.getContacts(); +}); + +const addContactFx = createEffect(async (contact: Contact) => { + const id = await contactService.addContact(contact); + + return { id, ...contact }; +}); + +const updateContactFx = createEffect(async (contact: Contact) => { + const id = await contactService.updateContact(contact); + + return { id, ...contact }; +}); + +const deleteContactFx = createEffect((contactId: string) => { + return contactService.deleteContact(contactId); +}); + +$contacts + .on(populateContactsFx.doneData, (_, contacts) => { + return contacts; + }) + .on(addContactFx.doneData, (state, contact) => { + return state.concat(contact); + }) + .on(deleteContactFx.doneData, (state, contactId) => { + return state.filter((s) => s.id !== contactId); + }) + .on(updateContactFx.doneData, (state, contact) => { + const position = state.findIndex((s) => s.id === contact.id); + + return splice(state, contact, position); + }); + +forward({ + from: appStarted, + to: populateContactsFx, +}); + +export const events = { + appStarted, +}; +export const effects = { + addContactFx, + deleteContactFx, + updateContactFx, }; diff --git a/src/renderer/entities/contact/model/types.ts b/src/renderer/entities/contact/model/types.ts new file mode 100644 index 0000000000..1010fd3095 --- /dev/null +++ b/src/renderer/entities/contact/model/types.ts @@ -0,0 +1,8 @@ +import { AccountId, Address } from '@renderer/domain/shared-kernel'; + +export type Contact = { + name: string; + address: Address; + accountId: AccountId; + matrixId?: string; +}; diff --git a/src/renderer/entities/contact/ui/ContactList.tsx b/src/renderer/entities/contact/ui/ContactList.tsx new file mode 100644 index 0000000000..9876375825 --- /dev/null +++ b/src/renderer/entities/contact/ui/ContactList.tsx @@ -0,0 +1,19 @@ +import { PropsWithChildren } from 'react'; + +import { FootnoteText } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; + +export const ContactList = ({ children }: PropsWithChildren) => { + const { t } = useI18n(); + + return ( +
    +
    + {t('addressBook.contactList.nameColumnTitle')} + {t('addressBook.contactList.matrixIdColumnTitle')} +
    + +
      {children}
    +
    + ); +}; diff --git a/src/renderer/entities/contact/ui/ContactList/ContactList.tsx b/src/renderer/entities/contact/ui/ContactList/ContactList.tsx deleted file mode 100644 index 3132c79f53..0000000000 --- a/src/renderer/entities/contact/ui/ContactList/ContactList.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { useState, useEffect } from 'react'; - -import { useI18n } from '@renderer/app/providers'; -import { EmptySearch } from '@renderer/pages/AddressBook/Overview/components'; -import { includes } from '@renderer/shared/lib/utils'; -import { AddressWithName } from '@renderer/entities/account'; -import { BodyText, FootnoteText, IconButton, Plate } from '@renderer/shared/ui'; -import { Contact } from '@renderer/entities/contact'; - -type Props = { - query?: string; - contacts: Contact[]; - onEditContact: (contact?: Contact) => void; -}; - -export const ContactList = ({ contacts, query, onEditContact }: Props) => { - const { t } = useI18n(); - - const [filteredContacts, setFilteredContacts] = useState([]); - - useEffect(() => { - const filtered = contacts - .filter((c) => includes(c.name, query) || includes(c.address, query) || includes(c.matrixId, query)) - .sort((a, b) => a.name.localeCompare(b.name)); - - setFilteredContacts(filtered); - }, [query, contacts]); - - if (filteredContacts.length === 0) { - return ; - } - - return ( -
    -
    - {t('addressBook.contactList.nameColumnTitle')} - {t('addressBook.contactList.matrixIdColumnTitle')} -
    - -
      - {filteredContacts.map((contact) => ( -
    • - - - {contact.matrixId || '-'} - onEditContact(contact)} /> - -
    • - ))} -
    -
    - ); -}; diff --git a/src/renderer/entities/contact/ui/ContactRow.tsx b/src/renderer/entities/contact/ui/ContactRow.tsx new file mode 100644 index 0000000000..685527bce2 --- /dev/null +++ b/src/renderer/entities/contact/ui/ContactRow.tsx @@ -0,0 +1,28 @@ +import { PropsWithChildren } from 'react'; + +import { BodyText, IconButton, Identicon, Plate, Truncate } from '@renderer/shared/ui'; +import { ContactDS } from '@renderer/shared/api/storage'; +import { copyToClipboard } from '@renderer/shared/lib/utils'; + +type Props = { + contact: ContactDS; +}; + +export const ContactRow = ({ contact, children }: PropsWithChildren) => { + return ( + +
    + +
    + {contact.name} +
    + + copyToClipboard(contact.address)} /> +
    +
    +
    + {contact.matrixId || '-'} + {children} +
    + ); +}; diff --git a/src/renderer/entities/contact/ui/EmptyContactList.tsx b/src/renderer/entities/contact/ui/EmptyContactList.tsx new file mode 100644 index 0000000000..20ff5b81cd --- /dev/null +++ b/src/renderer/entities/contact/ui/EmptyContactList.tsx @@ -0,0 +1,37 @@ +import { useNavigate } from 'react-router-dom'; + +import { BodyText, Button, Icon } from '@renderer/shared/ui'; +import { Paths, useI18n } from '@renderer/app/providers'; + +type Props = { + description?: string; + onNewContact?: () => void; +}; +export const EmptyContactList = ({ description, onNewContact }: Props) => { + const { t } = useI18n(); + const navigate = useNavigate(); + + const navigateToNewContact = () => { + if (onNewContact) { + onNewContact(); + } else { + navigate(Paths.CREATE_CONTACT); + } + }; + + return ( +
    + + {description || t('addressBook.contactList.noContactsLabel')} + + +
    + ); +}; diff --git a/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.tsx b/src/renderer/entities/contact/ui/EmptyFilteredContacts.tsx similarity index 90% rename from src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.tsx rename to src/renderer/entities/contact/ui/EmptyFilteredContacts.tsx index 7fb0dbfb7f..c10c6daf96 100644 --- a/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.tsx +++ b/src/renderer/entities/contact/ui/EmptyFilteredContacts.tsx @@ -1,7 +1,7 @@ import { Icon, BodyText } from '@renderer/shared/ui'; import { useI18n } from '@renderer/app/providers'; -export const EmptySearch = () => { +export const EmptyFilteredContacts = () => { const { t } = useI18n(); return ( diff --git a/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.test.tsx b/src/renderer/entities/contact/ui/__tests__/EmptyContactList.test.tsx similarity index 56% rename from src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.test.tsx rename to src/renderer/entities/contact/ui/__tests__/EmptyContactList.test.tsx index 2e42cdea21..fbecd2706f 100644 --- a/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.test.tsx +++ b/src/renderer/entities/contact/ui/__tests__/EmptyContactList.test.tsx @@ -1,6 +1,7 @@ import { render, screen } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; -import { EmptyContacts } from './EmptyContacts'; +import { EmptyContactList } from '../EmptyContactList'; jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ @@ -8,14 +9,12 @@ jest.mock('@renderer/app/providers', () => ({ }), })); -describe('pages/AddressBook/components/EmptyState/EmptyContacts.tsx', () => { +describe('entities/contact/ui/EmptyContactList', () => { test('should render component', () => { - const onAddContactSpy = jest.fn(); - - render(); + render(, { wrapper: MemoryRouter }); const label = screen.getByText('addressBook.contactList.noContactsLabel'); - const button = screen.getByText('addressBook.addContactButton'); + const button = screen.getByText('addressBook.createContact.addContactButton'); expect(label).toBeInTheDocument(); expect(button).toBeInTheDocument(); diff --git a/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.test.tsx b/src/renderer/entities/contact/ui/__tests__/EmptyFilteredContacts.test.tsx similarity index 68% rename from src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.test.tsx rename to src/renderer/entities/contact/ui/__tests__/EmptyFilteredContacts.test.tsx index a5fec042ea..210523e80a 100644 --- a/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptySearch.test.tsx +++ b/src/renderer/entities/contact/ui/__tests__/EmptyFilteredContacts.test.tsx @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react'; -import { EmptySearch } from './EmptySearch'; +import { EmptyFilteredContacts } from '../EmptyFilteredContacts'; jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ @@ -8,9 +8,9 @@ jest.mock('@renderer/app/providers', () => ({ }), })); -describe('pages/AddressBook/components/EmptyState/EmptySearch.tsx', () => { +describe('entities/contact/ui/EmptyFilteredContacts', () => { test('should render component', () => { - render(); + render(); const label = screen.getByText('addressBook.contactList.emptySearchLabel'); expect(label).toBeInTheDocument(); diff --git a/src/renderer/entities/contact/ui/index.ts b/src/renderer/entities/contact/ui/index.ts index 4baed554f9..6aa16fc348 100644 --- a/src/renderer/entities/contact/ui/index.ts +++ b/src/renderer/entities/contact/ui/index.ts @@ -1 +1,4 @@ -export * from './ContactList/ContactList'; +export { ContactRow } from './ContactRow'; +export { ContactList } from './ContactList'; +export { EmptyContactList } from './EmptyContactList'; +export { EmptyFilteredContacts } from './EmptyFilteredContacts'; diff --git a/src/renderer/entities/matrix/index.ts b/src/renderer/entities/matrix/index.ts deleted file mode 100644 index f41a696fd2..0000000000 --- a/src/renderer/entities/matrix/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './lib'; diff --git a/src/renderer/entities/matrix/lib/__tests__/matrix.test.ts b/src/renderer/entities/matrix/lib/__tests__/matrix.test.ts deleted file mode 100644 index 1b2426b28f..0000000000 --- a/src/renderer/entities/matrix/lib/__tests__/matrix.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -describe('service/source/matrix', () => { - test('should init', () => { - expect(1).toEqual(1); - }); -}); - -export {}; diff --git a/src/renderer/entities/signatory/model/signatory.ts b/src/renderer/entities/signatory/model/signatory.ts index beb708928e..4ca1634631 100644 --- a/src/renderer/entities/signatory/model/signatory.ts +++ b/src/renderer/entities/signatory/model/signatory.ts @@ -1,4 +1,8 @@ -import { Contact } from '../../contact/model/contact'; -import { PartialBy } from '@renderer/domain/utility'; +import { AccountId, Address } from '@renderer/domain/shared-kernel'; -export type Signatory = PartialBy; +export type Signatory = { + name?: string; + address: Address; + accountId: AccountId; + matrixId?: string; +}; diff --git a/src/renderer/entities/transaction/lib/common/types.ts b/src/renderer/entities/transaction/lib/common/types.ts index 8b46d93c2f..a4d1f840b7 100644 --- a/src/renderer/entities/transaction/lib/common/types.ts +++ b/src/renderer/entities/transaction/lib/common/types.ts @@ -26,7 +26,7 @@ export type ITransactionService = { callback: (executed: boolean, params: ExtrinsicResultParams | string) => void, ) => void; getTransactionFee: (transaction: Transaction, api: ApiPromise) => Promise; - getExtrinsicWeight: (extrinsic: SubmittableExtrinsic<'promise'>, api: ApiPromise) => Promise; + getExtrinsicWeight: (extrinsic: SubmittableExtrinsic<'promise'>) => Promise; getTransactionDeposit: (threshold: Threshold, api: ApiPromise) => string; getTransactionHash: (transaction: Transaction, api: ApiPromise) => HashData; decodeCallData: (api: ApiPromise, accountId: Address, callData: CallData) => DecodedTransaction; diff --git a/src/renderer/entities/transaction/lib/transactionService.ts b/src/renderer/entities/transaction/lib/transactionService.ts index a988f9bf1e..98207773be 100644 --- a/src/renderer/entities/transaction/lib/transactionService.ts +++ b/src/renderer/entities/transaction/lib/transactionService.ts @@ -345,15 +345,15 @@ export const useTransaction = (): ITransactionService => { const getTransactionFee = async (transaction: Transaction, api: ApiPromise): Promise => { const extrinsic = getExtrinsic[transaction.type](transaction.args, api); - const { partialFee } = await extrinsic.paymentInfo(transaction.address); + const paymentInfo = await extrinsic.paymentInfo(transaction.address); - return partialFee.toString(); + return paymentInfo.partialFee.toString(); }; - const getExtrinsicWeight = async (extrinsic: SubmittableExtrinsic<'promise'>, api: ApiPromise): Promise => { - const { weight } = await extrinsic.paymentInfo(extrinsic.signer); + const getExtrinsicWeight = async (extrinsic: SubmittableExtrinsic<'promise'>): Promise => { + const paymentInfo = await extrinsic.paymentInfo(extrinsic.signer); - return weight; + return paymentInfo.weight; }; const getTransactionDeposit = (threshold: Threshold, api: ApiPromise): string => { diff --git a/src/renderer/features/contacts/ContactFilter/index.ts b/src/renderer/features/contacts/ContactFilter/index.ts new file mode 100644 index 0000000000..d71fd80f3c --- /dev/null +++ b/src/renderer/features/contacts/ContactFilter/index.ts @@ -0,0 +1,2 @@ +export { ContactFilter } from './ui/ContactFilter'; +export * as filterModel from './model/contact-filter'; diff --git a/src/renderer/features/contacts/ContactFilter/model/contact-filter.ts b/src/renderer/features/contacts/ContactFilter/model/contact-filter.ts new file mode 100644 index 0000000000..55fc1da5ee --- /dev/null +++ b/src/renderer/features/contacts/ContactFilter/model/contact-filter.ts @@ -0,0 +1,25 @@ +import { combine, createEvent, createStore } from 'effector'; + +import { includes } from '@renderer/shared/lib/utils'; +import { contactModel } from '@renderer/entities/contact'; + +export const $filterQuery = createStore(''); +const setQuery = createEvent(); + +$filterQuery.on(setQuery, (_, query) => query); + +export const $contactsFiltered = combine(contactModel.$contacts, $filterQuery, (contacts, query) => { + return contacts + .filter((c) => { + const hasName = includes(c.name, query); + const hasAddress = includes(c.address, query); + const hasMatrixId = includes(c.matrixId, query); + + return hasName || hasAddress || hasMatrixId; + }) + .sort((a, b) => a.name.localeCompare(b.name)); +}); + +export const events = { + setQuery, +}; diff --git a/src/renderer/features/contacts/ContactFilter/ui/ContactFilter.tsx b/src/renderer/features/contacts/ContactFilter/ui/ContactFilter.tsx new file mode 100644 index 0000000000..2f207d3284 --- /dev/null +++ b/src/renderer/features/contacts/ContactFilter/ui/ContactFilter.tsx @@ -0,0 +1,15 @@ +import { SearchInput } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import * as filterModel from '../model/contact-filter'; + +export const ContactFilter = () => { + const { t } = useI18n(); + + return ( + + ); +}; diff --git a/src/renderer/features/contacts/CreateContactForm/index.ts b/src/renderer/features/contacts/CreateContactForm/index.ts new file mode 100644 index 0000000000..b07e53d1d8 --- /dev/null +++ b/src/renderer/features/contacts/CreateContactForm/index.ts @@ -0,0 +1,3 @@ +export { CreateContactForm } from './ui/CreateContactForm'; +export { CreateContactNavigation } from './ui/CreateContactNavigation'; +export * as createFormModel from './model/contact-form'; diff --git a/src/renderer/features/contacts/CreateContactForm/model/contact-form.ts b/src/renderer/features/contacts/CreateContactForm/model/contact-form.ts new file mode 100644 index 0000000000..839f29937e --- /dev/null +++ b/src/renderer/features/contacts/CreateContactForm/model/contact-form.ts @@ -0,0 +1,98 @@ +import { attach, createApi, createStore, forward, sample } from 'effector'; +import { createForm } from 'effector-forms'; + +import { Contact, contactModel } from '@renderer/entities/contact'; +import { toAccountId, validateAddress } from '@renderer/shared/lib/utils'; +import { validateFullUserName } from '@renderer/shared/api/matrix'; + +export type Callbacks = { + onSubmit: () => void; +}; + +const $callbacks = createStore(null); +const callbacksApi = createApi($callbacks, { + callbacksChanged: (state, props: Callbacks) => ({ ...state, ...props }), +}); + +export const contactForm = createForm({ + fields: { + name: { + init: '', + rules: [ + { name: 'required', errorText: 'addressBook.createContact.nameRequiredError', validator: Boolean }, + { + name: 'exist', + errorText: 'addressBook.createContact.nameExistsError', + source: contactModel.$contacts, + validator: validateNameExist, + }, + ], + }, + address: { + init: '', + rules: [ + { name: 'required', errorText: 'addressBook.createContact.accountIdRequiredError', validator: Boolean }, + { name: 'invalid', errorText: 'addressBook.createContact.accountIdIncorrectError', validator: validateAddress }, + { + name: 'exist', + errorText: 'addressBook.createContact.accountIdExistsError', + source: contactModel.$contacts, + validator: validateAddressExist, + }, + ], + }, + matrixId: { + init: '', + rules: [{ name: 'invalid', errorText: 'addressBook.createContact.matrixIdError', validator: validateMatrixId }], + }, + }, + validateOn: ['change', 'submit'], +}); + +function validateNameExist(value: string, _: unknown, contacts: Contact[]): boolean { + if (!value) return true; + + return contacts.every((contact) => contact.name.toLowerCase() !== value.toLowerCase()); +} + +function validateAddressExist(value: string, _: unknown, contacts: Contact[]): boolean { + if (!value) return true; + + const accountId = toAccountId(value); + + return contacts.every((contact) => contact.accountId !== accountId); +} + +function validateMatrixId(value: string): boolean { + if (!value) return true; + + return validateFullUserName(value); +} + +const createContactFx = attach({ + effect: contactModel.effects.addContactFx, + source: contactForm.$values, + mapParams: (_, data) => { + return { ...data, accountId: toAccountId(data.address) }; + }, +}); + +forward({ + from: contactForm.formValidated, + to: createContactFx, +}); + +sample({ + clock: createContactFx.doneData, + target: attach({ + source: $callbacks, + effect: (state) => state?.onSubmit(), + }), +}); + +export const $submitPending = createContactFx.pending; + +export const events = { + callbacksChanged: callbacksApi.callbacksChanged, + formInitiated: contactForm.reset, +}; diff --git a/src/renderer/features/contacts/CreateContactForm/ui/CreateContactForm.tsx b/src/renderer/features/contacts/CreateContactForm/ui/CreateContactForm.tsx new file mode 100644 index 0000000000..daa4854ddc --- /dev/null +++ b/src/renderer/features/contacts/CreateContactForm/ui/CreateContactForm.tsx @@ -0,0 +1,99 @@ +import { useStore } from 'effector-react'; +import { FormEvent, useEffect } from 'react'; +import { useForm } from 'effector-forms'; + +import * as createFormModel from '../model/contact-form'; +import { Button, Icon, Identicon, Input, InputHint } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; + +type Props = createFormModel.Callbacks; +export const CreateContactForm = ({ onSubmit }: Props) => { + const { t } = useI18n(); + + const { + submit, + isValid, + fields: { name, address, matrixId }, + } = useForm(createFormModel.contactForm); + + const pending = useStore(createFormModel.$submitPending); + + useEffect(() => { + createFormModel.events.formInitiated(); + }, []); + + useEffect(() => { + createFormModel.events.callbacksChanged({ onSubmit }); + }, [onSubmit]); + + const submitForm = (event: FormEvent) => { + event.preventDefault(); + submit(); + }; + + const canShowIdenticon = address?.value && !address?.hasError(); + + return ( +
    +
    + + + {t(name?.errorText())} + +
    + +
    + + ) : ( + + ) + } + onChange={address?.onChange} + /> + + {t(address?.errorText())} + +
    + +
    + + {t('addressBook.createContact.matrixIdHint')} + + {t(matrixId?.errorText())} + +
    + + +
    + ); +}; diff --git a/src/renderer/features/contacts/CreateContactForm/ui/CreateContactNavigation.tsx b/src/renderer/features/contacts/CreateContactForm/ui/CreateContactNavigation.tsx new file mode 100644 index 0000000000..50c429b6d5 --- /dev/null +++ b/src/renderer/features/contacts/CreateContactForm/ui/CreateContactNavigation.tsx @@ -0,0 +1,15 @@ +import { useNavigate } from 'react-router-dom'; + +import { Paths, useI18n } from '@renderer/app/providers'; +import { Button } from '@renderer/shared/ui'; + +export const CreateContactNavigation = () => { + const { t } = useI18n(); + const navigate = useNavigate(); + + return ( + + ); +}; diff --git a/src/renderer/features/contacts/EditContactForm/index.ts b/src/renderer/features/contacts/EditContactForm/index.ts new file mode 100644 index 0000000000..c2ca7e6e05 --- /dev/null +++ b/src/renderer/features/contacts/EditContactForm/index.ts @@ -0,0 +1,3 @@ +export { EditContactForm } from './ui/EditContactForm'; +export { EditContactNavigation } from './ui/EditContactNavigation'; +export * as editFormModel from './model/contact-form'; diff --git a/src/renderer/features/contacts/EditContactForm/model/contact-form.ts b/src/renderer/features/contacts/EditContactForm/model/contact-form.ts new file mode 100644 index 0000000000..11d6a4d53b --- /dev/null +++ b/src/renderer/features/contacts/EditContactForm/model/contact-form.ts @@ -0,0 +1,136 @@ +import { attach, combine, createApi, createStore, forward, sample } from 'effector'; +import { createForm } from 'effector-forms'; +import { not } from 'patronum'; + +import { Contact, contactModel } from '@renderer/entities/contact'; +import { toAccountId, validateAddress } from '@renderer/shared/lib/utils'; +import { validateFullUserName } from '@renderer/shared/api/matrix'; +import { ContactDS } from '@renderer/shared/api/storage'; + +export type Callbacks = { + onSubmit: () => void; +}; + +const $callbacks = createStore(null); +const callbacksApi = createApi($callbacks, { + callbacksChanged: (state, props: Callbacks) => ({ ...state, ...props }), +}); + +export const $contactToEdit = createStore(null); +const contactApi = createApi($contactToEdit, { + formInitiated: (state, props: ContactDS) => ({ ...state, ...props }), +}); + +export const contactForm = createForm({ + fields: { + name: { + init: '', + rules: [ + { name: 'required', errorText: 'addressBook.editContact.nameRequiredError', validator: Boolean }, + { + name: 'exist', + errorText: 'addressBook.editContact.nameExistsError', + source: combine({ + contactToEdit: $contactToEdit, + contacts: contactModel.$contacts, + }), + validator: validateNameExist, + }, + ], + }, + address: { + init: '', + rules: [ + { name: 'required', errorText: 'addressBook.editContact.accountIdRequiredError', validator: Boolean }, + { name: 'invalid', errorText: 'addressBook.editContact.accountIdIncorrectError', validator: validateAddress }, + { + name: 'exist', + errorText: 'addressBook.editContact.accountIdExistsError', + source: combine({ + contactToEdit: $contactToEdit, + contacts: contactModel.$contacts, + }), + validator: validateAddressExist, + }, + ], + }, + matrixId: { + init: '', + rules: [{ name: 'invalid', errorText: 'addressBook.editContact.matrixIdError', validator: validateMatrixId }], + }, + }, + validateOn: ['change', 'submit'], +}); + +sample({ + clock: contactApi.formInitiated, + filter: contactForm.$isDirty, + target: contactForm.reset, +}); + +sample({ + clock: contactApi.formInitiated, + filter: not(contactForm.$isDirty), + fn: ({ name, address, matrixId }) => ({ name, address, matrixId }), + target: contactForm.setForm, +}); + +type SourceParams = { + contactToEdit: Contact; + contacts: Contact[]; +}; +function validateNameExist(value: string, _: unknown, params: SourceParams): boolean { + if (!value) return true; + + const isSameName = value.toLowerCase() === params.contactToEdit.name.toLowerCase(); + const isUnique = params.contacts.every((contact) => contact.name.toLowerCase() !== value.toLowerCase()); + + return isSameName || isUnique; +} + +function validateAddressExist(value: string, _: unknown, params: SourceParams): boolean { + if (!value) return true; + + const accountId = toAccountId(value); + const isSameAddress = value.toLowerCase() === params.contactToEdit.address.toLowerCase(); + const isUnique = params.contacts.every((contact) => contact.accountId !== accountId); + + return isSameAddress || isUnique; +} + +function validateMatrixId(value: string): boolean { + if (!value) return true; + + return validateFullUserName(value); +} + +const editContactFx = attach({ + effect: contactModel.effects.updateContactFx, + source: { + contactToEdit: $contactToEdit, + form: contactForm.$values, + }, + mapParams: (_, { contactToEdit, form }) => { + return { ...form, id: contactToEdit?.id, accountId: toAccountId(form.address) }; + }, +}); + +forward({ + from: contactForm.formValidated, + to: editContactFx, +}); + +sample({ + clock: editContactFx.doneData, + target: attach({ + source: $callbacks, + effect: (state) => state?.onSubmit(), + }), +}); + +export const $submitPending = editContactFx.pending; + +export const events = { + callbacksChanged: callbacksApi.callbacksChanged, + formInitiated: contactApi.formInitiated, +}; diff --git a/src/renderer/features/contacts/EditContactForm/ui/EditContactForm.tsx b/src/renderer/features/contacts/EditContactForm/ui/EditContactForm.tsx new file mode 100644 index 0000000000..cf8d0db109 --- /dev/null +++ b/src/renderer/features/contacts/EditContactForm/ui/EditContactForm.tsx @@ -0,0 +1,105 @@ +import { useStore } from 'effector-react'; +import { FormEvent, useEffect } from 'react'; +import { useForm } from 'effector-forms'; + +import * as editFormModel from '../model/contact-form'; +import { Button, Icon, Identicon, Input, InputHint } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { ContactDS } from '@renderer/shared/api/storage'; + +type Props = editFormModel.Callbacks & { + contactToEdit: ContactDS; +}; +export const EditContactForm = ({ contactToEdit, onSubmit }: Props) => { + const { t } = useI18n(); + + const { + submit, + isValid, + fields: { name, address, matrixId }, + } = useForm(editFormModel.contactForm); + + const pending = useStore(editFormModel.$submitPending); + + useEffect(() => { + editFormModel.events.formInitiated(contactToEdit); + }, [contactToEdit]); + + useEffect(() => { + editFormModel.events.callbacksChanged({ onSubmit }); + }, [onSubmit]); + + const submitForm = (event: FormEvent) => { + event.preventDefault(); + submit(); + }; + + const canShowIdenticon = address?.value && !address?.hasError(); + + return ( +
    +
    + + + {t(name?.errorText())} + +
    + +
    + + ) : ( + + ) + } + onChange={address?.onChange} + /> + + {t('addressBook.editContact.editWarning')} + + + {t(address?.errorText())} + +
    + +
    + + {t('addressBook.editContact.matrixIdHint')} + + {t(matrixId?.errorText())} + +
    + + +
    + ); +}; diff --git a/src/renderer/features/contacts/EditContactForm/ui/EditContactNavigation.tsx b/src/renderer/features/contacts/EditContactForm/ui/EditContactNavigation.tsx new file mode 100644 index 0000000000..4f7fdad179 --- /dev/null +++ b/src/renderer/features/contacts/EditContactForm/ui/EditContactNavigation.tsx @@ -0,0 +1,17 @@ +import { useNavigate } from 'react-router-dom'; + +import { createLink, Paths } from '@renderer/app/providers'; +import { IconButton } from '@renderer/shared/ui'; + +type Props = { + contactId: string; +}; +export const EditContactNavigation = ({ contactId }: Props) => { + const navigate = useNavigate(); + + const navigateToEdit = () => { + navigate(createLink(Paths.EDIT_CONTACT, {}, { id: [contactId] })); + }; + + return ; +}; diff --git a/src/renderer/features/contacts/EditRouteGuard/index.ts b/src/renderer/features/contacts/EditRouteGuard/index.ts new file mode 100644 index 0000000000..66e7109346 --- /dev/null +++ b/src/renderer/features/contacts/EditRouteGuard/index.ts @@ -0,0 +1,2 @@ +export { EditRouteGuard } from './ui/EditRouteGuard'; +export * as editGuardModel from './model/edit-guard'; diff --git a/src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts b/src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts new file mode 100644 index 0000000000..e336b3bd34 --- /dev/null +++ b/src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts @@ -0,0 +1,55 @@ +import { attach, createApi, createEffect, createEvent, createStore, sample } from 'effector'; +import { NavigateFunction } from 'react-router-dom'; + +import { Contact, contactModel } from '@renderer/entities/contact'; +import { ContactDS } from '@renderer/shared/api/storage'; + +type Navigation = { + redirectPath: string; + navigate: NavigateFunction; +}; +const $navigation = createStore(null); +const navigationApi = createApi($navigation, { + navigateApiChanged: (state, { navigate, redirectPath }) => ({ ...state, navigate, redirectPath }), +}); + +export const $contact = createStore(null); + +const validateUrlParams = createEvent(); + +type ValidateParams = { + contactId: string | null; + contacts: ContactDS[]; +}; +const getContactFx = createEffect(({ contactId, contacts }: ValidateParams) => { + if (!contactId) return undefined; + + return contacts.find((contact) => contact.id?.toString() === contactId); +}); + +sample({ + clock: validateUrlParams, + source: contactModel.$contacts, + fn: (contacts, urlParams) => ({ contactId: urlParams.get('id'), contacts }), + target: getContactFx, +}); + +sample({ + clock: getContactFx.doneData, + fn: (contact) => contact || null, + target: $contact, +}); + +sample({ + clock: getContactFx.doneData, + filter: (contact) => !contact, + target: attach({ + source: $navigation, + effect: (state) => state?.navigate(state?.redirectPath, { replace: true }), + }), +}); + +export const events = { + navigateApiChanged: navigationApi.navigateApiChanged, + validateUrlParams, +}; diff --git a/src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx b/src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx new file mode 100644 index 0000000000..08d0a75325 --- /dev/null +++ b/src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx @@ -0,0 +1,26 @@ +import { ReactNode, useEffect } from 'react'; +import { useNavigate, useSearchParams } from 'react-router-dom'; +import { useStore } from 'effector-react'; + +import * as editGuardModel from '../model/edit-guard'; +import { Contact } from '@renderer/entities/contact'; + +type Props = { + redirectPath: string; + children?: ReactNode | ((contact: Contact) => ReactNode); +}; +export const EditRouteGuard = ({ redirectPath, children }: Props) => { + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + + const contact = useStore(editGuardModel.$contact); + + useEffect(() => { + editGuardModel.events.navigateApiChanged({ navigate, redirectPath }); + editGuardModel.events.validateUrlParams(searchParams); + }, [searchParams]); + + if (!contact) return null; + + return <>{typeof children === 'function' ? children(contact) : children}; +}; diff --git a/src/renderer/features/contacts/index.ts b/src/renderer/features/contacts/index.ts new file mode 100644 index 0000000000..6e005f7d84 --- /dev/null +++ b/src/renderer/features/contacts/index.ts @@ -0,0 +1,4 @@ +export * from './EditContactForm'; +export * from './CreateContactForm'; +export * from './ContactFilter'; +export * from './EditRouteGuard'; diff --git a/src/renderer/pages/AddressBook/Contacts/Contacts.tsx b/src/renderer/pages/AddressBook/Contacts/Contacts.tsx new file mode 100644 index 0000000000..dd9bf7f17d --- /dev/null +++ b/src/renderer/pages/AddressBook/Contacts/Contacts.tsx @@ -0,0 +1,60 @@ +import { Outlet } from 'react-router-dom'; +import { useUnit } from 'effector-react'; + +import { Header } from '@renderer/components/common'; +import { useI18n } from '@renderer/app/providers'; +import { + ContactFilter, + CreateContactNavigation, + EditContactNavigation, + filterModel, +} from '@renderer/features/contacts'; +import { + ContactList, + contactModel, + ContactRow, + EmptyContactList, + EmptyFilteredContacts, +} from '@renderer/entities/contact'; + +export const Contacts = () => { + const { t } = useI18n(); + + const [contacts, contactsFiltered] = useUnit([contactModel.$contacts, filterModel.$contactsFiltered]); + + const hasContacts = contacts.length > 0; + const hasContactsFiltered = contactsFiltered.length > 0; + + return ( + <> +
    +
    +
    + + +
    +
    + +
    +
    + {!hasContacts && } + + {hasContacts && !hasContactsFiltered && } + + {hasContacts && hasContactsFiltered && ( + + {contacts.map((contact) => ( + + + + ))} + + )} +
    +
    +
    + + + + ); +}; diff --git a/src/renderer/pages/AddressBook/CreateContact/CreateContact.tsx b/src/renderer/pages/AddressBook/CreateContact/CreateContact.tsx new file mode 100644 index 0000000000..b9e43a6013 --- /dev/null +++ b/src/renderer/pages/AddressBook/CreateContact/CreateContact.tsx @@ -0,0 +1,10 @@ +import { useNavigate } from 'react-router-dom'; + +import { Paths } from '@renderer/app/providers'; +import { CreateContactModal } from '@renderer/widgets'; + +export const CreateContact = () => { + const navigate = useNavigate(); + + return navigate(Paths.ADDRESS_BOOK)} />; +}; diff --git a/src/renderer/pages/AddressBook/EditContact/EditContact.tsx b/src/renderer/pages/AddressBook/EditContact/EditContact.tsx new file mode 100644 index 0000000000..5444a52cb0 --- /dev/null +++ b/src/renderer/pages/AddressBook/EditContact/EditContact.tsx @@ -0,0 +1,15 @@ +import { useNavigate } from 'react-router-dom'; + +import { Paths } from '@renderer/app/providers'; +import { EditContactModal } from '@renderer/widgets'; +import { EditRouteGuard } from '@renderer/features/contacts'; + +export const EditContact = () => { + const navigate = useNavigate(); + + return ( + + {(contact) => navigate(Paths.ADDRESS_BOOK)} />} + + ); +}; diff --git a/src/renderer/pages/AddressBook/ManageContact/ManageContact.test.tsx b/src/renderer/pages/AddressBook/ManageContact/ManageContact.test.tsx deleted file mode 100644 index 8a0e6adedc..0000000000 --- a/src/renderer/pages/AddressBook/ManageContact/ManageContact.test.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { render, screen, act } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; - -import { ManageContact } from './ManageContact'; - -jest.mock('@renderer/app/providers', () => ({ - useI18n: jest.fn().mockReturnValue({ - t: (key: string) => key, - }), -})); - -jest.mock('react-router-dom', () => ({ - useNavigate: jest.fn(), - useSearchParams: jest.fn().mockReturnValue([new URLSearchParams('id=7')]), -})); - -jest.mock('@renderer/entities/contact', () => ({ - useContact: jest.fn().mockReturnValue({ - getContact: jest.fn().mockResolvedValue({ - name: 'Contact', - address: '123', - accountId: '0x123', - matrixId: '@bob:matrix.com', - }), - }), -})); - -jest.mock('@renderer/components/forms', () => ({ - ContactForm: () => contactForm, -})); - -describe('pages/AddressBook/ContactForm', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - test('should render component', async () => { - await act(async () => { - render(, { wrapper: MemoryRouter }); - }); - - const form = screen.getByText('contactForm'); - expect(form).toBeInTheDocument(); - }); - - test('should render loader', () => { - render(, { wrapper: MemoryRouter }); - - const loader = screen.getByText('loader.svg'); - expect(loader).toBeInTheDocument(); - }); -}); diff --git a/src/renderer/pages/AddressBook/ManageContact/ManageContact.tsx b/src/renderer/pages/AddressBook/ManageContact/ManageContact.tsx deleted file mode 100644 index 2465d30aa3..0000000000 --- a/src/renderer/pages/AddressBook/ManageContact/ManageContact.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useNavigate, useSearchParams } from 'react-router-dom'; - -import { useContact, Contact } from '@renderer/entities/contact'; -import { ContactForm } from '@renderer/components/forms'; -import { Paths, useI18n } from '@renderer/app/providers'; -import { DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; -import { useToggle } from '@renderer/shared/lib/hooks'; -import { BaseModal, Loader } from '@renderer/shared/ui'; - -export const ManageContact = () => { - const { t } = useI18n(); - const navigate = useNavigate(); - const { getContact } = useContact(); - const [searchParams] = useSearchParams(); - const contactId = searchParams.get('id'); - - const [isContactModalOpen, toggleContactModal] = useToggle(true); - - const [contact, setContact] = useState(); - const [isLoading, setIsLoading] = useState(); - - useEffect(() => { - if (!contactId) return; - - setIsLoading(true); - // FIXME: actual ID is number - // @ts-ignore - getContact(Number(contactId)) - .then(setContact) - .catch((error) => console.warn(error)) - .finally(() => setIsLoading(false)); - }, [contactId]); - - const closeContactModal = () => { - toggleContactModal(); - setTimeout(() => navigate(Paths.ADDRESS_BOOK), DEFAULT_TRANSITION); - }; - - const isEdit = contactId && contact; - - return ( - - {isLoading ? ( - - ) : ( - - )} - - ); -}; diff --git a/src/renderer/pages/AddressBook/Overview/Overview.test.tsx b/src/renderer/pages/AddressBook/Overview/Overview.test.tsx deleted file mode 100644 index 63bd8c2b7c..0000000000 --- a/src/renderer/pages/AddressBook/Overview/Overview.test.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; - -import { Overview } from './Overview'; - -jest.mock('@renderer/app/providers', () => ({ - useI18n: jest.fn().mockReturnValue({ - t: (key: string) => key, - }), -})); - -jest.mock('react-router-dom', () => ({ - useNavigate: jest.fn(), - Outlet: () => 'outlet', -})); - -jest.mock('@renderer/entities/contact', () => ({ - useContact: jest.fn().mockReturnValue({ - getLiveContacts: jest.fn().mockReturnValue([]), - }), -})); - -jest.mock('./components', () => ({ - EmptyContacts: () => emptyContacts, - ContactList: () => contactList, -})); - -describe('screen/AddressBook/Overview', () => { - test('should render component', () => { - render(, { wrapper: MemoryRouter }); - - const text = screen.getByText('addressBook.title'); - expect(text).toBeInTheDocument(); - }); -}); diff --git a/src/renderer/pages/AddressBook/Overview/Overview.tsx b/src/renderer/pages/AddressBook/Overview/Overview.tsx deleted file mode 100644 index 8942c13c16..0000000000 --- a/src/renderer/pages/AddressBook/Overview/Overview.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { useState } from 'react'; -import { Outlet, useNavigate } from 'react-router-dom'; - -import { useI18n, createLink, Paths } from '@renderer/app/providers'; -import { Header } from '@renderer/components/common'; -import { useContact } from '@renderer/entities/contact'; -import { Button, SearchInput } from '@renderer/shared/ui'; -import { ContactDS } from '@renderer/shared/api/storage'; -import { EmptyContacts, ContactList } from './components'; - -export const Overview = () => { - const { t } = useI18n(); - const { getLiveContacts } = useContact(); - const navigate = useNavigate(); - - const [query, setQuery] = useState(''); - - const contacts = getLiveContacts(); - const isEmpty = contacts.length === 0; - - const openManageContact = (contact?: ContactDS) => { - if (contact) { - navigate(createLink(Paths.MANAGE_CONTACT, {}, { id: [contact.id as string] })); - } else { - navigate(Paths.MANAGE_CONTACT); - } - }; - - return ( - <> -
    -
    -
    - - - -
    -
    - -
    -
    - {isEmpty ? ( - - ) : ( - - )} -
    -
    -
    - - - - ); -}; diff --git a/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.tsx b/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.tsx deleted file mode 100644 index e18a78ecf4..0000000000 --- a/src/renderer/pages/AddressBook/Overview/components/EmptyState/EmptyContacts.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Icon, BodyText, Button } from '@renderer/shared/ui'; -import { useI18n } from '@renderer/app/providers'; - -type Props = { - description?: string; - onAddContact?: () => void; -}; - -export const EmptyContacts = ({ description, onAddContact }: Props) => { - const { t } = useI18n(); - - return ( -
    - - {description || t('addressBook.contactList.noContactsLabel')} - - {onAddContact && ( - - )} -
    - ); -}; diff --git a/src/renderer/pages/AddressBook/Overview/components/index.ts b/src/renderer/pages/AddressBook/Overview/components/index.ts deleted file mode 100644 index a4592a762f..0000000000 --- a/src/renderer/pages/AddressBook/Overview/components/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { ContactList } from '@renderer/entities/contact/ui/ContactList/ContactList'; -export { EmptyContacts } from './EmptyState/EmptyContacts'; -export { EmptySearch } from './EmptyState/EmptySearch'; diff --git a/src/renderer/pages/AddressBook/index.ts b/src/renderer/pages/AddressBook/index.ts index 04f6068b5a..2f3ad45727 100644 --- a/src/renderer/pages/AddressBook/index.ts +++ b/src/renderer/pages/AddressBook/index.ts @@ -1,2 +1,3 @@ -export { Overview } from './Overview/Overview'; -export { ManageContact } from './ManageContact/ManageContact'; +export { Contacts } from './Contacts/Contacts'; +export { CreateContact } from './CreateContact/CreateContact'; +export { EditContact } from './EditContact/EditContact'; diff --git a/src/renderer/pages/Operations/common/utils.ts b/src/renderer/pages/Operations/common/utils.ts index 13400485b8..186137116f 100644 --- a/src/renderer/pages/Operations/common/utils.ts +++ b/src/renderer/pages/Operations/common/utils.ts @@ -11,9 +11,9 @@ import { TransactionType, } from '@renderer/entities/transaction/model/transaction'; import { toAddress, formatSectionAndMethod } from '@renderer/shared/lib/utils'; -import { Contact } from '@renderer/entities/contact/model/contact'; import { Account } from '@renderer/entities/account/model/account'; import { Signatory } from '@renderer/entities/signatory/model/signatory'; +import type { Contact } from '@renderer/entities/contact'; export const UNKNOWN_TYPE = 'UNKNOWN_TYPE'; export const TRANSACTION_UNKNOWN = 'operations.titles.unknown'; diff --git a/src/renderer/pages/Operations/components/modals/ApproveTx.tsx b/src/renderer/pages/Operations/components/modals/ApproveTx.tsx index 0d26cad0b9..ae34e0de5f 100644 --- a/src/renderer/pages/Operations/components/modals/ApproveTx.tsx +++ b/src/renderer/pages/Operations/components/modals/ApproveTx.tsx @@ -9,13 +9,6 @@ import { AccountDS, MultisigTransactionDS } from '@renderer/shared/api/storage'; import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; import { Account, MultisigAccount, useAccount } from '@renderer/entities/account'; import { ExtendedChain } from '@renderer/entities/network'; -import { - Transaction, - TransactionType, - useTransaction, - OperationResult, - useCallDataDecoder, -} from '@renderer/entities/transaction'; import { Address, HexString, SigningType, Timepoint } from '@renderer/domain/shared-kernel'; import { toAddress, transferableAmount, TEST_ADDRESS } from '@renderer/shared/lib/utils'; import { getTransactionTitle } from '../../common/utils'; @@ -27,6 +20,13 @@ import OperationModalTitle from '@renderer/pages/Operations/components/Operation import { Signing } from '@renderer/pages/Transfer/components/ActionSteps'; import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; import { useMultisigEvent } from '@renderer/entities/multisig'; +import { + Transaction, + TransactionType, + useTransaction, + OperationResult, + useCallDataDecoder, +} from '@renderer/entities/transaction'; type Props = { tx: MultisigTransactionDS; @@ -93,7 +93,7 @@ const ApproveTx = ({ tx, account, connection }: Props) => { const transaction = getTxFromCallData(connection.api, tx.callData); - getExtrinsicWeight(transaction, connection.api).then(setTxWeight); + getExtrinsicWeight(transaction).then(setTxWeight); }, [tx.transaction, connection.api]); const goBack = () => { diff --git a/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx index 91fbbb2511..362b81a959 100644 --- a/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx @@ -43,7 +43,6 @@ jest.mock('@renderer/entities/asset', () => ({ AssetBalance: () => balance, })); -jest.mock('../../../Overview/components', () => ({ UnstakingDuration: () => 'unstaking_duration' })); jest.mock('../../components', () => ({ OperationForm: ({ children }: any) => { return ( diff --git a/src/renderer/services/dataVerification/index.ts b/src/renderer/services/dataVerification/index.ts index f79315d382..6e702f2621 100644 --- a/src/renderer/services/dataVerification/index.ts +++ b/src/renderer/services/dataVerification/index.ts @@ -1 +1 @@ -export { verify } from './dataVerification'; +export { validate } from './dataVerification'; diff --git a/src/renderer/entities/matrix/lib/__tests__/credentialStorage.test.ts b/src/renderer/shared/api/matrix/__tests__/credentialStorage.test.ts similarity index 95% rename from src/renderer/entities/matrix/lib/__tests__/credentialStorage.test.ts rename to src/renderer/shared/api/matrix/__tests__/credentialStorage.test.ts index 57c0ff0447..a3cd696d34 100644 --- a/src/renderer/entities/matrix/lib/__tests__/credentialStorage.test.ts +++ b/src/renderer/shared/api/matrix/__tests__/credentialStorage.test.ts @@ -1,4 +1,4 @@ -import CredentialStorage from '../credentialStorage'; +import CredentialStorage from '../service/credentialStorage'; import { ICredentialStorage } from '../common/types'; describe('service/matrix/credentialStorage', () => { diff --git a/src/renderer/entities/matrix/lib/__tests__/secretStorage.test.ts b/src/renderer/shared/api/matrix/__tests__/secretStorage.test.ts similarity index 96% rename from src/renderer/entities/matrix/lib/__tests__/secretStorage.test.ts rename to src/renderer/shared/api/matrix/__tests__/secretStorage.test.ts index 0d5406edaa..f4feb8f14d 100644 --- a/src/renderer/entities/matrix/lib/__tests__/secretStorage.test.ts +++ b/src/renderer/shared/api/matrix/__tests__/secretStorage.test.ts @@ -1,4 +1,4 @@ -import SecretStorage from '../secretStorage'; +import SecretStorage from '../service/secretStorage'; import { ISecretStorage } from '../common/types'; describe('service/matrix/secretStorage', () => { diff --git a/src/renderer/entities/matrix/lib/common/constants.ts b/src/renderer/shared/api/matrix/common/constants.ts similarity index 100% rename from src/renderer/entities/matrix/lib/common/constants.ts rename to src/renderer/shared/api/matrix/common/constants.ts diff --git a/src/renderer/entities/matrix/lib/common/errors.ts b/src/renderer/shared/api/matrix/common/errors.ts similarity index 100% rename from src/renderer/entities/matrix/lib/common/errors.ts rename to src/renderer/shared/api/matrix/common/errors.ts diff --git a/src/renderer/entities/matrix/lib/common/types.ts b/src/renderer/shared/api/matrix/common/types.ts similarity index 97% rename from src/renderer/entities/matrix/lib/common/types.ts rename to src/renderer/shared/api/matrix/common/types.ts index 09b86d4ac4..49dcaec151 100644 --- a/src/renderer/entities/matrix/lib/common/types.ts +++ b/src/renderer/shared/api/matrix/common/types.ts @@ -36,11 +36,6 @@ export interface ISecureMessenger { setEventCallbacks: (callbacks: Callbacks) => void; syncSpektrTimeline: () => Promise; - // Validators - validateShortUserName: (value: string) => boolean; - validateFullUserName: (value: string) => boolean; - // checkUserExists: (userId: string) => Promise; - // Verification verifyWithKey: (securityKey: string) => Promise; verifyWithFile: (securityFile: File) => Promise; diff --git a/src/renderer/shared/api/matrix/common/utils.ts b/src/renderer/shared/api/matrix/common/utils.ts new file mode 100644 index 0000000000..3534bb6cde --- /dev/null +++ b/src/renderer/shared/api/matrix/common/utils.ts @@ -0,0 +1,21 @@ +import { MATRIX_FULL_USERNAME_REGEX, MATRIX_SHORT_USERNAME_REGEX } from './constants'; + +/** + * Validate full username with server prefix + * Example: @my_name:matrix.org + * @param value user name value + * @return {Boolean} + */ +export const validateFullUserName = (value: string): boolean => { + return MATRIX_FULL_USERNAME_REGEX.test(value); +}; + +/** + * Validate short username without server prefix + * Example: my_name not @my_name:matrix.org + * @param value user name value + * @return {Boolean} + */ +export const validateShortUserName = (value: string): boolean => { + return MATRIX_SHORT_USERNAME_REGEX.test(value); +}; diff --git a/src/renderer/entities/matrix/lib/index.ts b/src/renderer/shared/api/matrix/index.ts similarity index 50% rename from src/renderer/entities/matrix/lib/index.ts rename to src/renderer/shared/api/matrix/index.ts index 41624369d2..8cbaf9afe2 100644 --- a/src/renderer/entities/matrix/lib/index.ts +++ b/src/renderer/shared/api/matrix/index.ts @@ -1,3 +1,4 @@ -export * from './matrix'; +export * from './service/matrix'; export * from './common/types'; export * from './common/constants'; +export * from './common/utils'; diff --git a/src/renderer/entities/matrix/lib/credentialStorage.ts b/src/renderer/shared/api/matrix/service/credentialStorage.ts similarity index 96% rename from src/renderer/entities/matrix/lib/credentialStorage.ts rename to src/renderer/shared/api/matrix/service/credentialStorage.ts index 93ddc79367..eb5f34a359 100644 --- a/src/renderer/entities/matrix/lib/credentialStorage.ts +++ b/src/renderer/shared/api/matrix/service/credentialStorage.ts @@ -1,4 +1,4 @@ -import { ICredentialStorage, Credential } from './common/types'; +import { ICredentialStorage, Credential } from '../common/types'; class CredentialStorage implements ICredentialStorage { private credsKey = 'matrix_credentials'; diff --git a/src/renderer/entities/matrix/lib/matrix.ts b/src/renderer/shared/api/matrix/service/matrix.ts similarity index 95% rename from src/renderer/entities/matrix/lib/matrix.ts rename to src/renderer/shared/api/matrix/service/matrix.ts index 418b2cd03b..45ffca8336 100644 --- a/src/renderer/entities/matrix/lib/matrix.ts +++ b/src/renderer/shared/api/matrix/service/matrix.ts @@ -1,8 +1,11 @@ import Olm from '@matrix-org/olm'; import { + AuthType, + AutoDiscovery, ClientEvent, createClient, Direction, + EventTimeline, EventType, IndexedDBCryptoStore, IndexedDBStore, @@ -13,9 +16,6 @@ import { Room, RoomMemberEvent, Visibility, - AutoDiscovery, - AuthType, - EventTimeline, } from 'matrix-js-sdk'; import { ISecretStorageKeyInfo } from 'matrix-js-sdk/lib/crypto/api'; import { deriveKey } from 'matrix-js-sdk/lib/crypto/key_passphrase'; @@ -24,36 +24,29 @@ import { SyncState } from 'matrix-js-sdk/lib/sync'; import { logger } from 'matrix-js-sdk/lib/logger'; import noop from 'lodash/noop'; +import { BASE_MATRIX_URL, KEY_FILE_MAX_SIZE, ROOM_CRYPTO_CONFIG, WELL_KNOWN_SERVERS } from '../common/constants'; +import MATRIX_ERRORS from '../common/errors'; import { - BASE_MATRIX_URL, - KEY_FILE_MAX_SIZE, - ROOM_CRYPTO_CONFIG, - WELL_KNOWN_SERVERS, - MATRIX_SHORT_USERNAME_REGEX, - MATRIX_FULL_USERNAME_REGEX, -} from './common/constants'; -import MATRIX_ERRORS from './common/errors'; -import { + ApprovePayload, + BaseMultisigPayload, Callbacks, + CancelPayload, Credential, ErrorObject, - MatrixError, + FinalApprovePayload, ICredentialStorage, InvitePayload, ISecretStorage, ISecureMessenger, + LoginFlow, + MatrixError, Membership, MultisigPayload, - SpektrExtras, RoomParams, - LoginFlow, - UpdatePayload, - ApprovePayload, - FinalApprovePayload, - CancelPayload, - BaseMultisigPayload, + SpektrExtras, SpektrMultisigEvent, -} from './common/types'; + UpdatePayload, +} from '../common/types'; import CredentialStorage from './credentialStorage'; import SecretStorage from './secretStorage'; import { nonNullable } from '@renderer/shared/lib/utils'; @@ -438,28 +431,6 @@ export class Matrix implements ISecureMessenger { this.eventCallbacks = handlers; } - /** - * Check does User already exist - * @param userId matrix identifier - * @return {Promise} - */ - // async checkUserExists(userId: string): Promise { - // if (!this.matrixClient) { - // throw this.createError('Client is not active'); - // } - // - // const username = userId.match(MatrixUserNameRegex); - // if (!username) { - // throw new Error('User ID can only contain characters a-z, 0-9, or =_-./'); - // } - // - // try { - // return await this.matrixClient.isUsernameAvailable(username?.[1]); - // } catch (error) { - // throw this.createError((error as Error).message, error); - // } - // } - /** * Send MST_UPDATE state event to the room * Initialize multi-sig transaction @@ -595,26 +566,6 @@ export class Matrix implements ISecureMessenger { return type === SpektrMultisigEvent.CANCEL; } - /** - * Validate short username without server prefix - * Example: my_name not @my_name:matrix.org - * @param value user name value - * @return {Boolean} - */ - validateShortUserName(value: string): boolean { - return MATRIX_SHORT_USERNAME_REGEX.test(value); - } - - /** - * Validate full username with server prefix - * Example: @my_name:matrix.org - * @param value user name value - * @return {Boolean} - */ - validateFullUserName(value: string): boolean { - return MATRIX_FULL_USERNAME_REGEX.test(value); - } - // ===================================================== // ====================== Getters ====================== // ===================================================== diff --git a/src/renderer/entities/matrix/lib/secretStorage.ts b/src/renderer/shared/api/matrix/service/secretStorage.ts similarity index 98% rename from src/renderer/entities/matrix/lib/secretStorage.ts rename to src/renderer/shared/api/matrix/service/secretStorage.ts index 9796d255be..bd823398d9 100644 --- a/src/renderer/entities/matrix/lib/secretStorage.ts +++ b/src/renderer/shared/api/matrix/service/secretStorage.ts @@ -1,4 +1,4 @@ -import { ISecretStorage } from './common/types'; +import { ISecretStorage } from '../common/types'; class SecretStorage implements ISecretStorage { private secretStorage: Map; diff --git a/src/renderer/shared/api/storage/common/types.ts b/src/renderer/shared/api/storage/common/types.ts index 4811c32da3..4543b01ee5 100644 --- a/src/renderer/shared/api/storage/common/types.ts +++ b/src/renderer/shared/api/storage/common/types.ts @@ -2,16 +2,16 @@ import { Table } from 'dexie'; import { Balance, BalanceKey } from '@renderer/entities/asset/model/balance'; import { Connection, ConnectionType } from '@renderer/domain/connection'; -import { Contact } from '@renderer/entities/contact/model/contact'; -import { Address, ChainId, AccountId, CallHash } from '@renderer/domain/shared-kernel'; +import { AccountId, Address, CallHash, ChainId } from '@renderer/domain/shared-kernel'; import { Wallet } from '@renderer/entities/wallet/model/wallet'; +import { Account, MultisigAccount } from '@renderer/entities/account/model/account'; +import { Notification } from '@renderer/entities/notification/model/notification'; +import type { Contact } from '@renderer/entities/contact'; import { MultisigEvent, MultisigTransaction, MultisigTransactionKey, } from '@renderer/entities/transaction/model/transaction'; -import { Account, MultisigAccount } from '@renderer/entities/account/model/account'; -import { Notification } from '@renderer/entities/notification/model/notification'; // ===================================================== // ================ Storage interface ================== diff --git a/src/renderer/shared/api/storage/contactStorage.ts b/src/renderer/shared/api/storage/contactStorage.ts index 9586d91c61..e6972fe6ed 100644 --- a/src/renderer/shared/api/storage/contactStorage.ts +++ b/src/renderer/shared/api/storage/contactStorage.ts @@ -1,5 +1,5 @@ -import { Contact } from '@renderer/entities/contact/model/contact'; -import { ContactDS, IContactStorage, TContact, ID } from './common/types'; +import type { Contact } from '@renderer/entities/contact'; +import { ContactDS, IContactStorage, ID, TContact } from './common/types'; export const useContactStorage = (db: TContact): IContactStorage => ({ getContact: (contactId: ID): Promise => { diff --git a/src/renderer/shared/lib/utils/__tests__/address.test.ts b/src/renderer/shared/lib/utils/__tests__/address.test.ts index 493b3e7289..37788df9b7 100644 --- a/src/renderer/shared/lib/utils/__tests__/address.test.ts +++ b/src/renderer/shared/lib/utils/__tests__/address.test.ts @@ -1,7 +1,7 @@ import { TEST_ACCOUNT_ID, TEST_ADDRESS } from '@renderer/shared/lib/utils'; import { toAddress, validateAddress } from '../address'; -describe('shared/utils/address', () => { +describe('shared/lib/utils/address', () => { test('should convert address to Polkadot', () => { const address = toAddress(TEST_ACCOUNT_ID, { prefix: 0 }); expect(address).toEqual(TEST_ADDRESS); diff --git a/src/renderer/shared/lib/utils/__tests__/arrays.test.ts b/src/renderer/shared/lib/utils/__tests__/arrays.test.ts new file mode 100644 index 0000000000..f2b7fa0fd6 --- /dev/null +++ b/src/renderer/shared/lib/utils/__tests__/arrays.test.ts @@ -0,0 +1,26 @@ +import { splice } from '../arrays'; + +describe('shared/lib/utils/arrays', () => { + test('should insert element in the beginning', () => { + const array = splice([1, 2, 3], 100, 0); + expect(array).toEqual([100, 2, 3]); + }); + + test('should insert element at the end', () => { + const array = splice([1, 2, 3], 100, 2); + expect(array).toEqual([1, 2, 100]); + }); + + test('should insert element in the middle', () => { + const array = splice([1, 2, 3], 100, 1); + expect(array).toEqual([1, 100, 3]); + }); + + test('should insert element in empty array', () => { + const array1 = splice([], 100, 0); + const array2 = splice([], 100, 1); + + expect(array1).toEqual([100]); + expect(array2).toEqual([100]); + }); +}); diff --git a/src/renderer/shared/lib/utils/__tests__/balance.test.ts b/src/renderer/shared/lib/utils/__tests__/balance.test.ts index e8c1ca9b1b..3b6dd1fa54 100644 --- a/src/renderer/shared/lib/utils/__tests__/balance.test.ts +++ b/src/renderer/shared/lib/utils/__tests__/balance.test.ts @@ -1,6 +1,6 @@ import { formatBalance } from '../balance'; -describe('shared/utils/balance', () => { +describe('shared/lib/utils/balance', () => { describe('formatBalance', () => { test('should calculate small amount', () => { const { value, suffix, decimalPlaces } = formatBalance('5923210799282', 12); diff --git a/src/renderer/shared/lib/utils/__tests__/strings.test.ts b/src/renderer/shared/lib/utils/__tests__/strings.test.ts index 5407ad1fd4..a0258147fa 100644 --- a/src/renderer/shared/lib/utils/__tests__/strings.test.ts +++ b/src/renderer/shared/lib/utils/__tests__/strings.test.ts @@ -1,6 +1,6 @@ import { formatSectionAndMethod } from '../strings'; -describe('shared/utils/strings', () => { +describe('shared/lib/utils/strings', () => { describe('formatSectionAndMethod', () => { test('should make capital and add :', () => { expect(formatSectionAndMethod('system', 'remark')).toEqual('System: Remark'); diff --git a/src/renderer/shared/lib/utils/__tests__/substrate.test.ts b/src/renderer/shared/lib/utils/__tests__/substrate.test.ts index 10806ff321..3f55f806b0 100644 --- a/src/renderer/shared/lib/utils/__tests__/substrate.test.ts +++ b/src/renderer/shared/lib/utils/__tests__/substrate.test.ts @@ -4,7 +4,7 @@ import { BN, BN_TWO } from '@polkadot/util'; import { getExpectedBlockTime } from '../substrate'; import { DEFAULT_TIME, THRESHOLD } from '@renderer/entities/network/lib/common/constants'; -describe('shared/utils/substrate', () => { +describe('shared/lib/utils/substrate', () => { const blockTime = new BN(10_000); const getTime = (params: any): BN => { diff --git a/src/renderer/shared/lib/utils/arrays.ts b/src/renderer/shared/lib/utils/arrays.ts new file mode 100644 index 0000000000..2689d54110 --- /dev/null +++ b/src/renderer/shared/lib/utils/arrays.ts @@ -0,0 +1,10 @@ +/** + * Get new array with item inserted at given position + * @param collection array of items + * @param item value to be inserted + * @param position at which position + * @return {Array} + */ +export function splice(collection: T[], item: T, position: number): T[] { + return [...collection.slice(0, position), item, ...collection.slice(position + 1)]; +} diff --git a/src/renderer/shared/lib/utils/index.ts b/src/renderer/shared/lib/utils/index.ts index e4f87f8483..b56801ed76 100644 --- a/src/renderer/shared/lib/utils/index.ts +++ b/src/renderer/shared/lib/utils/index.ts @@ -10,3 +10,4 @@ export * from './substrate'; export * from './time'; export * from './twMerge'; export * from './validation'; +export * from './arrays'; diff --git a/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.tsx b/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.tsx index b4d5289448..1704dffe4f 100644 --- a/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.tsx +++ b/src/renderer/shared/ui/Dropdowns/Combobox/Combobox.tsx @@ -3,6 +3,8 @@ import { Transition, Combobox as HeadlessCombobox } from '@headlessui/react'; import { cnTw, includes } from '@renderer/shared/lib/utils'; import { Props as InputProps } from '@renderer/shared/ui/Inputs/Input/Input'; +import { Position, ComboboxOption, Theme } from '../common/types'; +import { FootnoteText, Input } from '@renderer/shared/ui'; import { OptionsContainerStyle, OptionsContainerStyleTheme, @@ -10,8 +12,6 @@ import { OptionStyleTheme, ViewClass, } from '../common/constants'; -import { Position, ComboboxOption, Theme } from '../common/types'; -import { FootnoteText, Input } from '@renderer/shared/ui'; type Props = Omit & { options: ComboboxOption[]; diff --git a/src/renderer/widgets/ManageContactModal/index.ts b/src/renderer/widgets/ManageContactModal/index.ts new file mode 100644 index 0000000000..af680c2760 --- /dev/null +++ b/src/renderer/widgets/ManageContactModal/index.ts @@ -0,0 +1,2 @@ +export { EditContactModal } from './ui/EditContactModal/EditContactModal'; +export { CreateContactModal } from './ui/CreateContactModal/CreateContactModal'; diff --git a/src/renderer/widgets/ManageContactModal/ui/CreateContactModal/CreateContactModal.tsx b/src/renderer/widgets/ManageContactModal/ui/CreateContactModal/CreateContactModal.tsx new file mode 100644 index 0000000000..1ac9a6c992 --- /dev/null +++ b/src/renderer/widgets/ManageContactModal/ui/CreateContactModal/CreateContactModal.tsx @@ -0,0 +1,45 @@ +import { useEffect } from 'react'; + +import { useI18n } from '@renderer/app/providers'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { BaseModal } from '@renderer/shared/ui'; +import { CreateContactForm } from '@renderer/features/contacts'; + +type Props = { + isOpen?: boolean; + onClose: () => void; +}; +export const CreateContactModal = ({ isOpen = true, onClose }: Props) => { + const { t } = useI18n(); + + const [isModalOpen, toggleIsModalOpen] = useToggle(isOpen); + + useEffect(() => { + if (isOpen && !isModalOpen) { + toggleIsModalOpen(); + } + + if (!isOpen && isModalOpen) { + closeContactModal(); + } + }, [isOpen]); + + const closeContactModal = () => { + toggleIsModalOpen(); + setTimeout(onClose, DEFAULT_TRANSITION); + }; + + return ( + + + + ); +}; diff --git a/src/renderer/widgets/ManageContactModal/ui/EditContactModal/EditContactModal.tsx b/src/renderer/widgets/ManageContactModal/ui/EditContactModal/EditContactModal.tsx new file mode 100644 index 0000000000..79e93fa78b --- /dev/null +++ b/src/renderer/widgets/ManageContactModal/ui/EditContactModal/EditContactModal.tsx @@ -0,0 +1,47 @@ +import { useEffect } from 'react'; + +import { useI18n } from '@renderer/app/providers'; +import { useToggle } from '@renderer/shared/lib/hooks'; +import { DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { BaseModal } from '@renderer/shared/ui'; +import { EditContactForm } from '@renderer/features/contacts'; +import { Contact } from '@renderer/entities/contact'; + +type Props = { + contact: Contact; + isOpen?: boolean; + onClose: () => void; +}; +export const EditContactModal = ({ contact, isOpen = true, onClose }: Props) => { + const { t } = useI18n(); + + const [isModalOpen, toggleIsModalOpen] = useToggle(isOpen); + + useEffect(() => { + if (isOpen && !isModalOpen) { + toggleIsModalOpen(); + } + + if (!isOpen && isModalOpen) { + closeContactModal(); + } + }, [isOpen]); + + const closeContactModal = () => { + toggleIsModalOpen(); + setTimeout(onClose, DEFAULT_TRANSITION); + }; + + return ( + + + + ); +}; diff --git a/src/renderer/widgets/index.ts b/src/renderer/widgets/index.ts new file mode 100644 index 0000000000..8f83dec67d --- /dev/null +++ b/src/renderer/widgets/index.ts @@ -0,0 +1 @@ +export * from './ManageContactModal'; diff --git a/src/shared/locale/en.json b/src/shared/locale/en.json index bbebe3d9e3..e53a5a319c 100644 --- a/src/shared/locale/en.json +++ b/src/shared/locale/en.json @@ -4,7 +4,15 @@ "networksColumn": "Networks { chains }" }, "addressBook": { - "addContact": { + "contactList": { + "emptySearchDescription": "Try to search for another name", + "emptySearchLabel": "No contact with entered name were found", + "matrixIdColumnTitle": "Matrix ID", + "nameColumnTitle": "Name", + "noContactsButton": "Add your first contact", + "noContactsLabel": "The contact list is empty, you can add a new contact" + }, + "createContact": { "accountIdExistsError": "Account address already in use", "accountIdIncorrectError": "Incorrect account address", "accountIdLabel": "Account address", @@ -24,19 +32,26 @@ "typeAddressButton": "Type the address", "typeNameButton": "Type a name" }, - "addContactButton": "Add contact", - "contactList": { - "emptySearchDescription": "Try to search for another name", - "emptySearchLabel": "No contact with entered name were found", - "matrixIdColumnTitle": "Matrix ID", - "nameColumnTitle": "Name", - "noContactsButton": "Add your first contact", - "noContactsLabel": "The contact list is empty, you can add a new contact" - }, "editContact": { + "accountIdExistsError": "Account address already in use", + "accountIdIncorrectError": "Incorrect account address", + "accountIdLabel": "Account address", + "accountIdPlaceholder": "Enter or paste an address", + "accountIdRequiredError": "Account address is required", "editWarning": "If you have a multisig wallet with this contact, then changing the contact will not affect the multisig wallet", + "matrixIdError": "Incorrect matrix id", + "matrixIdHint": "Matrix ID allows to exchange multisig information with signatory", + "matrixIdLabel": "Matrix ID", + "matrixIdPlaceholder": "Enter @username:matrix.org", + "nameExistsError": "Name already in use", + "nameLabel": "Name", + "namePlaceholder": "Enter text", + "nameRequiredError": "Name is required", "saveContactButton": "Save", - "title": "Edit contact" + "title": "Edit contact", + "typeAddressAndNameButton": "Type a name and address", + "typeAddressButton": "Type the address", + "typeNameButton": "Type a name" }, "searchPlaceholder": "Search", "title": "Address Book" diff --git a/src/shared/locale/ru.json b/src/shared/locale/ru.json index a4252f1ead..2c3b070d32 100644 --- a/src/shared/locale/ru.json +++ b/src/shared/locale/ru.json @@ -4,7 +4,15 @@ "networksColumn": "Networks { chains }" }, "addressBook": { - "addContact": { + "contactList": { + "emptySearchDescription": "Try to search for another name", + "emptySearchLabel": "No contact with entered name were found", + "matrixIdColumnTitle": "Matrix ID", + "nameColumnTitle": "Name", + "noContactsButton": "Add your first contact", + "noContactsLabel": "Your address book is empty" + }, + "createContact": { "accountIdExistsError": "Account address already in use", "accountIdIncorrectError": "Incrorrect account address", "accountIdLabel": "адрес контакта", @@ -24,19 +32,26 @@ "typeAddressButton": "Type the address", "typeNameButton": "Type a name" }, - "addContactButton": "Добавить контакт", - "contactList": { - "emptySearchDescription": "Try to search for another name", - "emptySearchLabel": "No contact with entered name were found", - "matrixIdColumnTitle": "Matrix ID", - "nameColumnTitle": "Name", - "noContactsButton": "Add your first contact", - "noContactsLabel": "Your address book is empty" - }, "editContact": { + "accountIdExistsError": "Account address already in use", + "accountIdIncorrectError": "Incrorrect account address", + "accountIdLabel": "Адрес контакта", + "accountIdPlaceholder": "Enter or paste an address", + "accountIdRequiredError": "Account address is required", "editWarning": "If you have a multisig wallet with this contact, then changing the contact will not affect the multisig wallet", - "saveContactButton": "Save", - "title": "Edit contact" + "matrixIdError": "Incorrect matrix id", + "matrixIdHint": "В формате: @user_name:matrix.org", + "matrixIdLabel": "matrix id (optional)", + "matrixIdPlaceholder": "Enter or paste Matrix ID, ie: @bob:matrix.org", + "nameExistsError": "Name already in use", + "nameLabel": "Name", + "namePlaceholder": "Enter a name", + "nameRequiredError": "Name is required", + "saveContactButton": "Сохранить", + "title": "Редактировать контакт", + "typeAddressAndNameButton": "Type a name and address", + "typeAddressButton": "Type the address", + "typeNameButton": "Type a name" }, "searchPlaceholder": "Введите название или адрес...", "title": "Адресная книга" diff --git a/tests/integrations/dataVerification/dataVerification.base.test.ts b/tests/integrations/dataVerification/dataVerification.base.test.ts index aacc6799d7..0ed4ffa7f1 100644 --- a/tests/integrations/dataVerification/dataVerification.base.test.ts +++ b/tests/integrations/dataVerification/dataVerification.base.test.ts @@ -3,7 +3,7 @@ import { AccountInfo } from '@polkadot/types/interfaces'; import { Codec } from '@polkadot/types/types'; import chains from '../../../src/renderer/assets/chains/chains.json'; -import { validate } from '@renderer/services/dataVerification/dataVerification'; +import { validate } from '../../../src/renderer/services/dataVerification'; import { getTestAccounts, TestAccountsURL, diff --git a/tests/integrations/matrix/matrix.base.test.ts b/tests/integrations/matrix/matrix.base.test.ts index 5bfb53eed4..8d4972bfa5 100644 --- a/tests/integrations/matrix/matrix.base.test.ts +++ b/tests/integrations/matrix/matrix.base.test.ts @@ -1,5 +1,5 @@ -import { BASE_MATRIX_URL } from '../../../src/renderer/entities/matrix/lib/common/constants'; -import Matrix, { Membership, Signatory } from '../../../src/renderer/entities/matrix/lib'; +import { BASE_MATRIX_URL, Matrix, Membership } from '../../../src/renderer/shared/api/matrix'; +import { Signatory } from '../../../src/renderer/entities/signatory'; import { createRoom } from '../utils/matrixCreateRoom'; import { matrixLoginAndSync } from '../utils/matrixLogin'; import test_data from './matrix_data.json'; diff --git a/tests/integrations/utils/matrixCreateRoom.ts b/tests/integrations/utils/matrixCreateRoom.ts index 9e32d23358..21f89a6ac9 100644 --- a/tests/integrations/utils/matrixCreateRoom.ts +++ b/tests/integrations/utils/matrixCreateRoom.ts @@ -1,4 +1,4 @@ -import Matrix, { RoomParams, Signatory } from '../../../src/renderer/entities/matrix/lib'; +import Matrix, { RoomParams, Signatory } from '../../../src/renderer/shared/api/matrix'; export async function createRoom( matrix: Matrix, diff --git a/tests/integrations/utils/matrixLogin.ts b/tests/integrations/utils/matrixLogin.ts index 750049aa8b..3de8ae542a 100644 --- a/tests/integrations/utils/matrixLogin.ts +++ b/tests/integrations/utils/matrixLogin.ts @@ -1,4 +1,4 @@ -import Matrix from '../../../src/renderer/entities/matrix/lib'; +import Matrix from '../../../src/renderer/shared/api/matrix'; export async function matrixLoginAndSync(matrix: Matrix, login: string, password: string): Promise { await matrix.loginWithCreds(login, password); From 5183785bb71a1fd015a5bc6363def45446967d4e Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Wed, 16 Aug 2023 14:42:07 +0300 Subject: [PATCH 04/34] Fix: Effector filtered contacts (#1017) --- .../ContactFilter/model/contact-filter.ts | 17 +++++++++++++---- .../contacts/ContactFilter/ui/ContactFilter.tsx | 8 +++++++- .../pages/AddressBook/Contacts/Contacts.tsx | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/renderer/features/contacts/ContactFilter/model/contact-filter.ts b/src/renderer/features/contacts/ContactFilter/model/contact-filter.ts index 55fc1da5ee..e65c1a6bf5 100644 --- a/src/renderer/features/contacts/ContactFilter/model/contact-filter.ts +++ b/src/renderer/features/contacts/ContactFilter/model/contact-filter.ts @@ -1,12 +1,20 @@ -import { combine, createEvent, createStore } from 'effector'; +import { combine, createEvent, createStore, forward } from 'effector'; import { includes } from '@renderer/shared/lib/utils'; import { contactModel } from '@renderer/entities/contact'; +const componentMounted = createEvent(); + export const $filterQuery = createStore(''); -const setQuery = createEvent(); +const queryChanged = createEvent(); +const queryReset = createEvent(); + +$filterQuery.on(queryChanged, (_, query) => query).reset(queryReset); -$filterQuery.on(setQuery, (_, query) => query); +forward({ + from: componentMounted, + to: queryReset, +}); export const $contactsFiltered = combine(contactModel.$contacts, $filterQuery, (contacts, query) => { return contacts @@ -21,5 +29,6 @@ export const $contactsFiltered = combine(contactModel.$contacts, $filterQuery, ( }); export const events = { - setQuery, + componentMounted, + queryChanged, }; diff --git a/src/renderer/features/contacts/ContactFilter/ui/ContactFilter.tsx b/src/renderer/features/contacts/ContactFilter/ui/ContactFilter.tsx index 2f207d3284..fa9a0f5ce6 100644 --- a/src/renderer/features/contacts/ContactFilter/ui/ContactFilter.tsx +++ b/src/renderer/features/contacts/ContactFilter/ui/ContactFilter.tsx @@ -1,3 +1,5 @@ +import { useEffect } from 'react'; + import { SearchInput } from '@renderer/shared/ui'; import { useI18n } from '@renderer/app/providers'; import * as filterModel from '../model/contact-filter'; @@ -5,11 +7,15 @@ import * as filterModel from '../model/contact-filter'; export const ContactFilter = () => { const { t } = useI18n(); + useEffect(() => { + filterModel.events.componentMounted(); + }, []); + return ( ); }; diff --git a/src/renderer/pages/AddressBook/Contacts/Contacts.tsx b/src/renderer/pages/AddressBook/Contacts/Contacts.tsx index dd9bf7f17d..f123f713a5 100644 --- a/src/renderer/pages/AddressBook/Contacts/Contacts.tsx +++ b/src/renderer/pages/AddressBook/Contacts/Contacts.tsx @@ -43,7 +43,7 @@ export const Contacts = () => { {hasContacts && hasContactsFiltered && ( - {contacts.map((contact) => ( + {contactsFiltered.map((contact) => ( From 33547e9562bd085fb183b60c490dab69152b0ac9 Mon Sep 17 00:00:00 2001 From: leohar Date: Fri, 18 Aug 2023 15:56:05 +0500 Subject: [PATCH 05/34] update README.md, NOTICE.md (#1012) * update README.md, NOTICE.md * Update README.md Co-authored-by: Stepan Lavrentev <40560660+stepanLav@users.noreply.github.com> --------- Co-authored-by: Stepan Lavrentev <40560660+stepanLav@users.noreply.github.com> --- NOTICE.md | 4 +++- README.md | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/NOTICE.md b/NOTICE.md index 03eeb7cd99..11685e4a3b 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1,3 +1,5 @@ Nova Spektr - Polkadot, Kusama enterprise application -Copyright 2023 Novasama Technologies PTE. LTD. +Copyright 2022-2023 Novasama Technologies PTE. LTD. +This product includes software developed at Novasama Technologies PTE. LTD. +License Rights transferred from Novasama Technologies PTE. LTD to Novasama Technologies GmbH starting from 1st of April 2023 diff --git a/README.md b/README.md index 80e1e65168..7210e298ea 100644 --- a/README.md +++ b/README.md @@ -172,3 +172,7 @@ All issues are being tracked in the [Nova Spektr Support project](https://github # Feedback Your feedback is welcome. Use GitHub issues for submitting the feedback. All feedback is being tracked in the [Nova Spektr Feedback project](https://github.com/orgs/novasamatech/projects/5) + +## License +Nova Spektr - Polkadot, Kusama enterprise application is available under the Apache 2.0 license. See the LICENSE file for more info. +© Novasama Technologies GmbH 2023 From f44793d16245edbe06b6732963c8433b19284c14 Mon Sep 17 00:00:00 2001 From: Aleksandr Makhnev Date: Wed, 23 Aug 2023 14:11:23 +0500 Subject: [PATCH 06/34] feat: coingecko price adapter (#1027) --- docs/fiat-price.md | 55 +++++++++++++++++++ .../multisig/lib/multisigTx/common/utils.ts | 6 +- .../price/coingecko/CoingeckoAdapter.test.ts | 53 ++++++++++++++++++ .../api/price/coingecko/CoingeckoAdapter.ts | 51 +++++++++++++++++ .../shared/api/price/coingecko/consts.ts | 1 + src/renderer/shared/api/price/common/types.ts | 21 +++++++ .../shared/api/price/common/utils.test.ts | 48 ++++++++++++++++ src/renderer/shared/api/price/common/utils.ts | 37 +++++++++++++ src/renderer/shared/api/price/index.ts | 3 + 9 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 docs/fiat-price.md create mode 100644 src/renderer/shared/api/price/coingecko/CoingeckoAdapter.test.ts create mode 100644 src/renderer/shared/api/price/coingecko/CoingeckoAdapter.ts create mode 100644 src/renderer/shared/api/price/coingecko/consts.ts create mode 100644 src/renderer/shared/api/price/common/types.ts create mode 100644 src/renderer/shared/api/price/common/utils.test.ts create mode 100644 src/renderer/shared/api/price/common/utils.ts create mode 100644 src/renderer/shared/api/price/index.ts diff --git a/docs/fiat-price.md b/docs/fiat-price.md new file mode 100644 index 0000000000..b6008a6097 --- /dev/null +++ b/docs/fiat-price.md @@ -0,0 +1,55 @@ +# Fiat price + +Now fiat price feature consists of `PriceAdapter` type. Each adapter should implement two functions 1. to return price for selected crypto assets with selected currencies (fiat or crypto) and 2. return history data for selected asset, currency and time range. + +```typescript + getPrice: (ids: AssetId[], currencies: Currency[], includeRateChange: boolean) => Promise; + getHistoryData: (id: AssetId, currency: Currency, from: number, to: number) => Promise; +``` + +And we have two util functions, which can convert `PriceObject` format to `PriceDB` format. + +`PriceObject` is based on coingecko format. +`PriceDB` is based on IndexedDB storage object. + +```typescript +type PriceObject = Record; +type AssetPrice = Record; +type PriceItem = { + price: number; + change: number; +}; +``` + +example + +```typescript +const priceObject = { + polkadot: { + usd: { + price: 4.1 + change: -0.1 + } + } +} +``` + +```typescript +type PriceDB = { + assetId: AssetId; + currency: Currency; + price: number; + change: number; +}; +``` + +example + +```typescript +const priceArray = [{ + assetId: 'polkadot', + currency: 'usd', + price: 4.1, + change: -0.1 +}] +``` diff --git a/src/renderer/entities/multisig/lib/multisigTx/common/utils.ts b/src/renderer/entities/multisig/lib/multisigTx/common/utils.ts index 19e3fa28cf..91c5dc41f2 100644 --- a/src/renderer/entities/multisig/lib/multisigTx/common/utils.ts +++ b/src/renderer/entities/multisig/lib/multisigTx/common/utils.ts @@ -20,13 +20,13 @@ export const getPendingMultisigTxs = async ( return multisigs .filter(([, opt]) => opt.isSome) - .reduce((result, [storage, opt]) => { - if (opt.isNone) return result; + .reduce((acc, [storage, opt]) => { + if (opt.isNone) return acc; const params = opt.unwrap(); const [, callHash] = storage.args; - return [...result, { callHash, params }]; + return [...acc, { callHash, params }]; }, []); }; diff --git a/src/renderer/shared/api/price/coingecko/CoingeckoAdapter.test.ts b/src/renderer/shared/api/price/coingecko/CoingeckoAdapter.test.ts new file mode 100644 index 0000000000..a59f77c25b --- /dev/null +++ b/src/renderer/shared/api/price/coingecko/CoingeckoAdapter.test.ts @@ -0,0 +1,53 @@ +import { useCoinGeckoAdapter } from './CoingeckoAdapter'; + +describe('api/price/coingecko/CoinGeckoAdapter', () => { + test('get price from coingecko', async () => { + global.fetch = jest.fn(() => + Promise.resolve({ + json: () => + Promise.resolve({ + kusama: { + usd: 19.24, + usd_24h_change: -4.745815232356294, + rub: 1813.88, + rub_24h_change: -4.594057057555666, + }, + polkadot: { + usd: 4.42, + usd_24h_change: -1.4238849671619895, + rub: 416.81, + rub_24h_change: -1.266834320696305, + }, + }), + }), + ) as jest.Mock; + + const { getPrice } = useCoinGeckoAdapter(); + + const result = await getPrice(['kusama', 'polkadot'], ['usd', 'rub'], true); + + expect(result['kusama']['usd'].price).toBeDefined(); + expect(result['polkadot']['rub'].change).toBeDefined(); + }); + + test('get price from coingecko', async () => { + global.fetch = jest.fn(() => + Promise.resolve({ + json: () => + Promise.resolve({ + prices: [ + [1692700257389, 19.114019441153435], + [1692700503991, 19.10566535057447], + [1692700832303, 19.10643539870203], + ], + }), + }), + ) as jest.Mock; + + const { getHistoryData } = useCoinGeckoAdapter(); + + const result = await getHistoryData('kusama', 'usd', 1692700000, 1692701000); + + expect(result.length).toBe(3); + }); +}); diff --git a/src/renderer/shared/api/price/coingecko/CoingeckoAdapter.ts b/src/renderer/shared/api/price/coingecko/CoingeckoAdapter.ts new file mode 100644 index 0000000000..5571e6b173 --- /dev/null +++ b/src/renderer/shared/api/price/coingecko/CoingeckoAdapter.ts @@ -0,0 +1,51 @@ +import { AssetId, Currency, PriceObject, PriceAdapter, PriceItem, PriceRange } from '../common/types'; +import { getCurrencyChangeKey } from '../common/utils'; +import { COINGECKO_URL } from './consts'; + +export const useCoinGeckoAdapter = (): PriceAdapter => { + const getPrice = async (ids: AssetId[], currencies: Currency[], includeRateChange: boolean): Promise => { + const url = new URL(`${COINGECKO_URL}/simple/price`); + url.search = new URLSearchParams({ + ids: ids.join(','), + vs_currencies: currencies.join(','), + include_24hr_change: includeRateChange.toString(), + }).toString(); + + const response = await fetch(url); + + const data = await response.json(); + + return ids.reduce((acc, assetId) => { + acc[assetId] = currencies.reduce>((accPrice, currency) => { + accPrice[currency] = { + price: data[assetId][currency], + change: data[assetId][getCurrencyChangeKey(currency)], + }; + + return accPrice; + }, {}); + + return acc; + }, {}); + }; + + const getHistoryData = async (id: string, currency: string, from: number, to: number): Promise => { + const url = new URL(`${COINGECKO_URL}/coins/${id}/market_chart/range`); + url.search = new URLSearchParams({ + vs_currency: currency, + from: from.toString(), + to: to.toString(), + }).toString(); + + const response = await fetch(url); + + const data = await response.json(); + + return data.prices; + }; + + return { + getPrice, + getHistoryData, + }; +}; diff --git a/src/renderer/shared/api/price/coingecko/consts.ts b/src/renderer/shared/api/price/coingecko/consts.ts new file mode 100644 index 0000000000..a6fb2a747d --- /dev/null +++ b/src/renderer/shared/api/price/coingecko/consts.ts @@ -0,0 +1 @@ +export const COINGECKO_URL = 'https://api.coingecko.com/api/v3'; diff --git a/src/renderer/shared/api/price/common/types.ts b/src/renderer/shared/api/price/common/types.ts new file mode 100644 index 0000000000..1c25c6c485 --- /dev/null +++ b/src/renderer/shared/api/price/common/types.ts @@ -0,0 +1,21 @@ +export type Currency = string; +export type AssetId = string; + +export type PriceItem = { + price: number; + change: number; +}; +export type AssetPrice = Record; +export type PriceObject = Record; +export type PriceRange = [number, string]; +export type PriceDB = { + assetId: AssetId; + currency: Currency; + price: number; + change: number; +}; + +export type PriceAdapter = { + getPrice: (ids: AssetId[], currencies: Currency[], includeRateChange: boolean) => Promise; + getHistoryData: (id: AssetId, currency: Currency, from: number, to: number) => Promise; +}; diff --git a/src/renderer/shared/api/price/common/utils.test.ts b/src/renderer/shared/api/price/common/utils.test.ts new file mode 100644 index 0000000000..0bd97faea1 --- /dev/null +++ b/src/renderer/shared/api/price/common/utils.test.ts @@ -0,0 +1,48 @@ +import { convertPriceToObjectView, convertPriceToDBView, getCurrencyChangeKey } from './utils'; + +describe('api/price/common', () => { + test('get correct change key', () => { + const result = getCurrencyChangeKey('polkadot'); + + expect(result).toEqual('polkadot_24h_change'); + }); + + test('convert price from object to array', () => { + const result = convertPriceToDBView({ + kusama: { + usd: { + price: 19.06, + change: -5.22061353514796, + }, + rub: { + price: 1795.2, + change: -5.284952983744856, + }, + }, + polkadot: { + usd: { + price: 4.39, + change: -1.8926089775953392, + }, + rub: { + price: 413.87, + change: -1.9592075880855542, + }, + }, + }); + + expect(result.length).toEqual(4); + }); + + test('convert price from array to object', () => { + const result = convertPriceToObjectView([ + { assetId: 'kusama', currency: 'usd', price: 19.06, change: -5.22061353514796 }, + { assetId: 'kusama', currency: 'rub', price: 1795.2, change: -5.284952983744856 }, + { assetId: 'polkadot', currency: 'usd', price: 4.39, change: -1.8926089775953392 }, + { assetId: 'polkadot', currency: 'rub', price: 413.87, change: -1.9592075880855542 }, + ]); + + expect(result['kusama']['usd'].price).toEqual(19.06); + expect(result['polkadot']['rub'].change).toEqual(-1.9592075880855542); + }); +}); diff --git a/src/renderer/shared/api/price/common/utils.ts b/src/renderer/shared/api/price/common/utils.ts new file mode 100644 index 0000000000..ab454bdbd0 --- /dev/null +++ b/src/renderer/shared/api/price/common/utils.ts @@ -0,0 +1,37 @@ +import { PriceObject, PriceDB } from './types'; + +export const getCurrencyChangeKey = (currency: string): string => { + return `${currency}_24h_change`; +}; + +export const convertPriceToDBView = (price: PriceObject): PriceDB[] => { + const priceDB: PriceDB[] = []; + + Object.entries(price).forEach(([assetId, assetPrice]) => { + Object.entries(assetPrice).forEach(([currency, { price, change }]) => { + priceDB.push({ + assetId, + currency, + price, + change, + }); + }); + }); + + return priceDB; +}; + +export const convertPriceToObjectView = (prices: PriceDB[]): PriceObject => { + return prices.reduce((result, { assetId, currency, price, change }) => { + if (!result[assetId]) { + result[assetId] = {}; + } + + result[assetId][currency] = { + price, + change, + }; + + return result; + }, {}); +}; diff --git a/src/renderer/shared/api/price/index.ts b/src/renderer/shared/api/price/index.ts new file mode 100644 index 0000000000..b2dd9830df --- /dev/null +++ b/src/renderer/shared/api/price/index.ts @@ -0,0 +1,3 @@ +export * from './coingecko/CoingeckoAdapter'; +export * from './common/types'; +export * from './common/utils'; From 526322083c99c91ffcaf6e66555fc79082e5a87f Mon Sep 17 00:00:00 2001 From: Aleksandr Makhnev Date: Fri, 25 Aug 2023 17:03:54 +0500 Subject: [PATCH 07/34] feat: update metadata form chain (#1038) --- .../MultisigChainContext.tsx | 14 ++++++-- .../multisig/lib/multisigTx/common/consts.ts | 2 ++ .../multisig/lib/multisigTx/common/types.ts | 6 ++++ .../lib/multisigTx/multisigTxService.ts | 36 ++++++++++++++++--- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/renderer/app/providers/context/MultisigChainContext/MultisigChainContext.tsx b/src/renderer/app/providers/context/MultisigChainContext/MultisigChainContext.tsx index d2eaeed08d..d45a3772dc 100644 --- a/src/renderer/app/providers/context/MultisigChainContext/MultisigChainContext.tsx +++ b/src/renderer/app/providers/context/MultisigChainContext/MultisigChainContext.tsx @@ -25,8 +25,14 @@ const MultisigChainContext = createContext({} as Mult export const MultisigChainProvider = ({ children }: PropsWithChildren) => { const { connections } = useNetworkContext(); const { addTask } = useTaskQueue(); - const { subscribeMultisigAccount, updateMultisigTx, getMultisigTx, getLiveAccountMultisigTxs, updateCallData } = - useMultisigTx({ addTask }); + const { + subscribeMultisigAccount, + updateMultisigTx, + getMultisigTx, + getLiveAccountMultisigTxs, + updateCallData, + updateCallDataFromChain, + } = useMultisigTx({ addTask }); const { getActiveMultisigAccount } = useAccount(); const { updateEvent, getEvents, addEventWithQueue } = useMultisigEvent({ addTask }); @@ -47,6 +53,10 @@ export const MultisigChainProvider = ({ children }: PropsWithChildren) => { updateCallData(connection.api, tx, tx.callData); } + if (tx.blockCreated && tx.indexCreated && !tx.callData && !tx.transaction) { + updateCallDataFromChain(connection.api, tx, tx.blockCreated, tx.indexCreated); + } + if (!tx.dateCreated && tx.blockCreated) { const dateCreated = await getCreatedDateFromApi(tx.blockCreated, connection.api); updateMultisigTx({ ...tx, dateCreated }); diff --git a/src/renderer/entities/multisig/lib/multisigTx/common/consts.ts b/src/renderer/entities/multisig/lib/multisigTx/common/consts.ts index 43c0b519ff..23c1f810dd 100644 --- a/src/renderer/entities/multisig/lib/multisigTx/common/consts.ts +++ b/src/renderer/entities/multisig/lib/multisigTx/common/consts.ts @@ -1 +1,3 @@ export const QUERY_INTERVAL = 5000; +export const MULTISIG_EXTRINSIC_CALL_INDEX = 3; +export const DEFAULT_BLOCK_HASH = '0x0000000000000000000000000000000000000000000000000000000000000000'; diff --git a/src/renderer/entities/multisig/lib/multisigTx/common/types.ts b/src/renderer/entities/multisig/lib/multisigTx/common/types.ts index c0eca32e93..c8ddaa0223 100644 --- a/src/renderer/entities/multisig/lib/multisigTx/common/types.ts +++ b/src/renderer/entities/multisig/lib/multisigTx/common/types.ts @@ -30,6 +30,12 @@ export interface IMultisigTxService { indexCreated: number, ) => Promise; updateCallData: (api: ApiPromise, tx: MultisigTransaction, callData: CallData) => Promise; + updateCallDataFromChain: ( + api: ApiPromise, + tx: MultisigTransaction, + blockHeight: number, + extrinsicIndex: number, + ) => Promise; } export type PendingMultisigTransaction = { diff --git a/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts b/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts index b4068f7214..d89839e97a 100644 --- a/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts +++ b/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts @@ -8,7 +8,7 @@ import { MultisigTxInitStatus, } from '@renderer/entities/transaction/model/transaction'; import storage, { MultisigTransactionDS } from '../../../../shared/api/storage'; -import { QUERY_INTERVAL } from './common/consts'; +import { DEFAULT_BLOCK_HASH, MULTISIG_EXTRINSIC_CALL_INDEX, QUERY_INTERVAL } from './common/consts'; import { IMultisigTxService } from './common/types'; import { createTransactionPayload, @@ -208,11 +208,38 @@ export const useMultisigTx = ({ addTask }: Props): IMultisigTxService => { }; const updateCallData = async (api: ApiPromise, tx: MultisigTransaction, callData: CallData) => { - const chain = await getChainById(tx.chainId); + try { + const chain = await getChainById(tx.chainId); - const transaction = decodeCallData(api, toAddress(tx.accountId, { prefix: chain?.addressPrefix }), callData); + const transaction = decodeCallData(api, toAddress(tx.accountId, { prefix: chain?.addressPrefix }), callData); - await updateMultisigTx({ ...tx, callData, transaction }); + await updateMultisigTx({ ...tx, callData, transaction }); + } catch (e) { + console.log('Error during update callData: ', e); + } + }; + + const updateCallDataFromChain = async ( + api: ApiPromise, + tx: MultisigTransaction, + blockHeight: number, + extrinsicIndex: number, + ) => { + try { + const blockHash = await api.rpc.chain.getBlockHash(blockHeight); + if (blockHash.toHex() === DEFAULT_BLOCK_HASH) return; + + const { block } = await api.rpc.chain.getBlock(blockHash); + const extrinsic = block.extrinsics[extrinsicIndex]; + + if (!extrinsic.argsDef.call) return; + + const callData = extrinsic.args[MULTISIG_EXTRINSIC_CALL_INDEX].toHex(); + + updateCallData(api, tx, callData); + } catch (e) { + console.log('Error during update call data from chain', e); + } }; return { @@ -226,5 +253,6 @@ export const useMultisigTx = ({ addTask }: Props): IMultisigTxService => { updateMultisigTx, deleteMultisigTx, updateCallData, + updateCallDataFromChain, }; }; From b06d73857f4ef39138d6c086b4b0ba9efb1a54c7 Mon Sep 17 00:00:00 2001 From: egor0798 Date: Mon, 28 Aug 2023 12:23:05 +0200 Subject: [PATCH 08/34] Feat: App update with electron updater (#1037) feat: Auto update application from GitHub release --------- Co-authored-by: Egor B --- package.json | 5 ++- pnpm-lock.yaml | 95 +++++++++++++++++++++++++++------------ src/main/index.ts | 55 ++++++++++++++++++++++- webpack/webpack.shared.ts | 8 ++-- 4 files changed, 127 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index 2c338ce07a..9a7596e94f 100644 --- a/package.json +++ b/package.json @@ -68,8 +68,8 @@ "@polkadot/util": "^12.2.1", "@polkadot/util-crypto": "^12.2.1", "@substrate/connect": "^0.7.26", - "@substrate/txwrapper-orml": "^6.0.0", - "@substrate/txwrapper-polkadot": "^6.0.0", + "@substrate/txwrapper-orml": "^6.0.1", + "@substrate/txwrapper-polkadot": "^6.0.1", "@zxing/browser": "^0.1.3", "@zxing/library": "^0.20.0", "bignumber.js": "^9.0.2", @@ -84,6 +84,7 @@ "effector-forms": "^1.3.4", "effector-react": "^22.5.3", "electron-log": "5.0.0-beta.23", + "electron-updater": "^6.1.1", "electron-window-state": "^5.0.3", "graphql": "^16.6.0", "i18next": "^21.8.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b9138c5e3..09d8460d02 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,11 +39,11 @@ dependencies: specifier: ^0.7.26 version: 0.7.26 '@substrate/txwrapper-orml': - specifier: ^6.0.0 - version: 6.0.0(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + specifier: ^6.0.1 + version: 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) '@substrate/txwrapper-polkadot': - specifier: ^6.0.0 - version: 6.0.0(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + specifier: ^6.0.1 + version: 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) '@zxing/browser': specifier: ^0.1.3 version: 0.1.3(@zxing/library@0.20.0) @@ -86,6 +86,9 @@ dependencies: electron-log: specifier: 5.0.0-beta.23 version: 5.0.0-beta.23 + electron-updater: + specifier: ^6.1.1 + version: 6.1.1 electron-window-state: specifier: ^5.0.3 version: 5.0.3 @@ -3071,6 +3074,18 @@ packages: tslib: 2.5.0 dev: false + /@polkadot/keyring@12.2.2(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): + resolution: {integrity: sha512-z8MVdgrhzg/bFiR2i5/W06Ma+IPeisH7EtGuIQ+ZwXiCJlXMAGUy5spfk3fUbXYubCCqNycqFgKTYDM/rDhXSg==} + engines: {node: '>=16'} + peerDependencies: + '@polkadot/util': 12.2.2 + '@polkadot/util-crypto': 12.2.2 + dependencies: + '@polkadot/util': 12.2.1 + '@polkadot/util-crypto': 12.2.1(@polkadot/util@12.2.1) + tslib: 2.5.3 + dev: false + /@polkadot/keyring@12.2.2(@polkadot/util-crypto@12.2.2)(@polkadot/util@12.2.2): resolution: {integrity: sha512-z8MVdgrhzg/bFiR2i5/W06Ma+IPeisH7EtGuIQ+ZwXiCJlXMAGUy5spfk3fUbXYubCCqNycqFgKTYDM/rDhXSg==} engines: {node: '>=16'} @@ -5082,11 +5097,11 @@ packages: resolution: {integrity: sha512-QuU2nBql3J4KCnOWtWDw4n1K4JU0T79j54ZZvm/9nhsX6AIar13FyhsaBfs6QkJ2ixTQAnd7TocJIoJRWbqMZA==} dev: false - /@substrate/txwrapper-core@6.0.0(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): - resolution: {integrity: sha512-qkWwoLOBDpIYB2Eor2q2qOXcnGloaug0do/vamhNJQFjnaNHmjsiP5QJMLipiXy4EKzOvNmAy2FmQHPj7rvP1g==} + /@substrate/txwrapper-core@6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): + resolution: {integrity: sha512-pwYvrDgTyNRqOxxmmbhjpuPoZR8+WKYtmY4Eqv++FUUfPVo6xMQGps8n1LQAwKrA3/79F0dCrdV2LPDlUtjmYQ==} dependencies: '@polkadot/api': 10.8.1 - '@polkadot/keyring': 12.2.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + '@polkadot/keyring': 12.2.2(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) memoizee: 0.4.15 transitivePeerDependencies: - '@polkadot/util' @@ -5096,10 +5111,10 @@ packages: - utf-8-validate dev: false - /@substrate/txwrapper-orml@6.0.0(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): - resolution: {integrity: sha512-kjGc4tvtEzJyOLjF7a21OQwlKAwdIFArWWe3ZRm9SnOUk7y7pXNkaFWbTv8WnpYm4HLY8SvRge6TmWRfmX/UJg==} + /@substrate/txwrapper-orml@6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): + resolution: {integrity: sha512-hKrThXBdXs1/Hsn34N2KKoYpFJrBnYr8ruoshXdvjz3dJ/R475kjkEKLFf06ZegGSdMOuRvqcjQP5kU21PI0Tw==} dependencies: - '@substrate/txwrapper-core': 6.0.0(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + '@substrate/txwrapper-core': 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) transitivePeerDependencies: - '@polkadot/util' - '@polkadot/util-crypto' @@ -5108,11 +5123,11 @@ packages: - utf-8-validate dev: false - /@substrate/txwrapper-polkadot@6.0.0(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): - resolution: {integrity: sha512-bxqw8aC0GBEyxodM7lP488TECH1OHZLKH72yyicwKbPzXGsmtWzgDqCqUbPzZ5qEiEFw4MG43mwlZNtSUXDT1g==} + /@substrate/txwrapper-polkadot@6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): + resolution: {integrity: sha512-adIa8/npvB0+Y2chgx6cwE5BEcGK7He+yrLiSIUyd5f92pW+94nt48mOLyKm9fJl1Uvsc23Apn6USECAtouPfQ==} dependencies: - '@substrate/txwrapper-core': 6.0.0(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) - '@substrate/txwrapper-substrate': 6.0.0(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + '@substrate/txwrapper-core': 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + '@substrate/txwrapper-substrate': 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) transitivePeerDependencies: - '@polkadot/util' - '@polkadot/util-crypto' @@ -5121,10 +5136,10 @@ packages: - utf-8-validate dev: false - /@substrate/txwrapper-substrate@6.0.0(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): - resolution: {integrity: sha512-r1jyk/xcCdHDk4OBPKjj5bfsQ2Y34NsYX+hgu+1S1Liat2SwPN8kY3w6j3uNbjUr/xUsiLqLIt82cyQV29WmzQ==} + /@substrate/txwrapper-substrate@6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): + resolution: {integrity: sha512-LxskfiF1lf+/TMU1C50imLl9+NR+/Hx/BIqf4KfbiF7pXgGFN2bUs/50p5NIJmCt78DRNrHvtVoRraINeMbvLA==} dependencies: - '@substrate/txwrapper-core': 6.0.0(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + '@substrate/txwrapper-core': 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) transitivePeerDependencies: - '@polkadot/util' - '@polkadot/util-crypto' @@ -6804,7 +6819,6 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true /aria-query@5.1.3: resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} @@ -7679,6 +7693,16 @@ packages: - supports-color dev: true + /builder-util-runtime@9.2.1: + resolution: {integrity: sha512-2rLv/uQD2x+dJ0J3xtsmI12AlRyk7p45TEbE/6o/fbb633e/S3pPgm+ct+JHsoY7r39dKHnGEFk/AASRFdnXmA==} + engines: {node: '>=12.0.0'} + dependencies: + debug: 4.3.4(supports-color@5.5.0) + sax: 1.2.4 + transitivePeerDependencies: + - supports-color + dev: false + /builder-util@23.6.0: resolution: {integrity: sha512-QiQHweYsh8o+U/KNCZFSvISRnvRctb8m/2rB2I1JdByzvNKxPeFLlHFRPQRXab6aYeXc18j9LpsDLJ3sGQmWTQ==} dependencies: @@ -9563,6 +9587,21 @@ packages: resolution: {integrity: sha512-0IbC2cfr8w5LxTz+nmn2cJTGafsK9iauV2r5A5scfzyovqLrxuLoxOHE5OBobP3oVIggJT+0JfKnw9sm87c8Hw==} dev: true + /electron-updater@6.1.1: + resolution: {integrity: sha512-IBT3zJ4yO5UZMF2gOTC9HrlmG4OYSRtOiHKzNAShJvfuicdx6UaXoa6AvhcTxdx6zf/rJyFMRBISS9jhVwTfow==} + dependencies: + builder-util-runtime: 9.2.1 + fs-extra: 10.1.0 + js-yaml: 4.1.0 + lazy-val: 1.0.5 + lodash.escaperegexp: 4.1.2 + lodash.isequal: 4.5.0 + semver: 7.5.1 + typed-emitter: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /electron-window-state@5.0.3: resolution: {integrity: sha512-1mNTwCfkolXl3kMf50yW3vE2lZj0y92P/HYWFBrb+v2S/pCka5mdwN3cagKm458A7NjndSwijynXgcLWRodsVg==} engines: {node: '>=8.0.0'} @@ -10966,7 +11005,6 @@ packages: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.0 - dev: true /fs-extra@11.1.1: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} @@ -13214,7 +13252,6 @@ packages: hasBin: true dependencies: argparse: 2.0.1 - dev: true /jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} @@ -13334,7 +13371,6 @@ packages: universalify: 2.0.0 optionalDependencies: graceful-fs: 4.2.11 - dev: true /jsonparse@1.3.1: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} @@ -13443,7 +13479,6 @@ packages: /lazy-val@1.0.5: resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==} - dev: true /leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} @@ -13602,13 +13637,16 @@ packages: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: true + /lodash.escaperegexp@4.1.2: + resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + dev: false + /lodash.get@4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} dev: true /lodash.isequal@4.5.0: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - dev: true /lodash.isfunction@3.0.9: resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} @@ -13744,7 +13782,6 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 - dev: true /lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} @@ -16855,7 +16892,6 @@ packages: /sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} - dev: true /saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} @@ -16978,7 +17014,6 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 - dev: true /send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} @@ -18555,6 +18590,12 @@ packages: is-typed-array: 1.1.10 dev: true + /typed-emitter@2.1.0: + resolution: {integrity: sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==} + optionalDependencies: + rxjs: 7.8.1 + dev: false + /typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} dependencies: @@ -18746,7 +18787,6 @@ packages: /universalify@2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} - dev: true /unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} @@ -19602,7 +19642,6 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} diff --git a/src/main/index.ts b/src/main/index.ts index 7f348db4ff..f0dbb668ec 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,4 +1,5 @@ -import { app } from 'electron'; +import { app, dialog } from 'electron'; +import { autoUpdater } from 'electron-updater'; import { MainWindow } from './main'; import { makeAppWithSingleInstanceLock } from './factories/instance'; @@ -6,6 +7,58 @@ import { makeAppSetup } from './factories/setup'; makeAppWithSingleInstanceLock(async () => { app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required'); + autoUpdater.autoRunAppAfterInstall = true; + + app.on('ready', () => { + autoUpdater.checkForUpdates(); + }); + autoUpdater.on('checking-for-update', () => { + console.log('[app-updater] Checking for update...'); + }); + autoUpdater.on('update-not-available', () => { + console.log(`[app-updater] No updates available. Application is up to date.`); + }); + autoUpdater.on('update-available', (info) => { + console.log(`[app-updater] New update available ${info.releaseName} ${info.releaseDate} ${info.version}`); + }); + autoUpdater.on('download-progress', (progressObj) => { + console.log( + `[app-updater] Downloading update ${progressObj.percent}% of ${progressObj.total} bytes; ${progressObj.bytesPerSecond} bytes per second`, + ); + }); + autoUpdater.on('update-downloaded', (info) => { + console.log(`[app-updater] Downloaded update ${info.releaseName} ${info.releaseDate} ${info.version}`); + dialog + .showMessageBox({ + title: 'Update Available', + message: `A new version ${info.version} of Nova Spektr is ready to be installed.`, + detail: info.releaseNotes?.toString().replaceAll(/<[a-zA-Z0-9/]*>/g, ''), // clear html tags from changelog + type: 'question', + buttons: ['Install now', 'Install on next launch', 'Not now'], + defaultId: 0, + cancelId: 2, + }) + .then((result) => { + switch (result.response) { + case 0: + autoUpdater.quitAndInstall(); + break; + case 1: + autoUpdater.autoInstallOnAppQuit = true; + break; + case 2: + autoUpdater.autoInstallOnAppQuit = false; + break; + } + }); + }); + autoUpdater.on('update-cancelled', (info) => { + console.error(`[app-updater] Update cancelled ${info.releaseName} ${info.releaseDate} ${info.version}`); + }); + autoUpdater.on('error', (err) => { + console.error('[app-updater] Error on update', err); + dialog.showErrorBox('Error', 'Error updating the application'); + }); await app.whenReady(); await makeAppSetup(MainWindow); }); diff --git a/webpack/webpack.shared.ts b/webpack/webpack.shared.ts index 4ab108e3c9..55c3583b2a 100644 --- a/webpack/webpack.shared.ts +++ b/webpack/webpack.shared.ts @@ -121,11 +121,9 @@ const sharedConfig: Configuration = { }), new webpack.DefinePlugin({ - 'process.env': { - PRODUCT_NAME: JSON.stringify(APP_CONFIG.TITLE), - VERSION: JSON.stringify(APP_CONFIG.VERSION), - CHAINS_FILE: JSON.stringify(process.env.CHAINS_FILE), - }, + 'process.env.PRODUCT_NAME': JSON.stringify(APP_CONFIG.TITLE), + 'process.env.VERSION': JSON.stringify(APP_CONFIG.VERSION), + 'process.env.CHAINS_FILE': JSON.stringify(process.env.CHAINS_FILE), }), ], }; From c1ec0c1f94309ed2cc79c22256810b98226b6ce3 Mon Sep 17 00:00:00 2001 From: egor0798 Date: Wed, 30 Aug 2023 11:42:26 +0200 Subject: [PATCH 09/34] Feat/opration UI redesign (#1016) --- .eslintrc.js | 3 + .../common/Scanning/ScanSingleframeQr.tsx | 2 +- .../components/LoginForm/LoginForm.tsx | 1 + .../entities/account/model/account.ts | 4 +- .../entities/transaction/lib/index.ts | 1 + .../transaction/lib/validateBalance.ts | 88 +++++++++ src/renderer/features/operation/index.ts | 2 + src/renderer/features/operation/init/index.ts | 3 + .../features/operation/init/model/errors.ts | 9 + .../features/operation/init/model/index.ts | 2 + .../init/ui/MultiSelectMultishardHeader.tsx | 73 +++++++ .../init/ui/MultisigOperationHeader.tsx | 70 +++++++ .../operation/init/ui}/OperationFooter.tsx | 4 +- .../operation/init/ui/OperationHeader.tsx | 80 ++++++++ .../init/ui/SingleSelectMultishardHeader.tsx | 66 +++++++ .../ui/__tests__/OperationHeader.test.tsx | 94 +++++++++ src/renderer/features/operation/sign/index.ts | 2 + .../operation/sign/model/SignignProps.ts | 19 ++ .../features/operation/sign/model/index.ts | 1 + .../operation/sign/ui/Signing/Signing.tsx | 15 ++ .../sign/ui/VaultSigning/VaultSigning.tsx | 131 +++++++++++++ .../components/modals/ApproveTx.tsx | 88 ++++----- .../Operations/components/modals/RejectTx.tsx | 79 ++++---- .../Staking/Operations/Bond/Bond.test.tsx | 10 +- .../pages/Staking/Operations/Bond/Bond.tsx | 69 ++----- .../Bond/InitOperation/InitOperation.test.tsx | 7 +- .../Bond/InitOperation/InitOperation.tsx | 140 +++++--------- .../ChangeValidators.test.tsx | 21 +- .../ChangeValidators/ChangeValidators.tsx | 67 ++----- .../InitOperation/InitOperation.test.tsx | 7 +- .../InitOperation/InitOperation.tsx | 156 ++++++--------- .../Destination/Destination.test.tsx | 21 +- .../Operations/Destination/Destination.tsx | 67 ++----- .../InitOperation/InitOperation.test.tsx | 7 +- .../InitOperation/InitOperation.tsx | 152 +++++---------- .../InitOperation/InitOperation.test.tsx | 6 +- .../Redeem/InitOperation/InitOperation.tsx | 151 +++++---------- .../Staking/Operations/Redeem/Redeem.tsx | 68 ++----- .../InitOperation/InitOperation.test.tsx | 6 +- .../Restake/InitOperation/InitOperation.tsx | 147 +++++--------- .../Operations/Restake/Restake.test.tsx | 21 +- .../Staking/Operations/Restake/Restake.tsx | 67 ++----- .../InitOperation/InitOperation.test.tsx | 6 +- .../StakeMore/InitOperation/InitOperation.tsx | 137 +++++-------- .../Operations/StakeMore/StakeMore.test.tsx | 20 +- .../Operations/StakeMore/StakeMore.tsx | 67 ++----- .../InitOperation/InitOperation.test.tsx | 6 +- .../Unstake/InitOperation/InitOperation.tsx | 147 +++++--------- .../Operations/Unstake/Unstake.test.tsx | 21 +- .../Staking/Operations/Unstake/Unstake.tsx | 67 ++----- .../OperationForm/OperationForm.tsx | 13 +- .../Operations/components/Signing/Signing.tsx | 67 ------- .../Staking/Operations/components/index.ts | 1 - src/renderer/pages/Transfer/Transfer.tsx | 78 +++----- .../components/ActionSteps/InitOperation.tsx | 180 +++++++----------- .../components/ActionSteps/Signing.tsx | 110 ----------- .../Transfer/components/ActionSteps/index.ts | 3 +- .../Transfer/components/TransferForm.tsx | 85 +++------ 58 files changed, 1359 insertions(+), 1676 deletions(-) create mode 100644 src/renderer/entities/transaction/lib/validateBalance.ts create mode 100644 src/renderer/features/operation/index.ts create mode 100644 src/renderer/features/operation/init/index.ts create mode 100644 src/renderer/features/operation/init/model/errors.ts create mode 100644 src/renderer/features/operation/init/model/index.ts create mode 100644 src/renderer/features/operation/init/ui/MultiSelectMultishardHeader.tsx create mode 100644 src/renderer/features/operation/init/ui/MultisigOperationHeader.tsx rename src/renderer/{pages/Staking/Operations/components/OperationForm => features/operation/init/ui}/OperationFooter.tsx (97%) create mode 100644 src/renderer/features/operation/init/ui/OperationHeader.tsx create mode 100644 src/renderer/features/operation/init/ui/SingleSelectMultishardHeader.tsx create mode 100644 src/renderer/features/operation/init/ui/__tests__/OperationHeader.test.tsx create mode 100644 src/renderer/features/operation/sign/index.ts create mode 100644 src/renderer/features/operation/sign/model/SignignProps.ts create mode 100644 src/renderer/features/operation/sign/model/index.ts create mode 100644 src/renderer/features/operation/sign/ui/Signing/Signing.tsx create mode 100644 src/renderer/features/operation/sign/ui/VaultSigning/VaultSigning.tsx delete mode 100644 src/renderer/pages/Staking/Operations/components/Signing/Signing.tsx delete mode 100644 src/renderer/pages/Transfer/components/ActionSteps/Signing.tsx diff --git a/.eslintrc.js b/.eslintrc.js index 873e5eadd8..cf8c42f5d3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -12,6 +12,9 @@ module.exports = { node: true, jest: true, }, + globals: { + JSX: 'readonly', + }, parser: '@typescript-eslint/parser', extends: [ 'eslint:recommended', diff --git a/src/renderer/components/common/Scanning/ScanSingleframeQr.tsx b/src/renderer/components/common/Scanning/ScanSingleframeQr.tsx index 0bcb0c0ea2..5efccab7b0 100644 --- a/src/renderer/components/common/Scanning/ScanSingleframeQr.tsx +++ b/src/renderer/components/common/Scanning/ScanSingleframeQr.tsx @@ -45,7 +45,7 @@ const ScanSingleframeQr = ({ if (txPayload) return; setupTransaction().catch(() => console.warn('ScanSingleframeQr | setupTransaction() failed')); - }, []); + }, [transaction, api]); const setupTransaction = async (): Promise => { try { diff --git a/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx b/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx index b82a0910dd..666cc90442 100644 --- a/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx +++ b/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.tsx @@ -66,6 +66,7 @@ const LoginForm = () => { defaultValues: { homeserver: DEFAULT_HOMESERVER, username: '', password: '' }, }); + // @ts-ignore const homeserver = watch('homeserver'); useEffect(() => { diff --git a/src/renderer/entities/account/model/account.ts b/src/renderer/entities/account/model/account.ts index 14024a628f..9700ffc9e4 100644 --- a/src/renderer/entities/account/model/account.ts +++ b/src/renderer/entities/account/model/account.ts @@ -106,11 +106,11 @@ export const isMultisig = (account?: Account | MultisigAccount): account is Mult return hasSignatories && hasThreshold; }; -export function isMultishard(account?: Account | MultisigAccount): boolean { +export const isMultishard = (account?: Account | MultisigAccount): boolean => { if (!account) return false; return Boolean(account.walletId); -} +}; export function isWalletContact(account?: Account | MultisigAccount): boolean { if (!account) return false; diff --git a/src/renderer/entities/transaction/lib/index.ts b/src/renderer/entities/transaction/lib/index.ts index d7fdc340e8..acaaf8b7cd 100644 --- a/src/renderer/entities/transaction/lib/index.ts +++ b/src/renderer/entities/transaction/lib/index.ts @@ -3,3 +3,4 @@ export * from './transactionService'; export * from './common/utils'; export * from './common/types'; export * from './common/constants'; +export * from './validateBalance'; diff --git a/src/renderer/entities/transaction/lib/validateBalance.ts b/src/renderer/entities/transaction/lib/validateBalance.ts new file mode 100644 index 0000000000..98f590ec11 --- /dev/null +++ b/src/renderer/entities/transaction/lib/validateBalance.ts @@ -0,0 +1,88 @@ +import { BN } from '@polkadot/util'; +import { ApiPromise } from '@polkadot/api'; + +import { Balance, IBalanceService } from '@renderer/entities/asset'; +import { ITransactionService, Transaction } from '@renderer/entities/transaction'; +import { toAccountId, transferableAmount, ValidationErrors } from '@renderer/shared/lib/utils'; +import { ChainId } from '@renderer/domain/shared-kernel'; +import { PartialBy } from '@renderer/domain/utility'; +import { OperationError, OperationErrorType } from '@renderer/features/operation'; + +type Props = { + api: ApiPromise; + chainId: ChainId; + transaction: Transaction; + assetId: string; + getBalance: IBalanceService['getBalance']; + getTransactionFee: ITransactionService['getTransactionFee']; +}; + +export const validateBalance = async ( + props: PartialBy, +): Promise => { + if (!props.api || !props.transaction) return; + + const [balanceIsEnough, feeIsEnough] = await Promise.all([ + validateBalanceForAmount(props as Props), + validateBalanceForFee(props as Props), + ]); + if (!balanceIsEnough) { + return ValidationErrors.INSUFFICIENT_BALANCE; + } + if (!feeIsEnough) { + return ValidationErrors.INSUFFICIENT_BALANCE_FOR_FEE; + } +}; + +const getTokenBalance = ({ getBalance, transaction, assetId, chainId }: Props): Promise => { + return getBalance(toAccountId(transaction.address), chainId, assetId.toString()); +}; + +const getNativeTokenBalance = ({ assetId, transaction, chainId, getBalance }: Props): Promise => { + if (assetId === '0') return Promise.resolve(undefined); + + return getBalance(toAccountId(transaction.address), chainId, '0'); +}; + +const validateBalanceForAmount = async ({ transaction, ...props }: Props): Promise => { + const amount = transaction.args.value; + const tokenBalance = await getTokenBalance({ transaction, ...props }); + const transferableBalance = transferableAmount(tokenBalance); + + return new BN(transferableBalance).gte(new BN(amount)); +}; + +const validateBalanceForFee = async ({ transaction, getTransactionFee, api, ...props }: Props): Promise => { + const amount = transaction.args.value; + const nativeTokenBalance = await getNativeTokenBalance({ transaction, api, getTransactionFee, ...props }); + const tokenBalance = await getTokenBalance({ transaction, api, getTransactionFee, ...props }); + const transferableBalance = transferableAmount(tokenBalance); + const transferableNativeTokenBalance = transferableAmount(nativeTokenBalance); + const fee = await getTransactionFee(transaction, api); + + return nativeTokenBalance + ? new BN(transferableNativeTokenBalance).gte(new BN(fee)) + : new BN(transferableBalance).gte(new BN(fee).add(new BN(amount))); +}; + +export const getOperationErrors = ( + isFeeInvalid: boolean, + isDepositInvalid: boolean, + hasOtherErrors?: boolean, +): OperationErrorType[] => { + const errors: OperationErrorType[] = []; + + if (isDepositInvalid) { + errors.push(OperationError.INVALID_DEPOSIT); + } + + if (isFeeInvalid) { + errors.push(OperationError.INVALID_FEE); + } + + if (hasOtherErrors) { + errors.push(OperationError.EMPTY_ERROR); + } + + return errors; +}; diff --git a/src/renderer/features/operation/index.ts b/src/renderer/features/operation/index.ts new file mode 100644 index 0000000000..fe7f243b78 --- /dev/null +++ b/src/renderer/features/operation/index.ts @@ -0,0 +1,2 @@ +export * from './sign'; +export * from './init'; diff --git a/src/renderer/features/operation/init/index.ts b/src/renderer/features/operation/init/index.ts new file mode 100644 index 0000000000..c39738fc3a --- /dev/null +++ b/src/renderer/features/operation/init/index.ts @@ -0,0 +1,3 @@ +export * from './ui/OperationHeader'; +export * from './ui/OperationFooter'; +export * from './model'; diff --git a/src/renderer/features/operation/init/model/errors.ts b/src/renderer/features/operation/init/model/errors.ts new file mode 100644 index 0000000000..8839378753 --- /dev/null +++ b/src/renderer/features/operation/init/model/errors.ts @@ -0,0 +1,9 @@ +import { ObjectValues } from '@renderer/domain/utility'; + +export const OperationError = { + INVALID_FEE: 'staking.notEnoughBalanceForFeeError', + INVALID_DEPOSIT: 'staking.notEnoughBalanceForDepositError', + EMPTY_ERROR: ' ', // error for invalid state with no hint text +} as const; + +export type OperationErrorType = ObjectValues; diff --git a/src/renderer/features/operation/init/model/index.ts b/src/renderer/features/operation/init/model/index.ts new file mode 100644 index 0000000000..85cf88daa9 --- /dev/null +++ b/src/renderer/features/operation/init/model/index.ts @@ -0,0 +1,2 @@ +export { OperationError } from './errors'; +export type { OperationErrorType } from './errors'; diff --git a/src/renderer/features/operation/init/ui/MultiSelectMultishardHeader.tsx b/src/renderer/features/operation/init/ui/MultiSelectMultishardHeader.tsx new file mode 100644 index 0000000000..eb10883024 --- /dev/null +++ b/src/renderer/features/operation/init/ui/MultiSelectMultishardHeader.tsx @@ -0,0 +1,73 @@ +import { useEffect, useState } from 'react'; + +import { Account, MultisigAccount } from '@renderer/entities/account'; +import { InputHint, Select } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { ChainId } from '@renderer/domain/shared-kernel'; +import { OperationErrorType } from '@renderer/features/operation/init/model'; + +type Props = { + accounts: Account[]; + chainId: ChainId; + invalid?: boolean; + error?: OperationErrorType; + getAccountOption: (account: Account) => DropdownOption; + onAccountChange: (account: Account) => void; +}; + +export const MultiSelectMultishardHeader = ({ + accounts, + invalid, + chainId, + error, + getAccountOption, + onAccountChange, +}: Props) => { + const { t } = useI18n(); + + const [activeAccount, setActiveAccount] = useState>(); + const [accountsOptions, setAccountsOptions] = useState[]>([]); + + useEffect(() => { + const options = accounts.reduce((acc, account) => { + const isSameChain = !account.chainId || account.chainId === chainId; + + if (isSameChain) { + acc.push(getAccountOption(account)); + } + + return acc; + }, []); + + if (options.length === 0) return; + + setAccountsOptions(options); + + if (!activeAccount) { + changeAccount({ id: options[0].id, value: options[0].value }); + } + }, [accounts.length, getAccountOption]); + + const changeAccount = (account: DropdownResult) => { + onAccountChange(account.value); + setActiveAccount(account); + }; + + return ( +
    + + + {t('multisigOperations.noSignatory')} + + + {t(error || '')} + +
    + ); +}; diff --git a/src/renderer/pages/Staking/Operations/components/OperationForm/OperationFooter.tsx b/src/renderer/features/operation/init/ui/OperationFooter.tsx similarity index 97% rename from src/renderer/pages/Staking/Operations/components/OperationForm/OperationFooter.tsx rename to src/renderer/features/operation/init/ui/OperationFooter.tsx index b53805896a..5bf149fa71 100644 --- a/src/renderer/pages/Staking/Operations/components/OperationForm/OperationFooter.tsx +++ b/src/renderer/features/operation/init/ui/OperationFooter.tsx @@ -17,7 +17,7 @@ type Props = { onFeeLoading: (value: boolean) => void; }; -const OperationFooter = ({ +export const OperationFooter = ({ api, asset, transaction, @@ -77,5 +77,3 @@ const OperationFooter = ({ ); }; - -export default OperationFooter; diff --git a/src/renderer/features/operation/init/ui/OperationHeader.tsx b/src/renderer/features/operation/init/ui/OperationHeader.tsx new file mode 100644 index 0000000000..d8f7dddc57 --- /dev/null +++ b/src/renderer/features/operation/init/ui/OperationHeader.tsx @@ -0,0 +1,80 @@ +import { ChainId } from '@renderer/domain/shared-kernel'; +import { Account, isMultishard, isMultisig, MultisigAccount } from '@renderer/entities/account'; +import { SingleSelectMultishardHeader } from './SingleSelectMultishardHeader'; +import { MultiSelectMultishardHeader } from './MultiSelectMultishardHeader'; +import { DropdownOption } from '@renderer/shared/ui/Dropdowns/common/types'; +import { MultisigOperationHeader } from './MultisigOperationHeader'; +import { OperationError, OperationErrorType } from '@renderer/features/operation/init/model'; + +type Props = { + accounts: Account[] | [MultisigAccount]; + chainId: ChainId; + isMultiselect?: boolean; + invalid?: boolean; + errors?: OperationErrorType[]; + getAccountOption: (account: Account) => DropdownOption; + getSignatoryOption: (account: Account) => DropdownOption; + onSignatoryChange: (account: Account) => void; +} & AccountSelectProps; + +type SelectAccount = (account: Account) => void; +type MultiselectAccount = (accounts: Account[]) => void; + +type AccountSelectProps = + | { isMultiselect: true; onAccountChange: MultiselectAccount } + | { onAccountChange: SelectAccount }; + +export const OperationHeader = ({ + chainId, + isMultiselect, + accounts, + invalid, + errors = [], + getSignatoryOption, + getAccountOption, + onAccountChange, + onSignatoryChange, +}: Props) => { + const firstAccount = accounts[0]; + + const accountIsMultisig = isMultisig(firstAccount); + const accountIsMultishard = isMultishard(firstAccount); + + const multisigError = (accountIsMultisig && errors.find((e) => e === OperationError.INVALID_DEPOSIT)) || undefined; + const multishardError = (accountIsMultishard && errors.find((e) => e === OperationError.INVALID_FEE)) || undefined; + const emptyError = errors.find((e) => e === OperationError.EMPTY_ERROR); + + return ( +
    + {accountIsMultisig && ( + + )} + + {accountIsMultishard && + (isMultiselect ? ( + + ) : ( + + ))} +
    + ); +}; diff --git a/src/renderer/features/operation/init/ui/SingleSelectMultishardHeader.tsx b/src/renderer/features/operation/init/ui/SingleSelectMultishardHeader.tsx new file mode 100644 index 0000000000..3793026a21 --- /dev/null +++ b/src/renderer/features/operation/init/ui/SingleSelectMultishardHeader.tsx @@ -0,0 +1,66 @@ +import { useEffect, useState } from 'react'; + +import { Account, MultisigAccount } from '@renderer/entities/account'; +import { InputHint, MultiSelect } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { OperationErrorType } from '@renderer/features/operation/init/model'; + +type Props = { + accounts: Account[]; + invalid?: boolean; + error?: OperationErrorType; + getAccountOption: (account: Account) => DropdownOption; + onAccountsChange: (accounts: Account[]) => void; +}; + +export const SingleSelectMultishardHeader = ({ + accounts, + invalid, + error, + getAccountOption, + onAccountsChange, +}: Props) => { + const { t } = useI18n(); + + const [activeAccounts, setActiveAccounts] = useState[]>([]); + const [accountsOptions, setAccountsOptions] = useState[]>([]); + + useEffect(() => { + const formattedAccounts = accounts.map((account) => getAccountOption(account)); + + if (formattedAccounts.length === 0) return; + + setAccountsOptions(formattedAccounts); + }, [accounts.length, getAccountOption]); + + useEffect(() => { + if (accountsOptions.length === 0) return; + + const activeAccounts = accountsOptions.map(({ id, value }) => ({ id, value })); + changeAccount(activeAccounts); + }, [accountsOptions.length]); + + const changeAccount = (accounts: DropdownResult[]) => { + onAccountsChange(accounts.map((a) => a.value)); + setActiveAccounts(accounts); + }; + + return ( +
    + acc.id)} + options={accountsOptions} + onChange={changeAccount} + /> + + {t(error || '')} + +
    + ); +}; diff --git a/src/renderer/features/operation/init/ui/__tests__/OperationHeader.test.tsx b/src/renderer/features/operation/init/ui/__tests__/OperationHeader.test.tsx new file mode 100644 index 0000000000..049c320686 --- /dev/null +++ b/src/renderer/features/operation/init/ui/__tests__/OperationHeader.test.tsx @@ -0,0 +1,94 @@ +import { act, render, screen } from '@testing-library/react'; +import { ComponentProps } from 'react'; +import noop from 'lodash/noop'; + +import { OperationHeader } from '../OperationHeader'; +import { TEST_ACCOUNT_ID, TEST_ADDRESS, TEST_CHAIN_ID } from '@renderer/shared/lib/utils'; +import { AccountId, ChainType, CryptoType, SigningType } from '@renderer/domain/shared-kernel'; +import { Account } from '@renderer/entities/account'; + +const accountProps = { + signingType: SigningType.PARITY_SIGNER, + cryptoType: CryptoType.SR25519, + chainType: ChainType.SUBSTRATE, + isMain: true, + isActive: true, +}; +const SIGNATORY_ACCOUNT_ID: AccountId = '0x0dsfdsf'; +const SIGNATORY_ACCOUNT = { + accountId: SIGNATORY_ACCOUNT_ID, + name: 'signatory account', + ...accountProps, + address: TEST_ADDRESS, +}; +const props: Omit, 'accounts' | 'onSignatoryChange' | 'onAccountChange'> = { + chainId: TEST_CHAIN_ID, + getAccountOption: (account: Account) => ({ id: account.accountId, value: account, element: account.name }), + getSignatoryOption: (account: Account) => ({ id: account.accountId, value: account, element: account.name }), +}; + +jest.mock('@renderer/app/providers', () => ({ + useI18n: jest.fn().mockReturnValue({ + t: (key: string) => key, + }), +})); + +jest.mock('@renderer/entities/account', () => ({ + ...jest.requireActual('@renderer/entities/account'), + useAccount: jest.fn().mockReturnValue({ + getLiveAccounts: () => [SIGNATORY_ACCOUNT], + }), +})); + +describe('features/operation/init/OperationHeader', () => { + test('should render signatory selector for multisig and select first signatory', async () => { + const spySignatoryChange = jest.fn(); + + await act(async () => { + render( + , + ); + }); + // render(); + + const signatorySelect = screen.getByTestId('signatory-select'); + expect(signatorySelect).toBeInTheDocument(); + expect(spySignatoryChange).toBeCalledWith(SIGNATORY_ACCOUNT); + }); + + test('should render shard selector for multishard and select first shard', async () => { + const spyAccountChange = jest.fn(); + const SHARD_ACCOUNT = { ...SIGNATORY_ACCOUNT, walletId: '1' }; + + await act(async () => { + render( + , + ); + }); + // render(); + + const signatorySelect = screen.getByTestId('shards-select'); + expect(signatorySelect).toBeInTheDocument(); + expect(spyAccountChange).toBeCalledWith(SHARD_ACCOUNT); + }); +}); diff --git a/src/renderer/features/operation/sign/index.ts b/src/renderer/features/operation/sign/index.ts new file mode 100644 index 0000000000..c81da1ddae --- /dev/null +++ b/src/renderer/features/operation/sign/index.ts @@ -0,0 +1,2 @@ +export * from './ui/Signing/Signing'; +export * from './model'; diff --git a/src/renderer/features/operation/sign/model/SignignProps.ts b/src/renderer/features/operation/sign/model/SignignProps.ts new file mode 100644 index 0000000000..3f17b13422 --- /dev/null +++ b/src/renderer/features/operation/sign/model/SignignProps.ts @@ -0,0 +1,19 @@ +import { ApiPromise } from '@polkadot/api'; +import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; + +import { ChainId, HexString } from '@renderer/domain/shared-kernel'; +import { Account } from '@renderer/entities/account'; +import { Transaction } from '@renderer/entities/transaction'; +import { ValidationErrors } from '@renderer/shared/lib/utils'; + +export type SigningProps = { + chainId: ChainId; + api: ApiPromise; + addressPrefix: number; + accounts: Account[]; + signatory?: Account; + validateBalance?: () => Promise; + onGoBack: () => void; + transactions: Transaction[]; + onResult: (signatures: HexString[], unsignedTxs: UnsignedTransaction[]) => void; +}; diff --git a/src/renderer/features/operation/sign/model/index.ts b/src/renderer/features/operation/sign/model/index.ts new file mode 100644 index 0000000000..2732096c45 --- /dev/null +++ b/src/renderer/features/operation/sign/model/index.ts @@ -0,0 +1 @@ +export type { SigningProps } from './SignignProps'; diff --git a/src/renderer/features/operation/sign/ui/Signing/Signing.tsx b/src/renderer/features/operation/sign/ui/Signing/Signing.tsx new file mode 100644 index 0000000000..2d1d121a9b --- /dev/null +++ b/src/renderer/features/operation/sign/ui/Signing/Signing.tsx @@ -0,0 +1,15 @@ +import { SigningProps } from '../../model'; +import { SigningType } from '@renderer/domain/shared-kernel'; +import { VaultSigning } from '../VaultSigning/VaultSigning'; + +export const SigningFlow: Record JSX.Element | null> = { + [SigningType.MULTISIG]: (props) => , + [SigningType.PARITY_SIGNER]: (props) => , + [SigningType.WATCH_ONLY]: () => null, +}; + +export const Signing = (props: SigningProps) => { + const signingType = props.accounts[0].signingType; + + return SigningFlow[signingType](props); +}; diff --git a/src/renderer/features/operation/sign/ui/VaultSigning/VaultSigning.tsx b/src/renderer/features/operation/sign/ui/VaultSigning/VaultSigning.tsx new file mode 100644 index 0000000000..c519abd9e9 --- /dev/null +++ b/src/renderer/features/operation/sign/ui/VaultSigning/VaultSigning.tsx @@ -0,0 +1,131 @@ +import { useEffect, useState } from 'react'; +import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; + +import { SigningProps } from '@renderer/features/operation'; +import { useCountdown } from '@renderer/shared/lib/hooks'; +import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; +import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; +import { ValidationErrors } from '@renderer/shared/lib/utils'; +import { useTransaction } from '@renderer/entities/transaction'; +import { HexString } from '@renderer/domain/shared-kernel'; +import QrReaderWrapper from '@renderer/components/common/QrCode/QrReader/QrReaderWrapper'; + +export const VaultSigning = ({ + chainId, + api, + addressPrefix, + validateBalance, + onGoBack, + accounts, + signatory, + transactions, + onResult, +}: SigningProps) => { + const { verifySignature } = useTransaction(); + + const [countdown, resetCountdown] = useCountdown(api); + const [unsignedTxs, setUnsignedTxs] = useState([]); + const [txPayloads, setTxPayloads] = useState([]); + + const [validationError, setValidationError] = useState(); + + const isScanStep = !unsignedTxs.length && !txPayloads.length; + const isMultiframe = transactions.length > 1; + + useEffect(() => { + if (countdown === 0) { + scanAgain(); + } + }, [countdown]); + + const handleSignature = async (data: string | string[]) => { + const isMultishard = Array.isArray(data); + const signatures = isMultishard ? (data as HexString[]) : [data as HexString]; + const accountIds = isMultiframe ? accounts.map((t) => t.accountId) : [(signatory || accounts[0])?.accountId]; + + const isVerified = signatures.every((signature, index) => { + // TODO: Research complex verification + // TODO: research multishard signature verification + if (isMultishard) return true; + + const payload = txPayloads[index]; + const verifiablePayload = payload?.slice(1); + const verifiableComplexPayload = payload?.slice(2); + + const isVerified = + verifiablePayload && verifySignature(verifiablePayload, signature as HexString, accountIds[index]); + const isComplexVerified = + verifiableComplexPayload && + verifySignature(verifiableComplexPayload, signature as HexString, accountIds[index]); + + return isVerified || isComplexVerified; + }); + + const balanceValidationError = validateBalance && (await validateBalance()); + + if (!isVerified || balanceValidationError) { + setValidationError(balanceValidationError || ValidationErrors.INVALID_SIGNATURE); + } else { + onResult(signatures, unsignedTxs); + } + }; + + const scanAgain = () => { + setUnsignedTxs([]); + setTxPayloads([]); + }; + + const ScanStep = ( +
    + {isMultiframe ? ( + { + setUnsignedTxs(unsignedTx); + setTxPayloads(payloads); + }} + /> + ) : ( + { + setUnsignedTxs([unsignedTx]); + setTxPayloads([payload]); + }} + /> + )} +
    + ); + + const SignStep = ( +
    + +
    + ); + + if (isScanStep) { + return ScanStep; + } + + return SignStep; +}; diff --git a/src/renderer/pages/Operations/components/modals/ApproveTx.tsx b/src/renderer/pages/Operations/components/modals/ApproveTx.tsx index ae34e0de5f..dada398333 100644 --- a/src/renderer/pages/Operations/components/modals/ApproveTx.tsx +++ b/src/renderer/pages/Operations/components/modals/ApproveTx.tsx @@ -6,9 +6,17 @@ import { BN } from '@polkadot/util'; import { Icon, BaseModal, Button } from '@renderer/shared/ui'; import { useI18n } from '@renderer/app/providers'; import { AccountDS, MultisigTransactionDS } from '@renderer/shared/api/storage'; -import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { useToggle } from '@renderer/shared/lib/hooks'; import { Account, MultisigAccount, useAccount } from '@renderer/entities/account'; import { ExtendedChain } from '@renderer/entities/network'; +import { + Transaction, + TransactionType, + useTransaction, + OperationResult, + useCallDataDecoder, + validateBalance, +} from '@renderer/entities/transaction'; import { Address, HexString, SigningType, Timepoint } from '@renderer/domain/shared-kernel'; import { toAddress, transferableAmount, TEST_ADDRESS } from '@renderer/shared/lib/utils'; import { getTransactionTitle } from '../../common/utils'; @@ -17,16 +25,8 @@ import { useBalance } from '@renderer/entities/asset'; import Confirmation from '@renderer/pages/Operations/components/ActionSteps/Confirmation'; import SignatorySelectModal from '@renderer/pages/Operations/components/modals/SignatorySelectModal'; import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; -import { Signing } from '@renderer/pages/Transfer/components/ActionSteps'; -import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; import { useMultisigEvent } from '@renderer/entities/multisig'; -import { - Transaction, - TransactionType, - useTransaction, - OperationResult, - useCallDataDecoder, -} from '@renderer/entities/transaction'; +import { Signing } from '@renderer/features/operation'; type Props = { tx: MultisigTransactionDS; @@ -36,12 +36,11 @@ type Props = { const enum Step { CONFIRMATION, - SCANNING, SIGNING, SUBMIT, } -const AllSteps = [Step.CONFIRMATION, Step.SCANNING, Step.SIGNING, Step.SUBMIT]; +const AllSteps = [Step.CONFIRMATION, Step.SIGNING, Step.SUBMIT]; const ApproveTx = ({ tx, account, connection }: Props) => { const { t } = useI18n(); @@ -57,13 +56,11 @@ const ApproveTx = ({ tx, account, connection }: Props) => { const [isFeeModalOpen, toggleFeeModal] = useToggle(); const [activeStep, setActiveStep] = useState(Step.CONFIRMATION); - const [countdown, resetCountdown] = useCountdown(connection.api); const [signAccount, setSignAccount] = useState(); const [feeTx, setFeeTx] = useState(); const [approveTx, setApproveTx] = useState(); const [unsignedTx, setUnsignedTx] = useState(); - const [txPayload, setTxPayload] = useState(); const [txWeight, setTxWeight] = useState(); const [signature, setSignature] = useState(); @@ -80,6 +77,8 @@ const ApproveTx = ({ tx, account, connection }: Props) => { return isSignatory && notSigned && isCurrentChain && notWatchOnly; }); + const nativeAsset = connection.assets[0]; + useEffect(() => { setFeeTx(getMultisigTx(TEST_ADDRESS)); @@ -100,8 +99,9 @@ const ApproveTx = ({ tx, account, connection }: Props) => { setActiveStep(AllSteps.indexOf(activeStep) - 1); }; - const onSignResult = (signature: HexString) => { - setSignature(signature); + const onSignResult = (signature: HexString[], unsigned: UnsignedTransaction[]) => { + setSignature(signature[0]); + setUnsignedTx(unsigned[0]); setIsModalOpen(false); setActiveStep(Step.SUBMIT); }; @@ -111,8 +111,6 @@ const ApproveTx = ({ tx, account, connection }: Props) => { setActiveStep(Step.CONFIRMATION); }; - const nativeAsset = connection.assets[0]; - const getMultisigTx = (signer: Address): Transaction => { const signerAddress = toAddress(signer, { prefix: connection?.addressPrefix }); @@ -161,7 +159,7 @@ const ApproveTx = ({ tx, account, connection }: Props) => { const isValid = await validateBalanceForFee(account); if (isValid) { - setActiveStep(Step.SCANNING); + setActiveStep(Step.SIGNING); } else { toggleFeeModal(); } @@ -172,12 +170,22 @@ const ApproveTx = ({ tx, account, connection }: Props) => { const selectAccount = () => { if (unsignedAccounts.length === 1) { setSignAccount(unsignedAccounts[0]); - setActiveStep(Step.SCANNING); + setActiveStep(Step.SIGNING); } else { toggleSelectAccountModal(); } }; + const checkBalance = () => + validateBalance({ + api: connection.api, + chainId: tx.chainId, + transaction: approveTx, + assetId: nativeAsset?.assetId.toString(), + getBalance, + getTransactionFee, + }); + const thresholdReached = events.filter((e) => e.status === 'SIGNED').length === account.threshold - 1; const readyForSign = tx.status === 'SIGNING' && unsignedAccounts.length > 0; @@ -212,44 +220,20 @@ const ApproveTx = ({ tx, account, connection }: Props) => { )} - {activeStep === Step.SCANNING && approveTx && connection.api && signAccount && ( - { - setUnsignedTx(tx); - setTxPayload(txPayload); - setActiveStep(Step.SIGNING); - }} + onResult={onSignResult} /> )} - {activeStep === Step.SIGNING && ( -
    - {approveTx && connection.api && signAccount && ( - {}} - onResult={onSignResult} - /> - )} -
    - )} - { const { t } = useI18n(); @@ -46,11 +50,9 @@ const RejectTx = ({ tx, account, connection }: Props) => { const [isFeeModalOpen, toggleFeeModal] = useToggle(); const [activeStep, setActiveStep] = useState(Step.CONFIRMATION); - const [countdown, resetCountdown] = useCountdown(connection.api); const [rejectTx, setRejectTx] = useState(); const [unsignedTx, setUnsignedTx] = useState(); - const [txPayload, setTxPayload] = useState(); const [rejectReason, setRejectReason] = useState(''); const [signature, setSignature] = useState(); @@ -59,20 +61,31 @@ const RejectTx = ({ tx, account, connection }: Props) => { const signAccount = accounts.find((a) => a.accountId === tx.depositor); const transactionTitle = getTransactionTitle(tx.transaction); + const nativeAsset = connection.assets[0]; + + const checkBalance = () => + validateBalance({ + api: connection.api, + chainId: tx.chainId, + transaction: rejectTx, + assetId: nativeAsset?.assetId.toString(), + getBalance, + getTransactionFee, + }); + useEffect(() => { const accountId = signAccount?.accountId || account.signatories[0].accountId; setRejectTx(getMultisigTx(accountId)); }, [tx, signAccount?.accountId]); - const nativeAsset = connection.assets[0]; - const goBack = () => { setActiveStep(AllSteps.indexOf(activeStep) - 1); }; - const onSignResult = (signature: HexString) => { - setSignature(signature); + const onSignResult = (signature: HexString[], unsigned: UnsignedTransaction[]) => { + setUnsignedTx(unsigned[0]); + setSignature(signature[0]); setIsModalOpen(false); setActiveStep(Step.SUBMIT); }; @@ -130,7 +143,7 @@ const RejectTx = ({ tx, account, connection }: Props) => { if (isValid) { setRejectReason(reason); - setActiveStep(Step.SCANNING); + setActiveStep(Step.SIGNING); } else { toggleFeeModal(); } @@ -172,44 +185,20 @@ const RejectTx = ({ tx, account, connection }: Props) => { )} - {activeStep === Step.SCANNING && rejectTx && connection.api && signAccount && ( - { - setUnsignedTx(tx); - setTxPayload(txPayload); - setActiveStep(Step.SIGNING); - }} + onResult={onSignResult} /> )} - {activeStep === Step.SIGNING && ( -
    - {rejectTx && connection.api && signAccount && ( - {}} - onResult={onSignResult} - /> - )} -
    - )} - ({ onResult }: any) => { jest.mock('../components/index', () => ({ Validators: ({ onResult }: any) => mockButton('to confirm', onResult), - Confirmation: ({ onResult }: any) => mockButton('to scan', onResult), - Signing: ({ onResult }: any) => mockButton('to submit', onResult), + Confirmation: ({ onResult }: any) => mockButton('to sign', onResult), Submit: () => 'finish', })); +jest.mock('@renderer/features/operation', () => ({ + Signing: ({ onResult }: any) => mockButton('to submit', onResult), +})); + jest.mock( '@renderer/components/common/Scanning/ScanMultiframeQr', () => @@ -99,9 +102,6 @@ describe('pages/Staking/Bond', () => { nextButton = screen.getByRole('button', { name: 'to confirm' }); await act(async () => nextButton.click()); - nextButton = screen.getByRole('button', { name: 'to scan' }); - await act(async () => nextButton.click()); - nextButton = screen.getByRole('button', { name: 'to sign' }); await act(async () => nextButton.click()); diff --git a/src/renderer/pages/Staking/Operations/Bond/Bond.tsx b/src/renderer/pages/Staking/Operations/Bond/Bond.tsx index 94ee8dc462..a9a42e9988 100644 --- a/src/renderer/pages/Staking/Operations/Bond/Bond.tsx +++ b/src/renderer/pages/Staking/Operations/Bond/Bond.tsx @@ -7,23 +7,21 @@ import { RewardsDestination, ValidatorMap } from '@renderer/entities/staking'; import { useI18n, useNetworkContext, Paths } from '@renderer/app/providers'; import { Address, ChainId, HexString, AccountId } from '@renderer/domain/shared-kernel'; import { Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; -import { Validators, Confirmation, Signing, Submit, NoAsset } from '../components'; -import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { Validators, Confirmation, Submit, NoAsset } from '../components'; +import { useToggle } from '@renderer/shared/lib/hooks'; import { Account, MultisigAccount, isMultisig, useAccount } from '@renderer/entities/account'; import { BaseModal, Alert, Button, Loader } from '@renderer/shared/ui'; import InitOperation, { BondResult } from './InitOperation/InitOperation'; import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; import { DestinationType } from '../common/types'; -import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; -import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; import { UnstakingDuration } from '@renderer/pages/Staking/Overview/components'; import { isLightClient } from '@renderer/entities/network'; +import { Signing } from '@renderer/features/operation'; const enum Step { INIT, VALIDATORS, CONFIRMATION, - SCANNING, SIGNING, SUBMIT, } @@ -51,7 +49,6 @@ export const Bond = () => { const [multisigTx, setMultisigTx] = useState(); const [transactions, setTransactions] = useState([]); const [unsignedTransactions, setUnsignedTransactions] = useState([]); - const [txPayloads, setTxPayloads] = useState([]); const [accounts, setAccounts] = useState([]); const [signer, setSigner] = useState(); @@ -66,10 +63,9 @@ export const Bond = () => { const accounts = activeAccounts.filter((a) => a.id && accountIds.includes(a.id.toString())); setAccounts(accounts); - }, [activeAccounts.length]); + }, [activeAccounts.length, activeAccounts.length && activeAccounts[0].accountId]); const connection = connections[chainId]; - const [countdown, resetCountdown] = useCountdown(connection?.api); if (!connection || accountIds.length === 0) { return ; @@ -217,12 +213,8 @@ export const Bond = () => { }; }; - const onScanResult = (unsigned: UnsignedTransaction[]) => { + const onSignResult = (signatures: HexString[], unsigned: UnsignedTransaction[]) => { setUnsignedTransactions(unsigned); - setActiveStep(Step.SIGNING); - }; - - const onSignResult = (signatures: HexString[]) => { setSignatures(signatures); setActiveStep(Step.SUBMIT); }; @@ -265,7 +257,7 @@ export const Bond = () => { destination={destination} transaction={transactions[0]} multisigTx={multisigTx} - onResult={() => setActiveStep(Step.SCANNING)} + onResult={() => setActiveStep(Step.SIGNING)} onGoBack={goToPrevStep} {...explorersProps} > @@ -283,51 +275,16 @@ export const Bond = () => { )} )} - {activeStep === Step.SCANNING && ( -
    - {transactions.length > 1 ? ( - setActiveStep(Step.CONFIRMATION)} - onResetCountdown={resetCountdown} - onResult={(unsignedTx, payloads) => { - onScanResult(unsignedTx); - setTxPayloads(payloads); - }} - /> - ) : ( - setActiveStep(Step.CONFIRMATION)} - onResetCountdown={resetCountdown} - onResult={(unsignedTx, txPayload) => { - onScanResult([unsignedTx]); - setTxPayloads([txPayload]); - }} - /> - )} -
    - )} {activeStep === Step.SIGNING && ( 1 ? txAccounts.map((t) => t.accountId) : [(signer || txAccounts[0])?.accountId] - } - txPayloads={txPayloads} - multiQr={transactions.length > 1} + chainId={chainId} + api={api} + addressPrefix={addressPrefix} + signatory={signer} + accounts={txAccounts} + transactions={multisigTx ? [multisigTx] : transactions} + onGoBack={() => setActiveStep(Step.CONFIRMATION)} onResult={onSignResult} - onGoBack={() => setActiveStep(Step.SCANNING)} /> )} diff --git a/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.test.tsx index 5c87a11304..a237f5c7c6 100644 --- a/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.test.tsx @@ -43,11 +43,12 @@ jest.mock('@renderer/entities/asset', () => ({ })); jest.mock('../../components', () => ({ - OperationForm: ({ children }: any) => { + OperationForm: ({ header, footer }: any) => { return (
    + {header({ invalidBalance: false, invalidFee: false, invalidDeposit: false })}

    operationForm

    - {children({ invalidBalance: false, invalidFee: false, invalidDeposit: false })} + {footer}
    ); }, @@ -57,7 +58,7 @@ describe('pages/Staking/Bond/InitOperation', () => { const defaultProps = { api: {} as ApiPromise, chainId: '0x123' as ChainId, - accounts: [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }] as unknown as Account[], + accounts: [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID, walletId: 1 }] as unknown as Account[], asset: { assetId: 1, symbol: 'DOT', precision: 10 } as Asset, addressPrefix: 0, onResult: noop, diff --git a/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.tsx b/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.tsx index 7d36420aec..69617a7184 100644 --- a/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.tsx +++ b/src/renderer/pages/Staking/Operations/Bond/InitOperation/InitOperation.tsx @@ -2,23 +2,22 @@ import { ApiPromise } from '@polkadot/api'; import { BN } from '@polkadot/util'; import { useEffect, useState } from 'react'; -import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; +import { OperationError, OperationFooter, OperationHeader } from '@renderer/features/operation'; import { useI18n } from '@renderer/app/providers'; import { Asset, Balance as AccountBalance, useBalance } from '@renderer/entities/asset'; -import { AccountId, Address, ChainId, SigningType } from '@renderer/domain/shared-kernel'; +import { AccountId, Address, ChainId } from '@renderer/domain/shared-kernel'; import { Transaction, TransactionType } from '@renderer/entities/transaction'; -import { useAccount, Account, isMultisig, MultisigAccount } from '@renderer/entities/account'; +import { Account, isMultisig, MultisigAccount } from '@renderer/entities/account'; import { formatAmount, stakeableAmount, toAddress, nonNullable, TEST_ADDRESS } from '@renderer/shared/lib/utils'; import { useValidators } from '@renderer/entities/staking'; -import { InputHint, MultiSelect, Select } from '@renderer/shared/ui'; import { OperationForm } from '../../components'; import { - getSignatoryOptions, getStakeAccountOption, validateBalanceForFee, validateBalanceForFeeDeposit, validateStake, } from '../../common/utils'; +import { getSignatoryOption } from '@renderer/pages/Transfer/common/utils'; export type BondResult = { amount: string; @@ -39,12 +38,9 @@ type Props = { const InitOperation = ({ api, chainId, accounts, asset, addressPrefix, onResult }: Props) => { const { t } = useI18n(); - const { getLiveAccounts } = useAccount(); const { getMaxValidators } = useValidators(); const { getLiveAssetBalances } = useBalance(); - const dbAccounts = getLiveAccounts(); - const [fee, setFee] = useState(''); const [feeLoading, setFeeLoading] = useState(true); const [deposit, setDeposit] = useState(''); @@ -52,16 +48,14 @@ const InitOperation = ({ api, chainId, accounts, asset, addressPrefix, onResult const [minBalance, setMinBalance] = useState('0'); - const [stakeAccounts, setStakeAccounts] = useState[]>([]); - const [activeStakeAccounts, setActiveStakeAccounts] = useState[]>([]); + const [activeStakeAccounts, setActiveStakeAccounts] = useState<(Account | MultisigAccount)[]>([]); - const [activeSignatory, setActiveSignatory] = useState>(); - const [signatoryOptions, setSignatoryOptions] = useState[]>([]); + const [activeSignatory, setActiveSignatory] = useState(); const [transactions, setTransactions] = useState([]); const [activeBalances, setActiveBalances] = useState([]); - const firstAccount = activeStakeAccounts[0]?.value; + const firstAccount = activeStakeAccounts[0] || accounts[0]; const accountIsMultisig = isMultisig(firstAccount); const formFields = accountIsMultisig ? [{ name: 'amount' }, { name: 'destination' }, { name: 'description' }] @@ -72,17 +66,35 @@ const InitOperation = ({ api, chainId, accounts, asset, addressPrefix, onResult const signatoryIds = accountIsMultisig ? firstAccount.signatories.map((s) => s.accountId) : []; const signatoriesBalances = getLiveAssetBalances(signatoryIds, chainId, asset.assetId.toString()); - const signerBalance = signatoriesBalances.find((b) => b.accountId === activeSignatory?.value.accountId); + const signerBalance = signatoriesBalances.find((b) => b.accountId === activeSignatory?.accountId); + + useEffect(() => { + if (accounts.length === 0) return; + + setActiveStakeAccounts(accounts); + }, [accounts.length]); useEffect(() => { const balancesMap = new Map(balances.map((balance) => [balance.accountId, balance])); const newActiveBalances = activeStakeAccounts - .map((a) => balancesMap.get(a.id as AccountId)) + .map((a) => balancesMap.get(a.accountId)) .filter(nonNullable) as AccountBalance[]; setActiveBalances(newActiveBalances); }, [activeStakeAccounts.length, balances]); + const getAccountDropdownOption = (account: Account) => { + const balance = balances.find((b) => b.accountId === account.accountId); + + return getStakeAccountOption(account, { asset, fee, amount, balance, addressPrefix }); + }; + + const getSignatoryDrowdownOption = (account: Account) => { + const balance = signatoriesBalances.find((b) => b.accountId === account.accountId); + + return getSignatoryOption(account, { balance, asset, addressPrefix, fee, deposit }); + }; + useEffect(() => { if (accountIsMultisig || activeBalances.length === 1) { setMinBalance(stakeableAmount(activeBalances[0])); @@ -104,50 +116,16 @@ const InitOperation = ({ api, chainId, accounts, asset, addressPrefix, onResult }, [activeBalances.length, activeSignatory, signerBalance]); useEffect(() => { - const formattedAccounts = accounts.map((account) => { - const balance = balances.find((b) => b.accountId === account.accountId); - - return getStakeAccountOption(account, { asset, fee, amount, balance, addressPrefix }); - }); - - if (formattedAccounts.length === 0) return; - - setStakeAccounts(formattedAccounts); - }, [amount, fee, balances, accounts.length]); - - useEffect(() => { - if (!accountIsMultisig) return; - - const signerOptions = dbAccounts.reduce[]>((acc, signer) => { - const isWatchOnly = signer.signingType === SigningType.WATCH_ONLY; - const signerExist = signatoryIds.includes(signer.accountId); - if (!isWatchOnly && signerExist) { - const balance = signatoriesBalances.find((b) => b.accountId === signer.accountId); - - acc.push(getSignatoryOptions(signer, { addressPrefix, asset, balance })); - } - - return acc; - }, []); - - if (signerOptions.length === 0) return; - - setSignatoryOptions(signerOptions); - !activeSignatory && setActiveSignatory({ id: signerOptions[0].id, value: signerOptions[0].value }); - }, [accountIsMultisig, dbAccounts.length, signatoriesBalances]); - - useEffect(() => { - if (stakeAccounts.length === 0) return; - - const activeAccounts = stakeAccounts.map(({ id, value }) => ({ id, value })); - setActiveStakeAccounts(activeAccounts); - }, [stakeAccounts.length]); + if (accountIsMultisig) { + setActiveStakeAccounts(accounts); + } + }, [accountIsMultisig, firstAccount?.accountId]); useEffect(() => { const maxValidators = getMaxValidators(api); - const bondPayload = activeStakeAccounts.map(({ id }) => { - const address = toAddress(id, { prefix: addressPrefix }); + const bondPayload = activeStakeAccounts.map(({ accountId }) => { + const address = toAddress(accountId, { prefix: addressPrefix }); const commonPayload = { chainId, address }; const bondTx = { @@ -177,7 +155,7 @@ const InitOperation = ({ api, chainId, accounts, asset, addressPrefix, onResult }, [activeStakeAccounts.length, amount]); const submitBond = (data: { amount: string; destination?: string; description?: string }) => { - const selectedAccountIds = activeStakeAccounts.map((a) => a.id); + const selectedAccountIds = activeStakeAccounts.map((a) => a.accountId); const selectedAccounts = accounts.filter((account) => selectedAccountIds.includes(account.accountId)); onResult({ @@ -186,7 +164,7 @@ const InitOperation = ({ api, chainId, accounts, asset, addressPrefix, onResult destination: data.destination || '', ...(accountIsMultisig && { description: data.description || t('transactionMessage.bond', { amount: data.amount, asset: asset.symbol }), - signer: activeSignatory?.value, + signer: activeSignatory, }), }); }; @@ -221,9 +199,9 @@ const InitOperation = ({ api, chainId, accounts, asset, addressPrefix, onResult const canSubmit = !feeLoading && (activeStakeAccounts.length > 0 || Boolean(activeSignatory)); const getActiveAccounts = (): AccountId[] => { - if (!accountIsMultisig) return activeStakeAccounts.map((acc) => acc.id as AccountId); + if (!accountIsMultisig) return activeStakeAccounts.map((acc) => acc.accountId as AccountId); - return activeSignatory ? [activeSignatory.id as AccountId] : []; + return activeSignatory ? [activeSignatory.accountId] : []; }; return ( @@ -239,8 +217,20 @@ const InitOperation = ({ api, chainId, accounts, asset, addressPrefix, onResult validateBalance={validateBalance} validateFee={validateFee} validateDeposit={validateDeposit} + header={({ invalidBalance, invalidFee, invalidDeposit }) => ( + + )} footer={ - - {({ invalidBalance, invalidFee, invalidDeposit }) => - accountIsMultisig ? ( -
    - - {t('multisigOperations.noSignatory')} - - {t('staking.notEnoughBalanceForDepositError')} - -
    - ) : ( -
    - acc.id)} - options={validatorsAccounts} - onChange={setActiveValidatorsAccounts} - /> - - {t('staking.notEnoughBalanceForFeeError')} - -
    - )} - + /> ); }; diff --git a/src/renderer/pages/Staking/Operations/Destination/Destination.test.tsx b/src/renderer/pages/Staking/Operations/Destination/Destination.test.tsx index e6b82b81ad..3cc19b4235 100644 --- a/src/renderer/pages/Staking/Operations/Destination/Destination.test.tsx +++ b/src/renderer/pages/Staking/Operations/Destination/Destination.test.tsx @@ -56,23 +56,13 @@ jest.mock('./InitOperation/InitOperation', () => ({ onResult }: any) => { }); jest.mock('../components/index', () => ({ - Confirmation: ({ onResult }: any) => mockButton('to scan', onResult), - Signing: ({ onResult }: any) => mockButton('to submit', onResult), + Confirmation: ({ onResult }: any) => mockButton('to sign', onResult), Submit: () => 'finish', })); -jest.mock( - '@renderer/components/common/Scanning/ScanMultiframeQr', - () => - ({ onResult }: any) => - mockButton('to sign', onResult), -); -jest.mock( - '@renderer/components/common/Scanning/ScanSingleframeQr', - () => - ({ onResult }: any) => - mockButton('to sign', onResult), -); +jest.mock('@renderer/features/operation', () => ({ + Signing: ({ onResult }: any) => mockButton('to submit', onResult), +})); describe('pages/Staking/Destination', () => { test('should render component', async () => { @@ -92,9 +82,6 @@ describe('pages/Staking/Destination', () => { let nextButton = screen.getByRole('button', { name: 'to confirm' }); await act(async () => nextButton.click()); - nextButton = screen.getByRole('button', { name: 'to scan' }); - await act(async () => nextButton.click()); - nextButton = screen.getByRole('button', { name: 'to sign' }); await act(async () => nextButton.click()); diff --git a/src/renderer/pages/Staking/Operations/Destination/Destination.tsx b/src/renderer/pages/Staking/Operations/Destination/Destination.tsx index 06727310bd..44b35c00b5 100644 --- a/src/renderer/pages/Staking/Operations/Destination/Destination.tsx +++ b/src/renderer/pages/Staking/Operations/Destination/Destination.tsx @@ -7,20 +7,18 @@ import { RewardsDestination } from '@renderer/entities/staking'; import { useI18n, useNetworkContext, Paths } from '@renderer/app/providers'; import { Address, ChainId, HexString, AccountId } from '@renderer/domain/shared-kernel'; import { Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; -import { Confirmation, Signing, Submit, NoAsset } from '../components'; +import { Confirmation, Submit, NoAsset } from '../components'; import InitOperation, { DestinationResult } from './InitOperation/InitOperation'; -import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { useToggle } from '@renderer/shared/lib/hooks'; import { MultisigAccount, isMultisig, Account, useAccount } from '@renderer/entities/account'; import { DestinationType } from '../common/types'; import { BaseModal, Button, Loader } from '@renderer/shared/ui'; -import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; -import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; +import { Signing } from '@renderer/features/operation'; const enum Step { INIT, CONFIRMATION, - SCANNING, SIGNING, SUBMIT, } @@ -44,7 +42,6 @@ export const Destination = () => { const [multisigTx, setMultisigTx] = useState(); const [transactions, setTransactions] = useState([]); const [unsignedTransactions, setUnsignedTransactions] = useState([]); - const [txPayloads, setTxPayloads] = useState([]); const [accounts, setAccounts] = useState([]); const [txAccounts, setTxAccounts] = useState([]); @@ -63,7 +60,6 @@ export const Destination = () => { }, [activeAccounts.length]); const connection = connections[chainId]; - const [countdown, resetCountdown] = useCountdown(connection?.api); if (!connection || accountIds.length === 0) { return ; @@ -182,12 +178,8 @@ export const Destination = () => { }; }; - const onScanResult = (unsigned: UnsignedTransaction[]) => { + const onSignResult = (signatures: HexString[], unsigned: UnsignedTransaction[]) => { setUnsignedTransactions(unsigned); - setActiveStep(Step.SIGNING); - }; - - const onSignResult = (signatures: HexString[]) => { setSignatures(signatures); setActiveStep(Step.SUBMIT); }; @@ -217,56 +209,21 @@ export const Destination = () => { description={description} transaction={transactions[0]} multisigTx={multisigTx} - onResult={() => setActiveStep(Step.SCANNING)} + onResult={() => setActiveStep(Step.SIGNING)} onGoBack={goToPrevStep} {...explorersProps} /> )} - {activeStep === Step.SCANNING && ( -
    - {transactions.length > 1 ? ( - setActiveStep(Step.CONFIRMATION)} - onResetCountdown={resetCountdown} - onResult={(unsignedTx, payloads) => { - onScanResult(unsignedTx); - setTxPayloads(payloads); - }} - /> - ) : ( - setActiveStep(Step.CONFIRMATION)} - onResetCountdown={resetCountdown} - onResult={(unsignedTx, txPayload) => { - onScanResult([unsignedTx]); - setTxPayloads([txPayload]); - }} - /> - )} -
    - )} {activeStep === Step.SIGNING && ( 1 ? txAccounts.map((t) => t.accountId) : [(signer || txAccounts[0])?.accountId] - } - multiQr={transactions.length > 1} + chainId={chainId} + api={api} + addressPrefix={addressPrefix} + signatory={signer} + accounts={txAccounts} + transactions={multisigTx ? [multisigTx] : transactions} + onGoBack={() => setActiveStep(Step.CONFIRMATION)} onResult={onSignResult} - onGoBack={() => setActiveStep(Step.SCANNING)} /> )} diff --git a/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.test.tsx index 23256cebdf..733620fee8 100644 --- a/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.test.tsx @@ -37,11 +37,12 @@ jest.mock('@renderer/entities/asset', () => ({ })); jest.mock('../../components', () => ({ - OperationForm: ({ children }: any) => { + OperationForm: ({ header, footer }: any) => { return (
    + {header}

    operationForm

    - {children} + {footer}
    ); }, @@ -52,7 +53,7 @@ describe('pages/Staking/Destination/InitOperation', () => { api: {} as ApiPromise, chainId: '0x123' as ChainId, addressPrefix: 0, - accounts: [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }] as unknown as Account[], + accounts: [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID, walletId: 1 }] as unknown as Account[], asset: { assetId: 1, symbol: 'DOT', precision: 10 } as Asset, onResult: noop, }; diff --git a/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.tsx b/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.tsx index 6dc2b7f4c0..d47bb16f50 100644 --- a/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.tsx +++ b/src/renderer/pages/Staking/Operations/Destination/InitOperation/InitOperation.tsx @@ -1,21 +1,16 @@ import { ApiPromise } from '@polkadot/api'; import { useEffect, useState } from 'react'; -import { Select, MultiSelect, InputHint } from '@renderer/shared/ui'; -import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; import { useI18n } from '@renderer/app/providers'; import { Asset, useBalance, Balance as AccountBalance } from '@renderer/entities/asset'; -import { Address, ChainId, AccountId, SigningType } from '@renderer/domain/shared-kernel'; -import { Transaction, TransactionType } from '@renderer/entities/transaction'; -import { useAccount, Account, isMultisig } from '@renderer/entities/account'; +import { Address, ChainId, AccountId } from '@renderer/domain/shared-kernel'; +import { getOperationErrors, Transaction, TransactionType } from '@renderer/entities/transaction'; +import { Account, isMultisig } from '@renderer/entities/account'; import { toAddress, nonNullable, TEST_ADDRESS } from '@renderer/shared/lib/utils'; import { OperationForm } from '../../components'; -import { - getSignatoryOptions, - validateBalanceForFeeDeposit, - validateBalanceForFee, - getGeneralAccountOption, -} from '../../common/utils'; +import { validateBalanceForFeeDeposit, validateBalanceForFee, getGeneralAccountOption } from '../../common/utils'; +import { OperationFooter, OperationHeader } from '@renderer/features/operation'; +import { getSignatoryOption } from '@renderer/pages/Transfer/common/utils'; export type DestinationResult = { accounts: Account[]; @@ -35,25 +30,19 @@ type Props = { const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult }: Props) => { const { t } = useI18n(); - const { getLiveAccounts } = useAccount(); const { getLiveAssetBalances } = useBalance(); - const dbAccounts = getLiveAccounts(); - const [fee, setFee] = useState(''); const [feeLoading, setFeeLoading] = useState(true); const [deposit, setDeposit] = useState(''); - const [destAccounts, setDestAccounts] = useState[]>([]); - const [activeDestAccounts, setActiveDestAccounts] = useState[]>([]); - - const [activeSignatory, setActiveSignatory] = useState>(); - const [signatoryOptions, setSignatoryOptions] = useState[]>([]); + const [activeDestAccounts, setActiveDestAccounts] = useState([]); + const [activeSignatory, setActiveSignatory] = useState(); const [transactions, setTransactions] = useState([]); const [activeBalances, setActiveBalances] = useState([]); - const firstAccount = activeDestAccounts[0]?.value; + const firstAccount = activeDestAccounts[0] || accounts[0]; const accountIsMultisig = isMultisig(firstAccount); const formFields = accountIsMultisig ? [{ name: 'destination' }, { name: 'description' }] : [{ name: 'destination' }]; @@ -62,59 +51,31 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult const signatoryIds = accountIsMultisig ? firstAccount.signatories.map((s) => s.accountId) : []; const signatoriesBalances = getLiveAssetBalances(signatoryIds, chainId, asset.assetId.toString()); - const signerBalance = signatoriesBalances.find((b) => b.accountId === activeSignatory?.value.accountId); + const signerBalance = signatoriesBalances.find((b) => b.accountId === activeSignatory?.accountId); + + useEffect(() => { + if (accounts.length === 0) return; + + setActiveDestAccounts(accounts); + }, [accounts.length]); useEffect(() => { const balancesMap = new Map(balances.map((balance) => [balance.accountId, balance])); const newActiveBalances = activeDestAccounts - .map((a) => balancesMap.get(a.id as AccountId)) + .map((a) => balancesMap.get(a.accountId)) .filter(nonNullable) as AccountBalance[]; setActiveBalances(newActiveBalances); }, [activeDestAccounts.length, balances]); useEffect(() => { - const formattedAccounts = accounts.map((account) => { - const balance = balances.find((b) => b.accountId === account.accountId); - - return getGeneralAccountOption(account, { asset, fee, balance, addressPrefix }); - }); - - if (formattedAccounts.length === 0) return; - - setDestAccounts(formattedAccounts); - }, [fee, balances, accounts.length]); - - useEffect(() => { - if (!accountIsMultisig) return; - - const signerOptions = dbAccounts.reduce[]>((acc, signer) => { - const isWatchOnly = signer.signingType === SigningType.WATCH_ONLY; - const signerExist = signatoryIds.includes(signer.accountId); - if (!isWatchOnly && signerExist) { - const balance = signatoriesBalances.find((b) => b.accountId === signer.accountId); - - acc.push(getSignatoryOptions(signer, { addressPrefix, asset, balance })); - } - - return acc; - }, []); - - if (signerOptions.length === 0) return; - - setSignatoryOptions(signerOptions); - setActiveSignatory({ id: signerOptions[0].id, value: signerOptions[0].value }); - }, [accountIsMultisig, dbAccounts.length, signatoriesBalances.length]); - - useEffect(() => { - if (destAccounts.length === 0) return; - - const activeAccounts = destAccounts.map(({ id, value }) => ({ id, value })); - setActiveDestAccounts(activeAccounts); - }, [destAccounts.length]); + if (accountIsMultisig) { + setActiveDestAccounts(accounts); + } + }, [accountIsMultisig, firstAccount?.accountId]); useEffect(() => { - const newTransactions = activeDestAccounts.map(({ value }) => ({ + const newTransactions = activeDestAccounts.map((value) => ({ chainId, address: toAddress(value.accountId, { prefix: addressPrefix }), type: TransactionType.DESTINATION, @@ -124,8 +85,20 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult setTransactions(newTransactions); }, [activeDestAccounts.length]); + const getAccountDropdownOption = (account: Account) => { + const balance = balances.find((b) => b.accountId === account.accountId); + + return getGeneralAccountOption(account, { asset, fee, balance, addressPrefix }); + }; + + const getSignatoryDrowdownOption = (account: Account) => { + const balance = signatoriesBalances.find((b) => b.accountId === account.accountId); + + return getSignatoryOption(account, { balance, asset, addressPrefix, fee, deposit }); + }; + const submitDestination = (data: { destination?: string; description?: string }) => { - const selectedAccountIds = activeDestAccounts.map((stake) => stake.id); + const selectedAccountIds = activeDestAccounts.map((stake) => stake.accountId); const selectedAccounts = accounts.filter((account) => selectedAccountIds.includes(account.accountId)); onResult({ @@ -137,7 +110,7 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult t('transactionMessage.destination', { address: data.destination || t('transactionMessage.restakeDestination'), }), - signer: activeSignatory?.value, + signer: activeSignatory, }), }); }; @@ -160,9 +133,9 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult }; const getActiveAccounts = (): AccountId[] => { - if (!accountIsMultisig) return activeDestAccounts.map((acc) => acc.id as AccountId); + if (!accountIsMultisig) return activeDestAccounts.map((acc) => acc.accountId); - return activeSignatory ? [activeSignatory.id as AccountId] : []; + return activeSignatory ? [activeSignatory.accountId] : []; }; const isValidFee = validateFee(); @@ -179,8 +152,20 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult addressPrefix={addressPrefix} fields={formFields} asset={asset} + header={ + + } footer={ - } onSubmit={submitDestination} - > - {accountIsMultisig ? ( -
    - - {t('multisigOperations.noSignatory')} -
    - ) : ( - acc.id)} - options={redeemAccounts} - onChange={setActiveRedeemAccounts} - /> - ) - } - + /> ); }; diff --git a/src/renderer/pages/Staking/Operations/Redeem/Redeem.tsx b/src/renderer/pages/Staking/Operations/Redeem/Redeem.tsx index 1a54f66cec..8e264a02e9 100644 --- a/src/renderer/pages/Staking/Operations/Redeem/Redeem.tsx +++ b/src/renderer/pages/Staking/Operations/Redeem/Redeem.tsx @@ -7,18 +7,16 @@ import { ChainId, HexString, AccountId, Address } from '@renderer/domain/shared- import { Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; import { useAccount, Account, MultisigAccount, isMultisig } from '@renderer/entities/account'; import InitOperation, { RedeemResult } from './InitOperation/InitOperation'; -import { Confirmation, Signing, Submit, NoAsset } from '../components'; +import { Confirmation, Submit, NoAsset } from '../components'; import { getRelaychainAsset, toAddress, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; -import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { useToggle } from '@renderer/shared/lib/hooks'; import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; import { BaseModal, Button, Loader } from '@renderer/shared/ui'; -import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; -import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; +import { Signing } from '@renderer/features/operation'; const enum Step { INIT, CONFIRMATION, - SCANNING, SIGNING, SUBMIT, } @@ -47,7 +45,6 @@ export const Redeem = () => { const [multisigTx, setMultisigTx] = useState(); const [transactions, setTransactions] = useState([]); const [unsignedTransactions, setUnsignedTransactions] = useState([]); - const [txPayloads, setTxPayloads] = useState([]); const [signatures, setSignatures] = useState([]); @@ -61,8 +58,6 @@ export const Redeem = () => { const { api, explorers, addressPrefix, assets, name } = connections[chainId]; const asset = getRelaychainAsset(assets); - const [countdown, resetCountdown] = useCountdown(api); - useEffect(() => { const selectedAccounts = dbAccounts.reduce((acc, account) => { const accountExists = account.id && accountIds.includes(account.id.toString()); @@ -182,12 +177,8 @@ export const Redeem = () => { setActiveStep(Step.CONFIRMATION); }; - const onScanResult = (unsigned: UnsignedTransaction[]) => { + const onSignResult = (signatures: HexString[], unsigned: UnsignedTransaction[]) => { setUnsignedTransactions(unsigned); - setActiveStep(Step.SIGNING); - }; - - const onSignResult = (signatures: HexString[]) => { setSignatures(signatures); setActiveStep(Step.SUBMIT); }; @@ -219,56 +210,21 @@ export const Redeem = () => { description={description} transaction={transactions[0]} multisigTx={multisigTx} - onResult={() => setActiveStep(Step.SCANNING)} + onResult={() => setActiveStep(Step.SIGNING)} onGoBack={goToPrevStep} {...explorersProps} /> )} - {activeStep === Step.SCANNING && ( -
    - {transactions.length > 1 ? ( - setActiveStep(Step.CONFIRMATION)} - onResetCountdown={resetCountdown} - onResult={(unsignedTx, payloads) => { - onScanResult(unsignedTx); - setTxPayloads(payloads); - }} - /> - ) : ( - setActiveStep(Step.CONFIRMATION)} - onResetCountdown={resetCountdown} - onResult={(unsignedTx, txPayload) => { - onScanResult([unsignedTx]); - setTxPayloads([txPayload]); - }} - /> - )} -
    - )} {activeStep === Step.SIGNING && ( 1 ? txAccounts.map((t) => t.accountId) : [(signer || txAccounts[0])?.accountId] - } - multiQr={transactions.length > 1} + chainId={chainId} + api={api} + addressPrefix={addressPrefix} + signatory={signer} + accounts={txAccounts} + transactions={multisigTx ? [multisigTx] : transactions} + onGoBack={() => setActiveStep(Step.CONFIRMATION)} onResult={onSignResult} - onGoBack={() => setActiveStep(Step.SCANNING)} /> )} diff --git a/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.test.tsx index 83d9781739..9b6a6baa6f 100644 --- a/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.test.tsx @@ -46,11 +46,11 @@ jest.mock('@renderer/entities/asset', () => ({ })); jest.mock('../../components', () => ({ - OperationForm: ({ children }: any) => { + OperationForm: ({ header }: any) => { return (

    operationForm

    - {children({ invalidBalance: false, invalidFee: false, invalidDeposit: false })} + {header({ invalidBalance: false, invalidFee: false, invalidDeposit: false })}
    ); }, @@ -61,7 +61,7 @@ describe('pages/Staking/Restake/InitOperation', () => { api: {} as ApiPromise, chainId: '0x123' as ChainId, addressPrefix: 0, - accounts: [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }] as unknown as Account[], + accounts: [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID, walletId: 1 }] as unknown as Account[], asset: { assetId: 1, symbol: 'DOT', precision: 10 } as Asset, onResult: noop, }; diff --git a/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.tsx b/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.tsx index 33c9cc10d1..54915d63a2 100644 --- a/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.tsx +++ b/src/renderer/pages/Staking/Operations/Restake/InitOperation/InitOperation.tsx @@ -2,23 +2,22 @@ import { ApiPromise } from '@polkadot/api'; import { BN } from '@polkadot/util'; import { useEffect, useState } from 'react'; -import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; import { useI18n } from '@renderer/app/providers'; import { Asset, useBalance, Balance as AccountBalance } from '@renderer/entities/asset'; -import { ChainId, AccountId, SigningType } from '@renderer/domain/shared-kernel'; +import { ChainId, AccountId } from '@renderer/domain/shared-kernel'; import { Transaction, TransactionType } from '@renderer/entities/transaction'; -import { useAccount, Account, isMultisig } from '@renderer/entities/account'; +import { Account, isMultisig } from '@renderer/entities/account'; import { formatAmount, unlockingAmount, toAddress, nonNullable } from '@renderer/shared/lib/utils'; import { StakingMap, useStakingData } from '@renderer/entities/staking'; import { OperationForm } from '../../components'; -import { Select, MultiSelect, InputHint } from '@renderer/shared/ui'; import { getRestakeAccountOption, validateRestake, validateBalanceForFee, - getSignatoryOptions, validateBalanceForFeeDeposit, } from '../../common/utils'; +import { getSignatoryOption } from '@renderer/pages/Transfer/common/utils'; +import { OperationError, OperationFooter, OperationHeader } from '@renderer/features/operation'; export type RestakeResult = { accounts: Account[]; @@ -38,12 +37,9 @@ type Props = { const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult }: Props) => { const { t } = useI18n(); - const { getLiveAccounts } = useAccount(); const { subscribeStaking } = useStakingData(); const { getLiveAssetBalances } = useBalance(); - const dbAccounts = getLiveAccounts(); - const [fee, setFee] = useState(''); const [feeLoading, setFeeLoading] = useState(true); const [amount, setAmount] = useState(''); @@ -52,16 +48,13 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult const [minBalance, setMinBalance] = useState('0'); - const [restakeAccounts, setRestakeAccounts] = useState[]>([]); - const [activeRestakeAccounts, setActiveRestakeAccounts] = useState[]>([]); - - const [activeSignatory, setActiveSignatory] = useState>(); - const [signatoryOptions, setSignatoryOptions] = useState[]>([]); + const [activeRestakeAccounts, setActiveRestakeAccounts] = useState([]); + const [activeSignatory, setActiveSignatory] = useState(); const [transactions, setTransactions] = useState([]); const [activeBalances, setActiveBalances] = useState([]); - const firstAccount = activeRestakeAccounts[0]?.value; + const firstAccount = activeRestakeAccounts[0] || accounts[0]; const accountIsMultisig = isMultisig(firstAccount); const formFields = accountIsMultisig ? [{ name: 'amount' }, { name: 'description' }] : [{ name: 'amount' }]; @@ -70,10 +63,16 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult const signatoryIds = accountIsMultisig ? firstAccount.signatories.map((s) => s.accountId) : []; const signatoriesBalances = getLiveAssetBalances(signatoryIds, chainId, asset.assetId.toString()); - const signerBalance = signatoriesBalances.find((b) => b.accountId === activeSignatory?.value.accountId); + const signerBalance = signatoriesBalances.find((b) => b.accountId === activeSignatory?.accountId); useEffect(() => { - const addresses = activeRestakeAccounts.map((stake) => toAddress(stake.id, { prefix: addressPrefix })); + if (accounts.length === 0) return; + + setActiveRestakeAccounts(accounts); + }, [accounts.length]); + + useEffect(() => { + const addresses = activeRestakeAccounts.map((stake) => toAddress(stake.accountId, { prefix: addressPrefix })); let unsubStaking: () => void | undefined; (async () => { @@ -88,7 +87,7 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult useEffect(() => { const balancesMap = new Map(balances.map((balance) => [balance.accountId, balance])); const newActiveBalances = activeRestakeAccounts - .map((a) => balancesMap.get(a.id as AccountId)) + .map((a) => balancesMap.get(a.accountId)) .filter(nonNullable) as AccountBalance[]; setActiveBalances(newActiveBalances); @@ -98,7 +97,7 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult if (!Object.keys(staking).length) return; const stakedBalances = activeRestakeAccounts.map((a) => { - const address = toAddress(a.id, { prefix: addressPrefix }); + const address = toAddress(a.accountId, { prefix: addressPrefix }); return unlockingAmount(staking[address]?.unlocking); }); @@ -113,51 +112,11 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult }, [activeRestakeAccounts.length, staking]); useEffect(() => { - const formattedAccounts = accounts.map((account) => { - const balance = balances.find((b) => b.accountId === account.accountId); - const address = toAddress(account.accountId, { prefix: addressPrefix }); - const stake = staking[address]; - - return getRestakeAccountOption(account, { balance, stake, asset, fee, addressPrefix, amount }); - }); - - setRestakeAccounts(formattedAccounts); - }, [staking, amount, fee, balances, accounts.length]); - - useEffect(() => { - if (!accountIsMultisig) return; - - const signerOptions = dbAccounts.reduce[]>((acc, signer) => { - const isWatchOnly = signer.signingType === SigningType.WATCH_ONLY; - const signerExist = signatoryIds.includes(signer.accountId); - if (!isWatchOnly && signerExist) { - const balance = signatoriesBalances.find((b) => b.accountId === signer.accountId); - - acc.push(getSignatoryOptions(signer, { addressPrefix, asset, balance })); - } - - return acc; - }, []); - - if (signerOptions.length === 0) return; - - setSignatoryOptions(signerOptions); - setActiveSignatory({ id: signerOptions[0].id, value: signerOptions[0].value }); - }, [accountIsMultisig, dbAccounts.length, signatoriesBalances.length]); - - useEffect(() => { - if (restakeAccounts.length === 0) return; - - const activeAccounts = restakeAccounts.map(({ id, value }) => ({ id, value })); - setActiveRestakeAccounts(activeAccounts); - }, [restakeAccounts.length]); - - useEffect(() => { - const newTransactions = activeRestakeAccounts.map(({ value }) => { + const newTransactions = activeRestakeAccounts.map((account) => { return { chainId, type: TransactionType.RESTAKE, - address: toAddress(value.accountId, { prefix: addressPrefix }), + address: toAddress(account.accountId, { prefix: addressPrefix }), args: { value: formatAmount(amount, asset.precision) }, }; }); @@ -165,8 +124,22 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult setTransactions(newTransactions); }, [activeRestakeAccounts.length, amount]); + const getAccountDropdownOption = (account: Account) => { + const balance = balances.find((b) => b.accountId === account.accountId); + const address = toAddress(account.accountId, { prefix: addressPrefix }); + const stake = staking[address]; + + return getRestakeAccountOption(account, { balance, stake, asset, fee, addressPrefix, amount }); + }; + + const getSignatoryDrowdownOption = (account: Account) => { + const balance = signatoriesBalances.find((b) => b.accountId === account.accountId); + + return getSignatoryOption(account, { balance, asset, addressPrefix, fee, deposit }); + }; + const submitRestake = (data: { amount: string; description?: string }) => { - const selectedAccountIds = activeRestakeAccounts.map((stake) => stake.id); + const selectedAccountIds = activeRestakeAccounts.map((stake) => stake.accountId); const selectedAccounts = accounts.filter((account) => selectedAccountIds.includes(account.accountId)); onResult({ @@ -174,14 +147,14 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult amount: formatAmount(data.amount, asset.precision), ...(accountIsMultisig && { description: data.description || t('transactionMessage.restake', { amount: data.amount, asset: asset.symbol }), - signer: activeSignatory?.value, + signer: activeSignatory, }), }); }; const validateBalance = (amount: string): boolean => { return activeRestakeAccounts.every((a) => { - const address = toAddress(a.id, { prefix: addressPrefix }); + const address = toAddress(a.accountId, { prefix: addressPrefix }); return validateRestake(staking[address] || '0', amount, asset.precision); }); @@ -211,9 +184,9 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult }; const getActiveAccounts = (): AccountId[] => { - if (!accountIsMultisig) return activeRestakeAccounts.map((acc) => acc.id as AccountId); + if (!accountIsMultisig) return activeRestakeAccounts.map((acc) => acc.accountId); - return activeSignatory ? [activeSignatory.id as AccountId] : []; + return activeSignatory ? [activeSignatory.accountId] : []; }; const canSubmit = !feeLoading && (activeRestakeAccounts.length > 0 || Boolean(activeSignatory)); @@ -231,8 +204,20 @@ const InitOperation = ({ api, chainId, accounts, addressPrefix, asset, onResult validateBalance={validateBalance} validateFee={validateFee} validateDeposit={validateDeposit} + header={({ invalidBalance, invalidFee, invalidDeposit }) => ( + + )} footer={ - - {({ invalidBalance, invalidFee, invalidDeposit }) => - accountIsMultisig ? ( -
    - - {t('multisigOperations.noSignatory')} -
    - ) : ( - acc.id)} - options={stakeMoreAccounts} - onChange={setActiveStakeMoreAccounts} - /> - ) - } - + /> ); }; diff --git a/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.test.tsx b/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.test.tsx index 9d60f303f2..d2bc5e29e8 100644 --- a/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.test.tsx +++ b/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.test.tsx @@ -57,23 +57,14 @@ jest.mock('./InitOperation/InitOperation', () => ({ onResult }: any) => { }); jest.mock('../components/index', () => ({ - Confirmation: ({ onResult }: any) => mockButton('to scan', onResult), + Confirmation: ({ onResult }: any) => mockButton('to sign', onResult), Signing: ({ onResult }: any) => mockButton('to submit', onResult), Submit: () => 'finish', })); -jest.mock( - '@renderer/components/common/Scanning/ScanMultiframeQr', - () => - ({ onResult }: any) => - mockButton('to sign', onResult), -); -jest.mock( - '@renderer/components/common/Scanning/ScanSingleframeQr', - () => - ({ onResult }: any) => - mockButton('to sign', onResult), -); +jest.mock('@renderer/features/operation', () => ({ + Signing: ({ onResult }: any) => mockButton('to submit', onResult), +})); describe('pages/Staking/StakeMore', () => { test('should render component', async () => { @@ -95,9 +86,6 @@ describe('pages/Staking/StakeMore', () => { let nextButton = screen.getByRole('button', { name: 'to confirm' }); await act(async () => nextButton.click()); - nextButton = screen.getByRole('button', { name: 'to scan' }); - await act(async () => nextButton.click()); - nextButton = screen.getByRole('button', { name: 'to sign' }); await act(async () => nextButton.click()); diff --git a/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.tsx b/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.tsx index 4817f17ceb..dbd62aab45 100644 --- a/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.tsx +++ b/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.tsx @@ -6,19 +6,17 @@ import { useI18n, useNetworkContext, Paths } from '@renderer/app/providers'; import { ChainId, HexString, Address, AccountId } from '@renderer/domain/shared-kernel'; import { Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; import InitOperation, { StakeMoreResult } from './InitOperation/InitOperation'; -import { Confirmation, Signing, Submit, NoAsset } from '../components'; +import { Confirmation, Submit, NoAsset } from '../components'; import { getRelaychainAsset, toAddress, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; -import { useCountdown, useToggle } from '@renderer/shared/lib/hooks'; +import { useToggle } from '@renderer/shared/lib/hooks'; import { isMultisig, MultisigAccount, Account, useAccount } from '@renderer/entities/account'; import { Alert, BaseModal, Button, Loader } from '@renderer/shared/ui'; import OperationModalTitle from '@renderer/pages/Operations/components/OperationModalTitle'; -import ScanMultiframeQr from '@renderer/components/common/Scanning/ScanMultiframeQr'; -import ScanSingleframeQr from '@renderer/components/common/Scanning/ScanSingleframeQr'; +import { Signing } from '@renderer/features/operation'; const enum Step { INIT, CONFIRMATION, - SCANNING, SIGNING, SUBMIT, } @@ -43,7 +41,6 @@ export const StakeMore = () => { const [multisigTx, setMultisigTx] = useState(); const [transactions, setTransactions] = useState([]); const [unsignedTransactions, setUnsignedTransactions] = useState([]); - const [txPayloads, setTxPayloads] = useState([]); const [accounts, setAccounts] = useState([]); const [txAccounts, setTxAccounts] = useState([]); @@ -62,7 +59,6 @@ export const StakeMore = () => { }, [activeAccounts.length]); const connection = connections[chainId]; - const [countdown, resetCountdown] = useCountdown(connection?.api); if (!connection || accountIds.length === 0) { return ; @@ -177,12 +173,8 @@ export const StakeMore = () => { setActiveStep(Step.CONFIRMATION); }; - const onScanResult = (unsigned: UnsignedTransaction[]) => { + const onSignResult = (signatures: HexString[], unsigned: UnsignedTransaction[]) => { setUnsignedTransactions(unsigned); - setActiveStep(Step.SIGNING); - }; - - const onSignResult = (signatures: HexString[]) => { setSignatures(signatures); setActiveStep(Step.SUBMIT); }; @@ -215,7 +207,7 @@ export const StakeMore = () => { description={description} amounts={stakeMoreValues} multisigTx={multisigTx} - onResult={() => setActiveStep(Step.SCANNING)} + onResult={() => setActiveStep(Step.SIGNING)} onGoBack={goToPrevStep} {...explorersProps} > @@ -226,51 +218,16 @@ export const StakeMore = () => { )} )} - {activeStep === Step.SCANNING && ( -
    - {transactions.length > 1 ? ( - setActiveStep(Step.CONFIRMATION)} - onResetCountdown={resetCountdown} - onResult={(unsignedTx, payloads) => { - onScanResult(unsignedTx); - setTxPayloads(payloads); - }} - /> - ) : ( - setActiveStep(Step.CONFIRMATION)} - onResetCountdown={resetCountdown} - onResult={(unsignedTx, txPayload) => { - onScanResult([unsignedTx]); - setTxPayloads([txPayload]); - }} - /> - )} -
    - )} {activeStep === Step.SIGNING && ( 1 ? txAccounts.map((t) => t.accountId) : [(signer || txAccounts[0])?.accountId] - } - multiQr={transactions.length > 1} + chainId={chainId} + api={api} + addressPrefix={addressPrefix} + signatory={signer} + accounts={txAccounts} + transactions={multisigTx ? [multisigTx] : transactions} + onGoBack={() => setActiveStep(Step.CONFIRMATION)} onResult={onSignResult} - onGoBack={() => setActiveStep(Step.SCANNING)} /> )} diff --git a/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx b/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx index 362b81a959..b26a21a149 100644 --- a/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx +++ b/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.test.tsx @@ -44,11 +44,11 @@ jest.mock('@renderer/entities/asset', () => ({ })); jest.mock('../../components', () => ({ - OperationForm: ({ children }: any) => { + OperationForm: ({ header }: any) => { return (

    operationForm

    - {children({ invalidBalance: false, invalidFee: false, invalidDeposit: false })} + {header({ invalidBalance: false, invalidFee: false, invalidDeposit: false })}
    ); }, @@ -59,7 +59,7 @@ describe('pages/Staking/Unstake/InitOperation', () => { api: {} as ApiPromise, chainId: '0x123' as ChainId, addressPrefix: 0, - accounts: [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }] as unknown as Account[], + accounts: [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID, walletId: 1 }] as unknown as Account[], asset: { assetId: 1, symbol: 'DOT', precision: 10 } as Asset, onResult: noop, }; diff --git a/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.tsx b/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.tsx index 4d845eabc7..c15899f4ac 100644 --- a/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.tsx +++ b/src/renderer/pages/Staking/Operations/Unstake/InitOperation/InitOperation.tsx @@ -2,23 +2,22 @@ import { ApiPromise } from '@polkadot/api'; import { BN } from '@polkadot/util'; import { useEffect, useState } from 'react'; -import { DropdownOption, DropdownResult } from '@renderer/shared/ui/Dropdowns/common/types'; import { useI18n } from '@renderer/app/providers'; import { Asset, useBalance, Balance as AccountBalance } from '@renderer/entities/asset'; -import { ChainId, AccountId, SigningType } from '@renderer/domain/shared-kernel'; +import { ChainId, AccountId } from '@renderer/domain/shared-kernel'; import { Transaction, TransactionType } from '@renderer/entities/transaction'; -import { useAccount, isMultisig, Account } from '@renderer/entities/account'; +import { isMultisig, Account } from '@renderer/entities/account'; import { formatAmount, nonNullable, toAddress } from '@renderer/shared/lib/utils'; import { StakingMap, useStakingData } from '@renderer/entities/staking'; import { OperationForm } from '@renderer/pages/Staking/Operations/components'; -import { Select, MultiSelect, InputHint } from '@renderer/shared/ui'; import { getUnstakeAccountOption, validateBalanceForFee, validateUnstake, - getSignatoryOptions, validateBalanceForFeeDeposit, } from '../../common/utils'; +import { getSignatoryOption } from '@renderer/pages/Transfer/common/utils'; +import { OperationError, OperationFooter, OperationHeader } from '@renderer/features/operation'; export type UnstakeResult = { accounts: Account[]; @@ -39,12 +38,9 @@ type Props = { const InitOperation = ({ api, chainId, addressPrefix, accounts, asset, onResult }: Props) => { const { t } = useI18n(); - const { getLiveAccounts } = useAccount(); const { subscribeStaking, getMinNominatorBond } = useStakingData(); const { getLiveAssetBalances } = useBalance(); - const dbAccounts = getLiveAccounts(); - const [fee, setFee] = useState(''); const [feeLoading, setFeeLoading] = useState(true); const [deposit, setDeposit] = useState(''); @@ -54,16 +50,13 @@ const InitOperation = ({ api, chainId, addressPrefix, accounts, asset, onResult const [minBalance, setMinBalance] = useState('0'); const [minimumStake, setMinimumStake] = useState('0'); - const [unstakeAccounts, setUnstakeAccounts] = useState[]>([]); - const [activeUnstakeAccounts, setActiveUnstakeAccounts] = useState[]>([]); - - const [activeSignatory, setActiveSignatory] = useState>(); - const [signatoryOptions, setSignatoryOptions] = useState[]>([]); + const [activeUnstakeAccounts, setActiveUnstakeAccounts] = useState([]); + const [activeSignatory, setActiveSignatory] = useState(); const [transactions, setTransactions] = useState([]); const [activeBalances, setActiveBalances] = useState([]); - const firstAccount = activeUnstakeAccounts[0]?.value; + const firstAccount = activeUnstakeAccounts[0] || accounts[0]; const accountIsMultisig = isMultisig(firstAccount); const formFields = accountIsMultisig ? [{ name: 'amount' }, { name: 'description' }] : [{ name: 'amount' }]; @@ -72,14 +65,20 @@ const InitOperation = ({ api, chainId, addressPrefix, accounts, asset, onResult const signatoryIds = accountIsMultisig ? firstAccount.signatories.map((s) => s.accountId) : []; const signatoriesBalances = getLiveAssetBalances(signatoryIds, chainId, asset.assetId.toString()); - const signerBalance = signatoriesBalances.find((b) => b.accountId === activeSignatory?.value.accountId); + const signerBalance = signatoriesBalances.find((b) => b.accountId === activeSignatory?.accountId); useEffect(() => { getMinNominatorBond(api).then(setMinimumStake); }, [api]); useEffect(() => { - const addresses = activeUnstakeAccounts.map((stake) => toAddress(stake.id, { prefix: addressPrefix })); + if (accounts.length === 0) return; + + setActiveUnstakeAccounts(accounts); + }, [accounts.length]); + + useEffect(() => { + const addresses = activeUnstakeAccounts.map((stake) => toAddress(stake.accountId, { prefix: addressPrefix })); let unsubStaking: () => void | undefined; (async () => { @@ -94,7 +93,7 @@ const InitOperation = ({ api, chainId, addressPrefix, accounts, asset, onResult useEffect(() => { const balancesMap = new Map(balances.map((balance) => [balance.accountId, balance])); const newActiveBalances = activeUnstakeAccounts - .map((a) => balancesMap.get(a.id as AccountId)) + .map((a) => balancesMap.get(a.accountId)) .filter(nonNullable) as AccountBalance[]; setActiveBalances(newActiveBalances); @@ -104,7 +103,7 @@ const InitOperation = ({ api, chainId, addressPrefix, accounts, asset, onResult if (!Object.keys(staking).length) return; const stakedBalances = activeUnstakeAccounts.map((a) => { - const address = toAddress(a.id, { prefix: addressPrefix }); + const address = toAddress(a.accountId, { prefix: addressPrefix }); return staking[address]?.active || '0'; }); @@ -119,51 +118,11 @@ const InitOperation = ({ api, chainId, addressPrefix, accounts, asset, onResult }, [activeUnstakeAccounts.length, staking]); useEffect(() => { - const formattedAccounts = accounts.map((account) => { - const balance = balances.find((b) => b.accountId === account.accountId); - const address = toAddress(account.accountId, { prefix: addressPrefix }); - const stake = staking[address]; - - return getUnstakeAccountOption(account, { balance, stake, asset, addressPrefix, fee, amount }); - }); - - setUnstakeAccounts(formattedAccounts); - }, [staking, amount, fee, balances, accounts.length]); - - useEffect(() => { - if (!accountIsMultisig) return; - - const signerOptions = dbAccounts.reduce[]>((acc, signer) => { - const isWatchOnly = signer.signingType === SigningType.WATCH_ONLY; - const signerExist = signatoryIds.includes(signer.accountId); - if (!isWatchOnly && signerExist) { - const balance = signatoriesBalances.find((b) => b.accountId === signer.accountId); - - acc.push(getSignatoryOptions(signer, { addressPrefix, asset, balance })); - } - - return acc; - }, []); - - if (signerOptions.length === 0) return; - - setSignatoryOptions(signerOptions); - setActiveSignatory({ id: signerOptions[0].id, value: signerOptions[0].value }); - }, [accountIsMultisig, dbAccounts.length, signatoriesBalances.length]); - - useEffect(() => { - if (unstakeAccounts.length === 0) return; - - const activeAccounts = unstakeAccounts.map(({ id, value }) => ({ id, value })); - setActiveUnstakeAccounts(activeAccounts); - }, [unstakeAccounts.length]); - - useEffect(() => { - const newTransactions = activeUnstakeAccounts.map(({ value }) => { + const newTransactions = activeUnstakeAccounts.map(({ accountId }) => { return { chainId, type: TransactionType.UNSTAKE, - address: toAddress(value.accountId, { prefix: addressPrefix }), + address: toAddress(accountId, { prefix: addressPrefix }), args: { value: formatAmount(amount, asset.precision) }, }; }); @@ -171,8 +130,22 @@ const InitOperation = ({ api, chainId, addressPrefix, accounts, asset, onResult setTransactions(newTransactions); }, [minBalance, amount]); + const getAccountDropdownOption = (account: Account) => { + const balance = balances.find((b) => b.accountId === account.accountId); + const address = toAddress(account.accountId, { prefix: addressPrefix }); + const stake = staking[address]; + + return getUnstakeAccountOption(account, { balance, stake, asset, addressPrefix, fee, amount }); + }; + + const getSignatoryDrowdownOption = (account: Account) => { + const balance = signatoriesBalances.find((b) => b.accountId === account.accountId); + + return getSignatoryOption(account, { balance, asset, addressPrefix, fee, deposit }); + }; + const submitUnstake = (data: { amount: string; description?: string }) => { - const selectedAccountIds = activeUnstakeAccounts.map((stake) => stake.id); + const selectedAccountIds = activeUnstakeAccounts.map((stake) => stake.accountId); const selectedAccounts = accounts.filter((account) => selectedAccountIds.includes(account.accountId)); const amount = formatAmount(data.amount, asset.precision); @@ -189,14 +162,14 @@ const InitOperation = ({ api, chainId, addressPrefix, accounts, asset, onResult accounts: selectedAccounts, ...(accountIsMultisig && { description: data.description || t('transactionMessage.unstake', { amount: data.amount, asset: asset.symbol }), - signer: activeSignatory?.value, + signer: activeSignatory, }), }); }; const validateBalance = (amount: string): boolean => { return activeUnstakeAccounts.every((a) => { - const address = toAddress(a.id, { prefix: addressPrefix }); + const address = toAddress(a.accountId, { prefix: addressPrefix }); return validateUnstake(staking[address] || '0', amount, asset.precision); }); @@ -226,9 +199,9 @@ const InitOperation = ({ api, chainId, addressPrefix, accounts, asset, onResult }; const getActiveAccounts = (): AccountId[] => { - if (!accountIsMultisig) return activeUnstakeAccounts.map((acc) => acc.id as AccountId); + if (!accountIsMultisig) return activeUnstakeAccounts.map((acc) => acc.accountId); - return activeSignatory ? [activeSignatory.id as AccountId] : []; + return activeSignatory ? [activeSignatory.accountId] : []; }; const canSubmit = !feeLoading && (activeUnstakeAccounts.length > 0 || Boolean(activeSignatory)); @@ -246,8 +219,20 @@ const InitOperation = ({ api, chainId, addressPrefix, accounts, asset, onResult validateBalance={validateBalance} validateFee={validateFee} validateDeposit={validateDeposit} + header={({ invalidBalance, invalidFee, invalidDeposit }) => ( + + )} footer={ - - {({ invalidBalance, invalidFee, invalidDeposit }) => - accountIsMultisig ? ( -
    - - )} - - {isMultisig(activeAccount?.value) && ( - <> - {
      {chain.explorers?.map(({ name, account }) => (
    • - +
    • diff --git a/src/renderer/widgets/SendAssetModal/index.ts b/src/renderer/widgets/SendAssetModal/index.ts new file mode 100644 index 0000000000..a676c4954e --- /dev/null +++ b/src/renderer/widgets/SendAssetModal/index.ts @@ -0,0 +1 @@ +export { SendAssetModal } from './ui/SendAssetModal'; diff --git a/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx b/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx new file mode 100644 index 0000000000..dc9c844769 --- /dev/null +++ b/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx @@ -0,0 +1,166 @@ +import { useState } from 'react'; +import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; + +import { useI18n, useNetworkContext } from '@renderer/app/providers'; +import { HexString } from '@renderer/domain/shared-kernel'; +import { Transaction, useTransaction, validateBalance } from '@renderer/entities/transaction'; +import { Account, MultisigAccount } from '@renderer/entities/account'; +import { BaseModal, Button, Loader } from '@renderer/shared/ui'; +import { Confirmation, InitOperation, Submit } from './components/ActionSteps'; +import { Signing } from '@renderer/features/operation'; +import { Asset, useBalance } from '@renderer/entities/asset'; +import { OperationTitle } from '@renderer/components/common'; +import { Chain } from '@renderer/entities/chain'; +import { DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; +import { useToggle } from '@renderer/shared/lib/hooks'; + +const enum Step { + INIT, + CONFIRMATION, + SIGNING, + SUBMIT, +} + +type Props = { + chain: Chain; + asset: Asset; + onClose: () => void; +}; + +export const SendAssetModal = ({ chain, asset, onClose }: Props) => { + const { t } = useI18n(); + const { getBalance } = useBalance(); + const { getTransactionFee } = useTransaction(); + const { connections } = useNetworkContext(); + + const [isModalOpen, toggleIsModalOpen] = useToggle(true); + const [activeStep, setActiveStep] = useState(Step.INIT); + const [account, setAccount] = useState({} as Account); + const [signatory, setSignatory] = useState(); + const [description, setDescription] = useState(''); + const [transferTx, setTransferTx] = useState({} as Transaction); + const [multisigTx, setMultisigTx] = useState(); + const [unsignedTx, setUnsignedTx] = useState({} as UnsignedTransaction); + const [signature, setSignature] = useState('0x0'); + + const connection = connections[chain.chainId]; + const transaction = multisigTx || transferTx; + + const { api, assets, addressPrefix, explorers } = connection; + + const onInitResult = (transferTx: Transaction, multisig?: { multisigTx: Transaction; description: string }) => { + setTransferTx(transferTx); + setMultisigTx(multisig?.multisigTx || undefined); + setDescription(multisig?.description || ''); + setActiveStep(Step.CONFIRMATION); + }; + + const checkBalance = () => + validateBalance({ + api, + transaction, + chainId: chain.chainId, + assetId: asset.assetId.toString(), + getBalance, + getTransactionFee, + }); + + const onConfirmResult = () => { + setActiveStep(Step.SIGNING); + }; + + const onSignResult = (signature: HexString[], tx: UnsignedTransaction[]) => { + setUnsignedTx(tx[0]); + setSignature(signature[0]); + setActiveStep(Step.SUBMIT); + }; + + const closeSendModal = () => { + toggleIsModalOpen(); + setTimeout(onClose, DEFAULT_TRANSITION); + }; + + const commonProps = { explorers, addressPrefix }; + + if (activeStep === Step.SUBMIT) { + return api ? ( + + ) : ( +
      + +
      + ); + } + + return ( + } + contentClass={activeStep === Step.SIGNING ? '' : undefined} + panelClass="w-[440px]" + headerClass="py-3 px-5 max-w-[440px]" + onClose={closeSendModal} + > + {!api?.isConnected ? ( +
      + + +
      + ) : ( + <> + {activeStep === Step.INIT && asset && ( + + )} + {activeStep === Step.CONFIRMATION && ( + setActiveStep(Step.INIT)} + onResult={onConfirmResult} + /> + )} + {activeStep === Step.SIGNING && ( + setActiveStep(Step.CONFIRMATION)} + onResult={onSignResult} + /> + )} + + )} +
      + ); +}; diff --git a/src/renderer/pages/Transfer/common/constants.ts b/src/renderer/widgets/SendAssetModal/ui/common/constants.ts similarity index 100% rename from src/renderer/pages/Transfer/common/constants.ts rename to src/renderer/widgets/SendAssetModal/ui/common/constants.ts diff --git a/src/renderer/pages/Transfer/common/utils.tsx b/src/renderer/widgets/SendAssetModal/ui/common/utils.tsx similarity index 100% rename from src/renderer/pages/Transfer/common/utils.tsx rename to src/renderer/widgets/SendAssetModal/ui/common/utils.tsx diff --git a/src/renderer/pages/Transfer/components/ActionSteps/Confirmation.tsx b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx similarity index 94% rename from src/renderer/pages/Transfer/components/ActionSteps/Confirmation.tsx rename to src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx index 94c34bf262..8f3896708f 100644 --- a/src/renderer/pages/Transfer/components/ActionSteps/Confirmation.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx @@ -22,7 +22,16 @@ type Props = { onBack?: () => void; }; -const Confirmation = ({ account, connection, transaction, signatory, description, feeTx, onResult, onBack }: Props) => { +export const Confirmation = ({ + account, + connection, + transaction, + signatory, + description, + feeTx, + onResult, + onBack, +}: Props) => { const { t } = useI18n(); const { getWallet } = useWallet(); @@ -87,5 +96,3 @@ const Confirmation = ({ account, connection, transaction, signatory, description
    ); }; - -export default Confirmation; diff --git a/src/renderer/pages/Transfer/components/ActionSteps/InitOperation.tsx b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx similarity index 98% rename from src/renderer/pages/Transfer/components/ActionSteps/InitOperation.tsx rename to src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx index bda72aeb85..a5bb41ec80 100644 --- a/src/renderer/pages/Transfer/components/ActionSteps/InitOperation.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx @@ -23,7 +23,7 @@ type Props = { onResult: (transferTx: Transaction, multisig?: { multisigTx: Transaction; description: string }) => void; }; -const InitOperation = ({ +export const InitOperation = ({ api, chainId, network, @@ -63,6 +63,7 @@ const InitOperation = ({ useEffect(() => { setActiveAccount(accounts[0]); + onAccountChange(accounts[0]); }, [accounts.length, accounts[0]?.accountId]); useEffect(() => { @@ -85,7 +86,6 @@ const InitOperation = ({ }; const changeAccount = (account: Account | MultisigAccount) => { - console.log(account); onAccountChange(account); setActiveAccount(account); }; @@ -141,5 +141,3 @@ const InitOperation = ({ ); }; - -export default InitOperation; diff --git a/src/renderer/pages/Transfer/components/ActionSteps/Submit.tsx b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Submit.tsx similarity index 97% rename from src/renderer/pages/Transfer/components/ActionSteps/Submit.tsx rename to src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Submit.tsx index 354f9c74d0..fede8e5ff8 100644 --- a/src/renderer/pages/Transfer/components/ActionSteps/Submit.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Submit.tsx @@ -33,7 +33,7 @@ type Props = { onClose: () => void; }; -const Submit = ({ api, tx, multisigTx, account, unsignedTx, signature, description, onClose }: Props) => { +export const Submit = ({ api, tx, multisigTx, account, unsignedTx, signature, description, onClose }: Props) => { const { t } = useI18n(); const navigate = useNavigate(); @@ -174,5 +174,3 @@ const Submit = ({ api, tx, multisigTx, account, unsignedTx, signature, descripti ); }; - -export default Submit; diff --git a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/index.ts b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/index.ts new file mode 100644 index 0000000000..530a8b88b1 --- /dev/null +++ b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/index.ts @@ -0,0 +1,3 @@ +export { InitOperation } from './InitOperation'; +export { Confirmation } from './Confirmation'; +export { Submit } from './Submit'; diff --git a/src/renderer/pages/Transfer/components/Details.tsx b/src/renderer/widgets/SendAssetModal/ui/components/Details.tsx similarity index 100% rename from src/renderer/pages/Transfer/components/Details.tsx rename to src/renderer/widgets/SendAssetModal/ui/components/Details.tsx diff --git a/src/renderer/pages/Transfer/components/TransferForm.tsx b/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx similarity index 97% rename from src/renderer/pages/Transfer/components/TransferForm.tsx rename to src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx index 9b259b2f86..3510a6b75a 100644 --- a/src/renderer/pages/Transfer/components/TransferForm.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx @@ -1,24 +1,24 @@ import { BN } from '@polkadot/util'; import { ReactNode, useEffect, useState } from 'react'; -import { Controller, useForm, SubmitHandler } from 'react-hook-form'; +import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import { ApiPromise } from '@polkadot/api'; import { Trans } from 'react-i18next'; +import { AmountInput, Button, Icon, Identicon, Input, InputHint } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { Asset, AssetType, useBalance } from '@renderer/entities/asset'; +import { MultisigTxInitStatus, Transaction, TransactionType, useTransaction } from '@renderer/entities/transaction'; +import { AccountId, Address, ChainId } from '@renderer/domain/shared-kernel'; +import { useMultisigTx } from '@renderer/entities/multisig'; +import { Account, isMultisig, MultisigAccount } from '@renderer/entities/account'; import { + formatAmount, + getAssetId, toAccountId, - validateAddress, toAddress, - formatAmount, transferableAmount, - getAssetId, + validateAddress, } from '@renderer/shared/lib/utils'; -import { Icon, Identicon, Button, AmountInput, Input, InputHint } from '@renderer/shared/ui'; -import { useI18n } from '@renderer/app/providers'; -import { Asset, AssetType, useBalance } from '@renderer/entities/asset'; -import { Transaction, MultisigTxInitStatus, TransactionType, useTransaction } from '@renderer/entities/transaction'; -import { Address, ChainId, AccountId } from '@renderer/domain/shared-kernel'; -import { useMultisigTx } from '@renderer/entities/multisig'; -import { MultisigAccount, Account, isMultisig } from '@renderer/entities/account'; const DESCRIPTION_MAX_LENGTH = 120; diff --git a/src/renderer/widgets/index.ts b/src/renderer/widgets/index.ts index 8f83dec67d..cadca209ca 100644 --- a/src/renderer/widgets/index.ts +++ b/src/renderer/widgets/index.ts @@ -1 +1,3 @@ export * from './ManageContactModal'; +export * from './ReceiveAssetModal'; +export * from './SendAssetModal'; From 2a6c612299dd89785e731aed8ea35b077e85e64a Mon Sep 17 00:00:00 2001 From: Aleksandr Makhnev Date: Tue, 5 Sep 2023 19:38:37 +0500 Subject: [PATCH 11/34] feat: cache metadata (#1039) --- .../entities/network/lib/common/constants.ts | 2 + .../entities/network/lib/common/types.ts | 24 ++++++- .../entities/network/lib/metadataService.ts | 63 +++++++++++++++++++ .../entities/network/lib/networkService.ts | 25 ++++++-- .../network/lib/provider/CachedProvider.ts | 25 ++++++++ .../shared/api/storage/common/types.ts | 12 ++++ .../shared/api/storage/metadataStorage.ts | 25 ++++++++ src/renderer/shared/api/storage/storage.ts | 18 +++--- 8 files changed, 179 insertions(+), 15 deletions(-) create mode 100644 src/renderer/entities/network/lib/metadataService.ts create mode 100644 src/renderer/entities/network/lib/provider/CachedProvider.ts create mode 100644 src/renderer/shared/api/storage/metadataStorage.ts diff --git a/src/renderer/entities/network/lib/common/constants.ts b/src/renderer/entities/network/lib/common/constants.ts index cb23da1805..cf3ec45348 100644 --- a/src/renderer/entities/network/lib/common/constants.ts +++ b/src/renderer/entities/network/lib/common/constants.ts @@ -24,3 +24,5 @@ export const PROGRESSION_BASE = 2; export const THRESHOLD = BN_THOUSAND.div(BN_TWO); export const DEFAULT_TIME = new BN(6_000); export const ONE_DAY = new BN(24 * 60 * 60 * 1000); + +export const GET_METADATA_METHOD = 'state_getMetadata'; diff --git a/src/renderer/entities/network/lib/common/types.ts b/src/renderer/entities/network/lib/common/types.ts index 12f85c918f..48205224d7 100644 --- a/src/renderer/entities/network/lib/common/types.ts +++ b/src/renderer/entities/network/lib/common/types.ts @@ -1,9 +1,10 @@ import { ApiPromise } from '@polkadot/api'; import { ProviderInterface } from '@polkadot/rpc-provider/types'; +import { UnsubscribePromise } from '@polkadot/api/types'; import { Chain, RpcNode } from '@renderer/entities/chain/model/chain'; import { Connection, ConnectionType } from '@renderer/domain/connection'; -import { ChainId } from '@renderer/domain/shared-kernel'; +import { ChainId, HexString } from '@renderer/domain/shared-kernel'; import { Balance } from '@renderer/entities/asset/model/balance'; // ===================================================== @@ -63,3 +64,24 @@ export type ConnectProps = { attempt?: number; timeoutId?: any; }; + +export type Metadata = { + chainId: ChainId; + version: number; + metadata: HexString; +}; + +export interface IMetadataService { + /** + * If metadata exists return latest version from cache, else run syncMetadata and return new metadata + */ + getMetadata: (chainId: HexString) => Promise; + /** + * Update metadata from chain + */ + syncMetadata: (api: ApiPromise) => Promise; + /** + * Subscribe to subscribeRuntimeVersion and trigger syncMetadata if it will be changed + */ + subscribeMetadata: (api: ApiPromise) => UnsubscribePromise; +} diff --git a/src/renderer/entities/network/lib/metadataService.ts b/src/renderer/entities/network/lib/metadataService.ts new file mode 100644 index 0000000000..fa0feb10ab --- /dev/null +++ b/src/renderer/entities/network/lib/metadataService.ts @@ -0,0 +1,63 @@ +import { ApiPromise } from '@polkadot/api'; +import { UnsubscribePromise } from '@polkadot/api/types'; + +import { IMetadataService, Metadata } from './common/types'; +import { ChainId } from '@renderer/domain/shared-kernel'; +import storage from '@renderer/shared/api/storage'; + +export const useMetadata = (): IMetadataService => { + const metadataStorage = storage.connectTo('metadata'); + + if (!metadataStorage) { + throw new Error('=== 🔴 Metadata storage in not defined 🔴 ==='); + } + + const { getAllMetadata, addMetadata } = metadataStorage; + + const getMetadata = async (chainId: ChainId): Promise => { + const metadata = await getAllMetadata({ chainId }); + + if (metadata.length) { + const lastMetadata = metadata.reduce((acc, md) => { + if (md.version >= (acc.version || -1)) { + return md; + } + + return acc; + }, {} as Metadata); + + return lastMetadata; + } + }; + + const syncMetadata = async (api: ApiPromise): Promise => { + const [metadata, version] = await Promise.all([api.rpc.state.getMetadata(), api.rpc.state.getRuntimeVersion()]); + + const newMetadata: Metadata = { + metadata: metadata.toHex(), + version: version.specVersion.toNumber(), + chainId: api.genesisHash.toHex(), + }; + + await addMetadata(newMetadata); + + return newMetadata; + }; + + const subscribeMetadata = (api: ApiPromise): UnsubscribePromise => { + return api.rpc.state.subscribeRuntimeVersion(async (version) => { + const chainId = api.genesisHash.toHex(); + const oldMetadata = await getMetadata(chainId); + + if (!oldMetadata || version.specVersion.toNumber() > oldMetadata.version) { + syncMetadata(api); + } + }); + }; + + return { + getMetadata, + syncMetadata, + subscribeMetadata, + }; +}; diff --git a/src/renderer/entities/network/lib/networkService.ts b/src/renderer/entities/network/lib/networkService.ts index a164ea3e1c..23d07233c5 100644 --- a/src/renderer/entities/network/lib/networkService.ts +++ b/src/renderer/entities/network/lib/networkService.ts @@ -1,4 +1,4 @@ -import { ApiPromise, WsProvider, ScProvider } from '@polkadot/api'; +import { ApiPromise, ScProvider, WsProvider } from '@polkadot/api'; import * as Sc from '@substrate/connect'; import { ProviderInterface } from '@polkadot/rpc-provider/types'; import keyBy from 'lodash/keyBy'; @@ -11,8 +11,10 @@ import { ChainId } from '@renderer/domain/shared-kernel'; import { ISubscriptionService } from '../../../services/subscription/common/types'; import { useChainSpec } from './chainSpecService'; import { useChains } from './chainsService'; +import { useMetadata } from './metadataService'; import { AUTO_BALANCE_TIMEOUT, MAX_ATTEMPTS, PROGRESSION_BASE } from './common/constants'; import { ConnectionsMap, ConnectProps, INetworkService, RpcValidation } from './common/types'; +import { createCachedProvider } from './provider/CachedProvider'; export const useNetwork = (networkSubscription?: ISubscriptionService): INetworkService => { const chains = useRef>({}); @@ -20,6 +22,7 @@ export const useNetwork = (networkSubscription?: ISubscriptionService): const { getChainsData, sortChains } = useChains(); const { getKnownChain, getLightClientChains } = useChainSpec(); + const { subscribeMetadata, getMetadata } = useMetadata(); const connectionStorage = storage.connectTo('connections'); @@ -150,15 +153,19 @@ export const useNetwork = (networkSubscription?: ISubscriptionService): const knownChainId = getKnownChain(chainId); if (knownChainId) { - return new ScProvider(Sc, knownChainId); + const CachedScProvider = createCachedProvider(ScProvider, chainId, getMetadata); + + return new CachedScProvider(Sc, knownChainId); } else { throw new Error('Parachains do not support Substrate Connect yet'); } }; - const createWebsocketProvider = (rpcUrl: string): ProviderInterface => { + const createWebsocketProvider = (rpcUrl: string, chainId: ChainId): ProviderInterface => { // TODO: handle limited retries provider = new WsProvider(node.address, 5000, {1}, 11000); - return new WsProvider(rpcUrl, 2000); + const CachedWsProvider = createCachedProvider(WsProvider, chainId, getMetadata); + + return new CachedWsProvider(rpcUrl, 2000); }; const subscribeConnected = (chainId: ChainId, provider: ProviderInterface, type: ConnectionType, node?: RpcNode) => { @@ -168,6 +175,10 @@ export const useNetwork = (networkSubscription?: ISubscriptionService): const api = await ApiPromise.create({ provider, throwOnConnect: true, throwOnUnknown: true }); if (!api) await provider.disconnect(); + if (api) { + networkSubscription?.subscribe(chainId, subscribeMetadata(api)); + } + updateConnectionState( chainId, { @@ -245,7 +256,7 @@ export const useNetwork = (networkSubscription?: ISubscriptionService): if (type === ConnectionType.LIGHT_CLIENT) { provider.instance = createSubstrateProvider(chainId); } else if ([ConnectionType.RPC_NODE, ConnectionType.AUTO_BALANCE].includes(type) && node) { - provider.instance = createWebsocketProvider(node.url); + provider.instance = createWebsocketProvider(node.url, chainId); } setConnections((currentConnections) => ({ @@ -285,7 +296,9 @@ export const useNetwork = (networkSubscription?: ISubscriptionService): const validateRpcNode = (chainId: ChainId, rpcUrl: string): Promise => { return new Promise((resolve) => { - const provider = new WsProvider(rpcUrl); + const CachedWsProvider = createCachedProvider(WsProvider, chainId, getMetadata); + + const provider = new CachedWsProvider(rpcUrl); provider.on('connected', async () => { let isNetworkMatch = false; diff --git a/src/renderer/entities/network/lib/provider/CachedProvider.ts b/src/renderer/entities/network/lib/provider/CachedProvider.ts new file mode 100644 index 0000000000..3e2e184813 --- /dev/null +++ b/src/renderer/entities/network/lib/provider/CachedProvider.ts @@ -0,0 +1,25 @@ +import { ProviderInterface } from '@polkadot/rpc-provider/types'; + +import { GET_METADATA_METHOD } from '../common/constants'; +import { Metadata } from '../common/types'; +import { ChainId } from '@renderer/domain/shared-kernel'; + +export const createCachedProvider = ( + Provider: new (...args: any[]) => ProviderInterface, + chainId: ChainId, + getMetadata: (chainId: ChainId) => Promise, +) => { + class CachedProvider extends Provider { + async send(method: string, params: unknown[], ...args: any[]): Promise { + if (method === GET_METADATA_METHOD && params.length === 0) { + const metadata = await getMetadata(chainId); + + if (metadata) return metadata.metadata; + } + + return super.send(method, params, ...args); + } + } + + return CachedProvider; +}; diff --git a/src/renderer/shared/api/storage/common/types.ts b/src/renderer/shared/api/storage/common/types.ts index 4543b01ee5..52d981aceb 100644 --- a/src/renderer/shared/api/storage/common/types.ts +++ b/src/renderer/shared/api/storage/common/types.ts @@ -12,6 +12,7 @@ import { MultisigTransaction, MultisigTransactionKey, } from '@renderer/entities/transaction/model/transaction'; +import { Metadata } from '@renderer/entities/network'; // ===================================================== // ================ Storage interface ================== @@ -76,6 +77,14 @@ export interface IContactStorage { deleteContact: (contactId: ID) => Promise; } +export interface IMetadataStorage { + getMetadata: (chainId: ChainId, version: number) => Promise; + getAllMetadata: (where?: Partial) => Promise; + addMetadata: (metadata: Metadata) => Promise; + updateMetadata: (metadata: Metadata) => Promise; + deleteMetadata: (chainId: ChainId, version: number) => Promise; +} + export interface IMultisigTransactionStorage { getMultisigTx: ( accountId: AccountId, @@ -114,6 +123,7 @@ export type DataStorage = { multisigTransactions: IMultisigTransactionStorage; multisigEvents: IMultisigEventStorage; notifications: INotificationStorage; + metadata: IMetadataStorage; }; export type ID = string; @@ -127,6 +137,7 @@ export type AccountDS = WithID; export type MultisigTransactionDS = WithID; export type MultisigEventDS = WithID; export type NotificationDS = WithID; +export type MetadataDS = WithID; export type TWallet = Table; export type TContact = Table; @@ -136,3 +147,4 @@ export type TAccount = Table; export type TMultisigTransaction = Table; export type TMultisigEvent = Table; export type TNotification = Table; +export type TMetadata = Table; diff --git a/src/renderer/shared/api/storage/metadataStorage.ts b/src/renderer/shared/api/storage/metadataStorage.ts new file mode 100644 index 0000000000..27a292bdbb --- /dev/null +++ b/src/renderer/shared/api/storage/metadataStorage.ts @@ -0,0 +1,25 @@ +import type { Metadata } from '@renderer/entities/network'; +import { ID, IMetadataStorage, MetadataDS, TMetadata } from './common/types'; +import { ChainId } from '@renderer/domain/shared-kernel'; + +export const useMetadataStorage = (db: TMetadata): IMetadataStorage => ({ + getMetadata: (chainId: ChainId, version: number): Promise => { + return db.get([chainId, version]); + }, + + getAllMetadata: (where?: Partial): Promise => { + return where ? db.where(where).toArray() : db.toArray(); + }, + + addMetadata: (metadata: Metadata): Promise => { + return db.add(metadata); + }, + + updateMetadata: (metadata: Metadata): Promise => { + return db.put(metadata); + }, + + deleteMetadata: (chainId: ChainId, version: number): Promise => { + return db.delete([chainId, version.toString()]); + }, +}); diff --git a/src/renderer/shared/api/storage/storage.ts b/src/renderer/shared/api/storage/storage.ts index 3fa72a5213..b61e8960ba 100644 --- a/src/renderer/shared/api/storage/storage.ts +++ b/src/renderer/shared/api/storage/storage.ts @@ -11,6 +11,7 @@ import { TMultisigTransaction, TNotification, TMultisigEvent, + TMetadata, } from './common/types'; import { useBalanceStorage } from './balanceStorage'; import { useConnectionStorage } from './connectionStorage'; @@ -21,6 +22,7 @@ import { useTransactionStorage } from './transactionStorage'; import { useNotificationStorage } from './notificationStorage'; import { useMultisigEventStorage } from './multisigEventStorage'; import { upgradeEvents } from './common/upgrades'; +import { useMetadataStorage } from './metadataStorage'; class DexieStorage extends Dexie { connections: TConnection; @@ -31,6 +33,7 @@ class DexieStorage extends Dexie { multisigTransactions: TMultisigTransaction; multisigEvents: TMultisigEvent; notifications: TNotification; + metadata: TMetadata; constructor() { super('spektr'); @@ -49,18 +52,14 @@ class DexieStorage extends Dexie { // Move Multisig events from transaction to separate table this.version(17) .stores({ - connections: '++id,chainId,type', - wallets: '++id,isActive,type', - balances: '[accountId+chainId+assetId],[accountId+chainId]', - accounts: '++id,isActive,walletId,rootId,signingType', - contacts: '++id,name,accountId,matrixId', - multisigTransactions: - '[accountId+chainId+callHash+blockCreated+indexCreated],[accountId+status],[accountId+callHash],[callHash+status+chainId],accountId,status,callHash', multisigEvents: '++id,[txAccountId+txChainId+txCallHash+txBlock+txIndex],status,accountId', - notifications: '++id,type,read', }) .upgrade(upgradeEvents); + this.version(18).stores({ + metadata: '[chainId+version],chainId', + }); + this.connections = this.table('connections'); this.balances = this.table('balances'); this.wallets = this.table('wallets'); @@ -69,6 +68,7 @@ class DexieStorage extends Dexie { this.multisigTransactions = this.table('multisigTransactions'); this.multisigEvents = this.table('multisigEvents'); this.notifications = this.table('notifications'); + this.metadata = this.table('metadata'); } } @@ -97,6 +97,8 @@ class StorageFactory implements IStorage { return useMultisigEventStorage(this.dexieDB.multisigEvents) as DataStorage[T]; case 'notifications': return useNotificationStorage(this.dexieDB.notifications) as DataStorage[T]; + case 'metadata': + return useMetadataStorage(this.dexieDB.metadata) as DataStorage[T]; default: return undefined; } From 14f0c085d9ff689007759157da752759e9185dcf Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Wed, 6 Sep 2023 13:40:14 +0300 Subject: [PATCH 12/34] Feat: Xcm filter (#1046) --- package.json | 4 +- pnpm-lock.yaml | 29 +++-- .../transaction/lib/callDataDecoder.ts | 15 +++ .../transaction/lib/common/constants.ts | 12 ++ .../transaction/lib/transactionService.ts | 32 ++++++ .../entities/transaction/model/transaction.ts | 17 +-- .../operation/OperationsFilter/index.ts | 1 + .../OperationsFilter/lib/constants.ts | 1 + .../operation/OperationsFilter/lib/utils.ts | 93 ++++++++++++++++ .../OperationsFilter/ui/OperationsFilter.tsx} | 61 +++++----- src/renderer/features/operation/index.ts | 1 + .../pages/Operations/Operations.test.tsx | 24 ++-- src/renderer/pages/Operations/Operations.tsx | 6 +- src/renderer/pages/Operations/common/utils.ts | 104 +++--------------- .../shared/api/storage/common/types.ts | 2 +- .../shared/api/storage/common/upgrades.ts | 8 +- src/renderer/shared/api/storage/storage.ts | 1 - src/shared/locale/en.json | 1 + src/shared/locale/ru.json | 1 + 19 files changed, 256 insertions(+), 157 deletions(-) create mode 100644 src/renderer/features/operation/OperationsFilter/index.ts create mode 100644 src/renderer/features/operation/OperationsFilter/lib/constants.ts create mode 100644 src/renderer/features/operation/OperationsFilter/lib/utils.ts rename src/renderer/{pages/Operations/components/Filters.tsx => features/operation/OperationsFilter/ui/OperationsFilter.tsx} (75%) diff --git a/package.json b/package.json index d394b6451a..e1a2d542eb 100644 --- a/package.json +++ b/package.json @@ -72,8 +72,8 @@ "classnames": "^2.3.1", "crypto-browserify": "^3.12.0", "date-fns": "^2.29.3", - "dexie": "^3.2.2", - "dexie-react-hooks": "^1.1.1", + "dexie": "^3.2.4", + "dexie-react-hooks": "^1.1.6", "effector": "^22.8.6", "effector-forms": "^1.3.4", "effector-react": "^22.5.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6e87a22f1a..9bf084aba3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,11 +65,11 @@ dependencies: specifier: ^2.29.3 version: 2.29.3 dexie: - specifier: ^3.2.2 - version: 3.2.2 + specifier: ^3.2.4 + version: 3.2.4 dexie-react-hooks: - specifier: ^1.1.1 - version: 1.1.1(@types/react@18.0.14)(dexie@3.2.2)(react@18.2.0) + specifier: ^1.1.6 + version: 1.1.6(@types/react@18.0.14)(dexie@3.2.4)(react@18.2.0) effector: specifier: ^22.8.6 version: 22.8.6 @@ -2010,6 +2010,13 @@ packages: dependencies: regenerator-runtime: 0.14.0 + /@babel/runtime@7.22.15: + resolution: {integrity: sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.0 + dev: true + /@babel/runtime@7.5.5: resolution: {integrity: sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==} dependencies: @@ -5405,7 +5412,7 @@ packages: engines: {node: '>=14'} dependencies: '@babel/code-frame': 7.22.13 - '@babel/runtime': 7.22.11 + '@babel/runtime': 7.22.15 '@types/aria-query': 5.0.1 aria-query: 5.1.3 chalk: 4.1.2 @@ -9216,20 +9223,20 @@ packages: - supports-color dev: true - /dexie-react-hooks@1.1.1(@types/react@18.0.14)(dexie@3.2.2)(react@18.2.0): - resolution: {integrity: sha512-Cam5JP6PxHN564RvWEoe8cqLhosW0O4CAZ9XEVYeGHJBa6KEJlOpd9CUpV3kmU9dm2MrW97/lk7qkf1xpij7gA==} + /dexie-react-hooks@1.1.6(@types/react@18.0.14)(dexie@3.2.4)(react@18.2.0): + resolution: {integrity: sha512-xSblWtmPwhafWNWMECsW7zMMmBu8goH3QqTxEfwBNoNG1mgsM0oFclippev7ss9HhKICqBwTjgqpscci5Ed4mA==} peerDependencies: '@types/react': '>=16' - dexie: '>=3.1.0-alpha.1 <5.0.0' + dexie: ^3.2 || ^4.0.1-alpha react: '>=16' dependencies: '@types/react': 18.0.14 - dexie: 3.2.2 + dexie: 3.2.4 react: 18.2.0 dev: false - /dexie@3.2.2: - resolution: {integrity: sha512-q5dC3HPmir2DERlX+toCBbHQXW5MsyrFqPFcovkH9N2S/UW/H3H5AWAB6iEOExeraAu+j+zRDG+zg/D7YhH0qg==} + /dexie@3.2.4: + resolution: {integrity: sha512-VKoTQRSv7+RnffpOJ3Dh6ozknBqzWw/F3iqMdsZg958R0AS8AnY9x9d1lbwENr0gzeGJHXKcGhAMRaqys6SxqA==} engines: {node: '>=6.0'} dev: false diff --git a/src/renderer/entities/transaction/lib/callDataDecoder.ts b/src/renderer/entities/transaction/lib/callDataDecoder.ts index cb3bf0dd43..fbb2244815 100644 --- a/src/renderer/entities/transaction/lib/callDataDecoder.ts +++ b/src/renderer/entities/transaction/lib/callDataDecoder.ts @@ -136,6 +136,21 @@ export const useCallDataDecoder = (): ICallDataDecoder => { [TransactionType.TRANSFER]: (method, section, decoded): Record => { return { dest: decoded.args[0].toString(), value: decoded.args[1].toString() }; }, + [TransactionType.XCM_LIMITED_TRANSFER]: (method, section, decoded): Record => { + return {}; + }, + [TransactionType.XCM_TELEPORT]: (method, section, decoded): Record => { + return {}; + }, + [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: (method, section, decoded): Record => { + return {}; + }, + [TransactionType.POLKADOT_XCM_TELEPORT]: (method, section, decoded): Record => { + return {}; + }, + [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: (method, section, decoded): Record => { + return {}; + }, [TransactionType.ASSET_TRANSFER]: (method, section, decoded): Record => { return { assetId: decoded.args[0].toString(), diff --git a/src/renderer/entities/transaction/lib/common/constants.ts b/src/renderer/entities/transaction/lib/common/constants.ts index 684b9eaf13..78dcb70352 100644 --- a/src/renderer/entities/transaction/lib/common/constants.ts +++ b/src/renderer/entities/transaction/lib/common/constants.ts @@ -1,3 +1,5 @@ +import { TransactionType } from '../../model/transaction'; + export const MAX_WEIGHT = { refTime: '0', proofSize: '0', @@ -8,3 +10,13 @@ export const OLD_MULTISIG_ARGS_AMOUNT = 6; export const BOND_WITH_CONTROLLER_ARGS_AMOUNT = 3; export const CONTROLLER_ARG_NAME = 'controller'; + +export const TransferTypes = [TransactionType.TRANSFER, TransactionType.ASSET_TRANSFER, TransactionType.ORML_TRANSFER]; + +export const XcmTypes = [ + TransactionType.XCM_TELEPORT, + TransactionType.XCM_LIMITED_TRANSFER, + TransactionType.POLKADOT_XCM_TELEPORT, + TransactionType.POLKADOT_XCM_LIMITED_TRANSFER, + TransactionType.XTOKENS_TRANSFER_MULTIASSET, +]; diff --git a/src/renderer/entities/transaction/lib/transactionService.ts b/src/renderer/entities/transaction/lib/transactionService.ts index 98207773be..d8608e95ad 100644 --- a/src/renderer/entities/transaction/lib/transactionService.ts +++ b/src/renderer/entities/transaction/lib/transactionService.ts @@ -167,6 +167,22 @@ export const useTransaction = (): ITransactionService => { options, ); }, + // TODO: finish during XCM transfer + [TransactionType.XCM_LIMITED_TRANSFER]: (transaction, info, options, api) => { + return {} as UnsignedTransaction; + }, + [TransactionType.XCM_TELEPORT]: (transaction, info, options, api) => { + return {} as UnsignedTransaction; + }, + [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: (transaction, info, options, api) => { + return {} as UnsignedTransaction; + }, + [TransactionType.POLKADOT_XCM_TELEPORT]: (transaction, info, options, api) => { + return {} as UnsignedTransaction; + }, + [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: (transaction, info, options, api) => { + return {} as UnsignedTransaction; + }, [TransactionType.BOND]: (transaction, info, options, api) => { return isControllerMissing(api) ? bondWithoutController( @@ -282,6 +298,22 @@ export const useTransaction = (): ITransactionService => { ) => api.tx.multisig.approveAsMulti(threshold, otherSignatories, maybeTimepoint, callHash, maxWeight), [TransactionType.MULTISIG_CANCEL_AS_MULTI]: ({ threshold, otherSignatories, maybeTimepoint, callHash }, api) => api.tx.multisig.cancelAsMulti(threshold, otherSignatories, maybeTimepoint, callHash), + // TODO: finish during XCM transfer + [TransactionType.XCM_LIMITED_TRANSFER]: () => { + return {} as SubmittableExtrinsic<'promise'>; + }, + [TransactionType.XCM_TELEPORT]: () => { + return {} as SubmittableExtrinsic<'promise'>; + }, + [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: () => { + return {} as SubmittableExtrinsic<'promise'>; + }, + [TransactionType.POLKADOT_XCM_TELEPORT]: () => { + return {} as SubmittableExtrinsic<'promise'>; + }, + [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: () => { + return {} as SubmittableExtrinsic<'promise'>; + }, // controller arg removed from bond but changes not released yet // https://github.com/paritytech/substrate/pull/14039 // @ts-ignore diff --git a/src/renderer/entities/transaction/model/transaction.ts b/src/renderer/entities/transaction/model/transaction.ts index 07985f0b0a..2f843066c0 100644 --- a/src/renderer/entities/transaction/model/transaction.ts +++ b/src/renderer/entities/transaction/model/transaction.ts @@ -6,9 +6,17 @@ export const enum TransactionType { TRANSFER = 'transfer', ORML_TRANSFER = 'ormlTransfer', ASSET_TRANSFER = 'assetTransfer', + MULTISIG_AS_MULTI = 'multisig_as_multi', MULTISIG_APPROVE_AS_MULTI = 'multisig_approve_as_multi', MULTISIG_CANCEL_AS_MULTI = 'cancel_as_multi', + + XCM_LIMITED_TRANSFER = 'xcm_limited_reserve_transfer_assets', + XCM_TELEPORT = 'xcm_limited_teleport_assets', + POLKADOT_XCM_LIMITED_TRANSFER = 'polkadotxcm_limited_reserve_transfer_assets', + POLKADOT_XCM_TELEPORT = 'polkadotxcm_limited_teleport_assets', + XTOKENS_TRANSFER_MULTIASSET = 'xtokens_transfer_multiasset', + BOND = 'bond', STAKE_MORE = 'bondExtra', UNSTAKE = 'unbond', @@ -81,6 +89,7 @@ export type MultisigTransaction = { deposit?: string; depositor?: AccountId; description?: string; + xcmDestination?: ChainId; cancelDescription?: string; blockCreated: number; indexCreated: number; @@ -92,11 +101,3 @@ export type MultisigTransactionKey = Pick< MultisigTransaction, 'accountId' | 'callHash' | 'chainId' | 'indexCreated' | 'blockCreated' >; - -export function isDecodedTx(tx: Transaction | DecodedTransaction): tx is DecodedTransaction { - const hasType = tx.type !== undefined; - const hasMethod = (tx as DecodedTransaction).method !== undefined; - const hasSection = (tx as DecodedTransaction).section !== undefined; - - return !hasType && hasMethod && hasSection; -} diff --git a/src/renderer/features/operation/OperationsFilter/index.ts b/src/renderer/features/operation/OperationsFilter/index.ts new file mode 100644 index 0000000000..ecbfb93906 --- /dev/null +++ b/src/renderer/features/operation/OperationsFilter/index.ts @@ -0,0 +1 @@ +export { OperationsFilter } from './ui/OperationsFilter'; diff --git a/src/renderer/features/operation/OperationsFilter/lib/constants.ts b/src/renderer/features/operation/OperationsFilter/lib/constants.ts new file mode 100644 index 0000000000..40fe4461a2 --- /dev/null +++ b/src/renderer/features/operation/OperationsFilter/lib/constants.ts @@ -0,0 +1 @@ +export const UNKNOWN_TYPE = 'UNKNOWN_TYPE'; diff --git a/src/renderer/features/operation/OperationsFilter/lib/utils.ts b/src/renderer/features/operation/OperationsFilter/lib/utils.ts new file mode 100644 index 0000000000..4815e35013 --- /dev/null +++ b/src/renderer/features/operation/OperationsFilter/lib/utils.ts @@ -0,0 +1,93 @@ +import { TFunction } from 'react-i18next'; + +import { + MultisigTxInitStatus, + MultisigTxFinalStatus, + TransactionType, +} from '@renderer/entities/transaction/model/transaction'; +import { UNKNOWN_TYPE } from './constants'; + +export const getStatusOptions = (t: TFunction) => { + return [ + { + id: MultisigTxInitStatus.SIGNING, + value: MultisigTxInitStatus.SIGNING, + element: t('operation.status.signing'), + }, + { + id: MultisigTxFinalStatus.CANCELLED, + value: MultisigTxFinalStatus.CANCELLED, + element: t('operation.status.cancelled'), + }, + { + id: MultisigTxFinalStatus.ERROR, + value: MultisigTxFinalStatus.ERROR, + element: t('operation.status.error'), + }, + { + id: MultisigTxFinalStatus.ESTABLISHED, + value: MultisigTxFinalStatus.ESTABLISHED, + element: t('operation.status.established'), + }, + { + id: MultisigTxFinalStatus.EXECUTED, + value: MultisigTxFinalStatus.EXECUTED, + element: t('operation.status.executed'), + }, + ]; +}; + +export const getTransactionOptions = (t: TFunction) => { + return [ + { + id: TransactionType.TRANSFER, + value: TransactionType.TRANSFER, + element: t('operations.titles.transfer'), + }, + { + id: TransactionType.XCM_LIMITED_TRANSFER, + value: TransactionType.XCM_LIMITED_TRANSFER, + element: t('operations.titles.crossChainTransfer'), + }, + { + id: TransactionType.BOND, + value: TransactionType.BOND, + element: t('operations.titles.startStaking'), + }, + { + id: TransactionType.STAKE_MORE, + value: TransactionType.STAKE_MORE, + element: t('operations.titles.stakeMore'), + }, + { + id: TransactionType.DESTINATION, + value: TransactionType.DESTINATION, + element: t('operations.titles.destination'), + }, + { + id: TransactionType.NOMINATE, + value: TransactionType.NOMINATE, + element: t('operations.titles.nominate'), + }, + { + id: TransactionType.REDEEM, + value: TransactionType.REDEEM, + element: t('operations.titles.redeem'), + }, + { + id: TransactionType.RESTAKE, + value: TransactionType.RESTAKE, + element: t('operations.titles.restake'), + }, + { + id: TransactionType.UNSTAKE, + value: TransactionType.UNSTAKE, + element: t('operations.titles.unstake'), + }, + { + id: UNKNOWN_TYPE, + value: UNKNOWN_TYPE, + element: t('operations.titles.unknown'), + }, + ]; +}; diff --git a/src/renderer/pages/Operations/components/Filters.tsx b/src/renderer/features/operation/OperationsFilter/ui/OperationsFilter.tsx similarity index 75% rename from src/renderer/pages/Operations/components/Filters.tsx rename to src/renderer/features/operation/OperationsFilter/ui/OperationsFilter.tsx index 733d83d366..2f8e896823 100644 --- a/src/renderer/pages/Operations/components/Filters.tsx +++ b/src/renderer/features/operation/OperationsFilter/ui/OperationsFilter.tsx @@ -2,23 +2,25 @@ import { useEffect, useState } from 'react'; import { useI18n, useNetworkContext } from '@renderer/app/providers'; import { MultisigTransactionDS } from '@renderer/shared/api/storage'; -import { UNKNOWN_TYPE, getStatusOptions, getTransactionOptions, TransferTypes } from '../common/utils'; import { DropdownOption, DropdownResult } from '@renderer/shared/ui/types'; -import { MultisigTransaction, Transaction, TransactionType } from '@renderer/entities/transaction'; import { Button, MultiSelect } from '@renderer/shared/ui'; +import { MultisigTransaction, Transaction, TransactionType } from '@renderer/entities/transaction/model/transaction'; +import { TransferTypes, XcmTypes } from '@renderer/entities/transaction/lib/common/constants'; +import { getStatusOptions, getTransactionOptions } from '../lib/utils'; +import { UNKNOWN_TYPE } from '../lib/constants'; type FilterName = 'status' | 'network' | 'type'; type FiltersOptions = Record>; type SelectedFilters = Record; -const emptyOptions: FiltersOptions = { +const EmptyOptions: FiltersOptions = { status: new Set(), network: new Set(), type: new Set(), }; -const emptySelected: SelectedFilters = { +const EmptySelected: SelectedFilters = { status: [], network: [], type: [], @@ -28,10 +30,10 @@ const mapValues = (result: DropdownResult) => result.value; type Props = { txs: MultisigTransactionDS[]; - onChangeFilters: (filteredTxs: MultisigTransaction[]) => void; + onChange: (filteredTxs: MultisigTransaction[]) => void; }; -const Filters = ({ txs, onChangeFilters }: Props) => { +export const OperationsFilter = ({ txs, onChange }: Props) => { const { t } = useI18n(); const { connections } = useNetworkContext(); @@ -43,18 +45,19 @@ const Filters = ({ txs, onChangeFilters }: Props) => { element: c.name, })); - const [filtersOptions, setFiltersOptions] = useState(emptyOptions); - const [selectedOptions, setSelectedOptions] = useState(emptySelected); + const [filtersOptions, setFiltersOptions] = useState(EmptyOptions); + const [selectedOptions, setSelectedOptions] = useState(EmptySelected); useEffect(() => { setFiltersOptions(getAvailableFiltersOptions(txs)); - onChangeFilters(txs); + onChange(txs); }, [txs]); - const getFilterableType = (tx: MultisigTransaction): TransactionType | typeof UNKNOWN_TYPE => { + const getFilterableTxType = (tx: MultisigTransaction): TransactionType | typeof UNKNOWN_TYPE => { if (!tx.transaction?.type) return UNKNOWN_TYPE; if (TransferTypes.includes(tx.transaction.type)) return TransactionType.TRANSFER; + if (XcmTypes.includes(tx.transaction.type)) return TransactionType.XCM_LIMITED_TRANSFER; if (tx.transaction.type === TransactionType.BATCH_ALL) { const txMatch = tx.transaction.args?.transactions?.find((tx: Transaction) => { @@ -67,16 +70,14 @@ const Filters = ({ txs, onChangeFilters }: Props) => { return tx.transaction.type; }; - const getTransactionTypeOption = (tx: MultisigTransaction) => { - return TransactionOptions.find((s) => s.value === getFilterableType(tx)); - }; - - const getAvailableFiltersOptions = (transactions: MultisigTransaction[]) => - transactions.reduce( + const getAvailableFiltersOptions = (transactions: MultisigTransaction[]) => { + return transactions.reduce( (acc, tx) => { + const txType = getFilterableTxType(tx); + const statusOption = StatusOptions.find((s) => s.value === tx.status); - const networkOption = NetworkOptions.find((s) => s.value === tx.chainId); - const typeOption = getTransactionTypeOption(tx); + const networkOption = NetworkOptions.find((s) => s.value === tx.chainId || s.value === tx.xcmDestination); + const typeOption = TransactionOptions.find((s) => s.value === txType); if (statusOption) acc.status.add(statusOption); if (networkOption) acc.network.add(networkOption); @@ -90,23 +91,27 @@ const Filters = ({ txs, onChangeFilters }: Props) => { type: new Set(), }, ); - - const clearFilters = () => { - setSelectedOptions(emptySelected); - onChangeFilters(txs); }; - const filterTx = (tx: MultisigTransaction, filters: SelectedFilters) => - (!filters.status.length || filters.status.map(mapValues).includes(tx.status)) && - (!filters.network.length || filters.network.map(mapValues).includes(tx.chainId)) && - (!filters.type.length || filters.type.map(mapValues).includes(getFilterableType(tx))); + const filterTx = (tx: MultisigTransaction, filters: SelectedFilters) => { + return ( + (!filters.status.length || filters.status.map(mapValues).includes(tx.status)) && + (!filters.network.length || filters.network.map(mapValues).includes(tx.chainId)) && + (!filters.type.length || filters.type.map(mapValues).includes(getFilterableTxType(tx))) + ); + }; const handleFilterChange = (values: DropdownResult[], filterName: FilterName) => { const newSelectedOptions = { ...selectedOptions, [filterName]: values }; setSelectedOptions(newSelectedOptions); const filteredTxs = txs.filter((tx) => filterTx(tx, newSelectedOptions)); - onChangeFilters(filteredTxs); + onChange(filteredTxs); + }; + + const clearFilters = () => { + setSelectedOptions(EmptySelected); + onChange(txs); }; const filtersSelected = @@ -144,5 +149,3 @@ const Filters = ({ txs, onChangeFilters }: Props) => { ); }; - -export default Filters; diff --git a/src/renderer/features/operation/index.ts b/src/renderer/features/operation/index.ts index fe7f243b78..77984e6a8c 100644 --- a/src/renderer/features/operation/index.ts +++ b/src/renderer/features/operation/index.ts @@ -1,2 +1,3 @@ export * from './sign'; export * from './init'; +export * from './OperationsFilter'; diff --git a/src/renderer/pages/Operations/Operations.test.tsx b/src/renderer/pages/Operations/Operations.test.tsx index 288244b3bd..92a27586bc 100644 --- a/src/renderer/pages/Operations/Operations.test.tsx +++ b/src/renderer/pages/Operations/Operations.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react'; +import { render, screen, act } from '@testing-library/react'; import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { ConnectionType } from '@renderer/domain/connection'; @@ -21,12 +21,9 @@ jest.mock('@renderer/app/providers', () => ({ })), })); -const mockTxs = [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID }]; -const mockAccounts = [{ name: 'Test Account', accountId: TEST_ACCOUNT_ID }]; - jest.mock('@renderer/entities/multisig', () => ({ useMultisigTx: jest.fn().mockReturnValue({ - getLiveAccountMultisigTxs: () => mockTxs, + getLiveAccountMultisigTxs: () => [{ name: 'Test Wallet', accountId: TEST_ACCOUNT_ID, chainId: '0x00' }], }), useMultisigEvent: jest.fn().mockReturnValue({ getLiveEventsByKeys: jest.fn().mockResolvedValue([]), @@ -35,18 +32,25 @@ jest.mock('@renderer/entities/multisig', () => ({ jest.mock('@renderer/entities/account', () => ({ useAccount: jest.fn().mockReturnValue({ - getActiveMultisigAccount: () => mockAccounts, + getActiveMultisigAccount: () => [{ name: 'Test Account', accountId: TEST_ACCOUNT_ID }], }), })); jest.mock('./components/Operation', () => () => 'Operation'); -jest.mock('./components/Filters', () => () => 'Filters'); +jest.mock('@renderer/features/operation', () => ({ + OperationsFilter: () => 'filter', +})); -describe('screen/Operations', () => { - test('should render component', () => { - render(); +describe('pages/Operations', () => { + test('should render component', async () => { + await act(async () => { + render(); + }); const title = screen.getByText('operations.title'); + const filter = screen.getByText('filter'); + expect(title).toBeInTheDocument(); + expect(filter).toBeInTheDocument(); }); }); diff --git a/src/renderer/pages/Operations/Operations.tsx b/src/renderer/pages/Operations/Operations.tsx index 728a02d494..a3b5fc482e 100644 --- a/src/renderer/pages/Operations/Operations.tsx +++ b/src/renderer/pages/Operations/Operations.tsx @@ -8,11 +8,11 @@ import { useAccount, MultisigAccount } from '@renderer/entities/account'; import Operation from './components/Operation'; import { sortByDateDesc } from './common/utils'; import { FootnoteText } from '@renderer/shared/ui'; -import Filters from './components/Filters'; import { MultisigTransactionDS } from '@renderer/shared/api/storage'; import { useMultisigTx, useMultisigEvent } from '@renderer/entities/multisig'; import { Header } from '@renderer/components/common'; import { MultisigEvent, MultisigTransactionKey } from '@renderer/entities/transaction'; +import { OperationsFilter } from '@renderer/features/operation'; export const Operations = () => { const { t, dateLocale } = useI18n(); @@ -49,7 +49,7 @@ export const Operations = () => { useEffect(() => { setTxs(allTxs.filter((tx) => connections[tx.chainId])); - }, [allTxs]); + }, [allTxs.length]); useEffect(() => { setFilteredTxs([]); @@ -59,7 +59,7 @@ export const Operations = () => {
    - {Boolean(txs.length) && } + {Boolean(txs.length) && } {Boolean(filteredTxs.length) && (
    diff --git a/src/renderer/pages/Operations/common/utils.ts b/src/renderer/pages/Operations/common/utils.ts index 186137116f..443e6292b0 100644 --- a/src/renderer/pages/Operations/common/utils.ts +++ b/src/renderer/pages/Operations/common/utils.ts @@ -1,23 +1,13 @@ -import { TFunction } from 'react-i18next'; - import { IconNames } from '@renderer/shared/ui/Icon/data'; import { Explorer } from '@renderer/entities/chain/model/chain'; import { AccountId, HexString } from '@renderer/domain/shared-kernel'; -import { - DecodedTransaction, - MultisigTxFinalStatus, - MultisigTxInitStatus, - Transaction, - TransactionType, -} from '@renderer/entities/transaction/model/transaction'; +import { DecodedTransaction, Transaction, TransactionType } from '@renderer/entities/transaction/model/transaction'; import { toAddress, formatSectionAndMethod } from '@renderer/shared/lib/utils'; import { Account } from '@renderer/entities/account/model/account'; import { Signatory } from '@renderer/entities/signatory/model/signatory'; import type { Contact } from '@renderer/entities/contact'; -export const UNKNOWN_TYPE = 'UNKNOWN_TYPE'; export const TRANSACTION_UNKNOWN = 'operations.titles.unknown'; -export const TransferTypes = [TransactionType.TRANSFER, TransactionType.ASSET_TRANSFER, TransactionType.ORML_TRANSFER]; const TransactionTitles: Record = { // Transfer @@ -27,6 +17,12 @@ const TransactionTitles: Record = { [TransactionType.MULTISIG_AS_MULTI]: 'operations.titles.approveMultisig', [TransactionType.MULTISIG_APPROVE_AS_MULTI]: 'operations.titles.approveMultisig', [TransactionType.MULTISIG_CANCEL_AS_MULTI]: 'operations.titles.cancelMultisig', + // XCM + [TransactionType.XCM_LIMITED_TRANSFER]: 'operations.titles.crossChainTransfer', + [TransactionType.XCM_TELEPORT]: 'operations.titles.crossChainTransfer', + [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: 'operations.titles.crossChainTransfer', + [TransactionType.POLKADOT_XCM_TELEPORT]: 'operations.titles.crossChainTransfer', + [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: 'operations.titles.crossChainTransfer', // Staking [TransactionType.BOND]: 'operations.titles.startStaking', [TransactionType.NOMINATE]: 'operations.titles.nominate', @@ -48,6 +44,12 @@ const TransactionIcons: Record = { [TransactionType.MULTISIG_AS_MULTI]: 'transferMst', [TransactionType.MULTISIG_APPROVE_AS_MULTI]: 'transferMst', [TransactionType.MULTISIG_CANCEL_AS_MULTI]: 'transferMst', + // XCM + [TransactionType.XCM_LIMITED_TRANSFER]: 'unknownMst', + [TransactionType.XCM_TELEPORT]: 'unknownMst', + [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: 'unknownMst', + [TransactionType.POLKADOT_XCM_TELEPORT]: 'unknownMst', + [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: 'unknownMst', // Staking [TransactionType.BOND]: 'stakingMst', [TransactionType.NOMINATE]: 'stakingMst', @@ -114,86 +116,6 @@ export const getMultisigExtrinsicLink = ( return multisigLink.replace('{index}', `${blockCreated}-${indexCreated}`).replace('{callHash}', callHash); }; -export const getStatusOptions = (t: TFunction) => { - return [ - { - id: MultisigTxInitStatus.SIGNING, - value: MultisigTxInitStatus.SIGNING, - element: t('operation.status.signing'), - }, - { - id: MultisigTxFinalStatus.CANCELLED, - value: MultisigTxFinalStatus.CANCELLED, - element: t('operation.status.cancelled'), - }, - { - id: MultisigTxFinalStatus.ERROR, - value: MultisigTxFinalStatus.ERROR, - element: t('operation.status.error'), - }, - { - id: MultisigTxFinalStatus.ESTABLISHED, - value: MultisigTxFinalStatus.ESTABLISHED, - element: t('operation.status.established'), - }, - { - id: MultisigTxFinalStatus.EXECUTED, - value: MultisigTxFinalStatus.EXECUTED, - element: t('operation.status.executed'), - }, - ]; -}; - -export const getTransactionOptions = (t: TFunction) => { - return [ - { - id: TransactionType.TRANSFER, - value: TransactionType.TRANSFER, - element: t('operations.titles.transfer'), - }, - { - id: TransactionType.BOND, - value: TransactionType.BOND, - element: t('operations.titles.startStaking'), - }, - { - id: TransactionType.STAKE_MORE, - value: TransactionType.STAKE_MORE, - element: t('operations.titles.stakeMore'), - }, - { - id: TransactionType.DESTINATION, - value: TransactionType.DESTINATION, - element: t('operations.titles.destination'), - }, - { - id: TransactionType.NOMINATE, - value: TransactionType.NOMINATE, - element: t('operations.titles.nominate'), - }, - { - id: TransactionType.REDEEM, - value: TransactionType.REDEEM, - element: t('operations.titles.redeem'), - }, - { - id: TransactionType.RESTAKE, - value: TransactionType.RESTAKE, - element: t('operations.titles.restake'), - }, - { - id: TransactionType.UNSTAKE, - value: TransactionType.UNSTAKE, - element: t('operations.titles.unstake'), - }, - { - id: UNKNOWN_TYPE, - value: UNKNOWN_TYPE, - element: t('operations.titles.unknown'), - }, - ]; -}; - export const getTransactionAmount = (tx: Transaction | DecodedTransaction): string | null => { const txType = tx.type; if (!txType) return null; diff --git a/src/renderer/shared/api/storage/common/types.ts b/src/renderer/shared/api/storage/common/types.ts index 52d981aceb..591752e933 100644 --- a/src/renderer/shared/api/storage/common/types.ts +++ b/src/renderer/shared/api/storage/common/types.ts @@ -7,12 +7,12 @@ import { Wallet } from '@renderer/entities/wallet/model/wallet'; import { Account, MultisigAccount } from '@renderer/entities/account/model/account'; import { Notification } from '@renderer/entities/notification/model/notification'; import type { Contact } from '@renderer/entities/contact'; +import type { Metadata } from '@renderer/entities/network'; import { MultisigEvent, MultisigTransaction, MultisigTransactionKey, } from '@renderer/entities/transaction/model/transaction'; -import { Metadata } from '@renderer/entities/network'; // ===================================================== // ================ Storage interface ================== diff --git a/src/renderer/shared/api/storage/common/upgrades.ts b/src/renderer/shared/api/storage/common/upgrades.ts index bcbd015d7c..40abf27943 100644 --- a/src/renderer/shared/api/storage/common/upgrades.ts +++ b/src/renderer/shared/api/storage/common/upgrades.ts @@ -2,7 +2,13 @@ import { Transaction } from 'dexie'; import { MultisigEventDS } from './types'; -export const upgradeEvents = async (trans: Transaction) => { +/** + * Remove events from MultisigTransactions + * Add events to separate table MultisigEvents + * @param trans transactions from DB + * @return {Promise} + */ +export const upgradeEvents = async (trans: Transaction): Promise => { const txs = await trans.table('multisigTransactions').toArray(); const newEvents = txs .map((tx) => diff --git a/src/renderer/shared/api/storage/storage.ts b/src/renderer/shared/api/storage/storage.ts index b61e8960ba..38b68ee16c 100644 --- a/src/renderer/shared/api/storage/storage.ts +++ b/src/renderer/shared/api/storage/storage.ts @@ -49,7 +49,6 @@ class DexieStorage extends Dexie { notifications: '++id,type,read', }); - // Move Multisig events from transaction to separate table this.version(17) .stores({ multisigEvents: '++id,[txAccountId+txChainId+txCallHash+txBlock+txIndex],status,accountId', diff --git a/src/shared/locale/en.json b/src/shared/locale/en.json index e53a5a319c..91156b99cd 100644 --- a/src/shared/locale/en.json +++ b/src/shared/locale/en.json @@ -367,6 +367,7 @@ "noOperationsWalletNotMulti": "You can only view operations for your Multisig wallets. Transaction history for other types of wallets will be available later. Stay tuned.", "title": "Operations", "titles": { + "crossChainTransfer": "Cross-chain transfer", "destination": "Set reward destination", "nominate": "Change Validators", "redeem": "Withdraw Unstaked", diff --git a/src/shared/locale/ru.json b/src/shared/locale/ru.json index 2c3b070d32..51c0060dbe 100644 --- a/src/shared/locale/ru.json +++ b/src/shared/locale/ru.json @@ -367,6 +367,7 @@ "noOperationsWalletNotMulti": "You can only view operations for your Multisig wallets. Transaction history for other types of wallets will be available later. Stay tuned.", "title": "Операции", "titles": { + "crossChainTransfer": "Кросс-чейн трансфер", "destination": "Установить кошелёк получения награды", "nominate": "Смена валидаторов", "redeem": "Забрать", From ddac02b7fb1cdde01c4c2d64934ab84de32ec68c Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Wed, 6 Sep 2023 13:44:10 +0300 Subject: [PATCH 13/34] Feat: Xcm icon (#1047) --- .../images/arrows/cross-chain-arrow.svg | 3 ++ .../asset/ui/AssetBalance/AssetBalance.tsx | 4 +- .../NetworkAssets/NetworkAssets.tsx | 2 +- src/renderer/pages/Operations/Operations.tsx | 2 +- src/renderer/pages/Operations/common/utils.ts | 10 ++--- .../components/ActionSteps/Confirmation.tsx | 2 +- .../pages/Operations/components/Log.tsx | 39 ++++++++++++------- .../pages/Operations/components/Operation.tsx | 35 ++++++++++------- .../Operations/components/OperationStatus.tsx | 9 ++++- .../components/ShortTransactionInfo.test.tsx | 2 +- .../components/TransactionAmount.tsx | 6 +-- .../TransactionTitle.test.tsx | 12 +++--- .../TransactionTitle/TransactionTitle.tsx | 24 ++++++------ .../components/NetworkList/NetworkList.tsx | 2 +- .../ValidatorsModal/ValidatorsModal.tsx | 4 +- .../shared/ui/Accordion/Accordion.tsx | 27 +++++++------ src/renderer/shared/ui/Icon/data/arrow.tsx | 2 + .../components/ActionSteps/Confirmation.tsx | 2 +- tailwind.config.ts | 3 -- 19 files changed, 105 insertions(+), 85 deletions(-) create mode 100644 src/renderer/assets/images/arrows/cross-chain-arrow.svg diff --git a/src/renderer/assets/images/arrows/cross-chain-arrow.svg b/src/renderer/assets/images/arrows/cross-chain-arrow.svg new file mode 100644 index 0000000000..f6f06becf9 --- /dev/null +++ b/src/renderer/assets/images/arrows/cross-chain-arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/renderer/entities/asset/ui/AssetBalance/AssetBalance.tsx b/src/renderer/entities/asset/ui/AssetBalance/AssetBalance.tsx index 890a251d39..664c6e7b42 100644 --- a/src/renderer/entities/asset/ui/AssetBalance/AssetBalance.tsx +++ b/src/renderer/entities/asset/ui/AssetBalance/AssetBalance.tsx @@ -27,9 +27,7 @@ export const AssetBalance = ({ value, asset, className, showIcon, imgClassName, ); - if (!showIcon) { - return balance; - } + if (!showIcon) return balance; return ( diff --git a/src/renderer/pages/Assets/AssetsList/components/NetworkAssets/NetworkAssets.tsx b/src/renderer/pages/Assets/AssetsList/components/NetworkAssets/NetworkAssets.tsx index 1ef8b45476..53066a9cd5 100644 --- a/src/renderer/pages/Assets/AssetsList/components/NetworkAssets/NetworkAssets.tsx +++ b/src/renderer/pages/Assets/AssetsList/components/NetworkAssets/NetworkAssets.tsx @@ -86,7 +86,7 @@ export const NetworkAssets = ({ query, hideZeroBalance, chain, accounts, searchS
  • { .map(([date, txs]) => (
    {date} -
      +
        {txs .sort((a, b) => (b.dateCreated || 0) - (a.dateCreated || 0)) .map((tx) => ( diff --git a/src/renderer/pages/Operations/common/utils.ts b/src/renderer/pages/Operations/common/utils.ts index 443e6292b0..41ba62cd95 100644 --- a/src/renderer/pages/Operations/common/utils.ts +++ b/src/renderer/pages/Operations/common/utils.ts @@ -45,11 +45,11 @@ const TransactionIcons: Record = { [TransactionType.MULTISIG_APPROVE_AS_MULTI]: 'transferMst', [TransactionType.MULTISIG_CANCEL_AS_MULTI]: 'transferMst', // XCM - [TransactionType.XCM_LIMITED_TRANSFER]: 'unknownMst', - [TransactionType.XCM_TELEPORT]: 'unknownMst', - [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: 'unknownMst', - [TransactionType.POLKADOT_XCM_TELEPORT]: 'unknownMst', - [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: 'unknownMst', + [TransactionType.XCM_LIMITED_TRANSFER]: 'crossChain', + [TransactionType.XCM_TELEPORT]: 'crossChain', + [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: 'crossChain', + [TransactionType.POLKADOT_XCM_TELEPORT]: 'crossChain', + [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: 'crossChain', // Staking [TransactionType.BOND]: 'stakingMst', [TransactionType.NOMINATE]: 'stakingMst', diff --git a/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx b/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx index e7a2e14e54..52dd62c773 100644 --- a/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx +++ b/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx @@ -1,5 +1,5 @@ import { MultisigTransaction, Transaction, Fee } from '@renderer/entities/transaction'; -import TransactionAmount from '@renderer/pages/Operations/components/TransactionAmount'; +import { TransactionAmount } from '@renderer/pages/Operations/components/TransactionAmount'; import { DetailRow, FootnoteText } from '@renderer/shared/ui'; import { MultisigAccount } from '@renderer/entities/account'; import { ExtendedChain } from '@renderer/entities/network'; diff --git a/src/renderer/pages/Operations/components/Log.tsx b/src/renderer/pages/Operations/components/Log.tsx index e9e3dbc8c3..a838b9fdd2 100644 --- a/src/renderer/pages/Operations/components/Log.tsx +++ b/src/renderer/pages/Operations/components/Log.tsx @@ -4,17 +4,18 @@ import { format } from 'date-fns'; import { useI18n } from '@renderer/app/providers'; import { Account, MultisigAccount } from '@renderer/entities/account'; import { ExtendedChain } from '@renderer/entities/network'; -import { MultisigEvent, SigningStatus } from '@renderer/entities/transaction'; -import TransactionTitle from './TransactionTitle/TransactionTitle'; +import { MultisigEvent, SigningStatus } from '@renderer/entities/transaction/model/transaction'; +import { TransferTypes, XcmTypes } from '@renderer/entities/transaction/lib/common/constants'; +import { TransactionTitle } from './TransactionTitle/TransactionTitle'; import OperationStatus from './OperationStatus'; -import { getSignatoryName, getTransactionAmount, sortByDateAsc } from '../common/utils'; +import { getSignatoryName, sortByDateAsc } from '../common/utils'; import { BaseModal, BodyText, FootnoteText, Identicon } from '@renderer/shared/ui'; -import { getAssetById, toAddress, SS58_DEFAULT_PREFIX } from '@renderer/shared/lib/utils'; +import { toAddress, SS58_DEFAULT_PREFIX } from '@renderer/shared/lib/utils'; import { ExtrinsicExplorers } from '@renderer/components/common'; import { Contact } from '@renderer/entities/contact'; import { useMultisigEvent } from '@renderer/entities/multisig'; import { MultisigTransactionDS } from '@renderer/shared/api/storage'; -import { AssetIcon } from '@renderer/entities/asset'; +import { TransactionAmount } from '../components/TransactionAmount'; type Props = { tx: MultisigTransactionDS; @@ -42,9 +43,7 @@ const LogModal = ({ isOpen, onClose, tx, account, connection, contacts, accounts const { transaction, description, status } = tx; const approvals = events.filter((e) => e.status === 'SIGNED'); - const asset = getAssetById(transaction?.args.assetId, connection?.assets); const addressPrefix = connection?.addressPrefix || SS58_DEFAULT_PREFIX; - const showAsset = Boolean(transaction && getTransactionAmount(transaction)); const groupedEvents = groupBy(events, ({ dateCreated }) => format(new Date(dateCreated || 0), 'PP', { locale: dateLocale }), @@ -67,6 +66,12 @@ const LogModal = ({ isOpen, onClose, tx, account, connection, contacts, accounts return `${signatoryName} ${t(eventMessage)}`; }; + const showTxAmount = (): boolean => { + if (!transaction?.type) return false; + + return [...TransferTypes, ...XcmTypes].includes(transaction.type); + }; + return ( -
        - {showAsset && } - - -
        - -
        +
        + + {transaction && showTxAmount() && ( + + )} + + +
        diff --git a/src/renderer/pages/Operations/components/Operation.tsx b/src/renderer/pages/Operations/components/Operation.tsx index 9342266e0c..12fe0cb616 100644 --- a/src/renderer/pages/Operations/components/Operation.tsx +++ b/src/renderer/pages/Operations/components/Operation.tsx @@ -1,16 +1,16 @@ import { format } from 'date-fns'; import { useI18n } from '@renderer/app/providers'; -import TransactionTitle from './TransactionTitle/TransactionTitle'; +import { TransactionTitle } from './TransactionTitle/TransactionTitle'; import { MultisigAccount } from '@renderer/entities/account'; import { FootnoteText, Accordion } from '@renderer/shared/ui'; -import TransactionAmount from './TransactionAmount'; +import { TransactionAmount } from './TransactionAmount'; import OperationStatus from './OperationStatus'; import OperationFullInfo from './OperationFullInfo'; -import { getTransactionAmount } from '@renderer/pages/Operations/common/utils'; import { MultisigTransactionDS } from '@renderer/shared/api/storage'; import { useMultisigEvent } from '@renderer/entities/multisig'; import { ChainTitle } from '@renderer/entities/chain'; +import { getTransactionAmount } from '../common/utils'; type Props = { tx: MultisigTransactionDS; @@ -25,22 +25,27 @@ const Operation = ({ tx, account }: Props) => { const approvals = events?.filter((e) => e.status === 'SIGNED') || []; const initEvent = approvals.find((e) => e.accountId === tx.depositor); + const date = new Date(tx.dateCreated || initEvent?.dateCreated || Date.now()); return ( - -
        - - {format(new Date(tx.dateCreated || initEvent?.dateCreated || Date.now()), 'p', { locale: dateLocale })} - - - {tx.transaction && getTransactionAmount(tx.transaction) ? ( - - ) : ( - + +
        +
        + + {format(date, 'p', { locale: dateLocale })} + +
        + + + + {tx.transaction && getTransactionAmount(tx.transaction) && ( + )} - -
        + + + +
        diff --git a/src/renderer/pages/Operations/components/OperationStatus.tsx b/src/renderer/pages/Operations/components/OperationStatus.tsx index 496b6b09c0..db571a1e33 100644 --- a/src/renderer/pages/Operations/components/OperationStatus.tsx +++ b/src/renderer/pages/Operations/components/OperationStatus.tsx @@ -29,14 +29,19 @@ type Props = { status: MultisigTransaction['status']; signed?: number; threshold?: number; + className?: string; }; -const OperationStatus = ({ status, signed, threshold }: Props) => { +const OperationStatus = ({ status, signed, threshold, className }: Props) => { const { t } = useI18n(); return ( {status === 'SIGNING' diff --git a/src/renderer/pages/Operations/components/ShortTransactionInfo.test.tsx b/src/renderer/pages/Operations/components/ShortTransactionInfo.test.tsx index 08f0249a65..14f7ac8268 100644 --- a/src/renderer/pages/Operations/components/ShortTransactionInfo.test.tsx +++ b/src/renderer/pages/Operations/components/ShortTransactionInfo.test.tsx @@ -1,6 +1,6 @@ import { act, render, screen } from '@testing-library/react'; -import TransactionAmount from './TransactionAmount'; +import { TransactionAmount } from './TransactionAmount'; import { Transaction, TransactionType } from '@renderer/entities/transaction'; import { TEST_ADDRESS, TEST_CHAIN_ID } from '@renderer/shared/lib/utils'; diff --git a/src/renderer/pages/Operations/components/TransactionAmount.tsx b/src/renderer/pages/Operations/components/TransactionAmount.tsx index aea4ea1a0c..f4e9119036 100644 --- a/src/renderer/pages/Operations/components/TransactionAmount.tsx +++ b/src/renderer/pages/Operations/components/TransactionAmount.tsx @@ -4,7 +4,7 @@ import { DecodedTransaction, Transaction } from '@renderer/entities/transaction' import { useChains } from '@renderer/entities/network'; import { Asset, AssetBalance } from '@renderer/entities/asset'; import { getAssetById } from '@renderer/shared/lib/utils'; -import { getTransactionAmount } from '@renderer/pages/Operations/common/utils'; +import { getTransactionAmount } from '../common/utils'; type Props = { tx: Transaction | DecodedTransaction; @@ -12,7 +12,7 @@ type Props = { type BalanceProps = Pick, 'className' | 'showIcon' | 'wrapperClassName'>; -const TransactionAmount = ({ tx, ...balanceProps }: Props & BalanceProps) => { +export const TransactionAmount = ({ tx, ...balanceProps }: Props & BalanceProps) => { const { getChainById } = useChains(); const [assets, setAssets] = useState([]); @@ -27,5 +27,3 @@ const TransactionAmount = ({ tx, ...balanceProps }: Props & BalanceProps) => { return ; }; - -export default TransactionAmount; diff --git a/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.test.tsx b/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.test.tsx index 5333dd15a4..32959a316f 100644 --- a/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.test.tsx +++ b/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.test.tsx @@ -1,8 +1,8 @@ -import { act, render, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; -import TransactionTitle from './TransactionTitle'; import { Transaction, TransactionType } from '@renderer/entities/transaction'; import { TEST_ADDRESS, TEST_CHAIN_ID } from '@renderer/shared/lib/utils'; +import { TransactionTitle } from './TransactionTitle'; jest.mock('@renderer/app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ @@ -20,11 +20,9 @@ const transaction = { }, } as Transaction; -describe('screen/Operations/components/TransactionTitle', () => { - test('should render component', async () => { - await act(async () => { - render(); - }); +describe('pages/Operations/components/TransactionTitle', () => { + test('should render component', () => { + render(); const title = screen.getByText('operations.titles.transfer'); expect(title).toBeInTheDocument(); diff --git a/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.tsx b/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.tsx index d8a494ab13..8f0c1f088a 100644 --- a/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.tsx +++ b/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.tsx @@ -1,3 +1,5 @@ +import { PropsWithChildren } from 'react'; + import { Icon, BodyText, FootnoteText } from '@renderer/shared/ui'; import { useI18n } from '@renderer/app/providers'; import { DecodedTransaction, Transaction } from '@renderer/entities/transaction'; @@ -7,29 +9,27 @@ import { cnTw } from '@renderer/shared/lib/utils'; type Props = { tx?: Transaction | DecodedTransaction; description?: string; - withoutIcon?: boolean; className?: string; }; -const TransactionTitle = ({ tx, description, withoutIcon, className }: Props) => { +export const TransactionTitle = ({ tx, description, className, children }: PropsWithChildren) => { const { t } = useI18n(); const iconName = getIconName(tx); - const transactionTitle = getTransactionTitle(tx); + const title = getTransactionTitle(tx); return ( -
        - {!withoutIcon && ( -
        - +
        +
        + +
        +
        +
        + {t(title)} + {children}
        - )} -
        - {t(transactionTitle)} {description && {description} }
        ); }; - -export default TransactionTitle; diff --git a/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.tsx b/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.tsx index 73f19b3c7b..69242155fd 100644 --- a/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.tsx +++ b/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.tsx @@ -40,7 +40,7 @@ export const NetworkList = ({ title, isDefaultOpen, query, networkList, children return ( - +
        {title} diff --git a/src/renderer/pages/Staking/Overview/components/ValidatorsModal/ValidatorsModal.tsx b/src/renderer/pages/Staking/Overview/components/ValidatorsModal/ValidatorsModal.tsx index b0d712dd4c..7182d48fa8 100644 --- a/src/renderer/pages/Staking/Overview/components/ValidatorsModal/ValidatorsModal.tsx +++ b/src/renderer/pages/Staking/Overview/components/ValidatorsModal/ValidatorsModal.tsx @@ -134,7 +134,7 @@ export const ValidatorsModal = ({
        {elected.length > 0 && ( - +
        {t('staking.nominators.electedTitle')} ({elected.length}) @@ -162,7 +162,7 @@ export const ValidatorsModal = ({ {notElected.length > 0 && ( - +
        {t('staking.nominators.notElectedTitle')} ({notElected.length}) diff --git a/src/renderer/shared/ui/Accordion/Accordion.tsx b/src/renderer/shared/ui/Accordion/Accordion.tsx index 699001d8b8..e0de89843f 100644 --- a/src/renderer/shared/ui/Accordion/Accordion.tsx +++ b/src/renderer/shared/ui/Accordion/Accordion.tsx @@ -18,24 +18,27 @@ const Accordion = ({ className, isDefaultOpen, children }: PropsWithChildren) => { +const Button = ({ buttonClass, iconWrapper, children }: PropsWithChildren) => { return ( - + {({ open }) => ( <> {children} - +
        + +
        )}
        diff --git a/src/renderer/shared/ui/Icon/data/arrow.tsx b/src/renderer/shared/ui/Icon/data/arrow.tsx index 0f86809af6..caa1357cd0 100644 --- a/src/renderer/shared/ui/Icon/data/arrow.tsx +++ b/src/renderer/shared/ui/Icon/data/arrow.tsx @@ -6,6 +6,7 @@ import ArrowLeftCutoutImg, { ReactComponent as ArrowLeftCutoutSvg } from '@image import SendArrowImg, { ReactComponent as SendArrowSvg } from '@images/arrows/send-arrow.svg'; import ReceiveArrowImg, { ReactComponent as ReceiveArrowSvg } from '@images/arrows/receive-arrow.svg'; import CurveArrowImg, { ReactComponent as CurveArrowSvg } from '@images/arrows/arrow-curve-left-right.svg'; +import CrossChainImg, { ReactComponent as CrossChainSvg } from '@images/arrows/cross-chain-arrow.svg'; const ArrowImages = { arrowUp: { svg: ArrowUpSvg, img: ArrowUpImg }, @@ -16,6 +17,7 @@ const ArrowImages = { sendArrow: { svg: SendArrowSvg, img: SendArrowImg }, receiveArrow: { svg: ReceiveArrowSvg, img: ReceiveArrowImg }, curveArrow: { svg: CurveArrowSvg, img: CurveArrowImg }, + crossChain: { svg: CrossChainSvg, img: CrossChainImg }, } as const; export type Arrow = keyof typeof ArrowImages; diff --git a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx index 8f3896708f..3fceb6572f 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { Transaction, DepositWithLabel, Fee } from '@renderer/entities/transaction'; -import TransactionAmount from '@renderer/pages/Operations/components/TransactionAmount'; +import { TransactionAmount } from '@renderer/pages/Operations/components/TransactionAmount'; import { Button, DetailRow, FootnoteText, Icon } from '@renderer/shared/ui'; import { Account, MultisigAccount } from '@renderer/entities/account'; import { ExtendedChain } from '@renderer/entities/network'; diff --git a/tailwind.config.ts b/tailwind.config.ts index 1b44c77b84..f377bb9aff 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -89,9 +89,6 @@ export default { outlineOffset: { reduced: '-2px', // same as outline width, so it would be aligned by inner border of element }, - gridTemplateColumns: { - 'operation-card': '72px 182px 182px 130px 130px', - }, letterSpacing: { tight: '-.01em', }, From 7ec3c5047c61b5ef8db94118e3cc96ad7a6782a4 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Wed, 6 Sep 2023 13:45:34 +0300 Subject: [PATCH 14/34] Fix: Contacts subscription (#1049) --- .../CreateMultisigAccount.test.tsx | 20 ++++++++++--------- .../CreateMultisigAccount.tsx | 6 +++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx index e6762c8515..939f117b7a 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx @@ -1,8 +1,10 @@ -import { render, screen } from '@testing-library/react'; +import { render, screen, act } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; +import { fork } from 'effector'; import noop from 'lodash/noop'; import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; +import { contactModel } from '@renderer/entities/contact'; import { CreateMultisigAccount } from './CreateMultisigAccount'; jest.mock('@renderer/app/providers', () => ({ @@ -30,12 +32,6 @@ jest.mock('@renderer/entities/account', () => ({ }), })); -jest.mock('@renderer/entities/contact', () => ({ - useContact: jest.fn().mockReturnValue({ - getLiveContacts: jest.fn().mockReturnValue([]), - }), -})); - jest.mock('@renderer/entities/wallet', () => ({ useWallet: jest.fn().mockReturnValue({ getWallets: jest.fn().mockResolvedValue([]), @@ -63,8 +59,14 @@ jest.mock('./components', () => ({ })); describe('screen/CreateMultisigAccount', () => { - test('should render component', () => { - render(, { wrapper: MemoryRouter }); + test('should render component', async () => { + fork({ + values: new Map().set(contactModel.$contacts, []), + }); + + await act(async () => { + render(, { wrapper: MemoryRouter }); + }); const text = screen.getByText('createMultisigAccount.title'); const form = screen.getByText('walletForm'); diff --git a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx index f346a2e13f..aa0012be85 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx @@ -1,5 +1,6 @@ import { ComponentProps, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; +import { useStore } from 'effector-react'; import { BaseModal, HeaderTitleText, StatusLabel, Button } from '@renderer/shared/ui'; import { useI18n, useMatrix, Paths } from '@renderer/app/providers'; @@ -14,10 +15,10 @@ import { useToggle } from '@renderer/shared/lib/hooks'; import { OperationResult } from '@renderer/entities/transaction'; import { MatrixModal } from '../MatrixModal/MatrixModal'; import { Wallet, useWallet } from '@renderer/entities/wallet'; -import { useContact } from '@renderer/entities/contact'; import { ExtendedContact, ExtendedWallet } from './common/types'; import { SelectSignatories, ConfirmSignatories, WalletForm } from './components'; import { AccountId } from '@renderer/domain/shared-kernel'; +import { contactModel } from '@renderer/entities/contact'; type OperationResultProps = Pick, 'variant' | 'description'>; @@ -35,7 +36,6 @@ export const CreateMultisigAccount = ({ isOpen, onClose }: Props) => { const { t } = useI18n(); const { matrix, isLoggedIn } = useMatrix(); const { getWallets } = useWallet(); - const { getLiveContacts } = useContact(); const { getAccounts, addAccount, setActiveAccount } = useAccount(); const navigate = useNavigate(); @@ -52,7 +52,7 @@ export const CreateMultisigAccount = ({ isOpen, onClose }: Props) => { const [wallets, setWallets] = useState([]); const [accounts, setAccounts] = useState([]); - const contacts = getLiveContacts(); + const contacts = useStore(contactModel.$contacts); const signatories = signatoryWallets.concat(signatoryContacts); useEffect(() => { From 4a8de5cf6e732eb519ae1fd148f5718aa43e057f Mon Sep 17 00:00:00 2001 From: egor0798 Date: Wed, 6 Sep 2023 15:54:14 +0200 Subject: [PATCH 15/34] Fix: added env variable for updater (#1045) * feat: added env var to run updater conditionaly * chore: removed env var from package build script and added it to CI * chore: removed unused import form webpack shared * chore: removed unused log * chore: added autoInstallOnAppQuit reset * chore: removed autoInstallOnAppQuit flag reset * chore: pr review --------- Co-authored-by: Egor B Co-authored-by: Yaroslav Grachev --- .github/workflows/release.yaml | 3 ++- package.json | 6 ++++++ src/main/index.ts | 13 +++++++++++-- webpack/webpack.shared.ts | 1 + 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ab2541781e..96ab44e9da 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,6 +8,7 @@ on: env: CSC_FOR_PULL_REQUEST: true CI: true + BUILD_SOURCE: 'github' jobs: release-build: @@ -92,7 +93,7 @@ jobs: filename="${original%.*}" new="$filename"_x86_64.AppImage mv "$original" "$new" - + - name: Replace space by "-" run: | for file in *; do diff --git a/package.json b/package.json index d394b6451a..16f50a5b6b 100644 --- a/package.json +++ b/package.json @@ -16,30 +16,36 @@ "start:main": "cross-env NODE_ENV=staging pnpm r webpack:main:stage start:electron", "start:main:dev": "cross-env NODE_ENV=development pnpm r webpack:main:dev start:electron", "watch": "nodemon --watch src/main --watch src/renderer/bridge --watch src/shared --ignore src/main/resources --ext \"*\" --exec", + "webpack:renderer:dev": "webpack serve --config webpack/webpack.renderer.dev.ts", "webpack:renderer:stage": "webpack serve --config webpack/webpack.renderer.stage.ts", "webpack:renderer:prod": "webpack --config webpack/webpack.renderer.prod.ts", "webpack:main:dev": "webpack --config webpack/webpack.main.dev.ts", "webpack:main:stage": "webpack --config webpack/webpack.main.stage.ts", "webpack:main:prod": "webpack --config webpack/webpack.main.prod.ts", + "build": "cross-env NODE_ENV=production CHAINS_FILE=chains pnpm r clean:prod webpack:renderer:prod webpack:main:prod", "postbuild": "node scripts/postbuild.js", "dist": "electron-builder -p never", "clean:build": "rimraf release/build", "clean:prod": "rimraf release/dist", + "test": "jest --config=jest.config.ts --json --outputFile=jest-unit-results.json", "test:integration": "jest --config=tests/integrations/jest.config.ts --group=integration --json --outputFile=jest-results.json", "test:dataVerification": "jest --config=tests/integrations/jest.config.ts --group=dataVerification --json --outputFile=jest-results.json", "test:matrix": "jest --config=tests/integrations/jest.config.ts --group=matrix --json --outputFile=jest-results.json", "test:coverage": "jest --config=jest.config.ts --coverage --passWithNoTests | tee ./coverage.txt", "test:coverage-new-files": "jest --config=jest.config.ts --coverage --changedSince=dev | tee ./coverage.txt", + "lint": "eslint . --ext=js,ts,tsx,json", "lint:fix": "pnpm lint --fix", "lint:i18n-locale": "cross-env I18N=true eslint ./src/shared/locale/ --ext=json --plugin i18n-json", "lint:i18n-fix": "cross-env I18N=true pnpm lint:i18n-locale --fix", "lint:i18n-tsx": "cross-env I18N=true eslint . --ext=tsx --ignore-pattern=**/*.test.* --ignore-pattern=**/*.stories.* --plugin i18next", + "storybook": "start-storybook -p 6006", "build:storybook": "build-storybook", + "update:chains-file": "node scripts/buildChainsJson.js", "githook:pre-commit": "cross-env NODE_ENV=production lint-staged && tsc -p tsconfig.json", "githook:pre-push": "cross-env pnpm test:coverage", diff --git a/src/main/index.ts b/src/main/index.ts index f0dbb668ec..1b855714cf 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,12 +1,14 @@ import { app, dialog } from 'electron'; import { autoUpdater } from 'electron-updater'; +import * as process from 'process'; import { MainWindow } from './main'; import { makeAppWithSingleInstanceLock } from './factories/instance'; import { makeAppSetup } from './factories/setup'; -makeAppWithSingleInstanceLock(async () => { - app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required'); +const setupAutoUpdate = () => { + if (process.env.BUILD_SOURCE !== 'github') return; + autoUpdater.autoRunAppAfterInstall = true; app.on('ready', () => { @@ -59,6 +61,13 @@ makeAppWithSingleInstanceLock(async () => { console.error('[app-updater] Error on update', err); dialog.showErrorBox('Error', 'Error updating the application'); }); +}; + +makeAppWithSingleInstanceLock(async () => { + app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required'); + + setupAutoUpdate(); + await app.whenReady(); await makeAppSetup(MainWindow); }); diff --git a/webpack/webpack.shared.ts b/webpack/webpack.shared.ts index 55c3583b2a..047c0e91ac 100644 --- a/webpack/webpack.shared.ts +++ b/webpack/webpack.shared.ts @@ -123,6 +123,7 @@ const sharedConfig: Configuration = { new webpack.DefinePlugin({ 'process.env.PRODUCT_NAME': JSON.stringify(APP_CONFIG.TITLE), 'process.env.VERSION': JSON.stringify(APP_CONFIG.VERSION), + 'process.env.BUILD_SOURCE': JSON.stringify(process.env.BUILD_SOURCE), 'process.env.CHAINS_FILE': JSON.stringify(process.env.CHAINS_FILE), }), ], From 622b40f896ede4fa03a7dc4e3ba82999d9918aef Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Fri, 8 Sep 2023 13:17:58 +0300 Subject: [PATCH 16/34] fix: clear store contacts & assets (#1054) --- .../assets/AssetRouteGuard/model/asset-guard.ts | 14 ++++++++------ .../assets/AssetRouteGuard/ui/AssetRouteGuard.tsx | 4 ++++ .../contacts/EditRouteGuard/model/edit-guard.ts | 12 +++++++----- .../contacts/EditRouteGuard/ui/EditRouteGuard.tsx | 4 ++++ 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts b/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts index 6b3ca7fe51..15988cfe23 100644 --- a/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts +++ b/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts @@ -8,20 +8,21 @@ import { ChainId } from '@renderer/domain/shared-kernel'; const { getChainById } = useChains(); +const validateUrlParams = createEvent(); +const storeCleared = createEvent(); + +export const $chain = createStore(null).reset(storeCleared); +export const $asset = createStore(null).reset(storeCleared); + type Navigation = { redirectPath: string; navigate: NavigateFunction; }; -const $navigation = createStore(null); +const $navigation = createStore(null).reset(storeCleared); const navigationApi = createApi($navigation, { navigateApiChanged: (state, { navigate, redirectPath }) => ({ ...state, navigate, redirectPath }), }); -export const $chain = createStore(null); -export const $asset = createStore(null); - -const validateUrlParams = createEvent(); - type ValidateParams = { chainId: string | null; assetId: string | null; @@ -68,4 +69,5 @@ sample({ export const events = { navigateApiChanged: navigationApi.navigateApiChanged, validateUrlParams, + storeCleared, }; diff --git a/src/renderer/features/assets/AssetRouteGuard/ui/AssetRouteGuard.tsx b/src/renderer/features/assets/AssetRouteGuard/ui/AssetRouteGuard.tsx index e06ba705d5..aa49c63bf4 100644 --- a/src/renderer/features/assets/AssetRouteGuard/ui/AssetRouteGuard.tsx +++ b/src/renderer/features/assets/AssetRouteGuard/ui/AssetRouteGuard.tsx @@ -20,6 +20,10 @@ export const AssetRouteGuard = ({ redirectPath, children }: Props) => { useEffect(() => { assetGuardModel.events.navigateApiChanged({ navigate, redirectPath }); assetGuardModel.events.validateUrlParams(searchParams); + + return () => { + assetGuardModel.events.storeCleared(); + }; }, [searchParams]); if (!chain || !asset) return null; diff --git a/src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts b/src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts index e336b3bd34..82e7c177f6 100644 --- a/src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts +++ b/src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts @@ -4,19 +4,20 @@ import { NavigateFunction } from 'react-router-dom'; import { Contact, contactModel } from '@renderer/entities/contact'; import { ContactDS } from '@renderer/shared/api/storage'; +const validateUrlParams = createEvent(); +const storeCleared = createEvent(); + +export const $contact = createStore(null).reset(storeCleared); + type Navigation = { redirectPath: string; navigate: NavigateFunction; }; -const $navigation = createStore(null); +const $navigation = createStore(null).reset(storeCleared); const navigationApi = createApi($navigation, { navigateApiChanged: (state, { navigate, redirectPath }) => ({ ...state, navigate, redirectPath }), }); -export const $contact = createStore(null); - -const validateUrlParams = createEvent(); - type ValidateParams = { contactId: string | null; contacts: ContactDS[]; @@ -52,4 +53,5 @@ sample({ export const events = { navigateApiChanged: navigationApi.navigateApiChanged, validateUrlParams, + storeCleared, }; diff --git a/src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx b/src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx index 08d0a75325..3115bc6c86 100644 --- a/src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx +++ b/src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx @@ -18,6 +18,10 @@ export const EditRouteGuard = ({ redirectPath, children }: Props) => { useEffect(() => { editGuardModel.events.navigateApiChanged({ navigate, redirectPath }); editGuardModel.events.validateUrlParams(searchParams); + + return () => { + editGuardModel.events.storeCleared(); + }; }, [searchParams]); if (!contact) return null; From 4604c0a7a3bd176cab0ce6077a65097f8498de32 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Tue, 12 Sep 2023 09:53:14 +0300 Subject: [PATCH 17/34] Feat: XCM config & service (#1051) --- docs/effector.md | 27 ++ jest.config.ts | 1 + package.json | 4 +- pnpm-lock.yaml | 145 ++++++--- src/renderer/app/App.tsx | 5 - src/renderer/app/index.tsx | 4 + src/renderer/domain/identity.ts | 2 +- src/renderer/domain/shared-kernel.ts | 6 +- src/renderer/domain/validator.ts | 4 +- .../entities/contact/model/contact.ts | 9 +- .../entities/transaction/lib/common/types.ts | 20 +- .../transaction/lib/common/xcmMethods.ts | 59 ++++ src/renderer/entities/xcm/index.ts | 1 + src/renderer/entities/xcm/model/xcm-model.ts | 30 ++ .../shared/api/xcm/__tests__/mock/xcmData.ts | 150 +++++++++ .../api/xcm/__tests__/xcmService.test.ts | 88 ++++++ .../shared/api/xcm/common/constants.ts | 2 + src/renderer/shared/api/xcm/common/types.ts | 80 +++++ src/renderer/shared/api/xcm/index.ts | 2 + src/renderer/shared/api/xcm/xcmService.ts | 285 ++++++++++++++++++ src/renderer/shared/core/index.ts | 1 + .../shared/core/model/kernel-model.ts | 7 + src/renderer/widgets/SendAssetModal/index.ts | 1 + .../model/__tests__/send-asset.test.ts | 44 +++ .../SendAssetModal/model/send-asset.ts | 41 +++ .../SendAssetModal/ui/SendAssetModal.tsx | 7 +- .../SendAssetModal/ui/common/constants.ts | 4 - .../SendAssetModal/ui/components/Details.tsx | 3 +- 28 files changed, 961 insertions(+), 71 deletions(-) create mode 100644 docs/effector.md create mode 100644 src/renderer/entities/transaction/lib/common/xcmMethods.ts create mode 100644 src/renderer/entities/xcm/index.ts create mode 100644 src/renderer/entities/xcm/model/xcm-model.ts create mode 100644 src/renderer/shared/api/xcm/__tests__/mock/xcmData.ts create mode 100644 src/renderer/shared/api/xcm/__tests__/xcmService.test.ts create mode 100644 src/renderer/shared/api/xcm/common/constants.ts create mode 100644 src/renderer/shared/api/xcm/common/types.ts create mode 100644 src/renderer/shared/api/xcm/index.ts create mode 100644 src/renderer/shared/api/xcm/xcmService.ts create mode 100644 src/renderer/shared/core/index.ts create mode 100644 src/renderer/shared/core/model/kernel-model.ts create mode 100644 src/renderer/widgets/SendAssetModal/model/__tests__/send-asset.test.ts create mode 100644 src/renderer/widgets/SendAssetModal/model/send-asset.ts delete mode 100644 src/renderer/widgets/SendAssetModal/ui/common/constants.ts diff --git a/docs/effector.md b/docs/effector.md new file mode 100644 index 0000000000..6a733674a7 --- /dev/null +++ b/docs/effector.md @@ -0,0 +1,27 @@ +# Effector + +### Sample with type guard +Sometimes you need to filter `effect` result and pass it further into `target`, but TypeScript warns you that +types do not align: `error: clock should extend target type`. + +To fix this you need to provide a `type guard` as `filter` return value. + +Let's say `getConfigFx` returns `XcmConfig | null` and `calculateFinalConfigFx` accepts only `XcmConfig`. +To make it work we do the following: + +```typescript +const getConfigFx = createEffect((config: XcmConfig): XcmConfig | null => { + // some actions +}); + +const calculateFinalConfigFx = createEffect((config: XcmConfig): string => { + // some actions +}); + +sample({ + clock: getConfigFx.doneData, + filter: (config: XcmConfig | null): config is XcmConfig => Boolean(config), + target: calculateFinalConfigFx, +}); +``` +[Documentation](https://effector.dev/docs/typescript/typing-effector/#filter--fn) diff --git a/jest.config.ts b/jest.config.ts index b4336ec6ff..c85cc9bd7f 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -38,6 +38,7 @@ const config: Config = { '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|json)$)': '/scripts/fileTransform.js', }, transformIgnorePatterns: [], + testRegex: ['^.*\\.(test|spec)\\.[jt]sx?$'], moduleNameMapper: { '\\.(css|less|scss|sass)$': 'identity-obj-proxy', '^raptorq$': '/node_modules/raptorq/raptorq.js', diff --git a/package.json b/package.json index e1a2d542eb..a702206a19 100644 --- a/package.json +++ b/package.json @@ -62,8 +62,8 @@ "@polkadot/util": "^12.2.1", "@polkadot/util-crypto": "^12.2.1", "@substrate/connect": "^0.7.26", - "@substrate/txwrapper-orml": "^6.0.1", - "@substrate/txwrapper-polkadot": "^6.0.1", + "@substrate/txwrapper-orml": "^7.0.1", + "@substrate/txwrapper-polkadot": "^7.0.1", "@zxing/browser": "^0.1.3", "@zxing/library": "^0.20.0", "bignumber.js": "^9.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9bf084aba3..d2b1bf56b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + dependencies: '@apollo/client': specifier: ^3.7.1 @@ -18,7 +22,7 @@ dependencies: version: 12.2.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) '@polkadot/react-identicon': specifier: ^3.4.1 - version: 3.4.1(@babel/core@7.18.6)(@polkadot/keyring@12.2.1)(@polkadot/networks@12.4.2)(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + version: 3.4.1(@babel/core@7.18.6)(@polkadot/keyring@12.2.1)(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1)(react-dom@18.2.0)(react@18.2.0) '@polkadot/rpc-provider': specifier: ^10.7.1 version: 10.7.1 @@ -35,11 +39,11 @@ dependencies: specifier: ^0.7.26 version: 0.7.26 '@substrate/txwrapper-orml': - specifier: ^6.0.1 - version: 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + specifier: ^7.0.1 + version: 7.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) '@substrate/txwrapper-polkadot': - specifier: ^6.0.1 - version: 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + specifier: ^7.0.1 + version: 7.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) '@zxing/browser': specifier: ^0.1.3 version: 0.1.3(@zxing/library@0.20.0) @@ -135,7 +139,7 @@ dependencies: version: 3.0.0 styled-components: specifier: ^5.3.6 - version: 5.3.6(@babel/core@7.18.6)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + version: 5.3.6(@babel/core@7.18.6)(react-dom@18.2.0)(react@18.2.0) swiper: specifier: ^8.3.2 version: 8.3.2 @@ -191,7 +195,7 @@ devDependencies: version: 6.5.9(@swc/core@1.3.80)(eslint@8.19.0)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.3)(webpack-cli@5.1.4) '@storybook/react': specifier: ^6.5.9 - version: 6.5.9(@babel/core@7.18.6)(@storybook/builder-webpack5@6.5.9)(@storybook/manager-webpack5@6.5.9)(@swc/core@1.3.80)(eslint@8.19.0)(react-dom@18.2.0)(react@18.2.0)(require-from-string@2.0.2)(typescript@4.9.3)(webpack-cli@5.1.4)(webpack-dev-server@4.15.1) + version: 6.5.9(@babel/core@7.18.6)(@storybook/builder-webpack5@6.5.9)(@storybook/manager-webpack5@6.5.9)(@swc/core@1.3.80)(eslint@8.19.0)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.3)(webpack-cli@5.1.4)(webpack-dev-server@4.15.1) '@svgr/webpack': specifier: ^6.2.1 version: 6.2.1 @@ -215,7 +219,7 @@ devDependencies: version: 13.3.0(react-dom@18.2.0)(react@18.2.0) '@testing-library/user-event': specifier: ^14.2.1 - version: 14.2.1(@testing-library/dom@9.3.1) + version: 14.2.1 '@types/jest': specifier: ^28.1.4 version: 28.1.4 @@ -347,7 +351,7 @@ devDependencies: version: 15.0.0 jest-runner-groups: specifier: ^2.2.0 - version: 2.2.0(jest-docblock@29.6.3)(jest-runner@29.6.4) + version: 2.2.0 lint-staged: specifier: ^13.0.3 version: 13.0.3 @@ -3146,7 +3150,7 @@ packages: tslib: 2.6.2 dev: false - /@polkadot/react-identicon@3.4.1(@babel/core@7.18.6)(@polkadot/keyring@12.2.1)(@polkadot/networks@12.4.2)(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0): + /@polkadot/react-identicon@3.4.1(@babel/core@7.18.6)(@polkadot/keyring@12.2.1)(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-0S8fofxKus3IORdUSSnIaiF0HKA6F+G//3+KdwiAmAlQWyfs94njQKP4IgouFSbueVyYMh4fbthQ8civ65Bkgg==} engines: {node: '>=16'} peerDependencies: @@ -3158,7 +3162,7 @@ packages: react-is: '*' dependencies: '@polkadot/keyring': 12.2.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) - '@polkadot/ui-settings': 3.4.1(@polkadot/networks@12.4.2)(@polkadot/util@12.2.1) + '@polkadot/ui-settings': 3.4.1(@polkadot/util@12.2.1) '@polkadot/ui-shared': 3.4.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) '@polkadot/util': 12.2.1 '@polkadot/util-crypto': 12.2.1(@polkadot/util@12.2.1) @@ -3167,12 +3171,10 @@ packages: react: 18.2.0 react-copy-to-clipboard: 5.1.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0) - react-is: 18.2.0 - styled-components: 5.3.11(@babel/core@7.18.6)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.11(@babel/core@7.18.6)(react-dom@18.2.0)(react@18.2.0) tslib: 2.6.2 transitivePeerDependencies: - '@babel/core' - - '@polkadot/networks' dev: false /@polkadot/rpc-augment@10.7.1: @@ -3292,11 +3294,10 @@ packages: tslib: 2.6.2 dev: false - /@polkadot/ui-settings@3.4.1(@polkadot/networks@12.4.2)(@polkadot/util@12.2.1): + /@polkadot/ui-settings@3.4.1(@polkadot/util@12.2.1): resolution: {integrity: sha512-2ym8ipRl14dedExABx/+NBLxh/8W8yMukY72db+weguJBC8/AAgNAzSX4tub9IGivArSTgi2T2/zLNXEKYtA+Q==} engines: {node: '>=16'} peerDependencies: - '@polkadot/networks': '*' '@polkadot/util': '*' dependencies: '@polkadot/networks': 12.4.2 @@ -4787,7 +4788,7 @@ packages: - supports-color dev: true - /@storybook/react@6.5.9(@babel/core@7.18.6)(@storybook/builder-webpack5@6.5.9)(@storybook/manager-webpack5@6.5.9)(@swc/core@1.3.80)(eslint@8.19.0)(react-dom@18.2.0)(react@18.2.0)(require-from-string@2.0.2)(typescript@4.9.3)(webpack-cli@5.1.4)(webpack-dev-server@4.15.1): + /@storybook/react@6.5.9(@babel/core@7.18.6)(@storybook/builder-webpack5@6.5.9)(@storybook/manager-webpack5@6.5.9)(@swc/core@1.3.80)(eslint@8.19.0)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.3)(webpack-cli@5.1.4)(webpack-dev-server@4.15.1): resolution: {integrity: sha512-Rp+QaTQAzxJhwuzJXVd49mnIBLQRlF8llTxPT2YoGHdrGkku/zl/HblQ6H2yzEf15367VyzaAv/BpLsO9Jlfxg==} engines: {node: '>=10.13.0'} hasBin: true @@ -4852,7 +4853,6 @@ packages: react-refresh: 0.11.0 read-pkg-up: 7.0.1 regenerator-runtime: 0.13.9 - require-from-string: 2.0.2 ts-dedent: 2.2.0 typescript: 4.9.3 util-deprecate: 1.0.2 @@ -5032,8 +5032,8 @@ packages: resolution: {integrity: sha512-USEkXA46P9sqClL7PZv0QFsit4S8Im97wchKG0/H/9q3AT/S76r40UHfCr4Un7eBJPE23f7fU9BZ0ITpP9MCsA==} dev: false - /@substrate/txwrapper-core@6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): - resolution: {integrity: sha512-pwYvrDgTyNRqOxxmmbhjpuPoZR8+WKYtmY4Eqv++FUUfPVo6xMQGps8n1LQAwKrA3/79F0dCrdV2LPDlUtjmYQ==} + /@substrate/txwrapper-core@7.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): + resolution: {integrity: sha512-cr2p/Z6Q4FPKFiHg2DsXVC7GfI12AcMrsXuRCs4xnJO4ja8YCORhcgbQB5VFy+BYvV7j5tmilQSdkEyLhj+w1g==} dependencies: '@polkadot/api': 10.7.1 '@polkadot/keyring': 12.2.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) @@ -5046,10 +5046,10 @@ packages: - utf-8-validate dev: false - /@substrate/txwrapper-orml@6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): - resolution: {integrity: sha512-hKrThXBdXs1/Hsn34N2KKoYpFJrBnYr8ruoshXdvjz3dJ/R475kjkEKLFf06ZegGSdMOuRvqcjQP5kU21PI0Tw==} + /@substrate/txwrapper-orml@7.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): + resolution: {integrity: sha512-tSvci4361v0wRf1TuDIcjlMWJG6DCK0DfSTRV8Y8YHus8h2Vwp1oNdw0JqTTLsbq1ZwxjlK3wu8gQcWi6gBJgA==} dependencies: - '@substrate/txwrapper-core': 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + '@substrate/txwrapper-core': 7.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) transitivePeerDependencies: - '@polkadot/util' - '@polkadot/util-crypto' @@ -5058,11 +5058,11 @@ packages: - utf-8-validate dev: false - /@substrate/txwrapper-polkadot@6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): - resolution: {integrity: sha512-adIa8/npvB0+Y2chgx6cwE5BEcGK7He+yrLiSIUyd5f92pW+94nt48mOLyKm9fJl1Uvsc23Apn6USECAtouPfQ==} + /@substrate/txwrapper-polkadot@7.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): + resolution: {integrity: sha512-HYDfq8BnqWPzruafjEPCsvUGGp1s3zWub/Sms2q4e5D+KTTy3yyA9m/s9fod9bRdUO4wiHP3NNXkACp8nW3Psg==} dependencies: - '@substrate/txwrapper-core': 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) - '@substrate/txwrapper-substrate': 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + '@substrate/txwrapper-core': 7.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + '@substrate/txwrapper-substrate': 7.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) transitivePeerDependencies: - '@polkadot/util' - '@polkadot/util-crypto' @@ -5071,10 +5071,10 @@ packages: - utf-8-validate dev: false - /@substrate/txwrapper-substrate@6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): - resolution: {integrity: sha512-LxskfiF1lf+/TMU1C50imLl9+NR+/Hx/BIqf4KfbiF7pXgGFN2bUs/50p5NIJmCt78DRNrHvtVoRraINeMbvLA==} + /@substrate/txwrapper-substrate@7.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1): + resolution: {integrity: sha512-E7DKTq2p3l6i3BOoim0hunAPu6oJAyYv1J26mWXxO7/GS2127WHtrePXfEDFMDc4v/wezyqn7KR9E4K46YqJGQ==} dependencies: - '@substrate/txwrapper-core': 6.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) + '@substrate/txwrapper-core': 7.0.1(@polkadot/util-crypto@12.2.1)(@polkadot/util@12.2.1) transitivePeerDependencies: - '@polkadot/util' - '@polkadot/util-crypto' @@ -5450,13 +5450,11 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@testing-library/user-event@14.2.1(@testing-library/dom@9.3.1): + /@testing-library/user-event@14.2.1: resolution: {integrity: sha512-HOr1QiODrq+0j9lKU5i10y9TbhxMBMRMGimNx10asdmau9cb8Xb1Vyg0GvTwyIL2ziQyh2kAloOtAQFBQVuecA==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' - dependencies: - '@testing-library/dom': 9.3.1 dev: true /@tokenizer/token@0.3.0: @@ -5786,6 +5784,7 @@ packages: /@types/plist@3.0.2: resolution: {integrity: sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==} + requiresBuild: true dependencies: '@types/node': 18.0.0 xmlbuilder: 15.1.1 @@ -5915,6 +5914,7 @@ packages: /@types/verror@1.10.6: resolution: {integrity: sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==} + requiresBuild: true dev: true optional: true @@ -6558,10 +6558,8 @@ packages: ajv: 6.12.6 dev: true - /ajv-formats@2.1.1(ajv@8.12.0): + /ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 peerDependenciesMeta: ajv: optional: true @@ -6821,6 +6819,7 @@ packages: /array-find-index@1.0.2: resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: true optional: true @@ -6992,6 +6991,7 @@ packages: /async-each@1.0.6: resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==} + requiresBuild: true dev: true optional: true @@ -7242,7 +7242,7 @@ packages: '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.18.6) lodash: 4.17.21 picomatch: 2.3.1 - styled-components: 5.3.11(@babel/core@7.18.6)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.11(@babel/core@7.18.6)(react-dom@18.2.0)(react@18.2.0) transitivePeerDependencies: - '@babel/core' dev: false @@ -7257,7 +7257,7 @@ packages: '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.18.6) lodash: 4.17.21 picomatch: 2.3.1 - styled-components: 5.3.6(@babel/core@7.18.6)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.6(@babel/core@7.18.6)(react-dom@18.2.0)(react@18.2.0) transitivePeerDependencies: - '@babel/core' dev: false @@ -7345,6 +7345,7 @@ packages: /big-integer@1.6.51: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} + requiresBuild: true dev: true optional: true @@ -7384,6 +7385,7 @@ packages: /binary-extensions@1.13.1: resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: true optional: true @@ -7398,6 +7400,7 @@ packages: /bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + requiresBuild: true dependencies: file-uri-to-path: 1.0.0 dev: true @@ -7454,6 +7457,7 @@ packages: /boolean@3.2.0: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} + requiresBuild: true dev: true optional: true @@ -7473,6 +7477,7 @@ packages: /bplist-parser@0.1.1: resolution: {integrity: sha512-2AEM0FXy8ZxVLBuqX0hqt1gDwcnz2zygEkQ6zaD5Wko/sB9paUNwlpawrFtKeHUAQUOzjVy9AO4oeonqIHKA9Q==} + requiresBuild: true dependencies: big-integer: 1.6.51 dev: true @@ -7641,6 +7646,7 @@ packages: /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + requiresBuild: true dependencies: base64-js: 1.5.1 ieee754: 1.2.1 @@ -7853,6 +7859,7 @@ packages: /camelcase-keys@2.1.0: resolution: {integrity: sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: camelcase: 2.1.1 map-obj: 1.0.1 @@ -7871,6 +7878,7 @@ packages: /camelcase@2.1.1: resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: true optional: true @@ -7974,6 +7982,7 @@ packages: /chokidar@2.1.8: resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==} deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies + requiresBuild: true dependencies: anymatch: 2.0.0 async-each: 1.0.6 @@ -8349,6 +8358,7 @@ packages: /config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + requiresBuild: true dependencies: ini: 1.3.8 proto-list: 1.2.4 @@ -8576,6 +8586,7 @@ packages: /crc@3.8.0: resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==} + requiresBuild: true dependencies: buffer: 5.7.1 dev: true @@ -8926,6 +8937,7 @@ packages: /currently-unhandled@0.4.1: resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: array-find-index: 1.0.2 dev: true @@ -9830,6 +9842,7 @@ packages: /es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} + requiresBuild: true dev: true optional: true @@ -10508,6 +10521,7 @@ packages: /extsprintf@1.4.1: resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==} engines: {'0': node >=0.6.0} + requiresBuild: true dev: true optional: true @@ -10666,6 +10680,7 @@ packages: /file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + requiresBuild: true dev: true optional: true @@ -10741,6 +10756,7 @@ packages: /find-up@1.1.2: resolution: {integrity: sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: path-exists: 2.1.0 pinkie-promise: 2.0.1 @@ -11145,6 +11161,7 @@ packages: /get-stdin@4.0.1: resolution: {integrity: sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: true optional: true @@ -11915,6 +11932,7 @@ packages: resolution: {integrity: sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==} engines: {node: ^8.11.2 || >=10} os: [darwin] + requiresBuild: true dependencies: cli-truncate: 2.1.0 node-addon-api: 1.7.2 @@ -12013,6 +12031,7 @@ packages: /indent-string@2.1.0: resolution: {integrity: sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: repeating: 2.0.1 dev: true @@ -12159,6 +12178,7 @@ packages: /is-binary-path@1.0.1: resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: binary-extensions: 1.13.1 dev: true @@ -12286,6 +12306,7 @@ packages: /is-finite@1.1.0: resolution: {integrity: sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: true optional: true @@ -12481,6 +12502,7 @@ packages: /is-utf8@0.2.1: resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} + requiresBuild: true dev: true optional: true @@ -13045,15 +13067,12 @@ packages: slash: 3.0.0 dev: true - /jest-runner-groups@2.2.0(jest-docblock@29.6.3)(jest-runner@29.6.4): + /jest-runner-groups@2.2.0: resolution: {integrity: sha512-Sp/B9ZX0CDAKa9dIkgH0sGyl2eDuScV4SVvOxqhBMxqWpsNAkmol/C58aTFmPWZj+C0ZTW1r1BSu66MTCN+voA==} engines: {node: '>= 10.14.2'} peerDependencies: jest-docblock: '>= 24' jest-runner: '>= 24' - dependencies: - jest-docblock: 29.6.3 - jest-runner: 29.6.4 dev: true /jest-runner@29.6.4: @@ -13587,6 +13606,7 @@ packages: /load-json-file@1.1.0: resolution: {integrity: sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: graceful-fs: 4.2.11 parse-json: 2.2.0 @@ -13768,6 +13788,7 @@ packages: /loud-rejection@1.6.0: resolution: {integrity: sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: currently-unhandled: 0.4.1 signal-exit: 3.0.7 @@ -13897,6 +13918,7 @@ packages: /matcher@3.0.0: resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} engines: {node: '>=10'} + requiresBuild: true dependencies: escape-string-regexp: 4.0.0 dev: true @@ -14039,6 +14061,7 @@ packages: /meow@3.7.0: resolution: {integrity: sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: camelcase-keys: 2.1.0 decamelize: 1.2.0 @@ -14373,6 +14396,7 @@ packages: /nan@2.17.0: resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==} + requiresBuild: true dev: true optional: true @@ -14446,6 +14470,7 @@ packages: /node-addon-api@1.7.2: resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} + requiresBuild: true dev: true optional: true @@ -14595,6 +14620,7 @@ packages: /npm-conf@1.1.3: resolution: {integrity: sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==} engines: {node: '>=4'} + requiresBuild: true dependencies: config-chain: 1.1.13 pify: 3.0.0 @@ -14865,6 +14891,7 @@ packages: /os-homedir@1.0.2: resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: true optional: true @@ -15051,6 +15078,7 @@ packages: /parse-json@2.2.0: resolution: {integrity: sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: error-ex: 1.3.2 dev: true @@ -15111,11 +15139,13 @@ packages: /path-dirname@1.0.2: resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==} + requiresBuild: true dev: true /path-exists@2.1.0: resolution: {integrity: sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: pinkie-promise: 2.0.1 dev: true @@ -15160,6 +15190,7 @@ packages: /path-type@1.1.0: resolution: {integrity: sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: graceful-fs: 4.2.11 pify: 2.3.0 @@ -15250,6 +15281,7 @@ packages: /pinkie-promise@2.0.1: resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: pinkie: 2.0.4 dev: true @@ -15258,6 +15290,7 @@ packages: /pinkie@2.0.4: resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: true optional: true @@ -16004,6 +16037,7 @@ packages: /proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + requiresBuild: true dev: true optional: true @@ -16306,6 +16340,7 @@ packages: /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true /react-merge-refs@1.1.0: resolution: {integrity: sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==} @@ -16382,6 +16417,7 @@ packages: /read-pkg-up@1.0.1: resolution: {integrity: sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: find-up: 1.1.2 read-pkg: 1.1.0 @@ -16400,6 +16436,7 @@ packages: /read-pkg@1.1.0: resolution: {integrity: sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: load-json-file: 1.1.0 normalize-package-data: 2.5.0 @@ -16456,6 +16493,7 @@ packages: /readdirp@2.2.1: resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} engines: {node: '>=0.10'} + requiresBuild: true dependencies: graceful-fs: 4.2.11 micromatch: 3.1.10 @@ -16489,6 +16527,7 @@ packages: /redent@1.0.0: resolution: {integrity: sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: indent-string: 2.1.0 strip-indent: 1.0.1 @@ -16696,6 +16735,7 @@ packages: /repeating@2.0.1: resolution: {integrity: sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: is-finite: 1.1.0 dev: true @@ -16873,6 +16913,7 @@ packages: /roarr@2.15.4: resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} engines: {node: '>=8.0'} + requiresBuild: true dependencies: boolean: 3.2.0 detect-node: 2.1.0 @@ -17024,7 +17065,7 @@ packages: dependencies: '@types/json-schema': 7.0.12 ajv: 8.12.0 - ajv-formats: 2.1.1(ajv@8.12.0) + ajv-formats: 2.1.1 ajv-keywords: 5.1.0(ajv@8.12.0) dev: true @@ -17046,6 +17087,7 @@ packages: /semver-compare@1.0.0: resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} + requiresBuild: true dev: true optional: true @@ -17113,6 +17155,7 @@ packages: /serialize-error@7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} + requiresBuild: true dependencies: type-fest: 0.13.1 dev: true @@ -17324,6 +17367,7 @@ packages: /smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + requiresBuild: true dev: true optional: true @@ -17527,6 +17571,7 @@ packages: /sprintf-js@1.1.2: resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==} + requiresBuild: true dev: true optional: true @@ -17778,6 +17823,7 @@ packages: /strip-bom@2.0.0: resolution: {integrity: sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: is-utf8: 0.2.1 dev: true @@ -17812,6 +17858,7 @@ packages: resolution: {integrity: sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==} engines: {node: '>=0.10.0'} hasBin: true + requiresBuild: true dependencies: get-stdin: 4.0.1 dev: true @@ -17895,7 +17942,7 @@ packages: inline-style-parser: 0.1.1 dev: true - /styled-components@5.3.11(@babel/core@7.18.6)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0): + /styled-components@5.3.11(@babel/core@7.18.6)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==} engines: {node: '>=10'} peerDependencies: @@ -17913,14 +17960,13 @@ packages: hoist-non-react-statics: 3.3.2 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-is: 18.2.0 shallowequal: 1.1.0 supports-color: 5.5.0 transitivePeerDependencies: - '@babel/core' dev: false - /styled-components@5.3.6(@babel/core@7.18.6)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0): + /styled-components@5.3.6(@babel/core@7.18.6)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==} engines: {node: '>=10'} requiresBuild: true @@ -17939,7 +17985,6 @@ packages: hoist-non-react-statics: 3.3.2 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-is: 18.2.0 shallowequal: 1.1.0 supports-color: 5.5.0 transitivePeerDependencies: @@ -18425,6 +18470,7 @@ packages: /trim-newlines@1.0.0: resolution: {integrity: sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: true optional: true @@ -18645,6 +18691,7 @@ packages: /tunnel@0.0.6: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + requiresBuild: true dev: true optional: true @@ -18667,6 +18714,7 @@ packages: /type-fest@0.13.1: resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} engines: {node: '>=10'} + requiresBuild: true dev: true optional: true @@ -18970,6 +19018,7 @@ packages: /untildify@2.1.0: resolution: {integrity: sha512-sJjbDp2GodvkB0FZZcn7k6afVisqX5BZD7Yq3xp4nN2O15BBK0cLm3Vwn2vQaF7UDS0UUsrQMkkplmDI5fskig==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: os-homedir: 1.0.2 dev: true @@ -18986,6 +19035,7 @@ packages: /upath@1.2.0: resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} engines: {node: '>=4'} + requiresBuild: true dev: true optional: true @@ -19177,6 +19227,7 @@ packages: /verror@1.10.1: resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} engines: {node: '>=0.6.0'} + requiresBuild: true dependencies: assert-plus: 1.0.0 core-util-is: 1.0.2 diff --git a/src/renderer/app/App.tsx b/src/renderer/app/App.tsx index 0066916728..4cd180c77c 100644 --- a/src/renderer/app/App.tsx +++ b/src/renderer/app/App.tsx @@ -4,7 +4,6 @@ import { useNavigate, useRoutes } from 'react-router-dom'; import { FallbackScreen } from '@renderer/components/common'; import { useAccount } from '@renderer/entities/account'; -import { contactModel } from '@renderer/entities/contact'; import { ConfirmDialogProvider, I18Provider, @@ -26,10 +25,6 @@ const App = () => { const [showSplashScreen, setShowSplashScreen] = useState(true); const [isAccountsLoading, setIsAccountsLoading] = useState(true); - useEffect(() => { - contactModel.events.appStarted(); - }, []); - useEffect(() => { setTimeout(() => setShowSplashScreen(false), SPLASH_SCREEN_DELAY); diff --git a/src/renderer/app/index.tsx b/src/renderer/app/index.tsx index ae34b251e8..f73a3cd76f 100644 --- a/src/renderer/app/index.tsx +++ b/src/renderer/app/index.tsx @@ -2,6 +2,7 @@ import { createRoot } from 'react-dom/client'; import { HashRouter as Router } from 'react-router-dom'; import log from 'electron-log'; +import { kernelModel } from '@renderer/shared/core'; import App from './App'; import './i18n'; @@ -26,6 +27,8 @@ if (!container) { throw new Error('Root container is missing in index.html'); } +kernelModel.events.appStarted(); + createRoot(container).render( @@ -33,4 +36,5 @@ createRoot(container).render( ); // NOTE: React 18 Strict mode renders twice in DEV mode +// which leads to errors in components that use camera // https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state diff --git a/src/renderer/domain/identity.ts b/src/renderer/domain/identity.ts index 369a9b6ce0..1fbaf0ac2e 100644 --- a/src/renderer/domain/identity.ts +++ b/src/renderer/domain/identity.ts @@ -1,4 +1,4 @@ -import { Address } from '@renderer/domain/shared-kernel'; +import { Address } from './shared-kernel'; export type Identity = { subName: string; diff --git a/src/renderer/domain/shared-kernel.ts b/src/renderer/domain/shared-kernel.ts index 7e52cbf050..41e46705f5 100644 --- a/src/renderer/domain/shared-kernel.ts +++ b/src/renderer/domain/shared-kernel.ts @@ -1,10 +1,14 @@ +export type ChainId = HexString; export type HexString = `0x${string}`; + export type Address = string; export type AccountId = HexString; + export type Threshold = number; + export type CallData = HexString; export type CallHash = HexString; -export type ChainId = HexString; + export type EraIndex = number; export type Timepoint = { height: number; diff --git a/src/renderer/domain/validator.ts b/src/renderer/domain/validator.ts index db9556250b..da634b7bd9 100644 --- a/src/renderer/domain/validator.ts +++ b/src/renderer/domain/validator.ts @@ -1,5 +1,5 @@ -import { Identity } from '@renderer/domain/identity'; -import { Address, ChainId } from '@renderer/domain/shared-kernel'; +import { Identity } from './identity'; +import { Address, ChainId } from './shared-kernel'; export type Validator = { address: Address; diff --git a/src/renderer/entities/contact/model/contact.ts b/src/renderer/entities/contact/model/contact.ts index f9679c2280..25ef26c10d 100644 --- a/src/renderer/entities/contact/model/contact.ts +++ b/src/renderer/entities/contact/model/contact.ts @@ -1,6 +1,7 @@ -import { createEffect, createEvent, createStore, forward } from 'effector'; +import { createEffect, createStore, forward } from 'effector'; import { ContactDS } from '@renderer/shared/api/storage'; +import { kernelModel } from '@renderer/shared/core'; import { splice } from '@renderer/shared/lib/utils'; import { useContact } from '../lib'; import type { Contact } from './types'; @@ -8,7 +9,6 @@ import type { Contact } from './types'; const contactService = useContact(); export const $contacts = createStore([]); -const appStarted = createEvent(); const populateContactsFx = createEffect(() => { return contactService.getContacts(); @@ -47,13 +47,10 @@ $contacts }); forward({ - from: appStarted, + from: kernelModel.events.appStarted, to: populateContactsFx, }); -export const events = { - appStarted, -}; export const effects = { addContactFx, deleteContactFx, diff --git a/src/renderer/entities/transaction/lib/common/types.ts b/src/renderer/entities/transaction/lib/common/types.ts index a4d1f840b7..efa1dff978 100644 --- a/src/renderer/entities/transaction/lib/common/types.ts +++ b/src/renderer/entities/transaction/lib/common/types.ts @@ -1,7 +1,8 @@ import { ApiPromise } from '@polkadot/api'; -import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; +import { UnsignedTransaction, Args } from '@substrate/txwrapper-polkadot'; import { Weight } from '@polkadot/types/interfaces'; import { SubmittableExtrinsic } from '@polkadot/api/types'; +import { AnyJson } from '@polkadot/types/types'; import { Address, CallData, HexString, Timepoint, Threshold, AccountId } from '@renderer/domain/shared-kernel'; import { DecodedTransaction, Transaction } from '@renderer/entities/transaction/model/transaction'; @@ -57,3 +58,20 @@ export type ExtrinsicResultParams = { isFinalApprove: boolean; multisigError: string; }; + +export type XcmPallet = 'xcmPallet' | 'polkadotXcm'; + +export interface XcmPalletTransferArgs extends Args { + dest: AnyJson; + beneficiary: AnyJson; + assets: AnyJson; + feeAssetItem: number; + weightLimit: AnyJson; +} + +export interface XTokenPalletTransferArgs extends Args { + asset: AnyJson; + dest: AnyJson; + destWeight?: AnyJson; + destWeightLimit?: AnyJson; +} diff --git a/src/renderer/entities/transaction/lib/common/xcmMethods.ts b/src/renderer/entities/transaction/lib/common/xcmMethods.ts new file mode 100644 index 0000000000..ed12586ad9 --- /dev/null +++ b/src/renderer/entities/transaction/lib/common/xcmMethods.ts @@ -0,0 +1,59 @@ +import { BaseTxInfo, defineMethod, OptionsWithMeta, UnsignedTransaction } from '@substrate/txwrapper-polkadot'; + +import { XcmPalletTransferArgs, XcmPallet, XTokenPalletTransferArgs } from './types'; + +export function limitedReserveTransferAssets( + pallet: XcmPallet, + args: XcmPalletTransferArgs, + info: BaseTxInfo, + options: OptionsWithMeta, +): UnsignedTransaction { + return defineMethod( + { + method: { + args, + name: 'limitedReserveTransferAssets', + pallet, + }, + ...info, + }, + options, + ); +} + +export function limitedTeleportAssets( + pallet: XcmPallet, + args: XcmPalletTransferArgs, + info: BaseTxInfo, + options: OptionsWithMeta, +): UnsignedTransaction { + return defineMethod( + { + method: { + args, + name: 'limitedTeleportAssets', + pallet, + }, + ...info, + }, + options, + ); +} + +export function transferMultiAssets( + args: XTokenPalletTransferArgs, + info: BaseTxInfo, + options: OptionsWithMeta, +): UnsignedTransaction { + return defineMethod( + { + method: { + args, + name: 'transferMultiassets', + pallet: 'xTokens', + }, + ...info, + }, + options, + ); +} diff --git a/src/renderer/entities/xcm/index.ts b/src/renderer/entities/xcm/index.ts new file mode 100644 index 0000000000..061ebbc2f5 --- /dev/null +++ b/src/renderer/entities/xcm/index.ts @@ -0,0 +1 @@ +export * as xcmModel from './model/xcm-model'; diff --git a/src/renderer/entities/xcm/model/xcm-model.ts b/src/renderer/entities/xcm/model/xcm-model.ts new file mode 100644 index 0000000000..12131f4b32 --- /dev/null +++ b/src/renderer/entities/xcm/model/xcm-model.ts @@ -0,0 +1,30 @@ +import { createEffect, forward } from 'effector'; + +import { kernelModel } from '@renderer/shared/core'; +import { XcmConfig, getXcmConfig, fetchXcmConfig, saveXcmConfig } from '@renderer/shared/api/xcm'; + +const getConfigFx = createEffect((): XcmConfig | null => { + return getXcmConfig(); +}); +const fetchConfigFx = createEffect((): Promise => { + return fetchXcmConfig(); +}); +const saveConfigFx = createEffect((config: XcmConfig) => { + return saveXcmConfig(config); +}); + +forward({ + from: kernelModel.events.appStarted, + to: fetchConfigFx, +}); + +forward({ + from: fetchConfigFx.doneData, + to: saveConfigFx, +}); + +export const effects = { + getConfigFx, + fetchConfigFx, + saveConfigFx, +}; diff --git a/src/renderer/shared/api/xcm/__tests__/mock/xcmData.ts b/src/renderer/shared/api/xcm/__tests__/mock/xcmData.ts new file mode 100644 index 0000000000..0b4172f999 --- /dev/null +++ b/src/renderer/shared/api/xcm/__tests__/mock/xcmData.ts @@ -0,0 +1,150 @@ +import { Action, XcmConfig } from '../../common/types'; + +export const CONFIG: XcmConfig = { + assetsLocation: { + DOT: { + chainId: '91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3', + multiLocation: {}, + reserveFee: { + mode: { + type: 'proportional', + value: '92895362664', + }, + instructions: 'xtokensReserve', + }, + }, + + KAR: { + chainId: 'baf5aabe40646d11f0ee8abbdc64f4a4b7674925cba08e4a05ff9ebed6e2126b', + multiLocation: { parachainId: 2000, generalKey: '0x0080' }, + reserveFee: { + mode: { type: 'proportional', value: '10016000000000' }, + instructions: 'xtokensReserve', + }, + }, + ACA: { + chainId: 'fc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c', + multiLocation: { + parachainId: 2000, + generalKey: '0x0000', + }, + reserveFee: { + mode: { + type: 'proportional', + value: '10016000000000', + }, + instructions: 'xtokensReserve', + }, + }, + }, + instructions: { + xtokensDest: [Action.RESERVE_ASSET_DEPOSITED, Action.CLEAR_ORIGIN, Action.BUY_EXECUTION, Action.DEPOSIT_ASSET], + xtokensReserve: [Action.WITHDRAW_ASSET, Action.CLEAR_ORIGIN, Action.BUY_EXECUTION, Action.DEPOSIT_RESERVE_ASSET], + xcmPalletDest: [Action.RESERVE_ASSET_DEPOSITED, Action.CLEAR_ORIGIN, Action.BUY_EXECUTION, Action.DEPOSIT_ASSET], + xcmPalletTeleportDest: [ + Action.RECEIVE_TELEPORTED_ASSET, + Action.CLEAR_ORIGIN, + Action.BUY_EXECUTION, + Action.DEPOSIT_ASSET, + ], + }, + networkBaseWeight: { + b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe: '1000000000', + baf5aabe40646d11f0ee8abbdc64f4a4b7674925cba08e4a05ff9ebed6e2126b: '200000000', + fc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c: '200000000', + fe58ea77779b7abda7da4ec526d14db9b1e9cd40a217c34892af80a9b332b76d: '200000000', + '64a1c658a48b2e70a7fb1ad4c39eea35022568c20fc44a6e2e3d0a57aee6053b': '150000000', + e61a41c53f5dcd0beb09df93b34402aada44cb05117b71059cce40a2723a4e97: '150000000', + '91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3': '1000000000', + }, + chains: [ + { + chainId: 'fc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c', + assets: [ + { + assetId: 0, + assetLocation: 'ACA', + assetLocationPath: { + type: 'absolute', + }, + xcmTransfers: [ + { + destination: { + chainId: 'fe58ea77779b7abda7da4ec526d14db9b1e9cd40a217c34892af80a9b332b76d', + assetId: 3, + fee: { + mode: { + type: 'proportional', + value: '115534810638445', + }, + instructions: 'xtokensDest', + }, + }, + type: 'xtokens', + }, + { + destination: { + chainId: 'e61a41c53f5dcd0beb09df93b34402aada44cb05117b71059cce40a2723a4e97', + assetId: 3, + fee: { + mode: { + type: 'proportional', + value: '196078431372549', + }, + instructions: 'xtokensDest', + }, + }, + type: 'xtokens', + }, + ], + }, + { + assetId: 3, + assetLocation: 'DOT', + assetLocationPath: { + type: 'absolute', + }, + xcmTransfers: [ + { + destination: { + chainId: 'e61a41c53f5dcd0beb09df93b34402aada44cb05117b71059cce40a2723a4e97', + assetId: 1, + fee: { + mode: { + type: 'proportional', + value: '53711462025', + }, + instructions: 'xtokensDest', + }, + }, + type: 'xtokens', + }, + ], + }, + ], + }, + { + chainId: '401a1f9dca3da46f5c4091016c8a2f26dcea05865116b286f60f668207d1474b', + assets: [ + { + assetId: 4, + assetLocation: 'KAR', + assetLocationPath: { type: 'absolute' }, + xcmTransfers: [ + { + type: 'xtokens', + destination: { + chainId: 'baf5aabe40646d11f0ee8abbdc64f4a4b7674925cba08e4a05ff9ebed6e2126b', + assetId: 0, + fee: { + mode: { type: 'proportional', value: '10016000000000' }, + instructions: 'xtokensDest', + }, + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts b/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts new file mode 100644 index 0000000000..05528a9526 --- /dev/null +++ b/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts @@ -0,0 +1,88 @@ +import { ApiPromise } from '@polkadot/api'; + +import { XCM_KEY } from '../common/constants'; +import { estimateFee, getXcmConfig, getDestinationLocation } from '../xcmService'; +import { CONFIG } from '@renderer/shared/api/xcm/__tests__/mock/xcmData'; + +const mockApi = () => + ({ + createType: (_, typeParams) => typeParams, + } as ApiPromise); + +describe('shared/api/xcm/xcmService', () => { + beforeEach(() => { + localStorage.clear(); + }); + + test('should get empty config from localStorage', () => { + const config = getXcmConfig(); + expect(config).toEqual(null); + }); + + test('should get not empty config from localStorage', () => { + localStorage.setItem(XCM_KEY, JSON.stringify(CONFIG)); + + const config = getXcmConfig(); + expect(config).toEqual(CONFIG); + }); + + test('should calculate correct fee for ACA from Acala to Parallel ', () => { + const fee = estimateFee( + CONFIG.instructions, + CONFIG.networkBaseWeight, + CONFIG.assetsLocation['ACA'], + 'fc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c', + CONFIG.chains[0].assets[0].xcmTransfers[1], + ); + + expect(fee.toString()).toEqual('117647058823'); + }); + + test('should calculate correct fee for DOT from Acala to Parallel', () => { + const fee = estimateFee( + CONFIG.instructions, + CONFIG.networkBaseWeight, + CONFIG.assetsLocation['DOT'], + 'fc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c', + CONFIG.chains[0].assets[1].xcmTransfers[0], + ); + + expect(fee.toString()).toEqual('403808327'); + }); + + test('should calculate correct location for sibling prachain', () => { + const api = mockApi(); + + const location = getDestinationLocation(api, { parentId: '0x00' }, 2000) as any; + + expect(location.V2.parents).toEqual(1); + expect(location.V2.interior.X1.Parachain).toEqual(2000); + }); + + test('should calculate correct location for parent parachain', () => { + const api = mockApi(); + + const location = getDestinationLocation(api, { parentId: '0x00' }) as any; + + expect(location.V2.parents).toEqual(1); + expect(location.V2.interior).toEqual('Here'); + }); + + test('should calculate correct address location for parent parachain', () => { + const api = mockApi(); + + const location = getDestinationLocation(api, { parentId: '0x00' }, undefined, '0x00') as any; + + expect(location.V2.parents).toEqual(1); + expect(location.V2.interior.X1.AccountId32.id).toEqual('0x00'); + }); + + test('should calculate correct location for child parachain', () => { + const api = mockApi(); + + const location = getDestinationLocation(api, { parentId: undefined }, 2000) as any; + + expect(location.V2.parents).toEqual(0); + expect(location.V2.interior.X1.Parachain).toEqual(2000); + }); +}); diff --git a/src/renderer/shared/api/xcm/common/constants.ts b/src/renderer/shared/api/xcm/common/constants.ts new file mode 100644 index 0000000000..7a31d439bc --- /dev/null +++ b/src/renderer/shared/api/xcm/common/constants.ts @@ -0,0 +1,2 @@ +export const XCM_URL = 'https://raw.githubusercontent.com/novasamatech/nova-utils/master/xcm/v4/transfers.json'; +export const XCM_KEY = 'xcm-config'; diff --git a/src/renderer/shared/api/xcm/common/types.ts b/src/renderer/shared/api/xcm/common/types.ts new file mode 100644 index 0000000000..2e5223e491 --- /dev/null +++ b/src/renderer/shared/api/xcm/common/types.ts @@ -0,0 +1,80 @@ +import { HexString } from '@renderer/domain/shared-kernel'; + +export type AssetName = string; + +export type XcmConfig = { + assetsLocation: AssetsLocation; + instructions: Instructions; + networkBaseWeight: NetworkBaseWeight; + chains: ChainXCM[]; +}; + +export type MultiLocation = { + parents?: number; + parachainId?: number; + palletInstance?: number; + generalKey?: HexString; +}; + +export type Fee = { + instructions: InstructionType; + mode: FeeMode; +}; + +export type AssetLocation = { + chainId: string; + multiLocation: MultiLocation; + reserveFee: Fee; +}; +export type AssetsLocation = Record; + +export type InstructionType = 'xtokensDest' | 'xtokensReserve' | 'xcmPalletDest' | 'xcmPalletTeleportDest'; + +export type Instructions = Record; + +export type FeeModeType = 'proportional' | 'standard'; +export type FeeMode = { + type: FeeModeType; + value: string; +}; + +export type NetworkBaseWeight = { + [chainId: string]: string; +}; + +export type AssetXCM = { + assetId: number; + assetLocation: string; + assetLocationPath: { + type: PathType; + path?: MultiLocation; + }; + xcmTransfers: XcmTransfer[]; +}; + +export type ChainXCM = { + chainId: string; + assets: AssetXCM[]; +}; + +export type XcmTransfer = { + type: XcmTransferType; + destination: { + chainId: string; + assetId: number; + fee: Fee; + }; +}; + +export type XcmTransferType = 'xtokens' | 'xcmpallet'; +export type PathType = 'absolute' | 'relative' | 'concrete'; + +export const enum Action { + RESERVE_ASSET_DEPOSITED = 'ReserveAssetDeposited', + WITHDRAW_ASSET = 'WithdrawAsset', + RECEIVE_TELEPORTED_ASSET = 'ReceiveTeleportedAsset', + CLEAR_ORIGIN = 'ClearOrigin', + BUY_EXECUTION = 'BuyExecution', + DEPOSIT_RESERVE_ASSET = 'DepositReserveAsset', + DEPOSIT_ASSET = 'DepositAsset', +} diff --git a/src/renderer/shared/api/xcm/index.ts b/src/renderer/shared/api/xcm/index.ts new file mode 100644 index 0000000000..7d46d85074 --- /dev/null +++ b/src/renderer/shared/api/xcm/index.ts @@ -0,0 +1,2 @@ +export * from './common/types'; +export * from './xcmService'; diff --git a/src/renderer/shared/api/xcm/xcmService.ts b/src/renderer/shared/api/xcm/xcmService.ts new file mode 100644 index 0000000000..161ccb8dba --- /dev/null +++ b/src/renderer/shared/api/xcm/xcmService.ts @@ -0,0 +1,285 @@ +import { BN, BN_TEN, BN_ZERO } from '@polkadot/util'; +import { ApiPromise } from '@polkadot/api'; +import { VersionedMultiAsset, VersionedMultiLocation } from '@polkadot/types/interfaces'; + +import { XCM_URL, XCM_KEY } from './common/constants'; +import { + XcmConfig, + AssetLocation, + AssetName, + AssetXCM, + MultiLocation as LocalMultiLocation, + ChainXCM, + InstructionType, + Instructions, + NetworkBaseWeight, + XcmTransfer, +} from './common/types'; +import { AccountId, ChainId } from '@renderer/domain/shared-kernel'; +// TODO: Move chain to shared +import { Chain } from '@renderer/entities/chain'; + +export const fetchXcmConfig = async (): Promise => { + const response = await fetch(XCM_URL, { cache: 'default' }); + + return response.json(); +}; + +export const getXcmConfig = (): XcmConfig | null => { + const storageConfig = localStorage.getItem(XCM_KEY); + + try { + return storageConfig ? JSON.parse(storageConfig) : null; + } catch (error) { + console.error('Could not parse XCM config - ', error); + + return null; + } +}; + +export const saveXcmConfig = (config: XcmConfig) => { + localStorage.setItem(XCM_KEY, JSON.stringify(config)); +}; + +export const getAvailableDirections = (chains: ChainXCM[], assetId: number, chainId: ChainId): XcmTransfer[] => { + const chain = chains.find((c) => c.chainId === chainId); + const asset = chain?.assets.find((a) => a.assetId === assetId); + + return asset?.xcmTransfers || []; +}; + +export const weightToFee = (weight: BN, unitsPerSecond: BN): BN => { + const pico = BN_TEN.pow(new BN(12)); + + return weight.mul(unitsPerSecond).div(pico); +}; + +export const getEstimatedWeight = ( + instructions: Instructions, + instructionName: InstructionType, + instructionWeight: BN, +): BN => { + if (!instructionName || !instructionWeight) return BN_ZERO; + + const instruction = instructions[instructionName]; + + return instructionWeight.mul(new BN(instruction.length)); +}; + +export const estimateFee = ( + instructions: Instructions, + baseWeights: NetworkBaseWeight, + assetLocation: AssetLocation, + originChain: string, + xcmTransfer: XcmTransfer, +): BN => { + const weight = getEstimatedWeight( + instructions, + xcmTransfer.destination.fee.instructions, + new BN(xcmTransfer.destination.fee.mode.value), + ); + + const fee = weightToFee(weight, new BN(baseWeights[xcmTransfer.destination.chainId])); + + const isReserveChain = [originChain, xcmTransfer.destination.chainId].includes(assetLocation.chainId); + + if (isReserveChain) return fee; + + const reserveWeight = getEstimatedWeight( + instructions, + assetLocation.reserveFee.instructions, + new BN(assetLocation.reserveFee.mode.value), + ); + + const reserveFee = weightToFee(reserveWeight, new BN(baseWeights[assetLocation.chainId])); + + return fee.add(reserveFee); +}; + +const JunctionType: Record = { + parachainId: 'Parachain', + generalKey: 'GeneralKey', + palletInstance: 'PalletInstance', + accountKey: 'AccountKey20', + accountId: 'AccountId32', + generalIndex: 'GeneralIndex', +}; +type JunctionTypeKey = keyof typeof JunctionType; + +export const createJunctionFromObject = (data: {}) => { + const entries = Object.entries(data); + + if (entries.length === 0) return 'Here'; + + if (entries.length === 1) { + return { + X1: { + [JunctionType[entries[0][0] as JunctionTypeKey]]: entries[0][1], + }, + }; + } + + return { + [`X${entries.length}`]: entries.map((e) => ({ + [JunctionType[e[0] as JunctionTypeKey]]: e[1], + })), + }; +}; + +export const getAssetLocation = ( + api: ApiPromise, + asset: AssetXCM, + assets: Record, + amount: BN, +): VersionedMultiAsset | undefined => { + return { + relative: () => getRelativeAssetLocation(api, amount, assets[asset.assetLocation].multiLocation), + absolute: () => getAbsoluteAssetLocation(api, amount, assets[asset.assetLocation].multiLocation), + concrete: () => getConcreteAssetLocation(api, amount, asset.assetLocationPath.path), + }[asset.assetLocationPath.type](); +}; + +const getRelativeAssetLocation = ( + api: ApiPromise, + amount: BN, + assetLocation?: LocalMultiLocation, +): VersionedMultiAsset | undefined => { + if (!assetLocation) return; + + const { parachainId: _, ...location } = assetLocation; + + return api.createType('VersionedMultiAsset', { + V2: { + id: { + Concrete: { + parents: 0, + interior: Object.values(location).length ? createJunctionFromObject(location) : 'Here', + }, + }, + fun: { + Fungible: amount.toNumber(), + }, + }, + }); +}; + +const getAbsoluteAssetLocation = ( + api: ApiPromise, + amount: BN, + assetLocation?: LocalMultiLocation, +): VersionedMultiAsset | undefined => { + if (!assetLocation) return; + + return api.createType('VersionedMultiAsset', { + V2: { + id: { + Concrete: { + parents: 1, + interior: Object.values(assetLocation).length ? createJunctionFromObject(assetLocation) : 'Here', + }, + }, + fungibility: { + Fungible: amount.toNumber(), + }, + }, + }); +}; + +const getConcreteAssetLocation = ( + api: ApiPromise, + amount: BN, + assetLocation?: LocalMultiLocation, +): VersionedMultiAsset | undefined => { + if (!assetLocation) return; + + const { parents, ...location } = assetLocation; + + return api.createType('VersionedMultiAsset', { + V2: { + id: { + Concrete: { + parents, + interior: Object.values(location).length ? createJunctionFromObject(location) : 'Here', + }, + }, + fun: { + Fungible: amount.toNumber(), + }, + }, + }); +}; + +export const getDestinationLocation = ( + api: ApiPromise, + originChain: Pick, + destinationParaId?: number, + accountId?: AccountId, +): VersionedMultiLocation | undefined => { + if (originChain.parentId && destinationParaId) { + return getSiblingLocation(api, destinationParaId, accountId); + } + + if (originChain.parentId) { + return getParentLocation(api, accountId); + } + + if (destinationParaId) { + return getChildLocation(api, destinationParaId, accountId); + } + + return undefined; +}; + +const getChildLocation = (api: ApiPromise, parachainId: number, accountId?: AccountId): VersionedMultiLocation => { + const location: Record = { parachainId }; + + if (accountId) { + location.accountId = { + network: 'Any', + id: accountId, + }; + } + + return api.createType('VersionedMultiLocation', { + V2: { + parents: 0, + interior: createJunctionFromObject(location), + }, + }); +}; + +const getParentLocation = (api: ApiPromise, accountId?: AccountId): VersionedMultiLocation => { + const location: Record = {}; + + if (accountId) { + location.accountId = { + network: 'Any', + id: accountId, + }; + } + + return api.createType('VersionedMultiLocation', { + V2: { + parents: 1, + interior: createJunctionFromObject(location), + }, + }); +}; + +const getSiblingLocation = (api: ApiPromise, parachainId: number, accountId?: AccountId): VersionedMultiLocation => { + const location: Record = { parachainId }; + + if (accountId) { + location.accountId = { + network: 'Any', + id: accountId, + }; + } + + return api.createType('VersionedMultiLocation', { + V2: { + parents: 1, + interior: createJunctionFromObject(location), + }, + }); +}; diff --git a/src/renderer/shared/core/index.ts b/src/renderer/shared/core/index.ts new file mode 100644 index 0000000000..a66d38a5a4 --- /dev/null +++ b/src/renderer/shared/core/index.ts @@ -0,0 +1 @@ +export * as kernelModel from './model/kernel-model'; diff --git a/src/renderer/shared/core/model/kernel-model.ts b/src/renderer/shared/core/model/kernel-model.ts new file mode 100644 index 0000000000..35c883166d --- /dev/null +++ b/src/renderer/shared/core/model/kernel-model.ts @@ -0,0 +1,7 @@ +import { createEvent } from 'effector'; + +const appStarted = createEvent(); + +export const events = { + appStarted, +}; diff --git a/src/renderer/widgets/SendAssetModal/index.ts b/src/renderer/widgets/SendAssetModal/index.ts index a676c4954e..38c5b3da37 100644 --- a/src/renderer/widgets/SendAssetModal/index.ts +++ b/src/renderer/widgets/SendAssetModal/index.ts @@ -1 +1,2 @@ export { SendAssetModal } from './ui/SendAssetModal'; +export * as sendAssetModel from './model/send-asset'; diff --git a/src/renderer/widgets/SendAssetModal/model/__tests__/send-asset.test.ts b/src/renderer/widgets/SendAssetModal/model/__tests__/send-asset.test.ts new file mode 100644 index 0000000000..adfff11bda --- /dev/null +++ b/src/renderer/widgets/SendAssetModal/model/__tests__/send-asset.test.ts @@ -0,0 +1,44 @@ +import { fork, allSettled } from 'effector'; + +import * as sendAssetModel from '../../model/send-asset'; +import * as service from '@renderer/shared/api/xcm'; + +jest.mock('@renderer/shared/api/xcm', () => ({ + __esModule: true, + ...jest.requireActual('@renderer/shared/api/xcm'), +})); + +describe('widgets/SendAssetModal/model/send-asset', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('should call xcmConfigRequested and all related effects', async () => { + const spyFetchXcmConfig = jest.spyOn(service, 'fetchXcmConfig'); + spyFetchXcmConfig.mockImplementation(); + + const spyGetXcmConfig = jest.spyOn(service, 'getXcmConfig'); + spyGetXcmConfig.mockImplementation(); + + const spySaveXcmConfig = jest.spyOn(service, 'saveXcmConfig'); + spySaveXcmConfig.mockImplementation(); + + const scope = fork(); + await allSettled(sendAssetModel.events.xcmConfigRequested, { scope }); + + expect(spyGetXcmConfig).toHaveBeenCalled(); + expect(spyFetchXcmConfig).toHaveBeenCalled(); + expect(spySaveXcmConfig).toHaveBeenCalled(); + }); + + test('should call xcmConfigRequested and get final config', async () => { + jest.spyOn(service, 'fetchXcmConfig').mockResolvedValue('config' as any); + jest.spyOn(service, 'getXcmConfig').mockImplementation(); + jest.spyOn(service, 'saveXcmConfig').mockImplementation(); + + const scope = fork(); + await allSettled(sendAssetModel.events.xcmConfigRequested, { scope }); + + expect(scope.getState(sendAssetModel.$finalConfig)).toEqual('config'); + }); +}); diff --git a/src/renderer/widgets/SendAssetModal/model/send-asset.ts b/src/renderer/widgets/SendAssetModal/model/send-asset.ts new file mode 100644 index 0000000000..e9b7cc2d1f --- /dev/null +++ b/src/renderer/widgets/SendAssetModal/model/send-asset.ts @@ -0,0 +1,41 @@ +import { createStore, createEffect, createEvent, sample, forward, attach } from 'effector'; + +import { XcmConfig } from '@renderer/shared/api/xcm'; +import { xcmModel } from '@renderer/entities/xcm'; + +export const $finalConfig = createStore(null); +const xcmConfigRequested = createEvent(); + +// TODO: continue config calculation in xcm service task +const calculateFinalConfigFx = createEffect((config: XcmConfig): XcmConfig => { + return config; +}); + +const getConfigFx = attach({ effect: xcmModel.effects.getConfigFx }); +const saveConfigFx = attach({ effect: xcmModel.effects.saveConfigFx }); +const fetchConfigFx = attach({ effect: xcmModel.effects.fetchConfigFx }); + +forward({ + from: xcmConfigRequested, + to: [getConfigFx, fetchConfigFx], +}); + +sample({ + clock: getConfigFx.doneData, + filter: (config): config is XcmConfig => Boolean(config), + target: calculateFinalConfigFx, +}); + +forward({ + from: fetchConfigFx.doneData, + to: [saveConfigFx, calculateFinalConfigFx], +}); + +forward({ + from: calculateFinalConfigFx.doneData, + to: $finalConfig, +}); + +export const events = { + xcmConfigRequested, +}; diff --git a/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx b/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx index dc9c844769..995d252161 100644 --- a/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { useI18n, useNetworkContext } from '@renderer/app/providers'; @@ -13,6 +13,7 @@ import { OperationTitle } from '@renderer/components/common'; import { Chain } from '@renderer/entities/chain'; import { DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; import { useToggle } from '@renderer/shared/lib/hooks'; +import * as sendAssetModel from '../model/send-asset'; const enum Step { INIT, @@ -48,6 +49,10 @@ export const SendAssetModal = ({ chain, asset, onClose }: Props) => { const { api, assets, addressPrefix, explorers } = connection; + useEffect(() => { + sendAssetModel.events.xcmConfigRequested(); + }, []); + const onInitResult = (transferTx: Transaction, multisig?: { multisigTx: Transaction; description: string }) => { setTransferTx(transferTx); setMultisigTx(multisig?.multisigTx || undefined); diff --git a/src/renderer/widgets/SendAssetModal/ui/common/constants.ts b/src/renderer/widgets/SendAssetModal/ui/common/constants.ts deleted file mode 100644 index c23c65f292..0000000000 --- a/src/renderer/widgets/SendAssetModal/ui/common/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -// OPERATION DETAILS STYLE -export const RowStyle = 'flex justify-between items-center w-full'; -export const LabelStyle = 'text-text-tertiary'; -export const AddressStyle = 'text-footnote text-inherit'; diff --git a/src/renderer/widgets/SendAssetModal/ui/components/Details.tsx b/src/renderer/widgets/SendAssetModal/ui/components/Details.tsx index 4adfb71ffc..03352084dc 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/Details.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/Details.tsx @@ -2,10 +2,11 @@ import { useI18n } from '@renderer/app/providers'; import { Account, MultisigAccount, AddressWithExplorers } from '@renderer/entities/account'; import { ExtendedChain } from '@renderer/entities/network'; import { Transaction } from '@renderer/entities/transaction'; -import { AddressStyle } from '../common/constants'; import { Wallet } from '@renderer/entities/wallet'; import { DetailRow } from '@renderer/shared/ui'; +const AddressStyle = 'text-footnote text-inherit'; + type Props = { transaction: Transaction; account?: Account | MultisigAccount; From 34aff8ffafd6e81b33ac24244d0e4939013f7860 Mon Sep 17 00:00:00 2001 From: Aleksandr Makhnev Date: Tue, 12 Sep 2023 12:05:51 +0500 Subject: [PATCH 18/34] fix: update metadata (#1056) --- src/renderer/entities/network/lib/common/types.ts | 2 +- src/renderer/entities/network/lib/metadataService.ts | 9 +++++++-- .../entities/network/lib/provider/CachedProvider.ts | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/renderer/entities/network/lib/common/types.ts b/src/renderer/entities/network/lib/common/types.ts index 48205224d7..49a7377028 100644 --- a/src/renderer/entities/network/lib/common/types.ts +++ b/src/renderer/entities/network/lib/common/types.ts @@ -68,7 +68,7 @@ export type ConnectProps = { export type Metadata = { chainId: ChainId; version: number; - metadata: HexString; + metadata?: HexString; }; export interface IMetadataService { diff --git a/src/renderer/entities/network/lib/metadataService.ts b/src/renderer/entities/network/lib/metadataService.ts index fa0feb10ab..5577a445fe 100644 --- a/src/renderer/entities/network/lib/metadataService.ts +++ b/src/renderer/entities/network/lib/metadataService.ts @@ -12,7 +12,7 @@ export const useMetadata = (): IMetadataService => { throw new Error('=== 🔴 Metadata storage in not defined 🔴 ==='); } - const { getAllMetadata, addMetadata } = metadataStorage; + const { getAllMetadata, addMetadata, updateMetadata } = metadataStorage; const getMetadata = async (chainId: ChainId): Promise => { const metadata = await getAllMetadata({ chainId }); @@ -39,7 +39,7 @@ export const useMetadata = (): IMetadataService => { chainId: api.genesisHash.toHex(), }; - await addMetadata(newMetadata); + await updateMetadata(newMetadata); return newMetadata; }; @@ -50,6 +50,11 @@ export const useMetadata = (): IMetadataService => { const oldMetadata = await getMetadata(chainId); if (!oldMetadata || version.specVersion.toNumber() > oldMetadata.version) { + await addMetadata({ + version: version.specVersion.toNumber(), + chainId, + }); + syncMetadata(api); } }); diff --git a/src/renderer/entities/network/lib/provider/CachedProvider.ts b/src/renderer/entities/network/lib/provider/CachedProvider.ts index 3e2e184813..ef1f0384b8 100644 --- a/src/renderer/entities/network/lib/provider/CachedProvider.ts +++ b/src/renderer/entities/network/lib/provider/CachedProvider.ts @@ -14,7 +14,7 @@ export const createCachedProvider = ( if (method === GET_METADATA_METHOD && params.length === 0) { const metadata = await getMetadata(chainId); - if (metadata) return metadata.metadata; + if (metadata?.metadata) return metadata.metadata; } return super.send(method, params, ...args); From 0acbb1aa70dc8c01124c3c0aabd29cbe6172fd5a Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Thu, 14 Sep 2023 21:13:40 +0300 Subject: [PATCH 19/34] Feat: Xcm operations (#1058) --- docs/effector.md | 23 ++- .../GraphqlContext/GraphqlContext.test.tsx | 2 +- .../context/GraphqlContext/GraphqlContext.tsx | 22 +-- .../PrimaryLayout/Navigation/Navigation.tsx | 4 +- .../CreateMultisigAccount.test.tsx | 2 +- .../modals/MatrixModal/Matrix.test.tsx | 2 +- .../components/LoginForm/LoginForm.test.tsx | 2 +- .../asset/ui/AssetCard/AssetCard.test.tsx | 2 +- .../chain/ui/ChainTitle/ChainTitle.test.tsx | 14 +- .../chain/ui/ChainTitle/ChainTitle.tsx | 16 +- .../chain/ui/XcmChains/XcmChains.stories.tsx | 17 ++ .../chain/ui/XcmChains/XcmChains.test.tsx | 23 +++ .../entities/chain/ui/XcmChains/XcmChains.tsx | 24 +++ src/renderer/entities/chain/ui/index.ts | 1 + .../lib/multisigTx/multisigTxService.ts | 2 +- .../entities/network/lib/chainsService.ts | 15 +- .../entities/network/lib/common/types.ts | 6 +- .../entities/network/lib/networkService.ts | 2 +- .../transaction/lib/callDataDecoder.ts | 164 +++++++++++----- .../transaction/lib/common/constants.ts | 6 + .../entities/transaction/lib/common/utils.ts | 10 +- .../entities/transaction/model/transaction.ts | 1 - .../features/assets/AssetRouteGuard/index.ts | 1 - .../AssetRouteGuard/model/asset-guard.ts | 4 +- .../OperationsFilter/lib/constants.ts | 4 + .../OperationsFilter/ui/OperationsFilter.tsx | 12 +- .../Assets/AssetsList/AssetsList.test.tsx | 2 +- .../NetworkAssets/NetworkAssets.test.tsx | 2 +- .../SelectShardModal/SelectShardModal.tsx | 17 +- .../Vault/ManageStep/ManageStep.tsx | 15 +- .../ManageStepSingle/ManageStepSingle.tsx | 4 +- .../pages/Onboarding/WatchOnly/WatchOnly.tsx | 4 +- src/renderer/pages/Operations/Operations.tsx | 2 +- src/renderer/pages/Operations/common/utils.ts | 71 +++++-- .../components/ActionSteps/Confirmation.tsx | 23 ++- .../pages/Operations/components/Details.tsx | 34 +++- .../pages/Operations/components/Operation.tsx | 18 +- .../Operations/components/OperationStatus.tsx | 17 +- .../components/ShortTransactionInfo.test.tsx | 2 +- .../components/TransactionAmount.tsx | 4 +- .../components/modals/ApproveTx.tsx | 14 +- .../Operations/components/modals/RejectTx.tsx | 10 +- .../pages/Settings/Networks/Networks.test.tsx | 2 +- .../CustomRpcModal/CustomRpcModal.test.tsx | 2 +- .../NetworkItem/NetworkItem.test.tsx | 2 +- .../NetworkList/NetworkList.test.tsx | 2 +- .../NetworkSelector/NetworkSelector.test.tsx | 2 +- .../pages/Settings/Overview/Overview.test.tsx | 2 +- .../GeneralActions/GeneralActions.test.tsx | 2 +- .../MatrixAction/MatrixAction.test.tsx | 2 +- .../SocialLinks/SocialLinks.test.tsx | 2 +- .../components/Version/Version.test.tsx | 2 +- .../pages/Staking/Operations/Bond/Bond.tsx | 6 +- .../ChangeValidators/ChangeValidators.tsx | 6 +- .../Operations/Destination/Destination.tsx | 6 +- .../Staking/Operations/Redeem/Redeem.tsx | 6 +- .../Staking/Operations/Restake/Restake.tsx | 6 +- .../Operations/StakeMore/StakeMore.tsx | 6 +- .../Staking/Operations/Unstake/Unstake.tsx | 6 +- .../NetworkInfo/NetworkInfo.test.tsx | 2 +- .../components/NetworkInfo/NetworkInfo.tsx | 47 +++-- .../shared/api/xcm/__tests__/mock/xcmData.ts | 165 ++++++++++++++++ .../api/xcm/__tests__/xcmService.test.ts | 74 +++++++- src/renderer/shared/api/xcm/common/types.ts | 1 + src/renderer/shared/api/xcm/common/utils.ts | 5 + src/renderer/shared/api/xcm/xcmService.ts | 179 +++++++++++++++++- .../shared/ui/InfoLink/InfoLink.test.tsx | 2 +- .../ui/PopoverLink/PopoverLink.test.tsx | 2 +- .../SendAssetModal/ui/SendAssetModal.tsx | 4 +- src/shared/locale/en.json | 14 ++ src/shared/locale/ru.json | 14 ++ 71 files changed, 964 insertions(+), 227 deletions(-) create mode 100644 src/renderer/entities/chain/ui/XcmChains/XcmChains.stories.tsx create mode 100644 src/renderer/entities/chain/ui/XcmChains/XcmChains.test.tsx create mode 100644 src/renderer/entities/chain/ui/XcmChains/XcmChains.tsx create mode 100644 src/renderer/shared/api/xcm/common/utils.ts diff --git a/docs/effector.md b/docs/effector.md index 6a733674a7..69a06c97a6 100644 --- a/docs/effector.md +++ b/docs/effector.md @@ -1,7 +1,7 @@ # Effector ### Sample with type guard -Sometimes you need to filter `effect` result and pass it further into `target`, but TypeScript warns you that +Sometimes you need to filter `effector` result and pass it further into `target`, but TypeScript warns you that types do not align: `error: clock should extend target type`. To fix this you need to provide a `type guard` as `filter` return value. @@ -24,4 +24,25 @@ sample({ target: calculateFinalConfigFx, }); ``` + +### AppStarted event +Because `effetor` is a pure JS library, it's units could be used in any part of the app. +So in order to emit some important event like `appStarted` we can do the following: +```typescript +// index.tsx - app's entrypoint + +const container = document.getElementById('app'); +if (!container) { + throw new Error('Root container is missing in index.html'); +} + +kernelModel.events.appStarted(); + +createRoot(container).render( + + + +); +``` + [Documentation](https://effector.dev/docs/typescript/typing-effector/#filter--fn) diff --git a/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.test.tsx b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.test.tsx index 047f3317f7..00704474e3 100644 --- a/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.test.tsx +++ b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.test.tsx @@ -8,7 +8,7 @@ jest.mock('@renderer/entities/network', () => ({ useChains: jest.fn().mockReturnValue({ getStakingChainsData: jest .fn() - .mockResolvedValue([ + .mockReturnValue([ { chainId: '0x123', externalApi: { staking: [{ type: 'subquery', url: 'https://localhost:8080' }] } }, ]), }), diff --git a/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.tsx b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.tsx index 19e0282d50..962c788e26 100644 --- a/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.tsx +++ b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.tsx @@ -45,23 +45,21 @@ export const GraphqlProvider = ({ children }: PropsWithChildren) => { }, []); useEffect(() => { - (async () => { - const chainsData = await getStakingChainsData(); + const chainsData = getStakingChainsData(); - chainUrls.current = chainsData.reduce((acc, chain) => { - const subqueryMatch = chain.externalApi?.staking.find((api) => api.type === 'subquery'); + chainUrls.current = chainsData.reduce((acc, chain) => { + const subqueryMatch = chain.externalApi?.staking.find((api) => api.type === 'subquery'); - if (subqueryMatch) { - return { ...acc, [chain.chainId]: subqueryMatch.url }; - } + if (subqueryMatch) { + return { ...acc, [chain.chainId]: subqueryMatch.url }; + } - console.warn(`${chain.name} doesn't contain Subquery URL`); + console.warn(`${chain.name} doesn't contain Subquery URL`); - return acc; - }, {}); + return acc; + }, {}); - changeClient(getStakingNetwork()); - })(); + changeClient(getStakingNetwork()); }, []); const value = useMemo(() => ({ changeClient }), [changeClient]); diff --git a/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx b/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx index d31a2e1bab..e4169395d1 100644 --- a/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx +++ b/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx @@ -25,7 +25,9 @@ const Navigation = () => { const [chains, setChains] = useState({}); useEffect(() => { - getChainsData().then((chainsData) => setChains(keyBy(chainsData, 'chainId'))); + const chains = getChainsData(); + + setChains(keyBy(chains, 'chainId')); }, []); const activeAccounts = getActiveAccounts(); diff --git a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx index e6762c8515..a5f54c010f 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx @@ -62,7 +62,7 @@ jest.mock('./components', () => ({ ConfirmSignatories: () => confirmSignatories, })); -describe('screen/CreateMultisigAccount', () => { +describe('pages/CreateMultisigAccount', () => { test('should render component', () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/components/modals/MatrixModal/Matrix.test.tsx b/src/renderer/components/modals/MatrixModal/Matrix.test.tsx index 52b1a102d0..160212d5b7 100644 --- a/src/renderer/components/modals/MatrixModal/Matrix.test.tsx +++ b/src/renderer/components/modals/MatrixModal/Matrix.test.tsx @@ -22,7 +22,7 @@ jest.mock('./components/MatrixInfoPopover/MatrixInfoPopover', () => () => jest.mock('./components/Credentials/Credentials', () => () => Credentials); jest.mock('./components/Verification/Verification', () => () => Verification); -describe('screen/Settings/Matrix', () => { +describe('pages/Settings/Matrix', () => { afterEach(() => { jest.clearAllMocks(); }); diff --git a/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.test.tsx b/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.test.tsx index 2bc99df669..fc9dcb53eb 100644 --- a/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.test.tsx +++ b/src/renderer/components/modals/MatrixModal/components/LoginForm/LoginForm.test.tsx @@ -18,7 +18,7 @@ jest.mock('@renderer/app/providers', () => ({ }), })); -describe('screen/Settings/Matrix/LoginForm', () => { +describe('pages/Settings/Matrix/LoginForm', () => { const setupForm = async (withCredentials = false) => { const user = userEvent.setup({ delay: null }); diff --git a/src/renderer/entities/asset/ui/AssetCard/AssetCard.test.tsx b/src/renderer/entities/asset/ui/AssetCard/AssetCard.test.tsx index 97aa8bb46e..81c1da758a 100644 --- a/src/renderer/entities/asset/ui/AssetCard/AssetCard.test.tsx +++ b/src/renderer/entities/asset/ui/AssetCard/AssetCard.test.tsx @@ -33,7 +33,7 @@ const renderAssetCard = (canMakeActions = false) => { render(, { wrapper: BrowserRouter }); }; -describe('screen/Assets/AssetCard', () => { +describe('pages/Assets/AssetCard', () => { test('should render component', () => { renderAssetCard(); diff --git a/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.test.tsx b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.test.tsx index 636deb7727..e9c665b59d 100644 --- a/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.test.tsx +++ b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.test.tsx @@ -3,7 +3,7 @@ import { act, render, screen } from '@testing-library/react'; import { ChainTitle } from './ChainTitle'; import { TEST_CHAIN_ID } from '@renderer/shared/lib/utils'; -describe('ui/Chain', () => { +describe('ui/ChainTitle', () => { test('should render component', async () => { await act(async () => { render(); @@ -15,4 +15,16 @@ describe('ui/Chain', () => { const chainImage = screen.getByRole('img'); expect(chainImage).toBeInTheDocument(); }); + + test('should not render title', async () => { + await act(async () => { + render(); + }); + + const title = screen.queryByText('Polkadot'); + expect(title).not.toBeInTheDocument(); + + const chainImage = screen.getByRole('img'); + expect(chainImage).toBeInTheDocument(); + }); }); diff --git a/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.tsx b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.tsx index 47da63e3ad..ade2eb9701 100644 --- a/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.tsx +++ b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.tsx @@ -14,9 +14,17 @@ type Props = { fontClass?: string; className?: string; iconSize?: number; + showChainName?: boolean; } & (WithChain | WithChainId); -export const ChainTitle = ({ as: Tag = 'div', fontClass, className, iconSize = 16, ...chainProps }: Props) => { +export const ChainTitle = ({ + as: Tag = 'div', + showChainName = true, + fontClass, + className, + iconSize = 16, + ...chainProps +}: Props) => { const { getChainById } = useChains(); const [chainObj, setChainObj] = useState(); @@ -25,10 +33,14 @@ export const ChainTitle = ({ as: Tag = 'div', fontClass, className, iconSize = 1 if ('chain' in chainProps) { setChainObj(chainProps.chain); } else { - getChainById(chainProps.chainId).then(setChainObj); + setChainObj(getChainById(chainProps.chainId)); } }, []); + if (!showChainName) { + return ; + } + return ( diff --git a/src/renderer/entities/chain/ui/XcmChains/XcmChains.stories.tsx b/src/renderer/entities/chain/ui/XcmChains/XcmChains.stories.tsx new file mode 100644 index 0000000000..7795dc1de8 --- /dev/null +++ b/src/renderer/entities/chain/ui/XcmChains/XcmChains.stories.tsx @@ -0,0 +1,17 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import { XcmChains } from './XcmChains'; + +export default { + title: 'ui/XcmChains', + component: XcmChains, + parameters: { actions: { argTypesRegex: '^on.*' } }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ; + +export const Primary = Template.bind({}); +Primary.args = { + chainIdFrom: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3', + chainIdTo: '0xfc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c', +}; diff --git a/src/renderer/entities/chain/ui/XcmChains/XcmChains.test.tsx b/src/renderer/entities/chain/ui/XcmChains/XcmChains.test.tsx new file mode 100644 index 0000000000..ec13b799dc --- /dev/null +++ b/src/renderer/entities/chain/ui/XcmChains/XcmChains.test.tsx @@ -0,0 +1,23 @@ +import { render, screen } from '@testing-library/react'; + +import { XcmChains } from './XcmChains'; + +jest.mock('../ChainTitle/ChainTitle', () => ({ + ChainTitle: ({ chainId }: any) => {chainId}, +})); + +describe('ui/XcmChains', () => { + test('should render component', async () => { + render(); + + const chains = screen.getAllByText('0x111'); + expect(chains).toHaveLength(2); + }); + + test('should render single chain', async () => { + render(); + + const chains = screen.getByText('0x111'); + expect(chains).toBeInTheDocument(); + }); +}); diff --git a/src/renderer/entities/chain/ui/XcmChains/XcmChains.tsx b/src/renderer/entities/chain/ui/XcmChains/XcmChains.tsx new file mode 100644 index 0000000000..57efb5c765 --- /dev/null +++ b/src/renderer/entities/chain/ui/XcmChains/XcmChains.tsx @@ -0,0 +1,24 @@ +import { Icon } from '@renderer/shared/ui'; +import { ChainTitle } from '../ChainTitle/ChainTitle'; +import { ChainId } from '@renderer/domain/shared-kernel'; +import { cnTw } from '@renderer/shared/lib/utils'; + +type Props = { + chainIdFrom: ChainId; + chainIdTo?: ChainId; + className?: string; +}; + +export const XcmChains = ({ chainIdFrom, chainIdTo, className }: Props) => { + if (!chainIdTo) { + return ; + } + + return ( +
        + + + +
        + ); +}; diff --git a/src/renderer/entities/chain/ui/index.ts b/src/renderer/entities/chain/ui/index.ts index ca7d89d3b8..d2d7e40649 100644 --- a/src/renderer/entities/chain/ui/index.ts +++ b/src/renderer/entities/chain/ui/index.ts @@ -1,2 +1,3 @@ export { ChainTitle } from './ChainTitle/ChainTitle'; export { ChainIcon } from './ChainIcon/ChainIcon'; +export { XcmChains } from './XcmChains/XcmChains'; diff --git a/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts b/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts index d89839e97a..8d4e4c964e 100644 --- a/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts +++ b/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts @@ -209,7 +209,7 @@ export const useMultisigTx = ({ addTask }: Props): IMultisigTxService => { const updateCallData = async (api: ApiPromise, tx: MultisigTransaction, callData: CallData) => { try { - const chain = await getChainById(tx.chainId); + const chain = getChainById(tx.chainId); const transaction = decodeCallData(api, toAddress(tx.accountId, { prefix: chain?.addressPrefix }), callData); diff --git a/src/renderer/entities/network/lib/chainsService.ts b/src/renderer/entities/network/lib/chainsService.ts index 303d9f021d..e1260f2cb0 100644 --- a/src/renderer/entities/network/lib/chainsService.ts +++ b/src/renderer/entities/network/lib/chainsService.ts @@ -18,29 +18,26 @@ const CHAINS: Record = { }; export function useChains(): IChainService { - const getChainsData = (): Promise => { - return Promise.resolve(CHAINS[process.env.CHAINS_FILE || 'chains']); + const getChainsData = (): Chain[] => { + return CHAINS[process.env.CHAINS_FILE || 'chains']; }; - const getChainById = (chainId: ChainId): Promise => { + const getChainById = (chainId: ChainId): Chain | undefined => { const chainsData: Chain[] = CHAINS[process.env.CHAINS_FILE || 'chains']; - const chainMatch = chainsData.find((chain) => chain.chainId === chainId); - return Promise.resolve(chainMatch); + return chainsData.find((chain) => chain.chainId === chainId); }; - const getStakingChainsData = (): Promise => { + const getStakingChainsData = (): Chain[] => { const chainsData: Chain[] = CHAINS[process.env.CHAINS_FILE || 'chains']; - const stakingChains = chainsData.reduce((acc, chain) => { + return chainsData.reduce((acc, chain) => { if (getRelaychainAsset(chain.assets)) { acc.push(chain); } return acc; }, []); - - return Promise.resolve(stakingChains); }; const sortChains = (chains: T[]): T[] => { diff --git a/src/renderer/entities/network/lib/common/types.ts b/src/renderer/entities/network/lib/common/types.ts index 48205224d7..8847ddb493 100644 --- a/src/renderer/entities/network/lib/common/types.ts +++ b/src/renderer/entities/network/lib/common/types.ts @@ -12,9 +12,9 @@ import { Balance } from '@renderer/entities/asset/model/balance'; // ===================================================== export interface IChainService { - getChainsData: () => Promise; - getChainById: (chainId: ChainId) => Promise; - getStakingChainsData: () => Promise; + getChainsData: () => Chain[]; + getChainById: (chainId: ChainId) => Chain | undefined; + getStakingChainsData: () => Chain[]; sortChains: (chains: T[]) => T[]; sortChainsByBalance: (chains: Chain[], balances: Balance[]) => Chain[]; } diff --git a/src/renderer/entities/network/lib/networkService.ts b/src/renderer/entities/network/lib/networkService.ts index 23d07233c5..8a04da50ea 100644 --- a/src/renderer/entities/network/lib/networkService.ts +++ b/src/renderer/entities/network/lib/networkService.ts @@ -358,7 +358,7 @@ export const useNetwork = (networkSubscription?: ISubscriptionService): const setupConnections = async (): Promise => { try { - const chainsData = await getChainsData(); + const chainsData = getChainsData(); chains.current = keyBy(sortChains(chainsData), 'chainId'); const newConnections = await getNewConnections(); diff --git a/src/renderer/entities/transaction/lib/callDataDecoder.ts b/src/renderer/entities/transaction/lib/callDataDecoder.ts index fbb2244815..44a7031bc0 100644 --- a/src/renderer/entities/transaction/lib/callDataDecoder.ts +++ b/src/renderer/entities/transaction/lib/callDataDecoder.ts @@ -4,10 +4,17 @@ import { SubmittableExtrinsic } from '@polkadot/api/types'; import { HexString } from '@polkadot/util/types'; import { Type } from '@polkadot/types'; -import { Address, CallData } from '@renderer/domain/shared-kernel'; +import { parseXcmPalletExtrinsic, parseXTokensExtrinsic, decodeXcm } from '@renderer/shared/api/xcm'; +import { Address, CallData, ChainId } from '@renderer/domain/shared-kernel'; import { DecodedTransaction, TransactionType } from '@renderer/entities/transaction/model/transaction'; -import { BOND_WITH_CONTROLLER_ARGS_AMOUNT, OLD_MULTISIG_ARGS_AMOUNT } from './common/constants'; import { ICallDataDecoder } from './common/types'; +import { + BOND_WITH_CONTROLLER_ARGS_AMOUNT, + OLD_MULTISIG_ARGS_AMOUNT, + TRANSFER_SECTIONS, + STAKING_SECTION, + XCM_SECTIONS, +} from './common/constants'; export const useCallDataDecoder = (): ICallDataDecoder => { const getDataFromCallData = ( @@ -20,7 +27,6 @@ export const useCallDataDecoder = (): ICallDataDecoder => { } => { let extrinsicCall: Call; let decoded: SubmittableExtrinsic<'promise'> | null = null; - try { decoded = api.tx(callData); extrinsicCall = api.createType('Call', decoded.method); @@ -124,48 +130,77 @@ export const useCallDataDecoder = (): ICallDataDecoder => { method, section, chainId: genesisHash, - args: parser(method, section, decoded), + args: parser(decoded, genesisHash), type: transactionType, }; }; const getCallDataParser: Record< TransactionType, - (method: string, section: string, decoded: SubmittableExtrinsic<'promise'>) => Record + (decoded: SubmittableExtrinsic<'promise'>, chainId: ChainId) => Record > = { - [TransactionType.TRANSFER]: (method, section, decoded): Record => { + [TransactionType.TRANSFER]: (decoded, chainId): Record => { return { dest: decoded.args[0].toString(), value: decoded.args[1].toString() }; }, - [TransactionType.XCM_LIMITED_TRANSFER]: (method, section, decoded): Record => { - return {}; - }, - [TransactionType.XCM_TELEPORT]: (method, section, decoded): Record => { - return {}; - }, - [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: (method, section, decoded): Record => { - return {}; - }, - [TransactionType.POLKADOT_XCM_TELEPORT]: (method, section, decoded): Record => { - return {}; - }, - [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: (method, section, decoded): Record => { - return {}; - }, - [TransactionType.ASSET_TRANSFER]: (method, section, decoded): Record => { + [TransactionType.ASSET_TRANSFER]: (decoded, chainId): Record => { return { assetId: decoded.args[0].toString(), dest: decoded.args[1].toString(), value: decoded.args[2].toString(), }; }, - [TransactionType.ORML_TRANSFER]: (method, section, decoded): Record => { + [TransactionType.ORML_TRANSFER]: (decoded, chainId): Record => { return { dest: decoded.args[0].toString(), assetId: decoded.args[1].toString(), value: decoded.args[2].toString(), }; }, - [TransactionType.BOND]: (method, section, decoded): Record => { + [TransactionType.XCM_LIMITED_TRANSFER]: (decoded, chainId): Record => { + const parsedData = parseXcmPalletExtrinsic({ + dest: decoded.args[0].toHuman(), + beneficiary: decoded.args[1].toHuman(), + assets: decoded.args[2].toHuman(), + }); + + return decodeXcm(chainId, parsedData); + }, + [TransactionType.XCM_TELEPORT]: (decoded, chainId): Record => { + const parsedData = parseXcmPalletExtrinsic({ + dest: decoded.args[0].toHuman(), + beneficiary: decoded.args[1].toHuman(), + assets: decoded.args[2].toHuman(), + }); + + return decodeXcm(chainId, parsedData); + }, + [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: (decoded, chainId): Record => { + const parsedData = parseXcmPalletExtrinsic({ + dest: decoded.args[0].toHuman(), + beneficiary: decoded.args[1].toHuman(), + assets: decoded.args[2].toHuman(), + }); + + return decodeXcm(chainId, parsedData); + }, + [TransactionType.POLKADOT_XCM_TELEPORT]: (decoded, chainId): Record => { + const parsedData = parseXcmPalletExtrinsic({ + dest: decoded.args[0].toHuman(), + beneficiary: decoded.args[1].toHuman(), + assets: decoded.args[2].toHuman(), + }); + + return decodeXcm(chainId, parsedData); + }, + [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: (decoded, chainId): Record => { + const parsedData = parseXTokensExtrinsic({ + asset: decoded.args[0].toHuman(), + dest: decoded.args[1].toHuman(), + }); + + return decodeXcm(chainId, parsedData); + }, + [TransactionType.BOND]: (decoded, chainId): Record => { const args: Record = {}; let index = 0; if (decoded.args.length === BOND_WITH_CONTROLLER_ARGS_AMOUNT) { @@ -184,25 +219,25 @@ export const useCallDataDecoder = (): ICallDataDecoder => { return args; }, - [TransactionType.UNSTAKE]: (method, section, decoded): Record => { + [TransactionType.UNSTAKE]: (decoded, chainId): Record => { return { value: decoded.args[0].toString() }; }, [TransactionType.CHILL]: (): Record => { return {}; }, - [TransactionType.RESTAKE]: (method, section, decoded): Record => { + [TransactionType.RESTAKE]: (decoded, chainId): Record => { return { value: decoded.args[0].toString() }; }, [TransactionType.REDEEM]: (): Record => { return {}; }, - [TransactionType.NOMINATE]: (method, section, decoded): Record => { + [TransactionType.NOMINATE]: (decoded, chainId): Record => { return { targets: (decoded.args[0] as any).map((a: Type) => a.toString()) }; }, - [TransactionType.STAKE_MORE]: (method, section, decoded): Record => { + [TransactionType.STAKE_MORE]: (decoded, chainId): Record => { return { maxAdditional: decoded.args[0].toString() }; }, - [TransactionType.DESTINATION]: (method, section, decoded): Record => { + [TransactionType.DESTINATION]: (decoded, chainId): Record => { const args: Record = {}; try { args.payee = JSON.parse(decoded.args[0].toString()); @@ -213,10 +248,10 @@ export const useCallDataDecoder = (): ICallDataDecoder => { return args; }, - [TransactionType.BATCH_ALL]: (method, section, decoded): Record => { + [TransactionType.BATCH_ALL]: (decoded, chainId): Record => { return { calls: decoded.args[0].toHex() }; }, - [TransactionType.MULTISIG_AS_MULTI]: (method, section, decoded): Record => { + [TransactionType.MULTISIG_AS_MULTI]: (decoded, chainId): Record => { if (decoded.args.length === OLD_MULTISIG_ARGS_AMOUNT) { return { threshold: decoded.args[0], @@ -236,7 +271,7 @@ export const useCallDataDecoder = (): ICallDataDecoder => { maxWeight: decoded.args[4], }; }, - [TransactionType.MULTISIG_APPROVE_AS_MULTI]: (method, section, decoded): Record => { + [TransactionType.MULTISIG_APPROVE_AS_MULTI]: (decoded, chainId): Record => { return { threshold: decoded.args[0], otherSignatories: decoded.args[1], @@ -245,7 +280,7 @@ export const useCallDataDecoder = (): ICallDataDecoder => { maxWeight: decoded.args[4], }; }, - [TransactionType.MULTISIG_CANCEL_AS_MULTI]: (method, section, decoded): Record => { + [TransactionType.MULTISIG_CANCEL_AS_MULTI]: (decoded, chainId): Record => { return { threshold: decoded.args[0], otherSignatories: decoded.args[1], @@ -260,23 +295,60 @@ export const useCallDataDecoder = (): ICallDataDecoder => { }; const getTransactionType = (method: string, section: string): TransactionType | undefined => { + const transferType = getTransferTxType(method, section); + const stakingType = getStakingTxType(method, section); + const xcmType = getXcmTxType(method, section); + + return transferType || stakingType || xcmType; + }; + + const getTransferTxType = (method: string, section: string): TransactionType | undefined => { + if (!TRANSFER_SECTIONS.includes(section)) return; + const TRANSFER_METHODS = ['transfer', 'transferKeepAlive', 'transferAllowDeath']; if (TRANSFER_METHODS.includes(method) && section === 'balances') return TransactionType.TRANSFER; if (TRANSFER_METHODS.includes(method) && section === 'assets') return TransactionType.ASSET_TRANSFER; - if (method === 'transfer' && (section === 'currencies' || section === 'tokens')) - return TransactionType.ORML_TRANSFER; - - if (section !== 'staking') return undefined; - - if (method === 'bond') return TransactionType.BOND; - if (method === 'unbond') return TransactionType.UNSTAKE; - if (method === 'chill') return TransactionType.CHILL; - if (method === 'rebond') return TransactionType.RESTAKE; - if (method === 'withdrawUnbonded') return TransactionType.REDEEM; - if (method === 'nominate') return TransactionType.NOMINATE; - if (method === 'bondExtra') return TransactionType.STAKE_MORE; - if (method === 'setPayee') return TransactionType.DESTINATION; + if (method === 'transfer') return TransactionType.ORML_TRANSFER; + + return undefined; + }; + + const getStakingTxType = (method: string, section: string): TransactionType | undefined => { + if (!STAKING_SECTION.includes(section)) return; + + return { + bond: TransactionType.BOND, + unbond: TransactionType.UNSTAKE, + chill: TransactionType.CHILL, + rebond: TransactionType.RESTAKE, + withdrawUnbonded: TransactionType.REDEEM, + nominate: TransactionType.NOMINATE, + bondExtra: TransactionType.STAKE_MORE, + setPayee: TransactionType.DESTINATION, + }[method]; + }; + + const getXcmTxType = (method: string, section: string): TransactionType | undefined => { + if (!XCM_SECTIONS.includes(section)) return; + + if (section === 'xcmPallet') { + return { + limitedReserveTransferAssets: TransactionType.XCM_LIMITED_TRANSFER, + limitedTeleportAssets: TransactionType.XCM_TELEPORT, + }[method]; + } + + if (section === 'polkadotXcm') { + return { + limitedReserveTransferAssets: TransactionType.POLKADOT_XCM_LIMITED_TRANSFER, + limitedTeleportAssets: TransactionType.POLKADOT_XCM_TELEPORT, + }[method]; + } + + if (method === 'transferMultiasset' && section === 'xTokens') { + return TransactionType.XTOKENS_TRANSFER_MULTIASSET; + } return undefined; }; diff --git a/src/renderer/entities/transaction/lib/common/constants.ts b/src/renderer/entities/transaction/lib/common/constants.ts index 78dcb70352..f30fa360c3 100644 --- a/src/renderer/entities/transaction/lib/common/constants.ts +++ b/src/renderer/entities/transaction/lib/common/constants.ts @@ -11,6 +11,12 @@ export const BOND_WITH_CONTROLLER_ARGS_AMOUNT = 3; export const CONTROLLER_ARG_NAME = 'controller'; +export const TRANSFER_SECTIONS = ['balances', 'assets', 'currencies', 'tokens']; + +export const XCM_SECTIONS = ['xcmPallet', 'polkadotXcm', 'xTokens']; + +export const STAKING_SECTION = 'staking'; + export const TransferTypes = [TransactionType.TRANSFER, TransactionType.ASSET_TRANSFER, TransactionType.ORML_TRANSFER]; export const XcmTypes = [ diff --git a/src/renderer/entities/transaction/lib/common/utils.ts b/src/renderer/entities/transaction/lib/common/utils.ts index 23b1e468d7..60c49be656 100644 --- a/src/renderer/entities/transaction/lib/common/utils.ts +++ b/src/renderer/entities/transaction/lib/common/utils.ts @@ -1,8 +1,8 @@ import { ApiPromise } from '@polkadot/api'; import { SpRuntimeDispatchError } from '@polkadot/types/lookup'; -import { Transaction } from '@renderer/entities/transaction/model/transaction'; -import { MAX_WEIGHT, OLD_MULTISIG_ARGS_AMOUNT, CONTROLLER_ARG_NAME } from './constants'; +import { Transaction, DecodedTransaction } from '@renderer/entities/transaction/model/transaction'; +import { MAX_WEIGHT, OLD_MULTISIG_ARGS_AMOUNT, CONTROLLER_ARG_NAME, XcmTypes } from './constants'; export const decodeDispatchError = (error: SpRuntimeDispatchError, api: ApiPromise): string => { let errorInfo = error.toString(); @@ -30,3 +30,9 @@ export const getMaxWeight = (api: ApiPromise, transaction: Transaction) => { return (isOldMultisigPallet(api) && maxWeight.refTime) || maxWeight; }; + +export const isXcmTransaction = (transaction?: Transaction | DecodedTransaction): boolean => { + if (!transaction?.type) return false; + + return XcmTypes.includes(transaction.type); +}; diff --git a/src/renderer/entities/transaction/model/transaction.ts b/src/renderer/entities/transaction/model/transaction.ts index 2f843066c0..546ce71ccf 100644 --- a/src/renderer/entities/transaction/model/transaction.ts +++ b/src/renderer/entities/transaction/model/transaction.ts @@ -89,7 +89,6 @@ export type MultisigTransaction = { deposit?: string; depositor?: AccountId; description?: string; - xcmDestination?: ChainId; cancelDescription?: string; blockCreated: number; indexCreated: number; diff --git a/src/renderer/features/assets/AssetRouteGuard/index.ts b/src/renderer/features/assets/AssetRouteGuard/index.ts index 1ec5113fc2..4e64721c0c 100644 --- a/src/renderer/features/assets/AssetRouteGuard/index.ts +++ b/src/renderer/features/assets/AssetRouteGuard/index.ts @@ -1,2 +1 @@ export { AssetRouteGuard } from './ui/AssetRouteGuard'; -export * as assetGuardModel from './model/asset-guard'; diff --git a/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts b/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts index 6b3ca7fe51..d669b43d40 100644 --- a/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts +++ b/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts @@ -26,10 +26,10 @@ type ValidateParams = { chainId: string | null; assetId: string | null; }; -const getChainAndAssetFx = createEffect(async ({ chainId, assetId }: ValidateParams) => { +const getChainAndAssetFx = createEffect(({ chainId, assetId }: ValidateParams) => { if (!chainId || !assetId) return undefined; - const chain = await getChainById(chainId as ChainId); + const chain = getChainById(chainId as ChainId); const asset = chain?.assets.find((a) => a.assetId === Number(assetId)); return { chain, asset }; diff --git a/src/renderer/features/operation/OperationsFilter/lib/constants.ts b/src/renderer/features/operation/OperationsFilter/lib/constants.ts index 40fe4461a2..6f4e402119 100644 --- a/src/renderer/features/operation/OperationsFilter/lib/constants.ts +++ b/src/renderer/features/operation/OperationsFilter/lib/constants.ts @@ -1 +1,5 @@ +import { TransactionType } from '@renderer/entities/transaction/model/transaction'; + export const UNKNOWN_TYPE = 'UNKNOWN_TYPE'; + +export const TransferTypes = [TransactionType.TRANSFER, TransactionType.ASSET_TRANSFER, TransactionType.ORML_TRANSFER]; diff --git a/src/renderer/features/operation/OperationsFilter/ui/OperationsFilter.tsx b/src/renderer/features/operation/OperationsFilter/ui/OperationsFilter.tsx index 2f8e896823..d59d81b625 100644 --- a/src/renderer/features/operation/OperationsFilter/ui/OperationsFilter.tsx +++ b/src/renderer/features/operation/OperationsFilter/ui/OperationsFilter.tsx @@ -74,9 +74,10 @@ export const OperationsFilter = ({ txs, onChange }: Props) => { return transactions.reduce( (acc, tx) => { const txType = getFilterableTxType(tx); + const xcmDestination = tx.transaction?.args.destinationChain; const statusOption = StatusOptions.find((s) => s.value === tx.status); - const networkOption = NetworkOptions.find((s) => s.value === tx.chainId || s.value === tx.xcmDestination); + const networkOption = NetworkOptions.find((s) => s.value === tx.chainId || s.value === xcmDestination); const typeOption = TransactionOptions.find((s) => s.value === txType); if (statusOption) acc.status.add(statusOption); @@ -94,10 +95,13 @@ export const OperationsFilter = ({ txs, onChange }: Props) => { }; const filterTx = (tx: MultisigTransaction, filters: SelectedFilters) => { + const xcmDestination = tx.transaction?.args.destinationChain; + return ( - (!filters.status.length || filters.status.map(mapValues).includes(tx.status)) && - (!filters.network.length || filters.network.map(mapValues).includes(tx.chainId)) && - (!filters.type.length || filters.type.map(mapValues).includes(getFilterableTxType(tx))) + ((!filters.status.length || filters.status.map(mapValues).includes(tx.status)) && + (!filters.network.length || filters.network.map(mapValues).includes(tx.chainId))) || + (filters.network.map(mapValues).includes(xcmDestination) && + (!filters.type.length || filters.type.map(mapValues).includes(getFilterableTxType(tx)))) ); }; diff --git a/src/renderer/pages/Assets/AssetsList/AssetsList.test.tsx b/src/renderer/pages/Assets/AssetsList/AssetsList.test.tsx index ee9a497cfa..dd78c99dc2 100644 --- a/src/renderer/pages/Assets/AssetsList/AssetsList.test.tsx +++ b/src/renderer/pages/Assets/AssetsList/AssetsList.test.tsx @@ -57,7 +57,7 @@ jest.mock('./components/NetworkAssets/NetworkAssets', () => ({ NetworkAssets: () => NetworkAssets, })); -describe('screen/Assets/Assets', () => { +describe('pages/Assets/Assets', () => { test('should render component', () => { render(); diff --git a/src/renderer/pages/Assets/AssetsList/components/NetworkAssets/NetworkAssets.test.tsx b/src/renderer/pages/Assets/AssetsList/components/NetworkAssets/NetworkAssets.test.tsx index b11eebcdc9..ddb7407893 100644 --- a/src/renderer/pages/Assets/AssetsList/components/NetworkAssets/NetworkAssets.test.tsx +++ b/src/renderer/pages/Assets/AssetsList/components/NetworkAssets/NetworkAssets.test.tsx @@ -54,7 +54,7 @@ const accounts = [ }, ] as Account[]; -describe('screen/Assets/NetworkAssets', () => { +describe('pages/Assets/NetworkAssets', () => { test('should render component', () => { render(); diff --git a/src/renderer/pages/Assets/AssetsList/components/SelectShardModal/SelectShardModal.tsx b/src/renderer/pages/Assets/AssetsList/components/SelectShardModal/SelectShardModal.tsx index dcd75eceeb..3a59d00554 100644 --- a/src/renderer/pages/Assets/AssetsList/components/SelectShardModal/SelectShardModal.tsx +++ b/src/renderer/pages/Assets/AssetsList/components/SelectShardModal/SelectShardModal.tsx @@ -35,17 +35,16 @@ export const SelectShardModal = ({ isOpen, onClose, activeAccounts, accounts }: useEffect(() => { if (!accounts[0]?.walletId) return; - getChainsData().then((chains) => { - const chainsById = keyBy(sortChains(chains), 'chainId'); - const activeIds = activeAccounts.map((a) => a.id || ''); + const chains = getChainsData(); + const chainsById = keyBy(sortChains(chains), 'chainId'); + const activeIds = activeAccounts.map((a) => a.id || ''); - const multishard = getMultishardStructure(accounts, chainsById, accounts[0].walletId!); - const selectable = getSelectableShards(multishard, activeIds); + const multishard = getMultishardStructure(accounts, chainsById, accounts[0].walletId!); + const selectable = getSelectableShards(multishard, activeIds); - setChains(chainsById); - setShards(selectable); - setQuery(''); - }); + setChains(chainsById); + setShards(selectable); + setQuery(''); }, [activeAccounts]); const [shards, setShards] = useState({ rootAccounts: [], amount: 0 }); diff --git a/src/renderer/pages/Onboarding/Vault/ManageStep/ManageStep.tsx b/src/renderer/pages/Onboarding/Vault/ManageStep/ManageStep.tsx index 838a1a09a2..dff7fa8b6a 100644 --- a/src/renderer/pages/Onboarding/Vault/ManageStep/ManageStep.tsx +++ b/src/renderer/pages/Onboarding/Vault/ManageStep/ManageStep.tsx @@ -65,16 +65,15 @@ const ManageStep = ({ seedInfo, onBack, onComplete }: Props) => { const [accounts, setAccounts] = useState([]); useEffect(() => { - getChainsData().then((chains) => { - const chainsMap = keyBy(sortChains(chains), 'chainId'); - setChainsObject(chainsMap); + const chains = getChainsData(); + const chainsMap = keyBy(sortChains(chains), 'chainId'); + setChainsObject(chainsMap); - const filteredQrData = seedInfo.map((data) => filterByExistingChains(data, chainsMap)); + const filteredQrData = seedInfo.map((data) => filterByExistingChains(data, chainsMap)); - const names = filteredQrData.reduce((acc, data, index) => ({ ...acc, [getAccountId(index)]: data.name }), {}); - setAccountNames(names); - setAccounts(filteredQrData.map(formatAccount)); - }); + const names = filteredQrData.reduce((acc, data, index) => ({ ...acc, [getAccountId(index)]: data.name }), {}); + setAccountNames(names); + setAccounts(filteredQrData.map(formatAccount)); }, []); const filterByExistingChains = (seedInfo: SeedInfo, chainsMap: Record): SeedInfo => { diff --git a/src/renderer/pages/Onboarding/Vault/ManageStepSingle/ManageStepSingle.tsx b/src/renderer/pages/Onboarding/Vault/ManageStepSingle/ManageStepSingle.tsx index 6d9b3a85ed..bb769f8edc 100644 --- a/src/renderer/pages/Onboarding/Vault/ManageStepSingle/ManageStepSingle.tsx +++ b/src/renderer/pages/Onboarding/Vault/ManageStepSingle/ManageStepSingle.tsx @@ -41,7 +41,9 @@ const ManageStepSingle = ({ seedInfo, onBack, onComplete }: Props) => { }); useEffect(() => { - getChainsData().then((chains) => setChains(sortChains(chains))); + const chains = getChainsData(); + + setChains(sortChains(chains)); }, []); const submitHandler: SubmitHandler = async ({ walletName }) => { diff --git a/src/renderer/pages/Onboarding/WatchOnly/WatchOnly.tsx b/src/renderer/pages/Onboarding/WatchOnly/WatchOnly.tsx index a0c3befa7d..6a84f7cffc 100644 --- a/src/renderer/pages/Onboarding/WatchOnly/WatchOnly.tsx +++ b/src/renderer/pages/Onboarding/WatchOnly/WatchOnly.tsx @@ -58,7 +58,9 @@ const WatchOnly = ({ isOpen, onClose, onComplete }: Props) => { }, [address]); useEffect(() => { - getChainsData().then((chains) => setChains(sortChains(chains))); + const chains = getChainsData(); + + setChains(sortChains(chains)); }, []); const createWallet: SubmitHandler = async ({ walletName, address }) => { diff --git a/src/renderer/pages/Operations/Operations.tsx b/src/renderer/pages/Operations/Operations.tsx index 2820a0e6e3..149e8d0226 100644 --- a/src/renderer/pages/Operations/Operations.tsx +++ b/src/renderer/pages/Operations/Operations.tsx @@ -16,9 +16,9 @@ import { OperationsFilter } from '@renderer/features/operation'; export const Operations = () => { const { t, dateLocale } = useI18n(); + const { connections } = useNetworkContext(); const { getActiveMultisigAccount } = useAccount(); const { getLiveAccountMultisigTxs } = useMultisigTx({}); - const { connections } = useNetworkContext(); const { getLiveEventsByKeys } = useMultisigEvent({}); const account = getActiveMultisigAccount(); diff --git a/src/renderer/pages/Operations/common/utils.ts b/src/renderer/pages/Operations/common/utils.ts index 41ba62cd95..eb5e4b80c1 100644 --- a/src/renderer/pages/Operations/common/utils.ts +++ b/src/renderer/pages/Operations/common/utils.ts @@ -1,10 +1,11 @@ import { IconNames } from '@renderer/shared/ui/Icon/data'; -import { Explorer } from '@renderer/entities/chain/model/chain'; +import { Explorer } from '@renderer/entities/chain'; import { AccountId, HexString } from '@renderer/domain/shared-kernel'; import { DecodedTransaction, Transaction, TransactionType } from '@renderer/entities/transaction/model/transaction'; import { toAddress, formatSectionAndMethod } from '@renderer/shared/lib/utils'; -import { Account } from '@renderer/entities/account/model/account'; -import { Signatory } from '@renderer/entities/signatory/model/signatory'; +import { TransferTypes, XcmTypes } from '@renderer/entities/transaction'; +import { Account } from '@renderer/entities/account'; +import { Signatory } from '@renderer/entities/signatory'; import type { Contact } from '@renderer/entities/contact'; export const TRANSACTION_UNKNOWN = 'operations.titles.unknown'; @@ -36,6 +37,40 @@ const TransactionTitles: Record = { [TransactionType.BATCH_ALL]: 'operations.titles.unknown', }; +const TransactionTitlesModal: Record string> = { + // Transfer + [TransactionType.ASSET_TRANSFER]: (crossChain) => + `operations.modalTitles.${crossChain ? 'transferFrom' : 'transferOn'}`, + [TransactionType.ORML_TRANSFER]: (crossChain) => + `operations.modalTitles.${crossChain ? 'transferFrom' : 'transferOn'}`, + [TransactionType.TRANSFER]: (crossChain) => `operations.modalTitles.${crossChain ? 'transferFrom' : 'transferOn'}`, + [TransactionType.MULTISIG_AS_MULTI]: () => 'operations.modalTitles.approveMultisig', + [TransactionType.MULTISIG_APPROVE_AS_MULTI]: () => 'operations.modalTitles.approveMultisig', + [TransactionType.MULTISIG_CANCEL_AS_MULTI]: () => 'operations.modalTitles.cancelMultisig', + // XCM + [TransactionType.XCM_LIMITED_TRANSFER]: (crossChain) => + `operations.modalTitles.${crossChain ? 'transferFrom' : 'transferOn'}`, + [TransactionType.XCM_TELEPORT]: (crossChain) => + `operations.modalTitles.${crossChain ? 'transferFrom' : 'transferOn'}`, + [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: (crossChain) => + `operations.modalTitles.${crossChain ? 'transferFrom' : 'transferOn'}`, + [TransactionType.POLKADOT_XCM_TELEPORT]: (crossChain) => + `operations.modalTitles.${crossChain ? 'transferFrom' : 'transferOn'}`, + [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: (crossChain) => + `operations.modalTitles.${crossChain ? 'transferFrom' : 'transferOn'}`, + // Staking + [TransactionType.BOND]: () => 'operations.modalTitles.startStakingOn', + [TransactionType.NOMINATE]: () => 'operations.modalTitles.nominateOn', + [TransactionType.STAKE_MORE]: () => 'operations.modalTitles.stakeMoreOn', + [TransactionType.REDEEM]: () => 'operations.modalTitles.redeemOn', + [TransactionType.RESTAKE]: () => 'operations.modalTitles.restakeOn', + [TransactionType.DESTINATION]: () => 'operations.modalTitles.destinationOn', + [TransactionType.UNSTAKE]: () => 'operations.modalTitles.unstakeOn', + // Technical + [TransactionType.CHILL]: () => 'operations.modalTitles.unstakeOn', + [TransactionType.BATCH_ALL]: () => 'operations.modalTitles.unknownOn', +}; + const TransactionIcons: Record = { // Transfer [TransactionType.ASSET_TRANSFER]: 'transferMst', @@ -71,12 +106,29 @@ export const getTransactionTitle = (transaction?: Transaction | DecodedTransacti } if (transaction.type === TransactionType.BATCH_ALL) { - return getTransactionTitle(transaction?.args?.transactions?.[0]); + return getTransactionTitle(transaction.args?.transactions?.[0]); } return TransactionTitles[transaction.type]; }; +export const getModalTransactionTitle = ( + crossChain: boolean, + transaction?: Transaction | DecodedTransaction, +): string => { + if (!transaction) return TRANSACTION_UNKNOWN; + + if (!transaction.type) { + return formatSectionAndMethod(transaction.section, transaction.method); + } + + if (transaction.type === TransactionType.BATCH_ALL) { + return getModalTransactionTitle(crossChain, transaction.args?.transactions?.[0]); + } + + return TransactionTitlesModal[transaction.type](crossChain); +}; + export const getIconName = (transaction?: Transaction | DecodedTransaction): IconNames => { if (!transaction?.type) return 'question'; @@ -121,14 +173,9 @@ export const getTransactionAmount = (tx: Transaction | DecodedTransaction): stri if (!txType) return null; if ( - [ - TransactionType.ASSET_TRANSFER, - TransactionType.ORML_TRANSFER, - TransactionType.TRANSFER, - TransactionType.BOND, - TransactionType.RESTAKE, - TransactionType.UNSTAKE, - ].includes(txType) + [...TransferTypes, ...XcmTypes, TransactionType.BOND, TransactionType.RESTAKE, TransactionType.UNSTAKE].includes( + txType, + ) ) { return tx.args.value; } diff --git a/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx b/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx index 52dd62c773..c4952f5a9d 100644 --- a/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx +++ b/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx @@ -1,9 +1,10 @@ import { MultisigTransaction, Transaction, Fee } from '@renderer/entities/transaction'; import { TransactionAmount } from '@renderer/pages/Operations/components/TransactionAmount'; -import { DetailRow, FootnoteText } from '@renderer/shared/ui'; +import { DetailRow, FootnoteText, Icon } from '@renderer/shared/ui'; import { MultisigAccount } from '@renderer/entities/account'; import { ExtendedChain } from '@renderer/entities/network'; import { useI18n } from '@renderer/app/providers'; +import { getIconName } from '../../common/utils'; import Details from '../Details'; const AmountFontStyle = 'font-manrope text-text-primary text-[32px] leading-[36px] font-bold'; @@ -17,15 +18,23 @@ type Props = { const Confirmation = ({ tx, account, connection, feeTx }: Props) => { const { t } = useI18n(); + const iconName = getIconName(tx.transaction); + return (
        - {tx.transaction && } +
        +
        + +
        + + {tx.transaction && } - {tx.description && ( - - {tx.description} - - )} + {tx.description && ( + + {tx.description} + + )} +
        diff --git a/src/renderer/pages/Operations/components/Details.tsx b/src/renderer/pages/Operations/components/Details.tsx index 0345b417d9..7985e2ebea 100644 --- a/src/renderer/pages/Operations/components/Details.tsx +++ b/src/renderer/pages/Operations/components/Details.tsx @@ -6,11 +6,12 @@ import { Icon, Button, FootnoteText, DetailRow } from '@renderer/shared/ui'; import { copyToClipboard, truncate, cnTw } from '@renderer/shared/lib/utils'; import { useToggle } from '@renderer/shared/lib/hooks'; import { ExtendedChain } from '@renderer/entities/network'; -import { MultisigTransaction, Transaction, TransactionType } from '@renderer/entities/transaction'; +import { MultisigTransaction, Transaction, TransactionType, isXcmTransaction } from '@renderer/entities/transaction'; import ValidatorsModal from '@renderer/pages/Staking/Operations/components/Modals/ValidatorsModal/ValidatorsModal'; import { AddressStyle, DescriptionBlockStyle, InteractionStyle } from '../common/constants'; import { getMultisigExtrinsicLink } from '../common/utils'; import { AssetBalance } from '@renderer/entities/asset'; +import { ChainTitle } from '@renderer/entities/chain'; type Props = { tx: MultisigTransaction; @@ -85,13 +86,42 @@ const Details = ({ tx, account, connection, isCardDetails = true }: Props) => { )} + {isXcmTransaction(tx.transaction) && ( + <> + {isCardDetails && ( + + + + )} + + {isCardDetails && account && ( + + + + )} + + {transaction.args.destinationChain && ( + + + + )} + + )} + {transaction?.args.dest && ( diff --git a/src/renderer/pages/Operations/components/Operation.tsx b/src/renderer/pages/Operations/components/Operation.tsx index 12fe0cb616..a88638117c 100644 --- a/src/renderer/pages/Operations/components/Operation.tsx +++ b/src/renderer/pages/Operations/components/Operation.tsx @@ -9,8 +9,9 @@ import OperationStatus from './OperationStatus'; import OperationFullInfo from './OperationFullInfo'; import { MultisigTransactionDS } from '@renderer/shared/api/storage'; import { useMultisigEvent } from '@renderer/entities/multisig'; -import { ChainTitle } from '@renderer/entities/chain'; +import { ChainTitle, XcmChains } from '@renderer/entities/chain'; import { getTransactionAmount } from '../common/utils'; +import { isXcmTransaction } from '@renderer/entities/transaction'; type Props = { tx: MultisigTransactionDS; @@ -19,10 +20,9 @@ type Props = { const Operation = ({ tx, account }: Props) => { const { dateLocale } = useI18n(); - const { getLiveEventsByKeys } = useMultisigEvent({}); - const events = getLiveEventsByKeys([tx]); + const events = getLiveEventsByKeys([tx]); const approvals = events?.filter((e) => e.status === 'SIGNED') || []; const initEvent = approvals.find((e) => e.accountId === tx.depositor); const date = new Date(tx.dateCreated || initEvent?.dateCreated || Date.now()); @@ -31,7 +31,7 @@ const Operation = ({ tx, account }: Props) => {
        -
        +
        {format(date, 'p', { locale: dateLocale })} @@ -43,7 +43,15 @@ const Operation = ({ tx, account }: Props) => { )} - + {isXcmTransaction(tx.transaction) ? ( + + ) : ( + + )}
        diff --git a/src/renderer/pages/Operations/components/OperationStatus.tsx b/src/renderer/pages/Operations/components/OperationStatus.tsx index db571a1e33..b3738cb27a 100644 --- a/src/renderer/pages/Operations/components/OperationStatus.tsx +++ b/src/renderer/pages/Operations/components/OperationStatus.tsx @@ -1,5 +1,3 @@ -import cn from 'classnames'; - import { MultisigTransaction, MultisigTxFinalStatus, @@ -8,6 +6,7 @@ import { } from '@renderer/entities/transaction'; import { CaptionText } from '@renderer/shared/ui'; import { useI18n } from '@renderer/app/providers'; +import { cnTw } from '@renderer/shared/lib/utils'; const StatusTitle: Record = { [MultisigTxInitStatus.SIGNING]: 'operation.status.signing', @@ -35,21 +34,19 @@ type Props = { const OperationStatus = ({ status, signed, threshold, className }: Props) => { const { t } = useI18n(); + const text = + status === 'SIGNING' ? t('operation.signing', { signed, threshold: threshold || 0 }) : t(StatusTitle[status]); + return ( - {status === 'SIGNING' - ? t('operation.signing', { - signed: signed, - threshold: threshold || 0, - }) - : t(StatusTitle[status])} + {text} ); }; diff --git a/src/renderer/pages/Operations/components/ShortTransactionInfo.test.tsx b/src/renderer/pages/Operations/components/ShortTransactionInfo.test.tsx index 14f7ac8268..50b173d3b7 100644 --- a/src/renderer/pages/Operations/components/ShortTransactionInfo.test.tsx +++ b/src/renderer/pages/Operations/components/ShortTransactionInfo.test.tsx @@ -21,7 +21,7 @@ const transaction = { }, } as Transaction; -describe('screen/Operations/components/ShortTransactionInfo', () => { +describe('pages/Operations/components/ShortTransactionInfo', () => { test('should render component', async () => { await act(async () => { render( diff --git a/src/renderer/pages/Operations/components/TransactionAmount.tsx b/src/renderer/pages/Operations/components/TransactionAmount.tsx index f4e9119036..4761c6438d 100644 --- a/src/renderer/pages/Operations/components/TransactionAmount.tsx +++ b/src/renderer/pages/Operations/components/TransactionAmount.tsx @@ -17,7 +17,9 @@ export const TransactionAmount = ({ tx, ...balanceProps }: Props & BalanceProps) const [assets, setAssets] = useState([]); useEffect(() => { - getChainById(tx.chainId).then((chain) => setAssets(chain?.assets || [])); + const chain = getChainById(tx.chainId); + + setAssets(chain?.assets || []); }, []); const asset = getAssetById(tx.args.assetId, assets); diff --git a/src/renderer/pages/Operations/components/modals/ApproveTx.tsx b/src/renderer/pages/Operations/components/modals/ApproveTx.tsx index cd6f51588b..7e60645bf2 100644 --- a/src/renderer/pages/Operations/components/modals/ApproveTx.tsx +++ b/src/renderer/pages/Operations/components/modals/ApproveTx.tsx @@ -10,8 +10,8 @@ import { useToggle } from '@renderer/shared/lib/hooks'; import { Account, MultisigAccount, useAccount } from '@renderer/entities/account'; import { ExtendedChain } from '@renderer/entities/network'; import { Address, HexString, SigningType, Timepoint } from '@renderer/domain/shared-kernel'; -import { TEST_ADDRESS, toAddress, transferableAmount } from '@renderer/shared/lib/utils'; -import { getTransactionTitle } from '../../common/utils'; +import { TEST_ADDRESS, toAddress, transferableAmount, getAssetById } from '@renderer/shared/lib/utils'; +import { getModalTransactionTitle } from '../../common/utils'; import { Submit } from '../ActionSteps/Submit'; import { useBalance } from '@renderer/entities/asset'; import Confirmation from '@renderer/pages/Operations/components/ActionSteps/Confirmation'; @@ -26,6 +26,7 @@ import { useCallDataDecoder, useTransaction, validateBalance, + isXcmTransaction, } from '@renderer/entities/transaction'; type Props = { @@ -66,7 +67,10 @@ const ApproveTx = ({ tx, account, connection }: Props) => { const [signature, setSignature] = useState(); const accounts = getLiveAccounts(); - const transactionTitle = getTransactionTitle(tx.transaction); + const transactionTitle = getModalTransactionTitle(isXcmTransaction(tx.transaction), tx.transaction); + + const nativeAsset = connection.assets[0]; + const asset = getAssetById(tx.transaction?.args.assetId, connection.assets); const unsignedAccounts = accounts.filter((a) => { const isSignatory = account.signatories.find((s) => s.accountId === a.accountId); @@ -77,8 +81,6 @@ const ApproveTx = ({ tx, account, connection }: Props) => { return isSignatory && notSigned && isCurrentChain && notWatchOnly; }); - const nativeAsset = connection.assets[0]; - useEffect(() => { setFeeTx(getMultisigTx(TEST_ADDRESS)); @@ -205,7 +207,7 @@ const ApproveTx = ({ tx, account, connection }: Props) => { } + title={} contentClass={activeStep === Step.SIGNING ? '' : undefined} headerClass="py-3 px-5 max-w-[440px]" panelClass="w-[440px]" diff --git a/src/renderer/pages/Operations/components/modals/RejectTx.tsx b/src/renderer/pages/Operations/components/modals/RejectTx.tsx index d78b78bcb5..00f87a5fdd 100644 --- a/src/renderer/pages/Operations/components/modals/RejectTx.tsx +++ b/src/renderer/pages/Operations/components/modals/RejectTx.tsx @@ -9,8 +9,8 @@ import { useToggle } from '@renderer/shared/lib/hooks'; import { MultisigAccount, useAccount } from '@renderer/entities/account'; import { ExtendedChain } from '@renderer/entities/network'; import { Address, HexString, Timepoint } from '@renderer/domain/shared-kernel'; -import { toAddress, transferableAmount } from '@renderer/shared/lib/utils'; -import { getTransactionTitle } from '../../common/utils'; +import { toAddress, transferableAmount, getAssetById } from '@renderer/shared/lib/utils'; +import { getModalTransactionTitle } from '../../common/utils'; import { Submit } from '../ActionSteps/Submit'; import { useBalance } from '@renderer/entities/asset'; import RejectReasonModal from './RejectReasonModal'; @@ -23,6 +23,7 @@ import { useTransaction, OperationResult, validateBalance, + isXcmTransaction, } from '@renderer/entities/transaction'; type Props = { @@ -59,9 +60,10 @@ const RejectTx = ({ tx, account, connection }: Props) => { const accounts = getLiveAccounts(); const signAccount = accounts.find((a) => a.accountId === tx.depositor); - const transactionTitle = getTransactionTitle(tx.transaction); + const transactionTitle = getModalTransactionTitle(isXcmTransaction(tx.transaction), tx.transaction); const nativeAsset = connection.assets[0]; + const asset = getAssetById(tx.transaction?.args.assetId, connection.assets); const checkBalance = () => validateBalance({ @@ -164,7 +166,7 @@ const RejectTx = ({ tx, account, connection }: Props) => { isOpen={activeStep !== Step.SUBMIT && isModalOpen} title={ } diff --git a/src/renderer/pages/Settings/Networks/Networks.test.tsx b/src/renderer/pages/Settings/Networks/Networks.test.tsx index 30c648ddcd..91b7b65917 100644 --- a/src/renderer/pages/Settings/Networks/Networks.test.tsx +++ b/src/renderer/pages/Settings/Networks/Networks.test.tsx @@ -88,7 +88,7 @@ jest.mock('./components', () => ({ ), })); -describe('screen/Settings/Networks', () => { +describe('pages/Settings/Networks', () => { test('should render component', async () => { await act(async () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/pages/Settings/Networks/components/CustomRpcModal/CustomRpcModal.test.tsx b/src/renderer/pages/Settings/Networks/components/CustomRpcModal/CustomRpcModal.test.tsx index dd3fd77eec..8a59f66e43 100644 --- a/src/renderer/pages/Settings/Networks/components/CustomRpcModal/CustomRpcModal.test.tsx +++ b/src/renderer/pages/Settings/Networks/components/CustomRpcModal/CustomRpcModal.test.tsx @@ -16,7 +16,7 @@ jest.mock('@renderer/app/providers', () => ({ })), })); -describe('screen/Settings/Networks/CustomRpcModal', () => { +describe('pages/Settings/Networks/CustomRpcModal', () => { const defaultProps = { network: { name: 'Westend', diff --git a/src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.test.tsx b/src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.test.tsx index bdc0b5c57b..9c3068cee9 100644 --- a/src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.test.tsx +++ b/src/renderer/pages/Settings/Networks/components/NetworkItem/NetworkItem.test.tsx @@ -15,7 +15,7 @@ jest.mock('../NetworkSelector/NetworkSelector', () => ({ NetworkSelector: () => 'selector', })); -describe('screen/Settings/Networks/NetworkItem', () => { +describe('pages/Settings/Networks/NetworkItem', () => { const defaultProps = { networkItem: { icon: 'wnd.svg', diff --git a/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.test.tsx b/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.test.tsx index 341ad097e6..696d402939 100644 --- a/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.test.tsx +++ b/src/renderer/pages/Settings/Networks/components/NetworkList/NetworkList.test.tsx @@ -4,7 +4,7 @@ import { ConnectionStatus, ConnectionType } from '@renderer/domain/connection'; import { ExtendedChain } from '@renderer/entities/network'; import { NetworkList } from './NetworkList'; -describe('screen/Settings/Networks/NetworkList', () => { +describe('pages/Settings/Networks/NetworkList', () => { const children = () => 'children'; const networks: ExtendedChain[] = [ { diff --git a/src/renderer/pages/Settings/Networks/components/NetworkSelector/NetworkSelector.test.tsx b/src/renderer/pages/Settings/Networks/components/NetworkSelector/NetworkSelector.test.tsx index bf11ca46a3..6f6583e1c2 100644 --- a/src/renderer/pages/Settings/Networks/components/NetworkSelector/NetworkSelector.test.tsx +++ b/src/renderer/pages/Settings/Networks/components/NetworkSelector/NetworkSelector.test.tsx @@ -14,7 +14,7 @@ jest.mock('@renderer/app/providers', () => ({ jest.mock('@renderer/shared/lib/hooks'); -describe('screen/Settings/Networks/NetworkSelector', () => { +describe('pages/Settings/Networks/NetworkSelector', () => { beforeAll(() => { (useScrollTo as jest.Mock).mockReturnValue([{ current: {} }, noop]); }); diff --git a/src/renderer/pages/Settings/Overview/Overview.test.tsx b/src/renderer/pages/Settings/Overview/Overview.test.tsx index 90fa97fe90..e563d2bab8 100644 --- a/src/renderer/pages/Settings/Overview/Overview.test.tsx +++ b/src/renderer/pages/Settings/Overview/Overview.test.tsx @@ -15,7 +15,7 @@ jest.mock('./components', () => ({ Version: () => version, })); -describe('screen/Settings/Overview', () => { +describe('pages/Settings/Overview', () => { test('should render all parts', () => { render(); diff --git a/src/renderer/pages/Settings/Overview/components/GeneralActions/GeneralActions.test.tsx b/src/renderer/pages/Settings/Overview/components/GeneralActions/GeneralActions.test.tsx index 81414b0f9d..16e2dd48b9 100644 --- a/src/renderer/pages/Settings/Overview/components/GeneralActions/GeneralActions.test.tsx +++ b/src/renderer/pages/Settings/Overview/components/GeneralActions/GeneralActions.test.tsx @@ -10,7 +10,7 @@ jest.mock('@renderer/app/providers', () => ({ }), })); -describe('screen/Settings/Overview/GeneralActions', () => { +describe('pages/Settings/Overview/GeneralActions', () => { test('should render label and link to network', () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/pages/Settings/Overview/components/MatrixAction/MatrixAction.test.tsx b/src/renderer/pages/Settings/Overview/components/MatrixAction/MatrixAction.test.tsx index cce8b711da..ffd4b16bae 100644 --- a/src/renderer/pages/Settings/Overview/components/MatrixAction/MatrixAction.test.tsx +++ b/src/renderer/pages/Settings/Overview/components/MatrixAction/MatrixAction.test.tsx @@ -17,7 +17,7 @@ jest.mock('@renderer/components/modals', () => ({ MatrixModal: () => matrixModal, })); -describe('screen/Settings/Overview/MatrixAction', () => { +describe('pages/Settings/Overview/MatrixAction', () => { test('should render label and link to SMP', () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/pages/Settings/Overview/components/SocialLinks/SocialLinks.test.tsx b/src/renderer/pages/Settings/Overview/components/SocialLinks/SocialLinks.test.tsx index 2dc45d9235..085c832265 100644 --- a/src/renderer/pages/Settings/Overview/components/SocialLinks/SocialLinks.test.tsx +++ b/src/renderer/pages/Settings/Overview/components/SocialLinks/SocialLinks.test.tsx @@ -8,7 +8,7 @@ jest.mock('@renderer/app/providers', () => ({ }), })); -describe('screen/Settings/Overview/SocialLinks', () => { +describe('pages/Settings/Overview/SocialLinks', () => { test('should render all social links', () => { render(); diff --git a/src/renderer/pages/Settings/Overview/components/Version/Version.test.tsx b/src/renderer/pages/Settings/Overview/components/Version/Version.test.tsx index 9f6d55137d..266d2d5990 100644 --- a/src/renderer/pages/Settings/Overview/components/Version/Version.test.tsx +++ b/src/renderer/pages/Settings/Overview/components/Version/Version.test.tsx @@ -8,7 +8,7 @@ jest.mock('@renderer/app/providers', () => ({ }), })); -describe('screen/Settings/Overview/Version', () => { +describe('pages/Settings/Overview/Version', () => { test('should render app version', () => { process.env.VERSION = '1.0.0'; render(); diff --git a/src/renderer/pages/Staking/Operations/Bond/Bond.tsx b/src/renderer/pages/Staking/Operations/Bond/Bond.tsx index 805e5cd9e6..751757f73e 100644 --- a/src/renderer/pages/Staking/Operations/Bond/Bond.tsx +++ b/src/renderer/pages/Staking/Operations/Bond/Bond.tsx @@ -95,7 +95,7 @@ export const Bond = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={isBondModalOpen} - title={} + title={} onClose={closeBondModal} >
        @@ -116,7 +116,7 @@ export const Bond = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={isBondModalOpen} - title={} + title={} onClose={closeBondModal} >
        @@ -230,7 +230,7 @@ export const Bond = () => { panelClass="w-max" headerClass="py-3 px-5 max-w-[440px]" isOpen={activeStep !== Step.SUBMIT && isBondModalOpen} - title={} + title={} onClose={closeBondModal} > {activeStep === Step.INIT && ( diff --git a/src/renderer/pages/Staking/Operations/ChangeValidators/ChangeValidators.tsx b/src/renderer/pages/Staking/Operations/ChangeValidators/ChangeValidators.tsx index 7ba05e9912..3c123f44d7 100644 --- a/src/renderer/pages/Staking/Operations/ChangeValidators/ChangeValidators.tsx +++ b/src/renderer/pages/Staking/Operations/ChangeValidators/ChangeValidators.tsx @@ -91,7 +91,7 @@ export const ChangeValidators = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={isValidatorsModalOpen} - title={} + title={} onClose={closeValidatorsModal} >
        @@ -112,7 +112,7 @@ export const ChangeValidators = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={isValidatorsModalOpen} - title={} + title={} onClose={closeValidatorsModal} >
        @@ -201,7 +201,7 @@ export const ChangeValidators = () => { panelClass="w-max" headerClass="py-3 px-5 max-w-[440px]" isOpen={activeStep !== Step.SUBMIT && isValidatorsModalOpen} - title={} + title={} onClose={closeValidatorsModal} > {activeStep === Step.INIT && ( diff --git a/src/renderer/pages/Staking/Operations/Destination/Destination.tsx b/src/renderer/pages/Staking/Operations/Destination/Destination.tsx index caf6d525a3..1ba6d67704 100644 --- a/src/renderer/pages/Staking/Operations/Destination/Destination.tsx +++ b/src/renderer/pages/Staking/Operations/Destination/Destination.tsx @@ -89,7 +89,7 @@ export const Destination = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={isDestModalOpen} - title={} + title={} onClose={closeDestinationModal} >
        @@ -110,7 +110,7 @@ export const Destination = () => { panelClass="w-max" headerClass="py-3 px-5 max-w-[440px]" isOpen={isDestModalOpen} - title={} + title={} onClose={closeDestinationModal} >
        @@ -194,7 +194,7 @@ export const Destination = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={activeStep !== Step.SUBMIT && isDestModalOpen} - title={} + title={} onClose={closeDestinationModal} > {activeStep === Step.INIT && ( diff --git a/src/renderer/pages/Staking/Operations/Redeem/Redeem.tsx b/src/renderer/pages/Staking/Operations/Redeem/Redeem.tsx index 80643f73f3..c1c527d236 100644 --- a/src/renderer/pages/Staking/Operations/Redeem/Redeem.tsx +++ b/src/renderer/pages/Staking/Operations/Redeem/Redeem.tsx @@ -92,7 +92,7 @@ export const Redeem = () => { panelClass="w-max" headerClass="py-3 px-5 max-w-[440px]" isOpen={isRedeemModalOpen} - title={} + title={} onClose={closeRedeemModal} >
        @@ -113,7 +113,7 @@ export const Redeem = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={isRedeemModalOpen} - title={} + title={} onClose={closeRedeemModal} >
        @@ -193,7 +193,7 @@ export const Redeem = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={activeStep !== Step.SUBMIT && isRedeemModalOpen} - title={} + title={} onClose={closeRedeemModal} > {activeStep === Step.INIT && ( diff --git a/src/renderer/pages/Staking/Operations/Restake/Restake.tsx b/src/renderer/pages/Staking/Operations/Restake/Restake.tsx index bf4545be9f..a7f57d7271 100644 --- a/src/renderer/pages/Staking/Operations/Restake/Restake.tsx +++ b/src/renderer/pages/Staking/Operations/Restake/Restake.tsx @@ -88,7 +88,7 @@ export const Restake = () => { panelClass="w-max" headerClass="py-3 px-5 max-w-[440px]" isOpen={isRestakeModalOpen} - title={} + title={} onClose={closeRestakeModal} >
        @@ -109,7 +109,7 @@ export const Restake = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={isRestakeModalOpen} - title={} + title={} onClose={closeRestakeModal} >
        @@ -190,7 +190,7 @@ export const Restake = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={activeStep !== Step.SUBMIT && isRestakeModalOpen} - title={} + title={} onClose={closeRestakeModal} > {activeStep === Step.INIT && ( diff --git a/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.tsx b/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.tsx index ea82b0ea97..5f6ef1361b 100644 --- a/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.tsx +++ b/src/renderer/pages/Staking/Operations/StakeMore/StakeMore.tsx @@ -88,7 +88,7 @@ export const StakeMore = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={isStakeMoreModalOpen} - title={} + title={} onClose={closeStakeMoreModal} >
        @@ -109,7 +109,7 @@ export const StakeMore = () => { panelClass="w-max" headerClass="py-3 px-5 max-w-[440px]" isOpen={isStakeMoreModalOpen} - title={} + title={} onClose={closeStakeMoreModal} >
        @@ -190,7 +190,7 @@ export const StakeMore = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={activeStep !== Step.SUBMIT && isStakeMoreModalOpen} - title={} + title={} onClose={closeStakeMoreModal} > {activeStep === Step.INIT && ( diff --git a/src/renderer/pages/Staking/Operations/Unstake/Unstake.tsx b/src/renderer/pages/Staking/Operations/Unstake/Unstake.tsx index 5815241272..11445e7335 100644 --- a/src/renderer/pages/Staking/Operations/Unstake/Unstake.tsx +++ b/src/renderer/pages/Staking/Operations/Unstake/Unstake.tsx @@ -89,7 +89,7 @@ export const Unstake = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={isUnstakeModalOpen} - title={} + title={} onClose={closeUnstakeModal} >
        @@ -110,7 +110,7 @@ export const Unstake = () => { headerClass="py-3 px-5 max-w-[440px]" panelClass="w-max" isOpen={isUnstakeModalOpen} - title={} + title={} onClose={closeUnstakeModal} >
        @@ -209,7 +209,7 @@ export const Unstake = () => { panelClass="w-max" headerClass="py-3 px-5 max-w-[440px]" isOpen={activeStep !== Step.SUBMIT && isUnstakeModalOpen} - title={} + title={} onClose={closeUnstakeModal} > {activeStep === Step.INIT && ( diff --git a/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx index 748d145d39..95f2798702 100644 --- a/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx +++ b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx @@ -14,7 +14,7 @@ jest.mock('@renderer/app/providers', () => ({ jest.mock('@renderer/entities/network', () => ({ useChains: jest.fn().mockReturnValue({ sortChains: jest.fn((value: Chain[]) => value), - getChainsData: jest.fn().mockResolvedValue([ + getChainsData: jest.fn().mockReturnValue([ { chainId: '0x123', name: 'WND chain', diff --git a/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx index 31c3deca7e..d50e2c935e 100644 --- a/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx +++ b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx @@ -40,35 +40,34 @@ export const NetworkInfo = ({ const [activeNetwork, setActiveNetwork] = useState>(); useEffect(() => { - getChainsData().then((chainsData) => { - const relaychains = sortChains(chainsData).reduce[]>((acc, chain) => { - const { chainId, assets } = chain; + const chains = getChainsData(); + const relaychains = sortChains(chains).reduce[]>((acc, chain) => { + const { chainId, assets } = chain; - if (getRelaychainAsset(assets)) { - // without key dropdown doesn't show changes (thought functionally everything works fine) - // TODO look into it - const element = ( - - ); + if (getRelaychainAsset(assets)) { + // without key dropdown doesn't show changes (thought functionally everything works fine) + // TODO look into it + const element = ( + + ); - acc.push({ id: chainId, value: chain, element }); - } + acc.push({ id: chainId, value: chain, element }); + } - return acc; - }, []); + return acc; + }, []); - const settingsChainId = getStakingNetwork(); - const settingsChain = relaychains.find((chain) => chain.id === settingsChainId); + const settingsChainId = getStakingNetwork(); + const settingsChain = relaychains.find((chain) => chain.id === settingsChainId); - setNetworks(relaychains); - setActiveNetwork(settingsChain || { id: relaychains[0].id, value: relaychains[0].value }); - onNetworkChange(settingsChain?.value || relaychains[0].value); - }); + setNetworks(relaychains); + setActiveNetwork(settingsChain || { id: relaychains[0].id, value: relaychains[0].value }); + onNetworkChange(settingsChain?.value || relaychains[0].value); }, []); const totalInfo = [ diff --git a/src/renderer/shared/api/xcm/__tests__/mock/xcmData.ts b/src/renderer/shared/api/xcm/__tests__/mock/xcmData.ts index 0b4172f999..81af69bbaa 100644 --- a/src/renderer/shared/api/xcm/__tests__/mock/xcmData.ts +++ b/src/renderer/shared/api/xcm/__tests__/mock/xcmData.ts @@ -148,3 +148,168 @@ export const CONFIG: XcmConfig = { }, ], }; + +export const XCMPALLET_TRANSFER_KSM_BIFROST = { + dest: { + V2: { + parents: '0', + interior: { + X1: { + Parachain: '2,001', + }, + }, + }, + }, + beneficiary: { + V2: { + parents: '0', + interior: { + X1: { + AccountId32: { + network: 'Any', + id: '0x7a28037947ecebe0dd86dc0e910911cb33185fd0714b37b75943f67dcf9b6e7c', + }, + }, + }, + }, + }, + assets: { + V2: [ + { + id: { + Concrete: { + parents: '0', + interior: 'Here', + }, + }, + fun: { + Fungible: '10,070,392,000', + }, + }, + ], + }, +}; + +export const XCMPALLET_TRANSFER_HUB_ASTAR = { + dest: { + V2: { + parents: '1', + interior: { + X1: { + Parachain: '2,006', + }, + }, + }, + }, + beneficiary: { + V2: { + parents: '0', + interior: { + X1: { + AccountId32: { + network: 'Any', + id: '0x4d081065a791aaabf8c4c9ec8ed87dce10145c86869c66e80286645730d70c44', + }, + }, + }, + }, + }, + assets: { + V2: [ + { + id: { + Concrete: { + parents: '0', + interior: { + X2: { + col0: { + PalletInstance: 50, + }, + col1: { + GeneralIndex: '1984', + }, + }, + }, + }, + }, + fun: { + Fungible: '176,500,000', + }, + }, + ], + }, +}; + +export const XTOKENS_ACA_DOT = { + asset: { + V2: { + id: { + Concrete: { + parents: '1', + interior: 'Here', + }, + }, + fun: { + Fungible: '4,371,581,450', + }, + }, + }, + dest: { + V2: { + parents: '0', + interior: { + X1: { + AccountId32: { + network: 'Any', + id: '0x7a28037947ecebe0dd86dc0e910911cb33185fd0714b37b75943f67dcf9b6e7c', + }, + }, + }, + }, + }, +}; + +export const XTOKENS_ACA_PARALLEL = { + asset: { + V2: { + fun: { + Fungible: '617,647,058,823', + }, + id: { + Concrete: { + interior: { + X2: { + col0: { + Parachain: 2000, + }, + col1: { + GeneralKey: '0x0000', + }, + }, + }, + parents: 1, + }, + }, + }, + }, + dest: { + V2: { + parents: 1, + interior: { + X2: { + col0: { + Parachain: 2012, + }, + col1: { + AccountId32: { + id: '0xd02b1de0e29d201d48f1a48fb0ead05bf292366ffe90efec9368bb2c7849de59', + network: { + Any: 'NULL', + }, + }, + }, + }, + }, + }, + }, +}; diff --git a/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts b/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts index 05528a9526..eafd0c5187 100644 --- a/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts +++ b/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts @@ -1,8 +1,20 @@ import { ApiPromise } from '@polkadot/api'; import { XCM_KEY } from '../common/constants'; -import { estimateFee, getXcmConfig, getDestinationLocation } from '../xcmService'; -import { CONFIG } from '@renderer/shared/api/xcm/__tests__/mock/xcmData'; +import { + estimateFee, + getXcmConfig, + getDestinationLocation, + parseXTokensExtrinsic, + parseXcmPalletExtrinsic, +} from '../xcmService'; +import { + CONFIG, + XTOKENS_ACA_PARALLEL, + XTOKENS_ACA_DOT, + XCMPALLET_TRANSFER_KSM_BIFROST, + XCMPALLET_TRANSFER_HUB_ASTAR, +} from './mock/xcmData'; const mockApi = () => ({ @@ -85,4 +97,62 @@ describe('shared/api/xcm/xcmService', () => { expect(location.V2.parents).toEqual(0); expect(location.V2.interior.X1.Parachain).toEqual(2000); }); + + test('should parse xcmPallet relaychain > parachain', () => { + const result = parseXcmPalletExtrinsic(XCMPALLET_TRANSFER_KSM_BIFROST); + + expect(result).toEqual({ + isRelayToken: true, + amount: '10070392000', + destParachain: 2001, + destAccountId: '0x7a28037947ecebe0dd86dc0e910911cb33185fd0714b37b75943f67dcf9b6e7c', + assetGeneralIndex: '', + toRelayChain: false, + type: 'xcmPallet', + }); + }); + + test('should parse xcmPallet parachain > parachain', () => { + const result = parseXcmPalletExtrinsic(XCMPALLET_TRANSFER_HUB_ASTAR); + + expect(result).toEqual({ + isRelayToken: false, + amount: '176500000', + destParachain: 2006, + destAccountId: '0x4d081065a791aaabf8c4c9ec8ed87dce10145c86869c66e80286645730d70c44', + assetGeneralIndex: '1984', + toRelayChain: false, + type: 'xcmPallet', + }); + }); + + test('should parse xTokens parachain > parachain', () => { + const result = parseXTokensExtrinsic(XTOKENS_ACA_PARALLEL); + + expect(result).toEqual({ + isRelayToken: false, + amount: '617647058823', + destParachain: 2012, + destAccountId: '0xd02b1de0e29d201d48f1a48fb0ead05bf292366ffe90efec9368bb2c7849de59', + assetParachain: 2000, + assetGeneralKey: '0x0000', + toRelayChain: false, + type: 'xTokens', + }); + }); + + test('should parse xTokens parachain > relaychain', () => { + const result = parseXTokensExtrinsic(XTOKENS_ACA_DOT); + + expect(result).toEqual({ + isRelayToken: true, + amount: '4371581450', + destParachain: 0, + destAccountId: '0x7a28037947ecebe0dd86dc0e910911cb33185fd0714b37b75943f67dcf9b6e7c', + assetGeneralKey: '', + assetParachain: 0, + toRelayChain: true, + type: 'xTokens', + }); + }); }); diff --git a/src/renderer/shared/api/xcm/common/types.ts b/src/renderer/shared/api/xcm/common/types.ts index 2e5223e491..87cd024730 100644 --- a/src/renderer/shared/api/xcm/common/types.ts +++ b/src/renderer/shared/api/xcm/common/types.ts @@ -14,6 +14,7 @@ export type MultiLocation = { parachainId?: number; palletInstance?: number; generalKey?: HexString; + generalIndex?: string; }; export type Fee = { diff --git a/src/renderer/shared/api/xcm/common/utils.ts b/src/renderer/shared/api/xcm/common/utils.ts new file mode 100644 index 0000000000..5a09c53a4d --- /dev/null +++ b/src/renderer/shared/api/xcm/common/utils.ts @@ -0,0 +1,5 @@ +export const toRawString = (value?: string): string => { + if (!value) return ''; + + return value.replaceAll(',', ''); +}; diff --git a/src/renderer/shared/api/xcm/xcmService.ts b/src/renderer/shared/api/xcm/xcmService.ts index 161ccb8dba..8f12c55b41 100644 --- a/src/renderer/shared/api/xcm/xcmService.ts +++ b/src/renderer/shared/api/xcm/xcmService.ts @@ -1,6 +1,7 @@ import { BN, BN_TEN, BN_ZERO } from '@polkadot/util'; import { ApiPromise } from '@polkadot/api'; import { VersionedMultiAsset, VersionedMultiLocation } from '@polkadot/types/interfaces'; +import get from 'lodash/get'; import { XCM_URL, XCM_KEY } from './common/constants'; import { @@ -14,10 +15,15 @@ import { Instructions, NetworkBaseWeight, XcmTransfer, + PathType, } from './common/types'; -import { AccountId, ChainId } from '@renderer/domain/shared-kernel'; +import { AccountId, ChainId, HexString } from '@renderer/domain/shared-kernel'; // TODO: Move chain to shared import { Chain } from '@renderer/entities/chain'; +import { XcmPalletTransferArgs, XTokenPalletTransferArgs } from '@renderer/entities/transaction'; +import { useChains } from '@renderer/entities/network'; +import { getAssetId } from '@renderer/shared/lib/utils'; +import { toRawString } from './common/utils'; export const fetchXcmConfig = async (): Promise => { const response = await fetch(XCM_URL, { cache: 'default' }); @@ -132,11 +138,13 @@ export const getAssetLocation = ( assets: Record, amount: BN, ): VersionedMultiAsset | undefined => { - return { + const PathMap: Record VersionedMultiAsset | undefined> = { relative: () => getRelativeAssetLocation(api, amount, assets[asset.assetLocation].multiLocation), absolute: () => getAbsoluteAssetLocation(api, amount, assets[asset.assetLocation].multiLocation), concrete: () => getConcreteAssetLocation(api, amount, asset.assetLocationPath.path), - }[asset.assetLocationPath.type](); + }; + + return PathMap[asset.assetLocationPath.type](); }; const getRelativeAssetLocation = ( @@ -283,3 +291,168 @@ const getSiblingLocation = (api: ApiPromise, parachainId: number, accountId?: Ac }, }); }; + +type ParsedPayload = { + isRelayToken: boolean; + amount: string; + destParachain: number; + destAccountId: string; + toRelayChain: boolean; +}; + +type XcmPalletPayload = ParsedPayload & { + assetGeneralIndex: string; + type: 'xcmPallet'; +}; + +type XTokensPayload = ParsedPayload & { + assetGeneralKey: string; + assetParachain: number; + type: 'xTokens'; +}; + +export const parseXcmPalletExtrinsic = ( + args: Omit, +): XcmPalletPayload => { + const xcmVersion = Object.keys(args.dest as Object)[0]; + + const assetInterior = get(args.assets, `${xcmVersion}[0].id.Concrete.interior`) as Object; + const destInterior = get(args.dest, `${xcmVersion}.interior`) as Object; + const beneficiaryInterior = get(args.beneficiary, `${xcmVersion}.interior`) as Object; + + const parsedPayload = { + isRelayToken: assetInterior === 'Here', + amount: toRawString(get(args.assets, `${xcmVersion}[0].fun.Fungible`)), + destParachain: 0, + destAccountId: '', + assetGeneralIndex: '', + toRelayChain: destInterior === 'Here', + type: 'xcmPallet' as const, + }; + + const beneficiaryJunction = Object.keys(beneficiaryInterior)[0]; + parsedPayload.destAccountId = get(beneficiaryInterior, `${beneficiaryJunction}.AccountId32.id`); + + const destJunction = Object.keys(destInterior)[0]; + parsedPayload.destParachain = Number(toRawString(get(destInterior, `${destJunction}.Parachain`))); + + if (!parsedPayload.isRelayToken) { + const assetJunction = Object.keys(assetInterior)[0]; + const cols = getJunctionCols<{ GeneralIndex: string }>(assetInterior, assetJunction); + parsedPayload.assetGeneralIndex = toRawString(cols.GeneralIndex); + } + + return parsedPayload; +}; + +export const parseXTokensExtrinsic = ( + args: Omit, +): XTokensPayload => { + const xcmVersion = Object.keys(args.dest as Object)[0]; + + const assetInterior = get(args.asset, `${xcmVersion}.id.Concrete.interior`) as Object; + const destInterior = get(args.dest, `${xcmVersion}.interior`) as Object; + + const parsedPayload = { + isRelayToken: assetInterior === 'Here', + amount: toRawString(get(args.asset, `${xcmVersion}.fun.Fungible`)), + destParachain: 0, + destAccountId: '', + assetParachain: 0, + assetGeneralKey: '', + toRelayChain: false, + type: 'xTokens' as const, + }; + + if (!parsedPayload.isRelayToken) { + const assetJunction = Object.keys(assetInterior)[0]; + const cols = getJunctionCols<{ Parachain: number; GeneralKey: string }>(assetInterior, assetJunction); + parsedPayload.assetParachain = cols.Parachain; + parsedPayload.assetGeneralKey = cols.GeneralKey; + } + + const destJunction = Object.keys(destInterior)[0]; + parsedPayload.toRelayChain = destJunction === 'X1'; + + if (parsedPayload.toRelayChain) { + parsedPayload.destAccountId = get(destInterior, 'X1.AccountId32.id'); + } else { + const cols = getJunctionCols<{ Parachain?: number }>(destInterior, destJunction); + if (cols.Parachain) { + parsedPayload.destParachain = cols.Parachain; + parsedPayload.toRelayChain = false; + } + parsedPayload.destAccountId = get(cols, 'AccountId32.id'); + } + + return parsedPayload; +}; + +type DecodedPayload = { + assetId?: number | string; + destinationChain?: HexString; + value: string; + dest: string; +}; + +const getJunctionCols = (interior: Object, path: string): T => { + return Object.values(get(interior, path) as Object).reduce((acc, item) => { + return { ...acc, ...item }; + }, {}); +}; + +export const decodeXcm = (chainId: ChainId, data: XcmPalletPayload | XTokensPayload): DecodedPayload => { + const { getChainById } = useChains(); + + const config = getXcmConfig(); + if (!config) return {} as DecodedPayload; + + let destinationChain: HexString | undefined; + if (data.toRelayChain) { + destinationChain = getChainById(chainId)?.parentId; + } else { + const destination = Object.values(config.assetsLocation).find(({ multiLocation }) => { + return multiLocation.parachainId === data.destParachain; + }); + + destinationChain = destination ? `0x${destination.chainId}` : undefined; + } + + const configOriginChain = config.chains.find((c) => `0x${c.chainId}` === chainId); + + let assetId: number | string | undefined; + if (!data.isRelayToken && configOriginChain) { + const filteredAssetLocations = configOriginChain.assets.reduce<[number, AssetLocation][]>((acc, asset) => { + acc.push([asset.assetId, config.assetsLocation[asset.assetLocation]]); + + return acc; + }, []); + + const assetKeyVal = filteredAssetLocations.find(([_, { multiLocation }]) => { + const xcmPalletMatch = data.type === 'xcmPallet' && multiLocation.generalIndex === data.assetGeneralIndex; + + const xTokensMatch = + data.type === 'xTokens' && + multiLocation.parachainId === data.assetParachain && + multiLocation.generalKey === data.assetGeneralKey; + + return xcmPalletMatch || xTokensMatch; + }); + + if (assetKeyVal) { + const assetFromChain = getChainById(chainId)?.assets.find((asset) => asset.assetId === assetKeyVal[0]); + if (assetFromChain) { + assetId = getAssetId(assetFromChain); + } + } else { + console.log(`XCM config cannot handle - ${data}`); + } + } + + return { + assetId, + destinationChain, + value: data.amount, + dest: data.destAccountId, + }; +}; diff --git a/src/renderer/shared/ui/InfoLink/InfoLink.test.tsx b/src/renderer/shared/ui/InfoLink/InfoLink.test.tsx index 100109af36..580a346aab 100644 --- a/src/renderer/shared/ui/InfoLink/InfoLink.test.tsx +++ b/src/renderer/shared/ui/InfoLink/InfoLink.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'; import InfoLink from './InfoLink'; -describe('screen/Settings/InfoLink', () => { +describe('pages/Settings/InfoLink', () => { test('should render component', () => { render( diff --git a/src/renderer/shared/ui/PopoverLink/PopoverLink.test.tsx b/src/renderer/shared/ui/PopoverLink/PopoverLink.test.tsx index 1b9e67eaa8..93aa86dc71 100644 --- a/src/renderer/shared/ui/PopoverLink/PopoverLink.test.tsx +++ b/src/renderer/shared/ui/PopoverLink/PopoverLink.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'; import PopoverLink from './PopoverLink'; -describe('screen/Settings/PopoverLink', () => { +describe('pages/Settings/PopoverLink', () => { test('should render component', () => { render( diff --git a/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx b/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx index 995d252161..759946baf9 100644 --- a/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx @@ -111,7 +111,7 @@ export const SendAssetModal = ({ chain, asset, onClose }: Props) => { } + title={} contentClass={activeStep === Step.SIGNING ? '' : undefined} panelClass="w-[440px]" headerClass="py-3 px-5 max-w-[440px]" @@ -126,7 +126,7 @@ export const SendAssetModal = ({ chain, asset, onClose }: Props) => {
        ) : ( <> - {activeStep === Step.INIT && asset && ( + {activeStep === Step.INIT && ( Date: Thu, 14 Sep 2023 23:46:18 +0500 Subject: [PATCH 20/34] feat: xcm transfer flow (#1055) --- .github/workflows/release.yaml | 3 +- package.json | 6 + src/main/index.ts | 13 +- .../CreateMultisigAccount.test.tsx | 21 +- .../CreateMultisigAccount.tsx | 6 +- .../transaction/lib/common/constants.ts | 3 + .../entities/transaction/lib/common/types.ts | 2 +- .../entities/transaction/lib/common/utils.ts | 6 +- .../transaction/lib/common/xcmMethods.ts | 4 +- .../transaction/lib/transactionService.ts | 113 ++++++-- .../transaction/lib/validateBalance.ts | 8 +- .../transaction/ui/XcmFee/XcmFee.test.tsx | 46 ++++ .../entities/transaction/ui/XcmFee/XcmFee.tsx | 72 +++++ .../AssetRouteGuard/model/asset-guard.ts | 14 +- .../AssetRouteGuard/ui/AssetRouteGuard.tsx | 4 + .../EditRouteGuard/model/edit-guard.ts | 12 +- .../EditRouteGuard/ui/EditRouteGuard.tsx | 4 + .../operation/init/ui/OperationFooter.tsx | 28 +- .../components/TransactionAmount.tsx | 2 +- .../AccountSelectModal/AccountSelectModal.tsx | 46 ++++ .../AccountSelectModal/SelectableAccount.tsx | 31 +++ .../dataVerification/dataVerification.ts | 2 +- .../api/xcm/__tests__/xcmService.test.ts | 45 +--- src/renderer/shared/api/xcm/common/types.ts | 2 +- src/renderer/shared/api/xcm/xcmService.ts | 247 ++++++++++-------- src/renderer/shared/lib/utils/chains.ts | 5 + src/renderer/shared/lib/utils/index.ts | 1 + src/renderer/shared/lib/utils/substrate.ts | 8 + .../shared/ui/Buttons/common/constants.ts | 4 +- .../SendAssetModal/model/send-asset.ts | 207 ++++++++++++++- .../SendAssetModal/ui/SendAssetModal.tsx | 16 +- .../SendAssetModal/ui/common/utils.tsx | 9 +- .../components/ActionSteps/Confirmation.tsx | 22 +- .../components/ActionSteps/InitOperation.tsx | 69 ++++- .../SendAssetModal/ui/components/Details.tsx | 7 + .../ui/components/TransferForm.tsx | 200 ++++++++++++-- src/shared/locale/en.json | 11 +- src/shared/locale/ru.json | 11 +- webpack/webpack.shared.ts | 1 + 39 files changed, 1079 insertions(+), 232 deletions(-) create mode 100644 src/renderer/entities/transaction/ui/XcmFee/XcmFee.test.tsx create mode 100644 src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx create mode 100644 src/renderer/pages/Operations/components/modals/AccountSelectModal/AccountSelectModal.tsx create mode 100644 src/renderer/pages/Operations/components/modals/AccountSelectModal/SelectableAccount.tsx create mode 100644 src/renderer/shared/lib/utils/chains.ts diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ab2541781e..96ab44e9da 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,6 +8,7 @@ on: env: CSC_FOR_PULL_REQUEST: true CI: true + BUILD_SOURCE: 'github' jobs: release-build: @@ -92,7 +93,7 @@ jobs: filename="${original%.*}" new="$filename"_x86_64.AppImage mv "$original" "$new" - + - name: Replace space by "-" run: | for file in *; do diff --git a/package.json b/package.json index a702206a19..6af1a746a6 100644 --- a/package.json +++ b/package.json @@ -16,30 +16,36 @@ "start:main": "cross-env NODE_ENV=staging pnpm r webpack:main:stage start:electron", "start:main:dev": "cross-env NODE_ENV=development pnpm r webpack:main:dev start:electron", "watch": "nodemon --watch src/main --watch src/renderer/bridge --watch src/shared --ignore src/main/resources --ext \"*\" --exec", + "webpack:renderer:dev": "webpack serve --config webpack/webpack.renderer.dev.ts", "webpack:renderer:stage": "webpack serve --config webpack/webpack.renderer.stage.ts", "webpack:renderer:prod": "webpack --config webpack/webpack.renderer.prod.ts", "webpack:main:dev": "webpack --config webpack/webpack.main.dev.ts", "webpack:main:stage": "webpack --config webpack/webpack.main.stage.ts", "webpack:main:prod": "webpack --config webpack/webpack.main.prod.ts", + "build": "cross-env NODE_ENV=production CHAINS_FILE=chains pnpm r clean:prod webpack:renderer:prod webpack:main:prod", "postbuild": "node scripts/postbuild.js", "dist": "electron-builder -p never", "clean:build": "rimraf release/build", "clean:prod": "rimraf release/dist", + "test": "jest --config=jest.config.ts --json --outputFile=jest-unit-results.json", "test:integration": "jest --config=tests/integrations/jest.config.ts --group=integration --json --outputFile=jest-results.json", "test:dataVerification": "jest --config=tests/integrations/jest.config.ts --group=dataVerification --json --outputFile=jest-results.json", "test:matrix": "jest --config=tests/integrations/jest.config.ts --group=matrix --json --outputFile=jest-results.json", "test:coverage": "jest --config=jest.config.ts --coverage --passWithNoTests | tee ./coverage.txt", "test:coverage-new-files": "jest --config=jest.config.ts --coverage --changedSince=dev | tee ./coverage.txt", + "lint": "eslint . --ext=js,ts,tsx,json", "lint:fix": "pnpm lint --fix", "lint:i18n-locale": "cross-env I18N=true eslint ./src/shared/locale/ --ext=json --plugin i18n-json", "lint:i18n-fix": "cross-env I18N=true pnpm lint:i18n-locale --fix", "lint:i18n-tsx": "cross-env I18N=true eslint . --ext=tsx --ignore-pattern=**/*.test.* --ignore-pattern=**/*.stories.* --plugin i18next", + "storybook": "start-storybook -p 6006", "build:storybook": "build-storybook", + "update:chains-file": "node scripts/buildChainsJson.js", "githook:pre-commit": "cross-env NODE_ENV=production lint-staged && tsc -p tsconfig.json", "githook:pre-push": "cross-env pnpm test:coverage", diff --git a/src/main/index.ts b/src/main/index.ts index f0dbb668ec..1b855714cf 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,12 +1,14 @@ import { app, dialog } from 'electron'; import { autoUpdater } from 'electron-updater'; +import * as process from 'process'; import { MainWindow } from './main'; import { makeAppWithSingleInstanceLock } from './factories/instance'; import { makeAppSetup } from './factories/setup'; -makeAppWithSingleInstanceLock(async () => { - app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required'); +const setupAutoUpdate = () => { + if (process.env.BUILD_SOURCE !== 'github') return; + autoUpdater.autoRunAppAfterInstall = true; app.on('ready', () => { @@ -59,6 +61,13 @@ makeAppWithSingleInstanceLock(async () => { console.error('[app-updater] Error on update', err); dialog.showErrorBox('Error', 'Error updating the application'); }); +}; + +makeAppWithSingleInstanceLock(async () => { + app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required'); + + setupAutoUpdate(); + await app.whenReady(); await makeAppSetup(MainWindow); }); diff --git a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx index a5f54c010f..d9d437d36d 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx @@ -1,8 +1,10 @@ -import { render, screen } from '@testing-library/react'; +import { render, screen, act } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; +import { fork } from 'effector'; import noop from 'lodash/noop'; import { TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; +import { contactModel } from '@renderer/entities/contact'; import { CreateMultisigAccount } from './CreateMultisigAccount'; jest.mock('@renderer/app/providers', () => ({ @@ -30,12 +32,6 @@ jest.mock('@renderer/entities/account', () => ({ }), })); -jest.mock('@renderer/entities/contact', () => ({ - useContact: jest.fn().mockReturnValue({ - getLiveContacts: jest.fn().mockReturnValue([]), - }), -})); - jest.mock('@renderer/entities/wallet', () => ({ useWallet: jest.fn().mockReturnValue({ getWallets: jest.fn().mockResolvedValue([]), @@ -62,10 +58,15 @@ jest.mock('./components', () => ({ ConfirmSignatories: () => confirmSignatories, })); -describe('pages/CreateMultisigAccount', () => { - test('should render component', () => { - render(, { wrapper: MemoryRouter }); +describe('screen/CreateMultisigAccount', () => { + test('should render component', async () => { + fork({ + values: new Map().set(contactModel.$contacts, []), + }); + await act(async () => { + render(, { wrapper: MemoryRouter }); + }); const text = screen.getByText('createMultisigAccount.title'); const form = screen.getByText('walletForm'); const select = screen.getByText('selectSignatories'); diff --git a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx index f346a2e13f..aa0012be85 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.tsx @@ -1,5 +1,6 @@ import { ComponentProps, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; +import { useStore } from 'effector-react'; import { BaseModal, HeaderTitleText, StatusLabel, Button } from '@renderer/shared/ui'; import { useI18n, useMatrix, Paths } from '@renderer/app/providers'; @@ -14,10 +15,10 @@ import { useToggle } from '@renderer/shared/lib/hooks'; import { OperationResult } from '@renderer/entities/transaction'; import { MatrixModal } from '../MatrixModal/MatrixModal'; import { Wallet, useWallet } from '@renderer/entities/wallet'; -import { useContact } from '@renderer/entities/contact'; import { ExtendedContact, ExtendedWallet } from './common/types'; import { SelectSignatories, ConfirmSignatories, WalletForm } from './components'; import { AccountId } from '@renderer/domain/shared-kernel'; +import { contactModel } from '@renderer/entities/contact'; type OperationResultProps = Pick, 'variant' | 'description'>; @@ -35,7 +36,6 @@ export const CreateMultisigAccount = ({ isOpen, onClose }: Props) => { const { t } = useI18n(); const { matrix, isLoggedIn } = useMatrix(); const { getWallets } = useWallet(); - const { getLiveContacts } = useContact(); const { getAccounts, addAccount, setActiveAccount } = useAccount(); const navigate = useNavigate(); @@ -52,7 +52,7 @@ export const CreateMultisigAccount = ({ isOpen, onClose }: Props) => { const [wallets, setWallets] = useState([]); const [accounts, setAccounts] = useState([]); - const contacts = getLiveContacts(); + const contacts = useStore(contactModel.$contacts); const signatories = signatoryWallets.concat(signatoryContacts); useEffect(() => { diff --git a/src/renderer/entities/transaction/lib/common/constants.ts b/src/renderer/entities/transaction/lib/common/constants.ts index f30fa360c3..5046f58bfb 100644 --- a/src/renderer/entities/transaction/lib/common/constants.ts +++ b/src/renderer/entities/transaction/lib/common/constants.ts @@ -10,6 +10,7 @@ export const OLD_MULTISIG_ARGS_AMOUNT = 6; export const BOND_WITH_CONTROLLER_ARGS_AMOUNT = 3; export const CONTROLLER_ARG_NAME = 'controller'; +export const DEST_WEIGHT_ARG_NAME = 'destWeight'; export const TRANSFER_SECTIONS = ['balances', 'assets', 'currencies', 'tokens']; @@ -26,3 +27,5 @@ export const XcmTypes = [ TransactionType.POLKADOT_XCM_LIMITED_TRANSFER, TransactionType.XTOKENS_TRANSFER_MULTIASSET, ]; + +export const DEFAULT_FEE_ASSET_ITEM = 0; diff --git a/src/renderer/entities/transaction/lib/common/types.ts b/src/renderer/entities/transaction/lib/common/types.ts index efa1dff978..2dd5cc3856 100644 --- a/src/renderer/entities/transaction/lib/common/types.ts +++ b/src/renderer/entities/transaction/lib/common/types.ts @@ -72,6 +72,6 @@ export interface XcmPalletTransferArgs extends Args { export interface XTokenPalletTransferArgs extends Args { asset: AnyJson; dest: AnyJson; - destWeight?: AnyJson; destWeightLimit?: AnyJson; + destWeight?: number; } diff --git a/src/renderer/entities/transaction/lib/common/utils.ts b/src/renderer/entities/transaction/lib/common/utils.ts index 60c49be656..3660d13652 100644 --- a/src/renderer/entities/transaction/lib/common/utils.ts +++ b/src/renderer/entities/transaction/lib/common/utils.ts @@ -2,7 +2,7 @@ import { ApiPromise } from '@polkadot/api'; import { SpRuntimeDispatchError } from '@polkadot/types/lookup'; import { Transaction, DecodedTransaction } from '@renderer/entities/transaction/model/transaction'; -import { MAX_WEIGHT, OLD_MULTISIG_ARGS_AMOUNT, CONTROLLER_ARG_NAME, XcmTypes } from './constants'; +import { MAX_WEIGHT, OLD_MULTISIG_ARGS_AMOUNT, CONTROLLER_ARG_NAME, DEST_WEIGHT_ARG_NAME, XcmTypes } from './constants'; export const decodeDispatchError = (error: SpRuntimeDispatchError, api: ApiPromise): string => { let errorInfo = error.toString(); @@ -31,6 +31,10 @@ export const getMaxWeight = (api: ApiPromise, transaction: Transaction) => { return (isOldMultisigPallet(api) && maxWeight.refTime) || maxWeight; }; +export const hasDestWeight = (api: ApiPromise): boolean => { + return !!api.tx.xTokens.transferMultiasset.meta.args.find((n) => n.name.toString() === DEST_WEIGHT_ARG_NAME); +}; + export const isXcmTransaction = (transaction?: Transaction | DecodedTransaction): boolean => { if (!transaction?.type) return false; diff --git a/src/renderer/entities/transaction/lib/common/xcmMethods.ts b/src/renderer/entities/transaction/lib/common/xcmMethods.ts index ed12586ad9..ebb65aea19 100644 --- a/src/renderer/entities/transaction/lib/common/xcmMethods.ts +++ b/src/renderer/entities/transaction/lib/common/xcmMethods.ts @@ -40,7 +40,7 @@ export function limitedTeleportAssets( ); } -export function transferMultiAssets( +export function transferMultiAsset( args: XTokenPalletTransferArgs, info: BaseTxInfo, options: OptionsWithMeta, @@ -49,7 +49,7 @@ export function transferMultiAssets( { method: { args, - name: 'transferMultiassets', + name: 'transferMultiasset', pallet: 'xTokens', }, ...info, diff --git a/src/renderer/entities/transaction/lib/transactionService.ts b/src/renderer/entities/transaction/lib/transactionService.ts index d8608e95ad..2b2e3eb5b0 100644 --- a/src/renderer/entities/transaction/lib/transactionService.ts +++ b/src/renderer/entities/transaction/lib/transactionService.ts @@ -18,8 +18,16 @@ import { AccountId, HexString, Threshold } from '@renderer/domain/shared-kernel' import { Transaction, TransactionType } from '@renderer/entities/transaction/model/transaction'; import { createTxMetadata, toAccountId } from '@renderer/shared/lib/utils'; import { ITransactionService, HashData, ExtrinsicResultParams } from './common/types'; -import { decodeDispatchError, getMaxWeight, isControllerMissing, isOldMultisigPallet } from './common/utils'; +import { + decodeDispatchError, + getMaxWeight, + hasDestWeight, + isControllerMissing, + isOldMultisigPallet, +} from './common/utils'; import { useCallDataDecoder } from './callDataDecoder'; +import * as xcmMethods from './common/xcmMethods'; +import { DEFAULT_FEE_ASSET_ITEM } from './common/constants'; type BalancesTransferArgs = Parameters[0]; type BondWithoutContollerArgs = Omit[0], 'controller'>; @@ -167,21 +175,75 @@ export const useTransaction = (): ITransactionService => { options, ); }, - // TODO: finish during XCM transfer [TransactionType.XCM_LIMITED_TRANSFER]: (transaction, info, options, api) => { - return {} as UnsignedTransaction; + return xcmMethods.limitedReserveTransferAssets( + 'xcmPallet', + { + dest: transaction.args.xcmDest, + beneficiary: transaction.args.xcmBeneficiary, + assets: transaction.args.xcmAsset, + feeAssetItem: DEFAULT_FEE_ASSET_ITEM, + weightLimit: { Unlimited: true }, + }, + info, + options, + ); }, [TransactionType.XCM_TELEPORT]: (transaction, info, options, api) => { - return {} as UnsignedTransaction; + return xcmMethods.limitedTeleportAssets( + 'xcmPallet', + { + dest: transaction.args.xcmDest, + beneficiary: transaction.args.xcmBeneficiary, + assets: transaction.args.xcmAsset, + feeAssetItem: DEFAULT_FEE_ASSET_ITEM, + weightLimit: { Unlimited: true }, + }, + info, + options, + ); }, [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: (transaction, info, options, api) => { - return {} as UnsignedTransaction; + return xcmMethods.limitedReserveTransferAssets( + 'polkadotXcm', + { + dest: transaction.args.xcmDest, + beneficiary: transaction.args.xcmBeneficiary, + assets: transaction.args.xcmAsset, + feeAssetItem: DEFAULT_FEE_ASSET_ITEM, + weightLimit: { Unlimited: true }, + }, + info, + options, + ); }, [TransactionType.POLKADOT_XCM_TELEPORT]: (transaction, info, options, api) => { - return {} as UnsignedTransaction; + return xcmMethods.limitedTeleportAssets( + 'polkadotXcm', + { + dest: transaction.args.xcmDest, + beneficiary: transaction.args.xcmBeneficiary, + assets: transaction.args.xcmAsset, + feeAssetItem: DEFAULT_FEE_ASSET_ITEM, + weightLimit: { Unlimited: true }, + }, + info, + options, + ); }, [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: (transaction, info, options, api) => { - return {} as UnsignedTransaction; + const singleXcmAsset = { V2: transaction.args.xcmAsset.V2[0] }; + + return xcmMethods.transferMultiAsset( + { + dest: transaction.args.xcmDest, + asset: singleXcmAsset, + destWeightLimit: { Unlimited: true }, + destWeight: transaction.args.xcmWeight, + }, + info, + options, + ); }, [TransactionType.BOND]: (transaction, info, options, api) => { return isControllerMissing(api) @@ -298,21 +360,36 @@ export const useTransaction = (): ITransactionService => { ) => api.tx.multisig.approveAsMulti(threshold, otherSignatories, maybeTimepoint, callHash, maxWeight), [TransactionType.MULTISIG_CANCEL_AS_MULTI]: ({ threshold, otherSignatories, maybeTimepoint, callHash }, api) => api.tx.multisig.cancelAsMulti(threshold, otherSignatories, maybeTimepoint, callHash), - // TODO: finish during XCM transfer - [TransactionType.XCM_LIMITED_TRANSFER]: () => { - return {} as SubmittableExtrinsic<'promise'>; + [TransactionType.XCM_LIMITED_TRANSFER]: ({ xcmDest, xcmBeneficiary, xcmAsset }, api) => { + return api.tx.xcmPallet.limitedReserveTransferAssets(xcmDest, xcmBeneficiary, xcmAsset, DEFAULT_FEE_ASSET_ITEM, { + Unlimited: true, + }); }, - [TransactionType.XCM_TELEPORT]: () => { - return {} as SubmittableExtrinsic<'promise'>; + [TransactionType.XCM_TELEPORT]: ({ xcmDest, xcmBeneficiary, xcmAsset }, api) => { + return api.tx.xcmPallet.limitedTeleportAssets(xcmDest, xcmBeneficiary, xcmAsset, DEFAULT_FEE_ASSET_ITEM, { + Unlimited: true, + }); }, - [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: () => { - return {} as SubmittableExtrinsic<'promise'>; + [TransactionType.POLKADOT_XCM_LIMITED_TRANSFER]: ({ xcmDest, xcmBeneficiary, xcmAsset }, api) => { + return api.tx.polkadotXcm.limitedReserveTransferAssets( + xcmDest, + xcmBeneficiary, + xcmAsset, + DEFAULT_FEE_ASSET_ITEM, + { Unlimited: true }, + ); }, - [TransactionType.POLKADOT_XCM_TELEPORT]: () => { - return {} as SubmittableExtrinsic<'promise'>; + [TransactionType.POLKADOT_XCM_TELEPORT]: ({ xcmDest, xcmBeneficiary, xcmAsset }, api) => { + return api.tx.polkadotXcm.limitedTeleportAssets(xcmDest, xcmBeneficiary, xcmAsset, DEFAULT_FEE_ASSET_ITEM, { + Unlimited: true, + }); }, - [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: () => { - return {} as SubmittableExtrinsic<'promise'>; + [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: ({ xcmDest, xcmAsset, xcmWeight }, api) => { + const version = Object.keys(xcmAsset)[0]; + const singleXcmAsset = { [version]: xcmAsset[version][0] }; + const weight = hasDestWeight(api) ? xcmWeight : { Unlimited: true }; + + return api.tx.xTokens.transferMultiasset(xcmDest, singleXcmAsset, weight); }, // controller arg removed from bond but changes not released yet // https://github.com/paritytech/substrate/pull/14039 diff --git a/src/renderer/entities/transaction/lib/validateBalance.ts b/src/renderer/entities/transaction/lib/validateBalance.ts index 98f590ec11..68d73d7b16 100644 --- a/src/renderer/entities/transaction/lib/validateBalance.ts +++ b/src/renderer/entities/transaction/lib/validateBalance.ts @@ -53,16 +53,18 @@ const validateBalanceForAmount = async ({ transaction, ...props }: Props): Promi }; const validateBalanceForFee = async ({ transaction, getTransactionFee, api, ...props }: Props): Promise => { - const amount = transaction.args.value; + const amountBN = new BN(transaction.args.value); const nativeTokenBalance = await getNativeTokenBalance({ transaction, api, getTransactionFee, ...props }); const tokenBalance = await getTokenBalance({ transaction, api, getTransactionFee, ...props }); const transferableBalance = transferableAmount(tokenBalance); const transferableNativeTokenBalance = transferableAmount(nativeTokenBalance); const fee = await getTransactionFee(transaction, api); + const feeBN = new BN(fee); + const xcmFeeBN = new BN(transaction.args.xcmFee || 0); return nativeTokenBalance - ? new BN(transferableNativeTokenBalance).gte(new BN(fee)) - : new BN(transferableBalance).gte(new BN(fee).add(new BN(amount))); + ? new BN(transferableNativeTokenBalance).gte(feeBN) + : new BN(transferableBalance).gte(feeBN.add(amountBN).add(xcmFeeBN)); }; export const getOperationErrors = ( diff --git a/src/renderer/entities/transaction/ui/XcmFee/XcmFee.test.tsx b/src/renderer/entities/transaction/ui/XcmFee/XcmFee.test.tsx new file mode 100644 index 0000000000..8d0c7ecd3d --- /dev/null +++ b/src/renderer/entities/transaction/ui/XcmFee/XcmFee.test.tsx @@ -0,0 +1,46 @@ +import { act, render, screen } from '@testing-library/react'; + +import { Asset } from '@renderer/entities/asset'; +import { Transaction } from '@renderer/entities/transaction'; +import { XcmFee } from './XcmFee'; +import { ChainXCM, XcmConfig } from '@renderer/shared/api/xcm'; + +jest.mock('@renderer/components/common'); + +jest.mock('@renderer/app/providers', () => ({ + useI18n: jest.fn().mockReturnValue({ + t: (key: string) => key, + }), +})); + +jest.mock('@renderer/entities/transaction', () => ({ + useTransaction: jest.fn().mockReturnValue({ + getTransactionFee: jest.fn().mockResolvedValue('12'), + }), +})); + +jest.mock('@renderer/entities/asset', () => ({ + ...jest.requireActual('@renderer/entities/asset'), + AssetBalance: ({ value }: any) =>
        {value}
        , +})); + +describe('components/common/XcmFee', () => { + test('should render component', async () => { + const asset = { symbol: 'DOT', precision: 10 } as Asset; + const tx = { + address: '0x123', + chainId: '0x00', + type: 'xcm_limited_teleport_assets', + args: { + destinationChain: '0x00', + }, + } as Transaction; + + await act(async () => { + render(); + }); + + const value = screen.getByText('0'); + expect(value).toBeInTheDocument(); + }); +}); diff --git a/src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx b/src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx new file mode 100644 index 0000000000..74c513a2a9 --- /dev/null +++ b/src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx @@ -0,0 +1,72 @@ +import { BN } from '@polkadot/util'; +import { useEffect, useState, memo } from 'react'; + +import { Asset, AssetBalance } from '@renderer/entities/asset'; +import { Transaction } from '@renderer/entities/transaction'; +import { Shimmering } from '@renderer/shared/ui'; +import { XcmConfig, estimateFee } from '@renderer/shared/api/xcm'; +import { toLocalChainId } from '@renderer/shared/lib/utils'; + +type Props = { + multiply?: number; + asset: Asset; + config: XcmConfig; + transaction?: Transaction; + className?: string; + onFeeChange?: (fee: string) => void; + onFeeLoading?: (loading: boolean) => void; +}; + +export const XcmFee = memo( + ({ multiply = 1, config, asset, transaction, className, onFeeChange, onFeeLoading }: Props) => { + const [fee, setFee] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const updateFee = (fee: string) => { + setFee(fee); + onFeeChange?.(fee); + }; + + useEffect(() => { + onFeeLoading?.(isLoading); + }, [isLoading]); + + useEffect(() => { + setIsLoading(true); + + if (!transaction?.address) { + updateFee('0'); + setIsLoading(false); + } else { + const originChainId = toLocalChainId(transaction.chainId); + const destinationChainId = toLocalChainId(transaction.args.destinationChain); + const configChain = config.chains.find((c) => c.chainId === originChainId); + const configAsset = configChain?.assets.find((a) => a.assetId === asset.assetId); + const configXcmTransfer = configAsset?.xcmTransfers.find((t) => t.destination.chainId === destinationChainId); + + if (originChainId && configXcmTransfer && configAsset) { + const fee = estimateFee( + config, + config.assetsLocation[configAsset.assetLocation], + originChainId, + configXcmTransfer, + ); + + updateFee(fee.toString()); + } else { + updateFee('0'); + } + + setIsLoading(false); + } + }, [transaction]); + + if (isLoading) { + return ; + } + + const totalFee = new BN(fee).muln(multiply).toString(); + + return ; + }, +); diff --git a/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts b/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts index d669b43d40..dd25fce5ba 100644 --- a/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts +++ b/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts @@ -8,20 +8,21 @@ import { ChainId } from '@renderer/domain/shared-kernel'; const { getChainById } = useChains(); +const validateUrlParams = createEvent(); +const storeCleared = createEvent(); + +export const $chain = createStore(null).reset(storeCleared); +export const $asset = createStore(null).reset(storeCleared); + type Navigation = { redirectPath: string; navigate: NavigateFunction; }; -const $navigation = createStore(null); +const $navigation = createStore(null).reset(storeCleared); const navigationApi = createApi($navigation, { navigateApiChanged: (state, { navigate, redirectPath }) => ({ ...state, navigate, redirectPath }), }); -export const $chain = createStore(null); -export const $asset = createStore(null); - -const validateUrlParams = createEvent(); - type ValidateParams = { chainId: string | null; assetId: string | null; @@ -68,4 +69,5 @@ sample({ export const events = { navigateApiChanged: navigationApi.navigateApiChanged, validateUrlParams, + storeCleared, }; diff --git a/src/renderer/features/assets/AssetRouteGuard/ui/AssetRouteGuard.tsx b/src/renderer/features/assets/AssetRouteGuard/ui/AssetRouteGuard.tsx index e06ba705d5..aa49c63bf4 100644 --- a/src/renderer/features/assets/AssetRouteGuard/ui/AssetRouteGuard.tsx +++ b/src/renderer/features/assets/AssetRouteGuard/ui/AssetRouteGuard.tsx @@ -20,6 +20,10 @@ export const AssetRouteGuard = ({ redirectPath, children }: Props) => { useEffect(() => { assetGuardModel.events.navigateApiChanged({ navigate, redirectPath }); assetGuardModel.events.validateUrlParams(searchParams); + + return () => { + assetGuardModel.events.storeCleared(); + }; }, [searchParams]); if (!chain || !asset) return null; diff --git a/src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts b/src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts index e336b3bd34..82e7c177f6 100644 --- a/src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts +++ b/src/renderer/features/contacts/EditRouteGuard/model/edit-guard.ts @@ -4,19 +4,20 @@ import { NavigateFunction } from 'react-router-dom'; import { Contact, contactModel } from '@renderer/entities/contact'; import { ContactDS } from '@renderer/shared/api/storage'; +const validateUrlParams = createEvent(); +const storeCleared = createEvent(); + +export const $contact = createStore(null).reset(storeCleared); + type Navigation = { redirectPath: string; navigate: NavigateFunction; }; -const $navigation = createStore(null); +const $navigation = createStore(null).reset(storeCleared); const navigationApi = createApi($navigation, { navigateApiChanged: (state, { navigate, redirectPath }) => ({ ...state, navigate, redirectPath }), }); -export const $contact = createStore(null); - -const validateUrlParams = createEvent(); - type ValidateParams = { contactId: string | null; contacts: ContactDS[]; @@ -52,4 +53,5 @@ sample({ export const events = { navigateApiChanged: navigationApi.navigateApiChanged, validateUrlParams, + storeCleared, }; diff --git a/src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx b/src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx index 08d0a75325..3115bc6c86 100644 --- a/src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx +++ b/src/renderer/features/contacts/EditRouteGuard/ui/EditRouteGuard.tsx @@ -18,6 +18,10 @@ export const EditRouteGuard = ({ redirectPath, children }: Props) => { useEffect(() => { editGuardModel.events.navigateApiChanged({ navigate, redirectPath }); editGuardModel.events.validateUrlParams(searchParams); + + return () => { + editGuardModel.events.storeCleared(); + }; }, [searchParams]); if (!contact) return null; diff --git a/src/renderer/features/operation/init/ui/OperationFooter.tsx b/src/renderer/features/operation/init/ui/OperationFooter.tsx index 5bf149fa71..aa43a16466 100644 --- a/src/renderer/features/operation/init/ui/OperationFooter.tsx +++ b/src/renderer/features/operation/init/ui/OperationFooter.tsx @@ -3,8 +3,10 @@ import { ApiPromise } from '@polkadot/api'; import { Icon, FootnoteText, Tooltip } from '@renderer/shared/ui'; import { useI18n } from '@renderer/app/providers'; import { Asset } from '@renderer/entities/asset'; -import { Transaction, Deposit, Fee } from '@renderer/entities/transaction'; +import { Transaction, Deposit, Fee, XcmTypes } from '@renderer/entities/transaction'; import { MultisigAccount, Account, isMultisig } from '@renderer/entities/account'; +import { XcmConfig } from '@renderer/shared/api/xcm'; +import { XcmFee } from '@renderer/entities/transaction/ui/XcmFee/XcmFee'; type Props = { api: ApiPromise; @@ -12,6 +14,9 @@ type Props = { transaction: Transaction; account: Account | MultisigAccount; totalAccounts: number; + xcmConfig?: XcmConfig; + xcmAsset?: Asset; + onXcmFeeChange?: (value: string) => void; onDepositChange: (value: string) => void; onFeeChange: (value: string) => void; onFeeLoading: (value: boolean) => void; @@ -23,12 +28,18 @@ export const OperationFooter = ({ transaction, account, totalAccounts, + xcmConfig, + xcmAsset, + onXcmFeeChange, onDepositChange, onFeeChange, onFeeLoading, }: Props) => { const { t } = useI18n(); + // TODO: Check why transaction can be empty + const isXcmTransfer = XcmTypes.includes(transaction?.type); + return (
        {isMultisig(account) && ( @@ -74,6 +85,21 @@ export const OperationFooter = ({
        )} + + {isXcmTransfer && xcmConfig && xcmAsset && ( +
        + {t('operation.xcmFee')} + + + +
        + )}
        ); }; diff --git a/src/renderer/pages/Operations/components/TransactionAmount.tsx b/src/renderer/pages/Operations/components/TransactionAmount.tsx index 4761c6438d..cd03837da9 100644 --- a/src/renderer/pages/Operations/components/TransactionAmount.tsx +++ b/src/renderer/pages/Operations/components/TransactionAmount.tsx @@ -22,7 +22,7 @@ export const TransactionAmount = ({ tx, ...balanceProps }: Props & BalanceProps) setAssets(chain?.assets || []); }, []); - const asset = getAssetById(tx.args.assetId, assets); + const asset = getAssetById(tx.args.asset, assets); const value = getTransactionAmount(tx); if (!asset || !value) return null; diff --git a/src/renderer/pages/Operations/components/modals/AccountSelectModal/AccountSelectModal.tsx b/src/renderer/pages/Operations/components/modals/AccountSelectModal/AccountSelectModal.tsx new file mode 100644 index 0000000000..adb37ca6a2 --- /dev/null +++ b/src/renderer/pages/Operations/components/modals/AccountSelectModal/AccountSelectModal.tsx @@ -0,0 +1,46 @@ +import { BaseModal } from '@renderer/shared/ui'; +import { useI18n } from '@renderer/app/providers'; +import { AccountDS } from '@renderer/shared/api/storage'; +import { Chain } from '@renderer/entities/chain'; +import { SelectableAccount } from './SelectableAccount'; + +type Props = { + isOpen: boolean; + onClose: () => void; + onSelect: (account: AccountDS) => void; + accounts: AccountDS[]; + chain: Chain; +}; + +const AccountSelectModal = ({ isOpen, onClose, onSelect, accounts, chain }: Props) => { + const { t } = useI18n(); + + return ( + +
          + {accounts.map((a) => ( +
        • + +
        • + ))} +
        +
        + ); +}; + +export default AccountSelectModal; diff --git a/src/renderer/pages/Operations/components/modals/AccountSelectModal/SelectableAccount.tsx b/src/renderer/pages/Operations/components/modals/AccountSelectModal/SelectableAccount.tsx new file mode 100644 index 0000000000..755007464d --- /dev/null +++ b/src/renderer/pages/Operations/components/modals/AccountSelectModal/SelectableAccount.tsx @@ -0,0 +1,31 @@ +import { AccountAddress, AccountAddressProps } from '@renderer/entities/account'; +import { Icon } from '@renderer/shared/ui'; +import { Explorer } from '@renderer/entities/chain'; +import { ChainId } from '@renderer/domain/shared-kernel'; + +type Props = { + explorers?: Explorer[]; + value: T; + onSelected: (value: T) => void; + chainId: ChainId; +} & AccountAddressProps; + +export const SelectableAccount = ({ + explorers, + size = 20, + value, + onSelected, + chainId, + name, + ...addressProps +}: Props) => { + return ( + + ); +}; diff --git a/src/renderer/services/dataVerification/dataVerification.ts b/src/renderer/services/dataVerification/dataVerification.ts index e96bae23a2..f0ae54867b 100644 --- a/src/renderer/services/dataVerification/dataVerification.ts +++ b/src/renderer/services/dataVerification/dataVerification.ts @@ -11,7 +11,7 @@ async function getHeader(api: ApiPromise, chainId: number): Promise { return api.query.paras.heads(chainId); } -async function getParachainId(api: ApiPromise): Promise { +export async function getParachainId(api: ApiPromise): Promise { const parachainId = (await api.query.parachainInfo.parachainId()) as unknown as u32; return parachainId.toNumber(); diff --git a/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts b/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts index eafd0c5187..91a1098070 100644 --- a/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts +++ b/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts @@ -1,5 +1,3 @@ -import { ApiPromise } from '@polkadot/api'; - import { XCM_KEY } from '../common/constants'; import { estimateFee, @@ -16,11 +14,6 @@ import { XCMPALLET_TRANSFER_HUB_ASTAR, } from './mock/xcmData'; -const mockApi = () => - ({ - createType: (_, typeParams) => typeParams, - } as ApiPromise); - describe('shared/api/xcm/xcmService', () => { beforeEach(() => { localStorage.clear(); @@ -40,8 +33,7 @@ describe('shared/api/xcm/xcmService', () => { test('should calculate correct fee for ACA from Acala to Parallel ', () => { const fee = estimateFee( - CONFIG.instructions, - CONFIG.networkBaseWeight, + CONFIG, CONFIG.assetsLocation['ACA'], 'fc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c', CONFIG.chains[0].assets[0].xcmTransfers[1], @@ -52,8 +44,7 @@ describe('shared/api/xcm/xcmService', () => { test('should calculate correct fee for DOT from Acala to Parallel', () => { const fee = estimateFee( - CONFIG.instructions, - CONFIG.networkBaseWeight, + CONFIG, CONFIG.assetsLocation['DOT'], 'fc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c', CONFIG.chains[0].assets[1].xcmTransfers[0], @@ -63,39 +54,31 @@ describe('shared/api/xcm/xcmService', () => { }); test('should calculate correct location for sibling prachain', () => { - const api = mockApi(); - - const location = getDestinationLocation(api, { parentId: '0x00' }, 2000) as any; + const location = getDestinationLocation({ parentId: '0x00' }, 2000) as any; - expect(location.V2.parents).toEqual(1); - expect(location.V2.interior.X1.Parachain).toEqual(2000); + expect(location.parents).toEqual(1); + expect(location.interior.X1.Parachain).toEqual(2000); }); test('should calculate correct location for parent parachain', () => { - const api = mockApi(); + const location = getDestinationLocation({ parentId: '0x00' }) as any; - const location = getDestinationLocation(api, { parentId: '0x00' }) as any; - - expect(location.V2.parents).toEqual(1); - expect(location.V2.interior).toEqual('Here'); + expect(location.parents).toEqual(1); + expect(location.interior).toEqual('Here'); }); test('should calculate correct address location for parent parachain', () => { - const api = mockApi(); - - const location = getDestinationLocation(api, { parentId: '0x00' }, undefined, '0x00') as any; + const location = getDestinationLocation({ parentId: '0x00' }, undefined, '0x00') as any; - expect(location.V2.parents).toEqual(1); - expect(location.V2.interior.X1.AccountId32.id).toEqual('0x00'); + expect(location.parents).toEqual(1); + expect(location.interior.X1.AccountId32.id).toEqual('0x00'); }); test('should calculate correct location for child parachain', () => { - const api = mockApi(); - - const location = getDestinationLocation(api, { parentId: undefined }, 2000) as any; + const location = getDestinationLocation({ parentId: undefined }, 2000) as any; - expect(location.V2.parents).toEqual(0); - expect(location.V2.interior.X1.Parachain).toEqual(2000); + expect(location.parents).toEqual(0); + expect(location.interior.X1.Parachain).toEqual(2000); }); test('should parse xcmPallet relaychain > parachain', () => { diff --git a/src/renderer/shared/api/xcm/common/types.ts b/src/renderer/shared/api/xcm/common/types.ts index 87cd024730..3a88dc192f 100644 --- a/src/renderer/shared/api/xcm/common/types.ts +++ b/src/renderer/shared/api/xcm/common/types.ts @@ -67,7 +67,7 @@ export type XcmTransfer = { }; }; -export type XcmTransferType = 'xtokens' | 'xcmpallet'; +export type XcmTransferType = 'xtokens' | 'xcmpallet' | 'xcmpallet-teleport'; export type PathType = 'absolute' | 'relative' | 'concrete'; export const enum Action { diff --git a/src/renderer/shared/api/xcm/xcmService.ts b/src/renderer/shared/api/xcm/xcmService.ts index 8f12c55b41..969c497b12 100644 --- a/src/renderer/shared/api/xcm/xcmService.ts +++ b/src/renderer/shared/api/xcm/xcmService.ts @@ -1,9 +1,14 @@ import { BN, BN_TEN, BN_ZERO } from '@polkadot/util'; import { ApiPromise } from '@polkadot/api'; -import { VersionedMultiAsset, VersionedMultiLocation } from '@polkadot/types/interfaces'; import get from 'lodash/get'; import { XCM_URL, XCM_KEY } from './common/constants'; +import { AccountId, ChainId, HexString } from '@renderer/domain/shared-kernel'; +import { Chain } from '@renderer/entities/chain'; +import { getTypeVersion, toLocalChainId, getAssetId } from '@renderer/shared/lib/utils'; +import { XcmPalletTransferArgs, XTokenPalletTransferArgs } from '@renderer/entities/transaction'; +import { useChains } from '@renderer/entities/network'; +import { toRawString } from './common/utils'; import { XcmConfig, AssetLocation, @@ -13,17 +18,9 @@ import { ChainXCM, InstructionType, Instructions, - NetworkBaseWeight, XcmTransfer, PathType, } from './common/types'; -import { AccountId, ChainId, HexString } from '@renderer/domain/shared-kernel'; -// TODO: Move chain to shared -import { Chain } from '@renderer/entities/chain'; -import { XcmPalletTransferArgs, XTokenPalletTransferArgs } from '@renderer/entities/transaction'; -import { useChains } from '@renderer/entities/network'; -import { getAssetId } from '@renderer/shared/lib/utils'; -import { toRawString } from './common/utils'; export const fetchXcmConfig = async (): Promise => { const response = await fetch(XCM_URL, { cache: 'default' }); @@ -48,7 +45,7 @@ export const saveXcmConfig = (config: XcmConfig) => { }; export const getAvailableDirections = (chains: ChainXCM[], assetId: number, chainId: ChainId): XcmTransfer[] => { - const chain = chains.find((c) => c.chainId === chainId); + const chain = chains.find((c) => c.chainId === toLocalChainId(chainId)); const asset = chain?.assets.find((a) => a.assetId === assetId); return asset?.xcmTransfers || []; @@ -73,35 +70,59 @@ export const getEstimatedWeight = ( }; export const estimateFee = ( - instructions: Instructions, - baseWeights: NetworkBaseWeight, + config: XcmConfig, assetLocation: AssetLocation, originChain: string, xcmTransfer: XcmTransfer, ): BN => { const weight = getEstimatedWeight( - instructions, + config.instructions, xcmTransfer.destination.fee.instructions, - new BN(xcmTransfer.destination.fee.mode.value), + new BN(config.networkBaseWeight[xcmTransfer.destination.chainId]), ); - const fee = weightToFee(weight, new BN(baseWeights[xcmTransfer.destination.chainId])); + const fee = weightToFee(weight, new BN(xcmTransfer.destination.fee.mode.value)); const isReserveChain = [originChain, xcmTransfer.destination.chainId].includes(assetLocation.chainId); if (isReserveChain) return fee; const reserveWeight = getEstimatedWeight( - instructions, + config.instructions, assetLocation.reserveFee.instructions, - new BN(assetLocation.reserveFee.mode.value), + new BN(config.networkBaseWeight[assetLocation.chainId]), ); - const reserveFee = weightToFee(reserveWeight, new BN(baseWeights[assetLocation.chainId])); + const reserveFee = weightToFee(reserveWeight, new BN(assetLocation.reserveFee.mode.value)); return fee.add(reserveFee); }; +export const estimateRequiredDestWeight = ( + config: XcmConfig, + assetLocation: AssetLocation, + originChain: string, + xcmTransfer: XcmTransfer, +): BN => { + const weight = getEstimatedWeight( + config.instructions, + xcmTransfer.destination.fee.instructions, + new BN(config.networkBaseWeight[xcmTransfer.destination.chainId]), + ); + + const isReserveChain = [originChain, xcmTransfer.destination.chainId].includes(assetLocation.chainId); + + if (isReserveChain) return weight; + + const reserveWeight = getEstimatedWeight( + config.instructions, + assetLocation.reserveFee.instructions, + new BN(config.networkBaseWeight[assetLocation.chainId]), + ); + + return weight.gte(reserveWeight) ? weight : reserveWeight; +}; + const JunctionType: Record = { parachainId: 'Parachain', generalKey: 'GeneralKey', @@ -110,8 +131,22 @@ const JunctionType: Record = { accountId: 'AccountId32', generalIndex: 'GeneralIndex', }; + type JunctionTypeKey = keyof typeof JunctionType; +const JunctionHierarchyLevel: Record = { + parachainId: 0, + palletInstance: 1, + accountKey: 1, + accountId: 1, + generalKey: 2, + generalIndex: 2, +}; + +export const sortJunctions = (a: JunctionTypeKey, b: JunctionTypeKey): number => { + return JunctionHierarchyLevel[a] - JunctionHierarchyLevel[b]; +}; + export const createJunctionFromObject = (data: {}) => { const entries = Object.entries(data); @@ -126,9 +161,11 @@ export const createJunctionFromObject = (data: {}) => { } return { - [`X${entries.length}`]: entries.map((e) => ({ - [JunctionType[e[0] as JunctionTypeKey]]: e[1], - })), + [`X${entries.length}`]: entries + .sort((a, b) => sortJunctions(a[0], b[0])) + .map((e) => ({ + [JunctionType[e[0] as JunctionTypeKey]]: e[1], + })), }; }; @@ -137,108 +174,110 @@ export const getAssetLocation = ( asset: AssetXCM, assets: Record, amount: BN, -): VersionedMultiAsset | undefined => { - const PathMap: Record VersionedMultiAsset | undefined> = { - relative: () => getRelativeAssetLocation(api, amount, assets[asset.assetLocation].multiLocation), - absolute: () => getAbsoluteAssetLocation(api, amount, assets[asset.assetLocation].multiLocation), - concrete: () => getConcreteAssetLocation(api, amount, asset.assetLocationPath.path), +): Object | undefined => { + const PathMap: Record Object | undefined> = { + relative: () => getRelativeAssetLocation(assets[asset.assetLocation].multiLocation), + absolute: () => getAbsoluteAssetLocation(assets[asset.assetLocation].multiLocation), + concrete: () => getConcreteAssetLocation(asset.assetLocationPath.path), }; - return PathMap[asset.assetLocationPath.type](); + const location = PathMap[asset.assetLocationPath.type](); + const assetVersionType = getTypeVersion(api, 'VersionedMultiAssets'); + + return { + [assetVersionType]: [ + { + id: { + Concrete: location, + }, + fun: { + Fungible: amount, + }, + }, + ], + }; }; -const getRelativeAssetLocation = ( - api: ApiPromise, - amount: BN, - assetLocation?: LocalMultiLocation, -): VersionedMultiAsset | undefined => { +const getRelativeAssetLocation = (assetLocation?: LocalMultiLocation): Object | undefined => { if (!assetLocation) return; const { parachainId: _, ...location } = assetLocation; - return api.createType('VersionedMultiAsset', { - V2: { - id: { - Concrete: { - parents: 0, - interior: Object.values(location).length ? createJunctionFromObject(location) : 'Here', - }, - }, - fun: { - Fungible: amount.toNumber(), - }, - }, - }); + return { + parents: 0, + interior: createJunctionFromObject(location), + }; }; -const getAbsoluteAssetLocation = ( - api: ApiPromise, - amount: BN, - assetLocation?: LocalMultiLocation, -): VersionedMultiAsset | undefined => { +const getAbsoluteAssetLocation = (assetLocation?: LocalMultiLocation): Object | undefined => { if (!assetLocation) return; - return api.createType('VersionedMultiAsset', { - V2: { - id: { - Concrete: { - parents: 1, - interior: Object.values(assetLocation).length ? createJunctionFromObject(assetLocation) : 'Here', - }, - }, - fungibility: { - Fungible: amount.toNumber(), - }, - }, - }); + return { + parents: 1, + interior: createJunctionFromObject(assetLocation), + }; }; -const getConcreteAssetLocation = ( - api: ApiPromise, - amount: BN, - assetLocation?: LocalMultiLocation, -): VersionedMultiAsset | undefined => { +const getConcreteAssetLocation = (assetLocation?: LocalMultiLocation): Object | undefined => { if (!assetLocation) return; const { parents, ...location } = assetLocation; - return api.createType('VersionedMultiAsset', { - V2: { - id: { - Concrete: { - parents, - interior: Object.values(location).length ? createJunctionFromObject(location) : 'Here', - }, - }, - fun: { - Fungible: amount.toNumber(), - }, - }, - }); + return { + parents, + interior: createJunctionFromObject(location), + }; }; -export const getDestinationLocation = ( +export const getVersionedDestinationLocation = ( api: ApiPromise, originChain: Pick, destinationParaId?: number, accountId?: AccountId, -): VersionedMultiLocation | undefined => { +): Object | undefined => { + const location = getDestinationLocation(originChain, destinationParaId, accountId); + const version = getTypeVersion(api, 'VersionedMultiLocation'); + + return { + [version]: location, + }; +}; + +export const getDestinationLocation = ( + originChain: Pick, + destinationParaId?: number, + accountId?: AccountId, +): Object | undefined => { if (originChain.parentId && destinationParaId) { - return getSiblingLocation(api, destinationParaId, accountId); + return getSiblingLocation(destinationParaId, accountId); } if (originChain.parentId) { - return getParentLocation(api, accountId); + return getParentLocation(accountId); } if (destinationParaId) { - return getChildLocation(api, destinationParaId, accountId); + return getChildLocation(destinationParaId, accountId); } return undefined; }; -const getChildLocation = (api: ApiPromise, parachainId: number, accountId?: AccountId): VersionedMultiLocation => { +export const getAccountLocation = (accountId?: AccountId): Object | undefined => { + return { + parents: 0, + interior: { + X1: { + accountId32: { + network: 'Any', + id: accountId, + }, + }, + }, + }; +}; + +const getChildLocation = (parachainId: number, accountId?: AccountId): Object => { const location: Record = { parachainId }; if (accountId) { @@ -248,15 +287,13 @@ const getChildLocation = (api: ApiPromise, parachainId: number, accountId?: Acco }; } - return api.createType('VersionedMultiLocation', { - V2: { - parents: 0, - interior: createJunctionFromObject(location), - }, - }); + return { + parents: 0, + interior: createJunctionFromObject(location), + }; }; -const getParentLocation = (api: ApiPromise, accountId?: AccountId): VersionedMultiLocation => { +const getParentLocation = (accountId?: AccountId): Object => { const location: Record = {}; if (accountId) { @@ -266,15 +303,13 @@ const getParentLocation = (api: ApiPromise, accountId?: AccountId): VersionedMul }; } - return api.createType('VersionedMultiLocation', { - V2: { - parents: 1, - interior: createJunctionFromObject(location), - }, - }); + return { + parents: 1, + interior: createJunctionFromObject(location), + }; }; -const getSiblingLocation = (api: ApiPromise, parachainId: number, accountId?: AccountId): VersionedMultiLocation => { +const getSiblingLocation = (parachainId: number, accountId?: AccountId): Object => { const location: Record = { parachainId }; if (accountId) { @@ -284,12 +319,10 @@ const getSiblingLocation = (api: ApiPromise, parachainId: number, accountId?: Ac }; } - return api.createType('VersionedMultiLocation', { - V2: { - parents: 1, - interior: createJunctionFromObject(location), - }, - }); + return { + parents: 1, + interior: createJunctionFromObject(location), + }; }; type ParsedPayload = { diff --git a/src/renderer/shared/lib/utils/chains.ts b/src/renderer/shared/lib/utils/chains.ts new file mode 100644 index 0000000000..8d7bc3a35d --- /dev/null +++ b/src/renderer/shared/lib/utils/chains.ts @@ -0,0 +1,5 @@ +import { ChainId } from '@renderer/domain/shared-kernel'; + +export const toLocalChainId = (chainId?: ChainId): string | undefined => { + return chainId?.replace('0x', ''); +}; diff --git a/src/renderer/shared/lib/utils/index.ts b/src/renderer/shared/lib/utils/index.ts index b56801ed76..4aad14fd1b 100644 --- a/src/renderer/shared/lib/utils/index.ts +++ b/src/renderer/shared/lib/utils/index.ts @@ -11,3 +11,4 @@ export * from './time'; export * from './twMerge'; export * from './validation'; export * from './arrays'; +export * from './chains'; diff --git a/src/renderer/shared/lib/utils/substrate.ts b/src/renderer/shared/lib/utils/substrate.ts index fa3a549153..a320d32692 100644 --- a/src/renderer/shared/lib/utils/substrate.ts +++ b/src/renderer/shared/lib/utils/substrate.ts @@ -107,3 +107,11 @@ export const getCreatedDateFromApi = async (neededBlock: number, api: ApiPromise return getCreatedDate(neededBlock, currentBlock, blockTime.toNumber()); }; + +export const getTypeVersion = (api: ApiPromise, typeName: string): string => { + try { + return Object.keys(JSON.parse(api.registry.getDefinition(typeName) as string)?._enum).pop() || ''; + } catch (e) { + return ''; + } +}; diff --git a/src/renderer/shared/ui/Buttons/common/constants.ts b/src/renderer/shared/ui/Buttons/common/constants.ts index e5c840c157..8e17cdede9 100644 --- a/src/renderer/shared/ui/Buttons/common/constants.ts +++ b/src/renderer/shared/ui/Buttons/common/constants.ts @@ -15,8 +15,8 @@ export const ViewClass: Record<`${Variant}_${Pallet}`, string> = { }; export const SizeClass = { - sm: 'h-6.5 rounded-[34px] text-button-small', - md: 'h-10.5 rounded-[34px] text-button-large', + sm: 'h-6.5 box-border rounded-[34px] text-button-small', + md: 'h-10.5 box-border rounded-[34px] text-button-large', }; export const Padding = { diff --git a/src/renderer/widgets/SendAssetModal/model/send-asset.ts b/src/renderer/widgets/SendAssetModal/model/send-asset.ts index e9b7cc2d1f..ceebbd6d8a 100644 --- a/src/renderer/widgets/SendAssetModal/model/send-asset.ts +++ b/src/renderer/widgets/SendAssetModal/model/send-asset.ts @@ -1,12 +1,56 @@ import { createStore, createEffect, createEvent, sample, forward, attach } from 'effector'; +import { ApiPromise } from '@polkadot/api'; +import { BN } from '@polkadot/util'; +import { createGate } from 'effector-react'; -import { XcmConfig } from '@renderer/shared/api/xcm'; +import { + XcmConfig, + XcmTransfer, + getAvailableDirections, + AssetXCM, + getAssetLocation, + getAccountLocation, + estimateRequiredDestWeight, + getVersionedDestinationLocation, +} from '@renderer/shared/api/xcm'; import { xcmModel } from '@renderer/entities/xcm'; +import { Chain } from '@renderer/entities/chain'; +import { Asset } from '@renderer/entities/asset'; +import { getParachainId } from '@renderer/services/dataVerification/dataVerification'; +import { ExtendedChain } from '@renderer/entities/network'; +import { AccountId } from '@renderer/domain/shared-kernel'; +import { toLocalChainId } from '@renderer/shared/lib/utils'; -export const $finalConfig = createStore(null); const xcmConfigRequested = createEvent(); +const destinationChainSelected = createEvent(); +const accountIdSelected = createEvent(); +const amountChanged = createEvent(); +const xcmFeeChanged = createEvent(); +const storeCleared = createEvent(); + +export const $destinationChain = createStore(null).reset(storeCleared); +export const $finalConfig = createStore(null); +export const $xcmTransfer = createStore(null).reset(storeCleared); +export const $xcmAsset = createStore(null).reset(storeCleared); +export const $destinations = createStore([]).reset(storeCleared); +export const $destinationParaId = createStore(null).reset(storeCleared); +export const $accountId = createStore(null).reset(storeCleared); + +export const $txDest = createStore(null).reset(storeCleared); +export const $txBeneficiary = createStore(null).reset(storeCleared); +export const $txAsset = createStore(null).reset(storeCleared); +export const $xcmFee = createStore('').reset(storeCleared); +export const $amount = createStore('').reset(storeCleared); +export const $xcmWeight = createStore('').reset(storeCleared); + +export const $xcmProps = createStore<{ + chain?: Chain; + asset?: Asset; + api?: ApiPromise; +}>({}).reset(storeCleared); + +export const PropsGate = createGate<{ chain: Chain; asset: Asset; api?: ApiPromise }>('props'); -// TODO: continue config calculation in xcm service task const calculateFinalConfigFx = createEffect((config: XcmConfig): XcmConfig => { return config; }); @@ -15,17 +59,169 @@ const getConfigFx = attach({ effect: xcmModel.effects.getConfigFx }); const saveConfigFx = attach({ effect: xcmModel.effects.saveConfigFx }); const fetchConfigFx = attach({ effect: xcmModel.effects.fetchConfigFx }); +const getParaIdFx = createEffect(async (api: ApiPromise): Promise => { + try { + return getParachainId(api); + } catch (e) { + return null; + } +}); + +forward({ from: PropsGate.state, to: $xcmProps }); + forward({ from: xcmConfigRequested, to: [getConfigFx, fetchConfigFx], }); +sample({ + source: { props: $xcmProps, config: $finalConfig }, + fn: ({ config, props: { chain, asset } }) => { + if (!config || !asset || !chain) return []; + + return getAvailableDirections(config.chains, asset.assetId, chain.chainId); + }, + target: $destinations, +}); + +sample({ + clock: destinationChainSelected, + fn: (chain) => chain, + target: $destinationChain, +}); + +sample({ + clock: destinationChainSelected, + filter: (chain: ExtendedChain): chain is ExtendedChain & { api: ApiPromise } => Boolean(chain.api), + fn: ({ api }) => api, + target: getParaIdFx, +}); + +forward({ + from: getParaIdFx.doneData, + to: $destinationParaId, +}); + +sample({ + source: { + props: $xcmProps, + config: $finalConfig, + }, + fn: ({ config, props: { chain, asset } }) => { + if (!config || !chain || !asset) return null; + + const originChainId = toLocalChainId(chain.chainId); + const configChain = config.chains.find((c) => c.chainId === originChainId); + const configAsset = configChain?.assets.find((a) => a.assetId === asset.assetId); + + return configAsset || null; + }, + target: $xcmAsset, +}); + +sample({ + source: { + destinationChain: $destinationChain, + xcmAsset: $xcmAsset, + }, + fn: ({ xcmAsset, destinationChain }) => { + if (!xcmAsset || !destinationChain) return null; + + const destinationChainId = toLocalChainId(destinationChain.chainId); + const configXcmTransfer = xcmAsset?.xcmTransfers.find((t) => t.destination.chainId === destinationChainId); + + return configXcmTransfer || null; + }, + target: $xcmTransfer, +}); + +sample({ + source: { + xcmTransfer: $xcmTransfer, + props: $xcmProps, + accountId: $accountId, + paraId: $destinationParaId, + }, + fn: ({ xcmTransfer, props: { api, chain }, paraId, accountId }) => { + if (!xcmTransfer || !chain || !api) return null; + + return ( + (xcmTransfer.type === 'xtokens' && accountId + ? getVersionedDestinationLocation(api, chain, paraId || undefined, accountId) + : getVersionedDestinationLocation(api, chain, paraId || undefined)) || null + ); + }, + target: $txDest, +}); + +forward({ + from: accountIdSelected, + to: $accountId, +}); + +sample({ + source: $accountId, + + fn: (accountId) => { + if (!accountId || accountId === '0x00') return null; + + return getAccountLocation(accountId) || null; + }, + target: $txBeneficiary, +}); + +forward({ + from: xcmFeeChanged, + to: $xcmFee, +}); + +sample({ + source: { + config: $finalConfig, + xcmTransfer: $xcmTransfer, + asset: $xcmAsset, + props: $xcmProps, + }, + fn: ({ config, xcmTransfer, props: { chain }, asset }) => { + if (!config || !xcmTransfer || !chain || !asset) return ''; + + return estimateRequiredDestWeight( + config, + config.assetsLocation[asset.assetLocation], + toLocalChainId(chain.chainId)!, + xcmTransfer, + ).toString(); + }, + target: $xcmWeight, +}); + +sample({ + source: { + config: $finalConfig, + props: $xcmProps, + amount: $amount, + xcmAsset: $xcmAsset, + xcmFee: $xcmFee, + }, + fn: ({ props: { api }, xcmAsset, config, amount, xcmFee }) => { + if (!api || !xcmAsset || !amount || !config || !xcmFee) return null; + + return getAssetLocation(api, xcmAsset, config.assetsLocation, new BN(amount).add(new BN(xcmFee))) || null; + }, + target: $txAsset, +}); + sample({ clock: getConfigFx.doneData, filter: (config): config is XcmConfig => Boolean(config), target: calculateFinalConfigFx, }); +forward({ + from: amountChanged, + to: $amount, +}); + forward({ from: fetchConfigFx.doneData, to: [saveConfigFx, calculateFinalConfigFx], @@ -38,4 +234,9 @@ forward({ export const events = { xcmConfigRequested, + destinationChainSelected, + accountIdSelected, + amountChanged, + xcmFeeChanged, + storeCleared, }; diff --git a/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx b/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx index 759946baf9..0123118a27 100644 --- a/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx @@ -1,5 +1,6 @@ import { useState, useEffect } from 'react'; import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; +import { useStore, useGate } from 'effector-react'; import { useI18n, useNetworkContext } from '@renderer/app/providers'; import { HexString } from '@renderer/domain/shared-kernel'; @@ -33,6 +34,9 @@ export const SendAssetModal = ({ chain, asset, onClose }: Props) => { const { getBalance } = useBalance(); const { getTransactionFee } = useTransaction(); const { connections } = useNetworkContext(); + const config = useStore(sendAssetModel.$finalConfig); + const xcmAsset = useStore(sendAssetModel.$xcmAsset); + const destinationChain = useStore(sendAssetModel.$destinationChain); const [isModalOpen, toggleIsModalOpen] = useToggle(true); const [activeStep, setActiveStep] = useState(Step.INIT); @@ -49,8 +53,14 @@ export const SendAssetModal = ({ chain, asset, onClose }: Props) => { const { api, assets, addressPrefix, explorers } = connection; + useGate(sendAssetModel.PropsGate, { chain, asset, api }); + useEffect(() => { sendAssetModel.events.xcmConfigRequested(); + + return () => { + sendAssetModel.events.storeCleared(); + }; }, []); const onInitResult = (transferTx: Transaction, multisig?: { multisigTx: Transaction; description: string }) => { @@ -107,11 +117,13 @@ export const SendAssetModal = ({ chain, asset, onClose }: Props) => { ); } + const operationTitle = destinationChain?.chainId !== chain.chainId ? 'transfer.xcmTitle' : 'transfer.title'; + return ( } + title={} contentClass={activeStep === Step.SIGNING ? '' : undefined} panelClass="w-[440px]" headerClass="py-3 px-5 max-w-[440px]" @@ -141,6 +153,8 @@ export const SendAssetModal = ({ chain, asset, onClose }: Props) => { )} {activeStep === Step.CONFIRMATION && ( ( return { id: account.accountId + account.name, value: account, element }; }; + +export const getChainOption = ({ chainId }: Chain): DropdownOption => { + const element = ; + + return { id: chainId, value: chainId, element }; +}; diff --git a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx index 3fceb6572f..f2c4b99776 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; -import { Transaction, DepositWithLabel, Fee } from '@renderer/entities/transaction'; +import { Transaction, DepositWithLabel, Fee, XcmTypes } from '@renderer/entities/transaction'; import { TransactionAmount } from '@renderer/pages/Operations/components/TransactionAmount'; import { Button, DetailRow, FootnoteText, Icon } from '@renderer/shared/ui'; import { Account, MultisigAccount } from '@renderer/entities/account'; @@ -8,6 +8,8 @@ import { ExtendedChain } from '@renderer/entities/network'; import { useI18n } from '@renderer/app/providers'; import Details from '../Details'; import { Wallet, useWallet } from '@renderer/entities/wallet'; +import { XcmFee } from '@renderer/entities/transaction/ui/XcmFee/XcmFee'; +import { AssetXCM, XcmConfig } from '@renderer/shared/api/xcm'; const AmountFontStyle = 'font-manrope text-text-primary text-[32px] leading-[36px] font-bold'; @@ -18,6 +20,8 @@ type Props = { description?: string; connection: ExtendedChain; feeTx?: Transaction; + config?: XcmConfig; + xcmAsset?: AssetXCM; onResult?: () => void; onBack?: () => void; }; @@ -29,6 +33,8 @@ export const Confirmation = ({ signatory, description, feeTx, + config, + xcmAsset, onResult, onBack, }: Props) => { @@ -43,8 +49,16 @@ export const Confirmation = ({ account.walletId && getWallet(account.walletId).then((wallet) => setWallet(wallet)); }, [account]); + const isXcmTransfer = XcmTypes.includes(transaction?.type); + return (
        + {isXcmTransfer && ( +
        + +
        + )} + {transaction && } {description && ( @@ -76,6 +90,12 @@ export const Confirmation = ({ )} + {isXcmTransfer && config && xcmAsset && ( + + {config && } + + )} + {signatory && connection.api && ( { const { getActiveAccounts } = useAccount(); const { getLiveAssetBalances } = useBalance(); + const { connections } = useNetworkContext(); + const availableDestinations = useStore(sendAssetModel.$destinations); + const config = useStore(sendAssetModel.$finalConfig); + const xcmAsset = useStore(sendAssetModel.$txAsset); + const xcmDest = useStore(sendAssetModel.$txDest); + const xcmBeneficiary = useStore(sendAssetModel.$txBeneficiary); + const xcmTransfer = useStore(sendAssetModel.$xcmTransfer); + const xcmFee = useStore(sendAssetModel.$xcmFee); + const xcmWeight = useStore(sendAssetModel.$xcmWeight); const accounts = getActiveAccounts(); @@ -44,9 +56,9 @@ export const InitOperation = ({ const [amount, setAmount] = useState('0'); const [deposit, setDeposit] = useState('0'); const [tx, setTx] = useState(); + const [destinations, setDestinations] = useState([]); const [activeAccount, setActiveAccount] = useState(); - const [activeSignatory, setActiveSignatory] = useState(); const accountIds = accounts.map((account) => account.accountId); @@ -61,6 +73,24 @@ export const InitOperation = ({ nativeToken?.assetId.toString() || asset?.assetId.toString() || '', ); + useEffect(() => { + if (!availableDestinations?.length) return; + + const options = [...availableDestinations].reduce((acc, destination) => { + // eslint-disable-next-line i18next/no-literal-string + const chainId = `0x${destination.destination.chainId}` as ChainId; + const connection = connections[chainId]; + + if (connection && connection.connection.connectionType !== 'DISABLED') { + acc.push(connection); + } + + return acc; + }, []); + + setDestinations([connections[chainId], ...options]); + }, [availableDestinations.length]); + useEffect(() => { setActiveAccount(accounts[0]); onAccountChange(accounts[0]); @@ -95,20 +125,44 @@ export const InitOperation = ({ setActiveSignatory(account); }; + const changeDestinationChain = (chainId: ChainId) => { + sendAssetModel.events.destinationChainSelected(connections[chainId]); + }; + + const changeDestination = (accountId: AccountId) => { + sendAssetModel.events.accountIdSelected(accountId); + }; + + const changeAmount = (amount: string) => { + setAmount(amount); + sendAssetModel.events.amountChanged(amount); + }; + return (
        ); diff --git a/src/renderer/widgets/SendAssetModal/ui/components/Details.tsx b/src/renderer/widgets/SendAssetModal/ui/components/Details.tsx index 03352084dc..a8f87c36c1 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/Details.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/Details.tsx @@ -1,5 +1,6 @@ import { useI18n } from '@renderer/app/providers'; import { Account, MultisigAccount, AddressWithExplorers } from '@renderer/entities/account'; +import { ChainTitle } from '@renderer/entities/chain'; import { ExtendedChain } from '@renderer/entities/network'; import { Transaction } from '@renderer/entities/transaction'; import { Wallet } from '@renderer/entities/wallet'; @@ -62,6 +63,12 @@ const Details = ({ transaction, wallet, account, signatory, connection, withAdva )} + {transaction?.args.destinationChain && ( + + + + )} + {transaction?.args.dest && ( ; }; type Props = { api: ApiPromise; chainId: ChainId; + chain: Chain; network: string; account?: Account | MultisigAccount; + accounts?: Account[]; signer?: Account; asset: Asset; nativeToken: Asset; addressPrefix: number; fee: string; feeIsLoading: boolean; + xcmParams: { + dest?: Object; + beneficiary?: Object; + asset?: Object; + transfer?: XcmTransfer; + fee: string; + weight: string; + }; deposit: string; footer: ReactNode; header?: ReactNode; + destinations: Chain[]; + onSubmit: (transferTx: Transaction, multisig?: { multisigTx: Transaction; description: string }) => void; onChangeAmount: (amount: string) => void; + onDestinationChainChange: (destinationChain: ChainId) => void; + onDestinationChange: (accountId: AccountId) => void; onTxChange: (tx: Transaction) => void; }; @@ -52,6 +73,8 @@ export const TransferForm = ({ api, chainId, account, + accounts, + chain, signer, asset, addressPrefix, @@ -60,9 +83,13 @@ export const TransferForm = ({ onSubmit, onChangeAmount, onTxChange, + onDestinationChainChange, + onDestinationChange, feeIsLoading, fee, deposit, + destinations, + xcmParams, }: Props) => { const { t } = useI18n(); const { getBalance } = useBalance(); @@ -77,20 +104,41 @@ export const TransferForm = ({ const [transferTx, setTransferTx] = useState(); const [multisigTx, setMultisigTx] = useState(); const [multisigTxExist, setMultisigTxExist] = useState(false); + const [destinationOptions, setDestinationOptions] = useState[]>([]); + const [isSelectAccountModalOpen, setSelectAccountModalOpen] = useState(false); const { handleSubmit, control, watch, trigger, + setValue, formState: { isValid, isDirty }, } = useForm({ mode: 'onChange', defaultValues: { amount: '', destination: '', signatory: '', description: '' }, }); + useEffect(() => { + const destinationOptions = destinations.map(getChainOption); + + if (destinationOptions.length) { + setDestinationOptions(destinationOptions); + } + + setValue('destinationChain', destinationOptions[0]); + }, [destinations.length]); + const amount = watch('amount'); const destination = watch('destination'); + const destinationChain = watch('destinationChain'); + const destinationChainAccounts = accounts?.filter((a) => !a.rootId || a.chainId === destinationChain?.value) || []; + + useEffect(() => { + if (destinationChain) { + onDestinationChainChange(destinationChain.value); + } + }, [destinationChain]); useEffect(() => { isDirty && trigger('amount'); @@ -100,6 +148,10 @@ export const TransferForm = ({ onChangeAmount(formatAmount(amount, asset.precision)); }, [amount]); + useEffect(() => { + onDestinationChange(toAccountId(destination)); + }, [destination]); + const setupBalances = ( address: Address, callbackNativeToken: (balance: string) => void, @@ -142,18 +194,35 @@ export const TransferForm = ({ } }, [fee]); + const isXcmTransfer = destinationChain?.value !== chainId && !!xcmParams.transfer; + const isXcmValid = xcmParams.fee && xcmParams.asset && xcmParams.beneficiary && xcmParams.dest; + useEffect(() => { if (!account || !amount || !validateAddress(destination)) return; const transferPayload = getTransferTx(account.accountId); - if (isMultisig(account) && signer) { - setMultisigTx(getMultisigTx(account, signer.accountId, transferPayload)); + if (isMultisig(account) && signer && (!isXcmTransfer || isXcmValid)) { + const multisigTx = getMultisigTx(account, signer.accountId, transferPayload); + + setMultisigTx(multisigTx); } setTransferTx(transferPayload); onTxChange(transferPayload); - }, [account, signer, destination, amount]); + }, [account, signer, destination, amount, destinationChain, isXcmValid, isXcmTransfer]); + + const getXcmTransferType = (type: XcmTransferType): TransactionType => { + if (type === 'xtokens') { + return TransactionType.XTOKENS_TRANSFER_MULTIASSET; + } + + if (type === 'xcmpallet-teleport') { + return api.tx.xcmPallet ? TransactionType.XCM_TELEPORT : TransactionType.POLKADOT_XCM_TELEPORT; + } + + return api.tx.xcmPallet ? TransactionType.XCM_LIMITED_TRANSFER : TransactionType.POLKADOT_XCM_LIMITED_TRANSFER; + }; const getTransferTx = (accountId: AccountId): Transaction => { const TransferType: Record = { @@ -161,17 +230,36 @@ export const TransferForm = ({ [AssetType.STATEMINE]: TransactionType.ASSET_TRANSFER, }; - const transactionType = asset.type ? TransferType[asset.type] : TransactionType.TRANSFER; + const isNativeTransfer = !asset.type; + + let transactionType; + let args: any = { + dest: toAddress(destination, { prefix: addressPrefix }), + value: formatAmount(amount, asset.precision), + ...(!isNativeTransfer && { asset: getAssetId(asset) }), + }; + + if (isXcmTransfer && xcmParams.transfer) { + transactionType = getXcmTransferType(xcmParams.transfer.type); + + args = { + ...args, + destinationChain: destinationChain?.value, + xcmFee: xcmParams.fee, + xcmAsset: xcmParams.asset, + xcmDest: xcmParams.dest, + xcmBeneficiary: xcmParams.beneficiary, + xcmWeight: xcmParams.weight, + }; + } else { + transactionType = isNativeTransfer ? TransactionType.TRANSFER : TransferType[asset.type!]; + } return { chainId, address: toAddress(accountId, { prefix: addressPrefix }), type: transactionType, - args: { - dest: toAddress(destination, { prefix: addressPrefix }), - value: formatAmount(amount, asset.precision), - ...(transactionType !== TransactionType.TRANSFER && { asset: getAssetId(asset) }), - }, + args, }; }; @@ -206,13 +294,17 @@ export const TransferForm = ({ const validateBalance = (amount: string): boolean => { if (!accountBalance) return false; + const amountBN = new BN(formatAmount(amount, asset.precision)); + const xcmFeeBN = new BN(xcmParams.fee || 0); - return new BN(formatAmount(amount, asset.precision)).lte(new BN(accountBalance)); + return amountBN.add(xcmFeeBN).lte(new BN(accountBalance)); }; const validateBalanceForFee = (amount: string): boolean => { const balance = isMultisig(account) ? signerBalance : accountBalance; const nativeTokenBalance = isMultisig(account) ? signerNativeTokenBalance : accountNativeTokenBalance; + const amountBN = new BN(formatAmount(amount, asset.precision)); + const xcmFeeBN = new BN(xcmParams.fee || 0); if (!balance) return false; @@ -221,21 +313,25 @@ export const TransferForm = ({ } if (isMultisig(account)) { - return new BN(fee).lte(new BN(balance)); + return new BN(fee).add(amountBN).add(xcmFeeBN).lte(new BN(balance)); } - return new BN(fee).add(new BN(formatAmount(amount, asset.precision))).lte(new BN(balance)); + return new BN(fee).add(amountBN).add(xcmFeeBN).lte(new BN(balance)); }; const validateBalanceForFeeAndDeposit = (): boolean => { if (!isMultisig(account)) return true; if (!signerBalance) return false; + const amountBN = new BN(formatAmount(amount, asset.precision)); + const xcmFeeBN = new BN(xcmParams.fee || 0); + const feeBN = new BN(fee); + if (signerNativeTokenBalance) { - return new BN(deposit).add(new BN(fee)).lte(new BN(signerNativeTokenBalance)); + return new BN(deposit).add(feeBN).lte(new BN(signerNativeTokenBalance)); } - return new BN(deposit).add(new BN(fee)).lte(new BN(signerBalance)); + return new BN(deposit).add(feeBN).add(amountBN).add(xcmFeeBN).lte(new BN(signerBalance)); }; const submitTransaction: SubmitHandler = async ({ description }) => { @@ -265,10 +361,48 @@ export const TransferForm = ({ } }; + const handleMyselfClick = () => { + if (destinationChainAccounts.length > 1) { + setSelectAccountModalOpen(true); + } else if (account) { + handleAccountSelect(account); + } + }; + + const handleAccountSelect = (account: Account) => { + setSelectAccountModalOpen(false); + + if (!account) return; + + const prefix = + destinations.find((c) => c.chainId === destinationChain?.value)?.addressPrefix || SS58_DEFAULT_PREFIX; + const address = toAddress(account.accountId, { prefix }); + + setValue('destination', address, { shouldValidate: true }); + }; + return (
        {header} + + {Boolean(destinations?.length) && ( + ( + - ) : ( - +
        + {value && !error ? ( + + ) : ( + + )} +
        + } + suffixElement={ + isXcmTransfer && + !destination && ( + ) } - className="w-full" + wrapperClass="w-full h-10.5" invalid={Boolean(error)} value={value} label={t('transfer.recipientLabel')} @@ -377,9 +521,23 @@ export const TransferForm = ({ {t('transfer.multisigTransactionExist')} - + + {accounts && ( + setSelectAccountModalOpen(false)} + onSelect={handleAccountSelect} + /> + )} ); }; diff --git a/src/shared/locale/en.json b/src/shared/locale/en.json index c047d0df1f..1c660cab78 100644 --- a/src/shared/locale/en.json +++ b/src/shared/locale/en.json @@ -303,6 +303,7 @@ "deposit": "Multisig deposit", "depositor": "Depositor", "description": "Description", + "destinationChain": "To network", "fromNetwork": "From network", "multisigWallet": "Multisig wallet", "payee": "Rewards destination", @@ -332,6 +333,7 @@ "placeholder": "Reason...", "title": "Enter reject reason" }, + "selectAccount": "Select account", "selectSignatory": "Select signatory", "selectSignatoryTitle": "Select signatory", "signButton": "Sign with Polkadot Vault", @@ -345,7 +347,8 @@ "signing": "On signing" }, "successMessage": "Operation Approved", - "successRejectMessage": "Operation Rejected" + "successRejectMessage": "Operation Rejected", + "xcmFee": "Cross-chain fee" }, "operations": { "callData": { @@ -769,6 +772,8 @@ "amountPlaceholder": "0", "continueButton": "Continue", "descriptionLengthError": "Description must be { maxLength } symbols long", + "destinationChainLabel": "Recipient network", + "destinationChainPlaceholder": "Select recipient network", "editOperationButton": "Edit operation", "executing": "Submitting operation", "formTitle": "Send { asset } on { network }", @@ -780,6 +785,7 @@ "transactions": "Transactions" }, "multisigTransactionExist": "Multisig transaction already exists", + "myselfButton": "Myself", "networkDeposit": "Multisig deposit", "networkDepositHint": "The deposit stays locked on the depositor’s account until the multisig operation is executed or rejected", "networkFee": "Network fee", @@ -796,7 +802,8 @@ "signatoryLabel": "Signatory", "startSigningButton": "Sign with Polkadot Vault", "successMessage": "Operation Success", - "title": "Transfer { asset } on" + "title": "Transfer { asset } on", + "xcmTitle": "Transfer { asset } from" }, "transferDetails": { "fromNetwork": "From network", diff --git a/src/shared/locale/ru.json b/src/shared/locale/ru.json index 341baf1fd5..0955f1b1d5 100644 --- a/src/shared/locale/ru.json +++ b/src/shared/locale/ru.json @@ -303,6 +303,7 @@ "deposit": "Multisig депозит", "depositor": "Депозитор", "description": "Описание", + "destinationChain": "В сеть", "fromNetwork": "Из сети", "multisigWallet": "Отправитель", "payee": "Payee", @@ -332,6 +333,7 @@ "placeholder": "Причина...", "title": "Укажите причину отмены" }, + "selectAccount": "Select account", "selectSignatory": "Выберите подписанта", "selectSignatoryTitle": "Выберите подписанта", "signButton": "Подписать в Polkadot Vault", @@ -345,7 +347,8 @@ "signing": "Подписание" }, "successMessage": "Operation Approved", - "successRejectMessage": "Операция Отменена" + "successRejectMessage": "Операция Отменена", + "xcmFee": "Cross-chain fee" }, "operations": { "callData": { @@ -769,6 +772,8 @@ "amountPlaceholder": "0", "continueButton": "Продолжить", "descriptionLengthError": "Описание может быть { maxLength } символов в длину", + "destinationChainLabel": "Recipient network", + "destinationChainPlaceholder": "Select recipient network", "editOperationButton": "Редактировать операцию", "executing": "Выполнение транзакции", "formTitle": "Отправить { asset } в сети { network }", @@ -780,6 +785,7 @@ "transactions": "Транзакции" }, "multisigTransactionExist": "Мультисиг транзакция уже существует", + "myselfButton": "Myself", "networkDeposit": "Депозит", "networkDepositHint": "The deposit stays locked on the depositor’s account until the multisig operation is executed or rejected", "networkFee": "Комиссия сети", @@ -796,7 +802,8 @@ "signatoryLabel": "Подписант", "startSigningButton": "Подписать в Polkadot Vault", "successMessage": "Транзакция отправлена!", - "title": "Перевод { asset } в сети" + "title": "Перевод { asset } в сети", + "xcmTitle": "Transfer { asset } from" }, "transferDetails": { "fromNetwork": "Из сети", diff --git a/webpack/webpack.shared.ts b/webpack/webpack.shared.ts index 55c3583b2a..047c0e91ac 100644 --- a/webpack/webpack.shared.ts +++ b/webpack/webpack.shared.ts @@ -123,6 +123,7 @@ const sharedConfig: Configuration = { new webpack.DefinePlugin({ 'process.env.PRODUCT_NAME': JSON.stringify(APP_CONFIG.TITLE), 'process.env.VERSION': JSON.stringify(APP_CONFIG.VERSION), + 'process.env.BUILD_SOURCE': JSON.stringify(process.env.BUILD_SOURCE), 'process.env.CHAINS_FILE': JSON.stringify(process.env.CHAINS_FILE), }), ], From 2dcfdac900de773a3ca8b703bf216647b31202ed Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Fri, 15 Sep 2023 15:52:10 +0300 Subject: [PATCH 21/34] Fix: Xcm filter (#1061) --- .../GraphqlContext/GraphqlContext.test.tsx | 4 +- .../context/GraphqlContext/GraphqlContext.tsx | 5 +- .../PrimaryLayout/Navigation/Navigation.tsx | 5 +- .../CreateMultisigAccount.test.tsx | 4 +- .../chain/ui/ChainTitle/ChainTitle.tsx | 6 +- .../lib/multisigTx/multisigTxService.ts | 5 +- .../lib/__tests__/chainsService.test.ts | 15 +- .../entities/network/lib/chainsService.ts | 176 +++++++++--------- src/renderer/entities/network/lib/index.ts | 2 +- .../entities/network/lib/networkService.ts | 7 +- .../AssetRouteGuard/model/asset-guard.ts | 6 +- .../OperationsFilter/ui/OperationsFilter.tsx | 46 +++-- .../Assets/AssetsList/AssetsList.test.tsx | 4 +- .../pages/Assets/AssetsList/AssetsList.tsx | 5 +- .../SelectShardModal/SelectShardModal.tsx | 7 +- .../Vault/ManageStep/ManageStep.tsx | 8 +- .../ManageStepSingle/ManageStepSingle.tsx | 8 +- .../pages/Onboarding/WatchOnly/WatchOnly.tsx | 7 +- .../components/TransactionAmount.tsx | 5 +- .../pages/Settings/Networks/Networks.tsx | 7 +- .../pages/Staking/Overview/Overview.test.tsx | 4 +- .../NetworkInfo/NetworkInfo.test.tsx | 4 +- .../components/NetworkInfo/NetworkInfo.tsx | 7 +- src/renderer/shared/api/xcm/xcmService.ts | 10 +- 24 files changed, 172 insertions(+), 185 deletions(-) diff --git a/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.test.tsx b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.test.tsx index 00704474e3..d09d9b5a63 100644 --- a/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.test.tsx +++ b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.test.tsx @@ -5,13 +5,13 @@ import { GraphqlProvider, useGraphql } from './GraphqlContext'; jest.mock('@renderer/shared/lib/hooks'); jest.mock('@renderer/entities/network', () => ({ - useChains: jest.fn().mockReturnValue({ + chainsService: { getStakingChainsData: jest .fn() .mockReturnValue([ { chainId: '0x123', externalApi: { staking: [{ type: 'subquery', url: 'https://localhost:8080' }] } }, ]), - }), + }, })); jest.mock('@renderer/entities/settings', () => ({ diff --git a/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.tsx b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.tsx index 962c788e26..fbb52b415b 100644 --- a/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.tsx +++ b/src/renderer/app/providers/context/GraphqlContext/GraphqlContext.tsx @@ -3,7 +3,7 @@ import { onError } from '@apollo/client/link/error'; import { createContext, PropsWithChildren, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { useSettingsStorage } from '@renderer/entities/settings'; type GraphqlContextProps = { @@ -27,7 +27,6 @@ const errorLink = onError(({ graphQLErrors, networkError }) => { }); export const GraphqlProvider = ({ children }: PropsWithChildren) => { - const { getStakingChainsData } = useChains(); const { getStakingNetwork } = useSettingsStorage(); const chainUrls = useRef>({}); @@ -45,7 +44,7 @@ export const GraphqlProvider = ({ children }: PropsWithChildren) => { }, []); useEffect(() => { - const chainsData = getStakingChainsData(); + const chainsData = chainsService.getStakingChainsData(); chainUrls.current = chainsData.reduce((acc, chain) => { const subqueryMatch = chain.externalApi?.staking.find((api) => api.type === 'subquery'); diff --git a/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx b/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx index e4169395d1..85d4ab66e3 100644 --- a/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx +++ b/src/renderer/components/layout/PrimaryLayout/Navigation/Navigation.tsx @@ -9,7 +9,7 @@ import { MultisigTxInitStatus } from '@renderer/entities/transaction'; import WalletMenu from '@renderer/components/layout/PrimaryLayout/Wallets/WalletMenu'; import ActiveAccountCard from '@renderer/components/layout/PrimaryLayout/Wallets/ActiveAccountCard'; import NavItem, { Props as NavItemProps } from '../NavItem/NavItem'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { ChainsRecord } from '@renderer/components/layout/PrimaryLayout/Wallets/common/types'; import { Paths } from '../../../../app/providers/routes/paths'; import { useWallet } from '@renderer/entities/wallet'; @@ -18,14 +18,13 @@ import { Shimmering } from '@renderer/shared/ui'; const Navigation = () => { const { getActiveAccounts } = useAccount(); const { getLiveAccountMultisigTxs } = useMultisigTx({}); - const { getChainsData } = useChains(); const { getLiveWallets } = useWallet(); const wallets = getLiveWallets(); const [chains, setChains] = useState({}); useEffect(() => { - const chains = getChainsData(); + const chains = chainsService.getChainsData(); setChains(keyBy(chains, 'chainId')); }, []); diff --git a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx index d9d437d36d..c897e4f5eb 100644 --- a/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx +++ b/src/renderer/components/modals/CreateMultisigAccount/CreateMultisigAccount.test.tsx @@ -39,9 +39,9 @@ jest.mock('@renderer/entities/wallet', () => ({ })); jest.mock('@renderer/entities/network', () => ({ - useChains: jest.fn().mockReturnValue({ + chainsService: { getChainsData: jest.fn().mockResolvedValue([]), - }), + }, })); jest.mock('@renderer/components/modals/MatrixModal/MatrixModal', () => ({ diff --git a/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.tsx b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.tsx index ade2eb9701..189966e401 100644 --- a/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.tsx +++ b/src/renderer/entities/chain/ui/ChainTitle/ChainTitle.tsx @@ -2,7 +2,7 @@ import { ElementType, useEffect, useState } from 'react'; import { cnTw } from '@renderer/shared/lib/utils'; import { ChainId } from '@renderer/domain/shared-kernel'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { Chain as ChainType, ChainIcon } from '@renderer/entities/chain'; import TextBase from '@renderer/shared/ui/Typography/common/TextBase'; @@ -25,15 +25,13 @@ export const ChainTitle = ({ iconSize = 16, ...chainProps }: Props) => { - const { getChainById } = useChains(); - const [chainObj, setChainObj] = useState(); useEffect(() => { if ('chain' in chainProps) { setChainObj(chainProps.chain); } else { - setChainObj(getChainById(chainProps.chainId)); + setChainObj(chainsService.getChainById(chainProps.chainId)); } }, []); diff --git a/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts b/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts index 8d4e4c964e..f5e5f17853 100644 --- a/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts +++ b/src/renderer/entities/multisig/lib/multisigTx/multisigTxService.ts @@ -18,7 +18,7 @@ import { createNewEventsPayload, updateOldEventsPayload, } from './common/utils'; -import { useChains } from '../../../network/lib/chainsService'; +import { chainsService } from '../../../network/lib/chainsService'; import { useTransaction } from '../../../transaction/lib/transactionService'; import { CallData, AccountId } from '@renderer/domain/shared-kernel'; import { toAddress, getCurrentBlockNumber, getExpectedBlockTime } from '@renderer/shared/lib/utils'; @@ -37,7 +37,6 @@ export const useMultisigTx = ({ addTask }: Props): IMultisigTxService => { } const { getMultisigTx, getMultisigTxs, getAccountMultisigTxs, addMultisigTx, updateMultisigTx, deleteMultisigTx } = transactionStorage; - const { getChainById } = useChains(); const { decodeCallData } = useTransaction(); const { addEventWithQueue, getEvents, updateEvent } = useMultisigEvent({ addTask }); @@ -209,7 +208,7 @@ export const useMultisigTx = ({ addTask }: Props): IMultisigTxService => { const updateCallData = async (api: ApiPromise, tx: MultisigTransaction, callData: CallData) => { try { - const chain = getChainById(tx.chainId); + const chain = chainsService.getChainById(tx.chainId); const transaction = decodeCallData(api, toAddress(tx.accountId, { prefix: chain?.addressPrefix }), callData); diff --git a/src/renderer/entities/network/lib/__tests__/chainsService.test.ts b/src/renderer/entities/network/lib/__tests__/chainsService.test.ts index 5a2c5a9a07..368b1d25b3 100644 --- a/src/renderer/entities/network/lib/__tests__/chainsService.test.ts +++ b/src/renderer/entities/network/lib/__tests__/chainsService.test.ts @@ -1,13 +1,11 @@ -import { useChains } from '../chainsService'; +import { chainsService } from '../chainsService'; describe('service/chainsService', () => { test('should init', () => { - const { sortChains, sortChainsByBalance, getChainsData, getStakingChainsData } = useChains(); - - expect(sortChains).toBeDefined(); - expect(sortChainsByBalance).toBeDefined(); - expect(getChainsData).toBeDefined(); - expect(getStakingChainsData).toBeDefined(); + expect(chainsService.sortChains).toBeDefined(); + expect(chainsService.sortChainsByBalance).toBeDefined(); + expect(chainsService.getChainsData).toBeDefined(); + expect(chainsService.getStakingChainsData).toBeDefined(); }); test('should sort chains', () => { @@ -17,8 +15,7 @@ describe('service/chainsService', () => { const testnet = { name: 'Westend', options: ['testnet'] }; const parachain = { name: 'Acala' }; - const { sortChains } = useChains(); - const data = sortChains([testnet, polkadot, threeDPass, parachain, kusama]); + const data = chainsService.sortChains([testnet, polkadot, threeDPass, parachain, kusama]); expect(data).toEqual([polkadot, kusama, parachain, threeDPass, testnet]); }); diff --git a/src/renderer/entities/network/lib/chainsService.ts b/src/renderer/entities/network/lib/chainsService.ts index e1260f2cb0..e2e168d991 100644 --- a/src/renderer/entities/network/lib/chainsService.ts +++ b/src/renderer/entities/network/lib/chainsService.ts @@ -9,7 +9,7 @@ import chainsDev from '@renderer/assets/chains/chains_dev.json'; import { ChainId } from '@renderer/domain/shared-kernel'; import { getRelaychainAsset, nonNullable, totalAmount, ZERO_BALANCE } from '@renderer/shared/lib/utils'; import { Balance } from '@renderer/entities/asset/model/balance'; -import { ChainLike, IChainService } from './common/types'; +import { ChainLike } from './common/types'; import { isKusama, isPolkadot, isTestnet, isNameWithNumber } from './common/utils'; const CHAINS: Record = { @@ -17,95 +17,93 @@ const CHAINS: Record = { 'chains-dev': chainsDev, }; -export function useChains(): IChainService { - const getChainsData = (): Chain[] => { - return CHAINS[process.env.CHAINS_FILE || 'chains']; - }; - - const getChainById = (chainId: ChainId): Chain | undefined => { - const chainsData: Chain[] = CHAINS[process.env.CHAINS_FILE || 'chains']; - - return chainsData.find((chain) => chain.chainId === chainId); - }; - - const getStakingChainsData = (): Chain[] => { - const chainsData: Chain[] = CHAINS[process.env.CHAINS_FILE || 'chains']; - - return chainsData.reduce((acc, chain) => { - if (getRelaychainAsset(chain.assets)) { - acc.push(chain); - } - - return acc; - }, []); - }; - - const sortChains = (chains: T[]): T[] => { - let polkadot; - let kusama; - const testnets = [] as T[]; - const parachains = [] as T[]; - const numberchains = [] as T[]; - - chains.forEach((chain) => { - if (isPolkadot(chain.name)) polkadot = chain; - else if (isKusama(chain.name)) kusama = chain; - else if (isTestnet(chain.options)) testnets.push(chain); - else if (isNameWithNumber(chain.name)) numberchains.push(chain); - else parachains.push(chain); - }); +export const chainsService = { + getChainsData, + getChainById, + getStakingChainsData, + sortChains, + sortChainsByBalance, +}; + +function getChainsData(): Chain[] { + return CHAINS[process.env.CHAINS_FILE || 'chains']; +} + +function getChainById(chainId: ChainId): Chain | undefined { + const chainsData: Chain[] = CHAINS[process.env.CHAINS_FILE || 'chains']; + + return chainsData.find((chain) => chain.chainId === chainId); +} + +function getStakingChainsData(): Chain[] { + const chainsData: Chain[] = CHAINS[process.env.CHAINS_FILE || 'chains']; + + return chainsData.reduce((acc, chain) => { + if (getRelaychainAsset(chain.assets)) { + acc.push(chain); + } + + return acc; + }, []); +} + +function sortChains(chains: T[]): T[] { + let polkadot; + let kusama; + const testnets = [] as T[]; + const parachains = [] as T[]; + const numberchains = [] as T[]; + + chains.forEach((chain) => { + if (isPolkadot(chain.name)) polkadot = chain; + else if (isKusama(chain.name)) kusama = chain; + else if (isTestnet(chain.options)) testnets.push(chain); + else if (isNameWithNumber(chain.name)) numberchains.push(chain); + else parachains.push(chain); + }); + + return concat( + [polkadot, kusama].filter(nonNullable), + sortBy(parachains, 'name'), + sortBy(numberchains, 'name'), + sortBy(testnets, 'name'), + ); +} + +function sortChainsByBalance(chains: Chain[], balances: Balance[]): Chain[] { + const relaychains = { withBalance: [], noBalance: [] }; + const parachains = { withBalance: [], noBalance: [] }; + const numberchains = { withBalance: [], noBalance: [] }; + const testnets = { withBalance: [], noBalance: [] }; + + const balancesMap = keyBy(balances, (b) => `${b.chainId}_${b.assetId}`); - return concat( - [polkadot, kusama].filter(nonNullable), - sortBy(parachains, 'name'), - sortBy(numberchains, 'name'), - sortBy(testnets, 'name'), - ); - }; - - const sortChainsByBalance = (chains: Chain[], balances: Balance[]): Chain[] => { - const relaychains = { withBalance: [], noBalance: [] }; - const parachains = { withBalance: [], noBalance: [] }; - const numberchains = { withBalance: [], noBalance: [] }; - const testnets = { withBalance: [], noBalance: [] }; - - const balancesMap = keyBy(balances, (b) => `${b.chainId}_${b.assetId}`); - - chains.forEach((chain) => { - const hasBalance = chain.assets.some((a) => { - return totalAmount(balancesMap[`${chain.chainId}_${a.assetId}`]) !== ZERO_BALANCE; - }); - - let collection: Chain[] = hasBalance ? parachains.withBalance : parachains.noBalance; - - if (isPolkadot(chain.name) || isKusama(chain.name)) { - collection = hasBalance ? relaychains.withBalance : relaychains.noBalance; - } else if (isTestnet(chain.options)) { - collection = hasBalance ? testnets.withBalance : testnets.noBalance; - } else if (isNameWithNumber(chain.name)) { - collection = hasBalance ? numberchains.withBalance : numberchains.noBalance; - } - - collection.push(chain); + chains.forEach((chain) => { + const hasBalance = chain.assets.some((a) => { + return totalAmount(balancesMap[`${chain.chainId}_${a.assetId}`]) !== ZERO_BALANCE; }); - return concat( - orderBy(relaychains.withBalance, 'name', ['desc']), - orderBy(relaychains.noBalance, 'name', ['desc']), - sortBy(parachains.withBalance, 'name'), - sortBy(parachains.noBalance, 'name'), - sortBy(numberchains.withBalance, 'name'), - sortBy(numberchains.noBalance, 'name'), - sortBy(testnets.withBalance, 'name'), - sortBy(testnets.noBalance, 'name'), - ); - }; - - return { - getChainsData, - getChainById, - getStakingChainsData, - sortChains, - sortChainsByBalance, - }; + let collection: Chain[] = hasBalance ? parachains.withBalance : parachains.noBalance; + + if (isPolkadot(chain.name) || isKusama(chain.name)) { + collection = hasBalance ? relaychains.withBalance : relaychains.noBalance; + } else if (isTestnet(chain.options)) { + collection = hasBalance ? testnets.withBalance : testnets.noBalance; + } else if (isNameWithNumber(chain.name)) { + collection = hasBalance ? numberchains.withBalance : numberchains.noBalance; + } + + collection.push(chain); + }); + + return concat( + orderBy(relaychains.withBalance, 'name', ['desc']), + orderBy(relaychains.noBalance, 'name', ['desc']), + sortBy(parachains.withBalance, 'name'), + sortBy(parachains.noBalance, 'name'), + sortBy(numberchains.withBalance, 'name'), + sortBy(numberchains.noBalance, 'name'), + sortBy(testnets.withBalance, 'name'), + sortBy(testnets.noBalance, 'name'), + ); } diff --git a/src/renderer/entities/network/lib/index.ts b/src/renderer/entities/network/lib/index.ts index c94417b95a..9c0d8e48f9 100644 --- a/src/renderer/entities/network/lib/index.ts +++ b/src/renderer/entities/network/lib/index.ts @@ -1,5 +1,5 @@ export * from './chainSpecService'; -export * from './chainsService'; +export { chainsService } from './chainsService'; export * from './networkService'; export * from './common/types'; export * from './common/utils'; diff --git a/src/renderer/entities/network/lib/networkService.ts b/src/renderer/entities/network/lib/networkService.ts index 8a04da50ea..ef54651d21 100644 --- a/src/renderer/entities/network/lib/networkService.ts +++ b/src/renderer/entities/network/lib/networkService.ts @@ -10,7 +10,7 @@ import { Connection, ConnectionStatus, ConnectionType } from '@renderer/domain/c import { ChainId } from '@renderer/domain/shared-kernel'; import { ISubscriptionService } from '../../../services/subscription/common/types'; import { useChainSpec } from './chainSpecService'; -import { useChains } from './chainsService'; +import { chainsService } from './chainsService'; import { useMetadata } from './metadataService'; import { AUTO_BALANCE_TIMEOUT, MAX_ATTEMPTS, PROGRESSION_BASE } from './common/constants'; import { ConnectionsMap, ConnectProps, INetworkService, RpcValidation } from './common/types'; @@ -20,7 +20,6 @@ export const useNetwork = (networkSubscription?: ISubscriptionService): const chains = useRef>({}); const [connections, setConnections] = useState({}); - const { getChainsData, sortChains } = useChains(); const { getKnownChain, getLightClientChains } = useChainSpec(); const { subscribeMetadata, getMetadata } = useMetadata(); @@ -358,8 +357,8 @@ export const useNetwork = (networkSubscription?: ISubscriptionService): const setupConnections = async (): Promise => { try { - const chainsData = getChainsData(); - chains.current = keyBy(sortChains(chainsData), 'chainId'); + const chainsData = chainsService.getChainsData(); + chains.current = keyBy(chainsService.sortChains(chainsData), 'chainId'); const newConnections = await getNewConnections(); await clearConnections(); diff --git a/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts b/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts index dd25fce5ba..3dfed0ae5f 100644 --- a/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts +++ b/src/renderer/features/assets/AssetRouteGuard/model/asset-guard.ts @@ -3,11 +3,9 @@ import { NavigateFunction } from 'react-router-dom'; import { Chain } from '@renderer/entities/chain'; import { Asset } from '@renderer/entities/asset'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { ChainId } from '@renderer/domain/shared-kernel'; -const { getChainById } = useChains(); - const validateUrlParams = createEvent(); const storeCleared = createEvent(); @@ -30,7 +28,7 @@ type ValidateParams = { const getChainAndAssetFx = createEffect(({ chainId, assetId }: ValidateParams) => { if (!chainId || !assetId) return undefined; - const chain = getChainById(chainId as ChainId); + const chain = chainsService.getChainById(chainId as ChainId); const asset = chain?.assets.find((a) => a.assetId === Number(assetId)); return { chain, asset }; diff --git a/src/renderer/features/operation/OperationsFilter/ui/OperationsFilter.tsx b/src/renderer/features/operation/OperationsFilter/ui/OperationsFilter.tsx index d59d81b625..7b56c5d6ab 100644 --- a/src/renderer/features/operation/OperationsFilter/ui/OperationsFilter.tsx +++ b/src/renderer/features/operation/OperationsFilter/ui/OperationsFilter.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; -import { useI18n, useNetworkContext } from '@renderer/app/providers'; +import { useI18n } from '@renderer/app/providers'; import { MultisigTransactionDS } from '@renderer/shared/api/storage'; import { DropdownOption, DropdownResult } from '@renderer/shared/ui/types'; import { Button, MultiSelect } from '@renderer/shared/ui'; @@ -8,6 +8,8 @@ import { MultisigTransaction, Transaction, TransactionType } from '@renderer/ent import { TransferTypes, XcmTypes } from '@renderer/entities/transaction/lib/common/constants'; import { getStatusOptions, getTransactionOptions } from '../lib/utils'; import { UNKNOWN_TYPE } from '../lib/constants'; +import { ChainId } from '@renderer/domain/shared-kernel'; +import { chainsService } from '@renderer/entities/network'; type FilterName = 'status' | 'network' | 'type'; @@ -35,23 +37,29 @@ type Props = { export const OperationsFilter = ({ txs, onChange }: Props) => { const { t } = useI18n(); - const { connections } = useNetworkContext(); + + const [availableChains, setAvailableChains] = useState<{ chainId: ChainId; name: string }[]>([]); + const [filtersOptions, setFiltersOptions] = useState(EmptyOptions); + const [selectedOptions, setSelectedOptions] = useState(EmptySelected); + + useEffect(() => { + const chains = chainsService.getChainsData().map(({ chainId, name }) => ({ chainId, name })); + + setAvailableChains(chains); + }, []); const StatusOptions = getStatusOptions(t); const TransactionOptions = getTransactionOptions(t); - const NetworkOptions = Object.values(connections).map((c) => ({ - id: c.chainId, - value: c.chainId, - element: c.name, + const NetworkOptions = availableChains.map(({ chainId, name }) => ({ + id: chainId, + value: chainId, + element: name, })); - const [filtersOptions, setFiltersOptions] = useState(EmptyOptions); - const [selectedOptions, setSelectedOptions] = useState(EmptySelected); - useEffect(() => { setFiltersOptions(getAvailableFiltersOptions(txs)); onChange(txs); - }, [txs]); + }, [txs, availableChains]); const getFilterableTxType = (tx: MultisigTransaction): TransactionType | typeof UNKNOWN_TYPE => { if (!tx.transaction?.type) return UNKNOWN_TYPE; @@ -77,11 +85,13 @@ export const OperationsFilter = ({ txs, onChange }: Props) => { const xcmDestination = tx.transaction?.args.destinationChain; const statusOption = StatusOptions.find((s) => s.value === tx.status); - const networkOption = NetworkOptions.find((s) => s.value === tx.chainId || s.value === xcmDestination); + const originNetworkOption = NetworkOptions.find((s) => s.value === tx.chainId); + const destNetworkOption = NetworkOptions.find((s) => s.value === xcmDestination); const typeOption = TransactionOptions.find((s) => s.value === txType); if (statusOption) acc.status.add(statusOption); - if (networkOption) acc.network.add(networkOption); + if (originNetworkOption) acc.network.add(originNetworkOption); + if (destNetworkOption) acc.network.add(destNetworkOption); if (typeOption) acc.type.add(typeOption); return acc; @@ -97,12 +107,12 @@ export const OperationsFilter = ({ txs, onChange }: Props) => { const filterTx = (tx: MultisigTransaction, filters: SelectedFilters) => { const xcmDestination = tx.transaction?.args.destinationChain; - return ( - ((!filters.status.length || filters.status.map(mapValues).includes(tx.status)) && - (!filters.network.length || filters.network.map(mapValues).includes(tx.chainId))) || - (filters.network.map(mapValues).includes(xcmDestination) && - (!filters.type.length || filters.type.map(mapValues).includes(getFilterableTxType(tx)))) - ); + const hasStatus = !filters.status.length || filters.status.map(mapValues).includes(tx.status); + const hasOrigin = !filters.network.length || filters.network.map(mapValues).includes(tx.chainId); + const hasDestination = !filters.network.length || filters.network.map(mapValues).includes(xcmDestination); + const hasTxType = !filters.type.length || filters.type.map(mapValues).includes(getFilterableTxType(tx)); + + return hasStatus && (hasOrigin || hasDestination) && hasTxType; }; const handleFilterChange = (values: DropdownResult[], filterName: FilterName) => { diff --git a/src/renderer/pages/Assets/AssetsList/AssetsList.test.tsx b/src/renderer/pages/Assets/AssetsList/AssetsList.test.tsx index dd78c99dc2..5e34da521e 100644 --- a/src/renderer/pages/Assets/AssetsList/AssetsList.test.tsx +++ b/src/renderer/pages/Assets/AssetsList/AssetsList.test.tsx @@ -42,9 +42,9 @@ const CHAINS = [ ]; jest.mock('@renderer/entities/network', () => ({ - useChains: jest.fn().mockReturnValue({ + chainsService: { sortChainsByBalance: () => CHAINS, - }), + }, })); jest.mock('@renderer/entities/asset', () => ({ diff --git a/src/renderer/pages/Assets/AssetsList/AssetsList.tsx b/src/renderer/pages/Assets/AssetsList/AssetsList.tsx index 48623774c6..fd2bae2aab 100644 --- a/src/renderer/pages/Assets/AssetsList/AssetsList.tsx +++ b/src/renderer/pages/Assets/AssetsList/AssetsList.tsx @@ -8,7 +8,7 @@ import { Chain } from '@renderer/entities/chain'; import { ConnectionType } from '@renderer/domain/connection'; import { SigningType } from '@renderer/domain/shared-kernel'; import { useToggle } from '@renderer/shared/lib/hooks'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { useSettingsStorage } from '@renderer/entities/settings'; import { Account, isMultisig, useAccount } from '@renderer/entities/account'; import { AssetsFilters, NetworkAssets, SelectShardModal } from './components'; @@ -19,7 +19,6 @@ export const AssetsList = () => { const { connections } = useNetworkContext(); const { getActiveAccounts } = useAccount(); const { getLiveBalances } = useBalance(); - const { sortChainsByBalance } = useChains(); const { setHideZeroBalance, getHideZeroBalance } = useSettingsStorage(); const [isSelectShardsOpen, toggleSelectShardsOpen] = useToggle(); @@ -55,7 +54,7 @@ export const AssetsList = () => { return !isDisabled && hasMultiPallet; }); - setSortedChains(sortChainsByBalance(filteredChains, balances)); + setSortedChains(chainsService.sortChainsByBalance(filteredChains, balances)); }, [balances]); const updateHideZeroBalance = (value: boolean) => { diff --git a/src/renderer/pages/Assets/AssetsList/components/SelectShardModal/SelectShardModal.tsx b/src/renderer/pages/Assets/AssetsList/components/SelectShardModal/SelectShardModal.tsx index 3a59d00554..4e68862b33 100644 --- a/src/renderer/pages/Assets/AssetsList/components/SelectShardModal/SelectShardModal.tsx +++ b/src/renderer/pages/Assets/AssetsList/components/SelectShardModal/SelectShardModal.tsx @@ -5,7 +5,7 @@ import { AccountId, ChainId } from '@renderer/domain/shared-kernel'; import { BaseModal, Button, Checkbox, FootnoteText, SearchInput } from '@renderer/shared/ui'; import { useI18n } from '@renderer/app/providers'; import { AccountDS } from '@renderer/shared/api/storage'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { getMultishardStructure, getSelectableShards, @@ -28,15 +28,14 @@ type Props = { export const SelectShardModal = ({ isOpen, onClose, activeAccounts, accounts }: Props) => { const { t } = useI18n(); - const { getChainsData, sortChains } = useChains(); const [chains, setChains] = useState({}); useEffect(() => { if (!accounts[0]?.walletId) return; - const chains = getChainsData(); - const chainsById = keyBy(sortChains(chains), 'chainId'); + const chains = chainsService.getChainsData(); + const chainsById = keyBy(chainsService.sortChains(chains), 'chainId'); const activeIds = activeAccounts.map((a) => a.id || ''); const multishard = getMultishardStructure(accounts, chainsById, accounts[0].walletId!); diff --git a/src/renderer/pages/Onboarding/Vault/ManageStep/ManageStep.tsx b/src/renderer/pages/Onboarding/Vault/ManageStep/ManageStep.tsx index dff7fa8b6a..b04c661ffd 100644 --- a/src/renderer/pages/Onboarding/Vault/ManageStep/ManageStep.tsx +++ b/src/renderer/pages/Onboarding/Vault/ManageStep/ManageStep.tsx @@ -4,7 +4,7 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import { u8aToHex } from '@polkadot/util'; import { keyBy } from 'lodash'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { ID } from '@renderer/shared/api/storage'; import { useI18n } from '@renderer/app/providers'; import { Chain, Explorer, ChainTitle } from '@renderer/entities/chain'; @@ -42,8 +42,6 @@ type Props = { const ManageStep = ({ seedInfo, onBack, onComplete }: Props) => { const { t } = useI18n(); - const { getChainsData, sortChains } = useChains(); - const { handleSubmit, control, @@ -65,8 +63,8 @@ const ManageStep = ({ seedInfo, onBack, onComplete }: Props) => { const [accounts, setAccounts] = useState([]); useEffect(() => { - const chains = getChainsData(); - const chainsMap = keyBy(sortChains(chains), 'chainId'); + const chains = chainsService.getChainsData(); + const chainsMap = keyBy(chainsService.sortChains(chains), 'chainId'); setChainsObject(chainsMap); const filteredQrData = seedInfo.map((data) => filterByExistingChains(data, chainsMap)); diff --git a/src/renderer/pages/Onboarding/Vault/ManageStepSingle/ManageStepSingle.tsx b/src/renderer/pages/Onboarding/Vault/ManageStepSingle/ManageStepSingle.tsx index bb769f8edc..0ea3dafa96 100644 --- a/src/renderer/pages/Onboarding/Vault/ManageStepSingle/ManageStepSingle.tsx +++ b/src/renderer/pages/Onboarding/Vault/ManageStepSingle/ManageStepSingle.tsx @@ -6,7 +6,7 @@ import { u8aToHex } from '@polkadot/util'; import { useI18n } from '@renderer/app/providers'; import { Chain } from '@renderer/entities/chain'; import { ErrorType, SigningType } from '@renderer/domain/shared-kernel'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { Button, Input, InputHint, HeaderTitleText, SmallTitleText } from '@renderer/shared/ui'; import { SeedInfo } from '@renderer/components/common/QrCode/common/types'; import { useAccount, createAccount, AccountsList } from '@renderer/entities/account'; @@ -26,8 +26,6 @@ const ManageStepSingle = ({ seedInfo, onBack, onComplete }: Props) => { const accountId = u8aToHex(seedInfo[0].multiSigner?.public); const { addAccount, setActiveAccount } = useAccount(); - const { getChainsData, sortChains } = useChains(); - const [chains, setChains] = useState([]); const { @@ -41,9 +39,9 @@ const ManageStepSingle = ({ seedInfo, onBack, onComplete }: Props) => { }); useEffect(() => { - const chains = getChainsData(); + const chains = chainsService.getChainsData(); - setChains(sortChains(chains)); + setChains(chainsService.sortChains(chains)); }, []); const submitHandler: SubmitHandler = async ({ walletName }) => { diff --git a/src/renderer/pages/Onboarding/WatchOnly/WatchOnly.tsx b/src/renderer/pages/Onboarding/WatchOnly/WatchOnly.tsx index 6a84f7cffc..74ca1863be 100644 --- a/src/renderer/pages/Onboarding/WatchOnly/WatchOnly.tsx +++ b/src/renderer/pages/Onboarding/WatchOnly/WatchOnly.tsx @@ -15,7 +15,7 @@ import { import { useI18n } from '@renderer/app/providers'; import { Chain } from '@renderer/entities/chain'; import { ErrorType, AccountId, SigningType } from '@renderer/domain/shared-kernel'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { toAccountId, validateAddress } from '@renderer/shared/lib/utils'; import EmptyState from './EmptyState'; import { createAccount, useAccount, AccountsList } from '@renderer/entities/account'; @@ -34,7 +34,6 @@ type Props = { const WatchOnly = ({ isOpen, onClose, onComplete }: Props) => { const { t } = useI18n(); - const { getChainsData, sortChains } = useChains(); const { addAccount, setActiveAccount } = useAccount(); const [chains, setChains] = useState([]); @@ -58,9 +57,9 @@ const WatchOnly = ({ isOpen, onClose, onComplete }: Props) => { }, [address]); useEffect(() => { - const chains = getChainsData(); + const chains = chainsService.getChainsData(); - setChains(sortChains(chains)); + setChains(chainsService.sortChains(chains)); }, []); const createWallet: SubmitHandler = async ({ walletName, address }) => { diff --git a/src/renderer/pages/Operations/components/TransactionAmount.tsx b/src/renderer/pages/Operations/components/TransactionAmount.tsx index cd03837da9..776c9c9e7f 100644 --- a/src/renderer/pages/Operations/components/TransactionAmount.tsx +++ b/src/renderer/pages/Operations/components/TransactionAmount.tsx @@ -1,7 +1,7 @@ import { useEffect, useState, ComponentProps } from 'react'; import { DecodedTransaction, Transaction } from '@renderer/entities/transaction'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { Asset, AssetBalance } from '@renderer/entities/asset'; import { getAssetById } from '@renderer/shared/lib/utils'; import { getTransactionAmount } from '../common/utils'; @@ -13,11 +13,10 @@ type Props = { type BalanceProps = Pick, 'className' | 'showIcon' | 'wrapperClassName'>; export const TransactionAmount = ({ tx, ...balanceProps }: Props & BalanceProps) => { - const { getChainById } = useChains(); const [assets, setAssets] = useState([]); useEffect(() => { - const chain = getChainById(tx.chainId); + const chain = chainsService.getChainById(tx.chainId); setAssets(chain?.assets || []); }, []); diff --git a/src/renderer/pages/Settings/Networks/Networks.tsx b/src/renderer/pages/Settings/Networks/Networks.tsx index 463ee8400d..fd634bd506 100644 --- a/src/renderer/pages/Settings/Networks/Networks.tsx +++ b/src/renderer/pages/Settings/Networks/Networks.tsx @@ -6,7 +6,7 @@ import uniqBy from 'lodash/uniqBy'; import { useI18n, useNetworkContext, useConfirmContext, Paths } from '@renderer/app/providers'; import { BaseModal, SearchInput, BodyText, InfoLink, Icon } from '@renderer/shared/ui'; import { useToggle } from '@renderer/shared/lib/hooks'; -import { ExtendedChain, useChains } from '@renderer/entities/network'; +import { ExtendedChain, chainsService } from '@renderer/entities/network'; import { includes, DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; import { ConnectionType, ConnectionStatus } from '@renderer/domain/connection'; import { RpcNode } from '@renderer/entities/chain'; @@ -22,7 +22,6 @@ const DATA_VERIFICATION = 'https://docs.novaspektr.io/network-management/light-c export const Networks = () => { const { t } = useI18n(); const navigate = useNavigate(); - const { sortChains } = useChains(); const { confirm } = useConfirmContext(); const { connections, connectToNetwork, connectWithAutoBalance, removeRpcNode, getParachains } = useNetworkContext(); const { setBalanceIsValid } = useBalance(); @@ -240,7 +239,7 @@ export const Networks = () => { isDefaultOpen={false} query={query} title={t('settings.networks.disabledNetworksLabel')} - networkList={sortChains(inactive)} + networkList={chainsService.sortChains(inactive)} > {(network) => ( { isDefaultOpen query={query} title={t('settings.networks.activeNetworksLabel')} - networkList={sortChains(active)} + networkList={chainsService.sortChains(active)} > {(network) => ( ({ })); jest.mock('@renderer/entities/network', () => ({ - useChains: jest.fn().mockReturnValue({ + chainsService: { sortChains: (value: Chain[]) => value, getChainsData: jest.fn().mockResolvedValue([ { @@ -30,7 +30,7 @@ jest.mock('@renderer/entities/network', () => ({ name: 'My test chain', }, ]), - }), + }, })); jest.mock('@renderer/entities/account', () => ({ diff --git a/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx index 95f2798702..02a152d575 100644 --- a/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx +++ b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.test.tsx @@ -12,7 +12,7 @@ jest.mock('@renderer/app/providers', () => ({ })); jest.mock('@renderer/entities/network', () => ({ - useChains: jest.fn().mockReturnValue({ + chainsService: { sortChains: jest.fn((value: Chain[]) => value), getChainsData: jest.fn().mockReturnValue([ { @@ -28,7 +28,7 @@ jest.mock('@renderer/entities/network', () => ({ assets: [{ symbol: 'DOT', staking: 'relaychain', name: 'Polkadot' }], }, ]), - }), + }, })); jest.mock('@renderer/entities/settings', () => ({ diff --git a/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx index d50e2c935e..0b19468320 100644 --- a/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx +++ b/src/renderer/pages/Staking/Overview/components/NetworkInfo/NetworkInfo.tsx @@ -4,7 +4,7 @@ import { BN, BN_ZERO } from '@polkadot/util'; import { Select, FootnoteText, Plate, IconButton, Shimmering } from '@renderer/shared/ui'; import { DropdownOption, DropdownResult } from '@renderer/shared/ui/types'; import { getRelaychainAsset } from '@renderer/shared/lib/utils'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { useSettingsStorage } from '@renderer/entities/settings'; import { Chain, ChainTitle } from '@renderer/entities/chain'; import { useToggle } from '@renderer/shared/lib/hooks'; @@ -32,7 +32,6 @@ export const NetworkInfo = ({ onNetworkChange, }: PropsWithChildren) => { const { t } = useI18n(); - const { sortChains, getChainsData } = useChains(); const { getStakingNetwork, setStakingNetwork } = useSettingsStorage(); const [isChildrenShown, toggleChildren] = useToggle(); @@ -40,8 +39,8 @@ export const NetworkInfo = ({ const [activeNetwork, setActiveNetwork] = useState>(); useEffect(() => { - const chains = getChainsData(); - const relaychains = sortChains(chains).reduce[]>((acc, chain) => { + const chains = chainsService.getChainsData(); + const relaychains = chainsService.sortChains(chains).reduce[]>((acc, chain) => { const { chainId, assets } = chain; if (getRelaychainAsset(assets)) { diff --git a/src/renderer/shared/api/xcm/xcmService.ts b/src/renderer/shared/api/xcm/xcmService.ts index 969c497b12..74e4ef2a8a 100644 --- a/src/renderer/shared/api/xcm/xcmService.ts +++ b/src/renderer/shared/api/xcm/xcmService.ts @@ -7,7 +7,7 @@ import { AccountId, ChainId, HexString } from '@renderer/domain/shared-kernel'; import { Chain } from '@renderer/entities/chain'; import { getTypeVersion, toLocalChainId, getAssetId } from '@renderer/shared/lib/utils'; import { XcmPalletTransferArgs, XTokenPalletTransferArgs } from '@renderer/entities/transaction'; -import { useChains } from '@renderer/entities/network'; +import { chainsService } from '@renderer/entities/network'; import { toRawString } from './common/utils'; import { XcmConfig, @@ -435,14 +435,12 @@ const getJunctionCols = (interior: Object, path: string): T => }; export const decodeXcm = (chainId: ChainId, data: XcmPalletPayload | XTokensPayload): DecodedPayload => { - const { getChainById } = useChains(); - const config = getXcmConfig(); if (!config) return {} as DecodedPayload; let destinationChain: HexString | undefined; if (data.toRelayChain) { - destinationChain = getChainById(chainId)?.parentId; + destinationChain = chainsService.getChainById(chainId)?.parentId; } else { const destination = Object.values(config.assetsLocation).find(({ multiLocation }) => { return multiLocation.parachainId === data.destParachain; @@ -473,7 +471,9 @@ export const decodeXcm = (chainId: ChainId, data: XcmPalletPayload | XTokensPayl }); if (assetKeyVal) { - const assetFromChain = getChainById(chainId)?.assets.find((asset) => asset.assetId === assetKeyVal[0]); + const assetFromChain = chainsService + .getChainById(chainId) + ?.assets.find((asset) => asset.assetId === assetKeyVal[0]); if (assetFromChain) { assetId = getAssetId(assetFromChain); } From 11ddea5c53fd78e9ad241a8d4c2007db12e7bebb Mon Sep 17 00:00:00 2001 From: Aleksandr Makhnev Date: Fri, 15 Sep 2023 19:06:59 +0500 Subject: [PATCH 22/34] feat: placeholder for select dropdown (#1060) * fix: estimate fee from api * feat: placeholder for select and multiselect * fix: fee calculation for relaychain * chore: fix asset amount for small values --- .../transaction/lib/transactionService.ts | 7 +- .../entities/transaction/ui/XcmFee/XcmFee.tsx | 54 +++--- .../operation/init/ui/OperationFooter.tsx | 3 + .../api/xcm/__tests__/xcmService.test.ts | 6 +- src/renderer/shared/api/xcm/xcmService.ts | 175 +++++++++++++++++- src/renderer/shared/lib/utils/chains.ts | 4 + .../shared/ui/Dropdowns/Select/Select.tsx | 7 +- .../shared/ui/Dropdowns/common/types.ts | 1 + .../SendAssetModal/model/send-asset.ts | 15 +- .../SendAssetModal/ui/common/utils.tsx | 7 + .../components/ActionSteps/Confirmation.tsx | 9 +- .../components/ActionSteps/InitOperation.tsx | 7 + .../ui/components/TransferForm.tsx | 13 +- src/shared/locale/en.json | 2 + src/shared/locale/ru.json | 2 + 15 files changed, 268 insertions(+), 44 deletions(-) diff --git a/src/renderer/entities/transaction/lib/transactionService.ts b/src/renderer/entities/transaction/lib/transactionService.ts index 2b2e3eb5b0..513f8c6ee7 100644 --- a/src/renderer/entities/transaction/lib/transactionService.ts +++ b/src/renderer/entities/transaction/lib/transactionService.ts @@ -232,7 +232,8 @@ export const useTransaction = (): ITransactionService => { ); }, [TransactionType.XTOKENS_TRANSFER_MULTIASSET]: (transaction, info, options, api) => { - const singleXcmAsset = { V2: transaction.args.xcmAsset.V2[0] }; + const version = Object.keys(transaction.args.xcmAsset)[0]; + const singleXcmAsset = { [version]: transaction.args.xcmAsset[version][0] }; return xcmMethods.transferMultiAsset( { @@ -389,7 +390,7 @@ export const useTransaction = (): ITransactionService => { const singleXcmAsset = { [version]: xcmAsset[version][0] }; const weight = hasDestWeight(api) ? xcmWeight : { Unlimited: true }; - return api.tx.xTokens.transferMultiasset(xcmDest, singleXcmAsset, weight); + return api.tx.xTokens.transferMultiasset(singleXcmAsset, xcmDest, weight); }, // controller arg removed from bond but changes not released yet // https://github.com/paritytech/substrate/pull/14039 @@ -446,6 +447,8 @@ export const useTransaction = (): ITransactionService => { const getTransactionHash = (transaction: Transaction, api: ApiPromise): HashData => { const extrinsic = getExtrinsic[transaction.type](transaction.args, api); + console.log('xcmMethod', extrinsic.method.toJSON()); + return { callData: extrinsic.method.toHex(), callHash: extrinsic.method.hash.toHex(), diff --git a/src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx b/src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx index 74c513a2a9..f6b4a4f41f 100644 --- a/src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx +++ b/src/renderer/entities/transaction/ui/XcmFee/XcmFee.tsx @@ -1,13 +1,15 @@ import { BN } from '@polkadot/util'; import { useEffect, useState, memo } from 'react'; +import { ApiPromise } from '@polkadot/api'; import { Asset, AssetBalance } from '@renderer/entities/asset'; import { Transaction } from '@renderer/entities/transaction'; import { Shimmering } from '@renderer/shared/ui'; -import { XcmConfig, estimateFee } from '@renderer/shared/api/xcm'; +import { estimateFee, XcmConfig } from '@renderer/shared/api/xcm'; import { toLocalChainId } from '@renderer/shared/lib/utils'; type Props = { + api?: ApiPromise; multiply?: number; asset: Asset; config: XcmConfig; @@ -18,8 +20,8 @@ type Props = { }; export const XcmFee = memo( - ({ multiply = 1, config, asset, transaction, className, onFeeChange, onFeeLoading }: Props) => { - const [fee, setFee] = useState(''); + ({ multiply = 1, config, asset, transaction, className, onFeeChange, onFeeLoading, api }: Props) => { + const [fee, setFee] = useState('0'); const [isLoading, setIsLoading] = useState(false); const updateFee = (fee: string) => { @@ -32,32 +34,36 @@ export const XcmFee = memo( }, [isLoading]); useEffect(() => { - setIsLoading(true); + const handleFee = (fee: string) => { + updateFee(fee); + setIsLoading(false); + }; + setIsLoading(true); if (!transaction?.address) { - updateFee('0'); - setIsLoading(false); - } else { - const originChainId = toLocalChainId(transaction.chainId); - const destinationChainId = toLocalChainId(transaction.args.destinationChain); - const configChain = config.chains.find((c) => c.chainId === originChainId); - const configAsset = configChain?.assets.find((a) => a.assetId === asset.assetId); - const configXcmTransfer = configAsset?.xcmTransfers.find((t) => t.destination.chainId === destinationChainId); + handleFee('0'); - if (originChainId && configXcmTransfer && configAsset) { - const fee = estimateFee( - config, - config.assetsLocation[configAsset.assetLocation], - originChainId, - configXcmTransfer, - ); + return; + } - updateFee(fee.toString()); - } else { - updateFee('0'); - } + const originChainId = toLocalChainId(transaction.chainId); + const destinationChainId = toLocalChainId(transaction.args.destinationChain); + const configChain = config.chains.find((c) => c.chainId === originChainId); + const configAsset = configChain?.assets.find((a) => a.assetId === asset.assetId); + const configXcmTransfer = configAsset?.xcmTransfers.find((t) => t.destination.chainId === destinationChainId); - setIsLoading(false); + if (originChainId && configXcmTransfer && configAsset) { + estimateFee( + config, + config.assetsLocation[configAsset.assetLocation], + originChainId, + configXcmTransfer, + api, + transaction.args.xcmAsset, + transaction.args.xcmDest, + ).then((fee) => handleFee(fee.toString())); + } else { + handleFee('0'); } }, [transaction]); diff --git a/src/renderer/features/operation/init/ui/OperationFooter.tsx b/src/renderer/features/operation/init/ui/OperationFooter.tsx index aa43a16466..2d89b80e5e 100644 --- a/src/renderer/features/operation/init/ui/OperationFooter.tsx +++ b/src/renderer/features/operation/init/ui/OperationFooter.tsx @@ -10,6 +10,7 @@ import { XcmFee } from '@renderer/entities/transaction/ui/XcmFee/XcmFee'; type Props = { api: ApiPromise; + reserveApi?: ApiPromise; asset: Asset; transaction: Transaction; account: Account | MultisigAccount; @@ -30,6 +31,7 @@ export const OperationFooter = ({ totalAccounts, xcmConfig, xcmAsset, + reserveApi, onXcmFeeChange, onDepositChange, onFeeChange, @@ -94,6 +96,7 @@ export const OperationFooter = ({ asset={xcmAsset} transaction={transaction} config={xcmConfig} + api={reserveApi} onFeeChange={onXcmFeeChange} onFeeLoading={onFeeLoading} /> diff --git a/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts b/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts index 91a1098070..59f92c7ccb 100644 --- a/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts +++ b/src/renderer/shared/api/xcm/__tests__/xcmService.test.ts @@ -1,6 +1,6 @@ import { XCM_KEY } from '../common/constants'; import { - estimateFee, + estimateFeeFromConfig, getXcmConfig, getDestinationLocation, parseXTokensExtrinsic, @@ -32,7 +32,7 @@ describe('shared/api/xcm/xcmService', () => { }); test('should calculate correct fee for ACA from Acala to Parallel ', () => { - const fee = estimateFee( + const fee = estimateFeeFromConfig( CONFIG, CONFIG.assetsLocation['ACA'], 'fc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c', @@ -43,7 +43,7 @@ describe('shared/api/xcm/xcmService', () => { }); test('should calculate correct fee for DOT from Acala to Parallel', () => { - const fee = estimateFee( + const fee = estimateFeeFromConfig( CONFIG, CONFIG.assetsLocation['DOT'], 'fc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c', diff --git a/src/renderer/shared/api/xcm/xcmService.ts b/src/renderer/shared/api/xcm/xcmService.ts index 74e4ef2a8a..578197118c 100644 --- a/src/renderer/shared/api/xcm/xcmService.ts +++ b/src/renderer/shared/api/xcm/xcmService.ts @@ -5,7 +5,7 @@ import get from 'lodash/get'; import { XCM_URL, XCM_KEY } from './common/constants'; import { AccountId, ChainId, HexString } from '@renderer/domain/shared-kernel'; import { Chain } from '@renderer/entities/chain'; -import { getTypeVersion, toLocalChainId, getAssetId } from '@renderer/shared/lib/utils'; +import { getTypeVersion, toLocalChainId, getAssetId, TEST_ACCOUNT_ID } from '@renderer/shared/lib/utils'; import { XcmPalletTransferArgs, XTokenPalletTransferArgs } from '@renderer/entities/transaction'; import { chainsService } from '@renderer/entities/network'; import { toRawString } from './common/utils'; @@ -20,6 +20,7 @@ import { Instructions, XcmTransfer, PathType, + Action, } from './common/types'; export const fetchXcmConfig = async (): Promise => { @@ -69,7 +70,35 @@ export const getEstimatedWeight = ( return instructionWeight.mul(new BN(instruction.length)); }; -export const estimateFee = ( +export const estimateFee = async ( + config: XcmConfig, + assetLocation: AssetLocation, + originChain: string, + xcmTransfer: XcmTransfer, + api?: ApiPromise, + xcmAsset?: object, + xcmDest?: object, +): Promise => { + if (xcmTransfer.destination.fee.mode.type === 'proportional') { + return estimateFeeFromConfig(config, assetLocation, originChain, xcmTransfer); + } + + if (api && xcmAsset && xcmDest) { + const xcmAssetLocation = Object.values(xcmAsset)[0][0].id.Concrete; + const xcmDestLocation = Object.values(xcmDest as object)[0]; + + return estimateFeeFromApi( + api, + config.instructions[xcmTransfer.destination.fee.instructions], + xcmAssetLocation, + xcmDestLocation, + ); + } + + return new BN(0); +}; + +export const estimateFeeFromConfig = ( config: XcmConfig, assetLocation: AssetLocation, originChain: string, @@ -263,6 +292,15 @@ export const getDestinationLocation = ( return undefined; }; +export const getVersionedAccountLocation = (api: ApiPromise, accountId?: AccountId): Object | undefined => { + const location = getAccountLocation(accountId); + const version = getTypeVersion(api, 'VersionedMultiLocation'); + + return { + [version]: location, + }; +}; + export const getAccountLocation = (accountId?: AccountId): Object | undefined => { return { parents: 0, @@ -489,3 +527,136 @@ export const decodeXcm = (chainId: ChainId, data: XcmPalletPayload | XTokensPayl dest: data.destAccountId, }; }; + +const InstructionObjects: Record object> = { + [Action.WITHDRAW_ASSET]: (assetLocation: object) => { + return { + WithdrawAsset: [ + { + fun: { + Fungible: '0', + }, + id: assetLocation, + }, + ], + }; + }, + + [Action.CLEAR_ORIGIN]: () => { + return { + ClearOrigin: null, + }; + }, + + [Action.DEPOSIT_ASSET]: () => { + return { + DepositAsset: { + assets: { + Wild: 'All', + }, + maxAssets: 1, + beneficiary: { + interior: { + X1: { + AccountId32: { + network: 'Any', + id: TEST_ACCOUNT_ID, + }, + }, + }, + parents: 0, + }, + }, + }; + }, + + [Action.DEPOSIT_RESERVE_ASSET]: (_, destLocation) => { + return { + DepositReserveAsset: { + assets: { + Wild: 'All', + }, + dest: destLocation, + xcm: [], + }, + }; + }, + + [Action.RECEIVE_TELEPORTED_ASSET]: (assetLocation: object) => { + return { + ReceiveTeleportedAsset: [ + { + fun: { + Fungible: '0', + }, + id: { + Concrete: assetLocation, + }, + }, + ], + }; + }, + + [Action.RESERVE_ASSET_DEPOSITED]: (assetLocation: object) => { + return { + ReserveAssetDeposited: [ + { + fun: { + Fungible: '0', + }, + id: { + Concrete: assetLocation, + }, + }, + ], + }; + }, + + [Action.BUY_EXECUTION]: (assetLocation: object) => { + return { + BuyExecution: { + fees: { + id: { + Concrete: assetLocation, + }, + fun: { + Fungible: '0', + }, + }, + weightLimit: { + Unlimited: true, + }, + }, + }; + }, +}; + +export const estimateFeeFromApi = async ( + api: ApiPromise, + instructions: Action[], + assetLocation: object, + destLocation: object, +) => { + const pallet = api.tx.xcmPallet ? 'xcmPallet' : 'polkadotXcm'; + + const xcmVersion = getTypeVersion(api, 'VersionedXcm'); + + const message = { + [xcmVersion]: instructions.map((i) => InstructionObjects[i](assetLocation, destLocation)), + }; + + let paymentInfo; + + try { + paymentInfo = await api.tx[pallet].execute(message, 0).paymentInfo(TEST_ACCOUNT_ID); + } catch (e) { + paymentInfo = await api.tx[pallet] + .execute(message, { + refTime: '0', + proofSize: '0', + }) + .paymentInfo(TEST_ACCOUNT_ID); + } + + return paymentInfo.partialFee; +}; diff --git a/src/renderer/shared/lib/utils/chains.ts b/src/renderer/shared/lib/utils/chains.ts index 8d7bc3a35d..15c9bddb08 100644 --- a/src/renderer/shared/lib/utils/chains.ts +++ b/src/renderer/shared/lib/utils/chains.ts @@ -3,3 +3,7 @@ import { ChainId } from '@renderer/domain/shared-kernel'; export const toLocalChainId = (chainId?: ChainId): string | undefined => { return chainId?.replace('0x', ''); }; + +export const toHexChainId = (chainId?: string): ChainId | undefined => { + return `0x${chainId?.replace('0x', '')}`; +}; diff --git a/src/renderer/shared/ui/Dropdowns/Select/Select.tsx b/src/renderer/shared/ui/Dropdowns/Select/Select.tsx index cf64e5d4b0..4b9eccad4f 100644 --- a/src/renderer/shared/ui/Dropdowns/Select/Select.tsx +++ b/src/renderer/shared/ui/Dropdowns/Select/Select.tsx @@ -88,11 +88,14 @@ const Select = ({ position !== 'auto' && ViewClass[position], )} > - {options.map(({ id, value, element }) => ( + {options.map(({ id, value, element, disabled }) => ( cnTw(OptionStyle, OptionStyleTheme[theme](active, selected))} + disabled={disabled} + className={({ active, selected }) => + cnTw(OptionStyle, disabled ? 'cursor-default' : OptionStyleTheme[theme](active, selected)) + } > {['string', 'number'].includes(typeof element) ? ( {element} diff --git a/src/renderer/shared/ui/Dropdowns/common/types.ts b/src/renderer/shared/ui/Dropdowns/common/types.ts index e9c5f79f47..d4f463fc19 100644 --- a/src/renderer/shared/ui/Dropdowns/common/types.ts +++ b/src/renderer/shared/ui/Dropdowns/common/types.ts @@ -7,6 +7,7 @@ export type DropdownOption = { id: string; element: ReactNode; value: T; + disabled?: boolean; }; export type DropdownResult = { diff --git a/src/renderer/widgets/SendAssetModal/model/send-asset.ts b/src/renderer/widgets/SendAssetModal/model/send-asset.ts index ceebbd6d8a..1b1bb28d18 100644 --- a/src/renderer/widgets/SendAssetModal/model/send-asset.ts +++ b/src/renderer/widgets/SendAssetModal/model/send-asset.ts @@ -9,7 +9,7 @@ import { getAvailableDirections, AssetXCM, getAssetLocation, - getAccountLocation, + getVersionedAccountLocation, estimateRequiredDestWeight, getVersionedDestinationLocation, } from '@renderer/shared/api/xcm'; @@ -160,12 +160,11 @@ forward({ }); sample({ - source: $accountId, + source: { accountId: $accountId, props: $xcmProps }, + fn: ({ accountId, props: { api } }) => { + if (!accountId || accountId === '0x00' || !api) return null; - fn: (accountId) => { - if (!accountId || accountId === '0x00') return null; - - return getAccountLocation(accountId) || null; + return getVersionedAccountLocation(api, accountId) || null; }, target: $txBeneficiary, }); @@ -206,7 +205,9 @@ sample({ fn: ({ props: { api }, xcmAsset, config, amount, xcmFee }) => { if (!api || !xcmAsset || !amount || !config || !xcmFee) return null; - return getAssetLocation(api, xcmAsset, config.assetsLocation, new BN(amount).add(new BN(xcmFee))) || null; + const resultAmount = new BN(amount).add(new BN(xcmFee)); + + return getAssetLocation(api, xcmAsset, config.assetsLocation, resultAmount) || null; }, target: $txAsset, }); diff --git a/src/renderer/widgets/SendAssetModal/ui/common/utils.tsx b/src/renderer/widgets/SendAssetModal/ui/common/utils.tsx index b56c3fded7..0b84f02a6c 100644 --- a/src/renderer/widgets/SendAssetModal/ui/common/utils.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/common/utils.tsx @@ -7,6 +7,7 @@ import { DropdownOption } from '@renderer/shared/ui/Dropdowns/common/types'; import { toAddress, cnTw, transferableAmount } from '@renderer/shared/lib/utils'; import { Balance, Asset, AssetBalance } from '@renderer/entities/asset'; import { Chain, ChainTitle } from '@renderer/entities/chain'; +import { HelpText } from '@renderer/shared/ui'; type Params = { asset: Asset; @@ -91,3 +92,9 @@ export const getChainOption = ({ chainId }: Chain): DropdownOption => { return { id: chainId, value: chainId, element }; }; + +export const getPlaceholder = (text: string): DropdownOption => { + const element = {text}; + + return { id: text, value: text, element, disabled: true }; +}; diff --git a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx index f2c4b99776..fccf2bf7d7 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Confirmation.tsx @@ -92,7 +92,14 @@ export const Confirmation = ({ {isXcmTransfer && config && xcmAsset && ( - {config && } + {config && connection.api && ( + + )} )} diff --git a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx index f097c0022b..b77f6a4c03 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx @@ -12,6 +12,7 @@ import { getAccountOption, getSignatoryOption } from '../../common/utils'; import { OperationFooter, OperationHeader } from '@renderer/features/operation'; import * as sendAssetModel from '../../../model/send-asset'; import { useNetworkContext } from '@renderer/app/providers'; +import { toHexChainId } from '@renderer/shared/lib/utils'; type Props = { api: ApiPromise; @@ -48,6 +49,7 @@ export const InitOperation = ({ const xcmTransfer = useStore(sendAssetModel.$xcmTransfer); const xcmFee = useStore(sendAssetModel.$xcmFee); const xcmWeight = useStore(sendAssetModel.$xcmWeight); + const reserveAsset = useStore(sendAssetModel.$xcmAsset); const accounts = getActiveAccounts(); @@ -138,6 +140,10 @@ export const InitOperation = ({ sendAssetModel.events.amountChanged(amount); }; + const reserveChainId = + reserveAsset && config && toHexChainId(config.assetsLocation[reserveAsset.assetLocation].chainId); + const reserveApi = reserveChainId && connections[reserveChainId].api; + return (
        (); const [multisigTx, setMultisigTx] = useState(); const [multisigTxExist, setMultisigTxExist] = useState(false); - const [destinationOptions, setDestinationOptions] = useState[]>([]); + const [destinationOptions, setDestinationOptions] = useState[]>([]); const [isSelectAccountModalOpen, setSelectAccountModalOpen] = useState(false); const { @@ -123,7 +123,14 @@ export const TransferForm = ({ const destinationOptions = destinations.map(getChainOption); if (destinationOptions.length) { - setDestinationOptions(destinationOptions); + const [first, ...rest] = destinationOptions; + + setDestinationOptions([ + getPlaceholder(t('transfer.onChainPlaceholder')), + first, + getPlaceholder(t('transfer.crossChainPlaceholder')), + ...rest, + ]); } setValue('destinationChain', destinationOptions[0]); diff --git a/src/shared/locale/en.json b/src/shared/locale/en.json index 1c660cab78..eefc50cccb 100644 --- a/src/shared/locale/en.json +++ b/src/shared/locale/en.json @@ -771,6 +771,7 @@ "transfer": { "amountPlaceholder": "0", "continueButton": "Continue", + "crossChainPlaceholder": "Cross-chain", "descriptionLengthError": "Description must be { maxLength } symbols long", "destinationChainLabel": "Recipient network", "destinationChainPlaceholder": "Select recipient network", @@ -792,6 +793,7 @@ "notEnoughBalanceError": "Not enough funds to transfer specific amount", "notEnoughBalanceForDepositError": "Insufficient balance to pay deposit and network fee", "notEnoughBalanceForFeeError": "Insufficient balance to pay network fee", + "onChainPlaceholder": "On-chain", "pasteButton": "Paste", "recipientLabel": "Recipient", "recipientPlaceholder": "Address", diff --git a/src/shared/locale/ru.json b/src/shared/locale/ru.json index 0955f1b1d5..a18dab26d9 100644 --- a/src/shared/locale/ru.json +++ b/src/shared/locale/ru.json @@ -771,6 +771,7 @@ "transfer": { "amountPlaceholder": "0", "continueButton": "Продолжить", + "crossChainPlaceholder": "Cross-chain", "descriptionLengthError": "Описание может быть { maxLength } символов в длину", "destinationChainLabel": "Recipient network", "destinationChainPlaceholder": "Select recipient network", @@ -792,6 +793,7 @@ "notEnoughBalanceError": "Недостаточно средств для указанной суммы", "notEnoughBalanceForDepositError": "Недостаточно средств для оплаты депозита и комиссии сети", "notEnoughBalanceForFeeError": "Недостаточно средств для оплаты комиссии", + "onChainPlaceholder": "On-chain", "pasteButton": "Вставить", "recipientLabel": "Получатель", "recipientPlaceholder": "Address", From 96b671debc8d3f9ee584cc17c86667ec57e27263 Mon Sep 17 00:00:00 2001 From: Aleksandr Makhnev Date: Fri, 15 Sep 2023 21:35:46 +0500 Subject: [PATCH 23/34] fix: weight calculation (#1063) --- .../entities/transaction/lib/common/types.ts | 1 + .../transaction/lib/transactionService.ts | 8 +++++ .../components/modals/ApproveTx.tsx | 34 ++++++++++++++----- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/renderer/entities/transaction/lib/common/types.ts b/src/renderer/entities/transaction/lib/common/types.ts index 2dd5cc3856..24e0ad3d36 100644 --- a/src/renderer/entities/transaction/lib/common/types.ts +++ b/src/renderer/entities/transaction/lib/common/types.ts @@ -28,6 +28,7 @@ export type ITransactionService = { ) => void; getTransactionFee: (transaction: Transaction, api: ApiPromise) => Promise; getExtrinsicWeight: (extrinsic: SubmittableExtrinsic<'promise'>) => Promise; + getTxWeight: (transaction: Transaction, api: ApiPromise) => Promise; getTransactionDeposit: (threshold: Threshold, api: ApiPromise) => string; getTransactionHash: (transaction: Transaction, api: ApiPromise) => HashData; decodeCallData: (api: ApiPromise, accountId: Address, callData: CallData) => DecodedTransaction; diff --git a/src/renderer/entities/transaction/lib/transactionService.ts b/src/renderer/entities/transaction/lib/transactionService.ts index 513f8c6ee7..8bcdbae47d 100644 --- a/src/renderer/entities/transaction/lib/transactionService.ts +++ b/src/renderer/entities/transaction/lib/transactionService.ts @@ -468,6 +468,13 @@ export const useTransaction = (): ITransactionService => { return paymentInfo.weight; }; + const getTxWeight = async (transaction: Transaction, api: ApiPromise): Promise => { + const extrinsic = getExtrinsic[transaction.type](transaction.args, api); + const { weight } = await extrinsic.paymentInfo(transaction.address); + + return weight; + }; + const getTransactionDeposit = (threshold: Threshold, api: ApiPromise): string => { const { depositFactor, depositBase } = api.consts.multisig; const deposit = depositFactor.muln(threshold).add(depositBase); @@ -563,6 +570,7 @@ export const useTransaction = (): ITransactionService => { submitAndWatchExtrinsic, getTransactionFee, getExtrinsicWeight, + getTxWeight, getTransactionDeposit, getTransactionHash, decodeCallData, diff --git a/src/renderer/pages/Operations/components/modals/ApproveTx.tsx b/src/renderer/pages/Operations/components/modals/ApproveTx.tsx index 7e60645bf2..da8c2f76a5 100644 --- a/src/renderer/pages/Operations/components/modals/ApproveTx.tsx +++ b/src/renderer/pages/Operations/components/modals/ApproveTx.tsx @@ -27,6 +27,7 @@ import { useTransaction, validateBalance, isXcmTransaction, + MAX_WEIGHT, } from '@renderer/entities/transaction'; type Props = { @@ -47,7 +48,7 @@ const ApproveTx = ({ tx, account, connection }: Props) => { const { t } = useI18n(); const { getBalance } = useBalance(); const { getLiveAccounts } = useAccount(); - const { getTransactionFee, getExtrinsicWeight } = useTransaction(); + const { getTransactionFee, getExtrinsicWeight, getTxWeight } = useTransaction(); const { getTxFromCallData } = useCallDataDecoder(); const { getLiveTxEvents } = useMultisigEvent({}); const events = getLiveTxEvents(tx.accountId, tx.chainId, tx.callHash, tx.blockCreated, tx.indexCreated); @@ -89,12 +90,27 @@ const ApproveTx = ({ tx, account, connection }: Props) => { setApproveTx(getMultisigTx(signAccount?.accountId)); }, [tx, signAccount?.accountId, txWeight]); - useEffect(() => { - if (!tx.callData || !connection.api) return; + const initWeight = async () => { + let weight; + try { + if (!tx.callData || !connection.api) return; + + const transaction = getTxFromCallData(connection.api, tx.callData); + + weight = await getExtrinsicWeight(transaction); + } catch (e) { + if (tx.transaction?.args && connection.api) { + weight = await getTxWeight(tx.transaction as Transaction, connection.api); + } else { + weight = connection.api?.createType('Weight', MAX_WEIGHT); + } + } - const transaction = getTxFromCallData(connection.api, tx.callData); + setTxWeight(weight); + }; - getExtrinsicWeight(transaction).then(setTxWeight); + useEffect(() => { + initWeight(); }, [tx.transaction, connection.api]); const goBack = () => { @@ -200,9 +216,11 @@ const ApproveTx = ({ tx, account, connection }: Props) => { return ( <> - + {txWeight && ( + + )} Date: Mon, 18 Sep 2023 11:02:06 +0300 Subject: [PATCH 24/34] Fix: Approve xcm (#1062) --- .../entities/chain/ui/XcmChains/XcmChains.tsx | 2 +- .../SelectableSignatory.tsx | 1 - .../sign/ui/VaultSigning/VaultSigning.tsx | 83 +++++++++---------- .../components/ActionSteps/Confirmation.tsx | 4 +- .../AccountSelectModal/AccountSelectModal.tsx | 9 +- .../components/modals/ApproveTx.tsx | 31 +++---- .../Operations/components/modals/RejectTx.tsx | 6 +- .../modals/SignatorySelectModal.tsx | 44 +++++----- src/renderer/shared/lib/utils/balance.ts | 10 +-- 9 files changed, 92 insertions(+), 98 deletions(-) diff --git a/src/renderer/entities/chain/ui/XcmChains/XcmChains.tsx b/src/renderer/entities/chain/ui/XcmChains/XcmChains.tsx index 57efb5c765..c729e831e5 100644 --- a/src/renderer/entities/chain/ui/XcmChains/XcmChains.tsx +++ b/src/renderer/entities/chain/ui/XcmChains/XcmChains.tsx @@ -11,7 +11,7 @@ type Props = { export const XcmChains = ({ chainIdFrom, chainIdTo, className }: Props) => { if (!chainIdTo) { - return ; + return ; } return ( diff --git a/src/renderer/entities/signatory/ui/SelectableSignatory/SelectableSignatory.tsx b/src/renderer/entities/signatory/ui/SelectableSignatory/SelectableSignatory.tsx index db60e24b1d..5f64648ffe 100644 --- a/src/renderer/entities/signatory/ui/SelectableSignatory/SelectableSignatory.tsx +++ b/src/renderer/entities/signatory/ui/SelectableSignatory/SelectableSignatory.tsx @@ -27,7 +27,6 @@ export const SelectableSignatory = ({ const { getLiveBalance } = useBalance(); const popoverItems = useAddressInfo(address, explorers, true); - const balance = getLiveBalance(toAccountId(address), chainId, asset.assetId.toString()); return ( diff --git a/src/renderer/features/operation/sign/ui/VaultSigning/VaultSigning.tsx b/src/renderer/features/operation/sign/ui/VaultSigning/VaultSigning.tsx index c519abd9e9..5dcf53050e 100644 --- a/src/renderer/features/operation/sign/ui/VaultSigning/VaultSigning.tsx +++ b/src/renderer/features/operation/sign/ui/VaultSigning/VaultSigning.tsx @@ -26,7 +26,6 @@ export const VaultSigning = ({ const [countdown, resetCountdown] = useCountdown(api); const [unsignedTxs, setUnsignedTxs] = useState([]); const [txPayloads, setTxPayloads] = useState([]); - const [validationError, setValidationError] = useState(); const isScanStep = !unsignedTxs.length && !txPayloads.length; @@ -38,7 +37,7 @@ export const VaultSigning = ({ } }, [countdown]); - const handleSignature = async (data: string | string[]) => { + const handleSignature = async (data: string | string[]): Promise => { const isMultishard = Array.isArray(data); const signatures = isMultishard ? (data as HexString[]) : [data as HexString]; const accountIds = isMultiframe ? accounts.map((t) => t.accountId) : [(signatory || accounts[0])?.accountId]; @@ -75,43 +74,45 @@ export const VaultSigning = ({ setTxPayloads([]); }; - const ScanStep = ( -
        - {isMultiframe ? ( - { - setUnsignedTxs(unsignedTx); - setTxPayloads(payloads); - }} - /> - ) : ( - { - setUnsignedTxs([unsignedTx]); - setTxPayloads([payload]); - }} - /> - )} -
        - ); + if (isScanStep) { + return ( +
        + {isMultiframe ? ( + { + setUnsignedTxs(unsignedTx); + setTxPayloads(payloads); + }} + /> + ) : ( + { + setUnsignedTxs([unsignedTx]); + setTxPayloads([payload]); + }} + /> + )} +
        + ); + } - const SignStep = ( + return (
        ); - - if (isScanStep) { - return ScanStep; - } - - return SignStep; }; diff --git a/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx b/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx index c4952f5a9d..5933e162c9 100644 --- a/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx +++ b/src/renderer/pages/Operations/components/ActionSteps/Confirmation.tsx @@ -15,7 +15,7 @@ type Props = { connection: ExtendedChain; feeTx?: Transaction; }; -const Confirmation = ({ tx, account, connection, feeTx }: Props) => { +export const Confirmation = ({ tx, account, connection, feeTx }: Props) => { const { t } = useI18n(); const iconName = getIconName(tx.transaction); @@ -53,5 +53,3 @@ const Confirmation = ({ tx, account, connection, feeTx }: Props) => {
        ); }; - -export default Confirmation; diff --git a/src/renderer/pages/Operations/components/modals/AccountSelectModal/AccountSelectModal.tsx b/src/renderer/pages/Operations/components/modals/AccountSelectModal/AccountSelectModal.tsx index adb37ca6a2..51d0ce70a5 100644 --- a/src/renderer/pages/Operations/components/modals/AccountSelectModal/AccountSelectModal.tsx +++ b/src/renderer/pages/Operations/components/modals/AccountSelectModal/AccountSelectModal.tsx @@ -3,13 +3,14 @@ import { useI18n } from '@renderer/app/providers'; import { AccountDS } from '@renderer/shared/api/storage'; import { Chain } from '@renderer/entities/chain'; import { SelectableAccount } from './SelectableAccount'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { isOpen: boolean; - onClose: () => void; - onSelect: (account: AccountDS) => void; - accounts: AccountDS[]; chain: Chain; + accounts: AccountDS[]; + onSelect: (account: AccountDS) => void; + onClose: () => void; }; const AccountSelectModal = ({ isOpen, onClose, onSelect, accounts, chain }: Props) => { @@ -24,7 +25,7 @@ const AccountSelectModal = ({ isOpen, onClose, onSelect, accounts, chain }: Prop panelClass="w-[368px]" onClose={onClose} > -
          +
            7 && 'max-h-[332px] overflow-y-auto')}> {accounts.map((a) => (
          • { const unsignedAccounts = accounts.filter((a) => { const isSignatory = account.signatories.find((s) => s.accountId === a.accountId); - const notSigned = !events.find((e) => e.accountId === a.accountId); + const isSigned = events.some((e) => e.accountId === a.accountId); const isCurrentChain = !a.chainId || a.chainId === tx.chainId; - const notWatchOnly = account.signingType !== SigningType.WATCH_ONLY; + const isWatchOnly = a.signingType === SigningType.WATCH_ONLY; - return isSignatory && notSigned && isCurrentChain && notWatchOnly; + return isSignatory && !isSigned && isCurrentChain && !isWatchOnly; }); useEffect(() => { @@ -171,8 +171,9 @@ const ApproveTx = ({ tx, account, connection }: Props) => { return new BN(fee).lte(new BN(transferableAmount(balance))); }; - const handleAccountSelect = async (account: Account) => { + const selectSignerAccount = async (account: Account) => { setSignAccount(account); + toggleSelectAccountModal(); const isValid = await validateBalanceForFee(account); @@ -181,11 +182,9 @@ const ApproveTx = ({ tx, account, connection }: Props) => { } else { toggleFeeModal(); } - - toggleSelectAccountModal(); }; - const selectAccount = () => { + const trySetSignerAccount = () => { if (unsignedAccounts.length === 1) { setSignAccount(unsignedAccounts[0]); setActiveStep(Step.SIGNING); @@ -199,7 +198,7 @@ const ApproveTx = ({ tx, account, connection }: Props) => { api: connection.api, chainId: tx.chainId, transaction: approveTx, - assetId: nativeAsset?.assetId.toString(), + assetId: nativeAsset.assetId.toString(), getBalance, getTransactionFee, }); @@ -234,7 +233,11 @@ const ApproveTx = ({ tx, account, connection }: Props) => { {activeStep === Step.CONFIRMATION && ( <> - @@ -258,9 +261,9 @@ const ApproveTx = ({ tx, account, connection }: Props) => { isOpen={isSelectAccountModalOpen} accounts={unsignedAccounts} chain={connection} - asset={nativeAsset} + nativeAsset={nativeAsset} onClose={toggleSelectAccountModal} - onSelect={handleAccountSelect} + onSelect={selectSignerAccount} /> { api: connection.api, chainId: tx.chainId, transaction: rejectTx, - assetId: nativeAsset?.assetId.toString(), + assetId: nativeAsset.assetId.toString(), getBalance, getTransactionFee, }); diff --git a/src/renderer/pages/Operations/components/modals/SignatorySelectModal.tsx b/src/renderer/pages/Operations/components/modals/SignatorySelectModal.tsx index c12c6cbce1..c0b6194db9 100644 --- a/src/renderer/pages/Operations/components/modals/SignatorySelectModal.tsx +++ b/src/renderer/pages/Operations/components/modals/SignatorySelectModal.tsx @@ -4,46 +4,44 @@ import { AccountDS } from '@renderer/shared/api/storage'; import { Asset } from '@renderer/entities/asset'; import { Chain } from '@renderer/entities/chain'; import { SelectableSignatory } from '@renderer/entities/signatory'; +import { cnTw } from '@renderer/shared/lib/utils'; type Props = { isOpen: boolean; + chain: Chain; + nativeAsset: Asset; + accounts: AccountDS[]; onClose: () => void; onSelect: (account: AccountDS) => void; - accounts: AccountDS[]; - chain: Chain; - asset?: Asset; }; -const SignatorySelectModal = ({ isOpen, onClose, onSelect, accounts, asset, chain }: Props) => { +export const SignatorySelectModal = ({ isOpen, onClose, onSelect, accounts, nativeAsset, chain }: Props) => { const { t } = useI18n(); return ( -
              - {asset && - accounts.map((a) => ( -
            • - -
            • - ))} +
                7 && 'max-h-[332px] overflow-y-auto')}> + {accounts.map((account) => ( +
              • + +
              • + ))}
              ); }; - -export default SignatorySelectModal; diff --git a/src/renderer/shared/lib/utils/balance.ts b/src/renderer/shared/lib/utils/balance.ts index 6acc3914bd..b652b71a47 100644 --- a/src/renderer/shared/lib/utils/balance.ts +++ b/src/renderer/shared/lib/utils/balance.ts @@ -90,9 +90,10 @@ export const formatBalance = (balance = '0', precision = 0): FormattedBalance => export const totalAmount = (balance?: Balance): string => { if (!balance) return ZERO_BALANCE; - const { free = ZERO_BALANCE, reserved = ZERO_BALANCE } = balance; + const bnFree = new BN(balance.free || ZERO_BALANCE); + const bnReserved = new BN(balance.reserved || ZERO_BALANCE); - return new BN(free).add(new BN(reserved)).toString(); + return bnFree.add(bnReserved).toString(); }; export const lockedAmount = ({ locked = [] }: Balance): string => { @@ -105,9 +106,8 @@ export const lockedAmount = ({ locked = [] }: Balance): string => { export const transferableAmount = (balance?: Balance): string => { if (!balance) return ZERO_BALANCE; - const { free = ZERO_BALANCE, frozen = ZERO_BALANCE } = balance; - const bnFree = new BN(free); - const bnFrozen = new BN(frozen); + const bnFree = new BN(balance.free || ZERO_BALANCE); + const bnFrozen = new BN(balance.frozen || ZERO_BALANCE); return bnFree.gt(bnFrozen) ? bnFree.sub(bnFrozen).toString() : ZERO_BALANCE; }; From a64afa829330feb6b228873d21d4dcdfe0a09b24 Mon Sep 17 00:00:00 2001 From: Aleksandr Makhnev Date: Mon, 18 Sep 2023 15:40:29 +0500 Subject: [PATCH 25/34] fix: fee calculation for big multishard wallet (#1066) * fix: fee calculation for big multishard wallet * fix: amount with fee calculation --- .../widgets/SendAssetModal/model/send-asset.ts | 4 ++-- .../SendAssetModal/ui/components/TransferForm.tsx | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/renderer/widgets/SendAssetModal/model/send-asset.ts b/src/renderer/widgets/SendAssetModal/model/send-asset.ts index 1b1bb28d18..3dfa18e648 100644 --- a/src/renderer/widgets/SendAssetModal/model/send-asset.ts +++ b/src/renderer/widgets/SendAssetModal/model/send-asset.ts @@ -203,9 +203,9 @@ sample({ xcmFee: $xcmFee, }, fn: ({ props: { api }, xcmAsset, config, amount, xcmFee }) => { - if (!api || !xcmAsset || !amount || !config || !xcmFee) return null; + if (!api || !xcmAsset || !config) return null; - const resultAmount = new BN(amount).add(new BN(xcmFee)); + const resultAmount = new BN(amount || 0).add(new BN(xcmFee || 0)); return getAssetLocation(api, xcmAsset, config.assetsLocation, resultAmount) || null; }, diff --git a/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx b/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx index 026ad555b6..acc8046230 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx @@ -217,7 +217,19 @@ export const TransferForm = ({ setTransferTx(transferPayload); onTxChange(transferPayload); - }, [account, signer, destination, amount, destinationChain, isXcmValid, isXcmTransfer]); + }, [ + account, + signer, + destination, + amount, + destinationChain, + isXcmValid, + isXcmTransfer, + xcmParams.fee, + xcmParams.asset, + xcmParams.beneficiary, + xcmParams.dest, + ]); const getXcmTransferType = (type: XcmTransferType): TransactionType => { if (type === 'xtokens') { From c1087d53d098c84323292bffcbf434b9fe084554 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Mon, 18 Sep 2023 14:01:32 +0300 Subject: [PATCH 26/34] fix: reject with wow duplicate (#1064) --- .../pages/Operations/components/modals/RejectTx.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/renderer/pages/Operations/components/modals/RejectTx.tsx b/src/renderer/pages/Operations/components/modals/RejectTx.tsx index 8dc60422eb..df6adb3b29 100644 --- a/src/renderer/pages/Operations/components/modals/RejectTx.tsx +++ b/src/renderer/pages/Operations/components/modals/RejectTx.tsx @@ -8,7 +8,7 @@ import { AccountDS, MultisigTransactionDS } from '@renderer/shared/api/storage'; import { useToggle } from '@renderer/shared/lib/hooks'; import { MultisigAccount, useAccount } from '@renderer/entities/account'; import { ExtendedChain } from '@renderer/entities/network'; -import { Address, HexString, Timepoint } from '@renderer/domain/shared-kernel'; +import { Address, HexString, Timepoint, SigningType } from '@renderer/domain/shared-kernel'; import { toAddress, transferableAmount, getAssetById } from '@renderer/shared/lib/utils'; import { getModalTransactionTitle } from '../../common/utils'; import { useBalance } from '@renderer/entities/asset'; @@ -58,13 +58,19 @@ const RejectTx = ({ tx, account, connection }: Props) => { const [rejectReason, setRejectReason] = useState(''); const [signature, setSignature] = useState(); - const accounts = getLiveAccounts(); - const signAccount = accounts.find((a) => a.accountId === tx.depositor); const transactionTitle = getModalTransactionTitle(isXcmTransaction(tx.transaction), tx.transaction); const nativeAsset = connection.assets[0]; const asset = getAssetById(tx.transaction?.args.assetId, connection.assets); + const accounts = getLiveAccounts(); + const signAccount = accounts.find((a) => { + const isDepositor = a.accountId === tx.depositor; + const isWatchOnly = a.signingType === SigningType.WATCH_ONLY; + + return isDepositor && !isWatchOnly; + }); + const checkBalance = () => validateBalance({ api: connection.api, From 4f0e14955cec2e4f9a476d6c6f3ab26db4305e45 Mon Sep 17 00:00:00 2001 From: Egor B Date: Mon, 18 Sep 2023 13:45:33 +0200 Subject: [PATCH 27/34] feat: turned off electron updater --- src/main/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/index.ts b/src/main/index.ts index 1b855714cf..aa9d4c70a6 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -6,6 +6,7 @@ import { MainWindow } from './main'; import { makeAppWithSingleInstanceLock } from './factories/instance'; import { makeAppSetup } from './factories/setup'; +// @ts-ignore const setupAutoUpdate = () => { if (process.env.BUILD_SOURCE !== 'github') return; @@ -66,7 +67,7 @@ const setupAutoUpdate = () => { makeAppWithSingleInstanceLock(async () => { app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required'); - setupAutoUpdate(); + // setupAutoUpdate(); await app.whenReady(); await makeAppSetup(MainWindow); From ff8b1f1deeed00caf0f630987b1a0bb3c1f98621 Mon Sep 17 00:00:00 2001 From: Stepan Lavrentev <40560660+stepanLav@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:52:58 +0300 Subject: [PATCH 28/34] fix: update workflow (#1065) --- .github/workflows/update_chains_file.yaml | 2 +- src/renderer/assets/chains/chains.json | 629 +++++++++++++----- src/renderer/assets/chains/chains_dev.json | 718 ++++++++++++++++----- 3 files changed, 1022 insertions(+), 327 deletions(-) diff --git a/.github/workflows/update_chains_file.yaml b/.github/workflows/update_chains_file.yaml index 6baf66bd8e..f71f4e8d47 100644 --- a/.github/workflows/update_chains_file.yaml +++ b/.github/workflows/update_chains_file.yaml @@ -37,7 +37,7 @@ jobs: - name: ➡️ Make pull request uses: ./.github/workflows/make-pull-request with: - commit_path: src/renderer/services/network/common/*.json + commit_path: src/renderer/assets/chains/*.json commit_message: "ci: chains.json file" pr_title: "Update chains.json file" branch_name: update-chains-file diff --git a/src/renderer/assets/chains/chains.json b/src/renderer/assets/chains/chains.json index 81746b5b98..b15029d911 100644 --- a/src/renderer/assets/chains/chains.json +++ b/src/renderer/assets/chains/chains.json @@ -5,6 +5,10 @@ "chainId": "0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Polkadot.svg", "nodes": [ + { + "url": "wss://polkadot-rpc.dwellir.com", + "name": "Dwellir node" + }, { "url": "wss://rpc.polkadot.io", "name": "Parity node" @@ -14,20 +18,16 @@ "name": "LuckyFriday node" }, { - "url": "wss://polkadot.api.onfinality.io/public-ws", - "name": "OnFinality node" + "url": "wss://rpc.ibp.network/polkadot", + "name": "IBP network node" }, { "url": "wss://rpc.dotters.network/polkadot", "name": "Dotters Net node" }, { - "url": "wss://rpc.ibp.network/polkadot", - "name": "IBP network node" - }, - { - "url": "wss://polkadot-rpc.dwellir.com", - "name": "Dwellir node" + "url": "wss://polkadot.api.onfinality.io/public-ws", + "name": "OnFinality node" }, { "url": "wss://1rpc.io/dot", @@ -50,12 +50,10 @@ "name": "Subscan", "extrinsic": "https://polkadot.subscan.io/extrinsic/{hash}", "account": "https://polkadot.subscan.io/account/{address}", - "event": null, "multisig": "https://polkadot.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { "name": "Polkascan", - "extrinsic": "https://polkascan.io/polkadot/extrinsic/{hash}", "account": "https://polkascan.io/polkadot/account/{address}", "event": "https://polkascan.io/polkadot/event/{event}" }, @@ -94,6 +92,10 @@ "url": "wss://rpc.ibp.network/kusama", "name": "IBP network node" }, + { + "url": "wss://rpc.dotters.network/kusama", + "name": "Dotters Net node" + }, { "url": "wss://1rpc.io/ksm", "name": "Automata 1RPC node" @@ -102,10 +104,6 @@ "url": "wss://kusama-rpc.dwellir.com", "name": "Dwellir node" }, - { - "url": "wss://rpc.dotters.network/kusama", - "name": "Dotters Net node" - }, { "url": "wss://kusama.public.curie.radiumblock.co/ws", "name": "Radium node" @@ -139,12 +137,10 @@ "name": "Subscan", "extrinsic": "https://kusama.subscan.io/extrinsic/{hash}", "account": "https://kusama.subscan.io/account/{address}", - "event": null, "multisig": "https://kusama.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { "name": "Polkascan", - "extrinsic": "https://polkascan.io/kusama/extrinsic/{hash}", "account": "https://polkascan.io/kusama/account/{address}", "event": "https://polkascan.io/kusama/event/{event}" }, @@ -180,6 +176,14 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Westend_Testnet.svg", "options": ["testnet"], "nodes": [ + { + "url": "wss://rpc.ibp.network/westend", + "name": "IBP network node" + }, + { + "url": "wss://rpc.dotters.network/westend", + "name": "Dotters Net node" + }, { "url": "wss://westend-rpc.polkadot.io", "name": "Parity node" @@ -208,7 +212,6 @@ "name": "Subscan", "extrinsic": "https://westend.subscan.io/extrinsic/{hash}", "account": "https://westend.subscan.io/account/{address}", - "event": null, "multisig": "https://westend.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" } ], @@ -234,6 +237,14 @@ "parentId": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Kusama_Asset_Hub.svg", "nodes": [ + { + "url": "wss://sys.ibp.network/statemine", + "name": "IBP network node" + }, + { + "url": "wss://sys.dotters.network/statemine", + "name": "Dotters Net node" + }, { "url": "wss://statemine.api.onfinality.io/public-ws", "name": "OnFinality node" @@ -330,7 +341,6 @@ "name": "Subscan", "extrinsic": "https://statemine.subscan.io/extrinsic/{hash}", "account": "https://statemine.subscan.io/account/{address}", - "event": null, "multisig": "https://statemine.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -397,18 +407,18 @@ }, { "assetId": 1, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x0081", "currencyIdType": "AcalaPrimitivesCurrencyCurrencyId", "existentialDeposit": "10000000000", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 2, @@ -771,7 +781,6 @@ "name": "Subscan", "extrinsic": "https://karura.subscan.io/extrinsic/{hash}", "account": "https://karura.subscan.io/account/{address}", - "event": null, "multisig": "https://karura.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -896,15 +905,15 @@ }, { "assetId": 7, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "statemine", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "assetId": "18446744073709551616" }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 8, @@ -948,7 +957,6 @@ "name": "Subscan", "extrinsic": "https://shiden.subscan.io/extrinsic/{hash}", "account": "https://shiden.subscan.io/account/{address}", - "event": null, "multisig": "https://shiden.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1061,18 +1069,18 @@ }, { "assetId": 5, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x0302", "currencyIdType": "NodePrimitivesCurrencyCurrencyId", "existentialDeposit": "100000000", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 6, @@ -1161,6 +1169,34 @@ "transfersEnabled": true }, "name": "KINT Bitcoin" + }, + { + "assetId": 12, + "symbol": "vBNC", + "precision": 12, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vBNC.svg", + "typeExtras": { + "currencyIdScale": "0x0101", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "10000000000", + "transfersEnabled": true + }, + "name": "vBNC" + }, + { + "assetId": 13, + "symbol": "vMOVR", + "precision": 18, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vMOVR.svg", + "typeExtras": { + "currencyIdScale": "0x010a", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "1000000000000", + "transfersEnabled": true + }, + "name": "vMOVR" } ], "explorers": [ @@ -1168,7 +1204,6 @@ "name": "Subscan", "extrinsic": "https://bifrost-kusama.subscan.io/extrinsic/{hash}", "account": "https://bifrost-kusama.subscan.io/account/{address}", - "event": null, "multisig": "https://bifrost-kusama.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1232,18 +1267,18 @@ }, { "assetId": 2, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x02000000", "currencyIdType": "u32", "existentialDeposit": "10000000000", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 3, @@ -1295,7 +1330,6 @@ "name": "Subscan", "extrinsic": "https://basilisk.subscan.io/extrinsic/{hash}", "account": "https://basilisk.subscan.io/account/{address}", - "event": null, "multisig": "https://basilisk.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1348,7 +1382,6 @@ "name": "Subscan", "extrinsic": "https://altair.subscan.io/extrinsic/{hash}", "account": "https://altair.subscan.io/account/{address}", - "event": null, "multisig": "https://altair.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1404,15 +1437,15 @@ }, { "assetId": 2, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "statemine", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "assetId": "103" }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 3, @@ -1503,7 +1536,6 @@ "name": "Subscan", "extrinsic": "https://parallel-heiko.subscan.io/extrinsic/{hash}", "account": "https://parallel-heiko.subscan.io/account/{address}", - "event": null, "multisig": "https://parallel-heiko.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1555,7 +1587,6 @@ "name": "Subscan", "extrinsic": "https://edgeware.subscan.io/extrinsic/{hash}", "account": "https://edgeware.subscan.io/account/{address}", - "event": null, "multisig": "https://edgeware.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1643,15 +1674,15 @@ }, { "assetId": 5, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "statemine", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "assetId": "4" }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 6, @@ -1717,7 +1748,6 @@ "name": "Subscan", "extrinsic": "https://khala.subscan.io/extrinsic/{hash}", "account": "https://khala.subscan.io/account/{address}", - "event": null, "multisig": "https://khala.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1770,7 +1800,6 @@ "name": "Subscan", "extrinsic": "https://spiritnet.subscan.io/extrinsic/{hash}", "account": "https://spiritnet.subscan.io/account/{address}", - "event": null, "multisig": "https://spiritnet.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1810,7 +1839,6 @@ "symbol": "KMA", "precision": 12, "priceId": "calamari-network", - "staking": "parachain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Calamari_(KMA).svg", "name": "Calamari Network" } @@ -1820,7 +1848,6 @@ "name": "Subscan", "extrinsic": "https://calamari.subscan.io/extrinsic/{hash}", "account": "https://calamari.subscan.io/account/{address}", - "event": null, "multisig": "https://calamari.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1887,7 +1914,6 @@ "name": "Subscan", "extrinsic": "https://quartz.subscan.io/extrinsic/{hash}", "account": "https://quartz.subscan.io/account/{address}", - "event": null, "multisig": "https://quartz.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1950,7 +1976,6 @@ "name": "Subscan", "extrinsic": "https://pioneer.subscan.io/extrinsic/{hash}", "account": "https://pioneer.subscan.io/account/{address}", - "event": null, "multisig": "https://pioneer.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2026,18 +2051,18 @@ }, { "assetId": 2, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-acala", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x0001", "currencyIdType": "AcalaPrimitivesCurrencyCurrencyId", "existentialDeposit": "100000000000", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 3, @@ -2189,6 +2214,21 @@ }, { "assetId": 13, + "symbol": "DAI", + "precision": 18, + "type": "orml", + "priceId": "dai", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/DAI.svg", + "typeExtras": { + "currencyIdScale": "0x0254a37a01cd75b616d63e0ab665bffdb0143c52ae", + "currencyIdType": "AcalaPrimitivesCurrencyCurrencyId", + "existentialDeposit": "10000000000000000", + "transfersEnabled": true + }, + "name": "DAI" + }, + { + "assetId": 14, "symbol": "USDT", "precision": 6, "type": "orml", @@ -2208,7 +2248,6 @@ "name": "Subscan", "extrinsic": "https://acala.subscan.io/extrinsic/{hash}", "account": "https://acala.subscan.io/account/{address}", - "event": null, "multisig": "https://acala.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2345,15 +2384,15 @@ }, { "assetId": 8, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "statemine", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-acala", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "assetId": "18446744073709551617" }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 9, @@ -2373,7 +2412,6 @@ "name": "Subscan", "extrinsic": "https://astar.subscan.io/extrinsic/{hash}", "account": "https://astar.subscan.io/account/{address}", - "event": null, "multisig": "https://astar.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2454,15 +2492,15 @@ }, { "assetId": 4, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "statemine", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-acala", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "assetId": "104" }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 5, @@ -2620,7 +2658,6 @@ "name": "Subscan", "extrinsic": "https://parallel.subscan.io/extrinsic/{hash}", "account": "https://parallel.subscan.io/account/{address}", - "event": null, "multisig": "https://parallel.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2669,7 +2706,6 @@ "name": "Subscan", "extrinsic": "https://clv.subscan.io/extrinsic/{hash}", "account": "https://clv.subscan.io/account/{address}", - "event": null, "multisig": "https://clv.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2694,6 +2730,14 @@ "parentId": "0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Polkadot_Asset_Hub.svg", "nodes": [ + { + "url": "wss://sys.ibp.network/statemint", + "name": "IBP network node" + }, + { + "url": "wss://sys.dotters.network/statemint", + "name": "Dotters Net node" + }, { "url": "wss://statemint-rpc.polkadot.io", "name": "Parity node" @@ -2727,6 +2771,18 @@ "assetId": "1984" }, "name": "USD Tether" + }, + { + "assetId": 2, + "symbol": "USDC", + "precision": 6, + "type": "statemine", + "priceId": "usd-coin", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/USDC.svg", + "typeExtras": { + "assetId": "1337" + }, + "name": "USD Coin" } ], "explorers": [ @@ -2782,7 +2838,6 @@ "name": "Subscan", "extrinsic": "https://robonomics.subscan.io/extrinsic/{hash}", "account": "https://robonomics.subscan.io/account/{address}", - "event": null, "multisig": "https://robonomics.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2811,6 +2866,14 @@ "parentId": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Encointer.svg", "nodes": [ + { + "url": "wss://sys.ibp.network/encointer-kusama", + "name": "IBP network node" + }, + { + "url": "wss://sys.dotters.network/encointer-kusama", + "name": "Dotters Net node" + }, { "url": "wss://api.kusama.encointer.org", "name": "Encointer association node" @@ -2835,7 +2898,6 @@ "name": "Subscan", "extrinsic": "https://encointer.subscan.io/extrinsic/{hash}", "account": "https://encointer.subscan.io/account/{address}", - "event": null, "multisig": "https://encointer.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2943,18 +3005,18 @@ }, { "assetId": 5, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x0101000000", "currencyIdType": "InterbtcPrimitivesCurrencyId", "existentialDeposit": "0", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 6, @@ -3046,7 +3108,6 @@ "name": "Subscan", "extrinsic": "https://kintsugi.subscan.io/extrinsic/{hash}", "account": "https://kintsugi.subscan.io/account/{address}", - "event": null, "multisig": "https://kintsugi.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3143,7 +3204,6 @@ "name": "Subscan", "extrinsic": "https://picasso.subscan.io/extrinsic/{hash}", "account": "https://picasso.subscan.io/account/{address}", - "event": null, "multisig": "https://picasso.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3183,7 +3243,6 @@ "symbol": "ZTG", "precision": 10, "priceId": "zeitgeist", - "staking": "parachain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Zeitgeist_(ZTG).svg", "name": "Zeitgeist" } @@ -3193,7 +3252,6 @@ "name": "Subscan", "extrinsic": "https://zeitgeist.subscan.io/extrinsic/{hash}", "account": "https://zeitgeist.subscan.io/account/{address}", - "event": null, "multisig": "https://zeitgeist.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3248,7 +3306,6 @@ "name": "Subscan", "extrinsic": "https://litmus.subscan.io/extrinsic/{hash}", "account": "https://litmus.subscan.io/account/{address}", - "event": null, "multisig": "https://litmus.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3346,7 +3403,6 @@ "name": "Subscan", "extrinsic": "https://shadow.subscan.io/extrinsic/{hash}", "account": "https://shadow.subscan.io/account/{address}", - "event": null, "multisig": "https://shadow.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3395,7 +3451,6 @@ "name": "Subscan", "extrinsic": "https://integritee.subscan.io/extrinsic/{hash}", "account": "https://integritee.subscan.io/account/{address}", - "event": null, "multisig": "https://integritee.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3448,7 +3503,6 @@ "name": "Subscan", "extrinsic": "https://centrifuge-parachain.subscan.io/extrinsic/{hash}", "account": "https://centrifuge-parachain.subscan.io/account/{address}", - "event": null, "multisig": "https://centrifuge-parachain.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3504,7 +3558,6 @@ "name": "Subscan", "extrinsic": "https://efinity.subscan.io/extrinsic/{hash}", "account": "https://efinity.subscan.io/account/{address}", - "event": null, "multisig": "https://efinity.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3564,18 +3617,18 @@ }, { "assetId": 2, - "symbol": "DAI", + "symbol": "DAI-Acala", "precision": 18, "type": "orml", "priceId": "dai", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/DAI.svg", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/DAI-Acala.svg", "typeExtras": { "currencyIdScale": "0x02000000", "currencyIdType": "u32", "existentialDeposit": "10000000000000000", "transfersEnabled": true }, - "name": "DAI" + "name": "DAI on Acala" }, { "assetId": 3, @@ -3593,33 +3646,33 @@ }, { "assetId": 4, - "symbol": "WETH-worm", + "symbol": "WETH-Acala", "precision": 18, "type": "orml", "priceId": "ethereum-wormhole", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WETH-worm.svg", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WETH-Acala.svg", "typeExtras": { "currencyIdScale": "0x04000000", "currencyIdType": "u32", "existentialDeposit": "7000000000000", "transfersEnabled": true }, - "name": "Ethereum wormhol" + "name": "WETH-Acala" }, { "assetId": 5, - "symbol": "WBTC-worm", + "symbol": "WBTC-Acala", "precision": 8, "type": "orml", "priceId": "wrapped-bitcoin", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WBTC-worm.svg", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WBTC-Acala.svg", "typeExtras": { "currencyIdScale": "0x03000000", "currencyIdType": "u32", "existentialDeposit": "44", "transfersEnabled": true }, - "name": "Wrapped BTC" + "name": "WBTC-Acala" }, { "assetId": 6, @@ -3680,6 +3733,81 @@ "transfersEnabled": true }, "name": "USD Tether" + }, + { + "assetId": 10, + "symbol": "CFG", + "precision": 18, + "type": "orml", + "priceId": "centrifuge", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Centrifuge_(CFG).svg", + "typeExtras": { + "currencyIdScale": "0x0d000000", + "currencyIdType": "u32", + "existentialDeposit": "32467532467532500", + "transfersEnabled": true + }, + "name": "Centrifuge" + }, + { + "assetId": 11, + "symbol": "BNC", + "precision": 12, + "type": "orml", + "priceId": "bifrost-native-coin", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Bifrost_(BNC).svg", + "typeExtras": { + "currencyIdScale": "0x0e000000", + "currencyIdType": "u32", + "existentialDeposit": "68795189840", + "transfersEnabled": true + }, + "name": "Bifrost" + }, + { + "assetId": 12, + "symbol": "DAI-Moonbeam", + "precision": 18, + "type": "orml", + "priceId": "dai", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/DAI-Moonbeam.svg", + "typeExtras": { + "currencyIdScale": "0x12000000", + "currencyIdType": "u32", + "existentialDeposit": "10000000000000000", + "transfersEnabled": true + }, + "name": "DAI-Moonbeam" + }, + { + "assetId": 13, + "symbol": "WBTC-Moonbeam", + "precision": 8, + "type": "orml", + "priceId": "wrapped-bitcoin", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WBTC-Moonbeam.svg", + "typeExtras": { + "currencyIdScale": "0x13000000", + "currencyIdType": "u32", + "existentialDeposit": "34", + "transfersEnabled": true + }, + "name": "WBTC-Moonbeam" + }, + { + "assetId": 14, + "symbol": "WETH-Moonbeam", + "precision": 18, + "type": "orml", + "priceId": "ethereum-wormhole", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WETH-Moonbeam.svg", + "typeExtras": { + "currencyIdScale": "0x14000000", + "currencyIdType": "u32", + "existentialDeposit": "7000000000000", + "transfersEnabled": true + }, + "name": "WETH-Moonbeam" } ], "explorers": [ @@ -3687,7 +3815,6 @@ "name": "Subscan", "extrinsic": "https://hydradx.subscan.io/extrinsic/{hash}", "account": "https://hydradx.subscan.io/account/{address}", - "event": null, "multisig": "https://hydradx.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3932,7 +4059,6 @@ "name": "Subscan", "extrinsic": "https://interlay.subscan.io/extrinsic/{hash}", "account": "https://interlay.subscan.io/account/{address}", - "event": null, "multisig": "https://interlay.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3981,7 +4107,6 @@ "name": "Subscan", "extrinsic": "https://nodle.subscan.io/extrinsic/{hash}", "account": "https://nodle.subscan.io/account/{address}", - "event": null, "multisig": "https://nodle.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4038,7 +4163,6 @@ "name": "Subscan", "extrinsic": "https://phala.subscan.io/extrinsic/{hash}", "account": "https://phala.subscan.io/account/{address}", - "event": null, "multisig": "https://phala.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4077,7 +4201,6 @@ "assetId": 0, "symbol": "TUR", "precision": 10, - "staking": "turing", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Turing_(TUR).svg", "name": "Turing" }, @@ -4098,18 +4221,18 @@ }, { "assetId": 2, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x02000000", "currencyIdType": "u32", "existentialDeposit": "10000000000", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 3, @@ -4190,7 +4313,6 @@ "name": "Subscan", "extrinsic": "https://turing.subscan.io/extrinsic/{hash}", "account": "https://turing.subscan.io/account/{address}", - "event": null, "multisig": "https://turing.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4231,7 +4353,6 @@ "symbol": "AZERO", "precision": 12, "priceId": "aleph-zero", - "staking": "aleph-zero", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Aleph_Zero_(AZERO).svg", "name": "Aleph Zero" } @@ -4241,7 +4362,6 @@ "name": "Subscan", "extrinsic": "https://alephzero.subscan.io/extrinsic/{hash}", "account": "https://alephzero.subscan.io/account/{address}", - "event": null, "multisig": "https://alephzero.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" } ], @@ -4335,7 +4455,6 @@ "name": "Subscan", "extrinsic": "https://composable.subscan.io/extrinsic/{hash}", "account": "https://composable.subscan.io/account/{address}", - "event": null, "multisig": "https://composable.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4373,6 +4492,30 @@ "staking": "relaychain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Polkadex_(PDEX).svg", "name": "Polkadex" + }, + { + "assetId": 1, + "symbol": "DOT", + "precision": 12, + "type": "statemine", + "priceId": "polkadot", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Polkadot_(DOT).svg", + "typeExtras": { + "assetId": "95930534000017180603917534864279132680" + }, + "name": "Polkadot" + }, + { + "assetId": 2, + "symbol": "USDT", + "precision": 12, + "type": "statemine", + "priceId": "tether", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/USDT.svg", + "typeExtras": { + "assetId": "3496813586714279103986568049643838918" + }, + "name": "USD Tether" } ], "explorers": [ @@ -4380,7 +4523,6 @@ "name": "Subscan", "extrinsic": "https://polkadex.subscan.io/extrinsic/{hash}", "account": "https://polkadex.subscan.io/account/{address}", - "event": null, "multisig": "https://polkadex.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4532,6 +4674,62 @@ "transfersEnabled": true }, "name": "Astar" + }, + { + "assetId": 5, + "symbol": "vDOT", + "precision": 10, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vDOT.svg", + "typeExtras": { + "currencyIdScale": "0x0900", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "1000000", + "transfersEnabled": true + }, + "name": "vDOT" + }, + { + "assetId": 6, + "symbol": "vGLMR", + "precision": 18, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vGLMR.svg", + "typeExtras": { + "currencyIdScale": "0x0901", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "1000000000000", + "transfersEnabled": true + }, + "name": "vGLMR" + }, + { + "assetId": 7, + "symbol": "vFIL", + "precision": 18, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vFIL.svg", + "typeExtras": { + "currencyIdScale": "0x0904", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "1000000000000", + "transfersEnabled": true + }, + "name": "vFIL" + }, + { + "assetId": 8, + "symbol": "vASTR", + "precision": 18, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vASTR.svg", + "typeExtras": { + "currencyIdScale": "0x0903", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "10000000000000000", + "transfersEnabled": true + }, + "name": "vASTR" } ], "explorers": [ @@ -4539,7 +4737,6 @@ "name": "Subscan", "extrinsic": "https://bifrost.subscan.io/extrinsic/{hash}", "account": "https://bifrost.subscan.io/account/{address}", - "event": null, "multisig": "https://bifrost.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4634,7 +4831,6 @@ "name": "Subscan", "extrinsic": "https://unique.subscan.io/extrinsic/{hash}", "account": "https://unique.subscan.io/account/{address}", - "event": null, "multisig": "https://unique.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4844,7 +5040,6 @@ "name": "Subscan", "extrinsic": "https://mangatax.subscan.io/extrinsic/{hash}", "account": "https://mangatax.subscan.io/account/{address}", - "event": null, "multisig": "https://mangatax.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4922,6 +5117,12 @@ } ], "explorers": [ + { + "name": "Subscan", + "extrinsic": "https://bajun.subscan.io/extrinsic/{hash}", + "account": "https://bajun.subscan.io/account/{address}", + "multisig": "https://bajun.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" + }, { "name": "Polkaholic", "account": "https://polkaholic.io/account/{address}", @@ -5012,7 +5213,7 @@ } }, { - "name": "GM (PAUSED)", + "name": "GM", "addressPrefix": 7013, "chainId": "0x19a3733beb9cb8a970a308d835599e9005e02dc007a35440e461a451466776f8", "parentId": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", @@ -5021,10 +5222,6 @@ { "url": "wss://ws.gm.bldnodes.org", "name": "bLd node" - }, - { - "url": "wss://leemo.gmordie.com", - "name": "Leemo node" } ], "assets": [ @@ -5136,8 +5333,7 @@ { "name": "Ternoa explorer", "extrinsic": "https://explorer.ternoa.com/extrinsic/{hash}", - "account": "https://explorer.ternoa.com/account/{address}", - "event": null + "account": "https://explorer.ternoa.com/account/{address}" } ], "externalApi": { @@ -5201,8 +5397,12 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Totem.svg", "nodes": [ { - "url": "wss://k-ui.kapex.network", - "name": "Totem node" + "url": "wss://kapex-rpc.dwellir.com", + "name": "Dwellir node" + }, + { + "url": "wss://kapex-parachain.api.onfinality.io/public-ws", + "name": "OnFinality node" } ], "assets": [ @@ -5256,7 +5456,6 @@ "name": "Subscan", "extrinsic": "https://polymesh.subscan.io/extrinsic/{hash}", "account": "https://polymesh.subscan.io/account/{address}", - "event": null, "multisig": "https://polymesh.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" } ] @@ -5322,11 +5521,17 @@ "explorers": [ { "name": "Explorer", - "extrinsic": null, - "account": "https://explorer.mainnet.oct.network/myriad/accounts/{address}", - "event": null + "account": "https://explorer.mainnet.oct.network/myriad/accounts/{address}" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-myriad" + } + ] + } }, { "name": "XX network", @@ -5361,14 +5566,12 @@ { "name": "XX explorer", "extrinsic": "https://explorer.xx.network/extrinsics/{hash}", - "account": "https://explorer.xx.network/accounts/{address}", - "event": null + "account": "https://explorer.xx.network/accounts/{address}" }, { "name": "Polkastats", "extrinsic": "https://xx.polkastats.io/extrinsic/{hash}", - "account": "https://xx.polkastats.io/account/{address}", - "event": null + "account": "https://xx.polkastats.io/account/{address}" } ], "externalApi": { @@ -5431,7 +5634,15 @@ }, "name": "USD Tether" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-pendulum" + } + ] + } }, { "name": "Crust", @@ -5460,10 +5671,17 @@ "name": "Subscan", "extrinsic": "https://crust-parachain.subscan.io/extrinsic/{hash}", "account": "https://crust-parachain.subscan.io/account/{address}", - "event": null, "multisig": "https://crust-parachain.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-crust" + } + ] + } }, { "name": "Aventus", @@ -5491,10 +5709,17 @@ { "name": "Aventus explorer", "extrinsic": "https://explorer.mainnet.aventus.io/transaction/{hash}", - "account": "https://explorer.mainnet.aventus.io/address/{address}", - "event": null + "account": "https://explorer.mainnet.aventus.io/address/{address}" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-aventus" + } + ] + } }, { "name": "Hashed Network", @@ -5516,7 +5741,15 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Hashed_Network_(HASH).svg", "name": "Hashed" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-hashed-network" + } + ] + } }, { "name": "Bittensor", @@ -5538,7 +5771,15 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Bittensor_(TAO).svg", "name": "Fusotao token" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-bittensor" + } + ] + } }, { "name": "Ajuna", @@ -5590,10 +5831,17 @@ { "name": "3DPass explorer", "extrinsic": "https://explorer.3dpass.org/extrinsic/{hash}", - "account": "https://explorer.3dpass.org/account/{address}", - "event": null + "account": "https://explorer.3dpass.org/account/{address}" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-3dpass" + } + ] + } }, { "name": "Frequency", @@ -5651,9 +5899,8 @@ "explorers": [ { "name": "Varascan", - "extrinsic": null, "account": "https://explorer.vara-network.io/account/{address}", - "event": null + "event": "https://explorer.vara-network.io/event/{event}" } ] }, @@ -5685,6 +5932,14 @@ "parentId": "0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Polkadot_Bridge_Hub.svg", "nodes": [ + { + "url": "wss://sys.ibp.network/bridgehub-polkadot", + "name": "IBP network node" + }, + { + "url": "wss://sys.dotters.network/bridgehub-polkadot", + "name": "Dotters Net node" + }, { "url": "wss://polkadot-bridge-hub-rpc.polkadot.io", "name": "Parity node" @@ -5712,6 +5967,14 @@ "parentId": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Kusama_Bridge_Hub.svg", "nodes": [ + { + "url": "wss://sys.ibp.network/bridgehub-kusama", + "name": "IBP network node" + }, + { + "url": "wss://sys.dotters.network/bridgehub-kusama", + "name": "Dotters Net node" + }, { "url": "wss://kusama-bridge-hub-rpc.polkadot.io", "name": "Parity node" @@ -5740,20 +6003,20 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Polkadot_Collectives.svg", "nodes": [ { - "url": "wss://polkadot-collectives-rpc.polkadot.io", - "name": "Parity node" + "url": "wss://sys.ibp.network/collectives-polkadot", + "name": "IBP network node" }, { - "url": "wss://collectives.api.onfinality.io/public-ws", - "name": "Onfinality node" + "url": "wss://sys.dotters.network/collectives-polkadot", + "name": "Dotters Net node" }, { - "url": "wss://sys.dotters.network/collectives-polkadot", - "name": "via IBP-GeoDNS2" + "url": "wss://polkadot-collectives-rpc.polkadot.io", + "name": "Parity node" }, { - "url": "wss://sys.ibp.network/collectives-polkadot", - "name": "via IBP-GeoDNS1" + "url": "wss://collectives.api.onfinality.io/public-ws", + "name": "Onfinality node" }, { "url": "wss://collectives.public.curie.radiumblock.co/ws", @@ -5774,5 +6037,69 @@ "name": "Polkadot" } ] + }, + { + "name": "krest", + "addressPrefix": 42, + "chainId": "0xb3dd5ad6a82872b30aabaede8f41dfd4ff6c32ff82f8757d034a45be63cf104c", + "parentId": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/krest.svg", + "nodes": [ + { + "url": "wss://wss-krest.peaq.network", + "name": "Krest node" + } + ], + "assets": [ + { + "assetId": 0, + "symbol": "KREST", + "precision": 18, + "priceId": "krest", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/krest_(krest).svg", + "name": "KREST token" + } + ], + "explorers": [ + { + "name": "Subscan", + "extrinsic": "https://krest.subscan.io/extrinsic/{hash}", + "account": "https://krest.subscan.io/account/{address}", + "multisig": "https://krest.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" + } + ] + }, + { + "name": "Jur", + "addressPrefix": 42, + "chainId": "0x58d1393b47b11707978fbc07e77d7b6f7d9aa88d207dc008a52385f7dba6156a", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Jur.svg", + "nodes": [ + { + "url": "wss://jur-mainnet-archive-rpc-1.icebergnodes.io", + "name": "Iceberg node" + }, + { + "url": "wss://jur-archive-mainnet-1.simplystaking.xyz/VX68C07AR4K2/ws", + "name": "Simplystaking node" + } + ], + "assets": [ + { + "assetId": 0, + "symbol": "JUR", + "precision": 18, + "priceId": "jur", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Jur.svg", + "name": "Jur token" + } + ], + "explorers": [ + { + "name": "Polkascan", + "account": "https://jur.io/explorer/jur/account/{address}", + "event": "https://jur.io/explorer/jur/event/{event}" + } + ] } ] diff --git a/src/renderer/assets/chains/chains_dev.json b/src/renderer/assets/chains/chains_dev.json index b565b6fb5c..41a6b04704 100644 --- a/src/renderer/assets/chains/chains_dev.json +++ b/src/renderer/assets/chains/chains_dev.json @@ -6,28 +6,28 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Polkadot.svg", "nodes": [ { - "url": "wss://rpc-polkadot.luckyfriday.io", - "name": "LuckyFriday node" + "url": "wss://polkadot-rpc.dwellir.com", + "name": "Dwellir node" }, { "url": "wss://rpc.polkadot.io", "name": "Parity node" }, { - "url": "wss://polkadot.api.onfinality.io/public-ws", - "name": "OnFinality node" - }, - { - "url": "wss://rpc.dotters.network/polkadot", - "name": "Dotters Net node" + "url": "wss://rpc-polkadot.luckyfriday.io", + "name": "LuckyFriday node" }, { "url": "wss://rpc.ibp.network/polkadot", "name": "IBP network node" }, { - "url": "wss://polkadot-rpc.dwellir.com", - "name": "Dwellir node" + "url": "wss://rpc.dotters.network/polkadot", + "name": "Dotters Net node" + }, + { + "url": "wss://polkadot.api.onfinality.io/public-ws", + "name": "OnFinality node" }, { "url": "wss://1rpc.io/dot", @@ -50,12 +50,10 @@ "name": "Subscan", "extrinsic": "https://polkadot.subscan.io/extrinsic/{hash}", "account": "https://polkadot.subscan.io/account/{address}", - "event": null, "multisig": "https://polkadot.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { "name": "Polkascan", - "extrinsic": "https://polkascan.io/polkadot/extrinsic/{hash}", "account": "https://polkascan.io/polkadot/account/{address}", "event": "https://polkascan.io/polkadot/event/{event}" }, @@ -139,12 +137,10 @@ "name": "Subscan", "extrinsic": "https://kusama.subscan.io/extrinsic/{hash}", "account": "https://kusama.subscan.io/account/{address}", - "event": null, "multisig": "https://kusama.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { "name": "Polkascan", - "extrinsic": "https://polkascan.io/kusama/extrinsic/{hash}", "account": "https://polkascan.io/kusama/account/{address}", "event": "https://polkascan.io/kusama/event/{event}" }, @@ -180,6 +176,14 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Westend_Testnet.svg", "options": ["testnet"], "nodes": [ + { + "url": "wss://rpc.ibp.network/westend", + "name": "IBP network node" + }, + { + "url": "wss://rpc.dotters.network/westend", + "name": "Dotters Net node" + }, { "url": "wss://westend-rpc.polkadot.io", "name": "Parity node" @@ -208,7 +212,6 @@ "name": "Subscan", "extrinsic": "https://westend.subscan.io/extrinsic/{hash}", "account": "https://westend.subscan.io/account/{address}", - "event": null, "multisig": "https://westend.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" } ], @@ -216,13 +219,13 @@ "staking": [ { "type": "subquery", - "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-westend__bm92Y" + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-westend" } ], "history": [ { "type": "subquery", - "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-westend__bm92Y" + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-westend" } ] } @@ -280,6 +283,14 @@ "parentId": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Kusama_Asset_Hub.svg", "nodes": [ + { + "url": "wss://sys.ibp.network/statemine", + "name": "IBP network node" + }, + { + "url": "wss://sys.dotters.network/statemine", + "name": "Dotters Net node" + }, { "url": "wss://statemine.api.onfinality.io/public-ws", "name": "OnFinality node" @@ -376,7 +387,6 @@ "name": "Subscan", "extrinsic": "https://statemine.subscan.io/extrinsic/{hash}", "account": "https://statemine.subscan.io/account/{address}", - "event": null, "multisig": "https://statemine.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -443,18 +453,18 @@ }, { "assetId": 1, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x0081", "currencyIdType": "AcalaPrimitivesCurrencyCurrencyId", "existentialDeposit": "10000000000", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 2, @@ -817,7 +827,6 @@ "name": "Subscan", "extrinsic": "https://karura.subscan.io/extrinsic/{hash}", "account": "https://karura.subscan.io/account/{address}", - "event": null, "multisig": "https://karura.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -942,15 +951,15 @@ }, { "assetId": 7, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "statemine", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "assetId": "18446744073709551616" }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 8, @@ -994,7 +1003,6 @@ "name": "Subscan", "extrinsic": "https://shiden.subscan.io/extrinsic/{hash}", "account": "https://shiden.subscan.io/account/{address}", - "event": null, "multisig": "https://shiden.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1042,7 +1050,6 @@ "symbol": "BNC", "precision": 12, "priceId": "bifrost-native-coin", - "staking": "parachain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Bifrost_(BNC).svg", "name": "Bifrost" }, @@ -1108,18 +1115,18 @@ }, { "assetId": 5, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x0302", "currencyIdType": "NodePrimitivesCurrencyCurrencyId", "existentialDeposit": "100000000", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 6, @@ -1208,6 +1215,34 @@ "transfersEnabled": true }, "name": "KINT Bitcoin" + }, + { + "assetId": 12, + "symbol": "vBNC", + "precision": 12, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vBNC.svg", + "typeExtras": { + "currencyIdScale": "0x0101", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "10000000000", + "transfersEnabled": true + }, + "name": "vBNC" + }, + { + "assetId": 13, + "symbol": "vMOVR", + "precision": 18, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vMOVR.svg", + "typeExtras": { + "currencyIdScale": "0x010a", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "1000000000000", + "transfersEnabled": true + }, + "name": "vMOVR" } ], "explorers": [ @@ -1215,7 +1250,6 @@ "name": "Subscan", "extrinsic": "https://bifrost-kusama.subscan.io/extrinsic/{hash}", "account": "https://bifrost-kusama.subscan.io/account/{address}", - "event": null, "multisig": "https://bifrost-kusama.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1333,18 +1367,18 @@ }, { "assetId": 5, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x0101000000", "currencyIdType": "InterbtcPrimitivesCurrencyId", "existentialDeposit": "0", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 6, @@ -1436,7 +1470,6 @@ "name": "Subscan", "extrinsic": "https://kintsugi.subscan.io/extrinsic/{hash}", "account": "https://kintsugi.subscan.io/account/{address}", - "event": null, "multisig": "https://kintsugi.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1483,7 +1516,6 @@ "symbol": "EDG", "precision": 18, "priceId": "edgeware", - "staking": "aura-relaychain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Edgeware_(EDG).svg", "name": "Edgeware" } @@ -1493,7 +1525,6 @@ "name": "Subscan", "extrinsic": "https://edgeware.subscan.io/extrinsic/{hash}", "account": "https://edgeware.subscan.io/account/{address}", - "event": null, "multisig": "https://edgeware.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1550,15 +1581,15 @@ }, { "assetId": 2, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "statemine", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "assetId": "103" }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 3, @@ -1649,7 +1680,6 @@ "name": "Subscan", "extrinsic": "https://parallel-heiko.subscan.io/extrinsic/{hash}", "account": "https://parallel-heiko.subscan.io/account/{address}", - "event": null, "multisig": "https://parallel-heiko.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1709,18 +1739,18 @@ }, { "assetId": 2, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x02000000", "currencyIdType": "u32", "existentialDeposit": "10000000000", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 3, @@ -1772,7 +1802,6 @@ "name": "Subscan", "extrinsic": "https://basilisk.subscan.io/extrinsic/{hash}", "account": "https://basilisk.subscan.io/account/{address}", - "event": null, "multisig": "https://basilisk.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1825,7 +1854,6 @@ "name": "Subscan", "extrinsic": "https://altair.subscan.io/extrinsic/{hash}", "account": "https://altair.subscan.io/account/{address}", - "event": null, "multisig": "https://altair.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -1926,15 +1954,15 @@ }, { "assetId": 5, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "statemine", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "assetId": "4" }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 6, @@ -2000,7 +2028,6 @@ "name": "Subscan", "extrinsic": "https://khala.subscan.io/extrinsic/{hash}", "account": "https://khala.subscan.io/account/{address}", - "event": null, "multisig": "https://khala.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2053,7 +2080,6 @@ "name": "Subscan", "extrinsic": "https://spiritnet.subscan.io/extrinsic/{hash}", "account": "https://spiritnet.subscan.io/account/{address}", - "event": null, "multisig": "https://spiritnet.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2101,7 +2127,6 @@ "name": "Subscan", "extrinsic": "https://kilt-testnet.subscan.io/extrinsic/{hash}", "account": "https://kilt-testnet.subscan.io/account/{address}", - "event": null, "multisig": "https://kilt-testnet.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" } ] @@ -2124,7 +2149,6 @@ "symbol": "KMA", "precision": 12, "priceId": "calamari-network", - "staking": "parachain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Calamari_(KMA).svg", "name": "Calamari Network" } @@ -2134,7 +2158,6 @@ "name": "Subscan", "extrinsic": "https://calamari.subscan.io/extrinsic/{hash}", "account": "https://calamari.subscan.io/account/{address}", - "event": null, "multisig": "https://calamari.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2201,7 +2224,6 @@ "name": "Subscan", "extrinsic": "https://quartz.subscan.io/extrinsic/{hash}", "account": "https://quartz.subscan.io/account/{address}", - "event": null, "multisig": "https://quartz.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2264,7 +2286,6 @@ "name": "Subscan", "extrinsic": "https://pioneer.subscan.io/extrinsic/{hash}", "account": "https://pioneer.subscan.io/account/{address}", - "event": null, "multisig": "https://pioneer.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2340,18 +2361,18 @@ }, { "assetId": 2, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-acala", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x0001", "currencyIdType": "AcalaPrimitivesCurrencyCurrencyId", "existentialDeposit": "100000000000", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 3, @@ -2537,7 +2558,6 @@ "name": "Subscan", "extrinsic": "https://acala.subscan.io/extrinsic/{hash}", "account": "https://acala.subscan.io/account/{address}", - "event": null, "multisig": "https://acala.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2599,7 +2619,6 @@ "name": "Subscan", "extrinsic": "https://acala-testnet.subscan.io/extrinsic/{hash}", "account": "https://acala-testnet.subscan.io/account/{address}", - "event": null, "multisig": "https://acala-testnet.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" } ] @@ -2719,15 +2738,15 @@ }, { "assetId": 8, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "statemine", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-acala", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "assetId": "18446744073709551617" }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 9, @@ -2747,7 +2766,6 @@ "name": "Subscan", "extrinsic": "https://astar.subscan.io/extrinsic/{hash}", "account": "https://astar.subscan.io/account/{address}", - "event": null, "multisig": "https://astar.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -2828,15 +2846,15 @@ }, { "assetId": 4, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "statemine", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-acala", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "assetId": "104" }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 5, @@ -2994,7 +3012,6 @@ "name": "Subscan", "extrinsic": "https://parallel.subscan.io/extrinsic/{hash}", "account": "https://parallel.subscan.io/account/{address}", - "event": null, "multisig": "https://parallel.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3043,7 +3060,6 @@ "name": "Subscan", "extrinsic": "https://clv.subscan.io/extrinsic/{hash}", "account": "https://clv.subscan.io/account/{address}", - "event": null, "multisig": "https://clv.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3068,6 +3084,14 @@ "parentId": "0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Polkadot_Asset_Hub.svg", "nodes": [ + { + "url": "wss://sys.ibp.network/statemint", + "name": "IBP network node" + }, + { + "url": "wss://sys.dotters.network/statemint", + "name": "Dotters Net node" + }, { "url": "wss://statemint-rpc.polkadot.io", "name": "Parity node" @@ -3101,6 +3125,18 @@ "assetId": "1984" }, "name": "USD Tether" + }, + { + "assetId": 2, + "symbol": "USDC", + "precision": 6, + "type": "statemine", + "priceId": "usd-coin", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/USDC.svg", + "typeExtras": { + "assetId": "1337" + }, + "name": "USD Coin" } ], "explorers": [ @@ -3168,7 +3204,6 @@ "name": "Subscan", "extrinsic": "https://robonomics.subscan.io/extrinsic/{hash}", "account": "https://robonomics.subscan.io/account/{address}", - "event": null, "multisig": "https://robonomics.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3197,6 +3232,14 @@ "parentId": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Encointer.svg", "nodes": [ + { + "url": "wss://sys.ibp.network/encointer-kusama", + "name": "IBP network node" + }, + { + "url": "wss://sys.dotters.network/encointer-kusama", + "name": "Dotters Net node" + }, { "url": "wss://api.kusama.encointer.org", "name": "Encointer association node" @@ -3221,7 +3264,6 @@ "name": "Subscan", "extrinsic": "https://encointer.subscan.io/extrinsic/{hash}", "account": "https://encointer.subscan.io/account/{address}", - "event": null, "multisig": "https://encointer.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3314,7 +3356,6 @@ "name": "Subscan", "extrinsic": "https://picasso.subscan.io/extrinsic/{hash}", "account": "https://picasso.subscan.io/account/{address}", - "event": null, "multisig": "https://picasso.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3354,7 +3395,6 @@ "symbol": "ZTG", "precision": 10, "priceId": "zeitgeist", - "staking": "parachain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Zeitgeist_(ZTG).svg", "name": "Zeitgeist" } @@ -3364,7 +3404,6 @@ "name": "Subscan", "extrinsic": "https://zeitgeist.subscan.io/extrinsic/{hash}", "account": "https://zeitgeist.subscan.io/account/{address}", - "event": null, "multisig": "https://zeitgeist.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3419,7 +3458,6 @@ "name": "Subscan", "extrinsic": "https://litmus.subscan.io/extrinsic/{hash}", "account": "https://litmus.subscan.io/account/{address}", - "event": null, "multisig": "https://litmus.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3517,7 +3555,6 @@ "name": "Subscan", "extrinsic": "https://shadow.subscan.io/extrinsic/{hash}", "account": "https://shadow.subscan.io/account/{address}", - "event": null, "multisig": "https://shadow.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3566,7 +3603,6 @@ "name": "Subscan", "extrinsic": "https://integritee.subscan.io/extrinsic/{hash}", "account": "https://integritee.subscan.io/account/{address}", - "event": null, "multisig": "https://integritee.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3619,7 +3655,6 @@ "name": "Subscan", "extrinsic": "https://centrifuge-parachain.subscan.io/extrinsic/{hash}", "account": "https://centrifuge-parachain.subscan.io/account/{address}", - "event": null, "multisig": "https://centrifuge-parachain.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3675,7 +3710,6 @@ "name": "Subscan", "extrinsic": "https://efinity.subscan.io/extrinsic/{hash}", "account": "https://efinity.subscan.io/account/{address}", - "event": null, "multisig": "https://efinity.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -3735,18 +3769,18 @@ }, { "assetId": 2, - "symbol": "DAI-worm", + "symbol": "DAI-Acala", "precision": 18, "type": "orml", "priceId": "dai", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/DAI.svg", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/DAI-Acala.svg", "typeExtras": { "currencyIdScale": "0x02000000", "currencyIdType": "u32", "existentialDeposit": "10000000000000000", "transfersEnabled": true }, - "name": "DAI wormhol" + "name": "DAI on Acala" }, { "assetId": 3, @@ -3764,33 +3798,33 @@ }, { "assetId": 4, - "symbol": "WETH-worm", + "symbol": "WETH-Acala", "precision": 18, "type": "orml", "priceId": "ethereum-wormhole", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WETH-worm.svg", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WETH-Acala.svg", "typeExtras": { "currencyIdScale": "0x04000000", "currencyIdType": "u32", "existentialDeposit": "7000000000000", "transfersEnabled": true }, - "name": "Ethereum wormhol" + "name": "WETH-Acala" }, { "assetId": 5, - "symbol": "WBTC-worm", + "symbol": "WBTC-Acala", "precision": 8, "type": "orml", "priceId": "wrapped-bitcoin", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WBTC-worm.svg", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WBTC-Acala.svg", "typeExtras": { "currencyIdScale": "0x03000000", "currencyIdType": "u32", "existentialDeposit": "44", "transfersEnabled": true }, - "name": "Wrapped BTC" + "name": "WBTC-Acala" }, { "assetId": 6, @@ -3851,6 +3885,96 @@ "transfersEnabled": true }, "name": "USD Tether" + }, + { + "assetId": 10, + "symbol": "CFG", + "precision": 18, + "type": "orml", + "priceId": "centrifuge", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Centrifuge_(CFG).svg", + "typeExtras": { + "currencyIdScale": "0x0d000000", + "currencyIdType": "u32", + "existentialDeposit": "32467532467532500", + "transfersEnabled": true + }, + "name": "Centrifuge" + }, + { + "assetId": 11, + "symbol": "BNC", + "precision": 12, + "type": "orml", + "priceId": "bifrost-native-coin", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Bifrost_(BNC).svg", + "typeExtras": { + "currencyIdScale": "0x0e000000", + "currencyIdType": "u32", + "existentialDeposit": "68795189840", + "transfersEnabled": true + }, + "name": "Bifrost" + }, + { + "assetId": 12, + "symbol": "DAI-Moonbeam", + "precision": 18, + "type": "orml", + "priceId": "dai", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/DAI-Moonbeam.svg", + "typeExtras": { + "currencyIdScale": "0x12000000", + "currencyIdType": "u32", + "existentialDeposit": "10000000000000000", + "transfersEnabled": true + }, + "name": "DAI-Moonbeam" + }, + { + "assetId": 13, + "symbol": "WBTC-Moonbeam", + "precision": 8, + "type": "orml", + "priceId": "wrapped-bitcoin", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WBTC-Moonbeam.svg", + "typeExtras": { + "currencyIdScale": "0x13000000", + "currencyIdType": "u32", + "existentialDeposit": "34", + "transfersEnabled": true + }, + "name": "WBTC-Moonbeam" + }, + { + "assetId": 14, + "symbol": "WETH-Moonbeam", + "precision": 18, + "type": "orml", + "priceId": "ethereum-wormhole", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/WETH-Moonbeam.svg", + "typeExtras": { + "currencyIdScale": "0x14000000", + "currencyIdType": "u32", + "existentialDeposit": "7000000000000", + "transfersEnabled": true + }, + "name": "WETH-Moonbeam" + }, + { + "assetId": 15, + "symbol": "USDC", + "precision": 6, + "type": "orml", + "priceId": "usd-coin", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/USDC.svg", + "typeExtras": { + "currencyIdScale": "0x16000000", + "currencyIdType": "u32", + "existentialDeposit": "10000", + "transfersEnabled": true + }, + "name": "USD Coin" } ], "explorers": [ @@ -3858,7 +3982,6 @@ "name": "Subscan", "extrinsic": "https://hydradx.subscan.io/extrinsic/{hash}", "account": "https://hydradx.subscan.io/account/{address}", - "event": null, "multisig": "https://hydradx.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4103,7 +4226,6 @@ "name": "Subscan", "extrinsic": "https://interlay.subscan.io/extrinsic/{hash}", "account": "https://interlay.subscan.io/account/{address}", - "event": null, "multisig": "https://interlay.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4152,7 +4274,6 @@ "name": "Subscan", "extrinsic": "https://nodle.subscan.io/extrinsic/{hash}", "account": "https://nodle.subscan.io/account/{address}", - "event": null, "multisig": "https://nodle.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4231,7 +4352,6 @@ "name": "Subscan", "extrinsic": "https://phala.subscan.io/extrinsic/{hash}", "account": "https://phala.subscan.io/account/{address}", - "event": null, "multisig": "https://phala.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4270,7 +4390,6 @@ "assetId": 0, "symbol": "TUR", "precision": 10, - "staking": "turing", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Turing_(TUR).svg", "name": "Turing" }, @@ -4291,18 +4410,18 @@ }, { "assetId": 2, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "priceId": "acala-dollar", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "priceId": "ausd-seed-karura", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x02000000", "currencyIdType": "u32", "existentialDeposit": "10000000000", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 3, @@ -4383,7 +4502,6 @@ "name": "Subscan", "extrinsic": "https://turing.subscan.io/extrinsic/{hash}", "account": "https://turing.subscan.io/account/{address}", - "event": null, "multisig": "https://turing.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4424,7 +4542,6 @@ "symbol": "AZERO", "precision": 12, "priceId": "aleph-zero", - "staking": "aleph-zero", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Aleph_Zero_(AZERO).svg", "name": "Aleph Zero" } @@ -4434,7 +4551,6 @@ "name": "Subscan", "extrinsic": "https://alephzero.subscan.io/extrinsic/{hash}", "account": "https://alephzero.subscan.io/account/{address}", - "event": null, "multisig": "https://alephzero.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" } ], @@ -4528,7 +4644,6 @@ "name": "Subscan", "extrinsic": "https://composable.subscan.io/extrinsic/{hash}", "account": "https://composable.subscan.io/account/{address}", - "event": null, "multisig": "https://composable.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4566,6 +4681,30 @@ "staking": "relaychain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Polkadex_(PDEX).svg", "name": "Polkadex" + }, + { + "assetId": 1, + "symbol": "DOT", + "precision": 12, + "type": "statemine", + "priceId": "polkadot", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Polkadot_(DOT).svg", + "typeExtras": { + "assetId": "95930534000017180603917534864279132680" + }, + "name": "Polkadot" + }, + { + "assetId": 2, + "symbol": "USDT", + "precision": 12, + "type": "statemine", + "priceId": "tether", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/USDT.svg", + "typeExtras": { + "assetId": "3496813586714279103986568049643838918" + }, + "name": "USD Tether" } ], "explorers": [ @@ -4573,7 +4712,6 @@ "name": "Subscan", "extrinsic": "https://polkadex.subscan.io/extrinsic/{hash}", "account": "https://polkadex.subscan.io/account/{address}", - "event": null, "multisig": "https://polkadex.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4725,6 +4863,62 @@ "transfersEnabled": true }, "name": "Astar" + }, + { + "assetId": 5, + "symbol": "vDOT", + "precision": 10, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vDOT.svg", + "typeExtras": { + "currencyIdScale": "0x0900", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "1000000", + "transfersEnabled": true + }, + "name": "vDOT" + }, + { + "assetId": 6, + "symbol": "vGLMR", + "precision": 18, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vGLMR.svg", + "typeExtras": { + "currencyIdScale": "0x0901", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "1000000000000", + "transfersEnabled": true + }, + "name": "vGLMR" + }, + { + "assetId": 7, + "symbol": "vFIL", + "precision": 18, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vFIL.svg", + "typeExtras": { + "currencyIdScale": "0x0904", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "1000000000000", + "transfersEnabled": true + }, + "name": "vFIL" + }, + { + "assetId": 8, + "symbol": "vASTR", + "precision": 18, + "type": "orml", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/vASTR.svg", + "typeExtras": { + "currencyIdScale": "0x0903", + "currencyIdType": "NodePrimitivesCurrencyCurrencyId", + "existentialDeposit": "10000000000000000", + "transfersEnabled": true + }, + "name": "vASTR" } ], "explorers": [ @@ -4732,7 +4926,6 @@ "name": "Subscan", "extrinsic": "https://bifrost.subscan.io/extrinsic/{hash}", "account": "https://bifrost.subscan.io/account/{address}", - "event": null, "multisig": "https://bifrost.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4827,7 +5020,6 @@ "name": "Subscan", "extrinsic": "https://unique.subscan.io/extrinsic/{hash}", "account": "https://unique.subscan.io/account/{address}", - "event": null, "multisig": "https://unique.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -4846,7 +5038,7 @@ } }, { - "name": "Dora Factory", + "name": "Dora Factory (PAUSED)", "addressPrefix": 128, "chainId": "0x577d331ca43646f547cdaa07ad0aa387a383a93416764480665103081f3eaf14", "parentId": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", @@ -5037,7 +5229,6 @@ "name": "Subscan", "extrinsic": "https://mangatax.subscan.io/extrinsic/{hash}", "account": "https://mangatax.subscan.io/account/{address}", - "event": null, "multisig": "https://mangatax.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -5065,7 +5256,6 @@ "symbol": "TZERO", "precision": 12, "priceId": "aleph-zero", - "staking": "aleph-zero", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Default.svg", "name": "Aleph Zero Testnet" } @@ -5111,7 +5301,6 @@ "symbol": "POLYX", "precision": 6, "priceId": "polymesh", - "staking": "relaychain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Polymesh_(POLYX).svg", "name": "Polymesh Testnet" } @@ -5121,7 +5310,6 @@ "name": "Subscan", "extrinsic": "https://polymesh.subscan.io/extrinsic/{hash}", "account": "https://polymesh.subscan.io/account/{address}", - "event": null, "multisig": "https://polymesh.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" } ] @@ -5198,7 +5386,6 @@ "name": "Subscan", "extrinsic": "https://bajun.subscan.io/extrinsic/{hash}", "account": "https://bajun.subscan.io/account/{address}", - "event": null, "multisig": "https://bajun.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" }, { @@ -5322,10 +5509,6 @@ { "url": "wss://ws.gm.bldnodes.org", "name": "bLd node" - }, - { - "url": "wss://leemo.gmordie.com", - "name": "Leemo node" } ], "assets": [ @@ -5437,8 +5620,7 @@ { "name": "Ternoa explorer", "extrinsic": "https://explorer.ternoa.com/extrinsic/{hash}", - "account": "https://explorer.ternoa.com/account/{address}", - "event": null + "account": "https://explorer.ternoa.com/account/{address}" } ], "externalApi": { @@ -5474,7 +5656,6 @@ "symbol": "CAPS", "precision": 18, "priceId": "coin-capsule", - "staking": "relaychain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Ternoa_(CAPS).svg", "name": "Ternoa" } @@ -5483,8 +5664,7 @@ { "name": "Ternoa Alphanet explorer", "extrinsic": "https://explorer-alphanet.ternoa.dev/extrinsic/{hash}", - "account": "https://explorer-alphanet.ternoa.dev/{address}", - "event": null + "account": "https://explorer-alphanet.ternoa.dev/{address}" } ] }, @@ -5505,7 +5685,6 @@ "assetId": 0, "symbol": "TUR", "precision": 10, - "staking": "turing", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Turing_(TUR).svg", "name": "Turing" } @@ -5536,8 +5715,7 @@ { "name": "Governance2 Testnet explorer", "extrinsic": "https://gov2.statescan.io/extrinsic/{hash}", - "account": "https://gov2.statescan.io/account/{address}", - "event": null + "account": "https://gov2.statescan.io/account/{address}" } ] }, @@ -5587,8 +5765,12 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Totem.svg", "nodes": [ { - "url": "wss://k-ui.kapex.network", - "name": "Totem node" + "url": "wss://kapex-rpc.dwellir.com", + "name": "Dwellir node" + }, + { + "url": "wss://kapex-parachain.api.onfinality.io/public-ws", + "name": "OnFinality node" } ], "assets": [ @@ -5633,7 +5815,6 @@ "symbol": "POLYX", "precision": 6, "priceId": "polymesh", - "staking": "relaychain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Polymesh_(POLYX).svg", "name": "Polymesh Testnet" } @@ -5643,10 +5824,17 @@ "name": "Subscan", "extrinsic": "https://polymesh.subscan.io/extrinsic/{hash}", "account": "https://polymesh.subscan.io/account/{address}", - "event": null, "multisig": "https://polymesh.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet-polymesh" + } + ] + } }, { "name": "Beresheet", @@ -5768,9 +5956,7 @@ "explorers": [ { "name": "Explorer", - "extrinsic": null, - "account": "https://explorer.mainnet.oct.network/myriad/accounts/{address}", - "event": null + "account": "https://explorer.mainnet.oct.network/myriad/accounts/{address}" } ], "externalApi": { @@ -5806,9 +5992,7 @@ "explorers": [ { "name": "Explorer", - "extrinsic": null, - "account": "https://explorer.mainnet.oct.network/fusotao/accounts/{address}", - "event": null + "account": "https://explorer.mainnet.oct.network/fusotao/accounts/{address}" } ] }, @@ -5845,14 +6029,12 @@ { "name": "XX explorer", "extrinsic": "https://explorer.xx.network/extrinsics/{hash}", - "account": "https://explorer.xx.network/accounts/{address}", - "event": null + "account": "https://explorer.xx.network/accounts/{address}" }, { "name": "Polkastats", "extrinsic": "https://xx.polkastats.io/extrinsic/{hash}", - "account": "https://xx.polkastats.io/account/{address}", - "event": null + "account": "https://xx.polkastats.io/account/{address}" } ], "externalApi": { @@ -5950,8 +6132,7 @@ { "name": "Aventus testnet explorer", "extrinsic": "https://explorer.testnet.aventus.io/transaction/{hash}", - "account": "https://explorer.testnet.aventus.io/address/{address}", - "event": null + "account": "https://explorer.testnet.aventus.io/address/{address}" } ] }, @@ -5981,8 +6162,7 @@ { "name": "Aventus explorer", "extrinsic": "https://explorer.mainnet.aventus.io/transaction/{hash}", - "account": "https://explorer.mainnet.aventus.io/address/{address}", - "event": null + "account": "https://explorer.mainnet.aventus.io/address/{address}" } ], "externalApi": { @@ -6021,7 +6201,6 @@ "name": "Subscan", "extrinsic": "https://crust-parachain.subscan.io/extrinsic/{hash}", "account": "https://crust-parachain.subscan.io/account/{address}", - "event": null, "multisig": "https://crust-parachain.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" } ], @@ -6106,17 +6285,17 @@ }, { "assetId": 5, - "symbol": "aUSD", + "symbol": "aSEED", "precision": 12, "type": "orml", - "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aUSD.svg", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/aSEED.svg", "typeExtras": { "currencyIdScale": "0x0101000000", "currencyIdType": "InterbtcPrimitivesCurrencyId", "existentialDeposit": "0", "transfersEnabled": true }, - "name": "Acala USD" + "name": "Acala SEED" }, { "assetId": 6, @@ -6302,8 +6481,7 @@ { "name": "3DPass explorer", "extrinsic": "https://explorer.3dpass.org/extrinsic/{hash}", - "account": "https://explorer.3dpass.org/account/{address}", - "event": null + "account": "https://explorer.3dpass.org/account/{address}" } ], "externalApi": { @@ -6380,7 +6558,15 @@ "account": "https://polkaholic.io/account/{address}", "extrinsic": "https://polkaholic.io/tx/{hash}" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/frequency" + } + ] + } }, { "name": "Vara", @@ -6398,7 +6584,6 @@ "assetId": 0, "symbol": "VARA", "precision": 12, - "staking": "relaychain", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Vara.svg", "name": "Vara" } @@ -6406,11 +6591,18 @@ "explorers": [ { "name": "Varascan", - "extrinsic": null, "account": "https://explorer.vara-network.io/account/{address}", - "event": null + "event": "https://explorer.vara-network.io/event/{event}" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet---vara" + } + ] + } }, { "name": "GIANT", @@ -6431,7 +6623,15 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/GIANT.svg", "name": "Giant" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet---giant" + } + ] + } }, { "name": "GIANT Testnet", @@ -6463,6 +6663,14 @@ "parentId": "0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Polkadot_Bridge_Hub.svg", "nodes": [ + { + "url": "wss://sys.ibp.network/bridgehub-polkadot", + "name": "IBP network node" + }, + { + "url": "wss://sys.dotters.network/bridgehub-polkadot", + "name": "Dotters Net node" + }, { "url": "wss://polkadot-bridge-hub-rpc.polkadot.io", "name": "Parity node" @@ -6481,7 +6689,15 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Polkadot_(DOT).svg", "name": "Polkadot" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/polkadot-bridgehub" + } + ] + } }, { "name": "Kusama Bridge Hub", @@ -6490,6 +6706,14 @@ "parentId": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Kusama_Bridge_Hub.svg", "nodes": [ + { + "url": "wss://sys.ibp.network/bridgehub-kusama", + "name": "IBP network node" + }, + { + "url": "wss://sys.dotters.network/bridgehub-kusama", + "name": "Dotters Net node" + }, { "url": "wss://kusama-bridge-hub-rpc.polkadot.io", "name": "Parity node" @@ -6508,7 +6732,15 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Kusama_(KSM).svg", "name": "Kusama" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet---kusama-bridgehub" + } + ] + } }, { "name": "Polkadot Collectives", @@ -6518,20 +6750,20 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Polkadot_Collectives.svg", "nodes": [ { - "url": "wss://polkadot-collectives-rpc.polkadot.io", - "name": "Parity node" + "url": "wss://sys.ibp.network/collectives-polkadot", + "name": "IBP network node" }, { - "url": "wss://collectives.api.onfinality.io/public-ws", - "name": "Onfinality node" + "url": "wss://sys.dotters.network/collectives-polkadot", + "name": "Dotters Net node" }, { - "url": "wss://sys.dotters.network/collectives-polkadot", - "name": "via IBP-GeoDNS2" + "url": "wss://polkadot-collectives-rpc.polkadot.io", + "name": "Parity node" }, { - "url": "wss://sys.ibp.network/collectives-polkadot", - "name": "via IBP-GeoDNS1" + "url": "wss://collectives.api.onfinality.io/public-ws", + "name": "Onfinality node" }, { "url": "wss://collectives.public.curie.radiumblock.co/ws", @@ -6551,7 +6783,15 @@ "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Polkadot_(DOT).svg", "name": "Polkadot" } - ] + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/polkadot-collectives" + } + ] + } }, { "name": "Amplitude Foucoco", @@ -6795,5 +7035,133 @@ "name": "Interlay DOT" } ] + }, + { + "name": "krest", + "addressPrefix": 42, + "chainId": "0xb3dd5ad6a82872b30aabaede8f41dfd4ff6c32ff82f8757d034a45be63cf104c", + "parentId": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/krest.svg", + "nodes": [ + { + "url": "wss://wss-krest.peaq.network", + "name": "Krest node" + } + ], + "assets": [ + { + "assetId": 0, + "symbol": "KREST", + "precision": 18, + "priceId": "krest", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/krest_(krest).svg", + "name": "KREST token" + } + ], + "explorers": [ + { + "name": "Subscan", + "extrinsic": "https://krest.subscan.io/extrinsic/{hash}", + "account": "https://krest.subscan.io/account/{address}", + "multisig": "https://krest.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" + } + ], + "externalApi": { + "history": [ + { + "type": "subquery", + "url": "https://api.subquery.network/sq/nova-wallet/nova-wallet---krest" + } + ] + } + }, + { + "name": "Jur", + "addressPrefix": 42, + "chainId": "0x58d1393b47b11707978fbc07e77d7b6f7d9aa88d207dc008a52385f7dba6156a", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Jur.svg", + "nodes": [ + { + "url": "wss://jur-mainnet-archive-rpc-1.icebergnodes.io", + "name": "Iceberg node" + }, + { + "url": "wss://jur-archive-mainnet-1.simplystaking.xyz/VX68C07AR4K2/ws", + "name": "Simplystaking node" + } + ], + "assets": [ + { + "assetId": 0, + "symbol": "JUR", + "precision": 18, + "priceId": "jur", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Jur.svg", + "name": "Jur token" + } + ], + "explorers": [ + { + "name": "Polkascan", + "account": "https://jur.io/explorer/jur/account/{address}", + "event": "https://jur.io/explorer/jur/event/{event}" + } + ] + }, + { + "name": "Manta", + "addressPrefix": 77, + "chainId": "0xf3c7ad88f6a80f366c4be216691411ef0622e8b809b1046ea297ef106058d4eb", + "parentId": "0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Manta.svg", + "nodes": [ + { + "url": "wss://ws.manta.systems", + "name": "Manta node" + }, + { + "url": "wss://manta.api.onfinality.io/public-ws", + "name": "OnFinality node" + } + ], + "assets": [ + { + "assetId": 0, + "symbol": "MANTA", + "precision": 18, + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/Manta_(MANTA).svg", + "name": "Manta token" + } + ], + "explorers": [ + { + "name": "Subscan", + "extrinsic": "https://manta.subscan.io/extrinsic/{hash}", + "account": "https://manta.subscan.io/account/{address}", + "multisig": "https://manta.subscan.io/multisig_extrinsic/{index}?call_hash={callHash}" + } + ] + }, + { + "name": "Vara Testnet", + "addressPrefix": 137, + "chainId": "0x9b86ea7366584c5ddf67de243433fcc05732864933258de9467db46eb9bef8b5", + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/chains/Vara_Testnet.svg", + "options": ["testnet"], + "nodes": [ + { + "url": "wss://vit.vara-network.io", + "name": "Gear Tech test node" + } + ], + "assets": [ + { + "assetId": 0, + "symbol": "TVARA", + "precision": 12, + "icon": "https://raw.githubusercontent.com/novasamatech/nova-spektr-utils/main/icons/v1/assets/white/tVARA.svg", + "name": "Vara testnet" + } + ] } ] From 35abbfa4d18bb06f8a317e87a54e3a899c92201d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 12:16:59 +0000 Subject: [PATCH 29/34] ci: update version in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6af1a746a6..81da98d0d0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nova-spektr", "description": "Polkadot Enterprise application", - "version": "1.0.5", + "version": "1.0.6", "main": "./release/build/main.js", "license": "MIT", "author": { From c479e001db6b1c3d3858d2f8e19d52516bcf6c6d Mon Sep 17 00:00:00 2001 From: Stepan Lavrentev Date: Mon, 18 Sep 2023 16:40:40 +0300 Subject: [PATCH 30/34] chore: change version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 81da98d0d0..7433daab29 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nova-spektr", "description": "Polkadot Enterprise application", - "version": "1.0.6", + "version": "1.1.0", "main": "./release/build/main.js", "license": "MIT", "author": { From 04abd3a3aaae35dfc469f503721d55f18c0d930d Mon Sep 17 00:00:00 2001 From: Stepan Lavrentev <40560660+stepanLav@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:45:21 +0300 Subject: [PATCH 31/34] Fix/deposit validation (#1073) * fix: deposit validation * fix: remove amount from deposit validation --------- Co-authored-by: asmadek --- .../widgets/SendAssetModal/ui/components/TransferForm.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx b/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx index acc8046230..6af835a486 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx @@ -342,15 +342,13 @@ export const TransferForm = ({ if (!isMultisig(account)) return true; if (!signerBalance) return false; - const amountBN = new BN(formatAmount(amount, asset.precision)); - const xcmFeeBN = new BN(xcmParams.fee || 0); const feeBN = new BN(fee); if (signerNativeTokenBalance) { return new BN(deposit).add(feeBN).lte(new BN(signerNativeTokenBalance)); } - return new BN(deposit).add(feeBN).add(amountBN).add(xcmFeeBN).lte(new BN(signerBalance)); + return new BN(deposit).add(feeBN).lte(new BN(signerBalance)); }; const submitTransaction: SubmitHandler = async ({ description }) => { From 673dfb2f4f75457027fa71212c9c54536b4714f3 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Mon, 18 Sep 2023 16:46:32 +0300 Subject: [PATCH 32/34] fix: transfer icon (#1072) --- src/renderer/assets/images/mst/transfer.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/assets/images/mst/transfer.svg b/src/renderer/assets/images/mst/transfer.svg index f8447e3d91..bd7a13958c 100644 --- a/src/renderer/assets/images/mst/transfer.svg +++ b/src/renderer/assets/images/mst/transfer.svg @@ -1,3 +1,3 @@ - - + + From 71bd61b21ba0112c63870d7c86fffbad9740eb0a Mon Sep 17 00:00:00 2001 From: Stepan Lavrentev <40560660+stepanLav@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:40:09 +0300 Subject: [PATCH 33/34] Fix/operation icon (#1077) * fix: transfer icon * fix: staking & unknown icons --------- Co-authored-by: Yaroslav Grachev --- src/renderer/assets/images/mst/staking.svg | 6 ++---- src/renderer/assets/images/mst/unknown.svg | 4 ++-- .../components/TransactionTitle/TransactionTitle.tsx | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/renderer/assets/images/mst/staking.svg b/src/renderer/assets/images/mst/staking.svg index 5058664814..c6019cc9c3 100644 --- a/src/renderer/assets/images/mst/staking.svg +++ b/src/renderer/assets/images/mst/staking.svg @@ -1,5 +1,3 @@ - - - - + + diff --git a/src/renderer/assets/images/mst/unknown.svg b/src/renderer/assets/images/mst/unknown.svg index 12db45c34a..71210fc654 100644 --- a/src/renderer/assets/images/mst/unknown.svg +++ b/src/renderer/assets/images/mst/unknown.svg @@ -1,3 +1,3 @@ - - + + diff --git a/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.tsx b/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.tsx index 8f0c1f088a..cf91bbd5e5 100644 --- a/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.tsx +++ b/src/renderer/pages/Operations/components/TransactionTitle/TransactionTitle.tsx @@ -21,7 +21,7 @@ export const TransactionTitle = ({ tx, description, className, children }: Props return (
              - +
              From a551f41c45d421698ce0f435ab39506723663a9e Mon Sep 17 00:00:00 2001 From: Aleksandr Makhnev Date: Mon, 18 Sep 2023 20:07:17 +0500 Subject: [PATCH 34/34] fix: transfer modal close (#1074) --- .../transaction/lib/transactionService.ts | 2 -- .../pages/Assets/SendAsset/SendAsset.tsx | 6 +----- .../SendAssetModal/ui/SendAssetModal.tsx | 18 ++++++++++++------ .../components/ActionSteps/InitOperation.tsx | 2 +- .../ui/components/ActionSteps/Submit.tsx | 8 ++++++++ .../ui/components/TransferForm.tsx | 3 ++- 6 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/renderer/entities/transaction/lib/transactionService.ts b/src/renderer/entities/transaction/lib/transactionService.ts index 8bcdbae47d..e251ade137 100644 --- a/src/renderer/entities/transaction/lib/transactionService.ts +++ b/src/renderer/entities/transaction/lib/transactionService.ts @@ -447,8 +447,6 @@ export const useTransaction = (): ITransactionService => { const getTransactionHash = (transaction: Transaction, api: ApiPromise): HashData => { const extrinsic = getExtrinsic[transaction.type](transaction.args, api); - console.log('xcmMethod', extrinsic.method.toJSON()); - return { callData: extrinsic.method.toHex(), callHash: extrinsic.method.hash.toHex(), diff --git a/src/renderer/pages/Assets/SendAsset/SendAsset.tsx b/src/renderer/pages/Assets/SendAsset/SendAsset.tsx index 9fc5e0b455..02b1267249 100644 --- a/src/renderer/pages/Assets/SendAsset/SendAsset.tsx +++ b/src/renderer/pages/Assets/SendAsset/SendAsset.tsx @@ -1,15 +1,11 @@ -import { useNavigate } from 'react-router-dom'; - import { AssetRouteGuard } from '@renderer/features/assets'; import { Paths } from '@renderer/app/providers'; import { SendAssetModal } from '@renderer/widgets'; export const SendAsset = () => { - const navigate = useNavigate(); - return ( - {(chain, asset) => navigate(Paths.ASSETS)} />} + {(chain, asset) => } ); }; diff --git a/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx b/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx index 0123118a27..99247e0708 100644 --- a/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/SendAssetModal.tsx @@ -1,8 +1,9 @@ import { useState, useEffect } from 'react'; import { UnsignedTransaction } from '@substrate/txwrapper-polkadot'; import { useStore, useGate } from 'effector-react'; +import { useNavigate } from 'react-router-dom'; -import { useI18n, useNetworkContext } from '@renderer/app/providers'; +import { Paths, useI18n, useNetworkContext } from '@renderer/app/providers'; import { HexString } from '@renderer/domain/shared-kernel'; import { Transaction, useTransaction, validateBalance } from '@renderer/entities/transaction'; import { Account, MultisigAccount } from '@renderer/entities/account'; @@ -12,7 +13,6 @@ import { Signing } from '@renderer/features/operation'; import { Asset, useBalance } from '@renderer/entities/asset'; import { OperationTitle } from '@renderer/components/common'; import { Chain } from '@renderer/entities/chain'; -import { DEFAULT_TRANSITION } from '@renderer/shared/lib/utils'; import { useToggle } from '@renderer/shared/lib/hooks'; import * as sendAssetModel from '../model/send-asset'; @@ -26,11 +26,12 @@ const enum Step { type Props = { chain: Chain; asset: Asset; - onClose: () => void; }; -export const SendAssetModal = ({ chain, asset, onClose }: Props) => { +export const SendAssetModal = ({ chain, asset }: Props) => { const { t } = useI18n(); + const navigate = useNavigate(); + const { getBalance } = useBalance(); const { getTransactionFee } = useTransaction(); const { connections } = useNetworkContext(); @@ -92,7 +93,12 @@ export const SendAssetModal = ({ chain, asset, onClose }: Props) => { const closeSendModal = () => { toggleIsModalOpen(); - setTimeout(onClose, DEFAULT_TRANSITION); + // TODO: rework to context-free solution + navigate(Paths.ASSETS); + }; + + const closeSendModalFromSubmit = () => { + toggleIsModalOpen(); }; const commonProps = { explorers, addressPrefix }; @@ -107,7 +113,7 @@ export const SendAssetModal = ({ chain, asset, onClose }: Props) => { signature={signature} description={description} api={api} - onClose={closeSendModal} + onClose={closeSendModalFromSubmit} {...commonProps} /> ) : ( diff --git a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx index b77f6a4c03..012d66fa53 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/InitOperation.tsx @@ -142,7 +142,7 @@ export const InitOperation = ({ const reserveChainId = reserveAsset && config && toHexChainId(config.assetsLocation[reserveAsset.assetLocation].chainId); - const reserveApi = reserveChainId && connections[reserveChainId].api; + const reserveApi = reserveChainId && connections[reserveChainId]?.api; return (
              diff --git a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Submit.tsx b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Submit.tsx index fede8e5ff8..b237a03503 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Submit.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/ActionSteps/Submit.tsx @@ -53,6 +53,10 @@ export const Submit = ({ api, tx, multisigTx, account, unsignedTx, signature, de if (isMultisig(account) && successMessage) { navigate(Paths.OPERATIONS); + } else { + // TODO: rework to context-free solution + + navigate(Paths.ASSETS); } }; @@ -62,6 +66,10 @@ export const Submit = ({ api, tx, multisigTx, account, unsignedTx, signature, de if (isMultisig(account)) { navigate(Paths.OPERATIONS); + } else { + // TODO: rework to context-free solution + + navigate(Paths.ASSETS); } }; diff --git a/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx b/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx index 6af835a486..a66a16b236 100644 --- a/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx +++ b/src/renderer/widgets/SendAssetModal/ui/components/TransferForm.tsx @@ -322,6 +322,7 @@ export const TransferForm = ({ const validateBalanceForFee = (amount: string): boolean => { const balance = isMultisig(account) ? signerBalance : accountBalance; const nativeTokenBalance = isMultisig(account) ? signerNativeTokenBalance : accountNativeTokenBalance; + const amountBN = new BN(formatAmount(amount, asset.precision)); const xcmFeeBN = new BN(xcmParams.fee || 0); @@ -332,7 +333,7 @@ export const TransferForm = ({ } if (isMultisig(account)) { - return new BN(fee).add(amountBN).add(xcmFeeBN).lte(new BN(balance)); + return new BN(fee).lte(new BN(balance)); } return new BN(fee).add(amountBN).add(xcmFeeBN).lte(new BN(balance));