Skip to content

Commit b190fc1

Browse files
feat: add real shared worker (#1684)
Co-authored-by: Nick <[email protected]>
1 parent 89a5609 commit b190fc1

File tree

14 files changed

+494
-144
lines changed

14 files changed

+494
-144
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ _book
2828
docs/html
2929
.idea
3030
.vscode
31+
.env.sentry-build-plugin

packages/extension-polkagate/src/components/contexts.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const ToastContext = React.createContext<({ show: (message: string) => void })>(
2727
const UserAddedChainContext = React.createContext<UserAddedChains>({});
2828
const GenesisHashOptionsContext = React.createContext<DropdownOption[]>([]);
2929
const AccountIconThemeContext = React.createContext<AccountIconThemeContextType>({ accountIconTheme: undefined, setAccountIconTheme: noop });
30-
const WorkerContext = React.createContext<Worker | undefined>(undefined);
30+
const WorkerContext = React.createContext<MessagePort | undefined>(undefined);
3131

3232
export { AccountContext,
3333
AccountIconThemeContext,

packages/extension-polkagate/src/hooks/useAssetsBalances.ts

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ const FUNCTIONS = ['getAssetOnRelayChain', 'getAssetOnAssetHub', 'getAssetOnMult
133133
* @param addresses a list of users accounts' addresses
134134
* @returns a list of assets balances on different selected chains and a fetching timestamp
135135
*/
136-
export default function useAssetsBalances (accounts: AccountJson[] | null, setAlerts: Dispatch<SetStateAction<AlertType[]>>, genesisOptions: DropdownOption[], userAddedEndpoints: UserAddedChains, worker?: Worker): SavedAssets | undefined | null {
136+
export default function useAssetsBalances (accounts: AccountJson[] | null, setAlerts: Dispatch<SetStateAction<AlertType[]>>, genesisOptions: DropdownOption[], userAddedEndpoints: UserAddedChains, worker?: MessagePort): SavedAssets | undefined | null {
137137
const { t } = useTranslation();
138138

139139
const isTestnetEnabled = useIsTestnetEnabled();
@@ -282,22 +282,33 @@ export default function useAssetsBalances (accounts: AccountJson[] | null, setAl
282282
}
283283

284284
setFetchedAssets((fetchedAssets) => {
285-
const combinedAsset = fetchedAssets || DEFAULT_SAVED_ASSETS;
285+
// Create a new object reference each time
286+
const combinedAsset = {
287+
...(fetchedAssets || DEFAULT_SAVED_ASSETS),
288+
balances: {
289+
...(fetchedAssets?.balances || DEFAULT_SAVED_ASSETS.balances)
290+
}
291+
};
286292

287293
Object.keys(assets).forEach((address) => {
288-
if (combinedAsset.balances[address] === undefined) {
294+
if (!combinedAsset.balances[address]) {
289295
combinedAsset.balances[address] = {};
290296
}
291297

292-
/** to group assets by their chain's genesisHash */
293298
const { genesisHash } = assets[address][0];
294299

295-
combinedAsset.balances[address][genesisHash] = assets[address];
300+
// Create a new reference for this specific balances entry
301+
combinedAsset.balances[address] = {
302+
...(combinedAsset.balances[address] || {}),
303+
[genesisHash]: assets[address]
304+
};
296305
});
297306

298-
combinedAsset.timeStamp = Date.now();
299-
300-
return combinedAsset;
307+
// Ensure a new timestamp and object reference
308+
return {
309+
...combinedAsset,
310+
timeStamp: Date.now()
311+
};
301312
});
302313
}, [addresses]);
303314

@@ -306,8 +317,8 @@ export default function useAssetsBalances (accounts: AccountJson[] | null, setAl
306317
return;
307318
}
308319

309-
worker.onmessage = (e: MessageEvent<string>) => {
310-
const message = e.data;
320+
const handleMessage = (messageEvent: MessageEvent<string>) => {
321+
const message = messageEvent.data;
311322

312323
if (!message) {
313324
return; // may receive unknown messages!
@@ -372,6 +383,12 @@ export default function useAssetsBalances (accounts: AccountJson[] | null, setAl
372383

373384
combineAndSetAssets(_assets);
374385
};
386+
387+
worker.addEventListener('message', handleMessage);
388+
389+
return () => {
390+
worker.removeEventListener('message', handleMessage);
391+
};
375392
}, [combineAndSetAssets, handleRequestCount, worker]);
376393

377394
const fetchAssetOnRelayChain = useCallback((_addresses: string[], chainName: string) => {
@@ -382,13 +399,7 @@ export default function useAssetsBalances (accounts: AccountJson[] | null, setAl
382399
const functionName = 'getAssetOnRelayChain';
383400

384401
worker.postMessage({ functionName, parameters: { address: _addresses, chainName, userAddedEndpoints } });
385-
386-
worker.onerror = (err) => {
387-
console.log(err);
388-
};
389-
390-
handleWorkerMessages();
391-
}, [handleWorkerMessages, userAddedEndpoints, worker]);
402+
}, [userAddedEndpoints, worker]);
392403

393404
const fetchAssetOnAssetHubs = useCallback((_addresses: string[], chainName: string, assetsToBeFetched?: Asset[]) => {
394405
if (!worker) {
@@ -398,10 +409,6 @@ export default function useAssetsBalances (accounts: AccountJson[] | null, setAl
398409
const functionName = 'getAssetOnAssetHub';
399410

400411
worker.postMessage({ functionName, parameters: { address: _addresses, assetsToBeFetched, chainName, userAddedEndpoints } });
401-
402-
worker.onerror = (err) => {
403-
console.log(err);
404-
};
405412
}, [userAddedEndpoints, worker]);
406413

407414
const fetchAssetOnMultiAssetChain = useCallback((addresses: string[], chainName: string) => {
@@ -412,13 +419,7 @@ export default function useAssetsBalances (accounts: AccountJson[] | null, setAl
412419
const functionName = 'getAssetOnMultiAssetChain';
413420

414421
worker.postMessage({ functionName, parameters: { addresses, chainName, userAddedEndpoints } });
415-
416-
worker.onerror = (err) => {
417-
console.log(err);
418-
};
419-
420-
handleWorkerMessages();
421-
}, [handleWorkerMessages, userAddedEndpoints, worker]);
422+
}, [userAddedEndpoints, worker]);
422423

423424
const fetchMultiAssetChainAssets = useCallback((chainName: string) => {
424425
return addresses && fetchAssetOnMultiAssetChain(addresses, chainName);
@@ -480,14 +481,16 @@ export default function useAssetsBalances (accounts: AccountJson[] | null, setAl
480481
!multipleAssetsChainsNames.includes(toCamelCase(text) || '')
481482
);
482483

484+
handleWorkerMessages();
485+
483486
/** Fetch assets for all the selected chains by default */
484487
_selectedChains?.forEach((genesisHash) => {
485488
const isSingleTokenChain = !!singleAssetChains.find(({ value }) => value === genesisHash);
486489
const maybeMultiAssetChainName = multipleAssetsChainsNames.find((chainName) => chainName === getChainName(genesisHash));
487490

488491
fetchAssets(genesisHash, isSingleTokenChain, maybeMultiAssetChainName);
489492
});
490-
}, [FETCH_PATHS, addresses, fetchAssets, worker, isTestnetEnabled, isUpdate, selectedChains, genesisOptions]);
493+
}, [FETCH_PATHS, addresses, fetchAssets, worker, isTestnetEnabled, isUpdate, selectedChains, genesisOptions, handleWorkerMessages]);
491494

492495
return fetchedAssets;
493496
}

packages/extension-polkagate/src/hooks/useNFT.tsx

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,27 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import type { AccountJson } from '@polkadot/extension-base/background/types';
5-
import type { NftItemsType} from '../util/types';
5+
import type { NftItemsType } from '../util/types';
66

77
import { useCallback, useEffect, useState } from 'react';
88

99
import NftManager from '../class/nftManager';
1010
import { useTranslation } from '../components/translate';
1111
import useAlerts from './useAlerts';
12+
import { useWorker } from './useWorker';
13+
14+
export interface NftItemsWorker {
15+
functionName: string;
16+
results: NftItemsType;
17+
}
1218

1319
const nftManager = new NftManager();
20+
const NFT_FUNCTION_NAME = 'getNFTs';
1421

1522
export default function useNFT (accountsFromContext: AccountJson[] | null) {
1623
const { t } = useTranslation();
1724
const { notify } = useAlerts();
25+
const worker = useWorker();
1826

1927
const [fetching, setFetching] = useState<boolean>(false);
2028

@@ -27,18 +35,10 @@ export default function useNFT (accountsFromContext: AccountJson[] | null) {
2735

2836
const fetchNFTs = useCallback((addresses: string[]) => {
2937
setFetching(true);
30-
const getNFTsWorker: Worker = new Worker(new URL('../util/workers/getNFTs.js', import.meta.url));
31-
32-
getNFTsWorker.postMessage({ addresses });
38+
worker.postMessage({ functionName: NFT_FUNCTION_NAME, parameters: { addresses } });
3339

34-
getNFTsWorker.onerror = (err) => {
35-
console.error('Worker error:', err);
36-
setFetching(false);
37-
getNFTsWorker.terminate();
38-
};
39-
40-
getNFTsWorker.onmessage = (e: MessageEvent<string>) => {
41-
const NFTs = e.data;
40+
const handleMessage = (messageEvent: MessageEvent<string>) => {
41+
const NFTs = messageEvent.data;
4242

4343
if (!NFTs) {
4444
notify(t('Unable to fetch NFT/Unique items!'), 'info');
@@ -47,27 +47,35 @@ export default function useNFT (accountsFromContext: AccountJson[] | null) {
4747
return;
4848
}
4949

50-
let parsedNFTsInfo: NftItemsType;
50+
let parsedNFTsInfo: NftItemsWorker;
5151

5252
try {
53-
parsedNFTsInfo = JSON.parse(NFTs) as NftItemsType;
53+
parsedNFTsInfo = JSON.parse(NFTs) as NftItemsWorker;
54+
55+
// console.log('All fetched NFTs:', parsedNFTsInfo);
56+
57+
if (parsedNFTsInfo.functionName !== NFT_FUNCTION_NAME) {
58+
return;
59+
}
5460
} catch (error) {
5561
console.error('Failed to parse NFTs JSON:', error);
5662
// setFetching(false);
57-
getNFTsWorker.terminate();
5863

5964
return;
6065
}
6166

62-
// console.log('All fetched NFTs:', parsedNFTsInfo);
63-
6467
// Save all fetched items to Chrome storage
65-
saveToStorage(parsedNFTsInfo);
68+
saveToStorage(parsedNFTsInfo.results);
6669

6770
// setFetching(false);
68-
getNFTsWorker.terminate();
6971
};
70-
}, [notify, saveToStorage, t]);
72+
73+
worker.addEventListener('message', handleMessage);
74+
75+
return () => {
76+
worker.removeEventListener('message', handleMessage);
77+
};
78+
}, [notify, saveToStorage, t, worker]);
7179

7280
useEffect(() => {
7381
if (!fetching && addresses && addresses.length > 0 && onWhitelistedPath) {

packages/extension-polkagate/src/util/workers/shared-helpers/getAssetOnAssetHub.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,22 @@
44
import { closeWebsockets, fastestEndpoint, getChainEndpoints, metadataFromApi, toGetNativeToken } from '../utils';
55
import { getAssets } from './getAssets.js';
66

7-
// @ts-ignore
8-
9-
export async function getAssetOnAssetHub (addresses, assetsToBeFetched, chainName, userAddedEndpoints) {
7+
/**
8+
*
9+
* @param {string[]} addresses
10+
* @param {import('@polkagate/apps-config/assets/types').Asset[]} assetsToBeFetched
11+
* @param {string} chainName
12+
* @param {import('../../types').UserAddedChains} userAddedEndpoints
13+
* @param {MessagePort} port
14+
*/
15+
export async function getAssetOnAssetHub (addresses, assetsToBeFetched, chainName, userAddedEndpoints, port) {
1016
const endpoints = getChainEndpoints(chainName, userAddedEndpoints);
1117
const { api, connections } = await fastestEndpoint(endpoints);
1218

1319
const { metadata } = metadataFromApi(api);
1420

15-
postMessage(JSON.stringify({ functionName: 'getAssetOnAssetHub', metadata }));
21+
console.info('Shared worker, metadata fetched and sent for chain:', chainName);
22+
port.postMessage(JSON.stringify({ functionName: 'getAssetOnAssetHub', metadata }));
1623

1724
const results = await toGetNativeToken(addresses, api, chainName);
1825

@@ -32,6 +39,7 @@ export async function getAssetOnAssetHub (addresses, assetsToBeFetched, chainNam
3239

3340
await getAssets(addresses, api, nonNativeAssets, chainName, results);
3441

35-
postMessage(JSON.stringify({ functionName: 'getAssetOnAssetHub', results }));
42+
console.info('Shared worker, account assets fetched and send on chain:', chainName);
43+
port.postMessage(JSON.stringify({ functionName: 'getAssetOnAssetHub', results }));
3644
closeWebsockets(connections);
3745
}

packages/extension-polkagate/src/util/workers/shared-helpers/getAssetOnMultiAssetChain.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,50 @@
11
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
22
// SPDX-License-Identifier: Apache-2.0
33

4-
// @ts-nocheck
5-
64
import { getSubstrateAddress } from '../../utils';
75
// eslint-disable-next-line import/extensions
86
import { balancifyAsset, closeWebsockets, fastestEndpoint, getChainEndpoints, metadataFromApi, toGetNativeToken } from '../utils';
97

10-
export async function getAssetOnMultiAssetChain (assetsToBeFetched, addresses, chainName, userAddedEndpoints) {
8+
/**
9+
*
10+
* @param {import('@polkagate/apps-config/assets/types').Asset[]} assetsToBeFetched
11+
* @param {string[]} addresses
12+
* @param {string} chainName
13+
* @param {import('../../types').UserAddedChains} userAddedEndpoints
14+
* @param {MessagePort} port
15+
*/
16+
export async function getAssetOnMultiAssetChain (assetsToBeFetched, addresses, chainName, userAddedEndpoints, port) {
1117
const endpoints = getChainEndpoints(chainName, userAddedEndpoints);
1218
const { api, connections } = await fastestEndpoint(endpoints);
1319

1420
const { metadata } = metadataFromApi(api);
1521

16-
postMessage(JSON.stringify({ functionName: 'getAssetOnMultiAssetChain', metadata }));
22+
console.info('Shared worker, metadata fetched and sent for chain:', chainName);
23+
port.postMessage(JSON.stringify({ functionName: 'getAssetOnMultiAssetChain', metadata }));
1724

1825
const results = await toGetNativeToken(addresses, api, chainName);
1926

20-
const maybeTheAssetOfAddresses = addresses.map((address) => api.query.tokens.accounts.entries(address));
27+
const maybeTheAssetOfAddresses = addresses.map((address) => api.query['tokens']['accounts'].entries(address));
2128
const balanceOfAssetsOfAddresses = await Promise.all(maybeTheAssetOfAddresses);
2229

2330
balanceOfAssetsOfAddresses.flat().forEach((entry) => {
2431
if (!entry.length) {
2532
return;
2633
}
2734

35+
// @ts-ignore
2836
const formatted = entry[0].toHuman()[0];
2937
const storageKey = entry[0].toString();
3038

39+
// @ts-ignore
3140
const foundAsset = assetsToBeFetched.find((_asset) => {
32-
const currencyId = _asset?.extras?.currencyIdScale.replace('0x', '');
41+
const currencyId = _asset?.extras?.['currencyIdScale'].replace('0x', '');
3342

3443
return currencyId && storageKey.endsWith(currencyId);
3544
});
3645

3746
const balance = entry[1];
47+
// @ts-ignore
3848
const totalBalance = balance.free.add(balance.reserved);
3949

4050
if (foundAsset) {
@@ -52,12 +62,14 @@ export async function getAssetOnMultiAssetChain (assetsToBeFetched, addresses, c
5262

5363
const address = getSubstrateAddress(formatted);
5464

65+
// @ts-ignore
5566
results[address]?.push(asset) ?? (results[address] = [asset]);
5667
} else {
5768
console.info(`NOTE: There is an asset on ${chainName} for ${formatted} which is not whitelisted. assetInfo`, storageKey, balance?.toHuman());
5869
}
5970
});
6071

61-
postMessage(JSON.stringify({ functionName: 'getAssetOnMultiAssetChain', results }));
72+
console.info('Shared worker, account assets fetched and send on chain:', chainName);
73+
port.postMessage(JSON.stringify({ functionName: 'getAssetOnMultiAssetChain', results }));
6274
closeWebsockets(connections);
6375
}

0 commit comments

Comments
 (0)