From c3ce78821554eb9660370385f618ef929df09991 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 26 Sep 2019 17:16:56 +0200 Subject: [PATCH] More functional components (#1667) * More functional components * sudo setKey * Additional components --- packages/app-address-book/src/Address.tsx | 239 ++++++++---------- packages/app-settings/src/Developer.tsx | 205 +++++++-------- packages/app-staking/src/Overview/Address.tsx | 132 ++++------ .../app-staking/src/Overview/CurrentList.tsx | 105 +++----- packages/app-staking/src/Overview/Summary.tsx | 6 +- packages/app-staking/src/Overview/index.tsx | 14 +- packages/app-staking/src/index.css | 4 - packages/app-staking/src/index.tsx | 169 ++++++------- packages/app-staking/src/types.ts | 2 +- packages/app-sudo/src/SetKey.tsx | 172 ++++++------- .../app-treasury/src/Overview/Proposal.tsx | 116 +++------ 11 files changed, 490 insertions(+), 674 deletions(-) diff --git a/packages/app-address-book/src/Address.tsx b/packages/app-address-book/src/Address.tsx index ca70569cf1..583e14b5b6 100644 --- a/packages/app-address-book/src/Address.tsx +++ b/packages/app-address-book/src/Address.tsx @@ -6,7 +6,7 @@ import { KeyringAddress } from '@polkadot/ui-keyring/types'; import { ActionStatus } from '@polkadot/react-components/Status/types'; import { I18nProps } from '@polkadot/react-components/types'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import { AddressCard, AddressInfo, Button, ChainLock, Forget } from '@polkadot/react-components'; import keyring from '@polkadot/ui-keyring'; @@ -20,154 +20,121 @@ interface Props extends I18nProps { className?: string; } -interface State { - current?: KeyringAddress; - genesisHash: string | null; - isEditable: boolean; - isForgetOpen: boolean; - isTransferOpen: boolean; -} - const WITH_BALANCE = { available: true, free: true, total: true }; const WITH_EXTENDED = { nonce: true }; -class Address extends React.PureComponent { - public state: State; +const isEditable = true; - public constructor (props: Props) { - super(props); +function Address ({ address, className, t }: Props): React.ReactElement { + const [current, setCurrent] = useState(null); + const [genesisHash, setGenesisHash] = useState(null); + const [isForgetOpen, setIsForgetOpen] = useState(false); + const [isTransferOpen, setIsTransferOpen] = useState(false); - const { address } = this.props; + useEffect((): void => { const current = keyring.getAddress(address); - this.state = { - current, - genesisHash: (current && current.meta.genesisHash) || null, - isEditable: true, - isForgetOpen: false, - isTransferOpen: false - }; - } + setCurrent(current || null); + setGenesisHash((current && current.meta.genesisHash) || null); + }, []); - public render (): React.ReactNode { - const { address, className, t } = this.props; - const { current, genesisHash, isEditable, isForgetOpen, isTransferOpen } = this.state; + const _toggleForget = (): void => setIsForgetOpen(!isForgetOpen); + const _toggleTransfer = (): void => setIsTransferOpen(!isTransferOpen); + const _onForget = (): void => { + if (address) { + const status: Partial = { + account: address, + action: 'forget' + }; - return ( - -
- {isEditable && ( -
+ {isEditable && ( +
+
- {isEditable && ( -
- -
- )} - - } - className={className} - isEditable={isEditable} - type='address' - value={address} - withExplorer - withIndex - withTags - > - {address && current && ( - <> - {isForgetOpen && ( - - )} - {isTransferOpen && ( - - )} - - )} - -
- ); - } - - private toggleForget = (): void => { - this.setState(({ isForgetOpen }): Pick => ({ - isForgetOpen: !isForgetOpen - })); - } - - private toggleTransfer = (): void => { - this.setState(({ isTransferOpen }): Pick => ({ - isTransferOpen: !isTransferOpen - })); - } - - private onForget = (): void => { - const { address, t } = this.props; - - if (!address) { - return; - } - - const status: Partial = { - account: address, - action: 'forget' - }; - - try { - keyring.forgetAddress(address); - status.status = 'success'; - status.message = t('address forgotten'); - } catch (error) { - status.status = 'error'; - status.message = error.message; - } - } - - private onGenesisChange = (genesisHash: string | null): void => { - const { address } = this.props; - - this.setState({ genesisHash }, (): void => { - const account = keyring.getAddress(address); - - account && keyring.saveAddress(address, { ...account.meta, genesisHash }); - }); - } + )} + + } + className={className} + isEditable={isEditable} + type='address' + value={address} + withExplorer + withIndex + withTags + > + {address && current && ( + <> + {isForgetOpen && ( + + )} + {isTransferOpen && ( + + )} + + )} + + + ); } export default translate( diff --git a/packages/app-settings/src/Developer.tsx b/packages/app-settings/src/Developer.tsx index d22e4b7a49..b60fe27e86 100644 --- a/packages/app-settings/src/Developer.tsx +++ b/packages/app-settings/src/Developer.tsx @@ -4,7 +4,7 @@ import { AppProps, I18nProps } from '@polkadot/react-components/types'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import store from 'store'; import styled from 'styled-components'; import { getTypeRegistry } from '@polkadot/types'; @@ -15,96 +15,45 @@ import translate from './translate'; interface Props extends AppProps, I18nProps {} -interface State { - code: string; - isJsonValid: boolean; - isTypesValid: boolean; - types: Record | {}; - typesPlaceholder?: string; -} +const EMPTY_CODE = '{\n\n}'; +const EMPTY_TYPES = {}; -class Developer extends React.PureComponent { - private defaultCode = '{\n\n}'; - - public constructor (props: Props) { - super(props); +function Developer ({ className, onStatusChange, t }: Props): React.ReactElement { + const [code, setCode] = useState(EMPTY_CODE); + const [isJsonValid, setIsJsonValid] = useState(true); + const [isTypesValid, setIsTypesValid] = useState(true); + const [types, setTypes] = useState>(EMPTY_TYPES); + const [typesPlaceholder, setTypesPlaceholder] = useState(null); + useEffect((): void => { const types = store.get('types') || {}; - const names = Object.keys(types); - this.state = { - code: Object.keys(types).length ? JSON.stringify(types, null, 2) : this.defaultCode, - isJsonValid: true, - isTypesValid: true, - types: names.length ? types : {}, - typesPlaceholder: names.length - ? names.join(', ') - : undefined - }; - } + if (Object.keys(types).length) { + setCode(JSON.stringify(types, null, 2)); + setTypes({}); + setTypesPlaceholder(Object.keys(types).join(', ')); + } + }, []); - public render (): React.ReactNode { - const { className, t } = this.props; - const { code, isJsonValid, isTypesValid, types, typesPlaceholder } = this.state; - const typesHasNoEntries = Object.keys(types).length === 0; - - return ( -
-
-
- -
-
-
-
- -
-
- -
- ); - } - - private clearTypes = (): void => { + const _setState = ({ code, isJsonValid, isTypesValid, types, typesPlaceholder }: { code: string; isJsonValid: boolean; isTypesValid: boolean; types: Record; typesPlaceholder: string | null }): void => { + setCode(code); + setIsJsonValid(isJsonValid); + setIsTypesValid(isTypesValid); + setTypes(types); + setTypesPlaceholder(typesPlaceholder); + }; + const _clearTypes = (): void => { store.remove('types'); - this.setState({ - code: this.defaultCode, + + _setState({ + code: EMPTY_CODE, isJsonValid: true, isTypesValid: true, - types: {}, - typesPlaceholder: undefined + types: EMPTY_TYPES, + typesPlaceholder: null }); - } - - private onChangeTypes = (data: Uint8Array): void => { + }; + const _onChangeTypes = (data: Uint8Array): void => { const code = u8aToString(data); try { @@ -113,7 +62,7 @@ class Developer extends React.PureComponent { console.log('Detected types:', typesPlaceholder); - this.setState({ + _setState({ code, isJsonValid: true, isTypesValid: true, @@ -123,7 +72,7 @@ class Developer extends React.PureComponent { } catch (error) { console.error('Error registering types:', error); - this.setState({ + _setState({ code, isJsonValid: false, isTypesValid: false, @@ -131,56 +80,84 @@ class Developer extends React.PureComponent { typesPlaceholder: error.message }); } - } - - private onEditTypes = (code: string): void => { + }; + const _onEditTypes = (code: string): void => { try { if (!isJsonObject(code)) { - throw Error(this.props.t('This is not a valid JSON object.')); + throw Error(t('This is not a valid JSON object.')); } - this.setState((prevState: State): Pick => ({ - ...prevState, - code, - isJsonValid: true - })); - - this.onChangeTypes(stringToU8a(code)); + _onChangeTypes(stringToU8a(code)); } catch (e) { - this.setState((prevState: State): Pick => ({ - ...prevState, - code, - isJsonValid: false, - typesPlaceholder: e.message - })); + setCode(code); + setIsJsonValid(false); + setTypesPlaceholder(e.message); } - } - - private saveDeveloper = (): void => { - const { onStatusChange, t } = this.props; - const { types } = this.state; - + }; + const _saveDeveloper = (): void => { try { getTypeRegistry().register(types); - store.set('types', types); - - this.setState({ isTypesValid: true }); - + setIsTypesValid(true); onStatusChange({ status: 'success', action: t('Your custom types have been added') }); } catch (error) { console.error(error); - this.setState({ isTypesValid: false }); - + setIsTypesValid(false); onStatusChange({ status: 'error', action: t(`Error saving your custom types. ${error.message}`) }); } - } + }; + + const typesHasNoEntries = Object.keys(types).length === 0; + + return ( +
+
+
+ +
+
+
+
+ +
+
+ +
+ ); } export default translate( diff --git a/packages/app-staking/src/Overview/Address.tsx b/packages/app-staking/src/Overview/Address.tsx index 4b349cba7e..1864c4fb3e 100644 --- a/packages/app-staking/src/Overview/Address.tsx +++ b/packages/app-staking/src/Overview/Address.tsx @@ -82,8 +82,8 @@ class Address extends React.PureComponent { } public render (): React.ReactNode { - const { address, className, defaultName, filter } = this.props; - const { controllerId, stakers, stashId } = this.state; + const { address, className, defaultName, filter, isSubstrateV2, lastAuthor, lastBlock, t } = this.props; + const { controllerId, onlineStatus, sessionId, stakers, stashId } = this.state; const bonded = stakers && !stakers.own.isEmpty ? [stakers.own.unwrap(), stakers.total.unwrap().sub(stakers.own.unwrap())] : true; @@ -96,54 +96,66 @@ class Address extends React.PureComponent { return null; } + const isAuthor = !!lastBlock && !!lastAuthor && [address, controllerId, stashId].includes(lastAuthor); + const nominators = this.getNominators(); + return ( + {isAuthor && ( +
#{lastBlock}
+ )} + {controllerId && ( +
+ + +
+ )} + {!isSubstrateV2 && sessionId && ( +
+ + +
+ )} + + } className={className} defaultName={defaultName} - iconInfo={this.renderOnlineStatus()} + iconInfo={controllerId && onlineStatus && ( + + )} key={stashId || controllerId || undefined} value={stashId || address} withBalance={{ bonded }} > - {this.renderNominators()} + {nominators.length && ( +
+ + {t('Nominators ({{count}})', { + replace: { + count: nominators.length + } + })} + + {nominators.map(([who, bonded]): React.ReactNode => + + )} +
+ )}
); } - private renderKeys (): React.ReactNode { - const { address, isSubstrateV2, lastAuthor, lastBlock, t } = this.props; - const { controllerId, sessionId, stashId } = this.state; - const isAuthor = !!lastBlock && !!lastAuthor && [address, controllerId, stashId].includes(lastAuthor); - - return ( -
- {isAuthor - ?
#{lastBlock}
- : null - } - {controllerId - ? ( -
- - -
- ) - : null - } - {!isSubstrateV2 && sessionId - ? ( -
- - -
- ) - : null - } -
- ); - } - private getNominators (): [AccountId, Balance][] { const { stakers } = this.state; @@ -176,50 +188,6 @@ class Address extends React.PureComponent { return true; } - - private renderNominators (): React.ReactNode { - const { t } = this.props; - const nominators = this.getNominators(); - - if (!nominators.length) { - return null; - } - - return ( -
- - {t('Nominators ({{count}})', { - replace: { - count: nominators.length - } - })} - - {nominators.map(([who, bonded]): React.ReactNode => - - )} -
- ); - } - - private renderOnlineStatus (): React.ReactNode { - const { controllerId, onlineStatus } = this.state; - if (!controllerId || !onlineStatus) { - return null; - } - - return ( - - ); - } } export default withMulti( diff --git a/packages/app-staking/src/Overview/CurrentList.tsx b/packages/app-staking/src/Overview/CurrentList.tsx index 09a0b2ff5b..f4cf535f1d 100644 --- a/packages/app-staking/src/Overview/CurrentList.tsx +++ b/packages/app-staking/src/Overview/CurrentList.tsx @@ -6,81 +6,24 @@ import { BlockNumber } from '@polkadot/types/interfaces'; import { I18nProps } from '@polkadot/react-components/types'; import { ValidatorFilter } from '../types'; -import React from 'react'; +import React, { useState } from 'react'; import { Columar, Column, Dropdown, FilterOverlay } from '@polkadot/react-components'; import translate from '../translate'; import Address from './Address'; interface Props extends I18nProps { - currentValidatorsControllersV1OrStashesV2: string[]; + currentValidators: string[]; lastAuthor?: string; lastBlock: string; next: string[]; recentlyOnline: Record; } -interface State { - filter: ValidatorFilter; - filterOptions: { text: React.ReactNode; value: ValidatorFilter }[]; -} - -class CurrentList extends React.PureComponent { - public state: State; - - public constructor (props: Props) { - super(props); - - const { t } = props; - - this.state = { - filter: 'all', - filterOptions: [ - { text: t('Show all validators and intentions'), value: 'all' }, - { text: t('Show only my nominations'), value: 'iNominated' }, - { text: t('Show only with nominators'), value: 'hasNominators' }, - { text: t('Show only without nominators'), value: 'noNominators' }, - { text: t('Show only with warnings'), value: 'hasWarnings' }, - { text: t('Show only without warnings'), value: 'noWarnings' } - ] - }; - } - - public render (): React.ReactNode { - const { currentValidatorsControllersV1OrStashesV2, next, t } = this.props; - const { filter, filterOptions } = this.state; - return ( -
- - - - - - {this.renderColumn(currentValidatorsControllersV1OrStashesV2, t('validator'))} - - - {this.renderColumn(next, t('intention'))} - - -
- ); - } - - private renderColumn (addresses: string[], defaultName: string): React.ReactNode { - const { lastAuthor, lastBlock, recentlyOnline } = this.props; - const { filter } = this.state; +function CurrentList ({ currentValidators, lastAuthor, lastBlock, next, recentlyOnline, t }: Props): React.ReactElement { + const [filter, setFilter] = useState('all'); + const _renderColumn = (addresses: string[], defaultName: string): React.ReactNode => { return addresses.map((address): React.ReactNode => (
{ recentlyOnline={recentlyOnline} /> )); - } + }; - private onChangeFilter = (filter: ValidatorFilter): void => { - this.setState({ filter }); - } + return ( +
+ + + + + + {_renderColumn(currentValidators, t('validator'))} + + + {_renderColumn(next, t('intention'))} + + +
+ ); } export default translate(CurrentList); diff --git a/packages/app-staking/src/Overview/Summary.tsx b/packages/app-staking/src/Overview/Summary.tsx index d5608c8329..cbe0147f0c 100644 --- a/packages/app-staking/src/Overview/Summary.tsx +++ b/packages/app-staking/src/Overview/Summary.tsx @@ -17,7 +17,7 @@ import translate from '../translate'; interface Props extends I18nProps { allControllers: string[]; className?: string; - currentValidatorsControllersV1OrStashesV2: string[]; + currentValidators: string[]; lastAuthor?: string; lastBlock: string; staking_validatorCount?: BN; @@ -25,7 +25,7 @@ interface Props extends I18nProps { } function Summary (props: Props): React.ReactElement { - const { className, currentValidatorsControllersV1OrStashesV2, lastAuthor, lastBlock, next, style, staking_validatorCount, t } = props; + const { className, currentValidators, lastAuthor, lastBlock, next, style, staking_validatorCount, t } = props; return ( { >
- {currentValidatorsControllersV1OrStashesV2.length}/{staking_validatorCount ? staking_validatorCount.toString() : '-'} + {currentValidators.length}/{staking_validatorCount ? staking_validatorCount.toString() : '-'} {next.length} diff --git a/packages/app-staking/src/Overview/index.tsx b/packages/app-staking/src/Overview/index.tsx index 60ac14f1e6..b562b5bebf 100644 --- a/packages/app-staking/src/Overview/index.tsx +++ b/packages/app-staking/src/Overview/index.tsx @@ -22,18 +22,18 @@ interface Props extends BareProps, ComponentProps { // TODO: Switch to useState function Overview (props: Props): React.ReactElement { const { isSubstrateV2 } = useContext(ApiContext); - const { chain_subscribeNewHeads, allControllers, allStashes, currentValidatorsControllersV1OrStashesV2, recentlyOnline } = props; + const { chain_subscribeNewHeads, allControllers, allStashes, currentValidators, recentlyOnline } = props; let nextSorted: string[]; if (isSubstrateV2) { - // this is a V2 node currentValidatorsControllersV1OrStashesV2 is a list of stashes + // this is a V2 node currentValidators is a list of stashes nextSorted = allStashes.filter((address): boolean => - !currentValidatorsControllersV1OrStashesV2.includes(address) + !currentValidators.includes(address) ); } else { - // this is a V1 node currentValidatorsControllersV1OrStashesV2 is a list of controllers + // this is a V1 node currentValidators is a list of controllers nextSorted = allControllers.filter((address): boolean => - !currentValidatorsControllersV1OrStashesV2.includes(address) + !currentValidators.includes(address) ); } @@ -49,13 +49,13 @@ function Overview (props: Props): React.ReactElement {
[]]; - chain_bestNumber?: BlockNumber; + bestNumber?: BlockNumber; currentValidatorsControllersV1OrStashesV2?: AccountId[]; recentlyOnline?: AuthorityId[]; } @@ -36,84 +36,45 @@ interface Props extends AppProps, ApiProps, I18nProps { interface State { allControllers: string[]; allStashes: string[]; - currentValidatorsControllersV1OrStashesV2: string[]; + currentValidators: string[]; recentlyOnline: Record; } -class App extends React.PureComponent { - public state: State = { +function App ({ allAccounts, allStashesAndControllers = [[], []], bestNumber, className, currentValidatorsControllersV1OrStashesV2 = [], basePath, recentlyOnline: propsRecentlyOnline, t }: Props): React.ReactElement { + const [{ allControllers, allStashes, currentValidators, recentlyOnline }, setState] = useState({ allControllers: [], allStashes: [], - currentValidatorsControllersV1OrStashesV2: [], + currentValidators: [], recentlyOnline: {} - }; + }); - public static getDerivedStateFromProps (props: Props, state: State): Pick { - const { allStashesAndControllers = [[], []], chain_bestNumber, currentValidatorsControllersV1OrStashesV2 = [] } = props; - - const recentlyOnline: Record = { - ...(state.recentlyOnline || {}), - ...(props.recentlyOnline || []).reduce( - (result: Record, authorityId): Record => ({ - ...result, - [authorityId.toString()]: chain_bestNumber || createType('BlockNumber', new BN(0)) - }), - {} - ) - }; - - return { - allControllers: allStashesAndControllers[1].filter((optId): boolean => optId.isSome).map((accountId): string => - accountId.unwrap().toString() - ), - allStashes: allStashesAndControllers[0].filter((): boolean => true).map((accountId): string => accountId.toString()), - currentValidatorsControllersV1OrStashesV2: currentValidatorsControllersV1OrStashesV2.map((authorityId): string => + useEffect((): void => { + setState({ + allControllers: allStashesAndControllers[1] + .filter((optId): boolean => optId.isSome) + .map((accountId): string => accountId.unwrap().toString()), + allStashes: allStashesAndControllers[0] + .filter((): boolean => true) + .map((accountId): string => accountId.toString()), + currentValidators: currentValidatorsControllersV1OrStashesV2.map((authorityId): string => authorityId.toString() ), - recentlyOnline - }; - } + recentlyOnline: { + ...(recentlyOnline || {}), + ...(propsRecentlyOnline || []).reduce( + (result: Record, authorityId): Record => ({ + ...result, + [authorityId.toString()]: bestNumber || createType('BlockNumber', new BN(0)) + }), + {} + ) + } + }); + }, [allStashesAndControllers, bestNumber, currentValidatorsControllersV1OrStashesV2]); - public render (): React.ReactNode { - const { allAccounts, basePath, t } = this.props; - - return ( -
- -
-
- - - - -
- ); - } - - 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 { allControllers, allStashes, currentValidatorsControllersV1OrStashesV2, recentlyOnline } = this.state; - const { allAccounts } = this.props; - if (!allAccounts) { return null; } @@ -123,35 +84,67 @@ class App extends React.PureComponent { allAccounts={allAccounts} allControllers={allControllers} allStashes={allStashes} - currentValidatorsControllersV1OrStashesV2={currentValidatorsControllersV1OrStashesV2} + currentValidators={currentValidators} recentlyOnline={recentlyOnline} /> ); }; - } + }; + + return ( +
+ +
+
+ + + + +
+ ); } export default withMulti( - App, + styled(App)` + .rx--updated { + background: transparent !important; + } + `, translate, withCalls( - 'derive.chain.bestNumber', + ['derive.chain.bestNumber', { propName: 'bestNumber' }], ['derive.staking.controllers', { propName: 'allStashesAndControllers' }], ['query.session.validators', { propName: 'currentValidatorsControllersV1OrStashesV2' }], - [ - 'query.system.events', - { - propName: 'recentlyOnline', - transform: (value?: EventRecord[]): AuthorityId[] => - (value || []) - .filter(({ event: { method, section } }): boolean => - section === 'imOnline' && method === 'HeartbeatReceived' - ) - .map( - ({ event: { data: [authorityId] } }): AuthorityId => authorityId as AuthorityId - ) - } - ] + ['query.system.events', { + propName: 'recentlyOnline', + transform: (value?: EventRecord[]): AuthorityId[] => + (value || []) + .filter(({ event: { method, section } }): boolean => + section === 'imOnline' && method === 'HeartbeatReceived' + ) + .map(({ event: { data: [authorityId] } }): AuthorityId => + authorityId as AuthorityId + ) + }] ), withObservable(accountObservable.subject, { propName: 'allAccounts' }) ); diff --git a/packages/app-staking/src/types.ts b/packages/app-staking/src/types.ts index 43d373cf5d..5a5e3e8c12 100644 --- a/packages/app-staking/src/types.ts +++ b/packages/app-staking/src/types.ts @@ -14,7 +14,7 @@ export interface ComponentProps { allAccounts?: SubjectInfo; allControllers: string[]; allStashes: string[]; - currentValidatorsControllersV1OrStashesV2: string[]; + currentValidators: string[]; recentlyOnline: Record; } diff --git a/packages/app-sudo/src/SetKey.tsx b/packages/app-sudo/src/SetKey.tsx index ab10d0abfa..c2bfa8d8a0 100644 --- a/packages/app-sudo/src/SetKey.tsx +++ b/packages/app-sudo/src/SetKey.tsx @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { I18nProps } from '@polkadot/react-components/types'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { AddressMini, Icon, InputAddress, Labelled, TxButton } from '@polkadot/react-components'; import { ComponentProps } from './types'; @@ -13,110 +13,84 @@ import translate from './translate'; interface Props extends I18nProps, ComponentProps {} -interface State { - selected?: string; -} +function SetKey ({ allAccounts, className, isMine, sudoKey, t }: Props): React.ReactElement { + const [selected, setSelected] = useState(); -const SudoInputAddress = styled(InputAddress)` - margin: -0.25rem 0.5rem -0.25rem 0; -`; - -const SudoLabelled = styled(Labelled)` - align-items: center; -`; - -class SetKey extends React.PureComponent { - public state: State = {}; - - public constructor (props: Props) { - super(props); - - this.state = { - selected: props.sudoKey - }; - } - - public static getDerivedStateFromProps ({ sudoKey }: Props, state: State): Pick { - if (sudoKey && (!state.selected || sudoKey !== state.selected)) { - return { selected: sudoKey }; + useEffect((): void => { + if (sudoKey && !selected) { + setSelected(sudoKey); } - return {}; - } + }, [selected, sudoKey]); - public render (): React.ReactNode { - const { className, isMine, sudoKey, t } = this.props; - const { selected } = this.state; + const willLose = isMine && + !!Object.keys(allAccounts).length && + !!selected && + selected !== sudoKey && + !Object.keys(allAccounts).find((s): boolean => s === selected); - return ( -
-
- {isMine - ? ( - <> - - - - ) - : ( - +
+ {isMine + ? ( + <> + - - - ) - } -
- {this.willLose() && ( -
-
- - {t('You will no longer have sudo access')} -
-
- )} + isInput={true} + onChange={setSelected} + type='all' + /> + + + ) + : ( + + + + ) + }
- ); - } - - private onChange = (selected?: string): void => { - this.setState({ selected }); - } - - private willLose = (): boolean => { - const { allAccounts, isMine, sudoKey } = this.props; - const { selected } = this.state; - - return ( - isMine && - !!Object.keys(allAccounts).length && - !!selected && - selected !== sudoKey && - !Object.keys(allAccounts).find((s): boolean => s === selected) - ); - } + {willLose && ( +
+
+ + {t('You will no longer have sudo access')} +
+
+ )} +
+ ); } -export default translate(styled(SetKey as React.ComponentClass)` - align-items: flex-end; - justify-content: center; +export default translate( + styled(SetKey)` + align-items: flex-end; + justify-content: center; - .summary { - text-align: center; - } -`); + .summary { + text-align: center; + } + + .sudoInputAddress { + margin: -0.25rem 0.5rem -0.25rem 0; + } + + .sudoLabelled { + align-items: center; + } + ` +); diff --git a/packages/app-treasury/src/Overview/Proposal.tsx b/packages/app-treasury/src/Overview/Proposal.tsx index 0869f0e135..c50bbeab0b 100644 --- a/packages/app-treasury/src/Overview/Proposal.tsx +++ b/packages/app-treasury/src/Overview/Proposal.tsx @@ -6,7 +6,7 @@ import { TreasuryProposal as TreasuryProposalType } from '@polkadot/types/interf import { I18nProps } from '@polkadot/react-components/types'; import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; -import React from 'react'; +import React, { useEffect } from 'react'; import styled from 'styled-components'; import { Option } from '@polkadot/types'; import { ActionItem, Icon, TreasuryProposal } from '@polkadot/react-components'; @@ -30,83 +30,51 @@ interface Props extends I18nProps { onRespond: () => void; } -interface State { - isApproveOpen: boolean; -} +function ProposalDisplay ({ allAccounts, isApproved, onPopulate, onRespond, proposal, proposalId, t }: Props): React.ReactElement | null { + useEffect((): void => { + onPopulate(); + }, [proposal]); -class ProposalDisplay extends React.PureComponent { - public constructor (props: Props) { - super(props); - - const { proposal, onPopulate } = props; - if (proposal) { - onPopulate(); - } + if (!proposal) { + return null; } - public componentDidUpdate ({ proposal }: Props): void { - const { onPopulate } = this.props; + const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0; - if (this.props.proposal && !proposal) { - onPopulate(); - } - } - - public state: State = { - isApproveOpen: false - }; - - public render (): React.ReactNode { - const { proposal, proposalId } = this.props; - - if (!proposal) { - return null; - } - - return ( - - - - ); - } - - private renderAccessory (): React.ReactNode { - const { allAccounts, isApproved, onRespond, proposal, proposalId, t } = this.props; - - if (isApproved) { - return ( - - - {' '} - {t('Approved')} - - ); - } - - const hasAccounts = allAccounts && Object.keys(allAccounts).length !== 0; - if (!hasAccounts) { - return null; - } - - return ( - -

Proposal #{proposalId}

-
- -
-
- - } - proposalId={proposalId} - onSuccess={onRespond} - /> - ); - } + return ( + + + {' '} + {t('Approved')} + + ) + : hasAccounts + ? ( + +

Proposal #{proposalId}

+
+ +
+
+ + } + proposalId={proposalId} + onSuccess={onRespond} + /> + ) + : null + } + idNumber={proposalId} + > + +
+ ); } export default withMulti(