Refactor open window (#473)

* add mnemonic redirect like json

* undefined not allowed because request is a defined object

* Apply suggestions from code review

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* remove redirect

Co-authored-by: Jaco Greeff <jacogr@gmail.com>
This commit is contained in:
Thibaut Sardan 2020-10-01 16:13:47 +02:00 committed by GitHub
parent 3e357cc864
commit 7c01b9e658
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 47 additions and 26 deletions

View File

@ -3,9 +3,9 @@
import { MetadataDef } from '@polkadot/extension-inject/types'; import { MetadataDef } from '@polkadot/extension-inject/types';
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
import { AccountJson, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountCreateExternal, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestAuthorizeApprove, RequestAuthorizeReject, RequestDeriveCreate, ResponseDeriveValidate, RequestMetadataApprove, RequestMetadataReject, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSigningIsLocked, RequestSeedCreate, RequestTypes, ResponseAccountExport, RequestAccountForget, ResponseSeedCreate, RequestSeedValidate, RequestDeriveValidate, ResponseSeedValidate, ResponseType, SigningRequest, RequestJsonRestore, ResponseJsonRestore, RequestAccountChangePassword } from '../types'; import { AccountJson, AllowedPath, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountCreateExternal, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestAuthorizeApprove, RequestAuthorizeReject, RequestDeriveCreate, ResponseDeriveValidate, RequestMetadataApprove, RequestMetadataReject, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSigningIsLocked, RequestSeedCreate, RequestTypes, ResponseAccountExport, RequestAccountForget, ResponseSeedCreate, RequestSeedValidate, RequestDeriveValidate, ResponseSeedValidate, ResponseType, SigningRequest, RequestJsonRestore, ResponseJsonRestore, RequestAccountChangePassword } from '../types';
import { PASSWORD_EXPIRY_MS } from '@polkadot/extension-base/defaults'; import { ALLOWED_PATH, PASSWORD_EXPIRY_MS } from '@polkadot/extension-base/defaults';
import chrome from '@polkadot/extension-inject/chrome'; import chrome from '@polkadot/extension-inject/chrome';
import keyring from '@polkadot/ui-keyring'; import keyring from '@polkadot/ui-keyring';
import accountsObservable from '@polkadot/ui-keyring/observable/accounts'; import accountsObservable from '@polkadot/ui-keyring/observable/accounts';
@ -377,12 +377,17 @@ export default class Extension {
return true; return true;
} }
private windowOpen (path = '/'): boolean { private windowOpen (path: AllowedPath): boolean {
console.error('open', `${chrome.extension.getURL('index.html')}#${path}`); const url = `${chrome.extension.getURL('index.html')}#${path}`;
chrome.tabs.create({ if (!ALLOWED_PATH.includes(path)) {
url: `${chrome.extension.getURL('index.html')}#${path}` console.error('Not allowed to open the url:', url);
});
return false;
}
console.log('open', url);
chrome.tabs.create({ url });
return true; return true;
} }
@ -520,10 +525,7 @@ export default class Extension {
return this.signingSubscribe(id, port); return this.signingSubscribe(id, port);
case 'pri(window.open)': case 'pri(window.open)':
return this.windowOpen(); return this.windowOpen(request as AllowedPath);
case 'pri(window.open.json)':
return this.windowOpen('/account/restore-json');
default: default:
throw new Error(`Unable to handle message of type ${type}`); throw new Error(`Unable to handle message of type ${type}`);

View File

@ -10,6 +10,8 @@ import { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '@polkadot/keyri
import { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; import { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types';
import { TypeRegistry } from '@polkadot/types'; import { TypeRegistry } from '@polkadot/types';
import { ALLOWED_PATH } from '../defaults';
type KeysWithDefinedValues<T> = { type KeysWithDefinedValues<T> = {
[K in keyof T]: T[K] extends undefined ? never : K [K in keyof T]: T[K] extends undefined ? never : K
}[keyof T]; }[keyof T];
@ -97,8 +99,7 @@ export interface RequestSignatures {
'pri(signing.cancel)': [RequestSigningCancel, boolean]; 'pri(signing.cancel)': [RequestSigningCancel, boolean];
'pri(signing.isLocked)': [RequestSigningIsLocked, boolean]; 'pri(signing.isLocked)': [RequestSigningIsLocked, boolean];
'pri(signing.requests)': [RequestSigningSubscribe, boolean, SigningRequest[]]; 'pri(signing.requests)': [RequestSigningSubscribe, boolean, SigningRequest[]];
'pri(window.open)': [null, boolean]; 'pri(window.open)': [AllowedPath, boolean];
'pri(window.open.json)': [null, boolean];
// public/external requests, i.e. from a page // public/external requests, i.e. from a page
'pub(accounts.list)': [RequestAccountList, InjectedAccount[]]; 'pub(accounts.list)': [RequestAccountList, InjectedAccount[]];
'pub(accounts.subscribe)': [RequestAccountSubscribe, boolean, InjectedAccount[]]; 'pub(accounts.subscribe)': [RequestAccountSubscribe, boolean, InjectedAccount[]];
@ -348,3 +349,5 @@ export interface RequestJsonRestore {
export interface ResponseJsonRestore { export interface ResponseJsonRestore {
error: string | null; error: string | null;
} }
export type AllowedPath = typeof ALLOWED_PATH[number];

View File

@ -1,12 +1,14 @@
// Copyright 2019-2020 @polkadot/extension-base authors & contributors // Copyright 2019-2020 @polkadot/extension-base authors & contributors
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
const ALLOWED_PATH = ['/', '/account/restore-json'] as const;
const PORT_CONTENT = 'content'; const PORT_CONTENT = 'content';
const PORT_EXTENSION = 'extension'; const PORT_EXTENSION = 'extension';
const PASSWORD_EXPIRY_MIN = 15; const PASSWORD_EXPIRY_MIN = 15;
const PASSWORD_EXPIRY_MS = PASSWORD_EXPIRY_MIN * 60 * 1000; const PASSWORD_EXPIRY_MS = PASSWORD_EXPIRY_MIN * 60 * 1000;
export { export {
ALLOWED_PATH,
PASSWORD_EXPIRY_MIN, PASSWORD_EXPIRY_MIN,
PASSWORD_EXPIRY_MS, PASSWORD_EXPIRY_MS,
PORT_CONTENT, PORT_CONTENT,

View File

@ -0,0 +1,10 @@
// Copyright 2019-2020 @polkadot/extension-ui authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { useMemo } from 'react';
export default function useIsPopup (): boolean {
return useMemo(() => {
return window.innerWidth <= 560;
}, []);
}

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import { Message } from '@polkadot/extension-base/types'; import { Message } from '@polkadot/extension-base/types';
import { AccountJson, AuthorizeRequest, SigningRequest, RequestTypes, MessageTypes, ResponseTypes, SeedLengths, SubscriptionMessageTypes, MetadataRequest, MessageTypesWithNullRequest, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions, ResponseDeriveValidate, ResponseJsonRestore } from '@polkadot/extension-base/background/types'; import { AccountJson, AllowedPath, AuthorizeRequest, SigningRequest, RequestTypes, MessageTypes, ResponseTypes, SeedLengths, SubscriptionMessageTypes, MetadataRequest, MessageTypesWithNullRequest, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions, ResponseDeriveValidate, ResponseJsonRestore } from '@polkadot/extension-base/background/types';
import { Chain } from '@polkadot/extension-chains/types'; import { Chain } from '@polkadot/extension-chains/types';
import { MetadataDef } from '@polkadot/extension-inject/types'; import { MetadataDef } from '@polkadot/extension-inject/types';
import { KeypairType } from '@polkadot/util-crypto/types'; import { KeypairType } from '@polkadot/util-crypto/types';
@ -186,10 +186,6 @@ export async function validateSeed (suri: string, type?: KeypairType): Promise<{
return sendMessage('pri(seed.validate)', { suri, type }); return sendMessage('pri(seed.validate)', { suri, type });
} }
export async function windowOpen (): Promise<boolean> {
return sendMessage('pri(window.open)', null);
}
export async function validateDerivationPath (parentAddress: string, suri: string, parentPassword: string): Promise<ResponseDeriveValidate> { export async function validateDerivationPath (parentAddress: string, suri: string, parentPassword: string): Promise<ResponseDeriveValidate> {
return sendMessage('pri(derivation.validate)', { parentAddress, parentPassword, suri }); return sendMessage('pri(derivation.validate)', { parentAddress, parentPassword, suri });
} }
@ -198,8 +194,8 @@ export async function deriveAccount (parentAddress: string, suri: string, parent
return sendMessage('pri(derivation.create)', { genesisHash, name, parentAddress, parentPassword, password, suri }); return sendMessage('pri(derivation.create)', { genesisHash, name, parentAddress, parentPassword, password, suri });
} }
export async function jsonRestoreWindowOpen (): Promise<boolean> { export async function windowOpen (path: AllowedPath): Promise<boolean> {
return sendMessage('pri(window.open.json)', null); return sendMessage('pri(window.open)', path);
} }
export async function jsonVerifyFile (json: KeyringPair$Json): Promise<boolean> { export async function jsonVerifyFile (json: KeyringPair$Json): Promise<boolean> {

View File

@ -11,23 +11,25 @@ import plusIcon from '../assets/plus.svg';
import qrIcon from '../assets/qr.svg'; import qrIcon from '../assets/qr.svg';
import seedIcon from '../assets/secret.svg'; import seedIcon from '../assets/secret.svg';
import { AccountContext, Link, MediaContext, Menu, MenuDivider, MenuItem, Svg } from '../components'; import { AccountContext, Link, MediaContext, Menu, MenuDivider, MenuItem, Svg } from '../components';
import useIsPopup from '../hooks/useIsPopup';
import useTranslation from '../hooks/useTranslation'; import useTranslation from '../hooks/useTranslation';
import { jsonRestoreWindowOpen } from '../messaging'; import { windowOpen } from '../messaging';
interface Props extends ThemeProps { interface Props extends ThemeProps {
className?: string; className?: string;
reference: React.MutableRefObject<null>; reference: React.MutableRefObject<null>;
} }
const isPopup = window.innerWidth <= 480; const jsonPath = '/account/restore-json';
function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> { function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
const { t } = useTranslation(); const { t } = useTranslation();
const { master } = useContext(AccountContext); const { master } = useContext(AccountContext);
const mediaAllowed = useContext(MediaContext); const mediaAllowed = useContext(MediaContext);
const isPopup = useIsPopup();
const _openJson = useCallback((): void => { const _openJson = useCallback((): void => {
jsonRestoreWindowOpen().catch(console.error); windowOpen(jsonPath).catch(console.error);
}, []); }, []);
return ( return (
@ -55,7 +57,7 @@ function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
<MenuItem className='menuItem'> <MenuItem className='menuItem'>
<Link <Link
onClick={isPopup ? _openJson : undefined} onClick={isPopup ? _openJson : undefined}
to={isPopup ? undefined : '/account/restore-json'} to={isPopup ? undefined : jsonPath}
> >
<Svg src={fileIcon} /> <Svg src={fileIcon} />
<span>{t<string>('Restore account from backup JSON file')}</span> <span>{t<string>('Restore account from backup JSON file')}</span>

View File

@ -9,6 +9,7 @@ import settings from '@polkadot/ui-settings';
import FullScreenIcon from '../assets/fullscreen.svg'; import FullScreenIcon from '../assets/fullscreen.svg';
import { ActionText, Checkbox, Dropdown, Menu, MenuDivider, MenuItem, Svg, Switch, ThemeSwitchContext, themes } from '../components'; import { ActionText, Checkbox, Dropdown, Menu, MenuDivider, MenuItem, Svg, Switch, ThemeSwitchContext, themes } from '../components';
import useIsPopup from '../hooks/useIsPopup';
import useTranslation from '../hooks/useTranslation'; import useTranslation from '../hooks/useTranslation';
import { windowOpen } from '../messaging'; import { windowOpen } from '../messaging';
import getLanguageOptions from '../util/getLanguageOptions'; import getLanguageOptions from '../util/getLanguageOptions';
@ -23,7 +24,6 @@ interface Props extends ThemeProps {
reference: React.MutableRefObject<null>; reference: React.MutableRefObject<null>;
} }
const isPopup = window.innerWidth <= 560;
const prefixOptions = settings.availablePrefixes const prefixOptions = settings.availablePrefixes
.filter(({ value }) => value !== -1) .filter(({ value }) => value !== -1)
.map(({ text, value }): Option => ({ text, value: `${value}` })); .map(({ text, value }): Option => ({ text, value: `${value}` }));
@ -34,6 +34,7 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement<Prop
const [prefix, setPrefix] = useState(`${settings.prefix === -1 ? 42 : settings.prefix}`); const [prefix, setPrefix] = useState(`${settings.prefix === -1 ? 42 : settings.prefix}`);
const themeContext = useContext<Theme>(ThemeContext); const themeContext = useContext<Theme>(ThemeContext);
const setTheme = useContext(ThemeSwitchContext); const setTheme = useContext(ThemeSwitchContext);
const isPopup = useIsPopup();
const languageOptions = useMemo(() => getLanguageOptions(), []); const languageOptions = useMemo(() => getLanguageOptions(), []);
useEffect(() => { useEffect(() => {
@ -53,6 +54,11 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement<Prop
[setTheme] [setTheme]
); );
const _onWindowOpen = useCallback(
() => windowOpen('/'),
[]
);
const _onChangeLang = useCallback( const _onChangeLang = useCallback(
(value: string): void => { (value: string): void => {
settings.set({ i18nLang: value }); settings.set({ i18nLang: value });
@ -121,7 +127,7 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement<Prop
<ActionText <ActionText
className='openWindow' className='openWindow'
icon={FullScreenIcon} icon={FullScreenIcon}
onClick={windowOpen} onClick={_onWindowOpen}
text={t<string>('Open extension in new window')} text={t<string>('Open extension in new window')}
/> />
</MenuItem> </MenuItem>