diff --git a/packages/app-accounts/src/Overview.tsx b/packages/app-accounts/src/Overview.tsx index 7b3e2d3e9e..dfb611eb7e 100644 --- a/packages/app-accounts/src/Overview.tsx +++ b/packages/app-accounts/src/Overview.tsx @@ -3,13 +3,12 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { I18nProps } from '@polkadot/react-components/types'; -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import { ComponentProps } from './types'; import React, { useState } from 'react'; import keyring from '@polkadot/ui-keyring'; -import accountObservable from '@polkadot/ui-keyring/observable/accounts'; -import { getLedger, isLedger, withMulti, withObservable } from '@polkadot/react-api'; +import { getLedger, isLedger } from '@polkadot/react-api'; +import { useAccounts } from '@polkadot/react-hooks'; import { Button, CardGrid } from '@polkadot/react-components'; import CreateModal from './modals/Create'; @@ -20,7 +19,6 @@ import Banner from './Banner'; import translate from './translate'; interface Props extends ComponentProps, I18nProps { - accounts?: SubjectInfo[]; } // query the ledger for the address, adding it to the keyring @@ -36,11 +34,12 @@ async function queryLedger (): Promise { } } -function Overview ({ accounts, onStatusChange, t }: Props): React.ReactElement { +function Overview ({ onStatusChange, t }: Props): React.ReactElement { + const { allAccounts, hasAccounts } = useAccounts(); const [isCreateOpen, setIsCreateOpen] = useState(false); const [isImportOpen, setIsImportOpen] = useState(false); const [isQrOpen, setIsQrOpen] = useState(false); - const emptyScreen = !(isCreateOpen || isImportOpen || isQrOpen) && accounts && (Object.keys(accounts).length === 0); + const emptyScreen = !(isCreateOpen || isImportOpen || isQrOpen) && !hasAccounts; const _toggleCreate = (): void => setIsCreateOpen(!isCreateOpen); const _toggleImport = (): void => setIsImportOpen(!isImportOpen); @@ -105,7 +104,7 @@ function Overview ({ accounts, onStatusChange, t }: Props): React.ReactElement

)} - {accounts && Object.keys(accounts).map((address): React.ReactNode => ( + {allAccounts.map((address): React.ReactNode => ( { +function AccountsApp ({ basePath, onStatusChange, t }: Props): React.ReactElement { + const { hasAccounts } = useAccounts(); const [hidden, setHidden] = useState(['vanity']); useEffect((): void => { setHidden( - Object.keys(allAccounts).length !== 0 + hasAccounts ? [] : ['vanity'] ); - }, [allAccounts]); + }, [hasAccounts]); - const _renderComponent = (Component: React.ComponentType): (props: LocationProps) => React.ReactNode => { - // eslint-disable-next-line react/display-name - return ({ match }: LocationProps): React.ReactNode => { - return ( - - ); - }; - }; + const _renderComponent = (Component: React.ComponentType): React.ReactNode => ( + + ); return (

@@ -68,15 +58,11 @@ function AccountsApp ({ allAccounts = {}, basePath, location, onStatusChange, t /> - - + {_renderComponent(Vanity)} + {_renderComponent(Overview)}
); } -export default withMulti( - AccountsApp, - translate, - withObservable(accountObservable.subject, { propName: 'allAccounts' }) -); +export default translate(AccountsApp); diff --git a/packages/app-accounts/src/types.ts b/packages/app-accounts/src/types.ts index 81962b9590..936b4a1e12 100644 --- a/packages/app-accounts/src/types.ts +++ b/packages/app-accounts/src/types.ts @@ -7,13 +7,6 @@ import { ActionStatus } from '@polkadot/react-components/Status/types'; import { WithTranslation } from 'react-i18next'; -export interface LocationProps { - location: any; - match: { - params: Record; - }; -} - export interface BareProps { className?: string; style?: Record; @@ -21,7 +14,7 @@ export interface BareProps { export type I18nProps = BareProps & WithTranslation; -export type ComponentProps = AppProps & LocationProps; +export type ComponentProps = AppProps; export interface ModalProps { onClose: () => void; diff --git a/packages/app-address-book/src/Overview.tsx b/packages/app-address-book/src/Overview.tsx index b54cf8209e..c10191c9e6 100644 --- a/packages/app-address-book/src/Overview.tsx +++ b/packages/app-address-book/src/Overview.tsx @@ -3,25 +3,23 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { I18nProps } from '@polkadot/react-components/types'; -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import { ComponentProps } from './types'; import React, { useState } from 'react'; import { Button, CardGrid } from '@polkadot/react-components'; -import addressObservable from '@polkadot/ui-keyring/observable/addresses'; -import { withMulti, withObservable } from '@polkadot/react-api'; +import { useAddresses } from '@polkadot/react-hooks'; import CreateModal from './modals/Create'; import Address from './Address'; import translate from './translate'; interface Props extends ComponentProps, I18nProps { - addresses?: SubjectInfo[]; } -function Overview ({ addresses, onStatusChange, t }: Props): React.ReactElement { +function Overview ({ onStatusChange, t }: Props): React.ReactElement { + const { hasAddresses, allAddresses } = useAddresses(); const [isCreateOpen, setIsCreateOpen] = useState(false); - const emptyScreen = !isCreateOpen && (!addresses || Object.keys(addresses).length === 0); + const emptyScreen = !isCreateOpen && !hasAddresses; const _toggleCreate = (): void => setIsCreateOpen(!isCreateOpen); @@ -46,7 +44,7 @@ function Overview ({ addresses, onStatusChange, t }: Props): React.ReactElement< onStatusChange={onStatusChange} /> )} - {addresses && Object.keys(addresses).map((address): React.ReactNode => ( + {allAddresses.map((address): React.ReactNode => (
{ - const _renderComponent = (Component: React.ComponentType): () => React.ReactNode => { - // eslint-disable-next-line react/display-name - return (): React.ReactNode => - ; - }; - return (
@@ -47,7 +36,13 @@ function AddressBookApp ({ basePath, onStatusChange, t }: Props): React.ReactEle /> - + + +
); diff --git a/packages/app-democracy/src/Overview/Seconding.tsx b/packages/app-democracy/src/Overview/Seconding.tsx index f38eba81b1..84385653f2 100644 --- a/packages/app-democracy/src/Overview/Seconding.tsx +++ b/packages/app-democracy/src/Overview/Seconding.tsx @@ -4,26 +4,23 @@ import { AccountId } from '@polkadot/types/interfaces'; import { I18nProps } from '@polkadot/react-components/types'; -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import BN from 'bn.js'; import React, { useState } from 'react'; import { Button, InputAddress, Modal, TxButton } from '@polkadot/react-components'; -import accountObservable from '@polkadot/ui-keyring/observable/accounts'; -import { withMulti, withObservable } from '@polkadot/react-api'; +import { useAccounts } from '@polkadot/react-hooks'; import translate from '../translate'; interface Props extends I18nProps { - allAccounts?: SubjectInfo; depositors: AccountId[]; proposalId: BN | number; } -function Seconding ({ allAccounts, depositors, proposalId, t }: Props): React.ReactElement | null { +function Seconding ({ depositors, proposalId, t }: Props): React.ReactElement | null { + const { hasAccounts } = useAccounts(); const [accountId, setAccountId] = useState(null); const [isSecondingOpen, setIsSecondingOpen] = useState(false); - const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0; if (!hasAccounts) { return null; @@ -85,8 +82,4 @@ function Seconding ({ allAccounts, depositors, proposalId, t }: Props): React.Re ); } -export default withMulti( - Seconding, - translate, - withObservable(accountObservable.subject, { propName: 'allAccounts' }) -); +export default translate(Seconding); diff --git a/packages/app-democracy/src/Overview/Voting.tsx b/packages/app-democracy/src/Overview/Voting.tsx index ff901325e6..66b9eb9576 100644 --- a/packages/app-democracy/src/Overview/Voting.tsx +++ b/packages/app-democracy/src/Overview/Voting.tsx @@ -3,26 +3,23 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { I18nProps } from '@polkadot/react-components/types'; -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import BN from 'bn.js'; import React, { useState } from 'react'; import { Button, Dropdown, InputAddress, Modal, TxButton } from '@polkadot/react-components'; -import accountObservable from '@polkadot/ui-keyring/observable/accounts'; -import { withMulti, withObservable } from '@polkadot/react-api'; +import { useAccounts } from '@polkadot/react-hooks'; import translate from '../translate'; interface Props extends I18nProps { - allAccounts?: SubjectInfo; referendumId: BN | number; } -function Voting ({ allAccounts, referendumId, t }: Props): React.ReactElement | null { +function Voting ({ referendumId, t }: Props): React.ReactElement | null { + const { hasAccounts } = useAccounts(); const [accountId, setAccountId] = useState(null); const [isVotingOpen, setIsVotingOpen] = useState(false); const [voteValue, setVoteValue] = useState(true); - const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0; if (!hasAccounts) { return null; @@ -93,8 +90,4 @@ function Voting ({ allAccounts, referendumId, t }: Props): React.ReactElement | null { - const [stateValue, setStateValue] = useState(value); +function Entry ({ chain_bestNumber }: Props): React.ReactElement | null { + const { value } = useParams(); + const [stateValue, setStateValue] = useState(value); useEffect((): void => { if (value && value !== stateValue) { @@ -54,7 +50,4 @@ function Entry ({ chain_bestNumber, match: { params: { value } } }: Props): Reac ); } -export default withMulti( - withRouter(Entry), - withCalls('derive.chain.bestNumber') -); +export default withCalls('derive.chain.bestNumber')(Entry); diff --git a/packages/app-js/src/Playground.tsx b/packages/app-js/src/Playground.tsx index 427297dc7a..46d839ff91 100644 --- a/packages/app-js/src/Playground.tsx +++ b/packages/app-js/src/Playground.tsx @@ -8,7 +8,7 @@ import { AppProps, I18nProps } from '@polkadot/react-components/types'; import { Log, LogType, Snippet } from './types'; import React, { useRef, useState, useEffect } from 'react'; -import { withRouter, RouteComponentProps } from 'react-router'; +import { useHistory, useParams } from 'react-router-dom'; import { Transition } from 'semantic-ui-react'; import snappy from 'snappyjs'; import styled from 'styled-components'; @@ -41,17 +41,7 @@ interface Injected { window: null; } -interface Props extends AppProps, I18nProps, RouteComponentProps<{}> { - match: { - isExact: boolean; - params: { - base64?: string; - }; - path: string; - url: string; - }; - // FIXME wait for proper eslint integration in tslint, then hopefully remove this - history: any; +interface Props extends AppProps, I18nProps { } const snippets: Snippet[] = JSON.parse(JSON.stringify(allSnippets)); @@ -89,8 +79,10 @@ function decodeBase64 (base64: string): Snippet { } // FIXME This... ladies & gentlemen, is a mess that should be untangled -function Playground ({ className, history, match: { params: { base64 } }, t }: Props): React.ReactElement { +function Playground ({ className, t }: Props): React.ReactElement { const { api, isDevelopment } = useApiContext(); + const history = useHistory(); + const { base64 } = useParams(); const injectedRef = useRef(null); const [code, setCode] = useState(''); const [isAnimated, setIsAnimated] = useState(true); @@ -334,10 +326,8 @@ function Playground ({ className, history, match: { params: { base64 } }, t }: P ); } -const Routed = withRouter(Playground); - export default translate( - styled(Routed)` + styled(Playground)` display: flex; flex-direction: column; height: 100vh; diff --git a/packages/app-staking/src/Actions/Accounts.tsx b/packages/app-staking/src/Actions/Accounts.tsx index 101133b939..39fdbbb50b 100644 --- a/packages/app-staking/src/Actions/Accounts.tsx +++ b/packages/app-staking/src/Actions/Accounts.tsx @@ -2,17 +2,15 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { ApiProps } from '@polkadot/react-api/types'; import { I18nProps } from '@polkadot/react-components/types'; -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import { KeyringSectionOption } from '@polkadot/ui-keyring/options/types'; import { AccountId, StakingLedger } from '@polkadot/types/interfaces'; import { ComponentProps } from '../types'; import React, { useState } from 'react'; import styled from 'styled-components'; -import { withCalls, withMulti } from '@polkadot/react-api/with'; import { Button, CardGrid } from '@polkadot/react-components'; +import { trackStream, useApiContext } from '@polkadot/react-hooks'; import { AccountName } from '@polkadot/react-query'; import { Option } from '@polkadot/types'; import createOption from '@polkadot/ui-keyring/options/item'; @@ -21,12 +19,10 @@ import Account from './Account'; import StartStaking from './NewStake'; import translate from '../translate'; -interface Props extends I18nProps, ComponentProps, ApiProps { - queryBonded?: Option[]; - queryLedger?: Option[]; +interface Props extends I18nProps, ComponentProps { } -function getMyStashes (queryBonded?: Option[], queryLedger?: Option[], allAccounts?: SubjectInfo): string[] | null { +function getMyStashes (allAccounts: string[], queryBonded?: Option[], queryLedger?: Option[]): string[] | null { const result: string[] = []; if (!queryBonded || !queryLedger) { @@ -34,7 +30,7 @@ function getMyStashes (queryBonded?: Option[], queryLedger?: Option { - value.isSome && allAccounts && result.push(Object.keys(allAccounts)[index]); + value.isSome && result.push(allAccounts[index]); }); queryLedger.forEach((ledger): void => { @@ -48,9 +44,12 @@ function getMyStashes (queryBonded?: Option[], queryLedger?: Option { +function Accounts ({ allAccounts, allStashes, className, recentlyOnline, t }: Props): React.ReactElement { + const { api } = useApiContext(); + const queryBonded = trackStream[]>(api.query.staking.bonded.multi as any, [allAccounts]); + const queryLedger = trackStream[]>(api.query.staking.ledger.multi as any, [allAccounts]); const [isNewStakeOpen, setIsNewStateOpen] = useState(false); - const foundStashes = getMyStashes(queryBonded, queryLedger, allAccounts); + const foundStashes = getMyStashes(allAccounts, queryBonded, queryLedger); const stashOptions = allStashes.map((stashId): KeyringSectionOption => createOption(stashId, () as any) ); @@ -91,25 +90,10 @@ function Accounts ({ allAccounts, allStashes, className, queryBonded, queryLedge ); } -export default withMulti( +export default translate( styled(Accounts)` .ui--CardGrid-buttons { text-align: right; } - `, - translate, - withCalls( - ['query.staking.bonded', { - isMulti: true, - propName: 'queryBonded', - paramPick: ({ allAccounts }: Props): undefined | string[] => - allAccounts && Object.keys(allAccounts) - }], - ['query.staking.ledger', { - isMulti: true, - propName: 'queryLedger', - paramPick: ({ allAccounts }: Props): undefined | string[] => - allAccounts && Object.keys(allAccounts) - }] - ) + ` ); diff --git a/packages/app-staking/src/Query/index.tsx b/packages/app-staking/src/Query/index.tsx index 93cae7326e..efd7385312 100644 --- a/packages/app-staking/src/Query/index.tsx +++ b/packages/app-staking/src/Query/index.tsx @@ -6,21 +6,13 @@ import { I18nProps } from '@polkadot/react-components/types'; import { ComponentProps } from '../types'; import React, { useState } from 'react'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import { Button, InputAddressSimple } from '@polkadot/react-components'; import translate from '../translate'; import Validator from './Validator'; -interface Props extends I18nProps, ComponentProps, RouteComponentProps<{}> { - match: { - isExact: boolean; - params: { - value: string; - }; - path: string; - url: string; - }; +interface Props extends I18nProps, ComponentProps { } // const _onQuery = (): void => { @@ -46,7 +38,8 @@ interface Props extends I18nProps, ComponentProps, RouteComponentProps<{}> { // /> // -function Query ({ className, sessionRewards, match: { params: { value } }, t }: Props): React.ReactElement { +function Query ({ className, sessionRewards, t }: Props): React.ReactElement { + const { value } = useParams(); const [validatorId, setValidatorId] = useState(value || null); const _onQuery = (): void => { @@ -81,4 +74,4 @@ function Query ({ className, sessionRewards, match: { params: { value } }, t }: ); } -export default translate(withRouter(Query)); +export default translate(Query); diff --git a/packages/app-staking/src/index.tsx b/packages/app-staking/src/index.tsx index 955e26b72e..2d453b0f18 100644 --- a/packages/app-staking/src/index.tsx +++ b/packages/app-staking/src/index.tsx @@ -4,9 +4,7 @@ import { DerivedHeartbeats, DerivedStakingOverview } from '@polkadot/api-derive/types'; import { AppProps, I18nProps } from '@polkadot/react-components/types'; -import { ApiProps } from '@polkadot/react-api/types'; import { AccountId, BlockNumber } from '@polkadot/types/interfaces'; -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import { ComponentProps } from './types'; import React from 'react'; @@ -16,9 +14,7 @@ import styled from 'styled-components'; import { Option } from '@polkadot/types'; import { HelpOverlay } from '@polkadot/react-components'; import Tabs from '@polkadot/react-components/Tabs'; -import { withMulti, withObservable } from '@polkadot/react-api'; -import { trackStream, useApiContext, useSessionRewards } from '@polkadot/react-hooks'; -import accountObservable from '@polkadot/ui-keyring/observable/accounts'; +import { trackStream, useAccounts, useApiContext, useSessionRewards } from '@polkadot/react-hooks'; import Accounts from './Actions/Accounts'; import basicMd from './md/basic.md'; @@ -27,8 +23,7 @@ import Query from './Query'; import { MAX_SESSIONS } from './constants'; import translate from './translate'; -interface Props extends AppProps, ApiProps, I18nProps { - allAccounts?: SubjectInfo; +interface Props extends AppProps, I18nProps { } const EMPY_ACCOUNTS: string[] = []; @@ -43,8 +38,9 @@ function transformStakingControllers ([stashes, controllers]: [AccountId[], Opti ]; } -function App ({ allAccounts, basePath, className, t }: Props): React.ReactElement { +function App ({ basePath, className, t }: Props): React.ReactElement { const { api } = useApiContext(); + const { allAccounts, hasAccounts } = useAccounts(); const stakingControllers = trackStream<[string[], string[]]>(api.derive.staking.controllers, [], { transform: transformStakingControllers }); const bestNumber = trackStream(api.derive.chain.bestNumber, []); const recentlyOnline = trackStream(api.derive.imOnline.receivedHeartbeats, []); @@ -52,32 +48,22 @@ function App ({ allAccounts, basePath, className, t }: Props): React.ReactElemen const sessionRewards = useSessionRewards(MAX_SESSIONS); const routeMatch = useRouteMatch({ path: basePath, strict: true }); - const hasAccounts = !!allAccounts && Object.keys(allAccounts).length !== 0; const hasQueries = hasAccounts && !!(api.query.imOnline?.authoredBlocks); const [allStashes, allControllers] = stakingControllers || EMPTY_ALL; - const _renderComponent = (Component: React.ComponentType, className?: string): () => React.ReactNode => { - // eslint-disable-next-line react/display-name - return (): React.ReactNode => { - if (!allAccounts) { - return null; - } - - return ( - - ); - }; - }; + const _renderComponent = (Component: React.ComponentType, className?: string): React.ReactNode => ( + + ); return (
@@ -111,16 +97,16 @@ function App ({ allAccounts, basePath, className, t }: Props): React.ReactElemen /> - - - + {_renderComponent(Accounts)} + {_renderComponent(Query)} + {_renderComponent(Query)} - {_renderComponent(Overview, routeMatch?.isExact ? '' : 'staking--hidden')()} + {_renderComponent(Overview, routeMatch?.isExact ? '' : 'staking--hidden')}
); } -export default withMulti( +export default translate( styled(App)` .staking--hidden { display: none; @@ -137,7 +123,5 @@ export default withMulti( .staking--Chart+.staking--Chart { margin-top: 1.5rem; } - `, - translate, - withObservable(accountObservable.subject, { propName: 'allAccounts' }) + ` ); diff --git a/packages/app-staking/src/types.ts b/packages/app-staking/src/types.ts index ddbe66eccd..919148f1cf 100644 --- a/packages/app-staking/src/types.ts +++ b/packages/app-staking/src/types.ts @@ -5,12 +5,11 @@ import { DerivedFees, DerivedBalances, DerivedHeartbeats, DerivedStakingOverview } from '@polkadot/api-derive/types'; import { BlockNumber } from '@polkadot/types/interfaces'; import { SessionRewards } from '@polkadot/react-hooks/types'; -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; export type Nominators = Record; export interface ComponentProps { - allAccounts?: SubjectInfo; + allAccounts: string[]; allControllers: string[]; allStashes: string[]; bestNumber?: BlockNumber; diff --git a/packages/app-sudo/src/SetKey.tsx b/packages/app-sudo/src/SetKey.tsx index f25b68cf02..b85530b2b7 100644 --- a/packages/app-sudo/src/SetKey.tsx +++ b/packages/app-sudo/src/SetKey.tsx @@ -23,10 +23,9 @@ function SetKey ({ allAccounts, className, isMine, sudoKey, t }: Props): React.R }, [selected, sudoKey]); const willLose = isMine && - !!Object.keys(allAccounts).length && !!selected && selected !== sudoKey && - !Object.keys(allAccounts).find((s): boolean => s === selected); + allAccounts.some((s): boolean => s === selected); return (
diff --git a/packages/app-sudo/src/index.tsx b/packages/app-sudo/src/index.tsx index fe018a11e9..011adab330 100644 --- a/packages/app-sudo/src/index.tsx +++ b/packages/app-sudo/src/index.tsx @@ -4,14 +4,12 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { AppProps, I18nProps } from '@polkadot/react-components/types'; -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import { ComponentProps } from './types'; import React, { useEffect, useState } from 'react'; import { Route, Switch } from 'react-router'; import { Icon, Tabs } from '@polkadot/react-components'; -import { withCalls, withMulti, withObservable } from '@polkadot/react-api'; -import accountObservable from '@polkadot/ui-keyring/observable/accounts'; +import { trackStream, useAccounts, useApiContext } from '@polkadot/react-hooks'; import SetKey from './SetKey'; import Sudo from './Sudo'; @@ -19,17 +17,16 @@ import Sudo from './Sudo'; import translate from './translate'; interface Props extends AppProps, I18nProps { - allAccounts: SubjectInfo; - sudoKey?: string; } -function App ({ allAccounts, basePath, sudoKey, t }: Props): React.ReactElement { +function App ({ basePath, t }: Props): React.ReactElement { + const { api } = useApiContext(); + const sudoKey = trackStream(api.query.sudo.key, [], { transform: (k): string => k.toString() }); + const { allAccounts } = useAccounts(); const [isMine, setIsMine] = useState(false); useEffect((): void => { - setIsMine( - !!sudoKey && !!allAccounts && Object.keys(allAccounts).some((key): boolean => key === sudoKey) - ); + setIsMine(!!sudoKey && allAccounts.some((key): boolean => key === sudoKey)); }, [allAccounts, sudoKey]); const _renderComponent = (Component: React.ComponentType): () => React.ReactNode => { @@ -83,15 +80,4 @@ function App ({ allAccounts, basePath, sudoKey, t }: Props): React.ReactElement< ); } -export default withMulti( - App, - translate, - withCalls( - ['query.sudo.key', { - propName: 'sudoKey', - transform: (key): string => - key.toString() - }] - ), - withObservable(accountObservable.subject, { propName: 'allAccounts' }) -); +export default translate(App); diff --git a/packages/app-sudo/src/types.ts b/packages/app-sudo/src/types.ts index 78e0f326d6..7687cefeb7 100644 --- a/packages/app-sudo/src/types.ts +++ b/packages/app-sudo/src/types.ts @@ -2,10 +2,8 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; - export interface ComponentProps { - allAccounts: SubjectInfo; + allAccounts: string[]; isMine: boolean; sudoKey?: string; } diff --git a/packages/app-toolbox/src/index.tsx b/packages/app-toolbox/src/index.tsx index 956e12f091..512c4e3956 100644 --- a/packages/app-toolbox/src/index.tsx +++ b/packages/app-toolbox/src/index.tsx @@ -3,13 +3,11 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { AppProps, I18nProps } from '@polkadot/react-components/types'; -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import React from 'react'; import { Route, Switch } from 'react-router'; import Tabs from '@polkadot/react-components/Tabs'; -import accountObservable from '@polkadot/ui-keyring/observable/accounts'; -import { withMulti, withObservable } from '@polkadot/react-api'; +import { useAccounts } from '@polkadot/react-hooks'; import Hash from './Hash'; import Rpc from './Rpc'; @@ -18,11 +16,10 @@ import Verify from './Verify'; import translate from './translate'; interface Props extends AppProps, I18nProps { - allAccounts?: SubjectInfo; } -function ToolboxApp ({ allAccounts, basePath, t }: Props): React.ReactElement { - const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0; +function ToolboxApp ({ basePath, t }: Props): React.ReactElement { + const { hasAccounts } = useAccounts(); const tabs = [ { isRoot: true, @@ -64,8 +61,4 @@ function ToolboxApp ({ allAccounts, basePath, t }: Props): React.ReactElement void; } -function ProposalDisplay ({ allAccounts, isApproved, onPopulate, onRespond, proposal, proposalId, t }: Props): React.ReactElement | null { +function ProposalDisplay ({ isApproved, onPopulate, onRespond, proposal, proposalId, t }: Props): React.ReactElement | null { + const { hasAccounts } = useAccounts(); + useEffect((): void => { onPopulate(); }, [proposal]); @@ -39,8 +39,6 @@ function ProposalDisplay ({ allAccounts, isApproved, onPopulate, onRespond, prop return null; } - const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0; - return ( ): TreasuryProposalType | null => value.unwrapOr(null) }] - ), - withObservable(keyring.accounts.subject, { propName: 'allAccounts' }) + ) ); diff --git a/packages/app-treasury/src/Overview/Proposals.tsx b/packages/app-treasury/src/Overview/Proposals.tsx index 78f75f5f2a..279ed9ebda 100644 --- a/packages/app-treasury/src/Overview/Proposals.tsx +++ b/packages/app-treasury/src/Overview/Proposals.tsx @@ -8,21 +8,21 @@ import { I18nProps } from '@polkadot/react-components/types'; import BN from 'bn.js'; import React, { useEffect, useState } from 'react'; -import { RouteComponentProps } from 'react-router'; -import { withRouter } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { withCalls, withMulti } from '@polkadot/react-api'; import { Column } from '@polkadot/react-components'; import Proposal from './Proposal'; import translate from '../translate'; -interface Props extends I18nProps, RouteComponentProps<{}> { +interface Props extends I18nProps { isApprovals?: boolean; treasury_approvals?: BN[]; treasury_proposalCount?: BN; } -function ProposalsBase ({ history, isApprovals = false, treasury_approvals, treasury_proposalCount, t }: Props): React.ReactElement { +function ProposalsBase ({ isApprovals = false, treasury_approvals, treasury_proposalCount, t }: Props): React.ReactElement { + const history = useHistory(); const [isEmpty, setIsEmpty] = useState(true); const [proposalIndices, setProposalIndices] = useState([]); @@ -71,7 +71,7 @@ function ProposalsBase ({ history, isApprovals = false, treasury_approvals, trea } const Proposals = withMulti( - withRouter(ProposalsBase), + ProposalsBase, translate, withCalls( ['query.treasury.approvals', { diff --git a/packages/apps/src/Content/index.tsx b/packages/apps/src/Content/index.tsx index 8200ea142a..6fa7a3b336 100644 --- a/packages/apps/src/Content/index.tsx +++ b/packages/apps/src/Content/index.tsx @@ -2,10 +2,10 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { I18nProps } from '@polkadot/react-components/types'; +import { I18nProps as Props } from '@polkadot/react-components/types'; import React, { useContext } from 'react'; -import { withRouter, RouteComponentProps } from 'react-router'; +import { useLocation } from 'react-router-dom'; import styled from 'styled-components'; import routing from '@polkadot/apps-routing'; import { StatusContext } from '@polkadot/react-components'; @@ -15,8 +15,6 @@ import Status from './Status'; import translate from '../translate'; import NotFound from './NotFound'; -interface Props extends I18nProps, RouteComponentProps {} - const unknown = { display: { needsApi: undefined @@ -25,7 +23,8 @@ const unknown = { name: '' }; -function Content ({ className, location, t }: Props): React.ReactElement { +function Content ({ className, t }: Props): React.ReactElement { + const location = useLocation(); const { isApiConnected, isApiReady } = useApiContext(); const { queueAction, stqueue, txqueue } = useContext(StatusContext); const app = location.pathname.slice(1) || ''; @@ -58,26 +57,24 @@ function Content ({ className, location, t }: Props): React.ReactElement // React-router needs to be first, otherwise we have blocked updates export default translate( - withRouter( - styled(Content)` - background: #fafafa; - display: flex; - flex-direction: column; - flex-grow: 1; - height: 100%; - min-height: 100vh; - overflow-x: hidden; - overflow-y: auto; - width: 100%; - padding: 0 2rem; + styled(Content)` + background: #fafafa; + display: flex; + flex-direction: column; + flex-grow: 1; + height: 100%; + min-height: 100vh; + overflow-x: hidden; + overflow-y: auto; + width: 100%; + padding: 0 2rem; - @media(max-width: 768px) { - padding: 0 0.5rem; - } + @media(max-width: 768px) { + padding: 0 0.5rem; + } - .connecting { - padding: 1rem 0; - } - ` - ) + .connecting { + padding: 1rem 0; + } + ` ); diff --git a/packages/apps/src/SideBar/Item.tsx b/packages/apps/src/SideBar/Item.tsx index 7dd550ee4d..122dde1d9e 100644 --- a/packages/apps/src/SideBar/Item.tsx +++ b/packages/apps/src/SideBar/Item.tsx @@ -4,7 +4,6 @@ import { ApiProps } from '@polkadot/react-api/types'; import { I18nProps } from '@polkadot/react-components/types'; -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import { Route } from '@polkadot/apps-routing/types'; import { AccountId } from '@polkadot/types/interfaces'; @@ -12,9 +11,8 @@ import React, { useEffect, useState } from 'react'; import { NavLink } from 'react-router-dom'; import { ApiPromise } from '@polkadot/api'; import { Icon, Menu, Tooltip } from '@polkadot/react-components'; -import accountObservable from '@polkadot/ui-keyring/observable/accounts'; -import { withCalls, withMulti, withObservable } from '@polkadot/react-api'; -import { useApiContext } from '@polkadot/react-hooks'; +import { withCalls, withMulti } from '@polkadot/react-api'; +import { useAccounts, useApiContext } from '@polkadot/react-hooks'; import { isFunction } from '@polkadot/util'; import translate from '../translate'; @@ -22,7 +20,6 @@ import translate from '../translate'; interface Props extends I18nProps { isCollapsed: boolean; onClick: () => void; - allAccounts?: SubjectInfo; route: Route; sudoKey?: AccountId; } @@ -77,18 +74,14 @@ function checkVisible (name: string, { api, isApiReady, isApiConnected }: ApiPro return notFound.length === 0; } -function Item ({ allAccounts, route: { Modal, display, i18n, icon, name }, t, isCollapsed, onClick, sudoKey }: Props): React.ReactElement | null { +function Item ({ route: { Modal, display, i18n, icon, name }, t, isCollapsed, onClick, sudoKey }: Props): React.ReactElement | null { + const { allAccounts, hasAccounts } = useAccounts(); const apiProps = useApiContext(); - const [hasAccounts, setHasAccounts] = useState(false); const [hasSudo, setHasSudo] = useState(false); const [isVisible, setIsVisible] = useState(false); useEffect((): void => { - setHasAccounts(Object.keys(allAccounts || {}).length !== 0); - }, [allAccounts]); - - useEffect((): void => { - setHasSudo(!!sudoKey && Object.keys(allAccounts || {}).some((address): boolean => sudoKey.eq(address))); + setHasSudo(!!sudoKey && allAccounts.some((address): boolean => sudoKey.eq(address))); }, [allAccounts, sudoKey]); useEffect((): void => { @@ -149,6 +142,5 @@ export default withMulti( translate, withCalls( ['query.sudo.key', { propName: 'sudoKey' }] - ), - withObservable(accountObservable.subject, { propName: 'allAccounts' }) + ) ); diff --git a/packages/apps/src/overlays/Accounts.tsx b/packages/apps/src/overlays/Accounts.tsx index ac5b90a92b..54a4d8bba8 100644 --- a/packages/apps/src/overlays/Accounts.tsx +++ b/packages/apps/src/overlays/Accounts.tsx @@ -2,45 +2,27 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { I18nProps } from '@polkadot/react-components/types'; -import { ApiProps } from '@polkadot/react-api/types'; -import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; +import { I18nProps as Props } from '@polkadot/react-components/types'; -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { Link } from 'react-router-dom'; import styled from 'styled-components'; -import { withApi, withMulti, withObservable } from '@polkadot/react-api'; -import accountObservable from '@polkadot/ui-keyring/observable/accounts'; +import { useAccounts, useApiContext } from '@polkadot/react-hooks'; import translate from '../translate'; import BaseOverlay from './Base'; -interface Props extends I18nProps, ApiProps { - allAccounts?: SubjectInfo; -} - -function Accounts ({ allAccounts, isApiReady, className, t }: Props): React.ReactElement | null { - const [hasAccounts, setHasAccounts] = useState(false); +function Accounts ({ className, t }: Props): React.ReactElement | null { + const { hasAccounts } = useAccounts(); + const { isApiReady } = useApiContext(); const [isHidden, setIsHidden] = useState(false); - useEffect((): void => { - if (!allAccounts) { - return; - } - - const newHas = Object.keys(allAccounts).length !== 0; - - if (newHas !== hasAccounts) { - setHasAccounts(newHas); - } - }, [allAccounts]); - - const _onClose = (): void => setIsHidden(true); - if (!isApiReady || hasAccounts || isHidden) { return null; } + const _onClose = (): void => setIsHidden(true); + return ( | null { +function Inset ({ children, className, header, href, isCollapsible, isError, isSuccess, withBottomMargin, withTopMargin }: InsetProps): React.ReactElement | null { + const history = useHistory(); const [isCollapsed, setIsCollapsed] = useState(true); if (!children) { @@ -71,84 +71,82 @@ function Inset ({ children, className, header, history, href, isCollapsible, isE ); } -export default withRouter( - styled(Inset)` - & { - box-shadow: 0 3px 3px rgba(0,0,0,.2); - position: relative; - background: #fefefe; - padding: 1rem; - transition: all 0.2s; - display: flex; - flex-direction: column; +export default styled(Inset)` + & { + box-shadow: 0 3px 3px rgba(0,0,0,.2); + position: relative; + background: #fefefe; + padding: 1rem; + transition: all 0.2s; + display: flex; + flex-direction: column; - &.bottom-margin { - margin-bottom: 2rem; + &.bottom-margin { + margin-bottom: 2rem; + } + + &.top-margin { + margin-top: 2rem; + } + + &.error { + background: rgba(255, 0, 0, 0.05); + + &, h1, h2, h3, h4, h5, h6, p { + color: rgba(156, 0, 0) !important; + } + } + + &.success { + border: 1px solid rgb(168, 255, 136); + background: rgba(0, 255, 0, 0.05); + + &, h1, h2, h3, h4, h5, h6, p { + color: rgba(34, 125, 0) !important; + } + } + + .header { + cursor: pointer; + height: 2rem; + width: 100%; + + h3 { + line-height: 2rem; + margin-bottom: 0; } - &.top-margin { - margin-top: 2rem; - } + .icon { + height: 4rem; + width: 4rem; + font-size: 2rem; + color: rgba(0,0,0,0.35); + position: absolute; + right: 0; + top: 0; + line-height: 4rem; + transition: all 0.2s; + transform-origin: center center; - &.error { - background: rgba(255, 0, 0, 0.05); - - &, h1, h2, h3, h4, h5, h6, p { - color: rgba(156, 0, 0) !important; - } - } - - &.success { - border: 1px solid rgb(168, 255, 136); - background: rgba(0, 255, 0, 0.05); - - &, h1, h2, h3, h4, h5, h6, p { - color: rgba(34, 125, 0) !important; - } - } - - .header { - cursor: pointer; - height: 2rem; - width: 100%; - - h3 { - line-height: 2rem; - margin-bottom: 0; - } - - .icon { - height: 4rem; - width: 4rem; - font-size: 2rem; - color: rgba(0,0,0,0.35); - position: absolute; - right: 0; - top: 0; - line-height: 4rem; - transition: all 0.2s; - transform-origin: center center; - - &.collapsed { - transform: rotate(180deg); - } - } - } - - .children { &.collapsed { - display: none; - } - } - - &.as-link { - cursor: pointer; - - &:hover { - box-shadow: 0 5px 5px rgba(0,0,0,.2); - transform: translateY(-2px); + transform: rotate(180deg); } } } - ` -); + + .children { + &.collapsed { + display: none; + } + } + + &.as-link { + cursor: pointer; + + &:hover { + box-shadow: 0 5px 5px rgba(0,0,0,.2); + transform: translateY(-2px); + } + } + } +`; diff --git a/packages/react-hooks/src/index.ts b/packages/react-hooks/src/index.ts index 55f1cb88cf..00cb074109 100644 --- a/packages/react-hooks/src/index.ts +++ b/packages/react-hooks/src/index.ts @@ -2,7 +2,10 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -export { default as trackStream } from './trackStream'; +export { default as trackPromise } from './track/trackPromise'; +export { default as trackStream } from './track/trackStream'; +export { default as useAccounts } from './useAccounts'; +export { default as useAddresses } from './useAddresses'; export { default as useApiContext } from './useApiContext'; export { default as useDebounce } from './useDebounce'; export { default as useFavorites } from './useFavorites'; diff --git a/packages/react-hooks/src/track/trackPromise.tsx b/packages/react-hooks/src/track/trackPromise.tsx new file mode 100644 index 0000000000..34e5cba0e5 --- /dev/null +++ b/packages/react-hooks/src/track/trackPromise.tsx @@ -0,0 +1,58 @@ +// Copyright 2017-2019 @polkadot/react-hooks authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Options, Params } from './types'; + +import { useEffect, useRef, useState } from 'react'; + +import { extractParams, transformIdentity } from './util'; + +interface TrackFn { + (a: any, b: any, c: any): Promise; + (a: any, b: any): Promise; + (a: any): Promise; + (): Promise; +} + +// tracks a promise, typically an api.* call that +// - returns a promise with the value +// FIXME The typings here need some serious TLC +export default function trackPromise (fn: TrackFn | undefined, params: any, { paramMap = transformIdentity, transform = transformIdentity }: Options = {}): T | undefined { + const [value, setValue] = useState(); + const tracker = useRef<{ serialized: string | null }>({ serialized: null }); + + const _subscribe = (params: Params): void => { + setImmediate((): void => { + if (fn) { + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore We tried to get the typings right, close but no cigar... + fn(...params).then((value: any): void => setValue(transform(value))); + } + }); + }; + + // initial round, subscribe once + useEffect((): void => { + const [serialized, mappedParams] = extractParams(params, paramMap); + + tracker.current.serialized = serialized; + + if (mappedParams) { + _subscribe(mappedParams); + } + }, []); + + // on changes, re-get + useEffect((): void => { + const [serialized, mappedParams] = extractParams(params, paramMap); + + if (mappedParams && serialized !== tracker.current.serialized) { + tracker.current.serialized = serialized; + + _subscribe(mappedParams); + } + }, [params]); + + return value; +} diff --git a/packages/react-hooks/src/trackStream.tsx b/packages/react-hooks/src/track/trackStream.tsx similarity index 62% rename from packages/react-hooks/src/trackStream.tsx rename to packages/react-hooks/src/track/trackStream.tsx index 5c84de8fde..16d92fe3e7 100644 --- a/packages/react-hooks/src/trackStream.tsx +++ b/packages/react-hooks/src/track/trackStream.tsx @@ -2,15 +2,11 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Options, Params } from './types'; + import { useEffect, useRef, useState } from 'react'; -import { isNull, isUndefined } from '@polkadot/util'; -type Params = [] | [any] | [any, any] | [any, any, any]; - -interface Options { - paramMap?: (params: any) => any; - transform?: (value: any) => T; -} +import { dummyPromise, extractParams, transformIdentity } from './util'; interface TrackFnCallback { (value: T): void; @@ -25,41 +21,28 @@ interface TrackFn { (cb: TrackFnCallback): Promise; } -const dummySubscribe = Promise.resolve((): void => { - // we do nothing, this is a noop -}); - -function transformIdentity (value: any): any { - return value; -} - -// extract the serialized and mapped params, all ready for use in our call -function extractParams (params: any[], paramMap: (params: any[]) => any): [string, Params | null] { - return [ - JSON.stringify({ a: params }), - params.length === 0 || !params.some((param): boolean => isNull(param) || isUndefined(null)) - ? paramMap(params) - : null - ]; -} - // tracks a stream, typically an api.* call that // - returns a promise with an unsubscription // - has a callback to set the value // FIXME The typings here need some serious TLC export default function trackStream (fn: TrackFn | undefined, params: any, { paramMap = transformIdentity, transform = transformIdentity }: Options = {}): T | undefined { const [value, setValue] = useState(); - const tracker = useRef<{ serialized: string | null; subscriber: Promise }>({ serialized: null, subscriber: dummySubscribe }); + const tracker = useRef<{ serialized: string | null; subscriber: Promise }>({ serialized: null, subscriber: dummyPromise }); - const _subscribe = (params: Params): void => { - tracker.current.subscriber = fn - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore - // @ts-ignore We tried to get the typings right, close but no cigar... - ? fn(...params, (value: any): T => setValue(transform(value))) - : dummySubscribe; - }; const _unsubscribe = (): void => { tracker.current.subscriber.then((fn): void => fn()); + tracker.current.subscriber = dummyPromise; + }; + const _subscribe = (params: Params): void => { + _unsubscribe(); + + setImmediate((): void => { + tracker.current.subscriber = fn + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore We tried to get the typings right, close but no cigar... + ? fn(...params, (value: any): void => setValue(transform(value))) + : dummyPromise; + }); }; // initial round, subscribe once @@ -81,7 +64,7 @@ export default function trackStream (fn: TrackFn | undefined, params: a if (mappedParams && serialized !== tracker.current.serialized) { tracker.current.serialized = serialized; - _unsubscribe(); + _subscribe(mappedParams); } }, [params]); diff --git a/packages/react-hooks/src/track/types.ts b/packages/react-hooks/src/track/types.ts new file mode 100644 index 0000000000..46e3fc167d --- /dev/null +++ b/packages/react-hooks/src/track/types.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/react-hooks authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export type Params = [] | [any] | [any, any] | [any, any, any]; + +export interface Options { + paramMap?: (params: any) => any; + transform?: (value: any) => T; +} diff --git a/packages/react-hooks/src/track/util.ts b/packages/react-hooks/src/track/util.ts new file mode 100644 index 0000000000..adfd317234 --- /dev/null +++ b/packages/react-hooks/src/track/util.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/react-hooks authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Params } from './types'; + +import { isNull, isUndefined } from '@polkadot/util'; + +export const dummyPromise = Promise.resolve((): void => { + // we do nothing, this is a noop +}); + +export function transformIdentity (value: any): any { + return value; +} + +// extract the serialized and mapped params, all ready for use in our call +export function extractParams (params: any[], paramMap: (params: any[]) => any): [string, Params | null] { + return [ + JSON.stringify({ a: params }), + params.length === 0 || !params.some((param): boolean => isNull(param) || isUndefined(null)) + ? paramMap(params) + : null + ]; +} diff --git a/packages/react-hooks/src/useAccounts.tsx b/packages/react-hooks/src/useAccounts.tsx new file mode 100644 index 0000000000..87181ddb1b --- /dev/null +++ b/packages/react-hooks/src/useAccounts.tsx @@ -0,0 +1,30 @@ +// Copyright 2017-2019 @polkadot/react-hooks authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { useEffect, useState } from 'react'; +import accountObservable from '@polkadot/ui-keyring/observable/accounts'; + +interface UseAccounts { + allAccounts: string[]; + hasAccounts: boolean; +} + +export default function useAccounts (): UseAccounts { + const [state, setState] = useState({ allAccounts: [], hasAccounts: false }); + + useEffect((): () => void => { + const subscription = accountObservable.subject.subscribe((accounts): void => { + const allAccounts = accounts ? Object.keys(accounts) : []; + const hasAccounts = allAccounts.length !== 0; + + setState({ allAccounts, hasAccounts }); + }); + + return (): void => { + subscription.unsubscribe(); + }; + }, []); + + return state; +} diff --git a/packages/react-hooks/src/useAddresses.tsx b/packages/react-hooks/src/useAddresses.tsx new file mode 100644 index 0000000000..8b8a89ffdc --- /dev/null +++ b/packages/react-hooks/src/useAddresses.tsx @@ -0,0 +1,30 @@ +// Copyright 2017-2019 @polkadot/react-hooks authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { useEffect, useState } from 'react'; +import addressObservable from '@polkadot/ui-keyring/observable/addresses'; + +interface UseAccounts { + allAddresses: string[]; + hasAddresses: boolean; +} + +export default function useAccounts (): UseAccounts { + const [state, setState] = useState({ allAddresses: [], hasAddresses: false }); + + useEffect((): () => void => { + const subscription = addressObservable.subject.subscribe((addresses): void => { + const allAddresses = addresses ? Object.keys(addresses) : []; + const hasAddresses = allAddresses.length !== 0; + + setState({ allAddresses, hasAddresses }); + }); + + return (): void => { + subscription.unsubscribe(); + }; + }, []); + + return state; +} diff --git a/packages/react-hooks/src/useSessionRewards.tsx b/packages/react-hooks/src/useSessionRewards.tsx index 096ce0df36..3f8ec08f3f 100644 --- a/packages/react-hooks/src/useSessionRewards.tsx +++ b/packages/react-hooks/src/useSessionRewards.tsx @@ -113,47 +113,44 @@ async function loadSome (api: ApiPromise, fromHash: Hash, toHash: Hash): Promise export default function useSessionRewards (maxSessions: number): SessionRewards[] { const { api } = useApiContext(); const [getCache, setCache] = useCacheKey('hooks:sessionSlashes'); - const [results, setResults] = useState(fromJSON(getCache() || [])); const [filtered, setFiltered] = useState([]); useEffect((): void => { - let workQueue = results; + let workQueue = fromJSON(getCache() || []); - api.isReady.then(async (): Promise => { - const maxSessionsStore = maxSessions + 1; // assuming first is a bust - const sessionLength = (api.consts.babe?.epochDuration as BlockNumber || new BN(500)); - const count = Math.min(sessionLength.muln(maxSessionsStore).divn(10).toNumber(), 10000); - const bestHeader = await api.rpc.chain.getHeader(); - let toHash = bestHeader.hash; - let toNumber = bestHeader.number.unwrap().toBn(); - let fromHash = api.genesisHash; - let fromNumber = bnMax(toNumber.subn(count), new BN(1)); + setImmediate((): void => { + api.isReady.then(async (): Promise => { + const maxSessionsStore = maxSessions + 1; // assuming first is a bust + const sessionLength = (api.consts.babe?.epochDuration as BlockNumber || new BN(500)); + const count = Math.min(sessionLength.muln(maxSessionsStore).divn(10).toNumber(), 10000); + const bestHeader = await api.rpc.chain.getHeader(); + let toHash = bestHeader.hash; + let toNumber = bestHeader.number.unwrap().toBn(); + let fromHash = api.genesisHash; + let fromNumber = bnMax(toNumber.subn(count), new BN(1)); - while (true) { - fromHash = await api.rpc.chain.getBlockHash(fromNumber as any); + while (true) { + fromHash = await api.rpc.chain.getBlockHash(fromNumber as any); - const newQueue = await loadSome(api, fromHash, toHash); + const newQueue = await loadSome(api, fromHash, toHash); - workQueue = mergeResults(workQueue, newQueue); - toHash = fromHash; - toNumber = fromNumber; - fromNumber = bnMax(toNumber.subn(count), new BN(1)); + workQueue = mergeResults(workQueue, newQueue); + toHash = fromHash; + toNumber = fromNumber; + fromNumber = bnMax(toNumber.subn(count), new BN(1)); - setCache(toJSON(workQueue, maxSessionsStore)); - setResults(workQueue); + setCache(toJSON(workQueue, maxSessionsStore)); + setFiltered(workQueue.slice(-maxSessions)); - const lastNumber = workQueue[workQueue.length - 1]?.blockNumber; + const lastNumber = workQueue[workQueue.length - 1]?.blockNumber; - if (!lastNumber || fromNumber.eqn(1) || ((workQueue.length >= maxSessionsStore) && fromNumber.lt(lastNumber))) { - break; + if (!lastNumber || fromNumber.eqn(1) || ((workQueue.length >= maxSessionsStore) && fromNumber.lt(lastNumber))) { + break; + } } - } + }); }); }, []); - useEffect((): void => { - setFiltered(results.slice(-maxSessions)); - }, [results]); - return filtered; }