diff --git a/packages/extension-dapp/src/compat/index.ts b/packages/extension-dapp/src/compat/index.ts new file mode 100644 index 00000000000..37dc3c4858b --- /dev/null +++ b/packages/extension-dapp/src/compat/index.ts @@ -0,0 +1,11 @@ +// Copyright 2019 @polkadot/extension-dapp 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 singleSource from './singleSource'; + +export default function initCompat (): Promise { + return Promise.all([ + singleSource() + ]).then(() => true); +} diff --git a/packages/extension-dapp/src/compat/singleSource.ts b/packages/extension-dapp/src/compat/singleSource.ts new file mode 100644 index 00000000000..a0bbdb547a4 --- /dev/null +++ b/packages/extension-dapp/src/compat/singleSource.ts @@ -0,0 +1,69 @@ +// Copyright 2019 @polkadot/extension-dapp 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 { Signer } from '@polkadot/api/types'; +import { InjectedAccount, InjectedWindow } from '../types'; + +// RxJs interface, only what we need here +type Subscriber = { + subscribe: (cb: (value: Array) => void) => void +}; + +type SingleSourceAccount = { + address: string, + assets: Array<{ assetId: number }>, + name: string +}; + +type SingleSource = { + accounts$: Subscriber, + environment$: Subscriber, + signer: Signer +}; + +type SingleWindow = Window & InjectedWindow & { + SingleSource: SingleSource +}; + +// add a compat interface of SingleSource to window.injectedWeb3 +function injectSingleSource (win: SingleWindow): void { + let accounts: Array = []; + + // we don't yet have an accounts subscribe on the interface, simply get the + // accounts and store them, any get will resolve the last found values + win.SingleSource.accounts$.subscribe((_accounts) => { + accounts = _accounts.map(({ address, name }) => ({ + address, + name + })); + }); + + // decorate the compat interface + win.injectedWeb3['SingleSource'] = { + enable: async (origin: string) => ({ + accounts: { + get: async () => accounts + }, + signer: win.SingleSource.signer + }), + version: '0.0.0' + }; +} + +// returns the SingleSource instance, as per +// https://github.com/cennznet/singlesource-extension/blob/f7cb35b54e820bf46339f6b88ffede1b8e140de0/react-example/src/App.js#L19 +export default function initSingleSource (): Promise { + return new Promise((resolve) => { + window.addEventListener('load', () => { + const win = window as SingleWindow; + + if (win.SingleSource) { + injectSingleSource(win); + resolve(true); + } else { + resolve(false); + } + }); + }); +} diff --git a/packages/extension-dapp/src/index.ts b/packages/extension-dapp/src/index.ts index 51bb56cef5e..2ec5657bf69 100644 --- a/packages/extension-dapp/src/index.ts +++ b/packages/extension-dapp/src/index.ts @@ -4,11 +4,22 @@ import { InjectedAccountWithMeta, InjectedExtension, InjectedExtensionInfo, InjectedWindow } from './types'; +// our extension adaptor for other kinds of extensions +import compatInjector from './compat'; + // just a helper (otherwise we cast all-over, so shorter and more readable) -const injectedWeb3 = (window as InjectedWindow).injectedWeb3 || {}; +const win = window as InjectedWindow; + +// don't clobber the existing object, but ensure non-undefined +win.injectedWeb3 = win.injectedWeb3 || {}; + +// true when anything has been injected and is available +function web3IsInjected (): boolean { + return Object.keys(win.injectedWeb3).length !== 0; +} // have we found a properly constructed window.injectedWeb3 -const isWeb3Injected = Object.keys(injectedWeb3).length !== 0; +let isWeb3Injected = web3IsInjected(); // we keep the last promise created around (for queries) let web3EnablePromise: Promise> | null = null; @@ -17,29 +28,27 @@ export { isWeb3Injected, web3EnablePromise }; // enables all the providers found on the injected window interface export function web3Enable (originName: string): Promise> { - web3EnablePromise = Promise - .all( - Object - .entries(injectedWeb3) - .map(([name, { enable, version }]) => - Promise - .all([Promise.resolve({ name, version }), enable(originName)]) - .catch(() => [{ name, version }, null] as [InjectedExtensionInfo, null]) - ) + web3EnablePromise = compatInjector().then(() => + Promise.all( + Object.entries(win.injectedWeb3).map(([name, { enable, version }]) => + Promise + .all([Promise.resolve({ name, version }), enable(originName)]) + .catch(() => [{ name, version }, null] as [InjectedExtensionInfo, null]) + ) ) .then((values) => - values - .filter(([, ext]) => ext !== null) - .map(([info, ext]) => ({ ...info, ...ext } as InjectedExtension)) + values.filter(([, ext]) => ext).map(([info, ext]) => ({ ...info, ...ext } as InjectedExtension)) ) .catch(() => [] as Array) .then((values) => { const names = values.map(({ name, version }) => `${name}/${version}`); + isWeb3Injected = web3IsInjected(); console.log(`web3Enable: Enabled ${values.length} extensions ${names.join(', ')}`); return values; - }); + }) + ); return web3EnablePromise; }