Electron app: save accounts to FS, do not download backup, hide warning banner (#2880)

* Electron app: save accounts to FS, do not download backup, hide warning banner

* extract store
This commit is contained in:
Ivan Rukhavets 2020-06-04 20:58:48 +03:00 committed by GitHub
parent 314e038b88
commit 368fefef72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 76 additions and 8 deletions

View File

@ -7,6 +7,8 @@ import '@polkadot/apps/initSettings';
import 'semantic-ui-css/semantic.min.css';
import '@polkadot/react-components/i18n';
import electron from 'electron';
import path from 'path';
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';
import { HashRouter } from 'react-router-dom';
@ -18,11 +20,15 @@ import { BlockAuthors, Events } from '@polkadot/react-query';
import AccountSidebar from '@polkadot/app-accounts/Sidebar';
import { Api } from '@polkadot/react-api';
import Apps from '@polkadot/apps/Apps';
import { FileStore } from '@polkadot/ui-keyring/stores';
const rootId = 'root';
const rootElement = document.getElementById(rootId);
const theme = { theme: settings.uiTheme };
const defaultStorePath = path.join((electron.app || electron.remote.app).getPath('userData'), 'polkadot');
const store = new FileStore(defaultStorePath);
if (!rootElement) {
throw new Error(`Unable to find element with id '${rootId}'`);
}
@ -33,7 +39,10 @@ ReactDOM.render(
<Suspense fallback='...'>
<ThemeProvider theme={theme}>
<Queue>
<Api url={settings.apiUrl}>
<Api
store={store}
url={settings.apiUrl}
>
<BlockAuthors>
<Events>
<AccountSidebar>

View File

@ -7,6 +7,7 @@ import React from 'react';
import { availableExtensions } from '@polkadot/apps-config/extensions';
import { isWeb3Injected } from '@polkadot/extension-dapp';
import { stringUpperFirst } from '@polkadot/util';
import { onlyOnWeb } from '@polkadot/react-api/hoc';
import { useTranslation } from '../translate';
import Banner from './Banner';
@ -53,4 +54,4 @@ function BannerExtension (): React.ReactElement | null {
);
}
export default React.memo(BannerExtension);
export default onlyOnWeb(React.memo(BannerExtension));

View File

@ -17,6 +17,7 @@ import keyring from '@polkadot/ui-keyring';
import uiSettings from '@polkadot/ui-settings';
import { isHex, u8aToHex } from '@polkadot/util';
import { keyExtractSuri, mnemonicGenerate, mnemonicValidate, randomAsU8a } from '@polkadot/util-crypto';
import { getEnvironment } from '@polkadot/react-api/util';
import { useTranslation } from '../../translate';
import CreateConfirmation from './CreateConfirmation';
@ -132,7 +133,6 @@ export function downloadAccount ({ json, pair }: CreateResult): void {
const blob = new Blob([JSON.stringify(json)], { type: 'application/json; charset=utf-8' });
FileSaver.saveAs(blob, `${pair.address}.json`);
InputAddress.setLastValue('account', pair.address);
}
function createAccount (suri: string, pairType: KeypairType, { genesisHash, name, tags = [] }: CreateOptions, password: string, success: string): ActionStatus {
@ -147,7 +147,11 @@ function createAccount (suri: string, pairType: KeypairType, { genesisHash, name
status.status = 'success';
status.message = success;
downloadAccount(result);
InputAddress.setLastValue('account', address);
if (getEnvironment() === 'web') {
downloadAccount(result);
}
} catch (error) {
status.status = 'error';
status.message = (error as Error).message;

View File

@ -64,6 +64,7 @@ function createAccount (source: KeyringPair, suri: string, name: string, passwor
status.message = success;
downloadAccount(result);
InputAddress.setLastValue('account', address);
} catch (error) {
status.status = 'error';
status.message = (error as Error).message;

View File

@ -15,6 +15,8 @@ import { WsProvider } from '@polkadot/rpc-provider';
import { StatusContext } from '@polkadot/react-components/Status';
import { TokenUnit } from '@polkadot/react-components/InputNumber';
import keyring from '@polkadot/ui-keyring';
import { KeyringStore } from '@polkadot/ui-keyring/types';
import uiSettings from '@polkadot/ui-settings';
import ApiSigner from '@polkadot/react-signer/ApiSigner';
import { formatBalance, isTestChain } from '@polkadot/util';
@ -27,6 +29,7 @@ import registry from './typeRegistry';
interface Props {
children: React.ReactNode;
url?: string;
store?: KeyringStore;
}
interface InjectedAccountExt {
@ -94,7 +97,7 @@ async function retrieve (api: ApiPromise): Promise<ChainData> {
};
}
async function loadOnReady (api: ApiPromise): Promise<ApiState> {
async function loadOnReady (api: ApiPromise, store?: KeyringStore): Promise<ApiState> {
const { injectedAccounts, properties, systemChain, systemChainType, systemName, systemVersion } = await retrieve(api);
const ss58Format = uiSettings.prefix === -1
? properties.ss58Format.unwrapOr(DEFAULT_SS58).toNumber()
@ -123,6 +126,7 @@ async function loadOnReady (api: ApiPromise): Promise<ApiState> {
genesisHash: api.genesisHash,
isDevelopment,
ss58Format,
store,
type: 'ed25519'
}, injectedAccounts);
@ -146,7 +150,7 @@ async function loadOnReady (api: ApiPromise): Promise<ApiState> {
};
}
function Api ({ children, url }: Props): React.ReactElement<Props> | null {
function Api ({ children, store, url }: Props): React.ReactElement<Props> | null {
const { queuePayload, queueSetTxStatus } = useContext(StatusContext);
const [state, setState] = useState<ApiState>({ isApiReady: false } as unknown as ApiState);
const [isApiConnected, setIsApiConnected] = useState(false);
@ -168,7 +172,7 @@ function Api ({ children, url }: Props): React.ReactElement<Props> | null {
api.on('disconnected', () => setIsApiConnected(false));
api.on('ready', async (): Promise<void> => {
try {
setState(await loadOnReady(api));
setState(await loadOnReady(api, store));
} catch (error) {
console.error('Unable to load chain', error);
}

View File

@ -8,3 +8,4 @@ export { default as withCalls } from './calls';
export { default as withCallDiv } from './callDiv';
export { default as withMulti } from './multi';
export { default as withObservable } from './observable';
export * from './onlyOn';

View File

@ -0,0 +1,18 @@
// Copyright 2017-2020 @polkadot/app-accounts 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 { ComponentType } from 'react';
import { Environment } from '../types';
import { getEnvironment } from '../util';
const onlyOn = (environment: Environment) => <T extends ComponentType<any>>(component: T): T | (() => null) => {
if (getEnvironment() === environment) {
return component;
}
return () => null;
};
export const onlyOnWeb = onlyOn('web');
export const onlyOnApp = onlyOn('app');

View File

@ -60,3 +60,5 @@ export type BaseProps<T> = BareProps & CallProps & ChangeProps & {
};
export type Formatter = (value?: any) => string;
export type Environment = 'web' | 'app';

View File

@ -0,0 +1,26 @@
// Copyright 2017-2020 @polkadot/app-accounts 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 { Environment } from '../types';
// https://github.com/electron/electron/issues/2288
function isElectron () {
if (process?.versions?.electron) {
return true;
}
if (window?.process?.type === 'renderer') {
return true;
}
return navigator?.userAgent?.indexOf('Electron') >= 0;
}
export default function getEnvironment (): Environment {
if (isElectron()) {
return 'app';
}
return 'web';
}

View File

@ -2,6 +2,7 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import getEnvironment from './getEnvironment';
import getHistoric from './historic';
import intervalObservable from './intervalObservable';
import isEqual from './isEqual';
@ -11,5 +12,6 @@ export {
getHistoric,
intervalObservable,
isEqual,
triggerChange
triggerChange,
getEnvironment
};