diff --git a/packages/app-accounts/src/Account.tsx b/packages/app-accounts/src/Account.tsx index 8c6917f1fb5d..ad49685b1b1f 100644 --- a/packages/app-accounts/src/Account.tsx +++ b/packages/app-accounts/src/Account.tsx @@ -4,18 +4,23 @@ import { DeriveAccountInfo } from '@polkadot/api-derive/types'; import { ActionStatus } from '@polkadot/react-components/Status/types'; +import { RecoveryConfig } from '@polkadot/types/interfaces'; import React, { useState, useEffect } from 'react'; import { Label } from 'semantic-ui-react'; import styled from 'styled-components'; -import { AddressInfo, AddressSmall, Button, ChainLock, Forget, Icon, InputTags, LinkPolkascan, Menu, Popup, Input } from '@polkadot/react-components'; +import { AddressInfo, AddressSmall, Badge, Button, ChainLock, Forget, Icon, IdentityIcon, InputTags, LinkPolkascan, Menu, Popup, Input } from '@polkadot/react-components'; import { useApi, useCall, useToggle } from '@polkadot/react-hooks'; +import { Option } from '@polkadot/types'; import keyring from '@polkadot/ui-keyring'; +import { formatBalance, formatNumber } from '@polkadot/util'; import Backup from './modals/Backup'; import ChangePass from './modals/ChangePass'; import Derive from './modals/Derive'; import Identity from './modals/Identity'; +import RecoverAccount from './modals/RecoverAccount'; +import RecoverSetup from './modals/RecoverSetup'; import Transfer from './modals/Transfer'; import { useTranslation } from './translate'; @@ -31,6 +36,10 @@ function Account ({ address, className, filter, isFavorite, toggleFavorite }: Pr const { t } = useTranslation(); const api = useApi(); const info = useCall(api.api.derive.accounts.info as any, [address]); + const recoveryInfo = useCall(api.api.query.recovery?.recoverable, [address], { + transform: (opt: Option): RecoveryConfig | null => + opt.unwrapOr(null) + }); const [tags, setTags] = useState([]); const [accName, setAccName] = useState(''); const [genesisHash, setGenesisHash] = useState(null); @@ -43,6 +52,8 @@ function Account ({ address, className, filter, isFavorite, toggleFavorite }: Pr const [isForgetOpen, toggleForget] = useToggle(); const [isIdentityOpen, toggleIdentity] = useToggle(); const [isPasswordOpen, togglePassword] = useToggle(); + const [isRecoverAccountOpen, toggleRecoverAccount] = useToggle(); + const [isRecoverSetupOpen, toggleRecoverSetup] = useToggle(); const [isSettingsOpen, toggleSettings] = useToggle(); const [isTransferOpen, toggleTransfer] = useToggle(); @@ -158,6 +169,46 @@ function Account ({ address, className, filter, isFavorite, toggleFavorite }: Pr onClick={_onFavorite} /> + + {recoveryInfo && ( + +

{t('This account is recoverable, with the following friends:')}

+
+ {recoveryInfo.friends.map((friend, index): React.ReactNode => ( + + ))} +
+ + + + + + + + + + + + + + + +
{t('threshold')}{formatNumber(recoveryInfo.threshold)}
{t('delay')}{formatNumber(recoveryInfo.delayPeriod)}
{t('deposit')}{formatBalance(recoveryInfo.deposit)}
+ + } + info={} + isInline + isTooltip + type='online' + /> + )} + )} + {isRecoverAccountOpen && ( + + )} + {isRecoverSetupOpen && ( + + )} {isEditingTags @@ -321,6 +386,19 @@ function Account ({ address, className, filter, isFavorite, toggleFavorite }: Pr > {t('Forget this account')} + {api.api.tx.recovery?.createRecovery && ( + <> + + {!recoveryInfo && ( + + {t('Make recoverable')} + + )} + + {t('Initiate recovery for another')} + + + )} {!api.isDevelopment && ( <> diff --git a/packages/app-accounts/src/modals/RecoverAccount.tsx b/packages/app-accounts/src/modals/RecoverAccount.tsx new file mode 100644 index 000000000000..9d46fb3a0c71 --- /dev/null +++ b/packages/app-accounts/src/modals/RecoverAccount.tsx @@ -0,0 +1,52 @@ +// Copyright 2017-2020 @polkadot/app-staking 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 React, { useState } from 'react'; +import { InputAddress, Modal, TxButton } from '@polkadot/react-components'; + +import { useTranslation } from '../translate'; + +interface Props { + address: string; + className?: string; + onClose: () => void; +} + +export default function RecoverAccount ({ address, className, onClose }: Props): React.ReactElement { + const { t } = useTranslation(); + const [recover, setRecover] = useState(null); + + return ( + + + + + + + + + + ); +} diff --git a/packages/app-accounts/src/modals/RecoverSetup.tsx b/packages/app-accounts/src/modals/RecoverSetup.tsx new file mode 100644 index 000000000000..d81971601483 --- /dev/null +++ b/packages/app-accounts/src/modals/RecoverSetup.tsx @@ -0,0 +1,88 @@ +// Copyright 2017-2020 @polkadot/app-staking 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 BN from 'bn.js'; +import React, { useEffect, useState } from 'react'; +import { AddressMulti, InputAddress, InputNumber, Modal, TxButton } from '@polkadot/react-components'; +import { useAccounts, useAddresses } from '@polkadot/react-hooks'; + +import { useTranslation } from '../translate'; + +interface Props { + address: string; + className?: string; + onClose: () => void; +} + +const MAX_HELPERS = 16; + +export default function RecoverSetup ({ address, className, onClose }: Props): React.ReactElement { + const { t } = useTranslation(); + const { allAccounts } = useAccounts(); + const { allAddresses } = useAddresses(); + const [availableHelpers, setAvailableHelpers] = useState([]); + const [delay, setDelay] = useState(); + const [helpers, setHelpers] = useState([]); + const [threshold, setThreshold] = useState(); + + useEffect((): void => { + if (allAccounts && allAddresses) { + setAvailableHelpers( + [...allAccounts, ...allAddresses].filter((a): boolean => a !== address) + ); + } + }, [address, allAccounts, allAddresses]); + + const isErrorDelay = !delay; + const isErrorHelpers = !helpers.length; + const isErrorThreshold = !threshold || !threshold.gtn(0) || threshold.gtn(helpers.length); + + return ( + + + + + + + + + + + + ); +} diff --git a/packages/react-components/src/Status/index.tsx b/packages/react-components/src/Status/index.tsx index 2f3baefccfdf..884c41cab494 100644 --- a/packages/react-components/src/Status/index.tsx +++ b/packages/react-components/src/Status/index.tsx @@ -186,7 +186,7 @@ export default styled(Status)` position: fixed; right: 0.25rem; top: 0.25rem; - width: 20rem; + width: 23rem; z-index: 1001; .dismiss { @@ -233,7 +233,7 @@ export default styled(Status)` .short { font-size: 2.5rem; opacity: 0.75; - padding: 0.5rem; + padding: 0.5rem 0 0.5rem 0.5rem; i.icon { line-height: 1;