From b6f90bf4e4eee1503c2149031d98aae0411a88a6 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 28 Apr 2020 16:37:59 +0200 Subject: [PATCH] Columar modals (currently starting in staking) (#2649) --- .../src/Actions/Account/Nominate.tsx | 122 ++++++++++-------- .../page-staking/src/Actions/NewStake.tsx | 120 +++++++++++------ .../page-staking/src/Actions/useBondBlocks.ts | 25 ++++ .../page-staking/src/Targets/Nominate.tsx | 63 +++++---- .../react-components/src/Modal/Actions.tsx | 28 ++++ .../react-components/src/Modal/Column.tsx | 41 ++++++ .../react-components/src/Modal/Columns.tsx | 19 +++ .../src/{Modal.tsx => Modal/index.tsx} | 45 ++----- packages/react-components/src/Modal/types.tsx | 26 ++++ packages/react-components/src/styles/index.ts | 2 + 10 files changed, 340 insertions(+), 151 deletions(-) create mode 100644 packages/page-staking/src/Actions/useBondBlocks.ts create mode 100644 packages/react-components/src/Modal/Actions.tsx create mode 100644 packages/react-components/src/Modal/Column.tsx create mode 100644 packages/react-components/src/Modal/Columns.tsx rename packages/react-components/src/{Modal.tsx => Modal/index.tsx} (53%) create mode 100644 packages/react-components/src/Modal/types.tsx diff --git a/packages/page-staking/src/Actions/Account/Nominate.tsx b/packages/page-staking/src/Actions/Account/Nominate.tsx index 6fa53ac879..7a815c2d85 100644 --- a/packages/page-staking/src/Actions/Account/Nominate.tsx +++ b/packages/page-staking/src/Actions/Account/Nominate.tsx @@ -41,9 +41,14 @@ function autoPick (targets: SortedTargets): string[] { } function initialPick (targets: SortedTargets): Selected { - return targets.validators?.length - ? { isAutoSelect: true, selected: autoPick(targets) } - : { isAutoSelect: false, selected: [] }; + const selected = targets.validators?.length + ? autoPick(targets) + : []; + + return { + isAutoSelect: selected.length !== 0, + selected + }; } function Nominate ({ className, controllerId, next, nominating, onClose, stashId, targets, validators }: Props): React.ReactElement | null { @@ -85,58 +90,71 @@ function Nominate ({ className, controllerId, next, nominating, onClose, stashId - - - {isAutoSelect - ? ( - <> - ( - - )) - } - /> -
{t('The auto-selection is done on the current profitability of the validators taking your favorites into account. It is adjusted based on the commission and current range of backing for teh validator. The calculation may and will change over time, so it is rather a selection based on the current state of the network, not a predictor of future profitability.')}
- - ) - : ( - + + - ) - } - + + + + + + {isAutoSelect + ? ( + <> + ( + + )) + } + /> +
{t('The auto-selection is done on the current profitability of the validators taking your favorites into account. It is adjusted based on the commission and current range of backing for the validator. The calculation may and will change over time, so it is rather a selection based on the current state of the network, not a predictor of future profitability.')}
+ + ) + : ( + + ) + } + +
+ +

{t('Nominators can be selected automatically based on the current on-chain conditions or supplied manually as selected from the list of all currently available validators. In both cases, your favorites appear for the selection.')}

+

{t('Once transmitted the new selection will only take effect in 2 eras since the selection criteria for the next era was done at the end of the previous era. Until then, then nominations will show as inactive.')}

+
+
| null>(null); const [stashId, setStashId] = useState(null); + const bondedBlocks = useBondBlocks(); useEffect((): void => { setExtrinsic( @@ -57,47 +60,84 @@ function NewStake ({ className, isInElection }: Props): React.ReactElement - - - - - - + + + + + + + +

{t('Think of the stash as your cold wallet and the controller as your hot wallet. Funding operations are controlled by the stash, any other non-funding actions by the controller itself.')}

+

{t('To ensure optimal fund security using the same stash/controller is strongly discouraged, but not forbidden.')}

+
+
+ + + {t('available')}} + params={stashId} + /> + } + onChange={setAmount} + /> + {bondedBlocks && ( + + + + )} + + + +

{t('The amount placed at-stake should be no more that 95% of your available amount to protect against slashing events.')}

+

{t('Once bonded, it wil need to be unlocked/withdrawn and will be locked for at least the bonding duration.')}

+
+
+ + + + + +

{t('Rewards (once paid) can be deposited to either the stash or controller, with different effects.')}

+
+
(api.derive.session.progress, []); + const [duration, setDuration] = useState(); + + useEffect((): void => { + progress?.sessionLength.gt(ONE) && setDuration( + progress.eraLength.mul(api.consts.staking.bondingDuration) + ); + }, [api, progress]); + + return duration; +} diff --git a/packages/page-staking/src/Targets/Nominate.tsx b/packages/page-staking/src/Targets/Nominate.tsx index 11ebce6b57..f7ce9fd65d 100644 --- a/packages/page-staking/src/Targets/Nominate.tsx +++ b/packages/page-staking/src/Targets/Nominate.tsx @@ -54,32 +54,47 @@ function Nominate ({ className, ownNominators, targets }: Props): React.ReactEle - - - ( - - )) - } - /> + + + + + + +

