Make enable() call to extensions (#1194)
* Make enable() call to extensions * It works * Add overlay for injected (prompt) * Use signer from injectedPromise
This commit is contained in:
parent
cbc041fce5
commit
86a767de68
@ -43,19 +43,17 @@ ReactDOM.render(
|
||||
<Suspense fallback='...'>
|
||||
<Queue>
|
||||
<QueueConsumer>
|
||||
{({ queueExtrinsic, queueSetTxStatus }) => {
|
||||
return (
|
||||
<Api
|
||||
queueExtrinsic={queueExtrinsic}
|
||||
queueSetTxStatus={queueSetTxStatus}
|
||||
url={url}
|
||||
>
|
||||
<HashRouter>
|
||||
<Apps />
|
||||
</HashRouter>
|
||||
</Api>
|
||||
);
|
||||
}}
|
||||
{({ queueExtrinsic, queueSetTxStatus }) => (
|
||||
<Api
|
||||
queueExtrinsic={queueExtrinsic}
|
||||
queueSetTxStatus={queueSetTxStatus}
|
||||
url={url}
|
||||
>
|
||||
<HashRouter>
|
||||
<Apps />
|
||||
</HashRouter>
|
||||
</Api>
|
||||
)}
|
||||
</QueueConsumer>
|
||||
</Queue>
|
||||
</Suspense>,
|
||||
|
||||
@ -19,6 +19,24 @@ const isFirefox = typeof InstallTrigger !== 'undefined';
|
||||
|
||||
class Connecting extends React.PureComponent<Props> {
|
||||
render () {
|
||||
return this.renderExtension() || this.renderConnecting();
|
||||
}
|
||||
|
||||
private renderExtension () {
|
||||
const { isWaitingInjected, t } = this.props;
|
||||
|
||||
if (!isWaitingInjected) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<div>{t('Waiting for authorization from the extension. Please open the installed extension and approve or reject access.')}</div>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
private renderConnecting () {
|
||||
const { isApiConnected, t } = this.props;
|
||||
|
||||
if (isApiConnected) {
|
||||
|
||||
@ -2,10 +2,9 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Signer } from '@polkadot/api/types';
|
||||
import { ProviderInterface } from '@polkadot/rpc-provider/types';
|
||||
import { QueueTx$ExtrinsicAdd, QueueTx$MessageSetStatus } from '@polkadot/ui-app/Status/types';
|
||||
import { ApiProps } from './types';
|
||||
import { ApiProps, ApiInjectedProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import ApiPromise from '@polkadot/api/promise';
|
||||
@ -18,10 +17,11 @@ import { ChainProperties } from '@polkadot/types';
|
||||
import { formatBalance, isTestChain } from '@polkadot/util';
|
||||
|
||||
import ApiContext from './ApiContext';
|
||||
import { withInjected } from './Injected';
|
||||
|
||||
let api: ApiPromise;
|
||||
|
||||
type Props = {
|
||||
type Props = ApiInjectedProps & {
|
||||
children: React.ReactNode,
|
||||
queueExtrinsic: QueueTx$ExtrinsicAdd,
|
||||
queueSetTxStatus: QueueTx$MessageSetStatus,
|
||||
@ -34,26 +34,15 @@ type State = ApiProps & {
|
||||
|
||||
type InjectedAccount = { address: string, meta: { name: string } };
|
||||
|
||||
type WindowInjected = Window & {
|
||||
injectedWeb3: {
|
||||
[index: string]: {
|
||||
accounts: {
|
||||
get: () => Promise<Array<{ address: string, name: string }>>
|
||||
},
|
||||
signer: Signer
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { api };
|
||||
|
||||
export default class ApiWrapper extends React.PureComponent<Props, State> {
|
||||
class ApiWrapper extends React.PureComponent<Props, State> {
|
||||
state: State = {} as State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
const { queueExtrinsic, queueSetTxStatus, url } = props;
|
||||
const { injectedAvailable, queueExtrinsic, queueSetTxStatus, url } = props;
|
||||
const provider = new WsProvider(url);
|
||||
const signer = new ApiSigner(queueExtrinsic, queueSetTxStatus);
|
||||
|
||||
@ -72,13 +61,20 @@ export default class ApiWrapper extends React.PureComponent<Props, State> {
|
||||
this.state = {
|
||||
isApiConnected: false,
|
||||
isApiReady: false,
|
||||
isWaitingInjected: injectedAvailable,
|
||||
api,
|
||||
setApiUrl
|
||||
} as State;
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const { injectedPromise } = this.props;
|
||||
|
||||
this.subscribeEvents();
|
||||
|
||||
injectedPromise
|
||||
.then(() => this.setState({ isWaitingInjected: false }))
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
private subscribeEvents () {
|
||||
@ -102,28 +98,27 @@ export default class ApiWrapper extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
private async retrieveInjected (): Promise<Array<InjectedAccount>> {
|
||||
const { injectedPromise } = this.props;
|
||||
const injected: Array<InjectedAccount> = [];
|
||||
const injWindow = window as WindowInjected;
|
||||
const result = await injectedPromise;
|
||||
|
||||
if (injWindow.injectedWeb3) {
|
||||
console.log('api: found injectedWeb3', Object.keys(injWindow.injectedWeb3));
|
||||
if (result.length) {
|
||||
const providers = result.map(({ name, version }) => `${name}/${version}`);
|
||||
|
||||
console.log(`api: found injectedWeb3: ${providers.join(', ')}`);
|
||||
|
||||
const rerieved = await Promise.all(
|
||||
Object
|
||||
.entries(injWindow.injectedWeb3)
|
||||
.map(([source, { accounts }]) =>
|
||||
accounts
|
||||
.get()
|
||||
.then((accounts) =>
|
||||
accounts.map(({ address, name }) => ({
|
||||
address,
|
||||
meta: {
|
||||
name: `${name} (${source === 'polkadot-js' ? 'extension' : source})`,
|
||||
source
|
||||
}
|
||||
}))
|
||||
)
|
||||
result.map(({ accounts, name: source }) =>
|
||||
accounts.get().then((accounts) =>
|
||||
accounts.map(({ address, name }) => ({
|
||||
address,
|
||||
meta: {
|
||||
name: `${name} (${source === 'polkadot-js' ? 'extension' : source})`,
|
||||
source
|
||||
}
|
||||
}))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
rerieved.forEach((accounts) =>
|
||||
@ -176,7 +171,7 @@ export default class ApiWrapper extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { api, apiDefaultTx, chain, isApiConnected, isApiReady, isDevelopment, setApiUrl } = this.state;
|
||||
const { api, apiDefaultTx, chain, isApiConnected, isApiReady, isDevelopment, isWaitingInjected, setApiUrl } = this.state;
|
||||
|
||||
return (
|
||||
<ApiContext.Provider
|
||||
@ -186,6 +181,7 @@ export default class ApiWrapper extends React.PureComponent<Props, State> {
|
||||
isApiConnected,
|
||||
isApiReady: isApiReady && !!chain,
|
||||
isDevelopment,
|
||||
isWaitingInjected,
|
||||
setApiUrl
|
||||
}}
|
||||
>
|
||||
@ -194,3 +190,5 @@ export default class ApiWrapper extends React.PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withInjected(ApiWrapper);
|
||||
|
||||
44
packages/ui-api/src/Injected.tsx
Normal file
44
packages/ui-api/src/Injected.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-api 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 { ApiInjected, ApiInjectedProps, Subtract, WindowInjected } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
const objmap = (window as WindowInjected).injectedWeb3;
|
||||
const injectedAvailable = !!objmap && Object.keys(objmap).length !== 0;
|
||||
const injectedPromise = !injectedAvailable
|
||||
? Promise.resolve([] as Array<ApiInjected>)
|
||||
: Promise.all(Object.values(objmap).map(({ name, version, enable }) =>
|
||||
Promise.all([
|
||||
Promise.resolve({ name, version }),
|
||||
enable('polkadot-js/apps').catch(() => null)
|
||||
])))
|
||||
.then((values) => values.filter(([, result]) => result !== null))
|
||||
.then((values) => values.map(([info, result]) => ({ ...info, ...result })))
|
||||
.catch(() => [] as Array<ApiInjected>);
|
||||
|
||||
const InjectedContext = React.createContext({ injectedAvailable, injectedPromise });
|
||||
const InjectedConsumer = InjectedContext.Consumer;
|
||||
|
||||
export {
|
||||
InjectedConsumer
|
||||
};
|
||||
|
||||
export function withInjected<P extends ApiInjectedProps> (Component: React.ComponentType<P>): React.ComponentType<Subtract<P, ApiInjectedProps>> {
|
||||
return (props: Subtract<P, ApiInjectedProps>) => {
|
||||
return (
|
||||
<InjectedContext.Consumer>
|
||||
{(injected) => (
|
||||
// @ts-ignore Something here with the props are going wonky
|
||||
<Component
|
||||
{...props}
|
||||
injectedAvailable={injectedAvailable}
|
||||
injectedPromise={injectedPromise}
|
||||
/>
|
||||
)}
|
||||
</InjectedContext.Consumer>
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -5,6 +5,7 @@
|
||||
import Api, { api } from './Api';
|
||||
import ApiContext from './ApiContext';
|
||||
|
||||
export * from './Injected';
|
||||
export * from './with';
|
||||
|
||||
export {
|
||||
|
||||
@ -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 { Signer } from '@polkadot/api/types';
|
||||
import { SubmittableExtrinsicFunction } from '@polkadot/api/promise/types';
|
||||
import ApiPromise from '@polkadot/api/promise';
|
||||
|
||||
@ -20,9 +21,37 @@ export type ApiProps = {
|
||||
isApiConnected: boolean,
|
||||
isApiReady: boolean,
|
||||
isDevelopment: boolean,
|
||||
isWaitingInjected: boolean,
|
||||
setApiUrl: (url?: string) => void
|
||||
};
|
||||
|
||||
export type WindowInjectedInfo = {
|
||||
name: string,
|
||||
version: string
|
||||
};
|
||||
|
||||
export type WindowInjectedResult = {
|
||||
accounts: {
|
||||
get: () => Promise<Array<{ address: string, name: string }>>
|
||||
},
|
||||
signer: Signer
|
||||
};
|
||||
|
||||
export type WindowInjected = Window & {
|
||||
injectedWeb3: {
|
||||
[index: string]: WindowInjectedInfo & {
|
||||
enable: (origin: string) => Promise<WindowInjectedResult>
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export type ApiInjected = WindowInjectedInfo & WindowInjectedResult;
|
||||
|
||||
export type ApiInjectedProps = {
|
||||
injectedAvailable: boolean,
|
||||
injectedPromise: Promise<Array<ApiInjected>>
|
||||
};
|
||||
|
||||
export type OnChangeCb$Obs = { next: (value?: any) => any };
|
||||
export type OnChangeCb$Fn = (value?: any) => any;
|
||||
|
||||
|
||||
@ -2,10 +2,9 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Signer as ISigner } from '@polkadot/api/types';
|
||||
import { SubmittableResult } from '@polkadot/api/SubmittableExtrinsic';
|
||||
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
|
||||
import { ApiProps } from '@polkadot/ui-api/types';
|
||||
import { ApiProps, ApiInjectedProps } from '@polkadot/ui-api/types';
|
||||
import { I18nProps, BareProps } from '@polkadot/ui-app/types';
|
||||
import { RpcMethod } from '@polkadot/jsonrpc/types';
|
||||
import { KeyringPair } from '@polkadot/keyring/types';
|
||||
@ -14,8 +13,8 @@ import { QueueTx, QueueTx$MessageSetStatus, QueueTx$Result, QueueTx$Status } fro
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Modal } from '@polkadot/ui-app';
|
||||
import { withApi, withInjected, withMulti, withObservable } from '@polkadot/ui-api';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import { withApi, withMulti, withObservable } from '@polkadot/ui-api';
|
||||
import accountObservable from '@polkadot/ui-keyring/observable/accounts';
|
||||
import { assert, isFunction } from '@polkadot/util';
|
||||
import { format } from '@polkadot/util/logger';
|
||||
@ -29,7 +28,7 @@ type BaseProps = BareProps & {
|
||||
queueSetTxStatus: QueueTx$MessageSetStatus
|
||||
};
|
||||
|
||||
type Props = I18nProps & ApiProps & BaseProps & {
|
||||
type Props = I18nProps & ApiProps & ApiInjectedProps & BaseProps & {
|
||||
allAccounts?: SubjectInfo
|
||||
};
|
||||
|
||||
@ -40,14 +39,6 @@ type State = {
|
||||
unlockError?: string | null
|
||||
};
|
||||
|
||||
type WindowInjected = Window & {
|
||||
injectedWeb3: {
|
||||
[index: string]: {
|
||||
signer: ISigner
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Signer extends React.PureComponent<Props, State> {
|
||||
state: State = {
|
||||
isSendable: false,
|
||||
@ -322,7 +313,7 @@ class Signer extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
private async makeExtrinsicCall (extrinsic: SubmittableExtrinsic, { id, txFailedCb, txSuccessCb, txUpdateCb }: QueueTx, extrinsicCall: (...params: Array<any>) => any, pair?: KeyringPair): Promise<void> {
|
||||
const { api, queueSetTxStatus } = this.props;
|
||||
const { api, injectedPromise, queueSetTxStatus } = this.props;
|
||||
|
||||
console.log('makeExtrinsicCall: extrinsic ::', extrinsic.toHex());
|
||||
|
||||
@ -331,13 +322,13 @@ class Signer extends React.PureComponent<Props, State> {
|
||||
if (pair) {
|
||||
// set the signer
|
||||
if (pair.getMeta().isInjected) {
|
||||
const injWindow = window as WindowInjected;
|
||||
const source = pair.getMeta().source;
|
||||
const signer = source && injWindow.injectedWeb3 && injWindow.injectedWeb3[source].signer;
|
||||
const sources = await injectedPromise;
|
||||
const injected = source && sources.find(({ name }) => name === source);
|
||||
|
||||
assert(signer, 'Unable to find a signer');
|
||||
assert(injected, 'Unable to find a signer');
|
||||
|
||||
api.setSigner(signer);
|
||||
api.setSigner((injected as any).signer);
|
||||
|
||||
params.push(pair.address());
|
||||
} else {
|
||||
@ -403,5 +394,6 @@ export default withMulti(
|
||||
Signer,
|
||||
translate,
|
||||
withApi,
|
||||
withInjected,
|
||||
withObservable(accountObservable.subject, { propName: 'allAccounts' })
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user