From d260a0f285ba728893e564ebbf09de3425ae44d3 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Fri, 29 Dec 2023 10:38:45 +0300 Subject: [PATCH 01/12] feat: move proxy types --- .../entities/notification/lib/common/types.ts | 2 +- src/renderer/entities/proxy/index.ts | 1 - src/renderer/entities/proxy/lib/constants.ts | 16 +--------- src/renderer/entities/proxy/lib/types.ts | 13 ++------- .../entities/proxy/model/proxy-model.ts | 2 +- .../ui/ProxyAccount/ProxyAccount.stories.tsx | 2 +- .../ui/ProxyAccount/ProxyAccount.test.tsx | 2 +- .../proxy/ui/ProxyAccount/ProxyAccount.tsx | 29 +++++++++---------- .../QrGenerator/QrTextGenerator.test.tsx | 2 +- .../QrCode/QrGenerator/QrTxGenerator.test.tsx | 2 +- .../ui/QrCode/QrReader/QrReader.test.tsx | 2 +- .../NotificationProvider.tsx | 19 ++++++------ .../NotificationProvider/lib/constants.ts | 12 ++++++++ .../MultisigInvitedNotification.tsx | 0 .../ProxyCreatedNotification.tsx | 4 +-- .../components/ShortTransactionInfo.test.tsx | 2 +- src/renderer/shared/core/index.ts | 3 +- .../ui/FallbackScreen/FallbackScreen.test.tsx | 2 +- src/renderer/shared/ui/Header/Header.test.tsx | 2 +- 19 files changed, 53 insertions(+), 64 deletions(-) create mode 100644 src/renderer/features/notifications/NotificationProvider/lib/constants.ts rename src/renderer/features/notifications/NotificationProvider/{components => ui}/MultisigInvitedNotification.tsx (100%) rename src/renderer/features/notifications/NotificationProvider/{components => ui}/ProxyCreatedNotification.tsx (96%) diff --git a/src/renderer/entities/notification/lib/common/types.ts b/src/renderer/entities/notification/lib/common/types.ts index 04d15196b1..e85687b467 100644 --- a/src/renderer/entities/notification/lib/common/types.ts +++ b/src/renderer/entities/notification/lib/common/types.ts @@ -1,6 +1,6 @@ import { ID, NotificationDS } from '@shared/api/storage'; import type { AccountId, CallHash, ChainId, Timepoint } from '@shared/core'; -import { ProxyType } from '@entities/proxy'; +import { ProxyType } from '@shared/core'; export interface INotificationService { getNotifications: (where?: Partial) => Promise; diff --git a/src/renderer/entities/proxy/index.ts b/src/renderer/entities/proxy/index.ts index 0644047def..bd0e369179 100644 --- a/src/renderer/entities/proxy/index.ts +++ b/src/renderer/entities/proxy/index.ts @@ -1,4 +1,3 @@ export { proxyModel } from './model/proxy-model'; export * from './lib/constants'; export { ProxyAccount } from './ui/ProxyAccount/ProxyAccount'; -export { ProxyType } from './lib/types'; diff --git a/src/renderer/entities/proxy/lib/constants.ts b/src/renderer/entities/proxy/lib/constants.ts index d71eb22bcb..eefb845a6b 100644 --- a/src/renderer/entities/proxy/lib/constants.ts +++ b/src/renderer/entities/proxy/lib/constants.ts @@ -1,5 +1,4 @@ -import { ProxyType } from './types'; -import { AccountId, ProxyAccount } from '@shared/core'; +import { ProxyType } from '@shared/core'; export const ProxyTypeName: Record = { [ProxyType.ANY]: 'proxy.names.any', @@ -11,16 +10,3 @@ export const ProxyTypeName: Record = { [ProxyType.IDENTITY_JUDGEMENT]: 'proxy.names.identityJudgement', [ProxyType.NOMINATION_POOLS]: 'proxy.names.nominationPools', }; - -export const ProxyTypeOperation: Record = { - [ProxyType.ANY]: 'proxy.operations.any', - [ProxyType.NON_TRANSFER]: 'proxy.operations.nonTransfer', - [ProxyType.STAKING]: 'proxy.operations.staking', - [ProxyType.AUCTION]: 'proxy.operations.auction', - [ProxyType.CANCEL_PROXY]: 'proxy.operations.cancelProxy', - [ProxyType.GOVERNANCE]: 'proxy.operations.governance', - [ProxyType.IDENTITY_JUDGEMENT]: 'proxy.operations.identityJudgement', - [ProxyType.NOMINATION_POOLS]: 'proxy.operations.nominationPools', -}; - -export type ProxyStore = Record; diff --git a/src/renderer/entities/proxy/lib/types.ts b/src/renderer/entities/proxy/lib/types.ts index 55eba3596b..12ce2a89aa 100644 --- a/src/renderer/entities/proxy/lib/types.ts +++ b/src/renderer/entities/proxy/lib/types.ts @@ -1,10 +1,3 @@ -export const enum ProxyType { - ANY = 'Any', - NON_TRANSFER = 'NonTransfer', - STAKING = 'Staking', - AUCTION = 'Auction', - CANCEL_PROXY = 'CancelProxy', - GOVERNANCE = 'Governance', - IDENTITY_JUDGEMENT = 'IdentityJudgement', - NOMINATION_POOLS = 'NominationPools', -} +import type { AccountId, ProxyAccount } from '@shared/core'; + +export type ProxyStore = Record; diff --git a/src/renderer/entities/proxy/model/proxy-model.ts b/src/renderer/entities/proxy/model/proxy-model.ts index bd272202ff..5ad0e58cca 100644 --- a/src/renderer/entities/proxy/model/proxy-model.ts +++ b/src/renderer/entities/proxy/model/proxy-model.ts @@ -1,6 +1,6 @@ import { createEffect, createEvent, createStore, sample } from 'effector'; -import { ProxyStore } from '../lib/constants'; +import { ProxyStore } from '../lib/types'; import { proxyUtils } from '../lib/utils'; import type { ProxyAccount } from '@shared/core'; import { storageService } from '@shared/api/storage'; diff --git a/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.stories.tsx b/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.stories.tsx index 5cc456158f..c6200759a2 100644 --- a/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.stories.tsx +++ b/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.stories.tsx @@ -2,7 +2,7 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; import { TEST_ACCOUNT_ID } from '@shared/lib/utils'; import { ProxyAccount } from './ProxyAccount'; -import { ProxyType } from '@entities/proxy'; +import { ProxyType } from '@shared/core'; export default { title: 'ProxyAccount', diff --git a/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.test.tsx b/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.test.tsx index 5759702963..62975f8dbd 100644 --- a/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.test.tsx +++ b/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'; import { ProxyAccount } from './ProxyAccount'; import { TEST_ACCOUNT_ID, TEST_ADDRESS } from '@shared/lib/utils'; -import { ProxyType } from '@entities/proxy'; +import { ProxyType } from '@shared/core'; jest.mock('@app/providers', () => ({ useI18n: jest.fn().mockReturnValue({ diff --git a/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.tsx b/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.tsx index 3d61ca2609..02af81bef7 100644 --- a/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.tsx +++ b/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.tsx @@ -1,10 +1,9 @@ import { cnTw, toAddress, toShortAddress } from '@shared/lib/utils'; import { BodyText, DropdownIconButton, HelpText, Identicon, Truncate } from '@shared/ui'; -import { AccountId } from '@shared/core'; -import { ProxyType } from '../../lib/types'; -import { ProxyTypeName } from '@entities/proxy/lib/constants'; +import { AccountId, ProxyType } from '@shared/core'; import { useI18n } from '@app/providers'; import { DropdownIconButtonOption } from '@shared/ui/types'; +import { ProxyTypeName } from '../../lib/constants'; type Props = { className?: string; @@ -38,18 +37,6 @@ export const ProxyAccount = ({ addressToShow ); - const ActionButton = actions && ( - - - {actions.map((option) => ( - - - - ))} - - - ); - return (
@@ -61,7 +48,17 @@ export const ProxyAccount = ({ {t(ProxyTypeName[proxyType])}
- {ActionButton} + {actions && ( + + + {actions.map((option) => ( + + + + ))} + + + )} ); }; diff --git a/src/renderer/entities/transaction/ui/QrCode/QrGenerator/QrTextGenerator.test.tsx b/src/renderer/entities/transaction/ui/QrCode/QrGenerator/QrTextGenerator.test.tsx index a66b719f38..50a441594a 100644 --- a/src/renderer/entities/transaction/ui/QrCode/QrGenerator/QrTextGenerator.test.tsx +++ b/src/renderer/entities/transaction/ui/QrCode/QrGenerator/QrTextGenerator.test.tsx @@ -2,7 +2,7 @@ import { render } from '@testing-library/react'; import { QrTextGenerator } from './QrTextGenerator'; -describe('components/common/QrTextGenerator', () => { +describe('ui/QrTextGenerator', () => { test('should render text qr', () => { const { container } = render(); diff --git a/src/renderer/entities/transaction/ui/QrCode/QrGenerator/QrTxGenerator.test.tsx b/src/renderer/entities/transaction/ui/QrCode/QrGenerator/QrTxGenerator.test.tsx index 87bffae566..447b20c1e8 100644 --- a/src/renderer/entities/transaction/ui/QrCode/QrGenerator/QrTxGenerator.test.tsx +++ b/src/renderer/entities/transaction/ui/QrCode/QrGenerator/QrTxGenerator.test.tsx @@ -3,7 +3,7 @@ import { render } from '@testing-library/react'; import { QrTxGenerator } from './QrTxGenerator'; import { SigningType } from '@shared/core'; -describe('components/common/QrTxGenerator', () => { +describe('ui/QrTxGenerator', () => { test('should render transaction qr', () => { const { container } = render( ({ }), })); -describe('components/common/QrCode/QrReader', () => { +describe('ui/QrCode/QrReader', () => { const mockUserMedia = () => { const mediaValue = { getUserMedia: jest.fn().mockResolvedValue({ diff --git a/src/renderer/features/notifications/NotificationProvider/NotificationProvider.tsx b/src/renderer/features/notifications/NotificationProvider/NotificationProvider.tsx index 6bc1918c49..89dad4140c 100644 --- a/src/renderer/features/notifications/NotificationProvider/NotificationProvider.tsx +++ b/src/renderer/features/notifications/NotificationProvider/NotificationProvider.tsx @@ -1,19 +1,20 @@ import { MultisigNotificationType, Notification, ProxyNotificationType } from '@entities/notification'; -import { MultisigInvitedNotification } from './components/MultisigInvitedNotification'; -import { ProxyCreatedNotification } from './components/ProxyCreatedNotification'; +import { MultisigInvitedNotification } from './ui/MultisigInvitedNotification'; +import { ProxyCreatedNotification } from './ui/ProxyCreatedNotification'; type Props = { notification: Notification; }; -export const NotificationProvider = ({ notification }: Props) => - ({ +export const NotificationProvider = ({ notification }: Props) => { + return { [MultisigNotificationType.ACCOUNT_INVITED]: (n: Notification) => , - [MultisigNotificationType.MST_CREATED]: () => <>, - [MultisigNotificationType.MST_APPROVED]: () => <>, - [MultisigNotificationType.MST_EXECUTED]: () => <>, - [MultisigNotificationType.MST_CANCELLED]: () => <>, + [MultisigNotificationType.MST_CREATED]: () => null, + [MultisigNotificationType.MST_APPROVED]: () => null, + [MultisigNotificationType.MST_EXECUTED]: () => null, + [MultisigNotificationType.MST_CANCELLED]: () => null, [ProxyNotificationType.PROXY_CREATED]: (n: Notification) => ( ), - }[notification.type](notification)); + }[notification.type](notification); +}; diff --git a/src/renderer/features/notifications/NotificationProvider/lib/constants.ts b/src/renderer/features/notifications/NotificationProvider/lib/constants.ts new file mode 100644 index 0000000000..4d4758095d --- /dev/null +++ b/src/renderer/features/notifications/NotificationProvider/lib/constants.ts @@ -0,0 +1,12 @@ +import { ProxyType } from '@shared/core'; + +export const ProxyTypeOperation: Record = { + [ProxyType.ANY]: 'proxy.operations.any', + [ProxyType.NON_TRANSFER]: 'proxy.operations.nonTransfer', + [ProxyType.STAKING]: 'proxy.operations.staking', + [ProxyType.AUCTION]: 'proxy.operations.auction', + [ProxyType.CANCEL_PROXY]: 'proxy.operations.cancelProxy', + [ProxyType.GOVERNANCE]: 'proxy.operations.governance', + [ProxyType.IDENTITY_JUDGEMENT]: 'proxy.operations.identityJudgement', + [ProxyType.NOMINATION_POOLS]: 'proxy.operations.nominationPools', +}; diff --git a/src/renderer/features/notifications/NotificationProvider/components/MultisigInvitedNotification.tsx b/src/renderer/features/notifications/NotificationProvider/ui/MultisigInvitedNotification.tsx similarity index 100% rename from src/renderer/features/notifications/NotificationProvider/components/MultisigInvitedNotification.tsx rename to src/renderer/features/notifications/NotificationProvider/ui/MultisigInvitedNotification.tsx diff --git a/src/renderer/features/notifications/NotificationProvider/components/ProxyCreatedNotification.tsx b/src/renderer/features/notifications/NotificationProvider/ui/ProxyCreatedNotification.tsx similarity index 96% rename from src/renderer/features/notifications/NotificationProvider/components/ProxyCreatedNotification.tsx rename to src/renderer/features/notifications/NotificationProvider/ui/ProxyCreatedNotification.tsx index ddc89c3a7c..48ac2c6774 100644 --- a/src/renderer/features/notifications/NotificationProvider/components/ProxyCreatedNotification.tsx +++ b/src/renderer/features/notifications/NotificationProvider/ui/ProxyCreatedNotification.tsx @@ -7,8 +7,8 @@ import { BodyText, Identicon } from '@shared/ui'; import { useI18n } from '@app/providers'; import { toAddress } from '@shared/lib/utils'; import { ChainTitle } from '@entities/chain'; -import { ProxyTypeOperation } from '@entities/proxy'; import { WalletType } from '@shared/core'; +import { ProxyTypeOperation } from '../lib/constants'; type Props = { notification: Notification; @@ -26,7 +26,7 @@ export const ProxyCreatedNotification = ({ notification }: Props) => { const proxiedAccount = accounts.find((a) => a.accountId === typedNotification.proxiedAccountId); const proxiedWallet = wallets.find((w) => w.id === proxiedAccount?.walletId); - if (!proxiedWallet) return <>; + if (!proxiedWallet) return null; const identicon = ( { +describe('pages/Operations/ui/ShortTransactionInfo', () => { test('should render component', async () => { await act(async () => { render( diff --git a/src/renderer/shared/core/index.ts b/src/renderer/shared/core/index.ts index dba6d20fae..898b936980 100644 --- a/src/renderer/shared/core/index.ts +++ b/src/renderer/shared/core/index.ts @@ -51,4 +51,5 @@ export type { Validator } from './types/validator'; export { RewardsDestination } from './types/stake'; export type { Stake, Unlocking } from './types/stake'; -export type { ProxyAccount, PartialProxyAccount, ProxyType } from './types/proxy'; +export { ProxyType } from './types/proxy'; +export type { ProxyAccount, PartialProxyAccount } from './types/proxy'; diff --git a/src/renderer/shared/ui/FallbackScreen/FallbackScreen.test.tsx b/src/renderer/shared/ui/FallbackScreen/FallbackScreen.test.tsx index f9c729252d..2ede38e761 100644 --- a/src/renderer/shared/ui/FallbackScreen/FallbackScreen.test.tsx +++ b/src/renderer/shared/ui/FallbackScreen/FallbackScreen.test.tsx @@ -9,7 +9,7 @@ jest.mock('@app/providers', () => ({ }), })); -describe('components/common/FallbackScreen', () => { +describe('ui/FallbackScreen', () => { test('should render component', () => { render(, { wrapper: MemoryRouter }); diff --git a/src/renderer/shared/ui/Header/Header.test.tsx b/src/renderer/shared/ui/Header/Header.test.tsx index aaeefae858..a6c1783179 100644 --- a/src/renderer/shared/ui/Header/Header.test.tsx +++ b/src/renderer/shared/ui/Header/Header.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'; import { Header } from './Header'; -describe('components/common/Header', () => { +describe('ui/Header', () => { test('should render component', () => { render(
children
); From 6dbc4723d5053a9ca23eacd7311dd22f1fa73371 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Tue, 9 Jan 2024 12:59:45 +0300 Subject: [PATCH 02/12] feat: notifications effector --- .../MatrixContext/MatrixContext.test.tsx | 6 -- .../context/MatrixContext/MatrixContext.tsx | 32 ++++++++--- .../entities/network/model/network-model.ts | 14 ++--- src/renderer/entities/notification/index.ts | 2 +- .../entities/notification/lib/common/types.ts | 57 ------------------- .../entities/notification/lib/index.ts | 2 - .../notification/lib/notification-utils.ts | 27 +++++++++ .../notification/lib/notificationService.ts | 33 ----------- .../__tests__/notification-model.test.ts | 53 +++++++++++++++++ .../notification/model/notification-model.ts | 48 ++++++++++++++++ .../notifications/EmptyNotifications/index.ts | 1 + .../ui}/EmptyNotifications.tsx | 13 +++-- .../NotificationProvider.tsx | 20 ------- .../notifications/NotificationsList/index.ts | 1 + .../lib/constants.ts | 0 .../model/notification-list-model.ts | 21 +++++++ .../NotificationsList/ui/NotificationRow.tsx | 37 ++++++++++++ .../ui/NotificationsList.tsx | 28 +++++++++ .../notifies/MultisigInviteNotification.tsx} | 15 +++-- .../ui/notifies}/ProxyCreatedNotification.tsx | 16 +++--- src/renderer/features/notifications/index.ts | 3 +- .../pages/Notifications/Notifications.tsx | 41 ++----------- .../pages/Notifications/common/utils.ts | 2 - .../components/NotificationRow.tsx | 25 -------- src/renderer/pages/Operations/Operations.tsx | 2 +- src/renderer/pages/Operations/common/utils.ts | 14 ----- .../pages/Operations/components/LogModal.tsx | 4 +- .../api/storage/__tests__/storage.test.ts | 34 ----------- src/renderer/shared/api/storage/lib/types.ts | 12 +--- .../shared/api/storage/service/dexie.ts | 8 +-- .../storage/service/notificationStorage.ts | 12 ---- .../api/storage/service/storageService.ts | 1 + src/renderer/shared/core/index.ts | 3 + .../shared/core/types/notification.ts | 46 +++++++++++++++ src/renderer/shared/lib/utils/time.ts | 6 ++ 35 files changed, 343 insertions(+), 296 deletions(-) delete mode 100644 src/renderer/entities/notification/lib/common/types.ts delete mode 100644 src/renderer/entities/notification/lib/index.ts create mode 100644 src/renderer/entities/notification/lib/notification-utils.ts delete mode 100644 src/renderer/entities/notification/lib/notificationService.ts create mode 100644 src/renderer/entities/notification/model/__tests__/notification-model.test.ts create mode 100644 src/renderer/entities/notification/model/notification-model.ts create mode 100644 src/renderer/features/notifications/EmptyNotifications/index.ts rename src/renderer/{pages/Notifications/components => features/notifications/EmptyNotifications/ui}/EmptyNotifications.tsx (59%) delete mode 100644 src/renderer/features/notifications/NotificationProvider/NotificationProvider.tsx create mode 100644 src/renderer/features/notifications/NotificationsList/index.ts rename src/renderer/features/notifications/{NotificationProvider => NotificationsList}/lib/constants.ts (100%) create mode 100644 src/renderer/features/notifications/NotificationsList/model/notification-list-model.ts create mode 100644 src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx create mode 100644 src/renderer/features/notifications/NotificationsList/ui/NotificationsList.tsx rename src/renderer/features/notifications/{NotificationProvider/ui/MultisigInvitedNotification.tsx => NotificationsList/ui/notifies/MultisigInviteNotification.tsx} (60%) rename src/renderer/features/notifications/{NotificationProvider/ui => NotificationsList/ui/notifies}/ProxyCreatedNotification.tsx (73%) delete mode 100644 src/renderer/pages/Notifications/common/utils.ts delete mode 100644 src/renderer/pages/Notifications/components/NotificationRow.tsx delete mode 100644 src/renderer/shared/api/storage/__tests__/storage.test.ts delete mode 100644 src/renderer/shared/api/storage/service/notificationStorage.ts create mode 100644 src/renderer/shared/core/types/notification.ts diff --git a/src/renderer/app/providers/context/MatrixContext/MatrixContext.test.tsx b/src/renderer/app/providers/context/MatrixContext/MatrixContext.test.tsx index 92a687d74d..252030b25c 100644 --- a/src/renderer/app/providers/context/MatrixContext/MatrixContext.test.tsx +++ b/src/renderer/app/providers/context/MatrixContext/MatrixContext.test.tsx @@ -23,12 +23,6 @@ jest.mock('@entities/multisig', () => ({ }), })); -jest.mock('@entities/notification', () => ({ - useNotification: jest.fn().mockReturnValue({ - addNotification: jest.fn(), - }), -})); - jest.mock('@app/providers', () => ({ useMultisigChainContext: jest.fn(() => ({ addTask: () => undefined, diff --git a/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx b/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx index c1728eed31..4668f7bd28 100644 --- a/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx +++ b/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx @@ -3,10 +3,27 @@ import { useUnit } from 'effector-react'; import { getCreatedDateFromApi, toAddress, validateCallData } from '@shared/lib/utils'; import { useMultisigEvent, useMultisigTx } from '@entities/multisig'; -import { MultisigNotificationType, useNotification } from '@entities/notification'; import { useMultisigChainContext } from '@app/providers'; import { contactModel } from '@entities/contact'; -import type { Signatory, MultisigAccount, AccountId, Address, CallHash, ChainId } from '@shared/core'; +import { walletModel, accountUtils } from '@entities/wallet'; +import { networkModel } from '@entities/network'; +import { notificationModel } from '@entities/notification'; +import { + Signatory, + MultisigAccount, + AccountId, + Address, + CallHash, + ChainId, + NotificationType, + WalletType, + SigningType, + CryptoType, + ChainType, + AccountType, + MultisigInvite, + NoID, +} from '@shared/core'; import { ApprovePayload, BaseMultisigPayload, @@ -28,9 +45,6 @@ import { SigningStatus, useTransaction, } from '@entities/transaction'; -import { walletModel, accountUtils } from '@entities/wallet'; -import { WalletType, SigningType, CryptoType, ChainType, AccountType } from '@shared/core'; -import { networkModel } from '@entities/network'; type MatrixContextProps = { matrix: ISecureMessenger; @@ -48,7 +62,6 @@ export const MatrixProvider = ({ children }: PropsWithChildren) => { const { addTask } = useMultisigChainContext(); const { getMultisigTx, addMultisigTx, updateMultisigTx, updateCallData } = useMultisigTx({ addTask }); const { decodeCallData } = useTransaction(); - const { addNotification } = useNotification(); const { addEventWithQueue, updateEvent, getEvents } = useMultisigEvent({ addTask }); const apisRef = useRef(apis); @@ -100,7 +113,8 @@ export const MatrixProvider = ({ children }: PropsWithChildren) => { console.log(`No multisig account ${accountId} found. Joining room and adding wallet`); await joinRoom(roomId, content); - await addNotification({ + + notificationModel.events.notificationAdded({ smpRoomId: roomId, multisigAccountId: accountId, multisigAccountName: accountName, @@ -109,8 +123,8 @@ export const MatrixProvider = ({ children }: PropsWithChildren) => { originatorAccountId: creatorAccountId, read: true, dateCreated: Date.now(), - type: MultisigNotificationType.ACCOUNT_INVITED, - }); + type: NotificationType.MULTISIG_INVITE, + } as NoID); } else { console.log(`Multisig account ${accountId} already exists. Trying to change room to ${roomId}`); await changeRoom(roomId, mstAccount, content, creatorAccountId); diff --git a/src/renderer/entities/network/model/network-model.ts b/src/renderer/entities/network/model/network-model.ts index 256458bac4..8b6007b106 100644 --- a/src/renderer/entities/network/model/network-model.ts +++ b/src/renderer/entities/network/model/network-model.ts @@ -163,9 +163,9 @@ type CreateProviderParams = { providerType: ProviderType; }; const createProviderFx = createEffect(({ chainId, providerType, nodes }: CreateProviderParams): ProviderInterface => { - const bindedConnected = scopeBind(connected, { safe: true }); - const bindedDisconnected = scopeBind(disconnected, { safe: true }); - const bindedFailed = scopeBind(failed, { safe: true }); + const boundConnected = scopeBind(connected, { safe: true }); + const boundDisconnected = scopeBind(disconnected, { safe: true }); + const boundFailed = scopeBind(failed, { safe: true }); return networkService.createProvider( chainId, @@ -174,17 +174,17 @@ const createProviderFx = createEffect(({ chainId, providerType, nodes }: CreateP () => { console.info('🟢 provider connected ==> ', chainId); - bindedConnected(chainId); + boundConnected(chainId); }, () => { console.info('🔶 provider disconnected ==> ', chainId); - bindedDisconnected(chainId); + boundDisconnected(chainId); }, () => { console.info('🔴 provider error ==> ', chainId); - bindedFailed(chainId); + boundFailed(chainId); }, ); }); @@ -215,8 +215,8 @@ sample({ sample({ clock: chainStarted, source: { - connections: $connections, chains: $chains, + connections: $connections, }, filter: ({ connections }, chainId) => { return !connections[chainId] || isDisabled(connections[chainId]); diff --git a/src/renderer/entities/notification/index.ts b/src/renderer/entities/notification/index.ts index f41a696fd2..95a047fc0d 100644 --- a/src/renderer/entities/notification/index.ts +++ b/src/renderer/entities/notification/index.ts @@ -1 +1 @@ -export * from './lib'; +export { notificationModel } from './model/notification-model'; diff --git a/src/renderer/entities/notification/lib/common/types.ts b/src/renderer/entities/notification/lib/common/types.ts deleted file mode 100644 index e85687b467..0000000000 --- a/src/renderer/entities/notification/lib/common/types.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { ID, NotificationDS } from '@shared/api/storage'; -import type { AccountId, CallHash, ChainId, Timepoint } from '@shared/core'; -import { ProxyType } from '@shared/core'; - -export interface INotificationService { - getNotifications: (where?: Partial) => Promise; - getLiveNotifications: (where?: Partial) => NotificationDS[]; - addNotification: (notification: Notification) => Promise; -} - -export enum MultisigNotificationType { - ACCOUNT_INVITED = 'MultisigAccountInvitedNotification', - MST_CREATED = 'MultisigCreatedNotification', - MST_APPROVED = 'MultisigApprovedNotification', - MST_EXECUTED = 'MultisigExecutedNotification', - MST_CANCELLED = 'MultisigCancelledNotification', -} - -export enum ProxyNotificationType { - PROXY_CREATED = 'ProxyCreatedNotification', -} - -export type ProxyCreatedNotificationType = { - proxyAccountId: AccountId; - proxiedAccountId: AccountId; - proxyType: ProxyType; - chainId: ChainId; -}; - -export type MultisigAccountInvitedNotification = { - signatories: AccountId[]; - threshold: number; - multisigAccountName: string; -}; - -export type MultisigOperationNotification = { - callHash: CallHash; - callTimepoint: Timepoint; - chainId: ChainId; -}; - -export type MultisigNotification = { - multisigAccountId: AccountId; - originatorAccountId: AccountId; - smpRoomId: string; -} & (MultisigAccountInvitedNotification | MultisigOperationNotification); - -type NotificationBase = { - read: boolean; - dateCreated: number; -}; - -export type Notification = NotificationBase & - ( - | ({ type: MultisigNotificationType } & MultisigNotification) - | ({ type: ProxyNotificationType } & ProxyCreatedNotificationType) - ); diff --git a/src/renderer/entities/notification/lib/index.ts b/src/renderer/entities/notification/lib/index.ts deleted file mode 100644 index d3e11b7280..0000000000 --- a/src/renderer/entities/notification/lib/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './notificationService'; -export * from './common/types'; diff --git a/src/renderer/entities/notification/lib/notification-utils.ts b/src/renderer/entities/notification/lib/notification-utils.ts new file mode 100644 index 0000000000..d6ac9a76a3 --- /dev/null +++ b/src/renderer/entities/notification/lib/notification-utils.ts @@ -0,0 +1,27 @@ +import { NotificationType } from '@shared/core'; +import type { Notification, MultisigInvite, MultisigOperation, ProxyCreated } from '@shared/core'; + +export const notificationUtils = { + isMultisigInvite, + isMultisigOperation, + isProxyCreation, +}; + +function isMultisigInvite(notification: Notification): notification is MultisigInvite { + return notification.type === NotificationType.MULTISIG_INVITE; +} + +function isMultisigOperation(notification: Notification): notification is MultisigOperation { + const Operations = [ + NotificationType.MULTISIG_CREATED, + NotificationType.MULTISIG_APPROVED, + NotificationType.MULTISIG_CANCELLED, + NotificationType.MULTISIG_EXECUTED, + ]; + + return Operations.includes(notification.type); +} + +function isProxyCreation(notification: Notification): notification is ProxyCreated { + return notification.type === NotificationType.MULTISIG_INVITE; +} diff --git a/src/renderer/entities/notification/lib/notificationService.ts b/src/renderer/entities/notification/lib/notificationService.ts deleted file mode 100644 index 88e880825c..0000000000 --- a/src/renderer/entities/notification/lib/notificationService.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { useLiveQuery } from 'dexie-react-hooks'; - -import { storage, NotificationDS } from '@shared/api/storage'; -import { INotificationService, Notification } from './common/types'; - -export const useNotification = (): INotificationService => { - const notificationStorage = storage.connectTo('notifications'); - - if (!notificationStorage) { - throw new Error('=== 🔴 Notification storage in not defined 🔴 ==='); - } - const { getNotifications, addNotification } = notificationStorage; - - const getLiveNotifications = (where?: Partial): NotificationDS[] => { - const query = () => { - try { - return getNotifications(where); - } catch (error) { - console.warn('Error trying to get notifications'); - - return Promise.resolve([]); - } - }; - - return useLiveQuery(query, [], []); - }; - - return { - getNotifications, - getLiveNotifications, - addNotification, - }; -}; diff --git a/src/renderer/entities/notification/model/__tests__/notification-model.test.ts b/src/renderer/entities/notification/model/__tests__/notification-model.test.ts new file mode 100644 index 0000000000..79d8c66c71 --- /dev/null +++ b/src/renderer/entities/notification/model/__tests__/notification-model.test.ts @@ -0,0 +1,53 @@ +import { fork, allSettled } from 'effector'; + +import { Notification, NotificationType } from '@shared/core'; +import { storageService } from '@shared/api/storage'; +import { notificationModel } from '../notification-model'; + +const notifications = [ + { + id: 1, + read: true, + dateCreated: Date.now(), + type: NotificationType.MULTISIG_INVITE, + }, +] as Notification[]; + +const newNotification = { + id: 2, + read: true, + dateCreated: Date.now(), + type: NotificationType.MULTISIG_INVITE, +} as Notification; + +describe('entities/notification/model/notification-model', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('should populate $notifications on notificationsStarted', async () => { + const spyReadAll = jest.spyOn(storageService.notifications, 'readAll').mockResolvedValue(notifications); + + const scope = fork({ + values: new Map().set(notificationModel.$notifications, []), + }); + + await allSettled(notificationModel.events.notificationsStarted, { scope }); + + expect(spyReadAll).toHaveBeenCalled(); + expect(scope.getState(notificationModel.$notifications)).toEqual(notifications); + }); + + test('should add new notification on notificationAdded', async () => { + const spyCreate = jest.spyOn(storageService.notifications, 'create').mockResolvedValue(newNotification); + + const scope = fork({ + values: new Map().set(notificationModel.$notifications, []), + }); + + await allSettled(notificationModel.events.notificationAdded, { scope, params: newNotification }); + + expect(spyCreate).toHaveBeenCalled(); + expect(scope.getState(notificationModel.$notifications)).toEqual([newNotification]); + }); +}); diff --git a/src/renderer/entities/notification/model/notification-model.ts b/src/renderer/entities/notification/model/notification-model.ts new file mode 100644 index 0000000000..eedba2aab4 --- /dev/null +++ b/src/renderer/entities/notification/model/notification-model.ts @@ -0,0 +1,48 @@ +import { createStore, createEvent, sample, createEffect } from 'effector'; + +import { storageService } from '@shared/api/storage'; +import type { NoID, Notification } from '@shared/core'; + +const notificationsStarted = createEvent(); +const notificationAdded = createEvent>(); + +const $notifications = createStore([]); + +const populateNotificationsFx = createEffect((): Promise => { + return storageService.notifications.readAll(); +}); + +const addNotificationsFx = createEffect((notification: NoID): Promise => { + return storageService.notifications.create(notification); +}); + +sample({ + clock: notificationsStarted, + target: populateNotificationsFx, +}); + +sample({ + clock: populateNotificationsFx.doneData, + target: $notifications, +}); + +sample({ + clock: notificationAdded, + target: addNotificationsFx, +}); + +sample({ + clock: addNotificationsFx.doneData, + source: $notifications, + filter: (_, notification) => Boolean(notification), + fn: (notifications, notification) => notifications.concat(notification!), + target: $notifications, +}); + +export const notificationModel = { + $notifications, + events: { + notificationsStarted, + notificationAdded, + }, +}; diff --git a/src/renderer/features/notifications/EmptyNotifications/index.ts b/src/renderer/features/notifications/EmptyNotifications/index.ts new file mode 100644 index 0000000000..f1e7a12b27 --- /dev/null +++ b/src/renderer/features/notifications/EmptyNotifications/index.ts @@ -0,0 +1 @@ +export { EmptyNotifications } from './ui/EmptyNotifications'; diff --git a/src/renderer/pages/Notifications/components/EmptyNotifications.tsx b/src/renderer/features/notifications/EmptyNotifications/ui/EmptyNotifications.tsx similarity index 59% rename from src/renderer/pages/Notifications/components/EmptyNotifications.tsx rename to src/renderer/features/notifications/EmptyNotifications/ui/EmptyNotifications.tsx index 1209c9d1ae..5891f1df91 100644 --- a/src/renderer/pages/Notifications/components/EmptyNotifications.tsx +++ b/src/renderer/features/notifications/EmptyNotifications/ui/EmptyNotifications.tsx @@ -1,9 +1,16 @@ -import { Icon, BodyText } from '@shared/ui'; +import { useUnit } from 'effector-react'; + import { useI18n } from '@app/providers'; +import { Icon, BodyText } from '@shared/ui'; +import { notificationModel } from '@entities/notification'; -const EmptyNotifications = () => { +export const EmptyNotifications = () => { const { t } = useI18n(); + const notifications = useUnit(notificationModel.$notifications); + + if (notifications.length > 0) return null; + return (
@@ -11,5 +18,3 @@ const EmptyNotifications = () => {
); }; - -export default EmptyNotifications; diff --git a/src/renderer/features/notifications/NotificationProvider/NotificationProvider.tsx b/src/renderer/features/notifications/NotificationProvider/NotificationProvider.tsx deleted file mode 100644 index 89dad4140c..0000000000 --- a/src/renderer/features/notifications/NotificationProvider/NotificationProvider.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { MultisigNotificationType, Notification, ProxyNotificationType } from '@entities/notification'; -import { MultisigInvitedNotification } from './ui/MultisigInvitedNotification'; -import { ProxyCreatedNotification } from './ui/ProxyCreatedNotification'; - -type Props = { - notification: Notification; -}; - -export const NotificationProvider = ({ notification }: Props) => { - return { - [MultisigNotificationType.ACCOUNT_INVITED]: (n: Notification) => , - [MultisigNotificationType.MST_CREATED]: () => null, - [MultisigNotificationType.MST_APPROVED]: () => null, - [MultisigNotificationType.MST_EXECUTED]: () => null, - [MultisigNotificationType.MST_CANCELLED]: () => null, - [ProxyNotificationType.PROXY_CREATED]: (n: Notification) => ( - - ), - }[notification.type](notification); -}; diff --git a/src/renderer/features/notifications/NotificationsList/index.ts b/src/renderer/features/notifications/NotificationsList/index.ts new file mode 100644 index 0000000000..48ebaca35d --- /dev/null +++ b/src/renderer/features/notifications/NotificationsList/index.ts @@ -0,0 +1 @@ +export { NotificationsList } from './ui/NotificationsList'; diff --git a/src/renderer/features/notifications/NotificationProvider/lib/constants.ts b/src/renderer/features/notifications/NotificationsList/lib/constants.ts similarity index 100% rename from src/renderer/features/notifications/NotificationProvider/lib/constants.ts rename to src/renderer/features/notifications/NotificationsList/lib/constants.ts diff --git a/src/renderer/features/notifications/NotificationsList/model/notification-list-model.ts b/src/renderer/features/notifications/NotificationsList/model/notification-list-model.ts new file mode 100644 index 0000000000..7dcc4b70d7 --- /dev/null +++ b/src/renderer/features/notifications/NotificationsList/model/notification-list-model.ts @@ -0,0 +1,21 @@ +import { combine } from 'effector'; +import { groupBy } from 'lodash'; +import { format } from 'date-fns'; +import { enGB } from 'date-fns/locale'; + +import { notificationModel } from '@entities/notification'; +import { sortByDateDesc } from '@shared/lib/utils'; + +const $notificationGroups = combine(notificationModel.$notifications, (notifications) => { + if (notifications.length === 0) return []; + + const group = groupBy(notifications, ({ dateCreated }) => { + return format(new Date(dateCreated || 0), 'PP', { locale: enGB }); + }); + + return Object.entries(group).sort(sortByDateDesc); +}); + +export const notificationListModel = { + $notificationGroups, +}; diff --git a/src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx b/src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx new file mode 100644 index 0000000000..2e3f4bf3a3 --- /dev/null +++ b/src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx @@ -0,0 +1,37 @@ +import { format } from 'date-fns'; +import { ReactNode } from 'react'; + +import { FootnoteText } from '@shared/ui'; +import { useI18n } from '@app/providers'; +import { MultisigInviteNotification } from './notifies/MultisigInviteNotification'; +import { ProxyCreatedNotification } from './notifies/ProxyCreatedNotification'; +import { NotificationType } from '@shared/core'; +import type { Notification, MultisigInvite, ProxyCreated } from '@shared/core'; + +const Notifications: Record ReactNode> = { + [NotificationType.MULTISIG_INVITE]: (notify) => ( + + ), + [NotificationType.MULTISIG_CREATED]: () => null, + [NotificationType.MULTISIG_APPROVED]: () => null, + [NotificationType.MULTISIG_CANCELLED]: () => null, + [NotificationType.MULTISIG_EXECUTED]: () => null, + [NotificationType.PROXY_CREATED]: (notify) => , +}; + +type Props = { + notification: Notification; +}; + +export const NotificationRow = ({ notification }: Props) => { + const { dateLocale } = useI18n(); + + return ( +
  • + {Notifications[notification.type](notification)} + + {format(new Date(notification.dateCreated || 0), 'p', { locale: dateLocale })} + +
  • + ); +}; diff --git a/src/renderer/features/notifications/NotificationsList/ui/NotificationsList.tsx b/src/renderer/features/notifications/NotificationsList/ui/NotificationsList.tsx new file mode 100644 index 0000000000..afe83966df --- /dev/null +++ b/src/renderer/features/notifications/NotificationsList/ui/NotificationsList.tsx @@ -0,0 +1,28 @@ +import { useUnit } from 'effector-react'; + +import { FootnoteText } from '@shared/ui'; +import { NotificationRow } from './NotificationRow'; +import { notificationListModel } from '../model/notification-list-model'; + +export const NotificationsList = () => { + const notificationGroups = useUnit(notificationListModel.$notificationGroups); + + if (notificationGroups.length === 0) return null; + + return ( +
    + {notificationGroups.map(([date, notifications]) => ( +
    + {date} +
      + {notifications.map((notification) => ( +
    • + +
    • + ))} +
    +
    + ))} +
    + ); +}; diff --git a/src/renderer/features/notifications/NotificationProvider/ui/MultisigInvitedNotification.tsx b/src/renderer/features/notifications/NotificationsList/ui/notifies/MultisigInviteNotification.tsx similarity index 60% rename from src/renderer/features/notifications/NotificationProvider/ui/MultisigInvitedNotification.tsx rename to src/renderer/features/notifications/NotificationsList/ui/notifies/MultisigInviteNotification.tsx index f7deda2aa2..3eaf83e0c2 100644 --- a/src/renderer/features/notifications/NotificationProvider/ui/MultisigInvitedNotification.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/notifies/MultisigInviteNotification.tsx @@ -1,24 +1,23 @@ import { Trans } from 'react-i18next'; import { useUnit } from 'effector-react'; -import { MultisigAccountInvitedNotification, MultisigNotification, Notification } from '@entities/notification'; import { WalletIcon, walletModel } from '@entities/wallet'; +import type { MultisigInvite } from '@shared/core'; import { WalletType } from '@shared/core'; import { BodyText } from '@shared/ui'; import { useI18n } from '@app/providers'; type Props = { - notification: Notification; + notification: MultisigInvite; }; -export const MultisigInvitedNotification = ({ notification }: Props) => { +export const MultisigInviteNotification = ({ notification }: Props) => { const { t } = useI18n(); const wallets = useUnit(walletModel.$wallets); const accounts = useUnit(walletModel.$accounts); - const typedNotification = notification as Notification & MultisigNotification & MultisigAccountInvitedNotification; - const multisigAccount = accounts.find((a) => a.accountId === typedNotification.multisigAccountId); + const multisigAccount = accounts.find((a) => a.accountId === notification.multisigAccountId); const notificationWallet = wallets.find((w) => w.id === multisigAccount?.walletId); return ( @@ -31,9 +30,9 @@ export const MultisigInvitedNotification = ({ notification }: Props) => { t={t} i18nKey="notifications.details.newMultisigAccountDescription" values={{ - threshold: typedNotification.threshold, - signatories: typedNotification.signatories.length, - name: notificationWallet?.name || typedNotification.multisigAccountName, + threshold: notification.threshold, + signatories: notification.signatories.length, + name: notificationWallet?.name || notification.multisigAccountName, }} /> diff --git a/src/renderer/features/notifications/NotificationProvider/ui/ProxyCreatedNotification.tsx b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx similarity index 73% rename from src/renderer/features/notifications/NotificationProvider/ui/ProxyCreatedNotification.tsx rename to src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx index 48ac2c6774..8f7ce573f3 100644 --- a/src/renderer/features/notifications/NotificationProvider/ui/ProxyCreatedNotification.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx @@ -1,17 +1,16 @@ import { Trans } from 'react-i18next'; import { useUnit } from 'effector-react'; -import { Notification, ProxyCreatedNotificationType } from '@entities/notification'; import { WalletIcon, walletModel } from '@entities/wallet'; import { BodyText, Identicon } from '@shared/ui'; import { useI18n } from '@app/providers'; import { toAddress } from '@shared/lib/utils'; import { ChainTitle } from '@entities/chain'; -import { WalletType } from '@shared/core'; -import { ProxyTypeOperation } from '../lib/constants'; +import { WalletType, ProxyCreated } from '@shared/core'; +import { ProxyTypeOperation } from '../../lib/constants'; type Props = { - notification: Notification; + notification: ProxyCreated; }; export const ProxyCreatedNotification = ({ notification }: Props) => { @@ -20,10 +19,9 @@ export const ProxyCreatedNotification = ({ notification }: Props) => { const wallets = useUnit(walletModel.$wallets); const accounts = useUnit(walletModel.$accounts); - const typedNotification = notification as Notification & ProxyCreatedNotificationType; - const proxyAccount = accounts.find((a) => a.accountId === typedNotification.proxyAccountId); + const proxyAccount = accounts.find((a) => a.accountId === notification.proxyAccountId); const proxyWallet = wallets.find((w) => w.id === proxyAccount?.walletId); - const proxiedAccount = accounts.find((a) => a.accountId === typedNotification.proxiedAccountId); + const proxiedAccount = accounts.find((a) => a.accountId === notification.proxiedAccountId); const proxiedWallet = wallets.find((w) => w.id === proxiedAccount?.walletId); if (!proxiedWallet) return null; @@ -38,7 +36,7 @@ export const ProxyCreatedNotification = ({ notification }: Props) => { /> ); - const chain = ; + const chain = ; const walletIcon = ; @@ -64,7 +62,7 @@ export const ProxyCreatedNotification = ({ notification }: Props) => { i18nKey="notifications.details.proxyCreatedDetails" values={{ name: proxiedWallet?.name, - operations: t(ProxyTypeOperation[typedNotification.proxyType]), + operations: t(ProxyTypeOperation[notification.proxyType]), }} components={{ chain, walletIcon }} /> diff --git a/src/renderer/features/notifications/index.ts b/src/renderer/features/notifications/index.ts index e04d98efaf..edeea55dbc 100644 --- a/src/renderer/features/notifications/index.ts +++ b/src/renderer/features/notifications/index.ts @@ -1 +1,2 @@ -export { NotificationProvider } from './NotificationProvider/NotificationProvider'; +export { NotificationsList } from './NotificationsList'; +export { EmptyNotifications } from './EmptyNotifications'; diff --git a/src/renderer/pages/Notifications/Notifications.tsx b/src/renderer/pages/Notifications/Notifications.tsx index 3196f96eb9..15e610d7a4 100644 --- a/src/renderer/pages/Notifications/Notifications.tsx +++ b/src/renderer/pages/Notifications/Notifications.tsx @@ -1,47 +1,16 @@ -import { groupBy } from 'lodash'; -import { format } from 'date-fns'; - +import { Header } from '@shared/ui'; import { useI18n } from '@app/providers'; -import EmptyNotifications from './components/EmptyNotifications'; -import { sortByDate } from './common/utils'; -import { FootnoteText, Header } from '@shared/ui'; -import { useNotification } from '@entities/notification'; -import { NotificationRow } from './components/NotificationRow'; +import { NotificationsList, EmptyNotifications } from '@features/notifications'; export const Notifications = () => { - const { t, dateLocale } = useI18n(); - - const { getLiveNotifications } = useNotification(); - const notifications = getLiveNotifications(); - - const groupedNotifications = groupBy(notifications, ({ dateCreated }) => - format(new Date(dateCreated || 0), 'PP', { locale: dateLocale }), - ); + const { t } = useI18n(); return (
    -
    - {notifications.length ? ( - Object.entries(groupedNotifications) - .sort(sortByDate) - .map(([date, notifies]) => ( -
    - {date} -
      - {notifies - .sort((a, b) => (b.dateCreated || 0) - (a.dateCreated || 0)) - .map((notification) => ( - - ))} -
    -
    - )) - ) : ( - - )} -
    + +
    ); }; diff --git a/src/renderer/pages/Notifications/common/utils.ts b/src/renderer/pages/Notifications/common/utils.ts deleted file mode 100644 index 29db3933c5..0000000000 --- a/src/renderer/pages/Notifications/common/utils.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const sortByDate = ([dateA]: [string, any[]], [dateB]: [string, any[]]) => - new Date(dateA) < new Date(dateB) ? 1 : -1; diff --git a/src/renderer/pages/Notifications/components/NotificationRow.tsx b/src/renderer/pages/Notifications/components/NotificationRow.tsx deleted file mode 100644 index 0d0933e308..0000000000 --- a/src/renderer/pages/Notifications/components/NotificationRow.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { format } from 'date-fns'; - -import { FootnoteText } from '@shared/ui'; -import { useI18n } from '@app/providers'; -import { NotificationProvider } from '@features/notifications'; -import { Notification } from '@entities/notification'; - -type Props = { - notification: Notification; -}; - -export const NotificationRow = ({ notification }: Props) => { - const { dateLocale } = useI18n(); - - const { dateCreated } = notification; - - return ( -
  • - - - {format(new Date(dateCreated || 0), 'p', { locale: dateLocale })} - -
  • - ); -}; diff --git a/src/renderer/pages/Operations/Operations.tsx b/src/renderer/pages/Operations/Operations.tsx index ec9f5d8a79..e5a0dde6d0 100644 --- a/src/renderer/pages/Operations/Operations.tsx +++ b/src/renderer/pages/Operations/Operations.tsx @@ -6,7 +6,6 @@ import { useUnit } from 'effector-react'; import { useI18n } from '@app/providers'; import EmptyOperations from './components/EmptyState/EmptyOperations'; import Operation from './components/Operation'; -import { sortByDateDesc } from './common/utils'; import { FootnoteText, Header } from '@shared/ui'; import { MultisigTransactionDS } from '@shared/api/storage'; import { useMultisigTx, useMultisigEvent } from '@entities/multisig'; @@ -15,6 +14,7 @@ import { OperationsFilter } from '@features/operation'; import { walletModel, accountUtils } from '@entities/wallet'; import { priceProviderModel } from '@entities/price'; import { networkModel } from '@entities/network'; +import { sortByDateDesc } from '@shared/lib/utils'; export const Operations = () => { const { t, dateLocale } = useI18n(); diff --git a/src/renderer/pages/Operations/common/utils.ts b/src/renderer/pages/Operations/common/utils.ts index 6a9d08b414..500506ff55 100644 --- a/src/renderer/pages/Operations/common/utils.ts +++ b/src/renderer/pages/Operations/common/utils.ts @@ -125,20 +125,6 @@ export const getMultisigSignOperationTitle = ( return ''; }; -export const sortByDateDesc = ([dateA]: [string, T[]], [dateB]: [string, T[]]) => - new Date(dateA) < new Date(dateB) ? 1 : -1; - -export const sortByDateAsc = ([dateA]: [string, T[]], [dateB]: [string, T[]]) => - new Date(dateA) > new Date(dateB) ? 1 : -1; - -export const getExtrinsicLink = (hash?: HexString, explorers?: Explorer[]): string | undefined => { - const extrinsicLink = explorers?.find((e) => e.extrinsic)?.extrinsic; - - if (!extrinsicLink || !hash) return; - - return extrinsicLink.replace('{hash}', hash); -}; - export const getMultisigExtrinsicLink = ( callHash?: HexString, indexCreated?: number, diff --git a/src/renderer/pages/Operations/components/LogModal.tsx b/src/renderer/pages/Operations/components/LogModal.tsx index c19513d02d..e8f342daf0 100644 --- a/src/renderer/pages/Operations/components/LogModal.tsx +++ b/src/renderer/pages/Operations/components/LogModal.tsx @@ -7,9 +7,9 @@ import { chainsService, ExtendedChain } from '@entities/network'; import { MultisigEvent, SigningStatus } from '@entities/transaction/model/transaction'; import { TransactionTitle } from './TransactionTitle/TransactionTitle'; import OperationStatus from './OperationStatus'; -import { getSignatoryName, getTransactionAmount, sortByDateAsc } from '../common/utils'; +import { getSignatoryName, getTransactionAmount } from '../common/utils'; import { BaseModal, BodyText, FootnoteText, Identicon, ContextMenu, ExplorerLink, IconButton } from '@shared/ui'; -import { getAssetById, SS58_DEFAULT_PREFIX, toAddress, getExtrinsicExplorer } from '@shared/lib/utils'; +import { getAssetById, SS58_DEFAULT_PREFIX, toAddress, getExtrinsicExplorer, sortByDateAsc } from '@shared/lib/utils'; import { useMultisigEvent } from '@entities/multisig'; import { MultisigTransactionDS } from '@shared/api/storage'; import { AssetBalance } from '@entities/asset'; diff --git a/src/renderer/shared/api/storage/__tests__/storage.test.ts b/src/renderer/shared/api/storage/__tests__/storage.test.ts deleted file mode 100644 index 905534e49d..0000000000 --- a/src/renderer/shared/api/storage/__tests__/storage.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { storage } from '../service/dexie'; - -jest.mock( - 'dexie', - jest.fn().mockImplementation(() => { - return jest.fn().mockReturnValue({ - version: jest.fn().mockReturnValue({ - stores: jest.fn().mockReturnValue({ - upgrade: jest.fn(), - }), - }), - table: jest.fn(), - }); - }), -); - -describe('service/storage/storage', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - test('should return right data storage', () => { - const balancesStorage = storage.connectTo('balances'); - - expect(balancesStorage).toBeDefined(); - }); - - test('should return undefined for wrong storage name', () => { - // @ts-ignore remove TS warning about wrong storage - const wrongStorage = storage.connectTo('wrong_name'); - - expect(wrongStorage).toBeUndefined(); - }); -}); diff --git a/src/renderer/shared/api/storage/lib/types.ts b/src/renderer/shared/api/storage/lib/types.ts index 044579c260..b342408015 100644 --- a/src/renderer/shared/api/storage/lib/types.ts +++ b/src/renderer/shared/api/storage/lib/types.ts @@ -1,6 +1,5 @@ import { Table } from 'dexie'; -import { Notification } from '@entities/notification'; import type { Metadata } from '@entities/network'; import { MultisigEvent, MultisigTransaction, MultisigTransactionKey } from '@entities/transaction/model/transaction'; import type { @@ -14,6 +13,7 @@ import type { BalanceKey, ProxyAccount, Connection, + Notification, } from '@shared/core'; // ===================================================== @@ -75,10 +75,6 @@ export interface IMultisigTransactionStorage { ) => Promise; deleteMultisigTxs: (accountId: AccountId) => Promise; } -export interface INotificationStorage { - getNotifications: (where?: Partial) => Promise; - addNotification: (notification: Notification) => Promise; -} // ===================================================== // ================== Storage Schemes ================== @@ -88,7 +84,6 @@ export type DataStorage = { balances: IBalanceStorage; multisigTransactions: IMultisigTransactionStorage; multisigEvents: IMultisigEventStorage; - notifications: INotificationStorage; metadata: IMetadataStorage; }; @@ -98,7 +93,6 @@ type WithID = { id?: ID } & T; export type BalanceDS = WithID; export type MultisigTransactionDS = WithID; export type MultisigEventDS = WithID; -export type NotificationDS = WithID; export type MetadataDS = WithID; export type TWallet = Table; @@ -106,8 +100,8 @@ export type TContact = Table; export type TAccount = Table; export type TBalance = Table; export type TConnection = Table; -export type TProxy = Table; +export type TProxy = Table; export type TMultisigTransaction = Table; export type TMultisigEvent = Table; -export type TNotification = Table; +export type TNotification = Table; export type TMetadata = Table; diff --git a/src/renderer/shared/api/storage/service/dexie.ts b/src/renderer/shared/api/storage/service/dexie.ts index 23e7492043..56090f732d 100644 --- a/src/renderer/shared/api/storage/service/dexie.ts +++ b/src/renderer/shared/api/storage/service/dexie.ts @@ -2,7 +2,6 @@ import Dexie from 'dexie'; import { useBalanceStorage } from './balanceStorage'; import { useTransactionStorage } from './transactionStorage'; -import { useNotificationStorage } from './notificationStorage'; import { useMultisigEventStorage } from './multisigEventStorage'; import { useMetadataStorage } from './metadataStorage'; import { migrateEvents, migrateWallets } from '../migration'; @@ -62,8 +61,10 @@ class DexieStorage extends Dexie { }) .upgrade(migrateWallets); - this.version(20).stores({ + this.version(21).stores({ proxies: '++id', + connections: '++id', + notifications: '++id', }); this.connections = this.table('connections'); @@ -94,8 +95,6 @@ class StorageFactory implements IStorage { return useTransactionStorage(this.dexieDB.multisigTransactions) as DataStorage[T]; case 'multisigEvents': 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: @@ -114,4 +113,5 @@ export const dexieStorage = { contacts: dexie.contacts, connections: dexie.connections, proxies: dexie.proxies, + notifications: dexie.notifications, }; diff --git a/src/renderer/shared/api/storage/service/notificationStorage.ts b/src/renderer/shared/api/storage/service/notificationStorage.ts deleted file mode 100644 index a513025064..0000000000 --- a/src/renderer/shared/api/storage/service/notificationStorage.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Notification } from '@entities/notification'; -import { ID, INotificationStorage, NotificationDS, TNotification } from '../lib/types'; - -export const useNotificationStorage = (db: TNotification): INotificationStorage => ({ - getNotifications: (where?: Partial): Promise => { - return where ? db.where(where).toArray() : db.toArray(); - }, - - addNotification: (notification: Notification): Promise => { - return db.add(notification); - }, -}); diff --git a/src/renderer/shared/api/storage/service/storageService.ts b/src/renderer/shared/api/storage/service/storageService.ts index 0b935858bd..f5cccccc9d 100644 --- a/src/renderer/shared/api/storage/service/storageService.ts +++ b/src/renderer/shared/api/storage/service/storageService.ts @@ -114,4 +114,5 @@ export const storageService = { contacts: new StorageService(dexieStorage.contacts), connections: new StorageService(dexieStorage.connections), proxies: new StorageService(dexieStorage.proxies), + notifications: new StorageService(dexieStorage.notifications), }; diff --git a/src/renderer/shared/core/index.ts b/src/renderer/shared/core/index.ts index 898b936980..7d4c07607d 100644 --- a/src/renderer/shared/core/index.ts +++ b/src/renderer/shared/core/index.ts @@ -53,3 +53,6 @@ export type { Stake, Unlocking } from './types/stake'; export { ProxyType } from './types/proxy'; export type { ProxyAccount, PartialProxyAccount } from './types/proxy'; + +export type { Notification, MultisigInvite, MultisigOperation, ProxyCreated } from './types/notification'; +export { NotificationType } from './types/notification'; diff --git a/src/renderer/shared/core/types/notification.ts b/src/renderer/shared/core/types/notification.ts new file mode 100644 index 0000000000..c205a7d8cd --- /dev/null +++ b/src/renderer/shared/core/types/notification.ts @@ -0,0 +1,46 @@ +import { ProxyType } from '@shared/core'; +import type { ID, AccountId, CallHash, ChainId, Timepoint } from '@shared/core'; + +export const enum NotificationType { + MULTISIG_INVITE = 'MultisigAccountInvitedNotification', + MULTISIG_CREATED = 'MultisigCreatedNotification', + MULTISIG_APPROVED = 'MultisigApprovedNotification', + MULTISIG_EXECUTED = 'MultisigExecutedNotification', + MULTISIG_CANCELLED = 'MultisigCancelledNotification', + + PROXY_CREATED = 'ProxyCreatedNotification', +} + +type BaseNotification = { + id: ID; + read: boolean; + dateCreated: number; + type: NotificationType; +}; + +type MultisigBaseNotification = BaseNotification & { + multisigAccountId: AccountId; + originatorAccountId: AccountId; + smpRoomId: string; +}; + +export type MultisigInvite = MultisigBaseNotification & { + signatories: AccountId[]; + threshold: number; + multisigAccountName: string; +}; + +export type MultisigOperation = MultisigBaseNotification & { + callHash: CallHash; + callTimepoint: Timepoint; + chainId: ChainId; +}; + +export type ProxyCreated = BaseNotification & { + proxyAccountId: AccountId; + proxiedAccountId: AccountId; + proxyType: ProxyType; + chainId: ChainId; +}; + +export type Notification = MultisigInvite | MultisigOperation | ProxyCreated; diff --git a/src/renderer/shared/lib/utils/time.ts b/src/renderer/shared/lib/utils/time.ts index 0bd0715a19..362fffa1b9 100644 --- a/src/renderer/shared/lib/utils/time.ts +++ b/src/renderer/shared/lib/utils/time.ts @@ -75,3 +75,9 @@ export const getDurationParams = ( return {}; } }; + +export const sortByDateDesc = ([dateA]: [string, T[]], [dateB]: [string, T[]]): number => + new Date(dateA) < new Date(dateB) ? 1 : -1; + +export const sortByDateAsc = ([dateA]: [string, T[]], [dateB]: [string, T[]]): number => + new Date(dateA) > new Date(dateB) ? 1 : -1; From d37bf606fec324a4b205632b6e58582539c9dbcb Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Tue, 9 Jan 2024 16:33:04 +0300 Subject: [PATCH 03/12] feat: add notifications event --- src/renderer/app/index.tsx | 2 + .../context/MatrixContext/MatrixContext.tsx | 4 +- .../notification/lib/notification-utils.ts | 4 +- .../__tests__/notification-model.test.ts | 12 ++-- .../notification/model/notification-model.ts | 54 ++++++++++++-- .../entities/proxy/model/proxy-model.ts | 9 +-- .../model/wallet-connect-model.ts | 8 +-- .../NotificationsList/ui/NotificationRow.tsx | 11 ++- .../ui/notifies/ProxyCreatedNotification.tsx | 37 +++++----- .../ui/notifies/ProxyRemovedNotification.tsx | 72 +++++++++++++++++++ src/renderer/features/proxies/lib/utils.ts | 25 ++++++- .../features/proxies/model/proxies-model.ts | 42 ++++++++--- .../features/proxies/workers/proxy-worker.ts | 8 +-- src/renderer/shared/core/index.ts | 2 +- .../shared/core/types/notification.ts | 5 +- src/renderer/shared/core/types/proxy.ts | 2 +- 16 files changed, 228 insertions(+), 69 deletions(-) create mode 100644 src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx diff --git a/src/renderer/app/index.tsx b/src/renderer/app/index.tsx index 78437221bf..26a850a3c1 100644 --- a/src/renderer/app/index.tsx +++ b/src/renderer/app/index.tsx @@ -5,6 +5,7 @@ import log from 'electron-log'; import { App } from './App'; import { kernelModel } from '@shared/core'; import { networkModel } from '@entities/network'; +import { notificationModel } from '@entities/notification'; import { balanceSubscriptionModel } from '@features/balances'; import { assetsModel } from '@pages/Assets/Assets/model/assets-model'; import './i18n'; @@ -33,6 +34,7 @@ kernelModel.events.appStarted(); networkModel.events.networkStarted(); balanceSubscriptionModel.events.balancesSubscribed(); assetsModel.events.assetsStarted(); +notificationModel.events.notificationsStarted(); createRoot(container).render( diff --git a/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx b/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx index 4668f7bd28..a17b81b049 100644 --- a/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx +++ b/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx @@ -114,7 +114,7 @@ export const MatrixProvider = ({ children }: PropsWithChildren) => { await joinRoom(roomId, content); - notificationModel.events.notificationAdded({ + notificationModel.events.notificationsAdded([{ smpRoomId: roomId, multisigAccountId: accountId, multisigAccountName: accountName, @@ -124,7 +124,7 @@ export const MatrixProvider = ({ children }: PropsWithChildren) => { read: true, dateCreated: Date.now(), type: NotificationType.MULTISIG_INVITE, - } as NoID); + }] as NoID[]); } else { console.log(`Multisig account ${accountId} already exists. Trying to change room to ${roomId}`); await changeRoom(roomId, mstAccount, content, creatorAccountId); diff --git a/src/renderer/entities/notification/lib/notification-utils.ts b/src/renderer/entities/notification/lib/notification-utils.ts index d6ac9a76a3..91f419e4d3 100644 --- a/src/renderer/entities/notification/lib/notification-utils.ts +++ b/src/renderer/entities/notification/lib/notification-utils.ts @@ -1,5 +1,5 @@ import { NotificationType } from '@shared/core'; -import type { Notification, MultisigInvite, MultisigOperation, ProxyCreated } from '@shared/core'; +import type { Notification, MultisigInvite, MultisigOperation, ProxyAction } from '@shared/core'; export const notificationUtils = { isMultisigInvite, @@ -22,6 +22,6 @@ function isMultisigOperation(notification: Notification): notification is Multis return Operations.includes(notification.type); } -function isProxyCreation(notification: Notification): notification is ProxyCreated { +function isProxyCreation(notification: Notification): notification is ProxyAction { return notification.type === NotificationType.MULTISIG_INVITE; } diff --git a/src/renderer/entities/notification/model/__tests__/notification-model.test.ts b/src/renderer/entities/notification/model/__tests__/notification-model.test.ts index 79d8c66c71..94cd61e425 100644 --- a/src/renderer/entities/notification/model/__tests__/notification-model.test.ts +++ b/src/renderer/entities/notification/model/__tests__/notification-model.test.ts @@ -13,12 +13,12 @@ const notifications = [ }, ] as Notification[]; -const newNotification = { +const newNotifications = [{ id: 2, read: true, dateCreated: Date.now(), type: NotificationType.MULTISIG_INVITE, -} as Notification; +}] as Notification[]; describe('entities/notification/model/notification-model', () => { afterEach(() => { @@ -38,16 +38,16 @@ describe('entities/notification/model/notification-model', () => { expect(scope.getState(notificationModel.$notifications)).toEqual(notifications); }); - test('should add new notification on notificationAdded', async () => { - const spyCreate = jest.spyOn(storageService.notifications, 'create').mockResolvedValue(newNotification); + test('should add new notification on notificationsAdded', async () => { + const spyCreate = jest.spyOn(storageService.notifications, 'createAll').mockResolvedValue(newNotifications); const scope = fork({ values: new Map().set(notificationModel.$notifications, []), }); - await allSettled(notificationModel.events.notificationAdded, { scope, params: newNotification }); + await allSettled(notificationModel.events.notificationsAdded, { scope, params: newNotifications }); expect(spyCreate).toHaveBeenCalled(); - expect(scope.getState(notificationModel.$notifications)).toEqual([newNotification]); + expect(scope.getState(notificationModel.$notifications)).toEqual(newNotifications); }); }); diff --git a/src/renderer/entities/notification/model/notification-model.ts b/src/renderer/entities/notification/model/notification-model.ts index eedba2aab4..07813bd388 100644 --- a/src/renderer/entities/notification/model/notification-model.ts +++ b/src/renderer/entities/notification/model/notification-model.ts @@ -1,10 +1,10 @@ import { createStore, createEvent, sample, createEffect } from 'effector'; import { storageService } from '@shared/api/storage'; -import type { NoID, Notification } from '@shared/core'; +import { NoID, Notification } from '@shared/core'; const notificationsStarted = createEvent(); -const notificationAdded = createEvent>(); +const notificationsAdded = createEvent[]>(); const $notifications = createStore([]); @@ -12,8 +12,8 @@ const populateNotificationsFx = createEffect((): Promise => { return storageService.notifications.readAll(); }); -const addNotificationsFx = createEffect((notification: NoID): Promise => { - return storageService.notifications.create(notification); +const addNotificationsFx = createEffect((notifications: NoID[]): Promise => { + return storageService.notifications.createAll(notifications); }); sample({ @@ -27,7 +27,7 @@ sample({ }); sample({ - clock: notificationAdded, + clock: notificationsAdded, target: addNotificationsFx, }); @@ -43,6 +43,48 @@ export const notificationModel = { $notifications, events: { notificationsStarted, - notificationAdded, + notificationsAdded, }, }; +// async function insertInTable(table, collection) { +// const dbPromise = window.indexedDB.open('spektr'); +// +// // for some reason .then() does not working +// while (dbPromise.readyState == 'pending') { +// await new Promise((resolve) => { +// setTimeout(resolve, 1_000); +// }); +// console.log('waiting'); +// } +// const tx = dbPromise.result.transaction(table, 'readwrite'); +// console.log(tx); +// const store = tx.objectStore(table); +// let index = 6000; +// collection.forEach((item) => { +// index += 1; +// store.put(item); +// }); +// } +// // { +// // chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3', +// // dateCreated: Date.now(), +// // proxiedAccountId: '0x08eb319467ea54784cd9edfbd03bbcc53f7a021ed8d9ed2ca97b6ae46b3f6014', +// // proxyAccountId: '0x08eb319467ea54784cd9edfbd03bbcc53f7a021ed8d9ed2ca97b6ae46b3f6014', +// // proxyType: 'Any', +// // read: false, +// // type: 'ProxyCreatedNotification', +// // }, +// var notifs = [ +// { +// dateCreated: Date.now(), +// multisigAccountId: '0x08eb319467ea54784cd9edfbd03bbcc53f7a021ed8d9ed2ca97b6ae46b3f6014', +// multisigAccountName: 'My MST', +// originatorAccountId: '0x08eb319467ea54784cd9edfbd03bbcc53f7a021ed8d9ed2ca97b6ae46b3f6014', +// read: false, +// signatories: [], +// smpRoomId: '0x123', +// threshold: 2, +// type: 'MultisigAccountInvitedNotification', +// }] +// +// insertInTable('notifications', notifs); diff --git a/src/renderer/entities/proxy/model/proxy-model.ts b/src/renderer/entities/proxy/model/proxy-model.ts index 5ad0e58cca..f6c6dfb5fa 100644 --- a/src/renderer/entities/proxy/model/proxy-model.ts +++ b/src/renderer/entities/proxy/model/proxy-model.ts @@ -46,10 +46,11 @@ sample({ clock: proxiesRemoved, source: $proxies, fn: (proxies, proxiesToRemove) => { - return proxiesToRemove.reduce( - (acc, p) => ({ ...acc, [p.accountId]: acc[p.accountId].filter((pr) => !proxyUtils.isSameProxy(pr, p)) }), - proxies, - ); + return proxiesToRemove.reduce((acc, p) => { + acc[p.accountId] = acc[p.accountId].filter((pr) => !proxyUtils.isSameProxy(pr, p)); + + return acc; + }, proxies); }, target: $proxies, // TODO: update $proxies after effect }); diff --git a/src/renderer/entities/walletConnect/model/wallet-connect-model.ts b/src/renderer/entities/walletConnect/model/wallet-connect-model.ts index f03e8a50ad..a7cf734b42 100644 --- a/src/renderer/entities/walletConnect/model/wallet-connect-model.ts +++ b/src/renderer/entities/walletConnect/model/wallet-connect-model.ts @@ -68,8 +68,8 @@ const extendSessionsFx = createEffect((client: Client) => { }); const subscribeToEventsFx = createEffect((client: Client) => { - const bindedSessionUpdated = scopeBind(sessionUpdated); - const bindedReset = scopeBind(reset); + const boundSessionUpdated = scopeBind(sessionUpdated); + const boundReset = scopeBind(reset); client.on('session_update', ({ topic, params }) => { console.log('WC EVENT', 'session_update', { topic, params }); @@ -77,7 +77,7 @@ const subscribeToEventsFx = createEffect((client: Client) => { const _session = client.session.get(topic); const updatedSession = { ..._session, namespaces }; - bindedSessionUpdated(updatedSession); + boundSessionUpdated(updatedSession); }); client.on('session_ping', (args) => { @@ -90,7 +90,7 @@ const subscribeToEventsFx = createEffect((client: Client) => { client.on('session_delete', () => { console.log('WC EVENT', 'session_delete'); - bindedReset(); + boundReset(); }); }); diff --git a/src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx b/src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx index 2e3f4bf3a3..91ad6506c4 100644 --- a/src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx @@ -6,17 +6,16 @@ import { useI18n } from '@app/providers'; import { MultisigInviteNotification } from './notifies/MultisigInviteNotification'; import { ProxyCreatedNotification } from './notifies/ProxyCreatedNotification'; import { NotificationType } from '@shared/core'; -import type { Notification, MultisigInvite, ProxyCreated } from '@shared/core'; +import type { Notification, MultisigInvite, ProxyAction } from '@shared/core'; -const Notifications: Record ReactNode> = { - [NotificationType.MULTISIG_INVITE]: (notify) => ( - - ), +const Notifications: Record ReactNode> = { + [NotificationType.MULTISIG_INVITE]: (n) => , [NotificationType.MULTISIG_CREATED]: () => null, [NotificationType.MULTISIG_APPROVED]: () => null, [NotificationType.MULTISIG_CANCELLED]: () => null, [NotificationType.MULTISIG_EXECUTED]: () => null, - [NotificationType.PROXY_CREATED]: (notify) => , + [NotificationType.PROXY_CREATED]: (n) => , + [NotificationType.PROXY_REMOVED]: (n) => , }; type Props = { diff --git a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx index 8f7ce573f3..9449479df3 100644 --- a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx @@ -6,11 +6,11 @@ import { BodyText, Identicon } from '@shared/ui'; import { useI18n } from '@app/providers'; import { toAddress } from '@shared/lib/utils'; import { ChainTitle } from '@entities/chain'; -import { WalletType, ProxyCreated } from '@shared/core'; +import { WalletType, ProxyAction } from '@shared/core'; import { ProxyTypeOperation } from '../../lib/constants'; type Props = { - notification: ProxyCreated; + notification: ProxyAction; }; export const ProxyCreatedNotification = ({ notification }: Props) => { @@ -26,20 +26,6 @@ export const ProxyCreatedNotification = ({ notification }: Props) => { if (!proxiedWallet) return null; - const identicon = ( - - ); - - const chain = ; - - const walletIcon = ; - return (
    @@ -53,7 +39,17 @@ export const ProxyCreatedNotification = ({ notification }: Props) => { address: toAddress(proxyAccount?.accountId!), name: proxyWallet?.name, }} - components={{ identicon }} + components={{ + identicon: ( + + ), + }} /> @@ -61,10 +57,13 @@ export const ProxyCreatedNotification = ({ notification }: Props) => { t={t} i18nKey="notifications.details.proxyCreatedDetails" values={{ - name: proxiedWallet?.name, + name: proxiedWallet.name, operations: t(ProxyTypeOperation[notification.proxyType]), }} - components={{ chain, walletIcon }} + components={{ + chain: , + walletIcon: , + }} />
    diff --git a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx new file mode 100644 index 0000000000..ab90909957 --- /dev/null +++ b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx @@ -0,0 +1,72 @@ +import { Trans } from 'react-i18next'; +import { useUnit } from 'effector-react'; + +import { WalletIcon, walletModel } from '@entities/wallet'; +import { BodyText, Identicon } from '@shared/ui'; +import { useI18n } from '@app/providers'; +import { toAddress } from '@shared/lib/utils'; +import { ChainTitle } from '@entities/chain'; +import { WalletType, ProxyAction } from '@shared/core'; +import { ProxyTypeOperation } from '../../lib/constants'; + +type Props = { + notification: ProxyAction; +}; + +export const ProxyRemovedNotification = ({ notification }: Props) => { + const { t } = useI18n(); + + const wallets = useUnit(walletModel.$wallets); + const accounts = useUnit(walletModel.$accounts); + + const proxyAccount = accounts.find((a) => a.accountId === notification.proxyAccountId); + const proxyWallet = wallets.find((w) => w.id === proxyAccount?.walletId); + const proxiedAccount = accounts.find((a) => a.accountId === notification.proxiedAccountId); + const proxiedWallet = wallets.find((w) => w.id === proxiedAccount?.walletId); + + if (!proxiedWallet) return null; + + return ( +
    + +
    + {t('notifications.details.proxyCreatedTitle')} + + + ), + }} + /> + + + , + walletIcon: , + }} + /> + +
    +
    + ); +}; diff --git a/src/renderer/features/proxies/lib/utils.ts b/src/renderer/features/proxies/lib/utils.ts index ef21bf91f9..228a7432ec 100644 --- a/src/renderer/features/proxies/lib/utils.ts +++ b/src/renderer/features/proxies/lib/utils.ts @@ -2,7 +2,17 @@ import { u8aToHex } from '@polkadot/util'; import { decodeAddress } from '@polkadot/util-crypto'; import { WellKnownChain } from '@substrate/connect'; -import { AccountId, Chain, ChainId, ProxyAccount, AccountType, Account, ProxiedAccount, NoID } from '@shared/core'; +import { + AccountId, + Chain, + ChainId, + ProxyAccount, + AccountType, + Account, + ProxiedAccount, + NoID, + ProxyAction, NotificationType, +} from '@shared/core'; export const proxyWorkerUtils = { toAccountId, @@ -10,6 +20,7 @@ export const proxyWorkerUtils = { isSameProxy, getKnownChain, isProxiedAccount, + getNotification, }; /** @@ -57,3 +68,15 @@ function getKnownChain(chainId: ChainId): WellKnownChain | undefined { function isProxiedAccount(account: Pick): account is ProxiedAccount { return account.type === AccountType.PROXIED; } + +function getNotification(proxy: ProxyAccount, type: NotificationType): NoID { + return { + chainId: proxy.chainId, + dateCreated: Date.now(), + proxiedAccountId: proxy.proxiedAccountId, + proxyAccountId: proxy.accountId, + proxyType: proxy.proxyType, + read: false, + type, + }; +} diff --git a/src/renderer/features/proxies/model/proxies-model.ts b/src/renderer/features/proxies/model/proxies-model.ts index e1d21ae316..4e00f90d1a 100644 --- a/src/renderer/features/proxies/model/proxies-model.ts +++ b/src/renderer/features/proxies/model/proxies-model.ts @@ -3,11 +3,12 @@ import { createEndpoint } from '@remote-ui/rpc'; import { keyBy } from 'lodash'; import { once, spread } from 'patronum'; -import { Account, Chain, ChainId, Connection, ProxyAccount } from '@shared/core'; +import { Account, Chain, ChainId, Connection, ProxyAccount, NotificationType } from '@shared/core'; import { isDisabled, networkModel } from '@entities/network'; import { proxyWorkerUtils } from '../lib/utils'; import { accountUtils, walletModel } from '@entities/wallet'; import { proxyModel } from '@entities/proxy'; +import { notificationModel } from '@entities/notification'; // @ts-ignore const worker = new Worker(new URL('@features/proxies/workers/proxy-worker', import.meta.url)); @@ -23,13 +24,13 @@ type StartChainsProps = { connections: Record; }; const startChainsFx = createEffect(({ chains, connections }: StartChainsProps) => { - const bindedConnected = scopeBind(connected, { safe: true }); + const boundConnected = scopeBind(connected, { safe: true }); chains.forEach((chain) => { if (isDisabled(connections[chain.chainId])) return; endpoint.call.initConnection(chain, connections[chain.chainId]).then(() => { - bindedConnected(chain.chainId); + boundConnected(chain.chainId); }); }); }); @@ -55,8 +56,8 @@ const getProxiesFx = createEffect(({ chainId, accounts, proxies }: GetProxiesPar ) as Promise; }); -const disconnectFx = createEffect((chainId: ChainId): Promise => { - return endpoint.call.disconnect(chainId); +const disconnectFx = createEffect(async (chainId: ChainId) => { + await endpoint.call.disconnect(chainId); }); sample({ @@ -75,11 +76,11 @@ sample({ sample({ clock: connected, source: walletModel.$accounts, - fn: (accounts, chainId) => ({ - chainId, - accounts: accounts.filter((a) => accountUtils.isChainIdMatch(a, chainId)), - proxies: [], - }), + fn: (accounts, chainId) => { + const chainAccounts = accounts.filter((account) => accountUtils.isChainIdMatch(account, chainId)); + + return { chainId, accounts: chainAccounts, proxies: [] }; + }, target: getProxiesFx, }); @@ -91,6 +92,27 @@ spread({ }, }); +sample({ + clock: getProxiesFx.doneData, + fn: ({ proxiesToAdd, proxiesToRemove }) => { + const proxyAddedNotifications = proxiesToAdd.map((proxy) => + proxyWorkerUtils.getNotification(proxy, NotificationType.PROXY_CREATED), + ); + + const proxyRemovedNotifications = proxiesToRemove.map((proxy) => + proxyWorkerUtils.getNotification(proxy, NotificationType.PROXY_REMOVED), + ); + + return { proxyAddedNotifications, proxyRemovedNotifications }; + }, + target: spread({ + targets: { + proxyAddedNotifications: notificationModel.events.notificationsAdded, + proxyRemovedNotifications: notificationModel.events.notificationsAdded, + }, + }), +}); + sample({ clock: getProxiesFx.done, fn: ({ params: { chainId } }) => chainId, diff --git a/src/renderer/features/proxies/workers/proxy-worker.ts b/src/renderer/features/proxies/workers/proxy-worker.ts index 61f83fa6ca..c95b0ebaa2 100644 --- a/src/renderer/features/proxies/workers/proxy-worker.ts +++ b/src/renderer/features/proxies/workers/proxy-worker.ts @@ -61,14 +61,12 @@ function initConnection(chain: Chain, connection: Connection) { }); } -async function disconnect(chainId: ChainId) { +async function disconnect(chainId: ChainId): Promise { const api = state.apis[chainId]; - if (!api) return; + if (!api?.isConnected) return; - if (api.isConnected) { - await api.disconnect(); - } + await api.disconnect(); } type PartialProxiedAccount = Pick< diff --git a/src/renderer/shared/core/index.ts b/src/renderer/shared/core/index.ts index 7d4c07607d..30b1f1f618 100644 --- a/src/renderer/shared/core/index.ts +++ b/src/renderer/shared/core/index.ts @@ -54,5 +54,5 @@ export type { Stake, Unlocking } from './types/stake'; export { ProxyType } from './types/proxy'; export type { ProxyAccount, PartialProxyAccount } from './types/proxy'; -export type { Notification, MultisigInvite, MultisigOperation, ProxyCreated } from './types/notification'; +export type { Notification, MultisigInvite, MultisigOperation, ProxyAction } from './types/notification'; export { NotificationType } from './types/notification'; diff --git a/src/renderer/shared/core/types/notification.ts b/src/renderer/shared/core/types/notification.ts index c205a7d8cd..1a412caca5 100644 --- a/src/renderer/shared/core/types/notification.ts +++ b/src/renderer/shared/core/types/notification.ts @@ -9,6 +9,7 @@ export const enum NotificationType { MULTISIG_CANCELLED = 'MultisigCancelledNotification', PROXY_CREATED = 'ProxyCreatedNotification', + PROXY_REMOVED = 'ProxyRemovedNotification', } type BaseNotification = { @@ -36,11 +37,11 @@ export type MultisigOperation = MultisigBaseNotification & { chainId: ChainId; }; -export type ProxyCreated = BaseNotification & { +export type ProxyAction = BaseNotification & { proxyAccountId: AccountId; proxiedAccountId: AccountId; proxyType: ProxyType; chainId: ChainId; }; -export type Notification = MultisigInvite | MultisigOperation | ProxyCreated; +export type Notification = MultisigInvite | MultisigOperation | ProxyAction; diff --git a/src/renderer/shared/core/types/proxy.ts b/src/renderer/shared/core/types/proxy.ts index b9a8c6910e..696537d934 100644 --- a/src/renderer/shared/core/types/proxy.ts +++ b/src/renderer/shared/core/types/proxy.ts @@ -12,7 +12,7 @@ export type ProxyAccount = { id: ID; accountId: AccountId; proxiedAccountId: AccountId; - chainId: string; + chainId: ChainId; proxyType: ProxyType; delay: number; }; From 751c62060d00fd87147119e12376c421d0748608 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Wed, 10 Jan 2024 11:11:52 +0300 Subject: [PATCH 04/12] feat: notification prefill --- .../wallet/ui/WalletIcon/WalletIcon.tsx | 1 + .../features/navigation/ui/Navigation.tsx | 11 +---- .../NotificationsList/ui/NotificationRow.tsx | 5 ++- .../notifies/MultisigInviteNotification.tsx | 15 ++----- .../ui/notifies/ProxyCreatedNotification.tsx | 40 ++++++----------- .../ui/notifies/ProxyRemovedNotification.tsx | 44 +++++++------------ src/renderer/features/proxies/lib/utils.ts | 33 +++++++------- .../features/proxies/model/proxies-model.ts | 33 +++++++++----- .../features/proxies/workers/proxy-worker.ts | 18 ++------ .../shared/api/translation/locales/en.json | 12 ++--- .../assets/images/walletTypes/proxy.svg | 2 +- src/renderer/shared/core/index.ts | 2 +- .../shared/core/types/notification.ts | 5 ++- 13 files changed, 96 insertions(+), 125 deletions(-) diff --git a/src/renderer/entities/wallet/ui/WalletIcon/WalletIcon.tsx b/src/renderer/entities/wallet/ui/WalletIcon/WalletIcon.tsx index 6943ad7613..a1c2cabd81 100644 --- a/src/renderer/entities/wallet/ui/WalletIcon/WalletIcon.tsx +++ b/src/renderer/entities/wallet/ui/WalletIcon/WalletIcon.tsx @@ -29,6 +29,7 @@ type Props = { type: WalletType; className?: string; size?: number; + }; export const WalletIcon = ({ type, size = 20, className }: Props) => { diff --git a/src/renderer/features/navigation/ui/Navigation.tsx b/src/renderer/features/navigation/ui/Navigation.tsx index 46a33ab94a..c4a8c05362 100644 --- a/src/renderer/features/navigation/ui/Navigation.tsx +++ b/src/renderer/features/navigation/ui/Navigation.tsx @@ -1,26 +1,19 @@ -import { useEffect, useState } from 'react'; -import { keyBy } from 'lodash'; import { useUnit } from 'effector-react'; import { useMultisigTx } from '@entities/multisig'; import { MultisigTxInitStatus } from '@entities/transaction'; import { NavItem, Props as NavItemProps } from './NavItem'; -import { chainsService, ChainMap } from '@entities/network'; +import { networkModel } from '@entities/network'; import { Paths } from '@shared/routes'; import { walletModel } from '@entities/wallet'; import { BodyText } from '@shared/ui'; export const Navigation = () => { + const chains = useUnit(networkModel.$chains); const activeAccounts = useUnit(walletModel.$activeAccounts); const { getLiveAccountMultisigTxs } = useMultisigTx({}); - const [chains, setChains] = useState({}); - - useEffect(() => { - setChains(keyBy(chainsService.getChainsData(), 'chainId')); - }, []); - const txs = getLiveAccountMultisigTxs(activeAccounts.map((a) => a.accountId)).filter( (tx) => tx.status === MultisigTxInitStatus.SIGNING && chains[tx.chainId], ); diff --git a/src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx b/src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx index 91ad6506c4..18fd5b1b6d 100644 --- a/src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/NotificationRow.tsx @@ -5,8 +5,9 @@ import { FootnoteText } from '@shared/ui'; import { useI18n } from '@app/providers'; import { MultisigInviteNotification } from './notifies/MultisigInviteNotification'; import { ProxyCreatedNotification } from './notifies/ProxyCreatedNotification'; -import { NotificationType } from '@shared/core'; +import { ProxyRemovedNotification } from './notifies/ProxyRemovedNotification'; import type { Notification, MultisigInvite, ProxyAction } from '@shared/core'; +import { NotificationType } from '@shared/core'; const Notifications: Record ReactNode> = { [NotificationType.MULTISIG_INVITE]: (n) => , @@ -15,7 +16,7 @@ const Notifications: Record ReactNode> = [NotificationType.MULTISIG_CANCELLED]: () => null, [NotificationType.MULTISIG_EXECUTED]: () => null, [NotificationType.PROXY_CREATED]: (n) => , - [NotificationType.PROXY_REMOVED]: (n) => , + [NotificationType.PROXY_REMOVED]: (n) => , }; type Props = { diff --git a/src/renderer/features/notifications/NotificationsList/ui/notifies/MultisigInviteNotification.tsx b/src/renderer/features/notifications/NotificationsList/ui/notifies/MultisigInviteNotification.tsx index 3eaf83e0c2..5806b6c3ec 100644 --- a/src/renderer/features/notifications/NotificationsList/ui/notifies/MultisigInviteNotification.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/notifies/MultisigInviteNotification.tsx @@ -1,7 +1,6 @@ import { Trans } from 'react-i18next'; -import { useUnit } from 'effector-react'; -import { WalletIcon, walletModel } from '@entities/wallet'; +import { WalletIcon } from '@entities/wallet'; import type { MultisigInvite } from '@shared/core'; import { WalletType } from '@shared/core'; import { BodyText } from '@shared/ui'; @@ -14,25 +13,19 @@ type Props = { export const MultisigInviteNotification = ({ notification }: Props) => { const { t } = useI18n(); - const wallets = useUnit(walletModel.$wallets); - const accounts = useUnit(walletModel.$accounts); - - const multisigAccount = accounts.find((a) => a.accountId === notification.multisigAccountId); - const notificationWallet = wallets.find((w) => w.id === multisigAccount?.walletId); - return (
    - {t('notifications.details.newMultisigAccountTitle')} + {t('notifications.details.multisigInviteTitle')} diff --git a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx index 9449479df3..20206ceeb2 100644 --- a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx @@ -1,13 +1,14 @@ import { Trans } from 'react-i18next'; import { useUnit } from 'effector-react'; -import { WalletIcon, walletModel } from '@entities/wallet'; +import { WalletIcon } from '@entities/wallet'; import { BodyText, Identicon } from '@shared/ui'; import { useI18n } from '@app/providers'; import { toAddress } from '@shared/lib/utils'; import { ChainTitle } from '@entities/chain'; import { WalletType, ProxyAction } from '@shared/core'; import { ProxyTypeOperation } from '../../lib/constants'; +import { networkModel } from '@entities/network'; type Props = { notification: ProxyAction; @@ -16,39 +17,26 @@ type Props = { export const ProxyCreatedNotification = ({ notification }: Props) => { const { t } = useI18n(); - const wallets = useUnit(walletModel.$wallets); - const accounts = useUnit(walletModel.$accounts); + const chains = useUnit(networkModel.$chains); - const proxyAccount = accounts.find((a) => a.accountId === notification.proxyAccountId); - const proxyWallet = wallets.find((w) => w.id === proxyAccount?.walletId); - const proxiedAccount = accounts.find((a) => a.accountId === notification.proxiedAccountId); - const proxiedWallet = wallets.find((w) => w.id === proxiedAccount?.walletId); - - if (!proxiedWallet) return null; + const address = toAddress(notification.proxyAccountId, { prefix: chains[notification.chainId].addressPrefix }); return (
    - +
    + +
    +
    +
    {t('notifications.details.proxyCreatedTitle')} - ), + identicon: , }} /> @@ -57,12 +45,12 @@ export const ProxyCreatedNotification = ({ notification }: Props) => { t={t} i18nKey="notifications.details.proxyCreatedDetails" values={{ - name: proxiedWallet.name, + name: notification.proxiedWalletName, operations: t(ProxyTypeOperation[notification.proxyType]), }} components={{ chain: , - walletIcon: , + walletIcon: , }} /> diff --git a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx index ab90909957..8828fca44f 100644 --- a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx @@ -1,13 +1,14 @@ import { Trans } from 'react-i18next'; import { useUnit } from 'effector-react'; -import { WalletIcon, walletModel } from '@entities/wallet'; +import { WalletIcon } from '@entities/wallet'; import { BodyText, Identicon } from '@shared/ui'; import { useI18n } from '@app/providers'; import { toAddress } from '@shared/lib/utils'; import { ChainTitle } from '@entities/chain'; import { WalletType, ProxyAction } from '@shared/core'; import { ProxyTypeOperation } from '../../lib/constants'; +import { networkModel } from '@entities/network'; type Props = { notification: ProxyAction; @@ -16,53 +17,40 @@ type Props = { export const ProxyRemovedNotification = ({ notification }: Props) => { const { t } = useI18n(); - const wallets = useUnit(walletModel.$wallets); - const accounts = useUnit(walletModel.$accounts); + const chains = useUnit(networkModel.$chains); - const proxyAccount = accounts.find((a) => a.accountId === notification.proxyAccountId); - const proxyWallet = wallets.find((w) => w.id === proxyAccount?.walletId); - const proxiedAccount = accounts.find((a) => a.accountId === notification.proxiedAccountId); - const proxiedWallet = wallets.find((w) => w.id === proxiedAccount?.walletId); - - if (!proxiedWallet) return null; + const address = toAddress(notification.proxyAccountId, { prefix: chains[notification.chainId].addressPrefix }); return (
    - +
    + +
    +
    +
    - {t('notifications.details.proxyCreatedTitle')} + {t('notifications.details.proxyRemovedTitle')} - ), + identicon: , }} /> , - walletIcon: , + walletIcon: , }} /> diff --git a/src/renderer/features/proxies/lib/utils.ts b/src/renderer/features/proxies/lib/utils.ts index 228a7432ec..bbfbf07a39 100644 --- a/src/renderer/features/proxies/lib/utils.ts +++ b/src/renderer/features/proxies/lib/utils.ts @@ -2,17 +2,9 @@ import { u8aToHex } from '@polkadot/util'; import { decodeAddress } from '@polkadot/util-crypto'; import { WellKnownChain } from '@substrate/connect'; -import { - AccountId, - Chain, - ChainId, - ProxyAccount, - AccountType, - Account, - ProxiedAccount, - NoID, - ProxyAction, NotificationType, -} from '@shared/core'; +import type { AccountId, Chain, ChainId, ProxyAccount, Account, ProxiedAccount, ProxyAction, NoID } from '@shared/core'; +import { AccountType, NotificationType, Wallet } from '@shared/core'; +import { dictionary } from '@shared/lib/utils'; export const proxyWorkerUtils = { toAccountId, @@ -20,7 +12,7 @@ export const proxyWorkerUtils = { isSameProxy, getKnownChain, isProxiedAccount, - getNotification, + getNotification_NEW, }; /** @@ -69,14 +61,25 @@ function isProxiedAccount(account: Pick): account is ProxiedAcc return account.type === AccountType.PROXIED; } -function getNotification(proxy: ProxyAccount, type: NotificationType): NoID { - return { +function getNotification_NEW( + proxies: ProxyAccount[], + wallets: Wallet[], + accounts: Account[], + type: NotificationType, +): NoID[] { + const walletsMap = dictionary(wallets, 'id', ({ name, type }) => ({ name, type })); + const accountsWalletsMap = dictionary(accounts, 'accountId', (account) => walletsMap[account.walletId]); + + return proxies.map((proxy) => ({ chainId: proxy.chainId, dateCreated: Date.now(), proxiedAccountId: proxy.proxiedAccountId, + proxiedWalletType: accountsWalletsMap[proxy.proxiedAccountId].type, + proxiedWalletName: accountsWalletsMap[proxy.proxiedAccountId].name, proxyAccountId: proxy.accountId, proxyType: proxy.proxyType, + proxyWalletName: accountsWalletsMap[proxy.accountId].name, read: false, type, - }; + })); } diff --git a/src/renderer/features/proxies/model/proxies-model.ts b/src/renderer/features/proxies/model/proxies-model.ts index 4e00f90d1a..ecee8a3360 100644 --- a/src/renderer/features/proxies/model/proxies-model.ts +++ b/src/renderer/features/proxies/model/proxies-model.ts @@ -92,15 +92,30 @@ spread({ }, }); +sample({ + clock: getProxiesFx.done, + fn: ({ params: { chainId } }) => chainId, + target: disconnectFx, +}); + sample({ clock: getProxiesFx.doneData, - fn: ({ proxiesToAdd, proxiesToRemove }) => { - const proxyAddedNotifications = proxiesToAdd.map((proxy) => - proxyWorkerUtils.getNotification(proxy, NotificationType.PROXY_CREATED), + source: { + wallets: walletModel.$wallets, + accounts: walletModel.$accounts, + }, + fn: ({ wallets, accounts }, proxies) => { + const proxyAddedNotifications = proxyWorkerUtils.getNotification_NEW( + proxies.proxiesToAdd, + wallets, + accounts, + NotificationType.PROXY_CREATED, ); - - const proxyRemovedNotifications = proxiesToRemove.map((proxy) => - proxyWorkerUtils.getNotification(proxy, NotificationType.PROXY_REMOVED), + const proxyRemovedNotifications = proxyWorkerUtils.getNotification_NEW( + proxies.proxiesToRemove, + wallets, + accounts, + NotificationType.PROXY_REMOVED, ); return { proxyAddedNotifications, proxyRemovedNotifications }; @@ -113,10 +128,4 @@ sample({ }), }); -sample({ - clock: getProxiesFx.done, - fn: ({ params: { chainId } }) => chainId, - target: disconnectFx, -}); - export const proxiesModel = {}; diff --git a/src/renderer/features/proxies/workers/proxy-worker.ts b/src/renderer/features/proxies/workers/proxy-worker.ts index c95b0ebaa2..fac34bb5f1 100644 --- a/src/renderer/features/proxies/workers/proxy-worker.ts +++ b/src/renderer/features/proxies/workers/proxy-worker.ts @@ -1,24 +1,14 @@ import { createEndpoint } from '@remote-ui/rpc'; import { ScProvider, WsProvider } from '@polkadot/rpc-provider'; -import { ApiPromise } from '@polkadot/api'; -import isEqual from 'lodash/isEqual'; import { ProviderInterface } from '@polkadot/rpc-provider/types'; +import { ApiPromise } from '@polkadot/api'; +import { isEqual } from 'lodash'; import * as Sc from '@substrate/connect'; -import { - Chain, - ChainId, - Connection, - ConnectionType, - ProxyAccount, - ProxiedAccount, - Account, - AccountId, - NoID, -} from '@shared/core'; +import type { Chain, ChainId, Connection, ProxyAccount, ProxiedAccount, Account, AccountId, NoID } from '@shared/core'; +import { ConnectionType, ProxyVariant } from '@shared/core'; import { InitConnectionsResult } from '../lib/constants'; import { proxyWorkerUtils } from '../lib/utils'; -import { ProxyVariant } from '@/src/renderer/shared/core/types/proxy'; const state = { apis: {} as Record, diff --git a/src/renderer/shared/api/translation/locales/en.json b/src/renderer/shared/api/translation/locales/en.json index 29e7886444..917892cbf4 100644 --- a/src/renderer/shared/api/translation/locales/en.json +++ b/src/renderer/shared/api/translation/locales/en.json @@ -264,11 +264,13 @@ }, "notifications": { "details": { - "newMultisigAccountDescription": "{name} with threshold { threshold } out of { signatories } signatories", - "newMultisigAccountTitle": "New multisig wallet added", - "proxyCreatedDetails": " is now available for your {name} to control {operations}", + "multisigInviteDescription": "{name}  with threshold { threshold } out of { signatories } signatories", + "multisigInviteTitle": "New multisig wallet added", + "proxyCreatedDetails": "  is now available for your {name} to control {operations}", + "proxyRemovedDetails": "  is no longer available for your {name} to control {operations}", "proxyCreatedTitle": "New delegated authority wallet added", - "proxyCreatedWallet": "{name}  with {address}" + "proxyRemovedTitle": "Delegated authority wallet has been removed", + "proxyWalletAction": "{name}  with {address}" }, "noNotificationsDescription": "You don't have notifications yet", "title": "Notifications" @@ -560,7 +562,7 @@ "governance": "governance operations", "identityJudgement": "judgement operations", "nominationPools": "nominations operations", - "nonTransfer": "nonTransfer operations", + "nonTransfer": "non-transfer operations", "staking": "staking operations" } }, diff --git a/src/renderer/shared/assets/images/walletTypes/proxy.svg b/src/renderer/shared/assets/images/walletTypes/proxy.svg index 00f1648ae7..595a4326cd 100644 --- a/src/renderer/shared/assets/images/walletTypes/proxy.svg +++ b/src/renderer/shared/assets/images/walletTypes/proxy.svg @@ -1,3 +1,3 @@ - + diff --git a/src/renderer/shared/core/index.ts b/src/renderer/shared/core/index.ts index 30b1f1f618..2c07699640 100644 --- a/src/renderer/shared/core/index.ts +++ b/src/renderer/shared/core/index.ts @@ -51,7 +51,7 @@ export type { Validator } from './types/validator'; export { RewardsDestination } from './types/stake'; export type { Stake, Unlocking } from './types/stake'; -export { ProxyType } from './types/proxy'; +export { ProxyType, ProxyVariant } from './types/proxy'; export type { ProxyAccount, PartialProxyAccount } from './types/proxy'; export type { Notification, MultisigInvite, MultisigOperation, ProxyAction } from './types/notification'; diff --git a/src/renderer/shared/core/types/notification.ts b/src/renderer/shared/core/types/notification.ts index 1a412caca5..39c20ae832 100644 --- a/src/renderer/shared/core/types/notification.ts +++ b/src/renderer/shared/core/types/notification.ts @@ -1,4 +1,4 @@ -import { ProxyType } from '@shared/core'; +import { ProxyType, WalletType } from '@shared/core'; import type { ID, AccountId, CallHash, ChainId, Timepoint } from '@shared/core'; export const enum NotificationType { @@ -42,6 +42,9 @@ export type ProxyAction = BaseNotification & { proxiedAccountId: AccountId; proxyType: ProxyType; chainId: ChainId; + proxyWalletName: string; + proxiedWalletName: string; + proxiedWalletType: WalletType; }; export type Notification = MultisigInvite | MultisigOperation | ProxyAction; From ccbcc53a2948160c19b96ea35856be40b5ea734a Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Wed, 10 Jan 2024 15:37:57 +0300 Subject: [PATCH 05/12] feat: worker update --- src/renderer/app/index.tsx | 6 ++- src/renderer/features/proxies/index.ts | 4 +- .../features/proxies/lib/constants.ts | 8 ---- src/renderer/features/proxies/lib/types.ts | 8 ---- src/renderer/features/proxies/lib/utils.ts | 2 +- .../features/proxies/model/proxies-model.ts | 7 +++- .../features/proxies/workers/proxy-worker.ts | 42 +++++++++++++------ .../model/wallet-provider-model.ts | 26 ++++++++---- .../ui/WalletDetailsProvider.tsx | 11 ----- 9 files changed, 59 insertions(+), 55 deletions(-) delete mode 100644 src/renderer/features/proxies/lib/constants.ts delete mode 100644 src/renderer/features/proxies/lib/types.ts diff --git a/src/renderer/app/index.tsx b/src/renderer/app/index.tsx index 26a850a3c1..c08a5b7037 100644 --- a/src/renderer/app/index.tsx +++ b/src/renderer/app/index.tsx @@ -11,13 +11,14 @@ import { assetsModel } from '@pages/Assets/Assets/model/assets-model'; import './i18n'; import './index.css'; import './styles/theme/default.css'; +import { proxiesModel } from '@features/proxies'; log.variables.version = process.env.VERSION; log.variables.env = process.env.NODE_ENV; log.transports.console.format = '{y}/{m}/{d} {h}:{i}:{s}.{ms} [{env}#{version}]-{processType} [{level}] > {text}'; log.transports.console.useStyles = true; -Object.assign(console, log.functions); +// Object.assign(console, log.functions); log.errorHandler.startCatching({ showDialog: false, onError({ createIssue, error, processType, versions }) { @@ -35,6 +36,9 @@ networkModel.events.networkStarted(); balanceSubscriptionModel.events.balancesSubscribed(); assetsModel.events.assetsStarted(); notificationModel.events.notificationsStarted(); +setTimeout(() => { + proxiesModel.events.proxiesStarted(); +}, 1000); createRoot(container).render( diff --git a/src/renderer/features/proxies/index.ts b/src/renderer/features/proxies/index.ts index 9628dbdd49..39625241be 100644 --- a/src/renderer/features/proxies/index.ts +++ b/src/renderer/features/proxies/index.ts @@ -1,3 +1 @@ -export * from './lib/constants'; -export * from './model/proxies-model'; -export * from './workers/proxy-worker'; +export { proxiesModel } from './model/proxies-model'; diff --git a/src/renderer/features/proxies/lib/constants.ts b/src/renderer/features/proxies/lib/constants.ts deleted file mode 100644 index e0b1338f6c..0000000000 --- a/src/renderer/features/proxies/lib/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const enum ProxyWorkerCommands { - GET_PROXIES = 'getProxies', -} - -export const enum InitConnectionsResult { - SUCCESS = 'success', - FAILED = 'failed', -} diff --git a/src/renderer/features/proxies/lib/types.ts b/src/renderer/features/proxies/lib/types.ts deleted file mode 100644 index 139747554f..0000000000 --- a/src/renderer/features/proxies/lib/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ApiPromise } from '@polkadot/api'; - -import { ProxyWorkerCommands } from './constants'; - -export type GetProxyCommand = { - type: ProxyWorkerCommands.GET_PROXIES; - api: ApiPromise; -}; diff --git a/src/renderer/features/proxies/lib/utils.ts b/src/renderer/features/proxies/lib/utils.ts index bbfbf07a39..6a22c20169 100644 --- a/src/renderer/features/proxies/lib/utils.ts +++ b/src/renderer/features/proxies/lib/utils.ts @@ -2,7 +2,7 @@ import { u8aToHex } from '@polkadot/util'; import { decodeAddress } from '@polkadot/util-crypto'; import { WellKnownChain } from '@substrate/connect'; -import type { AccountId, Chain, ChainId, ProxyAccount, Account, ProxiedAccount, ProxyAction, NoID } from '@shared/core'; +import { AccountId, Chain, ChainId, ProxyAccount, Account, ProxiedAccount, ProxyAction, NoID } from '@shared/core'; import { AccountType, NotificationType, Wallet } from '@shared/core'; import { dictionary } from '@shared/lib/utils'; diff --git a/src/renderer/features/proxies/model/proxies-model.ts b/src/renderer/features/proxies/model/proxies-model.ts index ecee8a3360..842accc805 100644 --- a/src/renderer/features/proxies/model/proxies-model.ts +++ b/src/renderer/features/proxies/model/proxies-model.ts @@ -17,6 +17,7 @@ const endpoint = createEndpoint(worker, { callable: ['initConnection', 'getProxies', 'disconnect'], }); +const proxiesStarted = createEvent(); const connected = createEvent(); type StartChainsProps = { @@ -128,4 +129,8 @@ sample({ }), }); -export const proxiesModel = {}; +export const proxiesModel = { + events: { + proxiesStarted, + }, +}; diff --git a/src/renderer/features/proxies/workers/proxy-worker.ts b/src/renderer/features/proxies/workers/proxy-worker.ts index fac34bb5f1..3b1522a1d8 100644 --- a/src/renderer/features/proxies/workers/proxy-worker.ts +++ b/src/renderer/features/proxies/workers/proxy-worker.ts @@ -2,18 +2,32 @@ import { createEndpoint } from '@remote-ui/rpc'; import { ScProvider, WsProvider } from '@polkadot/rpc-provider'; import { ProviderInterface } from '@polkadot/rpc-provider/types'; import { ApiPromise } from '@polkadot/api'; -import { isEqual } from 'lodash'; +import isEqual from 'lodash/isEqual'; import * as Sc from '@substrate/connect'; -import type { Chain, ChainId, Connection, ProxyAccount, ProxiedAccount, Account, AccountId, NoID } from '@shared/core'; -import { ConnectionType, ProxyVariant } from '@shared/core'; -import { InitConnectionsResult } from '../lib/constants'; +import { + Chain, + ChainId, + Connection, + ConnectionType, + ProxyAccount, + ProxiedAccount, + Account, + AccountId, + ProxyVariant, + NoID, +} from '@shared/core'; import { proxyWorkerUtils } from '../lib/utils'; const state = { apis: {} as Record, }; +const InitConnectionsResult = { + SUCCESS: 'success', + FAILED: 'failed', +}; + function initConnection(chain: Chain, connection: Connection) { return new Promise((resolve) => { if (!chain) return; @@ -51,12 +65,14 @@ function initConnection(chain: Chain, connection: Connection) { }); } -async function disconnect(chainId: ChainId): Promise { +async function disconnect(chainId: ChainId) { const api = state.apis[chainId]; - if (!api?.isConnected) return; + if (!api) return; - await api.disconnect(); + if (api.isConnected) { + await api.disconnect(); + } } type PartialProxiedAccount = Pick< @@ -174,15 +190,15 @@ async function getProxies( }; } -function getConnectionStatus(chainId: ChainId): boolean { - const api = state.apis[chainId]; - - return Boolean(api?.isConnected); -} +// function getConnectionStatus(chainId: ChainId): boolean { +// const api = state.apis[chainId]; +// +// return Boolean(api?.isConnected); +// } // @ts-ignore const endpoint = createEndpoint(self); -endpoint.expose({ initConnection, getProxies, getConnectionStatus, disconnect }); +endpoint.expose({ initConnection, getProxies, disconnect }); console.log('proxy worker started successfully'); diff --git a/src/renderer/widgets/WalletDetails/model/wallet-provider-model.ts b/src/renderer/widgets/WalletDetails/model/wallet-provider-model.ts index 3d7f01472a..da631bc905 100644 --- a/src/renderer/widgets/WalletDetails/model/wallet-provider-model.ts +++ b/src/renderer/widgets/WalletDetails/model/wallet-provider-model.ts @@ -19,9 +19,13 @@ const $accounts = combine( }, ); -const $singleShardAccount = combine($accounts, (accounts): BaseAccount | undefined => { - return accountUtils.getBaseAccount(accounts); -}); +const $singleShardAccount = combine( + $accounts, + (accounts): BaseAccount | undefined => { + return accountUtils.getBaseAccount(accounts); + }, + { skipVoid: false }, +); const $multiShardAccounts = combine($accounts, (accounts): MultishardMap => { if (accounts.length === 0) return new Map(); @@ -41,6 +45,7 @@ const $multisigAccount = combine( return match && accountUtils.isMultisigAccount(match) ? match : undefined; }, + { skipVoid: false }, ); type VaultAccounts = { @@ -63,35 +68,38 @@ const $vaultAccounts = combine( accountsMap: walletDetailsUtils.getVaultAccountsMap(accounts), }; }, + { skipVoid: false }, ); const $signatoryContacts = combine( { - account: $accounts.map((accounts) => accounts[0]), + account: $accounts, accounts: walletModel.$accounts, }, ({ account, accounts }): Signatory[] => { - if (!account || !accountUtils.isMultisigAccount(account)) return []; + const multisigAccount = accounts[0]; + if (!multisigAccount || !accountUtils.isMultisigAccount(multisigAccount)) return []; const accountsMap = dictionary(accounts, 'accountId', () => true); - return account.signatories.filter((signatory) => !accountsMap[signatory.accountId]); + return multisigAccount.signatories.filter((signatory) => !accountsMap[signatory.accountId]); }, ); const $signatoryWallets = combine( { - account: $accounts.map((accounts) => accounts[0]), + account: $accounts, accounts: walletModel.$accounts, wallets: walletModel.$wallets, }, ({ account, accounts, wallets }): [AccountId, Wallet][] => { - if (!account || !accountUtils.isMultisigAccount(account)) return []; + const multisigAccount = accounts[0]; + if (!multisigAccount || !accountUtils.isMultisigAccount(multisigAccount)) return []; const walletsMap = dictionary(wallets, 'id'); const accountsMap = dictionary(accounts, 'accountId', (account) => account.walletId); - return account.signatories.reduce<[AccountId, Wallet][]>((acc, signatory) => { + return multisigAccount.signatories.reduce<[AccountId, Wallet][]>((acc, signatory) => { const wallet = walletsMap[accountsMap[signatory.accountId]]; if (wallet) { acc.push([signatory.accountId, wallet]); diff --git a/src/renderer/widgets/WalletDetails/ui/WalletDetailsProvider.tsx b/src/renderer/widgets/WalletDetails/ui/WalletDetailsProvider.tsx index 9eb3fe579a..e6d6d5ef6d 100644 --- a/src/renderer/widgets/WalletDetails/ui/WalletDetailsProvider.tsx +++ b/src/renderer/widgets/WalletDetails/ui/WalletDetailsProvider.tsx @@ -1,4 +1,3 @@ -import { useEffect } from 'react'; import { useUnit } from 'effector-react'; import { walletSelectModel } from '@features/wallets'; @@ -9,9 +8,6 @@ import { MultishardWalletDetails } from './MultishardWalletDetails'; import { VaultWalletDetails } from './VaultWalletDetails'; import { walletProviderModel } from '../model/wallet-provider-model'; import { walletUtils } from '@entities/wallet'; -import { proxyModel } from '@entities/proxy'; -// TODO: Remove when proxies will be used in UI -import { proxiesModel } from '@features/proxies'; export const WalletDetailsProvider = () => { const wallet = useUnit(walletSelectModel.$walletForDetails); @@ -22,13 +18,6 @@ export const WalletDetailsProvider = () => { const contacts = useUnit(walletProviderModel.$signatoryContacts); const vaultAccounts = useUnit(walletProviderModel.$vaultAccounts); const signatoryWallets = useUnit(walletProviderModel.$signatoryWallets); - // TODO: Remove when proxies will be used in UI - const proxies = useUnit(proxyModel.$proxies); - - useEffect(() => { - // TODO: Remove when proxies will be used in UI - console.log('proxies', proxies, proxiesModel); - }, [proxies]); if (!wallet) return null; From 64e9e28e7876cf1fe81e56ed7fd09c2d08aaa9d9 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Wed, 10 Jan 2024 16:19:37 +0300 Subject: [PATCH 06/12] feat: worker update v2 --- src/renderer/app/index.tsx | 4 +- src/renderer/entities/proxy/index.ts | 1 - src/renderer/entities/proxy/lib/constants.ts | 12 ---- src/renderer/entities/proxy/lib/types.ts | 3 - .../entities/proxy/model/proxy-model.ts | 5 +- .../proxy/ui/ProxyAccount/ProxyAccount.tsx | 12 +++- .../wallet/ui/WalletIcon/WalletIcon.tsx | 1 - .../ui/notifies/ProxyCreatedNotification.tsx | 2 +- .../ui/notifies/ProxyRemovedNotification.tsx | 2 +- src/renderer/features/proxies/lib/utils.ts | 60 ++---------------- .../features/proxies/lib/worker-utils.ts | 62 +++++++++++++++++++ .../features/proxies/model/proxies-model.ts | 40 ++++++------ .../features/proxies/workers/proxy-worker.ts | 16 +---- 13 files changed, 105 insertions(+), 115 deletions(-) delete mode 100644 src/renderer/entities/proxy/lib/constants.ts delete mode 100644 src/renderer/entities/proxy/lib/types.ts create mode 100644 src/renderer/features/proxies/lib/worker-utils.ts diff --git a/src/renderer/app/index.tsx b/src/renderer/app/index.tsx index c08a5b7037..9db7f6332a 100644 --- a/src/renderer/app/index.tsx +++ b/src/renderer/app/index.tsx @@ -36,9 +36,7 @@ networkModel.events.networkStarted(); balanceSubscriptionModel.events.balancesSubscribed(); assetsModel.events.assetsStarted(); notificationModel.events.notificationsStarted(); -setTimeout(() => { - proxiesModel.events.proxiesStarted(); -}, 1000); +proxiesModel.events.proxiesStarted(); createRoot(container).render( diff --git a/src/renderer/entities/proxy/index.ts b/src/renderer/entities/proxy/index.ts index bd0e369179..835ad9d100 100644 --- a/src/renderer/entities/proxy/index.ts +++ b/src/renderer/entities/proxy/index.ts @@ -1,3 +1,2 @@ export { proxyModel } from './model/proxy-model'; -export * from './lib/constants'; export { ProxyAccount } from './ui/ProxyAccount/ProxyAccount'; diff --git a/src/renderer/entities/proxy/lib/constants.ts b/src/renderer/entities/proxy/lib/constants.ts deleted file mode 100644 index eefb845a6b..0000000000 --- a/src/renderer/entities/proxy/lib/constants.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ProxyType } from '@shared/core'; - -export const ProxyTypeName: Record = { - [ProxyType.ANY]: 'proxy.names.any', - [ProxyType.NON_TRANSFER]: 'proxy.names.nonTransfer', - [ProxyType.STAKING]: 'proxy.names.staking', - [ProxyType.AUCTION]: 'proxy.names.auction', - [ProxyType.CANCEL_PROXY]: 'proxy.names.cancelProxy', - [ProxyType.GOVERNANCE]: 'proxy.names.governance', - [ProxyType.IDENTITY_JUDGEMENT]: 'proxy.names.identityJudgement', - [ProxyType.NOMINATION_POOLS]: 'proxy.names.nominationPools', -}; diff --git a/src/renderer/entities/proxy/lib/types.ts b/src/renderer/entities/proxy/lib/types.ts deleted file mode 100644 index 12ce2a89aa..0000000000 --- a/src/renderer/entities/proxy/lib/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { AccountId, ProxyAccount } from '@shared/core'; - -export type ProxyStore = Record; diff --git a/src/renderer/entities/proxy/model/proxy-model.ts b/src/renderer/entities/proxy/model/proxy-model.ts index f6c6dfb5fa..873de51d9a 100644 --- a/src/renderer/entities/proxy/model/proxy-model.ts +++ b/src/renderer/entities/proxy/model/proxy-model.ts @@ -1,10 +1,11 @@ import { createEffect, createEvent, createStore, sample } from 'effector'; -import { ProxyStore } from '../lib/types'; import { proxyUtils } from '../lib/utils'; -import type { ProxyAccount } from '@shared/core'; +import type { ProxyAccount, AccountId } from '@shared/core'; import { storageService } from '@shared/api/storage'; +type ProxyStore = Record; + const $proxies = createStore({}); const proxiesAdded = createEvent(); diff --git a/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.tsx b/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.tsx index 02af81bef7..62e68a5ae7 100644 --- a/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.tsx +++ b/src/renderer/entities/proxy/ui/ProxyAccount/ProxyAccount.tsx @@ -3,7 +3,17 @@ import { BodyText, DropdownIconButton, HelpText, Identicon, Truncate } from '@sh import { AccountId, ProxyType } from '@shared/core'; import { useI18n } from '@app/providers'; import { DropdownIconButtonOption } from '@shared/ui/types'; -import { ProxyTypeName } from '../../lib/constants'; + +const ProxyTypeName: Record = { + [ProxyType.ANY]: 'proxy.names.any', + [ProxyType.NON_TRANSFER]: 'proxy.names.nonTransfer', + [ProxyType.STAKING]: 'proxy.names.staking', + [ProxyType.AUCTION]: 'proxy.names.auction', + [ProxyType.CANCEL_PROXY]: 'proxy.names.cancelProxy', + [ProxyType.GOVERNANCE]: 'proxy.names.governance', + [ProxyType.IDENTITY_JUDGEMENT]: 'proxy.names.identityJudgement', + [ProxyType.NOMINATION_POOLS]: 'proxy.names.nominationPools', +}; type Props = { className?: string; diff --git a/src/renderer/entities/wallet/ui/WalletIcon/WalletIcon.tsx b/src/renderer/entities/wallet/ui/WalletIcon/WalletIcon.tsx index a1c2cabd81..6943ad7613 100644 --- a/src/renderer/entities/wallet/ui/WalletIcon/WalletIcon.tsx +++ b/src/renderer/entities/wallet/ui/WalletIcon/WalletIcon.tsx @@ -29,7 +29,6 @@ type Props = { type: WalletType; className?: string; size?: number; - }; export const WalletIcon = ({ type, size = 20, className }: Props) => { diff --git a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx index 20206ceeb2..f925d571c3 100644 --- a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx @@ -7,8 +7,8 @@ import { useI18n } from '@app/providers'; import { toAddress } from '@shared/lib/utils'; import { ChainTitle } from '@entities/chain'; import { WalletType, ProxyAction } from '@shared/core'; -import { ProxyTypeOperation } from '../../lib/constants'; import { networkModel } from '@entities/network'; +import { ProxyTypeOperation } from '../../lib/constants'; type Props = { notification: ProxyAction; diff --git a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx index 8828fca44f..824f57be15 100644 --- a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx @@ -7,8 +7,8 @@ import { useI18n } from '@app/providers'; import { toAddress } from '@shared/lib/utils'; import { ChainTitle } from '@entities/chain'; import { WalletType, ProxyAction } from '@shared/core'; -import { ProxyTypeOperation } from '../../lib/constants'; import { networkModel } from '@entities/network'; +import { ProxyTypeOperation } from '../../lib/constants'; type Props = { notification: ProxyAction; diff --git a/src/renderer/features/proxies/lib/utils.ts b/src/renderer/features/proxies/lib/utils.ts index 6a22c20169..8f822f780a 100644 --- a/src/renderer/features/proxies/lib/utils.ts +++ b/src/renderer/features/proxies/lib/utils.ts @@ -1,67 +1,17 @@ -import { u8aToHex } from '@polkadot/util'; -import { decodeAddress } from '@polkadot/util-crypto'; -import { WellKnownChain } from '@substrate/connect'; - -import { AccountId, Chain, ChainId, ProxyAccount, Account, ProxiedAccount, ProxyAction, NoID } from '@shared/core'; -import { AccountType, NotificationType, Wallet } from '@shared/core'; +import { NotificationType, Chain } from '@shared/core'; +import type { ProxyAccount, Account, ProxyAction, NoID, Wallet } from '@shared/core'; import { dictionary } from '@shared/lib/utils'; -export const proxyWorkerUtils = { - toAccountId, +export const proxiesUtils = { isRegularProxy, - isSameProxy, - getKnownChain, - isProxiedAccount, - getNotification_NEW, + getNotification, }; -/** - * Try to get account id of the address - * WARNING! Duplication for worker - * @param address account's address - * @return {String} - */ -export function toAccountId(address: string): AccountId { - try { - return u8aToHex(decodeAddress(address)); - } catch { - return '0x00'; - } -} - function isRegularProxy(chain: Chain): boolean { return Boolean(chain.options?.includes('regular_proxy')); } -function isSameProxy(oldProxy: NoID, newProxy: NoID): boolean { - return ( - oldProxy.accountId === newProxy.accountId && - oldProxy.proxiedAccountId === newProxy.proxiedAccountId && - oldProxy.chainId === newProxy.chainId && - oldProxy.proxyType === newProxy.proxyType && - oldProxy.delay === newProxy.delay - ); -} - -const enum Chains { - POLKADOT = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3', - KUSAMA = '0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe', -} - -const KnownChains: Record = { - [Chains.POLKADOT]: WellKnownChain.polkadot, - [Chains.KUSAMA]: WellKnownChain.ksmcc3, -}; - -function getKnownChain(chainId: ChainId): WellKnownChain | undefined { - return KnownChains[chainId]; -} - -function isProxiedAccount(account: Pick): account is ProxiedAccount { - return account.type === AccountType.PROXIED; -} - -function getNotification_NEW( +function getNotification( proxies: ProxyAccount[], wallets: Wallet[], accounts: Account[], diff --git a/src/renderer/features/proxies/lib/worker-utils.ts b/src/renderer/features/proxies/lib/worker-utils.ts new file mode 100644 index 0000000000..e56f760f61 --- /dev/null +++ b/src/renderer/features/proxies/lib/worker-utils.ts @@ -0,0 +1,62 @@ +import { u8aToHex } from '@polkadot/util'; +import { decodeAddress } from '@polkadot/util-crypto'; +import { WellKnownChain } from '@substrate/connect'; +import { ApiPromise } from '@polkadot/api'; + +import { AccountId, ChainId, ProxyAccount, Account, ProxiedAccount, NoID, AccountType } from '@shared/core'; + +export const proxyWorkerUtils = { + toAccountId, + isSameProxy, + getKnownChain, + isProxiedAccount, + isApiConnected, +}; + +/** + * Try to get account id of the address + * WARNING! Duplication for worker + * @param address account's address + * @return {String} + */ +export function toAccountId(address: string): AccountId { + try { + return u8aToHex(decodeAddress(address)); + } catch { + return '0x00'; + } +} + +function isSameProxy(oldProxy: NoID, newProxy: NoID): boolean { + return ( + oldProxy.accountId === newProxy.accountId && + oldProxy.proxiedAccountId === newProxy.proxiedAccountId && + oldProxy.chainId === newProxy.chainId && + oldProxy.proxyType === newProxy.proxyType && + oldProxy.delay === newProxy.delay + ); +} + +const enum Chains { + POLKADOT = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3', + KUSAMA = '0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe', +} + +const KnownChains: Record = { + [Chains.POLKADOT]: WellKnownChain.polkadot, + [Chains.KUSAMA]: WellKnownChain.ksmcc3, +}; + +function getKnownChain(chainId: ChainId): WellKnownChain | undefined { + return KnownChains[chainId]; +} + +function isProxiedAccount(account: Pick): account is ProxiedAccount { + return account.type === AccountType.PROXIED; +} + +function isApiConnected(apis: Record, chainId: ChainId): boolean { + const api = apis[chainId]; + + return Boolean(api?.isConnected); +} diff --git a/src/renderer/features/proxies/model/proxies-model.ts b/src/renderer/features/proxies/model/proxies-model.ts index 842accc805..c96f70b302 100644 --- a/src/renderer/features/proxies/model/proxies-model.ts +++ b/src/renderer/features/proxies/model/proxies-model.ts @@ -5,10 +5,10 @@ import { once, spread } from 'patronum'; import { Account, Chain, ChainId, Connection, ProxyAccount, NotificationType } from '@shared/core'; import { isDisabled, networkModel } from '@entities/network'; -import { proxyWorkerUtils } from '../lib/utils'; import { accountUtils, walletModel } from '@entities/wallet'; import { proxyModel } from '@entities/proxy'; import { notificationModel } from '@entities/notification'; +import { proxiesUtils } from '../lib/utils'; // @ts-ignore const worker = new Worker(new URL('@features/proxies/workers/proxy-worker', import.meta.url)); @@ -68,7 +68,7 @@ sample({ chains: networkModel.$chains, }, fn: ({ connections, chains }) => ({ - chains: Object.values(chains).filter(proxyWorkerUtils.isRegularProxy), + chains: Object.values(chains).filter(proxiesUtils.isRegularProxy), connections, }), target: startChainsFx, @@ -105,28 +105,24 @@ sample({ wallets: walletModel.$wallets, accounts: walletModel.$accounts, }, + filter: (_, proxies) => proxies.proxiesToAdd.length > 0, fn: ({ wallets, accounts }, proxies) => { - const proxyAddedNotifications = proxyWorkerUtils.getNotification_NEW( - proxies.proxiesToAdd, - wallets, - accounts, - NotificationType.PROXY_CREATED, - ); - const proxyRemovedNotifications = proxyWorkerUtils.getNotification_NEW( - proxies.proxiesToRemove, - wallets, - accounts, - NotificationType.PROXY_REMOVED, - ); - - return { proxyAddedNotifications, proxyRemovedNotifications }; + return proxiesUtils.getNotification(proxies.proxiesToAdd, wallets, accounts, NotificationType.PROXY_CREATED); }, - target: spread({ - targets: { - proxyAddedNotifications: notificationModel.events.notificationsAdded, - proxyRemovedNotifications: notificationModel.events.notificationsAdded, - }, - }), + target: notificationModel.events.notificationsAdded, +}); + +sample({ + clock: getProxiesFx.doneData, + source: { + wallets: walletModel.$wallets, + accounts: walletModel.$accounts, + }, + filter: (_, proxies) => proxies.proxiesToRemove.length > 0, + fn: ({ wallets, accounts }, proxies) => { + return proxiesUtils.getNotification(proxies.proxiesToRemove, wallets, accounts, NotificationType.PROXY_REMOVED); + }, + target: notificationModel.events.notificationsAdded, }); export const proxiesModel = { diff --git a/src/renderer/features/proxies/workers/proxy-worker.ts b/src/renderer/features/proxies/workers/proxy-worker.ts index 3b1522a1d8..3e14a0546f 100644 --- a/src/renderer/features/proxies/workers/proxy-worker.ts +++ b/src/renderer/features/proxies/workers/proxy-worker.ts @@ -17,7 +17,7 @@ import { ProxyVariant, NoID, } from '@shared/core'; -import { proxyWorkerUtils } from '../lib/utils'; +import { proxyWorkerUtils } from '../lib/worker-utils'; const state = { apis: {} as Record, @@ -66,13 +66,9 @@ function initConnection(chain: Chain, connection: Connection) { } async function disconnect(chainId: ChainId) { - const api = state.apis[chainId]; - - if (!api) return; + if (!proxyWorkerUtils.isApiConnected(state.apis, chainId)) return; - if (api.isConnected) { - await api.disconnect(); - } + await state.apis[chainId].disconnect(); } type PartialProxiedAccount = Pick< @@ -190,12 +186,6 @@ async function getProxies( }; } -// function getConnectionStatus(chainId: ChainId): boolean { -// const api = state.apis[chainId]; -// -// return Boolean(api?.isConnected); -// } - // @ts-ignore const endpoint = createEndpoint(self); From 9ab7c2fbc0a742e3abd90c8e4bfedcf745e73dc8 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Wed, 10 Jan 2024 16:44:28 +0300 Subject: [PATCH 07/12] chore: pr errors --- src/renderer/app/index.tsx | 2 +- .../context/MatrixContext/MatrixContext.tsx | 24 ++++++----- .../notification/model/notification-model.ts | 42 ------------------- .../entities/proxy/model/proxy-model.ts | 6 ++- .../shared/api/translation/locales/en.json | 2 +- 5 files changed, 19 insertions(+), 57 deletions(-) diff --git a/src/renderer/app/index.tsx b/src/renderer/app/index.tsx index 9db7f6332a..043cdb85dd 100644 --- a/src/renderer/app/index.tsx +++ b/src/renderer/app/index.tsx @@ -18,7 +18,7 @@ log.variables.env = process.env.NODE_ENV; log.transports.console.format = '{y}/{m}/{d} {h}:{i}:{s}.{ms} [{env}#{version}]-{processType} [{level}] > {text}'; log.transports.console.useStyles = true; -// Object.assign(console, log.functions); +Object.assign(console, log.functions); log.errorHandler.startCatching({ showDialog: false, onError({ createIssue, error, processType, versions }) { diff --git a/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx b/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx index a17b81b049..7d7e860c1f 100644 --- a/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx +++ b/src/renderer/app/providers/context/MatrixContext/MatrixContext.tsx @@ -114,17 +114,19 @@ export const MatrixProvider = ({ children }: PropsWithChildren) => { await joinRoom(roomId, content); - notificationModel.events.notificationsAdded([{ - smpRoomId: roomId, - multisigAccountId: accountId, - multisigAccountName: accountName, - signatories, - threshold, - originatorAccountId: creatorAccountId, - read: true, - dateCreated: Date.now(), - type: NotificationType.MULTISIG_INVITE, - }] as NoID[]); + notificationModel.events.notificationsAdded([ + { + smpRoomId: roomId, + multisigAccountId: accountId, + multisigAccountName: accountName, + signatories, + threshold, + originatorAccountId: creatorAccountId, + read: true, + dateCreated: Date.now(), + type: NotificationType.MULTISIG_INVITE, + }, + ] as NoID[]); } else { console.log(`Multisig account ${accountId} already exists. Trying to change room to ${roomId}`); await changeRoom(roomId, mstAccount, content, creatorAccountId); diff --git a/src/renderer/entities/notification/model/notification-model.ts b/src/renderer/entities/notification/model/notification-model.ts index 07813bd388..54d84266de 100644 --- a/src/renderer/entities/notification/model/notification-model.ts +++ b/src/renderer/entities/notification/model/notification-model.ts @@ -46,45 +46,3 @@ export const notificationModel = { notificationsAdded, }, }; -// async function insertInTable(table, collection) { -// const dbPromise = window.indexedDB.open('spektr'); -// -// // for some reason .then() does not working -// while (dbPromise.readyState == 'pending') { -// await new Promise((resolve) => { -// setTimeout(resolve, 1_000); -// }); -// console.log('waiting'); -// } -// const tx = dbPromise.result.transaction(table, 'readwrite'); -// console.log(tx); -// const store = tx.objectStore(table); -// let index = 6000; -// collection.forEach((item) => { -// index += 1; -// store.put(item); -// }); -// } -// // { -// // chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3', -// // dateCreated: Date.now(), -// // proxiedAccountId: '0x08eb319467ea54784cd9edfbd03bbcc53f7a021ed8d9ed2ca97b6ae46b3f6014', -// // proxyAccountId: '0x08eb319467ea54784cd9edfbd03bbcc53f7a021ed8d9ed2ca97b6ae46b3f6014', -// // proxyType: 'Any', -// // read: false, -// // type: 'ProxyCreatedNotification', -// // }, -// var notifs = [ -// { -// dateCreated: Date.now(), -// multisigAccountId: '0x08eb319467ea54784cd9edfbd03bbcc53f7a021ed8d9ed2ca97b6ae46b3f6014', -// multisigAccountName: 'My MST', -// originatorAccountId: '0x08eb319467ea54784cd9edfbd03bbcc53f7a021ed8d9ed2ca97b6ae46b3f6014', -// read: false, -// signatories: [], -// smpRoomId: '0x123', -// threshold: 2, -// type: 'MultisigAccountInvitedNotification', -// }] -// -// insertInTable('notifications', notifs); diff --git a/src/renderer/entities/proxy/model/proxy-model.ts b/src/renderer/entities/proxy/model/proxy-model.ts index 873de51d9a..32ec06031f 100644 --- a/src/renderer/entities/proxy/model/proxy-model.ts +++ b/src/renderer/entities/proxy/model/proxy-model.ts @@ -47,8 +47,10 @@ sample({ clock: proxiesRemoved, source: $proxies, fn: (proxies, proxiesToRemove) => { - return proxiesToRemove.reduce((acc, p) => { - acc[p.accountId] = acc[p.accountId].filter((pr) => !proxyUtils.isSameProxy(pr, p)); + return proxiesToRemove.reduce((acc, proxyAccount) => { + acc[proxyAccount.accountId] = acc[proxyAccount.accountId].filter( + (account) => !proxyUtils.isSameProxy(account, proxyAccount), + ); return acc; }, proxies); diff --git a/src/renderer/shared/api/translation/locales/en.json b/src/renderer/shared/api/translation/locales/en.json index 917892cbf4..8084df796f 100644 --- a/src/renderer/shared/api/translation/locales/en.json +++ b/src/renderer/shared/api/translation/locales/en.json @@ -267,8 +267,8 @@ "multisigInviteDescription": "{name}  with threshold { threshold } out of { signatories } signatories", "multisigInviteTitle": "New multisig wallet added", "proxyCreatedDetails": "  is now available for your {name} to control {operations}", - "proxyRemovedDetails": "  is no longer available for your {name} to control {operations}", "proxyCreatedTitle": "New delegated authority wallet added", + "proxyRemovedDetails": "  is no longer available for your {name} to control {operations}", "proxyRemovedTitle": "Delegated authority wallet has been removed", "proxyWalletAction": "{name}  with {address}" }, From 57fff714a36ffdcc2f6f2a6d553b6150c97dc5a1 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Wed, 10 Jan 2024 17:18:48 +0300 Subject: [PATCH 08/12] chore: pr update --- src/renderer/shared/core/types/notification.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer/shared/core/types/notification.ts b/src/renderer/shared/core/types/notification.ts index 39c20ae832..8722a78c29 100644 --- a/src/renderer/shared/core/types/notification.ts +++ b/src/renderer/shared/core/types/notification.ts @@ -38,11 +38,11 @@ export type MultisigOperation = MultisigBaseNotification & { }; export type ProxyAction = BaseNotification & { - proxyAccountId: AccountId; - proxiedAccountId: AccountId; - proxyType: ProxyType; chainId: ChainId; + proxyType: ProxyType; + proxyAccountId: AccountId; proxyWalletName: string; + proxiedAccountId: AccountId; proxiedWalletName: string; proxiedWalletType: WalletType; }; From e20a64d07695a43c5b8942d814f208a75ae0f2da Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Fri, 12 Jan 2024 10:58:27 +0300 Subject: [PATCH 09/12] chore: pr fixes --- .../widgets/WalletDetails/model/wallet-provider-model.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/renderer/widgets/WalletDetails/model/wallet-provider-model.ts b/src/renderer/widgets/WalletDetails/model/wallet-provider-model.ts index da631bc905..8e6ccaf2b0 100644 --- a/src/renderer/widgets/WalletDetails/model/wallet-provider-model.ts +++ b/src/renderer/widgets/WalletDetails/model/wallet-provider-model.ts @@ -88,11 +88,10 @@ const $signatoryContacts = combine( const $signatoryWallets = combine( { - account: $accounts, - accounts: walletModel.$accounts, + accounts: $accounts, wallets: walletModel.$wallets, }, - ({ account, accounts, wallets }): [AccountId, Wallet][] => { + ({ accounts, wallets }): [AccountId, Wallet][] => { const multisigAccount = accounts[0]; if (!multisigAccount || !accountUtils.isMultisigAccount(multisigAccount)) return []; From 190e341144aff703ac5be921cb5b87f862a5070b Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Fri, 12 Jan 2024 11:29:03 +0300 Subject: [PATCH 10/12] chore: pr fixes --- .../proxy/lib/__tests__/utils.test.ts | 10 +- .../wallet/lib/__tests__/wallet-utils.test.ts | 98 +++++----- .../proxies/lib/__tests__/utils.test.ts | 179 ++---------------- .../lib/__tests__/worker-utils.test.ts | 154 +++++++++++++++ .../features/proxies/lib/worker-utils.ts | 54 ++++-- .../lib/hooks/__tests__/useTaskQueue.test.ts | 2 +- .../lib/utils/__tests__/substrate.test.ts | 12 +- .../dataVerification.base.test.ts | 8 +- tests/integrations/matrix/matrix.base.test.ts | 2 +- .../migrations/migration-2.test.tsx | 11 +- 10 files changed, 271 insertions(+), 259 deletions(-) create mode 100644 src/renderer/features/proxies/lib/__tests__/worker-utils.test.ts diff --git a/src/renderer/entities/proxy/lib/__tests__/utils.test.ts b/src/renderer/entities/proxy/lib/__tests__/utils.test.ts index 46c29627e6..1d1d24c3f2 100644 --- a/src/renderer/entities/proxy/lib/__tests__/utils.test.ts +++ b/src/renderer/entities/proxy/lib/__tests__/utils.test.ts @@ -2,7 +2,7 @@ import { HexString, ProxyAccount, ProxyType } from '@shared/core'; import { proxyUtils } from '../utils'; describe('entities/proxy/lib/utils', () => { - it('should return true when oldProxy and newProxy have the same properties', () => { + test('should return true when oldProxy and newProxy have the same properties', () => { const oldProxy = { id: 1, accountId: '0x00', @@ -23,10 +23,10 @@ describe('entities/proxy/lib/utils', () => { const result = proxyUtils.isSameProxy(oldProxy, newProxy); - expect(result).toBe(true); + expect(result).toEqual(true); }); - it('should return false when oldProxy and newProxy have different properties', () => { + test('should return false when oldProxy and newProxy have different properties', () => { const oldProxy = { id: 1, accountId: '0x00', @@ -47,10 +47,10 @@ describe('entities/proxy/lib/utils', () => { const result = proxyUtils.isSameProxy(oldProxy, newProxy); - expect(result).toBe(false); + expect(result).toEqual(false); }); - it('should return the proxied name for a given proxied account', () => { + test('should return the proxied name for a given proxied account', () => { const proxiedAccount = { accountId: '0x01' as HexString, proxyType: 'Any' as ProxyType, diff --git a/src/renderer/entities/wallet/lib/__tests__/wallet-utils.test.ts b/src/renderer/entities/wallet/lib/__tests__/wallet-utils.test.ts index 0030171098..854ee28af1 100644 --- a/src/renderer/entities/wallet/lib/__tests__/wallet-utils.test.ts +++ b/src/renderer/entities/wallet/lib/__tests__/wallet-utils.test.ts @@ -2,147 +2,147 @@ import { Wallet, WalletType } from '@shared/core'; import { walletUtils } from '../wallet-utils'; describe('entities/wallet/lib/wallet-utils.ts', () => { - it('isPolkadotVault should return true if wallet type is PolkadotVault', () => { + test('isPolkadotVault should return true if wallet type is PolkadotVault', () => { const wallet: Pick = { type: WalletType.POLKADOT_VAULT }; - expect(walletUtils.isPolkadotVault(wallet)).toBe(true); + expect(walletUtils.isPolkadotVault(wallet)).toEqual(true); }); - it('isPolkadotVault should return false if wallet type is not PolkadotVault', () => { + test('isPolkadotVault should return false if wallet type is not PolkadotVault', () => { const wallet: Pick = { type: WalletType.NOVA_WALLET }; - expect(walletUtils.isPolkadotVault(wallet)).toBe(false); + expect(walletUtils.isPolkadotVault(wallet)).toEqual(false); }); - it('isMultiShard should return true when wallet type is MultiShard', () => { + test('isMultiShard should return true when wallet type is MultiShard', () => { const wallet: Pick = { type: WalletType.MULTISHARD_PARITY_SIGNER }; - expect(walletUtils.isMultiShard(wallet)).toBe(true); + expect(walletUtils.isMultiShard(wallet)).toEqual(true); }); - it('isMultiShard should return false when wallet type is not MultiShard', () => { + test('isMultiShard should return false when wallet type is not MultiShard', () => { const wallet: Pick = { type: WalletType.NOVA_WALLET }; - expect(walletUtils.isMultiShard(wallet)).toBe(false); + expect(walletUtils.isMultiShard(wallet)).toEqual(false); }); - it('isMultisig should return true when wallet type is Multisig', () => { + test('isMultisig should return true when wallet type is Multisig', () => { const wallet: Pick = { type: WalletType.MULTISIG }; - expect(walletUtils.isMultisig(wallet)).toBe(true); + expect(walletUtils.isMultisig(wallet)).toEqual(true); }); - it('isMultisig should return false when wallet type is not Multisig', () => { + test('isMultisig should return false when wallet type is not Multisig', () => { const wallet: Pick = { type: WalletType.NOVA_WALLET }; - expect(walletUtils.isMultisig(wallet)).toBe(false); + expect(walletUtils.isMultisig(wallet)).toEqual(false); }); - it('isNovaWallet should return true when wallet type is NovaWallet', () => { + test('isNovaWallet should return true when wallet type is NovaWallet', () => { const wallet: Pick = { type: WalletType.NOVA_WALLET }; - expect(walletUtils.isNovaWallet(wallet)).toBe(true); + expect(walletUtils.isNovaWallet(wallet)).toEqual(true); }); - it('isNovaWallet should return false when wallet type is not NovaWallet', () => { + test('isNovaWallet should return false when wallet type is not NovaWallet', () => { const wallet: Pick = { type: WalletType.POLKADOT_VAULT }; - expect(walletUtils.isMultisig(wallet)).toBe(false); + expect(walletUtils.isMultisig(wallet)).toEqual(false); }); - it('isProxied should return true when wallet type is Proxied', () => { + test('isProxied should return true when wallet type is Proxied', () => { const wallet: Pick = { type: WalletType.PROXIED }; - expect(walletUtils.isProxied(wallet)).toBe(true); + expect(walletUtils.isProxied(wallet)).toEqual(true); }); - it('isProxied should return false when wallet type is not Proxied', () => { + test('isProxied should return false when wallet type is not Proxied', () => { const wallet: Pick = { type: WalletType.POLKADOT_VAULT }; - expect(walletUtils.isProxied(wallet)).toBe(false); + expect(walletUtils.isProxied(wallet)).toEqual(false); }); - it('isSingleShard should return true when wallet type is SingleShard', () => { + test('isSingleShard should return true when wallet type is SingleShard', () => { const wallet: Pick = { type: WalletType.SINGLE_PARITY_SIGNER }; - expect(walletUtils.isSingleShard(wallet)).toBe(true); + expect(walletUtils.isSingleShard(wallet)).toEqual(true); }); - it('isSingleShard should return false when wallet type is not SingleShard', () => { + test('isSingleShard should return false when wallet type is not SingleShard', () => { const wallet: Pick = { type: WalletType.POLKADOT_VAULT }; - expect(walletUtils.isSingleShard(wallet)).toBe(false); + expect(walletUtils.isSingleShard(wallet)).toEqual(false); }); - it('isWalletConnect should return true when wallet type is WALLET_CONNECT', () => { + test('isWalletConnect should return true when wallet type is WALLET_CONNECT', () => { const wallet: Pick = { type: WalletType.WALLET_CONNECT }; - expect(walletUtils.isWalletConnect(wallet)).toBe(true); + expect(walletUtils.isWalletConnect(wallet)).toEqual(true); }); - it('isWalletConnect should return false when wallet type is not WALLET_CONNECT', () => { + test('isWalletConnect should return false when wallet type is not WALLET_CONNECT', () => { const wallet: Pick = { type: WalletType.POLKADOT_VAULT }; - expect(walletUtils.isWalletConnect(wallet)).toBe(false); + expect(walletUtils.isWalletConnect(wallet)).toEqual(false); }); - it('isWatchOnly should return true when wallet type is WATCH_ONLY', () => { + test('isWatchOnly should return true when wallet type is WATCH_ONLY', () => { const wallet: Pick = { type: WalletType.WATCH_ONLY }; - expect(walletUtils.isWatchOnly(wallet)).toBe(true); + expect(walletUtils.isWatchOnly(wallet)).toEqual(true); }); - it('isWatchOnly should return false when wallet type is not WATCH_ONLY', () => { + test('isWatchOnly should return false when wallet type is not WATCH_ONLY', () => { const wallet: Pick = { type: WalletType.POLKADOT_VAULT }; - expect(walletUtils.isWatchOnly(wallet)).toBe(false); + expect(walletUtils.isWatchOnly(wallet)).toEqual(false); }); - it('isWalletConnectGroup should return true when wallet type is in wallet connect group', () => { + test('isWalletConnectGroup should return true when wallet type is in wallet connect group', () => { const walletConnect: Pick = { type: WalletType.WALLET_CONNECT }; const novaWallet: Pick = { type: WalletType.NOVA_WALLET }; - expect(walletUtils.isWalletConnectGroup(walletConnect)).toBe(true); - expect(walletUtils.isWalletConnectGroup(novaWallet)).toBe(true); + expect(walletUtils.isWalletConnectGroup(walletConnect)).toEqual(true); + expect(walletUtils.isWalletConnectGroup(novaWallet)).toEqual(true); }); - it('isWalletConnectGroup should return true when wallet type is in wallet connect group', () => { + test('isWalletConnectGroup should return true when wallet type is in wallet connect group', () => { const singleshard: Pick = { type: WalletType.SINGLE_PARITY_SIGNER }; const multishard: Pick = { type: WalletType.MULTISHARD_PARITY_SIGNER }; const polkadotVault: Pick = { type: WalletType.POLKADOT_VAULT }; - expect(walletUtils.isPolkadotVaultGroup(singleshard)).toBe(true); - expect(walletUtils.isPolkadotVaultGroup(multishard)).toBe(true); - expect(walletUtils.isPolkadotVaultGroup(polkadotVault)).toBe(true); + expect(walletUtils.isPolkadotVaultGroup(singleshard)).toEqual(true); + expect(walletUtils.isPolkadotVaultGroup(multishard)).toEqual(true); + expect(walletUtils.isPolkadotVaultGroup(polkadotVault)).toEqual(true); }); - it('isWatchOnly should return false when wallet type is not WATCH_ONLY', () => { + test('isWatchOnly should return false when wallet type is not WATCH_ONLY', () => { const wallet: Pick = { type: WalletType.POLKADOT_VAULT }; - expect(walletUtils.isWatchOnly(wallet)).toBe(false); + expect(walletUtils.isWatchOnly(wallet)).toEqual(false); }); - it('isValidSignatory returns false if wallet is not provided', () => { + test('isValidSignatory returns false if wallet is not provided', () => { const result = walletUtils.isValidSignatory(); - expect(result).toBe(false); + expect(result).toEqual(false); }); - it('isValidSignatory returns false if wallet type is not in the valid signatory wallet types', () => { + test('isValidSignatory returns false if wallet type is not in the valid signatory wallet types', () => { const wallet: Pick = { type: WalletType.MULTISIG }; const result = walletUtils.isValidSignatory(wallet); - expect(result).toBe(false); + expect(result).toEqual(false); }); - it('isValidSignatory returns true if wallet type is in the valid signatory wallet types', () => { + test('isValidSignatory returns true if wallet type is in the valid signatory wallet types', () => { const wallet: Pick = { type: WalletType.SINGLE_PARITY_SIGNER }; const result = walletUtils.isValidSignatory(wallet); - expect(result).toBe(true); + expect(result).toEqual(true); }); - it('getWalletById should return the correct wallet when found', () => { + test('getWalletById should return the correct wallet when found', () => { const wallets = [ { id: 1, name: 'Wallet 1' }, { id: 2, name: 'Wallet 2' }, @@ -152,7 +152,7 @@ describe('entities/wallet/lib/wallet-utils.ts', () => { expect(walletUtils.getWalletById(wallets, 2)).toEqual({ id: 2, name: 'Wallet 2' }); }); - it('getWalletById should return undefined when wallet not found', () => { + test('getWalletById should return undefined when wallet not found', () => { const wallets = [ { id: 1, name: 'Wallet 1' }, { id: 2, name: 'Wallet 2' }, diff --git a/src/renderer/features/proxies/lib/__tests__/utils.test.ts b/src/renderer/features/proxies/lib/__tests__/utils.test.ts index 044b21d3f2..eb316730aa 100644 --- a/src/renderer/features/proxies/lib/__tests__/utils.test.ts +++ b/src/renderer/features/proxies/lib/__tests__/utils.test.ts @@ -1,182 +1,25 @@ -import { - AccountType, - Chain, - ChainType, - CryptoType, - PartialProxiedAccount, - ProxyAccount, - ProxyVariant, -} from '@shared/core'; -import { TEST_ACCOUNT_ID, TEST_ADDRESS } from '@shared/lib/utils'; -import { proxyWorkerUtils } from '../utils'; +import { Chain } from '@shared/core'; +import { proxiesUtils } from '../utils'; describe('features/proxies/lib/utils', () => { - it('should return true when oldProxy and newProxy have the same properties', () => { - const oldProxy = { - id: 1, - accountId: '0x00', - proxiedAccountId: '0x01', - chainId: '0x05', - proxyType: 'Any', - delay: 0, - } as ProxyAccount; - - const newProxy = { - id: 2, - accountId: '0x00', - proxiedAccountId: '0x01', - chainId: '0x05', - proxyType: 'Any', - delay: 0, - } as ProxyAccount; - - const result = proxyWorkerUtils.isSameProxy(oldProxy, newProxy); - - expect(result).toBe(true); - }); - - it('should return false when oldProxy and newProxy have different properties', () => { - const oldProxy = { - id: 1, - accountId: '0x00', - proxiedAccountId: '0x01', - chainId: '0x05', - proxyType: 'Any', - delay: 0, - } as ProxyAccount; - - const newProxy = { - id: 2, - accountId: '0x01', - proxiedAccountId: '0x02', - chainId: '0x05', - proxyType: 'Any', - delay: 0, - } as ProxyAccount; - - const result = proxyWorkerUtils.isSameProxy(oldProxy, newProxy); - - expect(result).toBe(false); - }); - - it('should return true when oldProxy and newProxy have the same properties', () => { - const oldProxied = { - id: 0, - walletId: 0, - name: 'Proxied wallet', - type: AccountType.PROXIED, - accountId: '0x00', - proxyAccountId: '0x01', - chainId: '0x05', - proxyType: 'Any', - delay: 0, - proxyVariant: ProxyVariant.REGULAR, - chainType: ChainType.SUBSTRATE, - cryptoType: CryptoType.SR25519, - } as PartialProxiedAccount; - - const newProxied = { - id: 2, - walletId: 1, - name: 'Proxied wallet 2', - type: AccountType.PROXIED, - accountId: '0x00', - proxyAccountId: '0x01', - chainId: '0x05', - proxyType: 'Any', - delay: 0, - proxyVariant: ProxyVariant.REGULAR, - chainType: ChainType.SUBSTRATE, - cryptoType: CryptoType.SR25519, - } as PartialProxiedAccount; - - const result = proxyWorkerUtils.isSameProxied(oldProxied, newProxied); - - expect(result).toBe(true); - }); - - it('should return false when oldProxied and newProxied have different properties', () => { - const oldProxied = { - id: 0, - walletId: 0, - name: 'Proxied wallet', - type: AccountType.PROXIED, - accountId: '0x00', - proxyAccountId: '0x01', - chainId: '0x05', - proxyType: 'Any', - delay: 0, - proxyVariant: ProxyVariant.REGULAR, - chainType: ChainType.SUBSTRATE, - cryptoType: CryptoType.SR25519, - } as PartialProxiedAccount; - - const newProxied = { - id: 2, - walletId: 1, - name: 'Proxied wallet 2', - type: AccountType.PROXIED, - accountId: '0x00', - proxyAccountId: '0x02', - chainId: '0x06', - proxyType: 'Any', - delay: 0, - proxyVariant: ProxyVariant.REGULAR, - chainType: ChainType.SUBSTRATE, - cryptoType: CryptoType.SR25519, - } as PartialProxiedAccount; - - const result = proxyWorkerUtils.isSameProxied(oldProxied, newProxied); - - expect(result).toBe(false); - }); - - it('should return the account id when given a valid address', () => { - const address = TEST_ADDRESS; - const expectedAccountId = TEST_ACCOUNT_ID; - - const result = proxyWorkerUtils.toAccountId(address); - - expect(result).toEqual(expectedAccountId); - }); - - it('should return "0x00" when given an invalid address', () => { - const address = 'invalid_address'; - const expectedAccountId = '0x00'; - - const result = proxyWorkerUtils.toAccountId(address); - - expect(result).toEqual(expectedAccountId); - }); - - it('should return true if "regular_proxy" is included in chain options', () => { + test('should return true if "regular_proxy" is included in chain options', () => { const chainWithRegularProxy = { options: ['regular_proxy'] } as Chain; - const result = proxyWorkerUtils.isRegularProxy(chainWithRegularProxy); + const result = proxiesUtils.isRegularProxy(chainWithRegularProxy); - expect(result).toBe(true); + expect(result).toEqual(true); }); - it('should return false if "regular_proxy" is not included in chain options', () => { + test('should return false if "regular_proxy" is not included in chain options', () => { const chainWithoutRegularProxy = { options: ['multisig'] } as Chain; - const result = proxyWorkerUtils.isRegularProxy(chainWithoutRegularProxy); + const result = proxiesUtils.isRegularProxy(chainWithoutRegularProxy); - expect(result).toBe(false); + expect(result).toEqual(false); }); - it('should return false if chain options is undefined', () => { + test('should return false if chain options is undefined', () => { const chainWithUndefinedOptions = {} as Chain; - const result = proxyWorkerUtils.isRegularProxy(chainWithUndefinedOptions); - - expect(result).toBe(false); - }); - - it('should return true if account type is PROXIED', () => { - const account = { type: AccountType.PROXIED }; - expect(proxyWorkerUtils.isProxiedAccount(account)).toBe(true); - }); + const result = proxiesUtils.isRegularProxy(chainWithUndefinedOptions); - it('should return false if account type is not PROXIED', () => { - const account = { type: AccountType.BASE }; - expect(proxyWorkerUtils.isProxiedAccount(account)).toBe(false); + expect(result).toEqual(false); }); }); diff --git a/src/renderer/features/proxies/lib/__tests__/worker-utils.test.ts b/src/renderer/features/proxies/lib/__tests__/worker-utils.test.ts new file mode 100644 index 0000000000..58173ecf07 --- /dev/null +++ b/src/renderer/features/proxies/lib/__tests__/worker-utils.test.ts @@ -0,0 +1,154 @@ +import { AccountType, ProxyVariant, ChainType, CryptoType } from '@shared/core'; +import type { PartialProxiedAccount, ProxyAccount } from '@shared/core'; +import { TEST_ACCOUNT_ID, TEST_ADDRESS } from '@shared/lib/utils'; +import { proxyWorkerUtils } from '../worker-utils'; + +describe('features/proxies/lib/worker-utils', () => { + test('should return true when oldProxy and newProxy have the same properties', () => { + const oldProxy = { + id: 1, + accountId: '0x00', + proxiedAccountId: '0x01', + chainId: '0x05', + proxyType: 'Any', + delay: 0, + } as ProxyAccount; + + const newProxy = { + id: 2, + accountId: '0x00', + proxiedAccountId: '0x01', + chainId: '0x05', + proxyType: 'Any', + delay: 0, + } as ProxyAccount; + + const result = proxyWorkerUtils.isSameProxy(oldProxy, newProxy); + + expect(result).toEqual(true); + }); + + test('should return false when oldProxy and newProxy have different properties', () => { + const oldProxy = { + id: 1, + accountId: '0x00', + proxiedAccountId: '0x01', + chainId: '0x05', + proxyType: 'Any', + delay: 0, + } as ProxyAccount; + + const newProxy = { + id: 2, + accountId: '0x01', + proxiedAccountId: '0x02', + chainId: '0x05', + proxyType: 'Any', + delay: 0, + } as ProxyAccount; + + const result = proxyWorkerUtils.isSameProxy(oldProxy, newProxy); + + expect(result).toEqual(false); + }); + + test('should return true when oldProxy and newProxy have the same properties', () => { + const oldProxied = { + id: 0, + walletId: 0, + name: 'Proxied wallet', + type: AccountType.PROXIED, + accountId: '0x00', + proxyAccountId: '0x01', + chainId: '0x05', + proxyType: 'Any', + delay: 0, + proxyVariant: ProxyVariant.REGULAR, + chainType: ChainType.SUBSTRATE, + cryptoType: CryptoType.SR25519, + } as PartialProxiedAccount; + + const newProxied = { + id: 2, + walletId: 1, + name: 'Proxied wallet 2', + type: AccountType.PROXIED, + accountId: '0x00', + proxyAccountId: '0x01', + chainId: '0x05', + proxyType: 'Any', + delay: 0, + proxyVariant: ProxyVariant.REGULAR, + chainType: ChainType.SUBSTRATE, + cryptoType: CryptoType.SR25519, + } as PartialProxiedAccount; + + const result = proxyWorkerUtils.isSameProxied(oldProxied, newProxied); + + expect(result).toEqual(true); + }); + + test('should return false when oldProxied and newProxied have different properties', () => { + const oldProxied = { + id: 0, + walletId: 0, + name: 'Proxied wallet', + type: AccountType.PROXIED, + accountId: '0x00', + proxyAccountId: '0x01', + chainId: '0x05', + proxyType: 'Any', + delay: 0, + proxyVariant: ProxyVariant.REGULAR, + chainType: ChainType.SUBSTRATE, + cryptoType: CryptoType.SR25519, + } as PartialProxiedAccount; + + const newProxied = { + id: 2, + walletId: 1, + name: 'Proxied wallet 2', + type: AccountType.PROXIED, + accountId: '0x00', + proxyAccountId: '0x02', + chainId: '0x06', + proxyType: 'Any', + delay: 0, + proxyVariant: ProxyVariant.REGULAR, + chainType: ChainType.SUBSTRATE, + cryptoType: CryptoType.SR25519, + } as PartialProxiedAccount; + + const result = proxyWorkerUtils.isSameProxied(oldProxied, newProxied); + + expect(result).toEqual(false); + }); + + test('should return the account id when given a valid address', () => { + const address = TEST_ADDRESS; + const expectedAccountId = TEST_ACCOUNT_ID; + + const result = proxyWorkerUtils.toAccountId(address); + + expect(result).toEqual(expectedAccountId); + }); + + test('should return "0x00" when given an invalid address', () => { + const address = 'invalid_address'; + const expectedAccountId = '0x00'; + + const result = proxyWorkerUtils.toAccountId(address); + + expect(result).toEqual(expectedAccountId); + }); + + test('should return true if account type is PROXIED', () => { + const account = { type: AccountType.PROXIED }; + expect(proxyWorkerUtils.isProxiedAccount(account)).toEqual(true); + }); + + test('should return false if account type is not PROXIED', () => { + const account = { type: AccountType.BASE }; + expect(proxyWorkerUtils.isProxiedAccount(account)).toEqual(false); + }); +}); diff --git a/src/renderer/features/proxies/lib/worker-utils.ts b/src/renderer/features/proxies/lib/worker-utils.ts index e56f760f61..69cce2b904 100644 --- a/src/renderer/features/proxies/lib/worker-utils.ts +++ b/src/renderer/features/proxies/lib/worker-utils.ts @@ -3,22 +3,26 @@ import { decodeAddress } from '@polkadot/util-crypto'; import { WellKnownChain } from '@substrate/connect'; import { ApiPromise } from '@polkadot/api'; -import { AccountId, ChainId, ProxyAccount, Account, ProxiedAccount, NoID, AccountType } from '@shared/core'; +import { + AccountId, + ChainId, + ProxyAccount, + AccountType, + Account, + ProxiedAccount, + NoID, + PartialProxiedAccount, +} from '@shared/core'; export const proxyWorkerUtils = { toAccountId, isSameProxy, - getKnownChain, + isSameProxied, isProxiedAccount, isApiConnected, + getKnownChain, }; -/** - * Try to get account id of the address - * WARNING! Duplication for worker - * @param address account's address - * @return {String} - */ export function toAccountId(address: string): AccountId { try { return u8aToHex(decodeAddress(address)); @@ -37,18 +41,14 @@ function isSameProxy(oldProxy: NoID, newProxy: NoID) ); } -const enum Chains { - POLKADOT = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3', - KUSAMA = '0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe', -} - -const KnownChains: Record = { - [Chains.POLKADOT]: WellKnownChain.polkadot, - [Chains.KUSAMA]: WellKnownChain.ksmcc3, -}; - -function getKnownChain(chainId: ChainId): WellKnownChain | undefined { - return KnownChains[chainId]; +function isSameProxied(oldProxy: PartialProxiedAccount, newProxy: PartialProxiedAccount): boolean { + return ( + oldProxy.accountId === newProxy.accountId && + oldProxy.proxyAccountId === newProxy.proxyAccountId && + oldProxy.chainId === newProxy.chainId && + oldProxy.proxyType === newProxy.proxyType && + oldProxy.delay === newProxy.delay + ); } function isProxiedAccount(account: Pick): account is ProxiedAccount { @@ -60,3 +60,17 @@ function isApiConnected(apis: Record, chainId: ChainId): bo return Boolean(api?.isConnected); } + +const MainChains = { + POLKADOT: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3', + KUSAMA: '0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe', +}; + +const KnownChains: Record = { + [MainChains.POLKADOT]: WellKnownChain.polkadot, + [MainChains.KUSAMA]: WellKnownChain.ksmcc3, +}; + +function getKnownChain(chainId: ChainId): WellKnownChain | undefined { + return KnownChains[chainId]; +} diff --git a/src/renderer/shared/lib/hooks/__tests__/useTaskQueue.test.ts b/src/renderer/shared/lib/hooks/__tests__/useTaskQueue.test.ts index 8dcddfe50c..79eb091834 100644 --- a/src/renderer/shared/lib/hooks/__tests__/useTaskQueue.test.ts +++ b/src/renderer/shared/lib/hooks/__tests__/useTaskQueue.test.ts @@ -32,7 +32,7 @@ describe('hooks/useTaskQueue', () => { expect(fn).toHaveBeenCalledTimes(1); }); - expect(result.current.tasks.length).toBe(1); + expect(result.current.tasks.length).toEqual(1); act(() => addTask(fn)); diff --git a/src/renderer/shared/lib/utils/__tests__/substrate.test.ts b/src/renderer/shared/lib/utils/__tests__/substrate.test.ts index 4f6ca2e2d8..63681b653f 100644 --- a/src/renderer/shared/lib/utils/__tests__/substrate.test.ts +++ b/src/renderer/shared/lib/utils/__tests__/substrate.test.ts @@ -49,8 +49,8 @@ describe('shared/lib/utils/substrate', () => { const result = getPalletAndCallByXcmTransferType(api, transferType); - expect(result.pallet).toBe(XcmPallets.XTOKENS); - expect(result.call).toBe('transferMultiasset'); + expect(result.pallet).toEqual(XcmPallets.XTOKENS); + expect(result.call).toEqual('transferMultiasset'); }); test('should return XCM_PALLET and "limitedReserveTransferAssets" call for XCMPALLET', () => { @@ -59,8 +59,8 @@ describe('shared/lib/utils/substrate', () => { const result = getPalletAndCallByXcmTransferType(api, transferType); - expect(result.pallet).toBe(XcmPallets.XCM_PALLET); - expect(result.call).toBe('limitedReserveTransferAssets'); + expect(result.pallet).toEqual(XcmPallets.XCM_PALLET); + expect(result.call).toEqual('limitedReserveTransferAssets'); }); test('should return XCM_PALLET and "limitedTeleportAssets" call for XCMPALLET_TELEPORT', () => { @@ -69,7 +69,7 @@ describe('shared/lib/utils/substrate', () => { const result = getPalletAndCallByXcmTransferType(api, transferType); - expect(result.pallet).toBe(XcmPallets.XCM_PALLET); - expect(result.call).toBe('limitedTeleportAssets'); + expect(result.pallet).toEqual(XcmPallets.XCM_PALLET); + expect(result.call).toEqual('limitedTeleportAssets'); }); }); diff --git a/tests/integrations/dataVerification/dataVerification.base.test.ts b/tests/integrations/dataVerification/dataVerification.base.test.ts index 3d39996394..f76c936b96 100644 --- a/tests/integrations/dataVerification/dataVerification.base.test.ts +++ b/tests/integrations/dataVerification/dataVerification.base.test.ts @@ -41,7 +41,7 @@ describe('Verification function can verify parachains', () => { const data = await parachainApi.query.system.account(parachainAccount?.account); const validationStatus = await validate(polkadotApi, parachainApi, storageKey, data); - expect(validationStatus).toBe(true); + expect(validationStatus).toEqual(true); }); test.each(kusamaParachains)('Can verify data for kusama parachain: $name', async (parachain) => { @@ -52,7 +52,7 @@ describe('Verification function can verify parachains', () => { const data = await parachainApi.query.system.account(parachainAccount?.account); const validationStatus = await validate(kusamaApi, parachainApi, storageKey, data); - expect(validationStatus).toBe(true); + expect(validationStatus).toEqual(true); }); test.each(kusamaParachains)('Verification return false if nonce was changed for $name', async (parachain) => { @@ -66,7 +66,7 @@ describe('Verification function can verify parachains', () => { data.set('nonce', new_nonce_value); const validationStatus = await validate(kusamaApi, parachainApi, storageKey, data); - expect(validationStatus).toBe(false); + expect(validationStatus).toEqual(false); }); test.each(polkadotParachains)('Verification return false if balance was changed for $name', async (parachain) => { @@ -80,6 +80,6 @@ describe('Verification function can verify parachains', () => { data.data.set('free', new_balance_value); const validationStatus = await validate(polkadotApi, parachainApi, storageKey, data); - expect(validationStatus).toBe(false); + expect(validationStatus).toEqual(false); }); }); diff --git a/tests/integrations/matrix/matrix.base.test.ts b/tests/integrations/matrix/matrix.base.test.ts index 8d4972bfa5..917c8fa170 100644 --- a/tests/integrations/matrix/matrix.base.test.ts +++ b/tests/integrations/matrix/matrix.base.test.ts @@ -29,7 +29,7 @@ describe('services/matrix', () => { test.each([test_data.test_accounts])('User %s can login', async (credentials) => { await matrixLoginAndSync(matrix, credentials.login, credentials.password); - expect(matrix.isLoggedIn).toBe(true); + expect(matrix.isLoggedIn).toEqual(true); }); test.each([test_data.test_accounts])('User %s can create the room', async (credentials) => { diff --git a/tests/integrations/migrations/migration-2.test.tsx b/tests/integrations/migrations/migration-2.test.tsx index 29dea0802e..c4c2a4cb79 100644 --- a/tests/integrations/migrations/migration-2.test.tsx +++ b/tests/integrations/migrations/migration-2.test.tsx @@ -17,6 +17,7 @@ class MyDatabase extends Dexie { }); this.accounts = this.table('accounts'); this.wallets = this.table('wallets'); + this.multisigEvents = this.table('multisigEvents'); } } @@ -58,7 +59,7 @@ describe('migrateWallets from 17 to 18', () => { db.close(); }); - it('multishard with one root and one shard migrate correctly', async () => { + test('multishard with one root and one shard migrate correctly', async () => { const { wallet, root, shard } = mockDataBuilder.buildMultishardWallet(); await pushDataToDB(db, db.accounts, [root, shard]); await pushDataToDB(db, db.wallets, [wallet]); @@ -90,7 +91,7 @@ describe('migrateWallets from 17 to 18', () => { }); }); - it('multisig migrate correctly', async () => { + test('multisig migrate correctly', async () => { const { signatoryAccount, multisigAccount } = mockDataBuilder.buildMultisigWallet(3, 3); await pushDataToDB(db, db.accounts, [signatoryAccount, multisigAccount]); @@ -119,7 +120,7 @@ describe('migrateWallets from 17 to 18', () => { expect(vaultWalletBD).toMatchObject({ signingType: 'signing_ps' }); }); - it('polkadot vault migrate correctly', async () => { + test('polkadot vault migrate correctly', async () => { const vaultAccount = mockDataBuilder.buildAccount(true, false, 'signing_ps'); await pushDataToDB(db, db.accounts, [vaultAccount]); @@ -139,7 +140,7 @@ describe('migrateWallets from 17 to 18', () => { expect(vaultWalletBD).toMatchObject({ signingType: 'signing_ps' }); }); - it('watch only wallet migrate correctly', async () => { + test('watch only wallet migrate correctly', async () => { const watchOnlyAccount = mockDataBuilder.buildAccount(true, false, 'signing_wo'); await pushDataToDB(db, db.accounts, [watchOnlyAccount]); @@ -157,6 +158,6 @@ describe('migrateWallets from 17 to 18', () => { }); expect(watchOnlyWalletBD).toMatchObject({ signingType: 'signing_wo' }); - expect(watchOnlyWalletBD.isActive).toBe(true); + expect(watchOnlyWalletBD.isActive).toEqual(true); }); }); From 95851a1593e96adf3ba5b5f1f191a4bfea80c580 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Fri, 12 Jan 2024 12:31:30 +0300 Subject: [PATCH 11/12] feat: proxied name --- .../entities/proxy/lib/__tests__/utils.test.ts | 2 +- src/renderer/entities/proxy/lib/utils.ts | 6 +++--- .../ui/notifies/ProxyCreatedNotification.tsx | 4 ++-- .../ui/notifies/ProxyRemovedNotification.tsx | 6 +++--- src/renderer/features/proxies/lib/utils.ts | 9 +++++---- .../features/proxies/model/proxies-model.ts | 15 ++++----------- .../features/proxies/workers/proxy-worker.ts | 1 + src/renderer/shared/core/types/notification.ts | 4 ++-- 8 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/renderer/entities/proxy/lib/__tests__/utils.test.ts b/src/renderer/entities/proxy/lib/__tests__/utils.test.ts index 1d1d24c3f2..d405ef272a 100644 --- a/src/renderer/entities/proxy/lib/__tests__/utils.test.ts +++ b/src/renderer/entities/proxy/lib/__tests__/utils.test.ts @@ -57,7 +57,7 @@ describe('entities/proxy/lib/utils', () => { }; const expectedName = 'Any for 0x01'; - const result = proxyUtils.getProxiedName(proxiedAccount); + const result = proxyUtils.getProxiedName(proxiedAccount.accountId, proxiedAccount.proxyType); expect(result).toEqual(expectedName); }); diff --git a/src/renderer/entities/proxy/lib/utils.ts b/src/renderer/entities/proxy/lib/utils.ts index f2b19c99b2..7f465af77c 100644 --- a/src/renderer/entities/proxy/lib/utils.ts +++ b/src/renderer/entities/proxy/lib/utils.ts @@ -1,5 +1,5 @@ import { toShortAddress } from '@shared/lib/utils'; -import type { ProxiedAccount, ProxyAccount } from '@shared/core'; +import { ProxyAccount, AccountId, ProxyType } from '@shared/core'; export const proxyUtils = { isSameProxy, @@ -17,6 +17,6 @@ function isSameProxy(oldProxy: ProxyAccount, newProxy: ProxyAccount) { } // TODO: Add i18n for wallet name -function getProxiedName(proxiedAccount: Pick): string { - return `${proxiedAccount.proxyType} for ${toShortAddress(proxiedAccount.accountId)}`; +function getProxiedName(accountId: AccountId, proxyType: ProxyType): string { + return `${proxyType} for ${toShortAddress(accountId)}`; } diff --git a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx index f925d571c3..9b8cd7f90f 100644 --- a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyCreatedNotification.tsx @@ -45,12 +45,12 @@ export const ProxyCreatedNotification = ({ notification }: Props) => { t={t} i18nKey="notifications.details.proxyCreatedDetails" values={{ - name: notification.proxiedWalletName, + name: notification.proxyWalletName, operations: t(ProxyTypeOperation[notification.proxyType]), }} components={{ chain: , - walletIcon: , + walletIcon: , }} /> diff --git a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx index 824f57be15..9545d86e25 100644 --- a/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx +++ b/src/renderer/features/notifications/NotificationsList/ui/notifies/ProxyRemovedNotification.tsx @@ -34,7 +34,7 @@ export const ProxyRemovedNotification = ({ notification }: Props) => { , }} @@ -45,12 +45,12 @@ export const ProxyRemovedNotification = ({ notification }: Props) => { t={t} i18nKey="notifications.details.proxyRemovedDetails" values={{ - name: notification.proxiedWalletName, + name: notification.proxyWalletName, operations: t(ProxyTypeOperation[notification.proxyType]), }} components={{ chain: , - walletIcon: , + walletIcon: , }} /> diff --git a/src/renderer/features/proxies/lib/utils.ts b/src/renderer/features/proxies/lib/utils.ts index 8f822f780a..b0b2d464a3 100644 --- a/src/renderer/features/proxies/lib/utils.ts +++ b/src/renderer/features/proxies/lib/utils.ts @@ -1,6 +1,7 @@ import { NotificationType, Chain } from '@shared/core'; import type { ProxyAccount, Account, ProxyAction, NoID, Wallet } from '@shared/core'; import { dictionary } from '@shared/lib/utils'; +import { proxyUtils } from '@entities/proxy'; export const proxiesUtils = { isRegularProxy, @@ -23,12 +24,12 @@ function getNotification( return proxies.map((proxy) => ({ chainId: proxy.chainId, dateCreated: Date.now(), - proxiedAccountId: proxy.proxiedAccountId, - proxiedWalletType: accountsWalletsMap[proxy.proxiedAccountId].type, - proxiedWalletName: accountsWalletsMap[proxy.proxiedAccountId].name, - proxyAccountId: proxy.accountId, proxyType: proxy.proxyType, + proxyAccountId: proxy.accountId, proxyWalletName: accountsWalletsMap[proxy.accountId].name, + proxyWalletType: accountsWalletsMap[proxy.accountId].type, + proxiedAccountId: proxy.proxiedAccountId, + proxiedWalletName: proxyUtils.getProxiedName(proxy.accountId, proxy.proxyType), read: false, type, })); diff --git a/src/renderer/features/proxies/model/proxies-model.ts b/src/renderer/features/proxies/model/proxies-model.ts index 5ad065637a..33fa0adc64 100644 --- a/src/renderer/features/proxies/model/proxies-model.ts +++ b/src/renderer/features/proxies/model/proxies-model.ts @@ -3,22 +3,18 @@ import { createEndpoint } from '@remote-ui/rpc'; import { keyBy } from 'lodash'; import { once, spread } from 'patronum'; -import { +import type { Account, AccountId, - AccountType, Chain, ChainId, - ChainType, Connection, - CryptoType, PartialProxiedAccount, ProxiedAccount, ProxyAccount, - SigningType, Wallet, - WalletType, NotificationType, } from '@shared/core'; +import { AccountType, ChainType, CryptoType, SigningType, WalletType, NotificationType } from '@shared/core'; import { isDisabled, networkModel } from '@entities/network'; import { accountUtils, walletModel } from '@entities/wallet'; import { proxyModel, proxyUtils } from '@entities/proxy'; @@ -82,7 +78,7 @@ const disconnectFx = createEffect(async (chainId: ChainId) => { const createProxiedWalletsFx = createEffect( (proxiedAccounts: PartialProxiedAccount[]): { wallet: Wallet; accounts: ProxiedAccount[] }[] => { return proxiedAccounts.map((proxied) => { - const walletName = proxyUtils.getProxiedName(proxied); + const walletName = proxyUtils.getProxiedName(proxied.accountId, proxied.proxyType); const wallet = { name: walletName, type: WalletType.PROXIED, @@ -100,10 +96,7 @@ const createProxiedWalletsFx = createEffect( } as ProxiedAccount, ]; - return { - wallet, - accounts, - }; + return { wallet, accounts }; }); }, ); diff --git a/src/renderer/features/proxies/workers/proxy-worker.ts b/src/renderer/features/proxies/workers/proxy-worker.ts index 91973d8436..db012b7a9d 100644 --- a/src/renderer/features/proxies/workers/proxy-worker.ts +++ b/src/renderer/features/proxies/workers/proxy-worker.ts @@ -175,6 +175,7 @@ async function getProxies( ep.proxyType === p.proxyType, ), ); + console.log('XXX ', proxiesToAdd, chainId); return { proxiesToAdd, diff --git a/src/renderer/shared/core/types/notification.ts b/src/renderer/shared/core/types/notification.ts index 8722a78c29..0ddfd3ea1c 100644 --- a/src/renderer/shared/core/types/notification.ts +++ b/src/renderer/shared/core/types/notification.ts @@ -42,9 +42,9 @@ export type ProxyAction = BaseNotification & { proxyType: ProxyType; proxyAccountId: AccountId; proxyWalletName: string; + proxyWalletType: WalletType; proxiedAccountId: AccountId; - proxiedWalletName: string; - proxiedWalletType: WalletType; + proxiedWalletName: AccountId; }; export type Notification = MultisigInvite | MultisigOperation | ProxyAction; From 0ddb7c6cff5c55c57bd626d2c340d0859d49b681 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Fri, 12 Jan 2024 16:09:11 +0300 Subject: [PATCH 12/12] feat: proxied name in notification --- .../__tests__/notification-model.test.ts | 14 +++++++----- .../entities/wallet/model/wallet-model.ts | 2 +- src/renderer/features/proxies/lib/utils.ts | 22 +++++++++---------- .../features/proxies/model/proxies-model.ts | 14 +++++------- .../features/proxies/workers/proxy-worker.ts | 1 - .../shared/core/types/notification.ts | 2 +- 6 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/renderer/entities/notification/model/__tests__/notification-model.test.ts b/src/renderer/entities/notification/model/__tests__/notification-model.test.ts index 94cd61e425..aa6c1e14ac 100644 --- a/src/renderer/entities/notification/model/__tests__/notification-model.test.ts +++ b/src/renderer/entities/notification/model/__tests__/notification-model.test.ts @@ -13,12 +13,14 @@ const notifications = [ }, ] as Notification[]; -const newNotifications = [{ - id: 2, - read: true, - dateCreated: Date.now(), - type: NotificationType.MULTISIG_INVITE, -}] as Notification[]; +const newNotifications = [ + { + id: 2, + read: true, + dateCreated: Date.now(), + type: NotificationType.MULTISIG_INVITE, + }, +] as Notification[]; describe('entities/notification/model/notification-model', () => { afterEach(() => { diff --git a/src/renderer/entities/wallet/model/wallet-model.ts b/src/renderer/entities/wallet/model/wallet-model.ts index 194ed78c40..869dea8f8e 100644 --- a/src/renderer/entities/wallet/model/wallet-model.ts +++ b/src/renderer/entities/wallet/model/wallet-model.ts @@ -122,7 +122,7 @@ const multishardCreatedFx = createEffect( // TODO: Move wallet creation to it's own feature const proxiedWalletsCreatedFx = createEffect( (proxiedWallets: CreateParams[]): Promise<(CreateResult | undefined)[]> => { - return Promise.all(proxiedWallets.map((p) => walletCreatedFx(p))); + return Promise.all(proxiedWallets.map(walletCreatedFx)); }, ); diff --git a/src/renderer/features/proxies/lib/utils.ts b/src/renderer/features/proxies/lib/utils.ts index b0b2d464a3..2167d64262 100644 --- a/src/renderer/features/proxies/lib/utils.ts +++ b/src/renderer/features/proxies/lib/utils.ts @@ -1,5 +1,5 @@ -import { NotificationType, Chain } from '@shared/core'; -import type { ProxyAccount, Account, ProxyAction, NoID, Wallet } from '@shared/core'; +import type { Account, ProxyAction, NoID, Wallet } from '@shared/core'; +import { NotificationType, Chain, type PartialProxiedAccount } from '@shared/core'; import { dictionary } from '@shared/lib/utils'; import { proxyUtils } from '@entities/proxy'; @@ -13,7 +13,7 @@ function isRegularProxy(chain: Chain): boolean { } function getNotification( - proxies: ProxyAccount[], + proxiedAccounts: PartialProxiedAccount[], wallets: Wallet[], accounts: Account[], type: NotificationType, @@ -21,15 +21,15 @@ function getNotification( const walletsMap = dictionary(wallets, 'id', ({ name, type }) => ({ name, type })); const accountsWalletsMap = dictionary(accounts, 'accountId', (account) => walletsMap[account.walletId]); - return proxies.map((proxy) => ({ - chainId: proxy.chainId, + return proxiedAccounts.map((proxied) => ({ + chainId: proxied.chainId, dateCreated: Date.now(), - proxyType: proxy.proxyType, - proxyAccountId: proxy.accountId, - proxyWalletName: accountsWalletsMap[proxy.accountId].name, - proxyWalletType: accountsWalletsMap[proxy.accountId].type, - proxiedAccountId: proxy.proxiedAccountId, - proxiedWalletName: proxyUtils.getProxiedName(proxy.accountId, proxy.proxyType), + proxyType: proxied.proxyType, + proxyAccountId: proxied.proxyAccountId, + proxyWalletName: accountsWalletsMap[proxied.proxyAccountId].name, + proxyWalletType: accountsWalletsMap[proxied.proxyAccountId].type, + proxiedAccountId: proxied.accountId, + proxiedWalletName: proxyUtils.getProxiedName(proxied.accountId, proxied.proxyType), read: false, type, })); diff --git a/src/renderer/features/proxies/model/proxies-model.ts b/src/renderer/features/proxies/model/proxies-model.ts index 33fa0adc64..8f33ae227c 100644 --- a/src/renderer/features/proxies/model/proxies-model.ts +++ b/src/renderer/features/proxies/model/proxies-model.ts @@ -167,10 +167,9 @@ sample({ wallets: walletModel.$wallets, accounts: walletModel.$accounts, }, - filter: (_, proxies) => proxies.proxiesToAdd.length > 0, - fn: ({ wallets, accounts }, proxies) => { - return proxiesUtils.getNotification(proxies.proxiesToAdd, wallets, accounts, NotificationType.PROXY_CREATED); - }, + filter: (_, data) => data.proxiedAccountsToAdd.length > 0, + fn: ({ wallets, accounts }, data) => + proxiesUtils.getNotification(data.proxiedAccountsToAdd, wallets, accounts, NotificationType.PROXY_CREATED), target: notificationModel.events.notificationsAdded, }); @@ -180,10 +179,9 @@ sample({ wallets: walletModel.$wallets, accounts: walletModel.$accounts, }, - filter: (_, proxies) => proxies.proxiesToRemove.length > 0, - fn: ({ wallets, accounts }, proxies) => { - return proxiesUtils.getNotification(proxies.proxiesToRemove, wallets, accounts, NotificationType.PROXY_REMOVED); - }, + filter: (_, data) => data.proxiedAccountsToRemove.length > 0, + fn: ({ wallets, accounts }, data) => + proxiesUtils.getNotification(data.proxiedAccountsToRemove, wallets, accounts, NotificationType.PROXY_REMOVED), target: notificationModel.events.notificationsAdded, }); diff --git a/src/renderer/features/proxies/workers/proxy-worker.ts b/src/renderer/features/proxies/workers/proxy-worker.ts index db012b7a9d..91973d8436 100644 --- a/src/renderer/features/proxies/workers/proxy-worker.ts +++ b/src/renderer/features/proxies/workers/proxy-worker.ts @@ -175,7 +175,6 @@ async function getProxies( ep.proxyType === p.proxyType, ), ); - console.log('XXX ', proxiesToAdd, chainId); return { proxiesToAdd, diff --git a/src/renderer/shared/core/types/notification.ts b/src/renderer/shared/core/types/notification.ts index 0ddfd3ea1c..25ec0bd8c7 100644 --- a/src/renderer/shared/core/types/notification.ts +++ b/src/renderer/shared/core/types/notification.ts @@ -44,7 +44,7 @@ export type ProxyAction = BaseNotification & { proxyWalletName: string; proxyWalletType: WalletType; proxiedAccountId: AccountId; - proxiedWalletName: AccountId; + proxiedWalletName: string; }; export type Notification = MultisigInvite | MultisigOperation | ProxyAction;