diff --git a/packages/app-staking/src/Actions/Account/index.tsx b/packages/app-staking/src/Actions/Account/index.tsx index 4edf884f4f..68c76f73fa 100644 --- a/packages/app-staking/src/Actions/Account/index.tsx +++ b/packages/app-staking/src/Actions/Account/index.tsx @@ -122,7 +122,7 @@ class Account extends React.PureComponent { public render (): React.ReactNode { const { className, isSubstrateV2, t } = this.props; - const { stashId } = this.state; + const { controllerId, hexSessionId, isBondExtraOpen, isStashValidating, isUnbondOpen, nominees, sessionIds, stashId } = this.state; if (!stashId) { return null; @@ -148,100 +148,79 @@ class Account extends React.PureComponent { unlocking: false }} > - {this.renderBondExtra()} + + {this.renderSetValidatorPrefs()} {this.renderNominate()} {this.renderSetControllerAccount()} {this.renderSetRewardDestination()} {this.renderSetSessionAccount()} - {this.renderUnbond()} {this.renderValidate()}
{this.renderControllerAccount()} - {!isSubstrateV2 && this.renderSessionAccount()} + {!isSubstrateV2 && sessionIds.length && ( +
+ +
+ )}
- {this.renderInfos()} +
- {this.renderNominee()} + {nominees && nominees.length && ( +
+ + {nominees.map((nomineeId, index): React.ReactNode => ( + + ))} +
+ )}
); } - private renderBondExtra (): React.ReactNode { - const { controllerId, isBondExtraOpen, stashId } = this.state; - - return ( - - ); - } - - private renderUnbond (): React.ReactNode { - const { controllerId, isUnbondOpen, stashId } = this.state; - - return ( - - ); - } - - private renderInfos (): React.ReactNode { - const { isSubstrateV2 } = this.props; - const { hexSessionId, isStashValidating, stashId } = this.state; - - return ( - - ); - } - - private renderNominee (): React.ReactNode { - const { t } = this.props; - const { nominees } = this.state; - - if (!nominees || !nominees.length) { - return null; - } - - return ( -
- - {nominees.map((nomineeId, index): React.ReactNode => ( - - ))} -
- ); - } - private renderOnlineStatus (): React.ReactNode { const { onlineStatus, controllerId } = this.state; @@ -285,32 +264,6 @@ class Account extends React.PureComponent { ); } - private renderSessionAccount (): React.ReactNode { - const { t } = this.props; - const { sessionIds } = this.state; - - if (!sessionIds.length) { - return null; - } - - return ( -
- -
- ); - } - private renderNominate (): React.ReactNode { const { stashOptions } = this.props; const { controllerId, isNominateOpen, nominees, stashId } = this.state; diff --git a/packages/app-storage/src/Query.tsx b/packages/app-storage/src/Query.tsx index 1875b4553f..27c601b1e5 100644 --- a/packages/app-storage/src/Query.tsx +++ b/packages/app-storage/src/Query.tsx @@ -5,7 +5,7 @@ import { I18nProps } from '@polkadot/react-components/types'; import { QueryTypes, StorageEntryPromise, StorageModuleQuery } from './types'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import { Compact } from '@polkadot/types'; import { Button, Labelled } from '@polkadot/react-components'; @@ -22,12 +22,6 @@ interface Props extends I18nProps { value: QueryTypes; } -interface State { - inputs?: React.ReactNode[]; - Component?: React.ComponentType<{}>; - spread: Record; -} - interface CacheInstance { Component: React.ComponentType; render: RenderFn; @@ -123,102 +117,95 @@ function getCachedComponent (query: QueryTypes): CacheInstance { return cache[id]; } -class Query extends React.PureComponent { - public state: State = { spread: {} }; +function Query ({ className, onRemove, value }: Props): React.ReactElement | null { + // const [inputs, setInputs] = useState([]); + const [{ Component }, setComponent] = useState>({}); + const [isSpreadable, setIsSpreadable] = useState(false); + const [spread, setSpread] = useState>({}); - public static getDerivedStateFromProps ({ value }: Props): Pick { - const Component = getCachedComponent(value).Component; - const inputs: React.ReactNode[] = isU8a(value.key) - ? [] - // FIXME We need to render the actual key params - // const { key, params } = value; - // const inputs = key.params.map(({ name, type }, index) => ( - // - // {name}={valueToText(type, params[index].value)} - // - // )); - : []; - - return { - Component, - inputs - }; - } - - public render (): React.ReactNode { - const { className, value } = this.props; - const { Component } = this.state; - const { id, isConst, key } = value; - const type = isConst - ? (key as unknown as ConstValue).meta.type.toString() - : isU8a(key) - ? 'Data' - : typeToString(key as StorageEntryPromise); - - if (!Component) { - return null; - } - - return ( -
-
- - {keyToName(isConst, key)}: {type} -
- } - > - - -
-
-
- {(key as StorageEntryPromise).creator && (key as StorageEntryPromise).creator.meta && ['Bytes', 'Data'].includes((key as StorageEntryPromise).creator.meta.type.toString()) && ( -
-
- + useEffect((): void => { + setComponent(getCachedComponent(value)); + // setInputs( + // isU8a(value.key) + // ? [] + // // FIXME We need to render the actual key params + // // const { key, params } = value; + // // const inputs = key.params.map(({ name, type }, index) => ( + // // + // // {name}={valueToText(type, params[index].value)} + // // + // // )); + // : [] + // ); + setIsSpreadable( + (value.key as StorageEntryPromise).creator && + (value.key as StorageEntryPromise).creator.meta && + ['Bytes', 'Data'].includes((value.key as StorageEntryPromise).creator.meta.type.toString()) ); + }, [value]); + + const { id, isConst, key } = value; + const type = isConst + ? (key as unknown as ConstValue).meta.type.toString() + : isU8a(key) + ? 'Data' + : typeToString(key as StorageEntryPromise); + + if (!Component) { + return null; } - private spreadHandler (id: number): () => void { + const _spreadHandler = (id: number): () => void => { return (): void => { - const { spread } = this.state; - cache[id].Component = cache[id].refresh(true, !!spread[id]); spread[id] = !spread[id]; - this.setState({ - ...this.state, - ...spread, - Component: cache[id].Component - }); + setComponent(cache[id]); + setSpread({ ...spread }); }; - } + }; + const _onRemove = (): void => { + delete cache[value.id]; - private onRemove = (): void => { - const { onRemove, value: { id } } = this.props; + onRemove(value.id); + }; - delete cache[id]; - - onRemove(id); - } + return ( +
+
+ + {keyToName(isConst, key)}: {type} +
+ } + > + + +
+
+
+ {isSpreadable && ( +
+
+ + ); } export default translate( - styled(Query as React.ComponentClass)` + styled(Query)` margin-bottom: 0.25em; label { diff --git a/packages/app-sudo/src/index.tsx b/packages/app-sudo/src/index.tsx index 60b81fdf11..fe018a11e9 100644 --- a/packages/app-sudo/src/index.tsx +++ b/packages/app-sudo/src/index.tsx @@ -4,14 +4,13 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { AppProps, I18nProps } from '@polkadot/react-components/types'; -import { ApiProps } from '@polkadot/react-api/types'; import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import { ComponentProps } from './types'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Route, Switch } from 'react-router'; import { Icon, Tabs } from '@polkadot/react-components'; -import { withApi, withCalls, withMulti, withObservable } from '@polkadot/react-api'; +import { withCalls, withMulti, withObservable } from '@polkadot/react-api'; import accountObservable from '@polkadot/ui-keyring/observable/accounts'; import SetKey from './SetKey'; @@ -19,70 +18,23 @@ import Sudo from './Sudo'; import translate from './translate'; -interface Props extends AppProps, ApiProps, I18nProps { +interface Props extends AppProps, I18nProps { allAccounts: SubjectInfo; - sudo_key?: string; + sudoKey?: string; } -interface State { - isMine: boolean; -} +function App ({ allAccounts, basePath, sudoKey, t }: Props): React.ReactElement { + const [isMine, setIsMine] = useState(false); -class App extends React.PureComponent { - public state: State = { - isMine: false - }; - - public static getDerivedStateFromProps ({ allAccounts = {}, sudo_key }: Props): State | null { - return { - isMine: !!sudo_key && !!Object.keys(allAccounts).find((key): boolean => key === sudo_key.toString()) - }; - } - - public render (): React.ReactNode { - const { basePath, t } = this.props; - const { isMine } = this.state; - - return ( -
-
- -
- {isMine ? ( - - - - - ) : ( -
-
- - {t('You do not have access to the current sudo key')} -
-
- )} -
+ useEffect((): void => { + setIsMine( + !!sudoKey && !!allAccounts && Object.keys(allAccounts).some((key): boolean => key === sudoKey) ); - } + }, [allAccounts, sudoKey]); - private renderComponent (Component: React.ComponentType): () => React.ReactNode { + const _renderComponent = (Component: React.ComponentType): () => React.ReactNode => { + // eslint-disable-next-line react/display-name return (): React.ReactNode => { - const { allAccounts = {}, sudo_key: sudoKey = '' } = this.props; - const { isMine } = this.state; - return ( { /> ); }; - } + }; + + return ( +
+
+ +
+ {isMine + ? ( + + + + + ) + : ( +
+
+ + {t('You do not have access to the current sudo key')} +
+
+ ) + } +
+ ); } export default withMulti( App, translate, - withApi, withCalls( ['query.sudo.key', { + propName: 'sudoKey', transform: (key): string => key.toString() }] diff --git a/packages/app-toolbox/src/Hash.tsx b/packages/app-toolbox/src/Hash.tsx index c8937d20c1..d6ce3b259c 100644 --- a/packages/app-toolbox/src/Hash.tsx +++ b/packages/app-toolbox/src/Hash.tsx @@ -4,7 +4,7 @@ import { I18nProps as Props } from '@polkadot/react-components/types'; -import React from 'react'; +import React, { useState } from 'react'; import { Input, Output, Static } from '@polkadot/react-components'; import { hexToU8a, isHex, stringToU8a } from '@polkadot/util'; import { blake2AsHex } from '@polkadot/util-crypto'; @@ -17,59 +17,52 @@ interface State { isHexData: boolean; } -class Hash extends React.PureComponent { - public state: State = { +function Hash ({ t }: Props): React.ReactElement { + const [{ data, hash, isHexData }, setState] = useState({ data: '', hash: blake2AsHex(stringToU8a(''), 256), isHexData: false + }); + + const _onChangeData = (data: string): void => { + const isHexData = isHex(data); + + setState({ + data, + hash: blake2AsHex( + isHexData + ? hexToU8a(data) + : stringToU8a(data), + 256 + ), + isHexData + }); }; - public render (): React.ReactNode { - return ( -
- {this.renderInput()} - {this.renderOutput()} + return ( +
+
+ +
+
+
- ); - } - - public renderInput (): React.ReactNode { - const { t } = this.props; - const { data, isHexData } = this.state; - - return ( - <> -
- -
-
- -
- - ); - } - - public renderOutput (): React.ReactNode { - const { t } = this.props; - const { hash } = this.state; - - return (
{ withCopy />
- ); - } - - private onChangeData = (data: string): void => { - const isHexData = isHex(data); - const hash = blake2AsHex( - isHexData - ? hexToU8a(data) - : stringToU8a(data), - 256 - ); - - this.setState({ data, hash, isHexData }); - } +
+ ); } export default translate(Hash); diff --git a/packages/app-toolbox/src/Sign.tsx b/packages/app-toolbox/src/Sign.tsx index 19fd729a0b..22d83aa5d0 100644 --- a/packages/app-toolbox/src/Sign.tsx +++ b/packages/app-toolbox/src/Sign.tsx @@ -5,7 +5,7 @@ import { I18nProps } from '@polkadot/react-components/types'; import { KeyringPair } from '@polkadot/keyring/types'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import { withMulti } from '@polkadot/react-api'; import { Button, Input, InputAddress, Output, Static } from '@polkadot/react-components'; @@ -28,16 +28,21 @@ interface State { signature: string; } -class Sign extends React.PureComponent { - public state: State; - - public constructor (props: Props) { - super(props); +function Sign ({ className, t }: Props): React.ReactElement { + const [state, setState] = useState({ + currentPair: null, + data: '', + isHexData: false, + isLocked: false, + isUnlockVisible: false, + signature: '' + }); + useEffect((): void => { const pairs = keyring.getPairs(); const currentPair = pairs[0] || null; - this.state = { + setState({ currentPair, data: '', isHexData: false, @@ -46,83 +51,62 @@ class Sign extends React.PureComponent { : false, isUnlockVisible: false, signature: '' - }; - } + }); + }, []); - public render (): React.ReactNode { - const { className } = this.props; - const { isLocked } = this.state; + const _nextState = ({ currentPair = state.currentPair, data = state.data, isHexData = state.isHexData, isUnlockVisible = state.isUnlockVisible }: Partial): void => { + const isLocked = !currentPair || currentPair.isLocked; + let signature = ''; - return ( -
- {this.renderAccount()} -
- {this.renderInput()} - {this.renderSignature()} - - {this.renderUnlock()} -
-
- ); - } + if (!isLocked && currentPair) { + signature = u8aToHex( + currentPair.sign( + isHexData + ? hexToU8a(data) + : stringToU8a(data) + ) + ); + } - public renderAccount (): React.ReactNode { - const { t } = this.props; + setState({ + currentPair, + data, + isHexData, + isLocked, + isUnlockVisible, + signature + }); + }; - return ( + const _toggleUnlock = (): void => + _nextState({ isUnlockVisible: !state.isUnlockVisible }); + const _onChangeAccount = (accountId: string | null): void => + _nextState({ currentPair: keyring.getPair(accountId || '') }); + const _onChangeData = (data: string): void => + _nextState({ data, isHexData: isHex(data) }); + + const { currentPair, data, isHexData, isLocked, isUnlockVisible, signature } = state; + + return ( +
- ); - } - - public renderUnlockWarning (): React.ReactNode { - const { t } = this.props; - const { isLocked } = this.state; - - if (!isLocked) { - return null; - } - - return ( -
-
- {t('You need to unlock this account to be able to sign data.')}
- -
-
- ); - } - - public renderInput (): React.ReactNode { - const { t } = this.props; - const { data, isHexData } = this.state; - - return ( - <> +
@@ -138,122 +122,78 @@ class Sign extends React.PureComponent { } />
- - ); - } - - public renderSignature (): React.ReactNode { - const { t } = this.props; - const { signature } = this.state; - - return ( -
- +
+ +
+ + {isUnlockVisible && ( + + )}
- ); - } - - public renderUnlock (): React.ReactNode { - const { currentPair, isUnlockVisible } = this.state; - - if (!isUnlockVisible) { - return null; - } - - return ( - - ); - } - - private nextState = (newState: Partial): void => { - this.setState( - (prevState: State): State => { - const { currentPair = prevState.currentPair, data = prevState.data, isHexData = prevState.isHexData, isUnlockVisible = prevState.isUnlockVisible } = newState; - const isLocked = !currentPair || currentPair.isLocked; - let signature = ''; - - if (!isLocked && currentPair) { - signature = u8aToHex( - currentPair.sign( - isHexData - ? hexToU8a(data) - : stringToU8a(data) - ) - ); - } - - return { - currentPair, - data, - isHexData, - isLocked, - isUnlockVisible, - signature - }; - } - ); - } - - private toggleUnlock = (): void => { - const { isUnlockVisible } = this.state; - - this.nextState({ - isUnlockVisible: !isUnlockVisible - }); - } - - private onChangeAccount = (accountId: string | null): void => { - const currentPair = keyring.getPair(accountId || ''); - - this.nextState({ currentPair }); - } - - private onChangeData = (data: string): void => { - const isHexData = isHex(data); - - this.nextState({ data, isHexData }); - } +
+ ); } export default withMulti( styled(Sign)` - position: relative; - width: 100%; - height: 100%; - - .unlock-overlay { - position: absolute; + .toolbox--Sign-input { + position: relative; width: 100%; height: 100%; - top:0; - left:0; - background-color: #0f0e0e7a; - } - .unlock-overlay-warning { - display: flex; - align-items: center; - justify-content: center; - height:100%; - } + .unlock-overlay { + position: absolute; + width: 100%; + height: 100%; + top:0; + left:0; + background-color: #0f0e0e7a; + } - .unlock-overlay-content { - color:#fff; - text-align:center; + .unlock-overlay-warning { + display: flex; + align-items: center; + justify-content: center; + height:100%; + } - .ui--Button-Group { - text-align: center; + .unlock-overlay-content { + color:#fff; + text-align:center; + + .ui--Button-Group { + text-align: center; + } } } `, diff --git a/packages/app-toolbox/src/Verify.tsx b/packages/app-toolbox/src/Verify.tsx index d938e0410f..d1873e123e 100644 --- a/packages/app-toolbox/src/Verify.tsx +++ b/packages/app-toolbox/src/Verify.tsx @@ -63,11 +63,21 @@ class Verify extends React.PureComponent { public render (): React.ReactNode { const { t } = this.props; - const { cryptoOptions, cryptoType, data, isHexData } = this.state; + const { cryptoOptions, cryptoType, data, defaultPublicKey, isHexData, isValid, isValidAddress, isValidSignature, signature } = this.state; return (
- {this.renderAddress()} +
+ +
{ value={data} />
- {this.renderSignature()} +
+ + } + isError={!isValidSignature} + help={t('The signature as by the account being checked, supplied as a hex-formatted string.')} + label={t('the supplied signature')} + onChange={this.onChangeSignature} + value={signature} + /> +
{ ); } - private renderAddress (): React.ReactNode { - const { t } = this.props; - const { defaultPublicKey, isValidAddress } = this.state; - - return ( -
- -
- ); - } - - private renderSignature (): React.ReactNode { - const { t } = this.props; - const { isValid, isValidSignature, signature } = this.state; - - return ( -
- - } - isError={!isValidSignature} - help={t('The signature as by the account being checked, supplied as a hex-formatted string.')} - label={t('the supplied signature')} - onChange={this.onChangeSignature} - value={signature} - /> -
- ); - } - private nextState (newState: Partial): void { this.setState( (prevState: State): Pick => { diff --git a/packages/app-treasury/src/Overview/Proposals.tsx b/packages/app-treasury/src/Overview/Proposals.tsx index 68462df216..78f75f5f2a 100644 --- a/packages/app-treasury/src/Overview/Proposals.tsx +++ b/packages/app-treasury/src/Overview/Proposals.tsx @@ -7,7 +7,7 @@ import { ProposalIndex } from '@polkadot/types/interfaces'; import { I18nProps } from '@polkadot/react-components/types'; import BN from 'bn.js'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { RouteComponentProps } from 'react-router'; import { withRouter } from 'react-router-dom'; import { withCalls, withMulti } from '@polkadot/react-api'; @@ -22,98 +22,68 @@ interface Props extends I18nProps, RouteComponentProps<{}> { treasury_proposalCount?: BN; } -interface State { - isEmpty: boolean; - isProposeOpen: boolean; - proposalIndices: BN[]; -} +function ProposalsBase ({ history, isApprovals = false, treasury_approvals, treasury_proposalCount, t }: Props): React.ReactElement { + const [isEmpty, setIsEmpty] = useState(true); + const [proposalIndices, setProposalIndices] = useState([]); -class ProposalsBase extends React.PureComponent { - public state: State = { - isEmpty: true, - isProposeOpen: false, - proposalIndices: [] as BN[] - }; - - public static getDerivedStateFromProps ({ isApprovals = false, treasury_approvals = [] as BN[], treasury_proposalCount = new BN(0) }: Props): Pick { + useEffect((): void => { let proposalIndices: BN[] = []; if (isApprovals) { - proposalIndices = treasury_approvals; - } else { + proposalIndices = treasury_approvals || []; + } else if (treasury_proposalCount && treasury_approvals) { for (let i = 0; i < treasury_proposalCount.toNumber(); i++) { if (!treasury_approvals.find((index): boolean => index.eqn(i))) { proposalIndices.push(new BN(i)); } } } - return { proposalIndices }; - } - public render (): React.ReactNode { - const { isApprovals, t } = this.props; - const { isEmpty } = this.state; - - return ( - <> - - {this.renderProposals()} - - - ); - } - - private renderProposals (): React.ReactNode { - const { isApprovals } = this.props; - const { proposalIndices } = this.state; - - return proposalIndices.map((proposalId): React.ReactNode => ( - - )); - } - - protected onRespond = (): void => { - const { history } = this.props; + setProposalIndices(proposalIndices); + }, [isApprovals, treasury_approvals, treasury_approvals]); + const _onRespond = (): void => { history.push('/council/motions'); - } + }; + const _onPopulateProposal = (): void => { + isEmpty && setIsEmpty(false); + }; - private onPopulateProposal = (): void => { - this.setState(({ isEmpty }: State): Pick | null => { - if (isEmpty) { - return { isEmpty: false }; - } - - return null; - }); - } + return ( + <> + + {proposalIndices.map((proposalId): React.ReactNode => ( + + ))} + + + ); } const Proposals = withMulti( withRouter(ProposalsBase), translate, withCalls( - [ - 'query.treasury.approvals', - { - transform: (value: ProposalIndex[]): BN[] => - value.map((proposalId): BN => new BN(proposalId)) - } - ], + ['query.treasury.approvals', { + transform: (value: ProposalIndex[]): BN[] => + value.map((proposalId): BN => new BN(proposalId)) + }], 'query.treasury.proposalCount' ) ); export default Proposals; -export const Approvals = (): JSX.Element => ; +export function Approvals (): React.ReactElement<{}> { + return ; +} diff --git a/packages/apps/src/SideBar/Item.tsx b/packages/apps/src/SideBar/Item.tsx index 5e05017479..45ebe04838 100644 --- a/packages/apps/src/SideBar/Item.tsx +++ b/packages/apps/src/SideBar/Item.tsx @@ -3,93 +3,40 @@ // 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 { Route } from '@polkadot/apps-routing/types'; -import React from 'react'; +import React, { useContext } from 'react'; import { NavLink } from 'react-router-dom'; import { Icon, Menu, Tooltip } from '@polkadot/react-components'; import accountObservable from '@polkadot/ui-keyring/observable/accounts'; -import { withApi, withCalls, withMulti, withObservable } from '@polkadot/react-api'; +import { ApiContext, withCalls, withMulti, withObservable } from '@polkadot/react-api'; import { isFunction } from '@polkadot/util'; import translate from '../translate'; -interface Props extends I18nProps, ApiProps { +interface Props extends I18nProps { isCollapsed: boolean; onClick: () => void; allAccounts?: SubjectInfo; route: Route; - sudo_key: string; + sudoKey: string; } -class Item extends React.PureComponent { - private _disabledLog: Map = new Map(); +const disabledLog: Map = new Map(); - public render (): React.ReactNode { - const { route: { Modal, i18n, icon, name }, t, isCollapsed, onClick } = this.props; +function logDisabled (route: string, message: string): void { + if (!disabledLog.get(route)) { + disabledLog.set(route, message); - if (!this.isVisible()) { - return null; - } - - const body = ( - <> - - {t(`sidebar.${name}`, i18n)} - - - ); - - return ( - - { - Modal - ? ( - - {body} - - ) - : ( - - {body} - - ) - } - - ); + console.warn(`Disabling ${route}: ${message}`); } +} - private logDisabled (route: string, message: string): void { - if (!this._disabledLog.get(route)) { - this._disabledLog.set(route, message); +function Item ({ allAccounts, route: { Modal, display: { isHidden, needsAccounts, needsApi, needsSudo }, i18n, icon, name }, t, isCollapsed, onClick, sudoKey }: Props): React.ReactElement | null { + const { api, isApiConnected, isApiReady } = useContext(ApiContext); - console.warn(`Disabling ${route}: ${message}`); - } - } - - private hasApi (endpoint: string): boolean { - const { api } = this.props; + const _hasApi = (endpoint: string): boolean => { const [area, section, method] = endpoint.split('.'); try { @@ -97,12 +44,10 @@ class Item extends React.PureComponent { } catch (error) { return false; } - } - - private isVisible (): boolean { - const { allAccounts = {}, isApiConnected, isApiReady, route: { display: { isHidden, needsAccounts, needsApi, needsSudo }, name }, sudo_key: sudoKey } = this.props; - const hasAccounts = Object.keys(allAccounts).length !== 0; - const hasSudo = !!Object.keys(allAccounts).find((address): boolean => address === sudoKey); + }; + const _isVisible = (): boolean => { + const hasAccounts = !!allAccounts && Object.keys(allAccounts).length !== 0; + const hasSudo = !!allAccounts && Object.keys(allAccounts).some((address): boolean => address === sudoKey); if (isHidden) { return false; @@ -114,33 +59,81 @@ class Item extends React.PureComponent { return false; } else if (needsSudo) { if (!hasSudo) { - this.logDisabled('sudo', 'Sudo key not available'); + logDisabled(name, 'Sudo key not available'); return false; } } const notFound = needsApi.filter((endpoint: string | string[]): boolean => { const hasApi = Array.isArray(endpoint) - ? endpoint.reduce((hasApi, endpoint): boolean => hasApi || this.hasApi(endpoint), false) - : this.hasApi(endpoint); + ? endpoint.reduce((hasApi, endpoint): boolean => hasApi || _hasApi(endpoint), false) + : _hasApi(endpoint); return !hasApi; }); if (notFound.length !== 0) { - this.logDisabled(name, `API not available: ${notFound}`); + logDisabled(name, `API not available: ${notFound}`); } return notFound.length === 0; + }; + + if (!_isVisible()) { + return null; } + + const body = ( + <> + + {t(`sidebar.${name}`, i18n)} + + + ); + + return ( + + {Modal + ? ( + + {body} + + ) + : ( + + {body} + + ) + } + + ); } export default withMulti( Item, translate, - withApi, withCalls( ['query.sudo.key', { + propName: 'sudoKey', transform: (key): string => key.toString() }] diff --git a/packages/apps/src/SideBar/index.tsx b/packages/apps/src/SideBar/index.tsx index 574e3cf74f..c78825930f 100644 --- a/packages/apps/src/SideBar/index.tsx +++ b/packages/apps/src/SideBar/index.tsx @@ -1,20 +1,17 @@ -/* eslint-disable @typescript-eslint/camelcase */ // Copyright 2017-2019 @polkadot/apps 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 { Route } from '@polkadot/apps-routing/types'; -import { ApiProps } from '@polkadot/react-api/types'; import { I18nProps } from '@polkadot/react-components/types'; import { SIDEBAR_MENU_THRESHOLD } from '../constants'; import './SideBar.css'; -import React from 'react'; +import React, { useContext, useState } from 'react'; import styled from 'styled-components'; import { Responsive } from 'semantic-ui-react'; import routing from '@polkadot/apps-routing'; -import { withApi, withMulti } from '@polkadot/react-api'; +import { ApiContext } from '@polkadot/react-api'; import { Button, ChainImg, Icon, Menu, media } from '@polkadot/react-components'; import { classes } from '@polkadot/react-components/util'; import { BestNumber, Chain } from '@polkadot/react-query'; @@ -24,7 +21,7 @@ import Item from './Item'; import NodeInfo from './NodeInfo'; import NetworkModal from '../modals/Network'; -interface Props extends ApiProps, I18nProps { +interface Props extends I18nProps { className?: string; collapse: () => void; handleResize: () => void; @@ -33,222 +30,137 @@ interface Props extends ApiProps, I18nProps { toggleMenu: () => void; } -interface State { - modals: Record; -} +function SideBar ({ className, collapse, handleResize, isCollapsed, toggleMenu, menuOpen }: Props): React.ReactElement { + const { api, isApiReady } = useContext(ApiContext); + const [modals, setModals] = useState>( + routing.routes.reduce((result: Record, route): Record => { + if (route && route.Modal) { + result[route.name] = false; + } -class SideBar extends React.PureComponent { - public state: State; + return result; + }, { network: false }) + ); - public constructor (props: Props) { - super(props); + const _toggleModal = (name: string): () => void => + (): void => setModals({ ...modals, [name]: !modals[name] }); - // setup modals for each of the actual modal routes - this.state = { - modals: routing.routes.reduce((result, route): Record => { - if (route && route.Modal) { - result[route.name] = false; - } - - return result; - }, { network: false } as unknown as Record) - }; - } - - public render (): React.ReactNode { - const { className, handleResize, isCollapsed, toggleMenu, menuOpen } = this.props; - - return ( - - - {this.renderModals()} - {this.state.modals.network && ( - - )} -
- -
- {this.renderLogo()} - {this.renderRoutes()} -
- {this.renderCollapse()} -
- + return ( + + + {routing.routes.map((route): React.ReactNode => ( + route && route.Modal + ? route.Modal && modals[route.name] + ? ( + + ) + :
+ : null + ))} + {modals.network && ( + + )} +
+ +
+ +
+ + {isApiReady && ( +
version {api.runtimeVersion.specVersion.toNumber()}
+ )} + +
+
+ {routing.routes.map((route, index): React.ReactNode => ( + route + ? ( + + ) + : ( +
+ +
- - ); - } - - private renderCollapse (): React.ReactNode { - const { isCollapsed } = this.props; - - return ( - -