Skip to content

Commit

Permalink
refactor: separate all the context into a individual file (PolkaGate#…
Browse files Browse the repository at this point in the history
  • Loading branch information
AMIRKHANEF authored Nov 28, 2024
1 parent 47cd15a commit db877b9
Show file tree
Hide file tree
Showing 20 changed files with 608 additions and 300 deletions.
2 changes: 0 additions & 2 deletions packages/extension-polkagate/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
{ "path": "../extension-inject/tsconfig.build.json"
},
{ "path": "../extension-mocks/tsconfig.build.json"
},
{ "path": "../extension-ui/tsconfig.build.json"
}
]
}
51 changes: 51 additions & 0 deletions packages/extension-ui/src/Popup/contexts/AccountAssetProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { SavedAssets } from '@polkadot/extension-polkagate/hooks/useAssetsBalances';

import React, { useContext, useEffect, useState } from 'react';

import { AccountContext, AccountsAssetsContext, AlertContext, GenesisHashOptionsContext, UserAddedChainContext, WorkerContext } from '@polkadot/extension-polkagate/src/components/contexts';
import { setStorage } from '@polkadot/extension-polkagate/src/components/Loading';
import useAssetsBalances, { ASSETS_NAME_IN_STORAGE } from '@polkadot/extension-polkagate/src/hooks/useAssetsBalances';
import useNFT from '@polkadot/extension-polkagate/src/hooks/useNFT';

