From f7df940f6f7ef77bda8100d93f7475e74981f704 Mon Sep 17 00:00:00 2001 From: Aleksandr Makhnev Date: Thu, 21 Nov 2024 12:46:17 +0500 Subject: [PATCH 1/4] fix: update signatory name if edited (#2692) --- .../entities/contact/model/contact-model.ts | 18 ++++++++++- src/renderer/shared/i18n/locales/en.json | 2 ++ .../widgets/CreateWallet/model/flow-model.ts | 31 ++++++++++++++++++- .../CreateWallet/model/signatory-model.ts | 5 +++ .../SelectSignatoriesThreshold.tsx | 10 ++++++ 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/renderer/entities/contact/model/contact-model.ts b/src/renderer/entities/contact/model/contact-model.ts index cc82c3d9f..f04ab140b 100644 --- a/src/renderer/entities/contact/model/contact-model.ts +++ b/src/renderer/entities/contact/model/contact-model.ts @@ -2,7 +2,7 @@ import { createEffect, createStore, sample } from 'effector'; import { storageService } from '@/shared/api/storage'; import { type Contact, kernelModel } from '@/shared/core'; -import { splice } from '@/shared/lib/utils'; +import { merge, splice } from '@/shared/lib/utils'; const $contacts = createStore([]); @@ -24,6 +24,14 @@ const updateContactFx = createEffect(async ({ id, ...rest }: Contact): Promise => { + if (contacts.length === 0) return []; + + await storageService.contacts.updateAll(contacts); + + return contacts; +}); + const deleteContactFx = createEffect(async (contactId: number): Promise => { await storageService.contacts.delete(contactId); @@ -44,6 +52,13 @@ $contacts const position = state.findIndex((s) => s.id === contact.id); return splice(state, contact, position); + }) + .on(updateContactsFx.doneData, (state, contacts) => { + return merge({ + a: state, + b: contacts, + mergeBy: (c) => c.id, + }); }); sample({ @@ -58,5 +73,6 @@ export const contactModel = { createContactsFx, deleteContactFx, updateContactFx, + updateContactsFx, }, }; diff --git a/src/renderer/shared/i18n/locales/en.json b/src/renderer/shared/i18n/locales/en.json index d1815b02d..437811e45 100644 --- a/src/renderer/shared/i18n/locales/en.json +++ b/src/renderer/shared/i18n/locales/en.json @@ -168,7 +168,9 @@ "notEnoughSignatories": "You need to select at least 2 signatories", "notEnoughSignatoriesTitle": "Not enough signatories", "notEmptySignatoryTitle": "Empty signatory", + "notEmptySignatoryNameTitle": "Empty signatory name", "notEmptySignatory": "No empty signatory allowed", + "notEmptySignatoryName": "No empty signatory name allowed", "ownAccountSelection": "Address", "restoreButton": "Restore", "searchContactPlaceholder": "Search", diff --git a/src/renderer/widgets/CreateWallet/model/flow-model.ts b/src/renderer/widgets/CreateWallet/model/flow-model.ts index 32b3cc5b9..be8c24508 100644 --- a/src/renderer/widgets/CreateWallet/model/flow-model.ts +++ b/src/renderer/widgets/CreateWallet/model/flow-model.ts @@ -410,9 +410,38 @@ sample({ contacts: contactModel.$contacts, }, fn: ({ signatories, contacts }) => { + const signatoriesWithoutSigner = signatories.slice(1); + const contactMap = new Map(contacts.map((c) => [c.accountId, c])); + const updatedContacts: Contact[] = []; + + for (const { address, name } of signatoriesWithoutSigner) { + const contact = contactMap.get(toAccountId(address)); + + if (!contact) continue; + + updatedContacts.push({ + ...contact, + name, + }); + } + + return updatedContacts; + }, + target: contactModel.effects.updateContactsFx, +}); + +sample({ + clock: signModel.output.formSubmitted, + source: { + signatories: signatoryModel.$signatories, + contacts: contactModel.$contacts, + }, + fn: ({ signatories, contacts }) => { + const contactsSet = new Set(contacts.map((c) => c.accountId)); + return signatories .slice(1) - .filter((signatory) => !contacts.some((contact) => contact.accountId === toAccountId(signatory.address))) + .filter((signatory) => !contactsSet.has(toAccountId(signatory.address))) .map( ({ address, name }) => ({ diff --git a/src/renderer/widgets/CreateWallet/model/signatory-model.ts b/src/renderer/widgets/CreateWallet/model/signatory-model.ts index bf0e1a7fb..8abd14a81 100644 --- a/src/renderer/widgets/CreateWallet/model/signatory-model.ts +++ b/src/renderer/widgets/CreateWallet/model/signatory-model.ts @@ -36,6 +36,10 @@ const $hasEmptySignatories = combine($signatories, (signatories) => { return signatories.map(({ address }) => address).includes(''); }); +const $hasEmptySignatoryName = combine($signatories, (signatories) => { + return signatories.map(({ name }) => name).includes(''); +}); + const $ownedSignatoriesWallets = combine( { wallets: walletModel.$wallets, signatories: $signatories }, ({ wallets, signatories }) => @@ -99,6 +103,7 @@ export const signatoryModel = { $ownedSignatoriesWallets, $hasDuplicateSignatories, $hasEmptySignatories, + $hasEmptySignatoryName, events: { addSignatory, changeSignatory, diff --git a/src/renderer/widgets/CreateWallet/ui/MultisigWallet/SelectSignatoriesThreshold.tsx b/src/renderer/widgets/CreateWallet/ui/MultisigWallet/SelectSignatoriesThreshold.tsx index 93251b26e..f319b6c57 100644 --- a/src/renderer/widgets/CreateWallet/ui/MultisigWallet/SelectSignatoriesThreshold.tsx +++ b/src/renderer/widgets/CreateWallet/ui/MultisigWallet/SelectSignatoriesThreshold.tsx @@ -34,6 +34,7 @@ export const SelectSignatoriesThreshold = () => { const ownedSignatoriesWallets = useUnit(signatoryModel.$ownedSignatoriesWallets); const hasDuplicateSignatories = useUnit(signatoryModel.$hasDuplicateSignatories); const hasEmptySignatories = useUnit(signatoryModel.$hasEmptySignatories); + const hasEmptySignatoryName = useUnit(signatoryModel.$hasEmptySignatoryName); const [hasClickedNext, setHasClickedNext] = useState(false); @@ -48,6 +49,7 @@ export const SelectSignatoriesThreshold = () => { hasEnoughSignatories && !multisigAlreadyExists && !hasEmptySignatories && + !hasEmptySignatoryName && isThresholdValid && !hasDuplicateSignatories; @@ -102,6 +104,14 @@ export const SelectSignatoriesThreshold = () => { > {t('createMultisigAccount.notEmptySignatory')} + + + {t('createMultisigAccount.notEmptySignatoryName')} +
From af4b908ccf42f403c503597de7671fa1917fd564 Mon Sep 17 00:00:00 2001 From: Yaroslav Grachev Date: Thu, 21 Nov 2024 10:48:37 +0300 Subject: [PATCH 2/4] Fix: Assets page for multisig (#2676) --- .../AssetsChainView/ui/AssetsChainView.tsx | 15 ++-- .../model/portfolio-model.ts | 70 +++++++++++-------- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/renderer/features/assets/AssetsChainView/ui/AssetsChainView.tsx b/src/renderer/features/assets/AssetsChainView/ui/AssetsChainView.tsx index 391f2b4c9..78ab72636 100644 --- a/src/renderer/features/assets/AssetsChainView/ui/AssetsChainView.tsx +++ b/src/renderer/features/assets/AssetsChainView/ui/AssetsChainView.tsx @@ -3,7 +3,7 @@ import { useEffect, useState } from 'react'; import { chainsService } from '@/shared/api/network'; import { type Account, type Chain } from '@/shared/core'; -import { isStringsMatchQuery } from '@/shared/lib/utils'; +import { isStringsMatchQuery, nullable } from '@/shared/lib/utils'; import { AssetsListView, EmptyAssetsState } from '@/entities/asset'; import { balanceModel } from '@/entities/balance'; import { networkModel, networkUtils } from '@/entities/network'; @@ -34,11 +34,11 @@ export const AssetsChainView = ({ query, activeShards, hideZeroBalances, assetsV if (!activeWallet || assetsView !== AssetsListView.CHAIN_CENTRIC || !activeShards.length) return; const isMultisig = walletUtils.isMultisig(activeWallet); + const multisigChainToInclude = isMultisig ? activeWallet.accounts[0].chainId : undefined; const availableChains = Object.values(chains).filter((chain) => { return activeWallet.accounts.some((account) => { return ( - activeWallet && accountUtils.isNonBaseVaultAccount(account, activeWallet) && accountUtils.isChainAndCryptoMatch(account, chain) ); @@ -46,14 +46,13 @@ export const AssetsChainView = ({ query, activeShards, hideZeroBalances, assetsV }); const filteredChains = availableChains.filter((c) => { - if (!connections[c.chainId]) { - return false; - } + const connection = connections[c.chainId]; - const isDisabled = networkUtils.isDisabledConnection(connections[c.chainId]); - const hasMultiPallet = !isMultisig || networkUtils.isMultisigSupported(c.options); + if (nullable(connection)) return false; + if (networkUtils.isDisabledConnection(connection)) return false; + if (!isMultisig) return true; - return !isDisabled && hasMultiPallet; + return networkUtils.isMultisigSupported(c.options) || multisigChainToInclude === c.chainId; }); const sortedChains = chainsService.sortChainsByBalance( diff --git a/src/renderer/features/assets/AssetsPortfolioView/model/portfolio-model.ts b/src/renderer/features/assets/AssetsPortfolioView/model/portfolio-model.ts index 0609c0291..f65df6409 100644 --- a/src/renderer/features/assets/AssetsPortfolioView/model/portfolio-model.ts +++ b/src/renderer/features/assets/AssetsPortfolioView/model/portfolio-model.ts @@ -2,7 +2,7 @@ import { createEffect, createEvent, createStore, restore, sample } from 'effecto import { once } from 'patronum'; import { type Account, type AssetByChains, type Balance, type Chain, type ChainId, type Wallet } from '@/shared/core'; -import { includes } from '@/shared/lib/utils'; +import { includes, nullable } from '@/shared/lib/utils'; import { AssetsListView } from '@/entities/asset'; import { balanceModel } from '@/entities/balance'; import { networkModel, networkUtils } from '@/entities/network'; @@ -34,8 +34,9 @@ type UpdateTokenParams = { const getUpdatedTokensFx = createEffect(({ activeWallet, chains }: UpdateTokenParams): AssetByChains[] => { const tokens = tokensService.getTokensData(); + const updatedTokens: AssetByChains[] = []; - return tokens.reduce((acc, token) => { + for (const token of tokens) { const filteredChains = token.chains.filter((chain) => { return activeWallet?.accounts.some((account) => { return ( @@ -45,12 +46,12 @@ const getUpdatedTokensFx = createEffect(({ activeWallet, chains }: UpdateTokenPa }); }); - if (filteredChains.length > 0) { - acc.push({ ...token, chains: filteredChains }); - } + if (filteredChains.length === 0) continue; + + updatedTokens.push({ ...token, chains: filteredChains }); + } - return acc; - }, [] as AssetByChains[]); + return updatedTokens; }); type PopulateBalanceParams = { @@ -61,15 +62,17 @@ type PopulateBalanceParams = { const populateTokensBalanceFx = createEffect( ({ activeTokens, balances, accounts }: PopulateBalanceParams): AssetByChains[] => { - return activeTokens.reduce((acc, token) => { + const tokens: AssetByChains[] = []; + + for (const token of activeTokens) { const [chainsWithBalance, totalBalance] = tokensService.getChainWithBalance(balances, token.chains, accounts); - if (chainsWithBalance.length > 0) { - acc.push({ ...token, chains: chainsWithBalance, totalBalance }); - } + if (chainsWithBalance.length === 0) continue; - return acc; - }, []); + tokens.push({ ...token, chains: chainsWithBalance, totalBalance }); + } + + return tokens; }, ); @@ -105,22 +108,29 @@ sample({ }, fn: ({ connections, chains, tokens, activeWallet }): AssetByChains[] => { const isMultisigWallet = walletUtils.isMultisig(activeWallet); + const hasAccounts = activeWallet!.accounts.length > 0; + const multisigChainToInclude = isMultisigWallet && hasAccounts ? activeWallet.accounts[0].chainId : undefined; + + const activeTokens: AssetByChains[] = []; - return tokens.reduce((acc, token) => { + for (const token of tokens) { const filteredChains = token.chains.filter((c) => { - if (!connections[c.chainId]) return false; - const isDisabled = networkUtils.isDisabledConnection(connections[c.chainId]); - const hasMultiPallet = networkUtils.isMultisigSupported(chains[c.chainId].options); + const connection = connections[c.chainId]; - return !isDisabled && (!isMultisigWallet || hasMultiPallet); + if (nullable(connection)) return false; + if (networkUtils.isDisabledConnection(connection)) return false; + if (nullable(chains[c.chainId])) return false; + if (!isMultisigWallet) return true; + + return networkUtils.isMultisigSupported(chains[c.chainId].options) || multisigChainToInclude === c.chainId; }); - if (filteredChains.length > 0) { - acc.push({ ...token, chains: filteredChains }); - } + if (filteredChains.length === 0) continue; + + activeTokens.push({ ...token, chains: filteredChains }); + } - return acc; - }, []); + return activeTokens; }, target: $activeTokens, }); @@ -148,7 +158,9 @@ sample({ clock: [$activeTokensWithBalance, queryChanged], source: { activeTokensWithBalance: $activeTokensWithBalance, query: $query }, fn: ({ activeTokensWithBalance, query }) => { - return activeTokensWithBalance.reduce((acc, token) => { + const filteredTokens: AssetByChains[] = []; + + for (const token of activeTokensWithBalance) { const filteredChains = token.chains.filter((chain) => { const hasSymbol = includes(chain.assetSymbol, query); const hasAssetName = includes(token.name, query); @@ -157,12 +169,12 @@ sample({ return hasSymbol || hasAssetName || hasChainName; }); - if (filteredChains.length > 0) { - acc.push({ ...token, chains: filteredChains }); - } + if (filteredChains.length === 0) continue; + + filteredTokens.push({ ...token, chains: filteredChains }); + } - return acc; - }, []); + return filteredTokens; }, target: $filteredTokens, }); From da289ce6a19a3e4a091adc07e692d23090e184af Mon Sep 17 00:00:00 2001 From: Sergey Zhuravlev Date: Thu, 21 Nov 2024 12:34:17 +0300 Subject: [PATCH 3/4] fix: wallet removing now leads to correct state (#2703) --- .../features/wallets/WalletSelect/lib/wallet-select-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/features/wallets/WalletSelect/lib/wallet-select-utils.ts b/src/renderer/features/wallets/WalletSelect/lib/wallet-select-utils.ts index d2c4efff5..a2939f5a6 100644 --- a/src/renderer/features/wallets/WalletSelect/lib/wallet-select-utils.ts +++ b/src/renderer/features/wallets/WalletSelect/lib/wallet-select-utils.ts @@ -31,7 +31,7 @@ const getWalletByGroups = (wallets: Wallet[], query = ''): Record { - return getWalletByGroups(wallets)[WalletType.POLKADOT_VAULT].at(0) ?? null; + return Object.values(getWalletByGroups(wallets)).flat().at(0) ?? null; }; export const walletSelectUtils = { From e6dd49971269fb099b1ee1d2dfab1f53ff8ebe34 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravlev Date: Thu, 21 Nov 2024 12:34:40 +0300 Subject: [PATCH 4/4] fix: restored code for closing in create multisig wallet model (#2704) --- src/renderer/widgets/CreateWallet/model/flow-model.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/renderer/widgets/CreateWallet/model/flow-model.ts b/src/renderer/widgets/CreateWallet/model/flow-model.ts index be8c24508..d5aaee220 100644 --- a/src/renderer/widgets/CreateWallet/model/flow-model.ts +++ b/src/renderer/widgets/CreateWallet/model/flow-model.ts @@ -264,6 +264,13 @@ sample({ target: $error, }); +sample({ + clock: walletModel.events.walletCreatedDone, + filter: ({ wallet, external }) => wallet.type === WalletType.MULTISIG && !external, + fn: ({ wallet }) => wallet.id, + target: walletProviderModel.events.completed, +}); + // Submit sample({