Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/app-js/src/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ export default styled(Playground)`
}

.js--Dropdown {
margin-right: 100px;
position: relative;
z-index: 200;

Expand Down
237 changes: 86 additions & 151 deletions packages/react-api/src/Api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.

import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { QueueTxPayloadAdd, QueueTxMessageSetStatus } from '@polkadot/react-components/Status/types';
import { ApiProps } from './types';
import { ApiState } from './types';

import React from 'react';
import React, { useEffect, useState } from 'react';
import ApiPromise from '@polkadot/api/promise';
import { isWeb3Injected, web3Accounts, web3Enable } from '@polkadot/extension-dapp';
import defaults from '@polkadot/rpc-provider/defaults';
import { WsProvider } from '@polkadot/rpc-provider';
import { TokenUnit } from '@polkadot/react-components/InputNumber';
import keyring from '@polkadot/ui-keyring';
Expand All @@ -31,7 +29,7 @@ interface Props {
url?: string;
}

interface State extends ApiProps {
interface State extends ApiState {
chain?: string | null;
}

Expand All @@ -50,163 +48,100 @@ let api: ApiPromise;

export { api };

export default class Api extends React.PureComponent<Props, State> {
public state: State = {} as unknown as State;
async function loadOnReady (api: ApiPromise): Promise<State> {
const [properties, _systemChain, _systemName, _systemVersion, injectedAccounts] = await Promise.all([
api.rpc.system.properties(),
api.rpc.system.chain(),
api.rpc.system.name(),
api.rpc.system.version(),
web3Accounts().then((accounts): InjectedAccountExt[] =>
accounts.map(({ address, meta }): InjectedAccountExt => ({
address,
meta: {
...meta,
name: `${meta.name} (${meta.source === 'polkadot-js' ? 'extension' : meta.source})`
}
}))
)
]);
const ss58Format = uiSettings.prefix === -1
? properties.ss58Format.unwrapOr(DEFAULT_SS58).toNumber()
: uiSettings.prefix;
const tokenSymbol = properties.tokenSymbol.unwrapOr('DEV').toString();
const tokenDecimals = properties.tokenDecimals.unwrapOr(DEFAULT_DECIMALS).toNumber();
const systemChain = _systemChain
? _systemChain.toString()
: '<unknown>';
const isDevelopment = isTestChain(systemChain);

console.log('api: found chain', systemChain, JSON.stringify(properties));

// first setup the UI helpers
formatBalance.setDefaults({
decimals: tokenDecimals,
unit: tokenSymbol
});
TokenUnit.setAbbr(tokenSymbol);

// finally load the keyring
keyring.loadAll({
addressPrefix: ss58Format,
genesisHash: api.genesisHash,
isDevelopment,
ss58Format,
type: 'ed25519'
}, injectedAccounts);

const defaultSection = Object.keys(api.tx)[0];
const defaultMethod = Object.keys(api.tx[defaultSection])[0];
const apiDefaultTx = api.tx[defaultSection][defaultMethod];
const apiDefaultTxSudo = (api.tx.system && api.tx.system.setCode) || apiDefaultTx;
const isSubstrateV2 = !!Object.keys(api.consts).length;

return {
apiDefaultTx,
apiDefaultTxSudo,
isApiReady: true,
isDevelopment,
isSubstrateV2,
systemChain,
systemName: _systemName.toString(),
systemVersion: _systemVersion.toString()
} as State;
}

constructor (props: Props) {
super(props);
export default function Api ({ children, queuePayload, queueSetTxStatus, url }: Props): React.ReactElement<Props> | null {
const [state, setState] = useState<State>({ isApiReady: false } as Partial<State> as State);
const [isApiConnected, setIsApiConnected] = useState(false);
const [isWaitingInjected, setIsWaitingInjected] = useState(isWeb3Injected);

const { queuePayload, queueSetTxStatus, url } = props;
// initial initialization
useEffect((): void => {
const provider = new WsProvider(url);
const signer = new ApiSigner(queuePayload, queueSetTxStatus);

const setApi = (provider: ProviderInterface): void => {
api = this.createApi(provider, signer);

this.setState({ api }, (): void => {
this.subscribeEvents();
});
};
const setApiUrl = (url: string = defaults.WS_URL): void =>
setApi(new WsProvider(url));

api = this.createApi(provider, signer);

this.state = {
api,
isApiConnected: false,
isApiReady: false,
isSubstrateV2: true,
isWaitingInjected: isWeb3Injected,
setApiUrl
} as unknown as State;
}

private createApi (provider: ProviderInterface, signer: ApiSigner): ApiPromise {
return new ApiPromise({
provider,
registry,
signer,
typesChain,
typesSpec
});
}

public componentDidMount (): void {
this.subscribeEvents();

injectedPromise
.then((): void => this.setState({ isWaitingInjected: false }))
.catch((error: Error) => console.error(error));
}

private subscribeEvents (): void {
const { api } = this.state;

api.on('connected', (): void => {
this.setState({ isApiConnected: true });
});

api.on('disconnected', (): void => {
this.setState({ isApiConnected: false });
});
api = new ApiPromise({ provider, registry, signer, typesChain, typesSpec });

api.on('connected', (): void => setIsApiConnected(true));
api.on('disconnected', (): void => setIsApiConnected(false));
api.on('ready', async (): Promise<void> => {
try {
await this.loadOnReady(api);
setState(await loadOnReady(api));
} catch (error) {
console.error('Unable to load chain', error);
}
});
}

private async loadOnReady (api: ApiPromise): Promise<void> {
const [properties, _systemChain, _systemName, _systemVersion, injectedAccounts] = await Promise.all([
api.rpc.system.properties(),
api.rpc.system.chain(),
api.rpc.system.name(),
api.rpc.system.version(),
web3Accounts().then((accounts): InjectedAccountExt[] =>
accounts.map(({ address, meta }): InjectedAccountExt => ({
address,
meta: {
...meta,
name: `${meta.name} (${meta.source === 'polkadot-js' ? 'extension' : meta.source})`
}
}))
)
]);
const ss58Format = uiSettings.prefix === -1
? properties.ss58Format.unwrapOr(DEFAULT_SS58).toNumber()
: uiSettings.prefix;
const tokenSymbol = properties.tokenSymbol.unwrapOr('DEV').toString();
const tokenDecimals = properties.tokenDecimals.unwrapOr(DEFAULT_DECIMALS).toNumber();
const systemChain = _systemChain
? _systemChain.toString()
: '<unknown>';
const isDevelopment = isTestChain(systemChain);

console.log('api: found chain', systemChain, JSON.stringify(properties));

// first setup the UI helpers
formatBalance.setDefaults({
decimals: tokenDecimals,
unit: tokenSymbol
});
TokenUnit.setAbbr(tokenSymbol);

// finally load the keyring
keyring.loadAll({
addressPrefix: ss58Format,
genesisHash: api.genesisHash,
isDevelopment,
ss58Format,
type: 'ed25519'
}, injectedAccounts);

const defaultSection = Object.keys(api.tx)[0];
const defaultMethod = Object.keys(api.tx[defaultSection])[0];
const apiDefaultTx = api.tx[defaultSection][defaultMethod];
const apiDefaultTxSudo =
(api.tx.system && api.tx.system.setCode) || // 2.x
(api.tx.consensus && api.tx.consensus.setCode) || // 1.x
apiDefaultTx; // other
const isSubstrateV2 = !!Object.keys(api.consts).length;

this.setState({
apiDefaultTx,
apiDefaultTxSudo,
isApiReady: true,
isDevelopment,
isSubstrateV2,
systemChain,
systemName: _systemName.toString(),
systemVersion: _systemVersion.toString()
});
}

public render (): React.ReactNode {
const { api, apiDefaultTx, apiDefaultTxSudo, isApiConnected, isApiReady, isDevelopment, isSubstrateV2, isWaitingInjected, setApiUrl, systemChain, systemName, systemVersion } = this.state;

return (
<ApiContext.Provider
value={{
api,
apiDefaultTx,
apiDefaultTxSudo,
isApiConnected,
isApiReady: isApiReady && !!systemChain,
isDevelopment,
isSubstrateV2,
isWaitingInjected,
setApiUrl,
systemChain,
systemName,
systemVersion
}}
>
{this.props.children}

injectedPromise
.then((): void => setIsWaitingInjected(false))
.catch((error: Error) => console.error(error));
}, []);

return api
? (
<ApiContext.Provider value={{ ...state, api, isApiConnected, isWaitingInjected }}>
{children}
</ApiContext.Provider>
);
}
)
: null;
}
12 changes: 7 additions & 5 deletions packages/react-api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,23 @@ export interface BareProps {
style?: Record<string, any>;
}

export interface ApiProps {
api: ApiPromise;
export interface ApiState {
apiDefaultTx: SubmittableExtrinsicFunction;
apiDefaultTxSudo: SubmittableExtrinsicFunction;
isApiConnected: boolean;
isApiReady: boolean;
isDevelopment: boolean;
isSubstrateV2: boolean;
isWaitingInjected: boolean;
setApiUrl: (url?: string) => void;
systemChain: string;
systemName: string;
systemVersion: string;
}

export interface ApiProps extends ApiState {
api: ApiPromise;
isWaitingInjected: boolean;
isApiConnected: boolean;
}

export interface OnChangeCbObs {
next: (value?: any) => any;
}
Expand Down