export default function AccountAssetProvider ({ children }: { children: React.ReactNode }) {
const { accounts } = useContext(AccountContext);
const genesisHashOptions = useContext(GenesisHashOptionsContext);
const { setAlerts } = useContext(AlertContext);
const userAddedChainCtx = useContext(UserAddedChainContext);
const worker = useContext(WorkerContext);

const [accountsAssets, setAccountsAssets] = useState<SavedAssets | null | undefined>();

const assetsOnChains = useAssetsBalances(accounts, setAlerts, genesisHashOptions, userAddedChainCtx, worker);

useNFT(accounts);

useEffect(() => {
assetsOnChains && setAccountsAssets({ ...assetsOnChains });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [assetsOnChains?.timeStamp]);

useEffect(() => {
/** remove forgotten accounts from assetChains if any */
if (accounts && assetsOnChains?.balances) {
Object.keys(assetsOnChains.balances).forEach((_address) => {
const found = accounts.find(({ address }) => address === _address);

if (!found) {
delete assetsOnChains.balances[_address];
setStorage(ASSETS_NAME_IN_STORAGE, assetsOnChains, true).catch(console.error);
}
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [accounts?.length, assetsOnChains?.timeStamp]);

return (
<AccountsAssetsContext.Provider value={{ accountsAssets, setAccountsAssets }}>
{children}
</AccountsAssetsContext.Provider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { IconTheme } from '@polkadot/react-identicon/types';

import React, { useEffect, useState } from 'react';

import { AccountIconThemeContext } from '@polkadot/extension-polkagate/src/components/contexts';
import { getStorage, watchStorage } from '@polkadot/extension-polkagate/src/components/Loading';
import { DEFAULT_ACCOUNT_ICON_THEME } from '@polkadot/extension-polkagate/src/util/constants';

export default function AccountIconThemeProvider ({ children }: { children: React.ReactNode }) {
const [accountIconTheme, setAccountIconTheme] = useState<IconTheme>(DEFAULT_ACCOUNT_ICON_THEME);

useEffect(() => {
getStorage('iconTheme')
.then((maybeTheme) => setAccountIconTheme((maybeTheme as IconTheme | undefined) || DEFAULT_ACCOUNT_ICON_THEME))
.catch(console.error);

const unsubscribe = watchStorage('iconTheme', setAccountIconTheme);

return () => {
unsubscribe();
};
}, []);

return (
<AccountIconThemeContext.Provider value={{ accountIconTheme, setAccountIconTheme }}>
{children}
</AccountIconThemeContext.Provider>
);
}
75 changes: 75 additions & 0 deletions packages/extension-ui/src/Popup/contexts/AccountProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { AccountJson, AccountsContext } from '@polkadot/extension-base/background/types';

import React, { useEffect, useState } from 'react';

import { canDerive } from '@polkadot/extension-base/utils';
import { AccountContext } from '@polkadot/extension-polkagate/src/components/contexts';
import { getStorage, type LoginInfo, updateStorage } from '@polkadot/extension-polkagate/src/components/Loading';
import { subscribeAccounts } from '@polkadot/extension-polkagate/src/messaging';
import { buildHierarchy } from '@polkadot/extension-polkagate/src/util/buildHierarchy';

function initAccountContext (accounts: AccountJson[]): AccountsContext {
const hierarchy = buildHierarchy(accounts);
const master = hierarchy.find(({ isExternal, type }) => !isExternal && canDerive(type));

return {
accounts,
hierarchy,
master
};
}

export default function AccountProvider ({ children }: { children: React.ReactNode }) {
const [accounts, setAccounts] = useState<null | AccountJson[]>(null);
const [accountCtx, setAccountCtx] = useState<AccountsContext>({ accounts: [], hierarchy: [] });
const [loginInfo, setLoginInfo] = useState<LoginInfo>();

useEffect(() => {
subscribeAccounts(setAccounts).catch(console.log);
}, []);

useEffect(() => {
const fetchLoginInfo = async () => {
chrome.storage.onChanged.addListener(function (changes, areaName) {
if (areaName === 'local' && 'loginInfo' in changes) {
const newValue = changes['loginInfo'].newValue as LoginInfo;

setLoginInfo(newValue);
}
});
const info = await getStorage('loginInfo') as LoginInfo;

setLoginInfo(info);
};

fetchLoginInfo().catch(console.error);
}, []);

useEffect(() => {
if (!loginInfo) {
return;
}

if (loginInfo.status !== 'forgot') {
setAccountCtx(initAccountContext(accounts || []));
} else if (loginInfo.status === 'forgot') {
setAccountCtx(initAccountContext([]));
const addresses = accounts?.map((account) => account.address);

updateStorage('loginInfo', { addressesToForget: addresses }).catch(console.error);
}
}, [accounts, loginInfo]);

if (!accounts) {
return null;
}

return (
<AccountContext.Provider value={accountCtx}>
{children}
</AccountContext.Provider>
);
}
20 changes: 20 additions & 0 deletions packages/extension-ui/src/Popup/contexts/ActionProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import React, { useCallback } from 'react';

import { ActionContext } from '@polkadot/extension-polkagate/src/components/contexts';

export default function ActionProvider ({ children }: { children: React.ReactNode }) {
const onAction = useCallback((to?: string): void => {
if (to) {
window.location.hash = to;
}
}, []);

return (
<ActionContext.Provider value={onAction}>
{children}
</ActionContext.Provider>
);
}
18 changes: 18 additions & 0 deletions packages/extension-ui/src/Popup/contexts/AlertProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { AlertType } from '@polkadot/extension-polkagate/util/types';

import React, { useState } from 'react';

import { AlertContext } from '@polkadot/extension-polkagate/src/components/contexts';

export default function AlertProvider ({ children }: { children: React.ReactNode }) {
const [alerts, setAlerts] = useState<AlertType[]>([]);

return (
<AlertContext.Provider value={{ alerts, setAlerts }}>
{children}
</AlertContext.Provider>
);
}
22 changes: 22 additions & 0 deletions packages/extension-ui/src/Popup/contexts/ApiProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { APIs } from '@polkadot/extension-polkagate/util/types';

import React, { useState } from 'react';

import { APIContext } from '@polkadot/extension-polkagate/src/components/contexts';

export default function ApiProvider ({ children }: { children: React.ReactNode }) {
const [apis, setApis] = useState<APIs>({});

const updateApis = React.useCallback((change: APIs) => {
setApis(change);
}, []);

return (
<APIContext.Provider value={{ apis, setIt: updateApis }}>
{children}
</APIContext.Provider>
);
}
61 changes: 61 additions & 0 deletions packages/extension-ui/src/Popup/contexts/CurrencyProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { CurrencyItemType } from '@polkadot/extension-polkagate/src/fullscreen/homeFullScreen/partials/Currency';
import type { Prices, PricesInCurrencies } from '@polkadot/extension-polkagate/src/util/types';

import React, { useEffect, useState } from 'react';

import { CurrencyContext } from '@polkadot/extension-polkagate/src/components/contexts';
import { getStorage, setStorage } from '@polkadot/extension-polkagate/src/components/Loading';
import usePriceIds from '@polkadot/extension-polkagate/src/hooks/usePriceIds';
import { isPriceUpToDate } from '@polkadot/extension-polkagate/src/hooks/usePrices';
import { getPrices } from '@polkadot/extension-polkagate/src/util/api';

interface CurrencyProviderProps {
children: React.ReactNode;
}

export default function CurrencyProvider ({ children }: CurrencyProviderProps) {
const priceIds = usePriceIds();

const [currency, setCurrency] = useState<CurrencyItemType>();
const isFetchingPricesRef = React.useRef(false);

useEffect(() => {
if (priceIds && currency?.code && !isFetchingPricesRef.current) {
isFetchingPricesRef.current = true;

getStorage('pricesInCurrencies')
.then((res) => {
const savedPricesInCurrencies = (res || {}) as PricesInCurrencies;
const maybeSavedPriceInCurrentCurrencyCode = savedPricesInCurrencies[currency.code];

if (maybeSavedPriceInCurrentCurrencyCode && isPriceUpToDate(maybeSavedPriceInCurrentCurrencyCode.date)) {
/** price in the selected currency is already updated hence no need to fetch again */
// TODO: FixMe: what if users change selected chainS during price validity period?
return;
}

getPrices(priceIds, currency.code.toLowerCase())
.then((newPrices) => {
delete (newPrices as Prices).currencyCode;
savedPricesInCurrencies[currency.code] = newPrices;
setStorage('pricesInCurrencies', savedPricesInCurrencies)
.catch(console.error);
})
.catch(console.error);
})
.catch(console.error)
.finally(() => {
isFetchingPricesRef.current = false;
});
}
}, [currency?.code, priceIds]);

return (
<CurrencyContext.Provider value={{ currency, setCurrency }}>
{children}
</CurrencyContext.Provider>
);
}
22 changes: 22 additions & 0 deletions packages/extension-ui/src/Popup/contexts/FetchingProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { Fetching } from '@polkadot/extension-polkagate/util/types';

import React, { useCallback, useState } from 'react';

import { FetchingContext } from '@polkadot/extension-polkagate/src/components/contexts';

export default function FetchingProvider ({ children }: { children: React.ReactNode }) {
const [fetching, setFetching] = useState<Fetching>({});

const set = useCallback((change: Fetching) => {
setFetching(change);
}, []);

return (
<FetchingContext.Provider value={{ fetching, set }}>
{children}
</FetchingContext.Provider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import React from 'react';

import { GenesisHashOptionsContext } from '@polkadot/extension-polkagate/src/components/contexts';
import useGenesisHashOptions from '@polkadot/extension-polkagate/src/hooks/useGenesisHashOptions';

export default function GenesisHashOptionsProvider ({ children }: { children: React.ReactNode }) {
const genesisHashOptionsCtx = useGenesisHashOptions();

return (
<GenesisHashOptionsContext.Provider value={genesisHashOptionsCtx}>
{children}
</GenesisHashOptionsContext.Provider>
);
}
49 changes: 49 additions & 0 deletions packages/extension-ui/src/Popup/contexts/MediaProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import React, { useContext, useEffect, useState } from 'react';

import { MediaContext, SettingsContext } from '@polkadot/extension-polkagate/src/components/contexts';

interface MediaProviderProps {
children: React.ReactNode;
}

// Request permission for video, based on access we can hide/show import
async function requestMediaAccess (cameraOn: boolean): Promise<boolean> {
if (!cameraOn) {
return false;
}

try {
await navigator.mediaDevices.getUserMedia({ video: true });

return true;
} catch (error) {
console.error('Permission for video declined', (error as Error).message);
}

return false;
}

export default function MediaProvider ({ children }: MediaProviderProps) {
const settings = useContext(SettingsContext);
const [cameraOn, setCameraOn] = useState(settings.camera === 'on');
const [mediaAllowed, setMediaAllowed] = useState(false);

useEffect(() => {
setCameraOn(settings.camera === 'on');
}, [settings.camera]);

useEffect(() => {
requestMediaAccess(cameraOn)
.then(setMediaAllowed)
.catch(console.error);
}, [cameraOn]);

return (
<MediaContext.Provider value={cameraOn && mediaAllowed}>
{children}
</MediaContext.Provider>
);
}
Loading

0 comments on commit db877b9

Please sign in to comment.