{t('One of your available nomination accounts, keyed by the stash. The transaction will be sent from the controller.')}

+
+
+ + + ( + + )) + } + /> + + +

{t('The selected validators to nominate, either via the "currently best algorithm" or via a manual selection.')}

+

{t('Once transmitted the new selection will only take effect in 2 eras since the selection criteria for the next era was done at the end of the previous era. Until then, then nominations will show as inactive.')}

+
+
{ + return ( + + + + {withOr && } + {children} + + + ); +} + +export default React.memo(Actions); diff --git a/packages/react-components/src/Modal/Column.tsx b/packages/react-components/src/Modal/Column.tsx new file mode 100644 index 0000000000..2336e3b1b9 --- /dev/null +++ b/packages/react-components/src/Modal/Column.tsx @@ -0,0 +1,41 @@ +// Copyright 2017-2020 @polkadot/react-components 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 { ColumnProps } from './types'; + +import React from 'react'; +import styled from 'styled-components'; + +function Column ({ children, className }: ColumnProps): React.ReactElement { + return ( +
{children}
+ ); +} + +export default React.memo(styled(Column)` + &:nth-child(1) { + flex: 100%; + } + + &:nth-child(2) { + display: none; + flex: 0%; + font-size: 0.9rem; + opacity: 0.75; + } + + @media only screen and (min-width: 1024px) { + &:nth-child(1), + &:only-child { + flex: 0 66.6%; + } + + &:nth-child(2) { + box-sizing: border-box; + display: block; + flex: 0 33.3%; + padding-left: 1rem; + } + } +`); diff --git a/packages/react-components/src/Modal/Columns.tsx b/packages/react-components/src/Modal/Columns.tsx new file mode 100644 index 0000000000..6a6c573bea --- /dev/null +++ b/packages/react-components/src/Modal/Columns.tsx @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/react-components 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 { ColumnProps } from './types'; + +import React from 'react'; +import styled from 'styled-components'; + +function Columns ({ children, className }: ColumnProps): React.ReactElement { + return ( +
{children}
+ ); +} + +export default React.memo(styled(Columns)` + display: flex; + flex-layout: row; +`); diff --git a/packages/react-components/src/Modal.tsx b/packages/react-components/src/Modal/index.tsx similarity index 53% rename from packages/react-components/src/Modal.tsx rename to packages/react-components/src/Modal/index.tsx index 8769083e54..d89abb2e40 100644 --- a/packages/react-components/src/Modal.tsx +++ b/packages/react-components/src/Modal/index.tsx @@ -2,31 +2,19 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { BareProps } from './types'; +import { ActionsProps, ColumnProps, ModalProps } from './types'; import React from 'react'; import SUIModal from 'semantic-ui-react/dist/commonjs/modules/Modal/Modal'; -import Button from './Button'; -import ButtonCancel from './ButtonCancel'; -import { classes } from './util'; - -interface ModalProps extends BareProps { - children: React.ReactNode; - header?: React.ReactNode; - open?: boolean; - [index: string]: any; -} - -interface ActionsProps extends BareProps { - cancelLabel?: string; - children: React.ReactNode; - withOr?: boolean; - onCancel: () => void; -} +import Actions from './Actions'; +import Column from './Column'; +import Columns from './Columns'; type ModalType = React.FC & { Actions: React.FC; + Column: React.FC; + Columns: React.FC; Content: typeof SUIModal.Content; Header: typeof SUIModal.Header; Description: typeof SUIModal.Description; @@ -38,7 +26,7 @@ function ModalBase (props: ModalProps): React.ReactElement { return ( { ); } -function Actions ({ cancelLabel, children, className, onCancel, withOr = true }: ActionsProps): React.ReactElement { - return ( - - - - {withOr && } - {children} - - - ); -} - const Modal = React.memo(ModalBase) as unknown as ModalType; -Modal.Actions = React.memo(Actions); +Modal.Actions = Actions; +Modal.Column = Column; +Modal.Columns = Columns; Modal.Content = SUIModal.Content; Modal.Header = SUIModal.Header; Modal.Description = SUIModal.Description; diff --git a/packages/react-components/src/Modal/types.tsx b/packages/react-components/src/Modal/types.tsx new file mode 100644 index 0000000000..6b5dd6ef2e --- /dev/null +++ b/packages/react-components/src/Modal/types.tsx @@ -0,0 +1,26 @@ +// Copyright 2017-2020 @polkadot/react-components 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 { BareProps } from '../types'; + +import React from 'react'; + +export interface ColumnProps { + children: React.ReactNode; + className?: string; +} + +export interface ModalProps extends BareProps { + children: React.ReactNode; + header?: React.ReactNode; + open?: boolean; + [index: string]: any; +} + +export interface ActionsProps extends BareProps { + cancelLabel?: string; + children: React.ReactNode; + withOr?: boolean; + onCancel: () => void; +} diff --git a/packages/react-components/src/styles/index.ts b/packages/react-components/src/styles/index.ts index 6bde4eaaad..e2f9a987d2 100644 --- a/packages/react-components/src/styles/index.ts +++ b/packages/react-components/src/styles/index.ts @@ -146,7 +146,9 @@ export default createGlobalStyle` &.error, &.warning { + font-size: 0.9rem; margin-left: 2.25rem; + padding: 0.75rem 1rem; } &.nomargin {