diff --git a/.123trigger b/.123trigger index 8c0f2357d8..553d62ba4d 100644 --- a/.123trigger +++ b/.123trigger @@ -1,2 +1,2 @@ -10 -0.45.2 \ No newline at end of file +11 +0.45.2 diff --git a/package.json b/package.json index cd1022faf8..7ef9d524d0 100644 --- a/package.json +++ b/package.json @@ -35,10 +35,10 @@ "resolutions": { "@polkadot/api": "^1.26.0-beta.1", "@polkadot/api-contract": "^1.26.0-beta.1", - "@polkadot/keyring": "^2.18.1", + "@polkadot/keyring": "^3.0.0-beta.3", "@polkadot/types": "^1.26.0-beta.1", - "@polkadot/util": "^2.18.1", - "@polkadot/util-crypto": "^2.18.1", + "@polkadot/util": "^3.0.0-beta.3", + "@polkadot/util-crypto": "^3.0.0-beta.3", "@polkadot/wasm-crypto": "^1.2.1", "babel-core": "^7.0.0-bridge.0" }, diff --git a/packages/apps/webpack.base.config.js b/packages/apps/webpack.base.config.js index 186965709e..4fb1d34154 100644 --- a/packages/apps/webpack.base.config.js +++ b/packages/apps/webpack.base.config.js @@ -149,7 +149,7 @@ function createWebpack (ENV, context) { ...mapChunks('polkadot', [ /* 00 */ /node_modules\/@polkadot\/(wasm)/, /* 01 */ /node_modules\/(@polkadot\/(api|metadata|rpc|types))/, - /* 02 */ /node_modules\/(@polkadot\/(extension|keyring|react|ui|util|vanitygen)|@edgeware|@ledgerhq|@zondax|edgeware)/ + /* 02 */ /node_modules\/(@polkadot\/(extension|keyring|react|ui|util|vanitygen)|@acala-network|@edgeware|@ledgerhq|@open-web3|@zondax|edgeware)/ ]), ...mapChunks('react', [ /* 00 */ /node_modules\/(@fortawesome)/, diff --git a/packages/page-accounts/src/Accounts/modals/Backup.tsx b/packages/page-accounts/src/Accounts/modals/Backup.tsx index 5c8e6cb0c7..dba6550f6d 100644 --- a/packages/page-accounts/src/Accounts/modals/Backup.tsx +++ b/packages/page-accounts/src/Accounts/modals/Backup.tsx @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import FileSaver from 'file-saver'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { AddressRow, Button, Modal, Password } from '@polkadot/react-components'; import keyring from '@polkadot/ui-keyring'; @@ -14,52 +14,42 @@ interface Props { address: string; } -interface ContentProps { - address: string; - doBackup: () => void; - isPassTouched: boolean; - isPassValid: boolean; - onChangePass: (password: string) => void; - password: string; -} - function Backup ({ address, onClose }: Props): React.ReactElement { const { t } = useTranslation(); - const [password, setPassword] = useState(''); - const [isPassTouched, setIsPassTouched] = useState(false); + const [isBusy, setIsBusy] = useState(false); + const [{ isPassTouched, password }, setPassword] = useState({ isPassTouched: false, password: '' }); const [backupFailed, setBackupFailed] = useState(false); - const isPassValid = useMemo(() => - keyring.isPassValid(password) && !backupFailed, - [password, backupFailed]); + const isPassValid = !backupFailed && keyring.isPassValid(password); const _onChangePass = useCallback( - (value: string): void => { - if (!isPassTouched) { - setIsPassTouched(true); - } - + (password: string): void => { setBackupFailed(false); - setPassword(value); + setPassword({ isPassTouched: true, password }); }, - [isPassTouched] + [] ); const _doBackup = useCallback( (): void => { - try { - const addressKeyring = address && keyring.getPair(address); - const json = addressKeyring && keyring.backupAccount(addressKeyring, password); - const blob = new Blob([JSON.stringify(json)], { type: 'application/json; charset=utf-8' }); + setIsBusy(true); + setTimeout((): void => { + try { + const addressKeyring = address && keyring.getPair(address); + const json = addressKeyring && keyring.backupAccount(addressKeyring, password); + const blob = new Blob([JSON.stringify(json)], { type: 'application/json; charset=utf-8' }); - FileSaver.saveAs(blob, `${address}.json`); - } catch (error) { - setBackupFailed(true); - console.error(error); + FileSaver.saveAs(blob, `${address}.json`); + } catch (error) { + setBackupFailed(true); + setIsBusy(false); + console.error(error); - return; - } + return; + } - onClose(); + setIsBusy(false); + onClose(); + }, 0); }, [address, onClose, password] ); @@ -69,18 +59,33 @@ function Backup ({ address, onClose }: Props): React.ReactElement { className='app--accounts-Modal' header={t('Backup account')} > - + + +

{t('An encrypted backup file will be created once you have pressed the "Download" button. This can be used to re-import your account on any other machine.')}

+

{t('Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.')}

+
+ ('The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.')} + isError={isPassTouched && !isPassValid} + label={t('password')} + onChange={_onChangePass} + onEnter={_doBackup} + tabIndex={0} + value={password} + /> +
+
+
); } export default React.memo(styled(Button)` border: none; + cursor: pointer; font-size: 0.92857142857rem; // 13/14px + position: relative; text-align: center; - &:not(:disabled) { - cursor: pointer; - } - &:not(.hasLabel) { padding: 0.75em; @@ -55,11 +57,6 @@ export default React.memo(styled(Button)` border-radius: 0.25rem; } - &:disabled { - cursor: not-allowed; - opacity: 0.15; - } - &:focus { outline:0; } @@ -78,10 +75,18 @@ export default React.memo(styled(Button)` color: inherit !important; } + &.isBusy { + cursor: wait; + } + &.isCircular { border-radius: 10rem; } + &.isDisabled { + cursor: not-allowed; + } + &.isFull { display: block; width: 100%; @@ -90,4 +95,31 @@ export default React.memo(styled(Button)` &.isIcon { background: transparent; } + + .ui--Button-spinner { + visibility: hidden; + } + + .ui--Button-overlay { + background: rgba(255, 255, 255, 0.75); + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + visibility: hidden; + } + + &.isBusy { + .ui--Button-spinner { + visibility: visible; + } + } + + &.isBusy, + &.isDisabled { + .ui--Button-overlay { + visibility: visible; + } + } `); diff --git a/packages/react-components/src/Button/types.ts b/packages/react-components/src/Button/types.ts index d7100501eb..dc16d6ac12 100644 --- a/packages/react-components/src/Button/types.ts +++ b/packages/react-components/src/Button/types.ts @@ -3,7 +3,6 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { IconName } from '@fortawesome/fontawesome-svg-core'; -import { ButtonProps as SUIButtonProps } from 'semantic-ui-react/dist/commonjs/elements/Button/Button'; import { BareProps } from '../types'; export type Button$Callback = () => void | Promise; @@ -11,9 +10,9 @@ export type Button$Callback = () => void | Promise; export interface ButtonProps { children?: React.ReactNode; className?: string; - icon?: IconName; - isAnimated?: SUIButtonProps['animated']; + icon: IconName; isBasic?: boolean; + isBusy?: boolean; isCircular?: boolean; isDisabled?: boolean; isFull?: boolean; diff --git a/packages/react-components/src/Password.tsx b/packages/react-components/src/Password.tsx index 250f58f6f6..e976d346f5 100644 --- a/packages/react-components/src/Password.tsx +++ b/packages/react-components/src/Password.tsx @@ -3,7 +3,6 @@ // of the Apache-2.0 license. See the LICENSE file for details. import React from 'react'; -import { MAX_PASS_LEN } from '@polkadot/ui-keyring/defaults'; import Input from './Input'; @@ -39,7 +38,6 @@ function Password ({ autoFocus, children, className = '', defaultValue, help, is isFull={isFull} label={label} labelExtra={labelExtra} - maxLength={MAX_PASS_LEN} name={name} onChange={onChange} onEnter={onEnter} diff --git a/packages/react-components/src/Spinner.tsx b/packages/react-components/src/Spinner.tsx index 14b0a7318e..46cb7f033e 100644 --- a/packages/react-components/src/Spinner.tsx +++ b/packages/react-components/src/Spinner.tsx @@ -11,17 +11,22 @@ import spinnerSrc from './Spinner-2.png'; interface Props { className?: string; label?: React.ReactNode; - variant?: 'app' | 'push' | 'mini'; + variant?: 'app' | 'cover' | 'push' | 'mini'; } +// prefetch +const img = new Image(); + +img.src = spinnerSrc as string; + function Spinner ({ className = '', label, variant = 'app' }: Props): React.ReactElement | null { const { t } = useTranslation(); return ( -
+
{variant === 'app' &&
{label || t('Retrieving data')}
}
@@ -34,7 +39,20 @@ export default React.memo(styled(Spinner)` margin: 0 auto; text-align: center; + &.isCover { + bottom: 0; + left: 0; + position: absolute; + right: 0; + + img { + margin: 0 auto; + opacity: 1; + } + } + img { + border: 1px solid transparent; border-radius: 10rem; opacity: 0.75; } diff --git a/packages/react-components/src/TxButton.tsx b/packages/react-components/src/TxButton.tsx index b5e96fc447..088f92846c 100644 --- a/packages/react-components/src/TxButton.tsx +++ b/packages/react-components/src/TxButton.tsx @@ -14,7 +14,7 @@ import Button from './Button'; import { StatusContext } from './Status'; import { useTranslation } from './translate'; -function TxButton ({ accountId, className = '', extrinsic: propsExtrinsic, icon, isBasic, isDisabled, isIcon, isNegative, isPrimary, isUnsigned, label, onClick, onFailed, onSendRef, onStart, onSuccess, onUpdate, params, tooltip, tx, withSpinner }: Props): React.ReactElement { +function TxButton ({ accountId, className = '', extrinsic: propsExtrinsic, icon, isBasic, isBusy, isDisabled, isIcon, isNegative, isPrimary, isUnsigned, label, onClick, onFailed, onSendRef, onStart, onSuccess, onUpdate, params, tooltip, tx, withSpinner }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const { queueExtrinsic } = useContext(StatusContext); @@ -87,6 +87,7 @@ function TxButton ({ accountId, className = '', extrinsic: propsExtrinsic, icon, className={className} icon={icon || 'check'} isBasic={isBasic} + isBusy={isBusy} isDisabled={isSending || isDisabled || needsAccount} isIcon={isIcon} isNegative={isNegative} diff --git a/packages/react-components/src/styles/index.ts b/packages/react-components/src/styles/index.ts index c977bb4f55..2499eb2ab2 100644 --- a/packages/react-components/src/styles/index.ts +++ b/packages/react-components/src/styles/index.ts @@ -121,17 +121,6 @@ export default createGlobalStyle` position: relative; text-align: left; - &:not(:hover):not(.keepAlive) { - .ui.button:not(.disabled) { - background: #eee !important; - color: #555 !important; - } - - .ui.button.show-on-hover { - visibility: hidden; - } - } - > ul { margin: 0; padding: 0; diff --git a/packages/react-components/src/styles/semantic.ts b/packages/react-components/src/styles/semantic.ts index 8773b3ed50..e47aee15bf 100644 --- a/packages/react-components/src/styles/semantic.ts +++ b/packages/react-components/src/styles/semantic.ts @@ -149,14 +149,6 @@ export default css` opacity: 1; } - .ui.button:disabled, - .ui.buttons .disabled.button, - .ui.disabled.active.button, - .ui.disabled.button, - .ui.disabled.button:hover { - opacity: 0.2 !important; - } - /* modals aligned to top, not center */ .ui.dimmer { background-color: rgba(0,0,0,0.5); diff --git a/packages/react-components/src/types.ts b/packages/react-components/src/types.ts index c40870690e..bd1b2253ef 100644 --- a/packages/react-components/src/types.ts +++ b/packages/react-components/src/types.ts @@ -57,6 +57,7 @@ export interface TxButtonProps extends TxProps { className?: string; icon?: IconName; isBasic?: boolean; + isBusy?: boolean; isDisabled?: boolean; isIcon?: boolean; isNegative?: boolean; diff --git a/packages/react-signer/src/PaymentInfo.tsx b/packages/react-signer/src/PaymentInfo.tsx index 442c4c5d12..52da60732c 100644 --- a/packages/react-signer/src/PaymentInfo.tsx +++ b/packages/react-signer/src/PaymentInfo.tsx @@ -29,7 +29,7 @@ function PaymentInfo ({ accountId, className = '', extrinsic }: Props): React.Re useEffect((): void => { // eslint-disable-next-line @typescript-eslint/unbound-method accountId && extrinsic && isFunction(extrinsic.paymentInfo) && isFunction(api.rpc.payment?.queryInfo) && - setImmediate((): void => { + setTimeout((): void => { try { extrinsic .paymentInfo(accountId) @@ -38,7 +38,7 @@ function PaymentInfo ({ accountId, className = '', extrinsic }: Props): React.Re } catch (error) { console.error((error as Error).message); } - }); + }, 0); }, [api, accountId, extrinsic, mountedRef]); if (!dispatchInfo) { diff --git a/packages/react-signer/src/TxSigned.tsx b/packages/react-signer/src/TxSigned.tsx index e9e0ce789d..35d24eeff2 100644 --- a/packages/react-signer/src/TxSigned.tsx +++ b/packages/react-signer/src/TxSigned.tsx @@ -264,8 +264,7 @@ function TxSigned ({ className, currentItem, requestAddress }: Props): React.Rea const _doStart = useCallback( (): void => { setBusy(true); - - setImmediate((): void => { + setTimeout((): void => { if (_unlock()) { isSubmit ? currentItem.payload @@ -275,7 +274,7 @@ function TxSigned ({ className, currentItem, requestAddress }: Props): React.Rea } else { setBusy(false); } - }); + }, 0); }, [_onSend, _onSendPayload, _onSign, _unlock, currentItem, isSubmit, queueSetTxStatus, senderInfo] ); @@ -338,38 +337,37 @@ function TxSigned ({ className, currentItem, requestAddress }: Props): React.Rea +