Skip to content

Commit

Permalink
feat: Refactor for reusable ExtensionAccountsProvider context (#1466)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ross Bulat authored Oct 7, 2023
1 parent 34985f1 commit 13380bb
Show file tree
Hide file tree
Showing 170 changed files with 1,560 additions and 1,319 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"@ledgerhq/hw-transport-webhid": "^6.27.19",
"@polkadot-cloud/assets": "^0.1.16",
"@polkadot-cloud/core": "^0.1.31",
"@polkadot-cloud/react": "^0.1.69",
"@polkadot-cloud/react": "^0.1.70",
"@polkadot-cloud/utils": "^0.0.20",
"@polkadot/api": "^10.9.1",
"@polkadot/keyring": "^12.1.1",
Expand Down
5 changes: 4 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ThemesProvider } from 'contexts/Themes';
import { i18next } from 'locale';
import { Providers } from 'Providers';
import { NetworkProvider } from 'contexts/Network';
import { ActiveAccountsProvider } from 'contexts/ActiveAccounts';

export const App: React.FC = () => {
let network = localStorage.getItem('network');
Expand All @@ -21,7 +22,9 @@ export const App: React.FC = () => {
<I18nextProvider i18n={i18next}>
<ThemesProvider>
<NetworkProvider>
<Providers />
<ActiveAccountsProvider>
<Providers />
</ActiveAccountsProvider>
</NetworkProvider>
</ThemesProvider>
</I18nextProvider>
Expand Down
11 changes: 9 additions & 2 deletions src/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import { BalancesProvider } from 'contexts/Balances';
import { BondedProvider } from 'contexts/Bonded';
import { ConnectProvider } from 'contexts/Connect';
import { ExtensionsProvider, OverlayProvider } from '@polkadot-cloud/react';
import { ExtrinsicsProvider } from 'contexts/Extrinsics';
import { FastUnstakeProvider } from 'contexts/FastUnstake';
Expand Down Expand Up @@ -41,10 +40,16 @@ import { ThemedRouter } from 'Themes';
import type { AnyJson } from 'types';
import type { FC } from 'react';
import { withProviders } from 'library/Hooks';
import { ExtensionAccountsProvider } from 'contexts/Connect/ExtensionAccounts';
import { OtherAccountsProvider } from 'contexts/Connect/OtherAccounts';
import { useActiveAccounts } from 'contexts/ActiveAccounts';
import { DappName } from 'consts';
import { ImportedAccountsProvider } from 'contexts/Connect/ImportedAccounts';

// Embed providers from hook.
export const Providers = () => {
const { network } = useNetwork();
const { activeAccount } = useActiveAccounts();

// !! Provider order matters
const providers: Array<FC<AnyJson> | [FC<AnyJson>, AnyJson]> = [
Expand All @@ -55,7 +60,9 @@ export const Providers = () => {
VaultHardwareProvider,
LedgerHardwareProvider,
ExtensionsProvider,
ConnectProvider,
[ExtensionAccountsProvider, { network, activeAccount, dappName: DappName }],
OtherAccountsProvider,
ImportedAccountsProvider,
HelpProvider,
NetworkMetricsProvider,
SubscanProvider,
Expand Down
11 changes: 7 additions & 4 deletions src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
} from 'react-router-dom';
import { Prompt } from 'library/Prompt';
import { PagesConfig } from 'config/pages';
import { useConnect } from 'contexts/Connect';
import { useNotifications } from 'contexts/Notifications';
import { useUi } from 'contexts/UI';
import { ErrorFallbackApp, ErrorFallbackRoutes } from 'library/ErrorBoundary';
Expand All @@ -30,14 +29,18 @@ import { SideMenu } from 'library/SideMenu';
import { Tooltip } from 'library/Tooltip';
import { Overlays } from 'overlay';
import { useNetwork } from 'contexts/Network';
import { useActiveAccounts } from 'contexts/ActiveAccounts';
import { useOtherAccounts } from 'contexts/Connect/OtherAccounts';
import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts';

export const RouterInner = () => {
const { t } = useTranslation();
const { network } = useNetwork();
const { pathname } = useLocation();
const { accounts } = useImportedAccounts();
const { addNotification } = useNotifications();
const { accountsInitialised, accounts, activeAccount, connectToAccount } =
useConnect();
const { accountsInitialised } = useOtherAccounts();
const { activeAccount, setActiveAccount } = useActiveAccounts();
const { sideMenuOpen, sideMenuMinimised, setContainerRefs } = useUi();

// Scroll to top of the window on every page change or network change.
Expand All @@ -59,7 +62,7 @@ export const RouterInner = () => {
if (aUrl) {
const account = accounts.find((a) => a.address === aUrl);
if (account && aUrl !== activeAccount) {
connectToAccount(account);
setActiveAccount(account?.address || null);
addNotification({
title: t('accountConnected', { ns: 'library' }),
subtitle: `${t('connectedTo', { ns: 'library' })} ${
Expand Down
14 changes: 14 additions & 0 deletions src/contexts/ActiveAccounts/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only
/* eslint-disable @typescript-eslint/no-unused-vars */

import type { ActiveAccountsContextInterface } from './types';

export const defaultActiveAccountsContext: ActiveAccountsContextInterface = {
activeAccount: null,
activeProxy: null,
activeProxyType: null,
getActiveAccount: () => null,
setActiveAccount: (address, updateLocal) => {},
setActiveProxy: (address, updateLocal) => {},
};
80 changes: 80 additions & 0 deletions src/contexts/ActiveAccounts/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import type { ReactNode } from 'react';
import { createContext, useContext, useEffect, useRef, useState } from 'react';
import type { MaybeAddress } from 'types';
import { setStateWithRef } from '@polkadot-cloud/utils';
import { useNetwork } from 'contexts/Network';
import type { ActiveAccountsContextInterface, ActiveProxy } from './types';
import { defaultActiveAccountsContext } from './defaults';

export const ActiveAccountsContext =
createContext<ActiveAccountsContextInterface>(defaultActiveAccountsContext);

export const ActiveAccountsProvider = ({
children,
}: {
children: ReactNode;
}) => {
const { network } = useNetwork();

// Store the currently active account.
const [activeAccount, setActiveAccountState] = useState<MaybeAddress>(null);
const activeAccountRef = useRef<string | null>(activeAccount);

// Store the active proxy account.
const [activeProxy, setActiveProxyState] = useState<ActiveProxy>(null);
const activeProxyRef = useRef(activeProxy);

// Setter for the active proxy account.
const setActiveProxy = (newActiveProxy: ActiveProxy, updateLocal = true) => {
if (updateLocal)
if (newActiveProxy) {
localStorage.setItem(
`${network}_active_proxy`,
JSON.stringify(newActiveProxy)
);
} else {
localStorage.removeItem(`${network}_active_proxy`);
}
setStateWithRef(newActiveProxy, setActiveProxyState, activeProxyRef);
};

// Setter for the active account.
const setActiveAccount = (
newActiveAccount: MaybeAddress,
local: boolean = true
) => {
if (local)
if (newActiveAccount === null) {
localStorage.removeItem(`${network}_active_account`);
} else {
localStorage.setItem(`${network}_active_account`, newActiveAccount);
}
setStateWithRef(newActiveAccount, setActiveAccountState, activeAccountRef);
};

// Getter for the active account.
const getActiveAccount = () => activeAccountRef.current;

// Disconnect from the active account on network change, but don't remove local record.
useEffect(() => setActiveAccount(null, false), [network]);

return (
<ActiveAccountsContext.Provider
value={{
activeAccount: activeAccountRef.current,
activeProxy: activeProxyRef.current?.address ?? null,
activeProxyType: activeProxyRef.current?.proxyType ?? null,
setActiveAccount,
getActiveAccount,
setActiveProxy,
}}
>
{children}
</ActiveAccountsContext.Provider>
);
};

export const useActiveAccounts = () => useContext(ActiveAccountsContext);
18 changes: 18 additions & 0 deletions src/contexts/ActiveAccounts/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import type { MaybeAddress } from 'types';

export interface ActiveAccountsContextInterface {
activeAccount: MaybeAddress;
activeProxy: MaybeAddress;
activeProxyType: string | null;
getActiveAccount: () => string | null;
setActiveAccount: (address: MaybeAddress, updateLocal?: boolean) => void;
setActiveProxy: (address: ActiveProxy, updateLocal?: boolean) => void;
}

export type ActiveProxy = {
address: MaybeAddress;
proxyType: string;
} | null;
6 changes: 3 additions & 3 deletions src/contexts/Balances/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import type { MaybeAccount } from 'types';
import type { MaybeAddress } from 'types';
import { defaultLedger } from './defaults';
import type { Ledger } from './types';

Expand All @@ -10,11 +10,11 @@ import type { Ledger } from './types';
* @summary Get an account's ledger record according to a key.
* @param {Ledger} ledgers
* @param {string} key
* @param { MaybeAccount } address
* @param { MaybeAddress } address
* @returns Ledger
*/
export const getLedger = (
ledgers: Ledger[],
key: 'stash' | 'address',
address: MaybeAccount
address: MaybeAddress
): Ledger => ledgers.find((l) => l[key] === address) || defaultLedger;
17 changes: 10 additions & 7 deletions src/contexts/Balances/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import {
import BigNumber from 'bignumber.js';
import React, { useRef, useState } from 'react';
import { useApi } from 'contexts/Api';
import { useConnect } from 'contexts/Connect';
import type { AnyApi, MaybeAccount } from 'types';
import type { AnyApi, MaybeAddress } from 'types';
import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks';
import { useNetwork } from 'contexts/Network';
import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts';
import { useOtherAccounts } from 'contexts/Connect/OtherAccounts';
import { getLedger } from './Utils';
import * as defaults from './defaults';
import type {
Expand All @@ -36,7 +37,9 @@ export const BalancesProvider = ({
}) => {
const { api, isReady } = useApi();
const { network } = useNetwork();
const { accounts, addExternalAccount, getAccount } = useConnect();
const { accounts } = useImportedAccounts();
const { getAccount } = useImportedAccounts();
const { addExternalAccount } = useOtherAccounts();

const [balances, setBalances] = useState<Balances[]>([]);
const balancesRef = useRef(balances);
Expand Down Expand Up @@ -189,21 +192,21 @@ export const BalancesProvider = ({
}, [network]);

// Gets a ledger for a stash address.
const getStashLedger = (address: MaybeAccount) => {
const getStashLedger = (address: MaybeAddress) => {
return getLedger(ledgersRef.current, 'stash', address);
};

// Gets an account's balance metadata.
const getBalance = (address: MaybeAccount) =>
const getBalance = (address: MaybeAddress) =>
balancesRef.current.find((a) => a.address === address)?.balance ||
defaults.defaultBalance;

// Gets an account's locks.
const getLocks = (address: MaybeAccount) =>
const getLocks = (address: MaybeAddress) =>
balancesRef.current.find((a) => a.address === address)?.locks ?? [];

// Gets an account's nonce.
const getNonce = (address: MaybeAccount) =>
const getNonce = (address: MaybeAddress) =>
balancesRef.current.find((a) => a.address === address)?.nonce ?? 0;

return (
Expand Down
12 changes: 6 additions & 6 deletions src/contexts/Balances/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
// SPDX-License-Identifier: GPL-3.0-only

import type BigNumber from 'bignumber.js';
import type { MaybeAccount } from 'types';
import type { MaybeAddress } from 'types';

export interface BalancesContextInterface {
ledgers: Ledger[];
balances: Balances[];
getStashLedger: (a: MaybeAccount) => Ledger;
getBalance: (address: MaybeAccount) => Balance;
getLocks: (address: MaybeAccount) => BalanceLock[];
getNonce: (address: MaybeAccount) => number;
getStashLedger: (a: MaybeAddress) => Ledger;
getBalance: (address: MaybeAddress) => Balance;
getLocks: (address: MaybeAddress) => BalanceLock[];
getNonce: (address: MaybeAddress) => number;
}

export interface Balances {
Expand Down Expand Up @@ -42,7 +42,7 @@ export interface BalanceLock {
}

export interface Ledger {
address: MaybeAccount;
address: MaybeAddress;
stash: string | null;
active: BigNumber;
total: BigNumber;
Expand Down
16 changes: 9 additions & 7 deletions src/contexts/Bonded/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ import {
} from '@polkadot-cloud/utils';
import React, { useEffect, useRef, useState } from 'react';
import { useApi } from 'contexts/Api';
import { useConnect } from 'contexts/Connect';
import type { AnyApi, MaybeAccount } from 'types';
import type { AnyApi, MaybeAddress } from 'types';
import { useEffectIgnoreInitial } from '@polkadot-cloud/react/hooks';
import { useNetwork } from 'contexts/Network';
import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts';
import { useOtherAccounts } from 'contexts/Connect/OtherAccounts';
import * as defaults from './defaults';
import type { BondedAccount, BondedContextInterface } from './types';

export const BondedProvider = ({ children }: { children: React.ReactNode }) => {
const { network } = useNetwork();
const { api, isReady } = useApi();
const { accounts, addExternalAccount } = useConnect();
const { accounts } = useImportedAccounts();
const { addExternalAccount } = useOtherAccounts();

// Balance accounts state.
const [bondedAccounts, setBondedAccounts] = useState<BondedAccount[]>([]);
Expand Down Expand Up @@ -132,18 +134,18 @@ export const BondedProvider = ({ children }: { children: React.ReactNode }) => {
return unsub;
};

const getBondedAccount = (address: MaybeAccount) =>
const getBondedAccount = (address: MaybeAddress) =>
bondedAccountsRef.current.find((a) => a.address === address)?.bonded ||
null;

const getAccountNominations = (address: MaybeAccount) =>
const getAccountNominations = (address: MaybeAddress) =>
bondedAccountsRef.current.find((a) => a.address === address)?.nominations
?.targets || [];

const getAccount = (address: MaybeAccount) =>
const getAccount = (address: MaybeAddress) =>
bondedAccountsRef.current.find((a) => a.address === address) || null;

const isController = (address: MaybeAccount) =>
const isController = (address: MaybeAddress) =>
bondedAccountsRef.current.filter((a) => (a?.bonded || '') === address)
?.length > 0 || false;

Expand Down
10 changes: 5 additions & 5 deletions src/contexts/Bonded/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2023 @paritytech/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import type { MaybeAccount } from 'types';
import type { MaybeAddress } from 'types';

export interface BondedAccount {
address?: string;
Expand All @@ -17,9 +17,9 @@ export interface Nominations {
export type Targets = string[];

export interface BondedContextInterface {
getAccount: (address: MaybeAccount) => BondedAccount | null;
getBondedAccount: (address: MaybeAccount) => string | null;
getAccountNominations: (address: MaybeAccount) => Targets;
isController: (address: MaybeAccount) => boolean;
getAccount: (address: MaybeAddress) => BondedAccount | null;
getBondedAccount: (address: MaybeAddress) => string | null;
getAccountNominations: (address: MaybeAddress) => Targets;
isController: (address: MaybeAddress) => boolean;
bondedAccounts: BondedAccount[];
}
Loading

0 comments on commit 13380bb

Please sign in to comment.