Swap to eslint (#72)
* 626 problems (430 errors, 196 warnings) * A start. * < 200 * 80 * 0
This commit is contained in:
parent
da16a494e8
commit
c70d9a0e63
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
**/build/*
|
||||||
|
**/coverage/*
|
||||||
|
**/node_modules/*
|
||||||
1
.eslintrc.js
Normal file
1
.eslintrc.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('@polkadot/dev-react/config/eslint');
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const config = require('@polkadot/dev/config/jest');
|
const config = require('@polkadot/dev/config/jest');
|
||||||
|
|
||||||
module.exports = Object.assign({}, config, {
|
module.exports = Object.assign({}, config, {
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
"build": "NODE_ENV=production polkadot-dev-build-ts",
|
"build": "NODE_ENV=production polkadot-dev-build-ts",
|
||||||
"build:zip": "(cd packages/extension/build && zip -r -FS ../../../master-build.zip *)",
|
"build:zip": "(cd packages/extension/build && zip -r -FS ../../../master-build.zip *)",
|
||||||
"check": "yarn lint",
|
"check": "yarn lint",
|
||||||
"lint": "tslint --project . && tsc --noEmit --pretty",
|
"lint": "eslint --ext .js,.jsx,.ts,.tsx . && tsc --noEmit --pretty",
|
||||||
"clean": "polkadot-dev-clean-build",
|
"clean": "polkadot-dev-clean-build",
|
||||||
"postinstall": "polkadot-dev-yarn-only",
|
"postinstall": "polkadot-dev-yarn-only",
|
||||||
"test": "echo \"no tests... yet\"",
|
"test": "echo \"no tests... yet\"",
|
||||||
|
|||||||
@ -8,5 +8,5 @@ import singleSource from './singleSource';
|
|||||||
export default function initCompat (): Promise<boolean> {
|
export default function initCompat (): Promise<boolean> {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
singleSource()
|
singleSource()
|
||||||
]).then(() => true);
|
]).then((): boolean => true);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,34 +3,34 @@
|
|||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { Signer } from '@polkadot/api/types';
|
import { Signer } from '@polkadot/api/types';
|
||||||
import { InjectedAccount, InjectedWindow } from '../types';
|
import { Injected, InjectedAccount, InjectedWindow } from '../types';
|
||||||
|
|
||||||
// RxJs interface, only the bare-bones of what we need here
|
// RxJs interface, only the bare-bones of what we need here
|
||||||
type Subscriber<T> = {
|
interface Subscriber<T> {
|
||||||
subscribe: (cb: (value: T) => void) => {
|
subscribe: (cb: (value: T) => void) => {
|
||||||
unsubscribe (): any
|
unsubscribe (): void;
|
||||||
}
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
type SingleSourceAccount = {
|
interface SingleSourceAccount {
|
||||||
address: string,
|
address: string;
|
||||||
assets: Array<{ assetId: number }>,
|
assets: { assetId: number }[];
|
||||||
name: string
|
name: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
type SingleSource = {
|
interface SingleSource {
|
||||||
accounts$: Subscriber<Array<SingleSourceAccount>>,
|
accounts$: Subscriber<SingleSourceAccount[]>;
|
||||||
environment$: Subscriber<string>,
|
environment$: string[];
|
||||||
signer: Signer
|
signer: Signer;
|
||||||
};
|
}
|
||||||
|
|
||||||
type SingleWindow = Window & InjectedWindow & {
|
type SingleWindow = Window & InjectedWindow & {
|
||||||
SingleSource: SingleSource
|
SingleSource: SingleSource;
|
||||||
};
|
};
|
||||||
|
|
||||||
// transfor the SingleSource accounts into a simple address/name array
|
// transfor the SingleSource accounts into a simple address/name array
|
||||||
function transformAccounts (accounts: Array<SingleSourceAccount>): Array<InjectedAccount> {
|
function transformAccounts (accounts: SingleSourceAccount[]): InjectedAccount[] {
|
||||||
return accounts.map(({ address, name }) => ({
|
return accounts.map(({ address, name }): InjectedAccount => ({
|
||||||
address,
|
address,
|
||||||
name
|
name
|
||||||
}));
|
}));
|
||||||
@ -38,24 +38,25 @@ function transformAccounts (accounts: Array<SingleSourceAccount>): Array<Injecte
|
|||||||
|
|
||||||
// add a compat interface of SingleSource to window.injectedWeb3
|
// add a compat interface of SingleSource to window.injectedWeb3
|
||||||
function injectSingleSource (win: SingleWindow): void {
|
function injectSingleSource (win: SingleWindow): void {
|
||||||
let accounts: Array<InjectedAccount> = [];
|
let accounts: InjectedAccount[] = [];
|
||||||
|
|
||||||
// we don't yet have an accounts subscribe on the interface, simply get the
|
// 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
|
// accounts and store them, any get will resolve the last found values
|
||||||
win.SingleSource.accounts$.subscribe((_accounts) => {
|
win.SingleSource.accounts$.subscribe((_accounts): void => {
|
||||||
accounts = transformAccounts(_accounts);
|
accounts = transformAccounts(_accounts);
|
||||||
});
|
});
|
||||||
|
|
||||||
// decorate the compat interface
|
// decorate the compat interface
|
||||||
win.injectedWeb3['SingleSource'] = {
|
win.injectedWeb3['SingleSource'] = {
|
||||||
enable: async (origin: string) => ({
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
enable: async (origin: string): Promise<Injected> => ({
|
||||||
accounts: {
|
accounts: {
|
||||||
get: async () =>
|
get: async (): Promise<InjectedAccount[]> =>
|
||||||
accounts,
|
accounts,
|
||||||
subscribe: (cb: (accounts: Array<InjectedAccount>) => any) => {
|
subscribe: (cb: (accounts: InjectedAccount[]) => void): () => void => {
|
||||||
const sub = win.SingleSource.accounts$.subscribe((accounts) =>
|
const sub = win.SingleSource.accounts$.subscribe((accounts): void => {
|
||||||
cb(transformAccounts(accounts))
|
cb(transformAccounts(accounts));
|
||||||
);
|
});
|
||||||
|
|
||||||
return (): void => {
|
return (): void => {
|
||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
@ -71,8 +72,8 @@ function injectSingleSource (win: SingleWindow): void {
|
|||||||
// returns the SingleSource instance, as per
|
// returns the SingleSource instance, as per
|
||||||
// https://github.com/cennznet/singlesource-extension/blob/f7cb35b54e820bf46339f6b88ffede1b8e140de0/react-example/src/App.js#L19
|
// https://github.com/cennznet/singlesource-extension/blob/f7cb35b54e820bf46339f6b88ffede1b8e140de0/react-example/src/App.js#L19
|
||||||
export default function initSingleSource (): Promise<boolean> {
|
export default function initSingleSource (): Promise<boolean> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve): void => {
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', (): void => {
|
||||||
const win = window as SingleWindow;
|
const win = window as SingleWindow;
|
||||||
|
|
||||||
if (win.SingleSource) {
|
if (win.SingleSource) {
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { InjectedAccount, InjectedAccountWithMeta, InjectedExtension, InjectedWindow, Unsubcall } from './types';
|
import { Injected, InjectedAccount, InjectedAccountWithMeta, InjectedExtension, InjectedExtensionInfo, InjectedWindow, Unsubcall } from './types';
|
||||||
|
|
||||||
// our extension adaptor for other kinds of extensions
|
// our extension adaptor for other kinds of extensions
|
||||||
import compatInjector from './compat';
|
import compatInjector from './compat';
|
||||||
@ -24,8 +24,8 @@ function throwError (method: string): never {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// internal helper to map from Array<InjectedAccount> -> Array<InjectedAccountWithMeta>
|
// internal helper to map from Array<InjectedAccount> -> Array<InjectedAccountWithMeta>
|
||||||
function mapAccounts (source: string, list: Array<InjectedAccount>): Array<InjectedAccountWithMeta> {
|
function mapAccounts (source: string, list: InjectedAccount[]): InjectedAccountWithMeta[] {
|
||||||
return list.map(({ address, name }) => ({
|
return list.map(({ address, name }): InjectedAccountWithMeta => ({
|
||||||
address,
|
address,
|
||||||
meta: { name, source }
|
meta: { name, source }
|
||||||
}));
|
}));
|
||||||
@ -35,65 +35,70 @@ function mapAccounts (source: string, list: Array<InjectedAccount>): Array<Injec
|
|||||||
let isWeb3Injected = web3IsInjected();
|
let isWeb3Injected = web3IsInjected();
|
||||||
|
|
||||||
// we keep the last promise created around (for queries)
|
// we keep the last promise created around (for queries)
|
||||||
let web3EnablePromise: Promise<Array<InjectedExtension>> | null = null;
|
let web3EnablePromise: Promise<InjectedExtension[]> | null = null;
|
||||||
|
|
||||||
export { isWeb3Injected, web3EnablePromise };
|
export { isWeb3Injected, web3EnablePromise };
|
||||||
|
|
||||||
// enables all the providers found on the injected window interface
|
// enables all the providers found on the injected window interface
|
||||||
export function web3Enable (originName: string): Promise<Array<InjectedExtension>> {
|
export function web3Enable (originName: string): Promise<InjectedExtension[]> {
|
||||||
web3EnablePromise = compatInjector().then(() =>
|
web3EnablePromise = compatInjector()
|
||||||
Promise.all(
|
.then((): Promise<InjectedExtension[]> =>
|
||||||
Object.entries(win.injectedWeb3).map(([name, { enable, version }]) =>
|
Promise
|
||||||
Promise.all([
|
.all(
|
||||||
Promise.resolve({ name, version }),
|
Object.entries(win.injectedWeb3).map(([name, { enable, version }]): Promise<[InjectedExtensionInfo, Injected | void]> =>
|
||||||
enable(originName).catch((error: Error) => {
|
Promise.all([
|
||||||
console.error(`Error initializing ${name}: ${error.message}`);
|
Promise.resolve({ name, version }),
|
||||||
})
|
enable(originName).catch((error: Error): void => {
|
||||||
])
|
console.error(`Error initializing ${name}: ${error.message}`);
|
||||||
)
|
})
|
||||||
)
|
])
|
||||||
.then((values) =>
|
)
|
||||||
values
|
)
|
||||||
.filter(([, ext]) => ext)
|
.then((values: [InjectedExtensionInfo, Injected | void][]): InjectedExtension[] =>
|
||||||
.map(([info, ext]) => {
|
values
|
||||||
// if we don't have an accounts subscriber, add a single-shot version
|
.filter(([, ext]): boolean => !!ext)
|
||||||
if (ext && !ext.accounts.subscribe) {
|
.map(([info, ext]): InjectedExtension => {
|
||||||
ext.accounts.subscribe = (cb: (accounts: Array<InjectedAccount>) => any): Unsubcall => {
|
// if we don't have an accounts subscriber, add a single-shot version
|
||||||
ext.accounts.get().then(cb).catch(console.error);
|
if (ext && !ext.accounts.subscribe) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
ext.accounts.subscribe = (cb: (accounts: InjectedAccount[]) => any): Unsubcall => {
|
||||||
|
ext.accounts.get().then(cb).catch(console.error);
|
||||||
|
|
||||||
return (): void => {
|
return (): void => {
|
||||||
// no ubsubscribe needed, this is a single-shot
|
// no ubsubscribe needed, this is a single-shot
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...info, ...ext } as InjectedExtension;
|
const injected: Partial<InjectedExtension> = { ...info, ...ext };
|
||||||
|
|
||||||
|
return injected as InjectedExtension;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch((): InjectedExtension[] => [] as InjectedExtension[])
|
||||||
|
.then((values): InjectedExtension[] => {
|
||||||
|
const names = values.map(({ name, version }): string => `${name}/${version}`);
|
||||||
|
|
||||||
|
isWeb3Injected = web3IsInjected();
|
||||||
|
console.log(`web3Enable: Enabled ${values.length} extension${values.length !== 1 ? 's' : ''}: ${names.join(', ')}`);
|
||||||
|
|
||||||
|
return values;
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
.catch(() => [] as Array<InjectedExtension>)
|
|
||||||
.then((values) => {
|
|
||||||
const names = values.map(({ name, version }) => `${name}/${version}`);
|
|
||||||
|
|
||||||
isWeb3Injected = web3IsInjected();
|
|
||||||
console.log(`web3Enable: Enabled ${values.length} extension${values.length !== 1 ? 's' : ''}: ${names.join(', ')}`);
|
|
||||||
|
|
||||||
return values;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return web3EnablePromise;
|
return web3EnablePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieve all the accounts accross all providers
|
// retrieve all the accounts accross all providers
|
||||||
export async function web3Accounts (): Promise<Array<InjectedAccountWithMeta>> {
|
export async function web3Accounts (): Promise<InjectedAccountWithMeta[]> {
|
||||||
if (!web3EnablePromise) {
|
if (!web3EnablePromise) {
|
||||||
return throwError('web3Accounts');
|
return throwError('web3Accounts');
|
||||||
}
|
}
|
||||||
|
|
||||||
const accounts: Array<InjectedAccountWithMeta> = [];
|
const accounts: InjectedAccountWithMeta[] = [];
|
||||||
const injected = await web3EnablePromise;
|
const injected = await web3EnablePromise;
|
||||||
const retrieved = await Promise.all(
|
const retrieved = await Promise.all(
|
||||||
injected.map(async ({ accounts, name: source }) => {
|
injected.map(async ({ accounts, name: source }): Promise<InjectedAccountWithMeta[]> => {
|
||||||
try {
|
try {
|
||||||
const list = await accounts.get();
|
const list = await accounts.get();
|
||||||
|
|
||||||
@ -105,57 +110,46 @@ export async function web3Accounts (): Promise<Array<InjectedAccountWithMeta>> {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
retrieved.forEach((result) => accounts.push(...result));
|
retrieved.forEach((result): void => {
|
||||||
|
accounts.push(...result);
|
||||||
|
});
|
||||||
|
|
||||||
const addresses = accounts.map(({ address }) => address);
|
const addresses = accounts.map(({ address }): string => address);
|
||||||
|
|
||||||
console.log(`web3Accounts: Found ${accounts.length} address${accounts.length !== 1 ? 'es' : ''}: ${addresses.join(', ')}`);
|
console.log(`web3Accounts: Found ${accounts.length} address${accounts.length !== 1 ? 'es' : ''}: ${addresses.join(', ')}`);
|
||||||
|
|
||||||
return accounts;
|
return accounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function web3AccountsSubscribe (cb: (accounts: Array<InjectedAccountWithMeta>) => any): Promise<Unsubcall> {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export async function web3AccountsSubscribe (cb: (accounts: InjectedAccountWithMeta[]) => any): Promise<Unsubcall> {
|
||||||
if (!web3EnablePromise) {
|
if (!web3EnablePromise) {
|
||||||
return throwError('web3AccountsSubscribe');
|
return throwError('web3AccountsSubscribe');
|
||||||
}
|
}
|
||||||
|
|
||||||
const accounts: { [source: string]: Array<InjectedAccount> } = {};
|
const accounts: Record<string, InjectedAccount[]> = {};
|
||||||
const triggerUpdate = (): void => {
|
const triggerUpdate = (): void => {
|
||||||
cb(Object.entries(accounts).reduce((result, [source, list]) => {
|
cb(Object.entries(accounts).reduce((result, [source, list]): InjectedAccountWithMeta[] => {
|
||||||
result.push(...mapAccounts(source, list));
|
result.push(...mapAccounts(source, list));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}, [] as Array<InjectedAccountWithMeta>));
|
}, [] as InjectedAccountWithMeta[]));
|
||||||
};
|
};
|
||||||
|
|
||||||
const unsubs = (await web3EnablePromise).map(({ accounts: { subscribe }, name: source }) =>
|
const unsubs = (await web3EnablePromise).map(({ accounts: { subscribe }, name: source }): Unsubcall =>
|
||||||
subscribe((result) => {
|
subscribe((result): void => {
|
||||||
accounts[source] = result;
|
accounts[source] = result;
|
||||||
triggerUpdate();
|
triggerUpdate();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return (): void => {
|
return (): void => {
|
||||||
unsubs.forEach((unsub) => unsub());
|
unsubs.forEach((unsub): void => {
|
||||||
|
unsub();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// find a specific provider based on an address
|
|
||||||
export async function web3FromAddress (address: string): Promise<InjectedExtension> {
|
|
||||||
if (!web3EnablePromise) {
|
|
||||||
return throwError('web3FromAddress');
|
|
||||||
}
|
|
||||||
|
|
||||||
const accounts = await web3Accounts();
|
|
||||||
const found = address && accounts.find((account) => account.address === address);
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
throw new Error(`web3FromAddress: Unable to find injected ${address}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return web3FromSource(found.meta.source);
|
|
||||||
}
|
|
||||||
|
|
||||||
// find a specific provider based on the name
|
// find a specific provider based on the name
|
||||||
export async function web3FromSource (source: string): Promise<InjectedExtension> {
|
export async function web3FromSource (source: string): Promise<InjectedExtension> {
|
||||||
if (!web3EnablePromise) {
|
if (!web3EnablePromise) {
|
||||||
@ -163,7 +157,7 @@ export async function web3FromSource (source: string): Promise<InjectedExtension
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sources = await web3EnablePromise;
|
const sources = await web3EnablePromise;
|
||||||
const found = source && sources.find(({ name }) => name === source);
|
const found = source && sources.find(({ name }): boolean => name === source);
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
throw new Error(`web3FromSource: Unable to find an injected ${source}`);
|
throw new Error(`web3FromSource: Unable to find an injected ${source}`);
|
||||||
@ -171,3 +165,19 @@ export async function web3FromSource (source: string): Promise<InjectedExtension
|
|||||||
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// find a specific provider based on an address
|
||||||
|
export async function web3FromAddress (address: string): Promise<InjectedExtension> {
|
||||||
|
if (!web3EnablePromise) {
|
||||||
|
return throwError('web3FromAddress');
|
||||||
|
}
|
||||||
|
|
||||||
|
const accounts = await web3Accounts();
|
||||||
|
const found = address && accounts.find((account): boolean => account.address === address);
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
throw new Error(`web3FromAddress: Unable to find injected ${address}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return web3FromSource(found.meta.source);
|
||||||
|
}
|
||||||
|
|||||||
@ -14,17 +14,18 @@ export interface InjectedAccount {
|
|||||||
export interface InjectedAccountWithMeta {
|
export interface InjectedAccountWithMeta {
|
||||||
address: string;
|
address: string;
|
||||||
meta: {
|
meta: {
|
||||||
name: string,
|
name: string;
|
||||||
source: string
|
source: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InjectedAccounts {
|
export interface InjectedAccounts {
|
||||||
get: () => Promise<Array<InjectedAccount>>;
|
get: () => Promise<InjectedAccount[]>;
|
||||||
subscribe: (cb: (accounts: Array<InjectedAccount>) => any) => Unsubcall;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
subscribe: (cb: (accounts: InjectedAccount[]) => any) => Unsubcall;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InjectedSigner extends Signer {}
|
export type InjectedSigner = Signer;
|
||||||
|
|
||||||
export interface InjectedExtensionInfo {
|
export interface InjectedExtensionInfo {
|
||||||
name: string;
|
name: string;
|
||||||
@ -41,10 +42,8 @@ export interface InjectedWindowProvider {
|
|||||||
version: string;
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InjectedWindow = Window & {
|
export interface InjectedWindow extends Window {
|
||||||
injectedWeb3: {
|
injectedWeb3: Record<string, InjectedWindowProvider>;
|
||||||
[index: string]: InjectedWindowProvider
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InjectedExtension = InjectedExtensionInfo & Injected;
|
export type InjectedExtension = InjectedExtensionInfo & Injected;
|
||||||
|
|||||||
@ -11,22 +11,22 @@ import { ActionBar, Address, Link, withOnAction } from '../../components';
|
|||||||
import { editAccount } from '../../messaging';
|
import { editAccount } from '../../messaging';
|
||||||
import { Name } from '../../partials';
|
import { Name } from '../../partials';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
address: string,
|
address: string;
|
||||||
className?: string,
|
className?: string;
|
||||||
onAction: OnActionFromCtx
|
onAction: OnActionFromCtx;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Account ({ address, className, onAction }: Props) {
|
function Account ({ address, className, onAction }: Props): React.ReactElement<Props> {
|
||||||
const [isEditing, setEditing] = useState(false);
|
const [isEditing, setEditing] = useState(false);
|
||||||
const [editedname, setName] = useState<string | null>(null);
|
const [editedname, setName] = useState<string | null>(null);
|
||||||
|
|
||||||
const toggleEdit = () =>
|
const toggleEdit = (): void =>
|
||||||
setEditing(!isEditing);
|
setEditing(!isEditing);
|
||||||
const saveChanges = () => {
|
const saveChanges = (): void => {
|
||||||
if (editedname && editedname !== name) {
|
if (editedname && editedname !== name) {
|
||||||
editAccount(address, editedname)
|
editAccount(address, editedname)
|
||||||
.then(() => onAction())
|
.then((): void => onAction())
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,18 +9,18 @@ import React from 'react';
|
|||||||
import { Button, Header, Tip, withAccounts } from '../../components';
|
import { Button, Header, Tip, withAccounts } from '../../components';
|
||||||
import Account from './Account';
|
import Account from './Account';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
accounts: AccountsFromCtx
|
accounts: AccountsFromCtx;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Accounts ({ accounts }: Props) {
|
function Accounts ({ accounts }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header label='accounts' />
|
<Header label='accounts' />
|
||||||
{
|
{
|
||||||
(accounts.length === 0)
|
(accounts.length === 0)
|
||||||
? <Tip header='add accounts' type='warn'>You currently don't have any accounts. Either create a new account or if you have an existing account you wish to use, import it with the seed phrase</Tip>
|
? <Tip header='add accounts' type='warn'>You currently don't have any accounts. Either create a new account or if you have an existing account you wish to use, import it with the seed phrase</Tip>
|
||||||
: accounts.map(({ address }) => (
|
: accounts.map(({ address }): React.ReactNode => (
|
||||||
<Account
|
<Account
|
||||||
address={address}
|
address={address}
|
||||||
key={address}
|
key={address}
|
||||||
|
|||||||
@ -11,23 +11,23 @@ import styled from 'styled-components';
|
|||||||
import { ActionBar, Button, Icon, IconBox, Link, Tip, defaults, withOnAction } from '../../components';
|
import { ActionBar, Button, Icon, IconBox, Link, Tip, defaults, withOnAction } from '../../components';
|
||||||
import { approveAuthRequest, rejectAuthRequest } from '../../messaging';
|
import { approveAuthRequest, rejectAuthRequest } from '../../messaging';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
authId: string,
|
authId: string;
|
||||||
className?: string,
|
className?: string;
|
||||||
isFirst: boolean,
|
isFirst: boolean;
|
||||||
onAction: OnActionFromCtx,
|
onAction: OnActionFromCtx;
|
||||||
request: MessageAuthorize,
|
request: MessageAuthorize;
|
||||||
url: string
|
url: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Request ({ authId, className, isFirst, onAction, request: { origin }, url }: Props) {
|
function Request ({ authId, className, isFirst, onAction, request: { origin }, url }: Props): React.ReactElement<Props> {
|
||||||
const onApprove = () =>
|
const onApprove = (): Promise<void> =>
|
||||||
approveAuthRequest(authId)
|
approveAuthRequest(authId)
|
||||||
.then(() => onAction())
|
.then((): void => onAction())
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
const onReject = () =>
|
const onReject = (): Promise<void> =>
|
||||||
rejectAuthRequest(authId)
|
rejectAuthRequest(authId)
|
||||||
.then(() => onAction())
|
.then((): void => onAction())
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -9,15 +9,15 @@ import React from 'react';
|
|||||||
import { Header, withAuthRequests } from '../../components';
|
import { Header, withAuthRequests } from '../../components';
|
||||||
import Request from './Request';
|
import Request from './Request';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
requests: AuthRequestsFromCtx
|
requests: AuthRequestsFromCtx;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Authorize ({ requests }: Props) {
|
function Authorize ({ requests }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header label='authorize' />
|
<Header label='authorize' />
|
||||||
{requests.map(([id, request, url], index) => (
|
{requests.map(([id, request, url], index): React.ReactNode => (
|
||||||
<Request
|
<Request
|
||||||
authId={id}
|
authId={id}
|
||||||
isFirst={index === 0}
|
isFirst={index === 0}
|
||||||
|
|||||||
@ -10,27 +10,27 @@ import { Address, Button, Header, Loading, TextArea, withOnAction } from '../com
|
|||||||
import { createAccount, createSeed } from '../messaging';
|
import { createAccount, createSeed } from '../messaging';
|
||||||
import { Back, Name, Password } from '../partials';
|
import { Back, Name, Password } from '../partials';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
onAction: OnActionFromCtx
|
onAction: OnActionFromCtx;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Create ({ onAction }: Props) {
|
function Create ({ onAction }: Props): React.ReactElement<Props> {
|
||||||
const [account, setAccount] = useState<null | { address: string, seed: string }>(null);
|
const [account, setAccount] = useState<null | { address: string; seed: string }>(null);
|
||||||
const [name, setName] = useState<string | null>(null);
|
const [name, setName] = useState<string | null>(null);
|
||||||
const [password, setPassword] = useState<string | null>(null);
|
const [password, setPassword] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect((): void => {
|
||||||
createSeed()
|
createSeed()
|
||||||
.then(setAccount)
|
.then(setAccount)
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// FIXME Duplicated between here and Import.tsx
|
// FIXME Duplicated between here and Import.tsx
|
||||||
const onCreate = () => {
|
const onCreate = (): void => {
|
||||||
// this should always be the case
|
// this should always be the case
|
||||||
if (name && password && account) {
|
if (name && password && account) {
|
||||||
createAccount(name, password, account.seed)
|
createAccount(name, password, account.seed)
|
||||||
.then(() => onAction('/'))
|
.then((): void => onAction('/'))
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,14 +11,14 @@ import { Address, Button, Header, Tip, withOnAction } from '../components';
|
|||||||
import { forgetAccount } from '../messaging';
|
import { forgetAccount } from '../messaging';
|
||||||
import { Back } from '../partials';
|
import { Back } from '../partials';
|
||||||
|
|
||||||
type Props = RouteComponentProps<{ address: string }> & {
|
interface Props extends RouteComponentProps<{ address: string }> {
|
||||||
onAction: OnActionFromCtx
|
onAction: OnActionFromCtx;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Forget ({ match: { params: { address } }, onAction }: Props) {
|
function Forget ({ match: { params: { address } }, onAction }: Props): React.ReactElement<Props> {
|
||||||
const onClick = () =>
|
const onClick = (): Promise<void> =>
|
||||||
forgetAccount(address)
|
forgetAccount(address)
|
||||||
.then(() => onAction('/'))
|
.then((): void => onAction('/'))
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -10,26 +10,26 @@ import { Address, Button, Header, TextArea, withOnAction } from '../components';
|
|||||||
import { createAccount, validateSeed } from '../messaging';
|
import { createAccount, validateSeed } from '../messaging';
|
||||||
import { Back, Name, Password } from '../partials';
|
import { Back, Name, Password } from '../partials';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
onAction: OnActionFromCtx
|
onAction: OnActionFromCtx;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Import ({ onAction }: Props) {
|
function Import ({ onAction }: Props): React.ReactElement<Props> {
|
||||||
const [account, setAccount] = useState<null | { address: string, seed: string }>(null);
|
const [account, setAccount] = useState<null | { address: string; seed: string }>(null);
|
||||||
const [name, setName] = useState<string | null>(null);
|
const [name, setName] = useState<string | null>(null);
|
||||||
const [password, setPassword] = useState<string | null>(null);
|
const [password, setPassword] = useState<string | null>(null);
|
||||||
|
|
||||||
const onChangeSeed = (seed: string) =>
|
const onChangeSeed = (seed: string): Promise<void> =>
|
||||||
validateSeed(seed)
|
validateSeed(seed)
|
||||||
.then(setAccount)
|
.then(setAccount)
|
||||||
.catch(() => setAccount(null));
|
.catch((): void => setAccount(null));
|
||||||
|
|
||||||
// FIXME Duplicated between here and Create.tsx
|
// FIXME Duplicated between here and Create.tsx
|
||||||
const onCreate = () => {
|
const onCreate = (): void => {
|
||||||
// this should always be the case
|
// this should always be the case
|
||||||
if (name && password && account) {
|
if (name && password && account) {
|
||||||
createAccount(name, password, account.seed)
|
createAccount(name, password, account.seed)
|
||||||
.then(() => onAction('/'))
|
.then((): void => onAction('/'))
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,9 +9,10 @@ import findChain from '@polkadot/extension/chains';
|
|||||||
import { Metadata, Method, ExtrinsicEra } from '@polkadot/types';
|
import { Metadata, Method, ExtrinsicEra } from '@polkadot/types';
|
||||||
import { formatNumber } from '@polkadot/util';
|
import { formatNumber } from '@polkadot/util';
|
||||||
|
|
||||||
type MethodJson = {
|
interface MethodJson {
|
||||||
args: { [index: string]: any }
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
};
|
args: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
blockNumber: number;
|
blockNumber: number;
|
||||||
@ -24,7 +25,7 @@ interface Props {
|
|||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMethod (data: string, meta?: Metadata | null) {
|
function renderMethod (data: string, meta?: Metadata | null): React.ReactNode {
|
||||||
if (!meta) {
|
if (!meta) {
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
@ -73,7 +74,7 @@ function renderMortality (era: ExtrinsicEra, blockNumber: number): string {
|
|||||||
return `mortal (birth #${formatNumber(mortal.birth(blockNumber))}, death #${formatNumber(mortal.death(blockNumber))})`;
|
return `mortal (birth #${formatNumber(mortal.birth(blockNumber))}, death #${formatNumber(mortal.death(blockNumber))})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Details ({ blockNumber, className, genesisHash, isDecoded, era, method, nonce, url }: Props) {
|
function Details ({ blockNumber, className, genesisHash, isDecoded, era, method, nonce, url }: Props): React.ReactElement<Props> {
|
||||||
const chain = findChain(genesisHash);
|
const chain = findChain(genesisHash);
|
||||||
const eera = new ExtrinsicEra(era);
|
const eera = new ExtrinsicEra(era);
|
||||||
|
|
||||||
|
|||||||
@ -12,22 +12,22 @@ import { approveSignRequest, cancelSignRequest } from '../../messaging';
|
|||||||
import Details from './Details';
|
import Details from './Details';
|
||||||
import Unlock from './Unlock';
|
import Unlock from './Unlock';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
isFirst: boolean,
|
isFirst: boolean;
|
||||||
onAction: OnActionFromCtx,
|
onAction: OnActionFromCtx;
|
||||||
request: MessageExtrinsicSign,
|
request: MessageExtrinsicSign;
|
||||||
signId: string,
|
signId: string;
|
||||||
url: string
|
url: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Request ({ isFirst, onAction, request: { address, blockNumber, era, genesisHash, method, nonce }, signId, url }: Props) {
|
function Request ({ isFirst, onAction, request: { address, blockNumber, era, genesisHash, method, nonce }, signId, url }: Props): React.ReactElement<Props> {
|
||||||
const onCancel = () =>
|
const onCancel = (): Promise<void> =>
|
||||||
cancelSignRequest(signId)
|
cancelSignRequest(signId)
|
||||||
.then(() => onAction())
|
.then((): void => onAction())
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
const onSign = (password: string) =>
|
const onSign = (password: string): Promise<void> =>
|
||||||
approveSignRequest(signId, password)
|
approveSignRequest(signId, password)
|
||||||
.then(() => onAction());
|
.then((): void => onAction());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Address address={address}>
|
<Address address={address}>
|
||||||
|
|||||||
@ -6,20 +6,20 @@ import React, { useState, useEffect } from 'react';
|
|||||||
|
|
||||||
import { Button, Input } from '../../components';
|
import { Button, Input } from '../../components';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
className?: string,
|
className?: string;
|
||||||
onSign: (password: string) => Promise<void>
|
onSign: (password: string) => Promise<void>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default function Unlock ({ className, onSign }: Props) {
|
export default function Unlock ({ className, onSign }: Props): React.ReactElement<Props> {
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
|
|
||||||
const onClick = () =>
|
const onClick = (): Promise<void> =>
|
||||||
onSign(password)
|
onSign(password)
|
||||||
.catch((error) => setError(error.message));
|
.catch((error): void => setError(error.message));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect((): void => {
|
||||||
if (error) {
|
if (error) {
|
||||||
setError('');
|
setError('');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,15 +9,15 @@ import React from 'react';
|
|||||||
import { Header, withSignRequests } from '../../components';
|
import { Header, withSignRequests } from '../../components';
|
||||||
import Request from './Request';
|
import Request from './Request';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
requests: SignRequestsFromCtx
|
requests: SignRequestsFromCtx;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Signing ({ requests }: Props) {
|
function Signing ({ requests }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header label='transactions' />
|
<Header label='transactions' />
|
||||||
{requests.map(([id, request, url], index) => (
|
{requests.map(([id, request, url], index): React.ReactNode => (
|
||||||
<Request
|
<Request
|
||||||
isFirst={index === 0}
|
isFirst={index === 0}
|
||||||
key={id}
|
key={id}
|
||||||
|
|||||||
@ -8,11 +8,11 @@ import React from 'react';
|
|||||||
|
|
||||||
import { Box, Button, Header, withOnAction } from '../components';
|
import { Box, Button, Header, withOnAction } from '../components';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
onAction: OnActionFromCtx
|
onAction: OnActionFromCtx;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Welcome ({ onAction }: Props) {
|
function Welcome ({ onAction }: Props): React.ReactElement<Props> {
|
||||||
const onClick = (): void => {
|
const onClick = (): void => {
|
||||||
window.localStorage.setItem('welcome_read', 'ok');
|
window.localStorage.setItem('welcome_read', 'ok');
|
||||||
onAction();
|
onAction();
|
||||||
@ -26,7 +26,7 @@ function Welcome ({ onAction }: Props) {
|
|||||||
<ul>
|
<ul>
|
||||||
<li>We do not send any clicks, pageviews or events to a central server</li>
|
<li>We do not send any clicks, pageviews or events to a central server</li>
|
||||||
<li>We do not use any trackers or analytics</li>
|
<li>We do not use any trackers or analytics</li>
|
||||||
<li>We don't collect keys, addresses or any information - your information never leaves this machine</li>
|
<li>We don't collect keys, addresses or any information - your information never leaves this machine</li>
|
||||||
</ul>
|
</ul>
|
||||||
... we are not in the information collection business (even anonymized).
|
... we are not in the information collection business (even anonymized).
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -19,12 +19,11 @@ import Import from './Import';
|
|||||||
import Signing from './Signing';
|
import Signing from './Signing';
|
||||||
import Welcome from './Welcome';
|
import Welcome from './Welcome';
|
||||||
|
|
||||||
type Props = {};
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export default function Popup (props: {}): React.ReactElement<{}> {
|
||||||
export default function Popup (props: Props) {
|
const [accounts, setAccounts] = useState<null | KeyringJson[]>(null);
|
||||||
const [accounts, setAccounts] = useState<null | Array<KeyringJson>>(null);
|
const [authRequests, setAuthRequests] = useState<null | AuthorizeRequest[]>(null);
|
||||||
const [authRequests, setAuthRequests] = useState<null | Array<AuthorizeRequest>>(null);
|
const [signRequests, setSignRequests] = useState<null | SigningRequest[]>(null);
|
||||||
const [signRequests, setSignRequests] = useState<null | Array<SigningRequest>>(null);
|
|
||||||
const [isWelcomeDone, setWelcomeDone] = useState(false);
|
const [isWelcomeDone, setWelcomeDone] = useState(false);
|
||||||
|
|
||||||
const onAction = (to?: string): void => {
|
const onAction = (to?: string): void => {
|
||||||
|
|||||||
@ -5,12 +5,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
function ActionBar ({ children, className }: Props) {
|
function ActionBar ({ children, className }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -11,17 +11,17 @@ import Identicon from '@polkadot/ui-identicon';
|
|||||||
import IconBox from './IconBox';
|
import IconBox from './IconBox';
|
||||||
import { withAccounts } from './contexts';
|
import { withAccounts } from './contexts';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
accounts: AccountsFromCtx,
|
accounts: AccountsFromCtx;
|
||||||
address?: string | null,
|
address?: string | null;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string,
|
className?: string;
|
||||||
name?: React.ReactNode | null,
|
name?: React.ReactNode | null;
|
||||||
theme?: 'polkadot' | 'substrate'
|
theme?: 'polkadot' | 'substrate';
|
||||||
};
|
}
|
||||||
|
|
||||||
function Address ({ accounts, address, children, className, name, theme = 'polkadot' }: Props) {
|
function Address ({ accounts, address, children, className, name, theme = 'polkadot' }: Props): React.ReactElement<Props> {
|
||||||
const account = accounts.find((account) => account.address === address);
|
const account = accounts.find((account): boolean => account.address === address);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconBox
|
<IconBox
|
||||||
|
|||||||
@ -7,12 +7,12 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Box ({ children, className }: Props) {
|
function Box ({ children, className }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<article className={className}>
|
<article className={className}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -7,20 +7,20 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
children?: React.ReactNode,
|
children?: React.ReactNode;
|
||||||
isDanger?: boolean,
|
isDanger?: boolean;
|
||||||
isDisabled?: boolean,
|
isDisabled?: boolean;
|
||||||
isSmall?: boolean,
|
isSmall?: boolean;
|
||||||
label?: string,
|
label?: string;
|
||||||
onClick?: () => any,
|
onClick?: () => void | Promise<void>;
|
||||||
to?: string
|
to?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
const DISABLED_OPACITY = '0.3';
|
const DISABLED_OPACITY = '0.3';
|
||||||
|
|
||||||
function Button ({ children, className, isDisabled, label, onClick, to }: Props) {
|
function Button ({ children, className, isDisabled, label, onClick, to }: Props): React.ReactElement<Props> {
|
||||||
const _onClick = (): void => {
|
const _onClick = (): void => {
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
return;
|
return;
|
||||||
@ -44,54 +44,26 @@ function Button ({ children, className, isDisabled, label, onClick, to }: Props)
|
|||||||
|
|
||||||
export default styled(Button)`
|
export default styled(Button)`
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: ${({ isSmall }) =>
|
display: ${({ isSmall }): string => isSmall ? 'inline-block' : 'block'};
|
||||||
isSmall
|
|
||||||
? 'inline-block'
|
|
||||||
: 'block'
|
|
||||||
};
|
|
||||||
margin: ${defaults.boxMargin};
|
margin: ${defaults.boxMargin};
|
||||||
padding: ${defaults.boxPadding};
|
padding: ${defaults.boxPadding};
|
||||||
width: ${({ isSmall }) =>
|
width: ${({ isSmall }): string => isSmall ? 'auto' : '100%'};
|
||||||
isSmall
|
|
||||||
? 'auto'
|
|
||||||
: '100%'
|
|
||||||
};
|
|
||||||
|
|
||||||
button {
|
button {
|
||||||
background: ${({ isDanger }) =>
|
background: ${({ isDanger }): string => isDanger ? defaults.btnBgDanger : defaults.btnBg};
|
||||||
isDanger
|
border: ${defaults.btnBorder}${({ isDanger }): string => isDanger ? defaults.btnColorDanger : defaults.btnColor};
|
||||||
? defaults.btnBgDanger
|
|
||||||
: defaults.btnBg
|
|
||||||
};
|
|
||||||
border: ${defaults.btnBorder}${({ isDanger }) =>
|
|
||||||
isDanger
|
|
||||||
? defaults.btnColorDanger
|
|
||||||
: defaults.btnColor
|
|
||||||
};
|
|
||||||
border-radius: ${defaults.borderRadius};
|
border-radius: ${defaults.borderRadius};
|
||||||
color: ${({ isDanger }) =>
|
color: ${({ isDanger }): string => isDanger ? defaults.btnColorDanger : defaults.btnColor};
|
||||||
isDanger
|
|
||||||
? defaults.btnColorDanger
|
|
||||||
: defaults.btnColor
|
|
||||||
};
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
font-size: ${defaults.fontSize};
|
font-size: ${defaults.fontSize};
|
||||||
opacity: ${({ isDisabled }) =>
|
opacity: ${({ isDisabled }): string => isDisabled ? DISABLED_OPACITY : '0.8'};
|
||||||
isDisabled
|
|
||||||
? DISABLED_OPACITY
|
|
||||||
: '0.8'
|
|
||||||
};
|
|
||||||
padding: ${defaults.btnPadding};
|
padding: ${defaults.btnPadding};
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: ${({ isDisabled }) =>
|
opacity: ${({ isDisabled }): string => isDisabled ? DISABLED_OPACITY : '1.0'};
|
||||||
isDisabled
|
|
||||||
? DISABLED_OPACITY
|
|
||||||
: '1.0'
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -7,13 +7,13 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
children?: React.ReactNode,
|
children?: React.ReactNode;
|
||||||
className?: string,
|
className?: string;
|
||||||
label?: string
|
label?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Header ({ children, className, label }: Props) {
|
function Header ({ children, className, label }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<h2 className={className}>
|
<h2 className={className}>
|
||||||
{label}{children}
|
{label}{children}
|
||||||
|
|||||||
@ -5,13 +5,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
className?: string,
|
className?: string;
|
||||||
icon: string,
|
icon: string;
|
||||||
onClick?: () => any
|
onClick?: () => void | Promise<void>;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Icon ({ className, icon, onClick }: Props) {
|
function Icon ({ className, icon, onClick }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`${className} icon`}
|
className={`${className} icon`}
|
||||||
@ -26,10 +26,9 @@ export default styled(Icon)`
|
|||||||
background: white;
|
background: white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
cursor: ${({ onClick }) =>
|
cursor: ${({ onClick }): string =>
|
||||||
onClick
|
onClick
|
||||||
? 'pointer'
|
? 'pointer'
|
||||||
: 'inherit'
|
: 'inherit'};
|
||||||
};
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -8,16 +8,16 @@ import styled from 'styled-components';
|
|||||||
import Box from './Box';
|
import Box from './Box';
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string,
|
className?: string;
|
||||||
icon: React.ReactNode,
|
icon: React.ReactNode;
|
||||||
intro: React.ReactNode,
|
intro: React.ReactNode;
|
||||||
name?: React.ReactNode | null,
|
name?: React.ReactNode | null;
|
||||||
theme?: 'polkadot' | 'substrate'
|
theme?: 'polkadot' | 'substrate';
|
||||||
};
|
}
|
||||||
|
|
||||||
function IconBox ({ children, className, icon, intro }: Props) {
|
function IconBox ({ children, className, icon, intro }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<Box className='details'>
|
<Box className='details'>
|
||||||
|
|||||||
@ -8,20 +8,20 @@ import styled from 'styled-components';
|
|||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
className?: string,
|
className?: string;
|
||||||
defaultValue?: string | null,
|
defaultValue?: string | null;
|
||||||
isError?: boolean,
|
isError?: boolean;
|
||||||
isFocussed?: boolean,
|
isFocussed?: boolean;
|
||||||
isReadOnly?: boolean,
|
isReadOnly?: boolean;
|
||||||
label?: string | null,
|
label?: string | null;
|
||||||
onBlur?: () => void,
|
onBlur?: () => void;
|
||||||
onChange?: (value: string) => void,
|
onChange?: (value: string) => void;
|
||||||
type?: 'text' | 'password',
|
type?: 'text' | 'password';
|
||||||
value?: string
|
value?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Input ({ className, defaultValue, label, isFocussed, isReadOnly, onBlur, onChange, type = 'text', value }: Props) {
|
function Input ({ className, defaultValue, label, isFocussed, isReadOnly, onBlur, onChange, type = 'text', value }: Props): React.ReactElement<Props> {
|
||||||
const _onChange = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>): void => {
|
const _onChange = ({ target: { value } }: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
onChange && onChange(value.trim());
|
onChange && onChange(value.trim());
|
||||||
};
|
};
|
||||||
@ -46,35 +46,17 @@ function Input ({ className, defaultValue, label, isFocussed, isReadOnly, onBlur
|
|||||||
|
|
||||||
export default styled(Input)`
|
export default styled(Input)`
|
||||||
input {
|
input {
|
||||||
background: ${({ isError, isReadOnly }) =>
|
background: ${({ isError, isReadOnly }): string => isError ? defaults.box.error.background : (isReadOnly ? '#eee' : '#fff')};
|
||||||
isError
|
border-color: ${({ isError }): string => isError ? defaults.box.error.border : defaults.inputBorder};
|
||||||
? defaults.box.error.background
|
|
||||||
: isReadOnly
|
|
||||||
? '#eee'
|
|
||||||
: '#fff'
|
|
||||||
};
|
|
||||||
border-color: ${({ isError }) =>
|
|
||||||
isError
|
|
||||||
? defaults.box.error.border
|
|
||||||
: defaults.inputBorder
|
|
||||||
};
|
|
||||||
border-radius: ${defaults.borderRadius};
|
border-radius: ${defaults.borderRadius};
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: ${({ isError }) =>
|
color: ${({ isError }): string => isError ? defaults.box.error.border : defaults.color};
|
||||||
isError
|
|
||||||
? defaults.box.error.border
|
|
||||||
: defaults.color
|
|
||||||
};
|
|
||||||
display: block;
|
display: block;
|
||||||
font-family: ${defaults.fontFamily};
|
font-family: ${defaults.fontFamily};
|
||||||
font-size: ${defaults.fontSize};
|
font-size: ${defaults.fontSize};
|
||||||
padding: ${({ label }) =>
|
padding: ${({ label }): string => label ? defaults.inputPaddingLabel : defaults.inputPadding};
|
||||||
label
|
|
||||||
? defaults.inputPaddingLabel
|
|
||||||
: defaults.inputPadding
|
|
||||||
};
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&:read-only {
|
&:read-only {
|
||||||
|
|||||||
@ -7,13 +7,13 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
children: React.ReactNode,
|
children: React.ReactNode;
|
||||||
className?: string,
|
className?: string;
|
||||||
label?: string | null
|
label?: string | null;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Label ({ children, className, label }: Props) {
|
function Label ({ children, className, label }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
{label && <label>{label}</label>}
|
{label && <label>{label}</label>}
|
||||||
|
|||||||
@ -8,15 +8,15 @@ import { Link as RouterLink } from 'react-router-dom';
|
|||||||
|
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
children?: React.ReactNode,
|
children?: React.ReactNode;
|
||||||
className?: string,
|
className?: string;
|
||||||
isDanger?: boolean,
|
isDanger?: boolean;
|
||||||
onClick?: () => void,
|
onClick?: () => void;
|
||||||
to?: string
|
to?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Link ({ children, className, onClick, to }: Props) {
|
function Link ({ children, className, onClick, to }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
to
|
to
|
||||||
? <RouterLink className={className} onClick={onClick} to={to}>{children}</RouterLink>
|
? <RouterLink className={className} onClick={onClick} to={to}>{children}</RouterLink>
|
||||||
@ -25,28 +25,16 @@ function Link ({ children, className, onClick, to }: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default styled(Link)`
|
export default styled(Link)`
|
||||||
color: ${({ isDanger }) =>
|
color: ${({ isDanger }): string => isDanger ? defaults.linkColorDanger : defaults.linkColor};
|
||||||
isDanger
|
|
||||||
? defaults.linkColorDanger
|
|
||||||
: defaults.linkColor
|
|
||||||
};
|
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: ${({ isDanger }) =>
|
color: ${({ isDanger }): string => isDanger ? defaults.linkColorDanger : defaults.linkColor};
|
||||||
isDanger
|
|
||||||
? defaults.linkColorDanger
|
|
||||||
: defaults.linkColor
|
|
||||||
};
|
|
||||||
opacity: 1.0;
|
opacity: 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:visited {
|
&:visited {
|
||||||
color: ${({ isDanger }) =>
|
color: ${({ isDanger }): string => isDanger ? defaults.linkColorDanger : defaults.linkColor};
|
||||||
isDanger
|
|
||||||
? defaults.linkColorDanger
|
|
||||||
: defaults.linkColor
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default function Loading ({ children }: Props) {
|
export default function Loading ({ children }: Props): React.ReactElement<Props> {
|
||||||
if (!children) {
|
if (!children) {
|
||||||
return (
|
return (
|
||||||
<div>... loading ...</div>
|
<div>... loading ...</div>
|
||||||
|
|||||||
@ -8,17 +8,17 @@ import styled from 'styled-components';
|
|||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
className?: string,
|
className?: string;
|
||||||
isError?: boolean,
|
isError?: boolean;
|
||||||
isFocussed?: boolean,
|
isFocussed?: boolean;
|
||||||
isReadOnly?: boolean,
|
isReadOnly?: boolean;
|
||||||
label: string,
|
label: string;
|
||||||
onChange?: (value: string) => void,
|
onChange?: (value: string) => void;
|
||||||
value?: string
|
value?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
function TextArea ({ className, isFocussed, isReadOnly, label, onChange, value }: Props) {
|
function TextArea ({ className, isFocussed, isReadOnly, label, onChange, value }: Props): React.ReactElement<Props> {
|
||||||
const _onChange = ({ target: { value } }: React.ChangeEvent<HTMLTextAreaElement>): void => {
|
const _onChange = ({ target: { value } }: React.ChangeEvent<HTMLTextAreaElement>): void => {
|
||||||
onChange && onChange(value.trim());
|
onChange && onChange(value.trim());
|
||||||
};
|
};
|
||||||
@ -40,35 +40,17 @@ function TextArea ({ className, isFocussed, isReadOnly, label, onChange, value }
|
|||||||
|
|
||||||
export default styled(TextArea)`
|
export default styled(TextArea)`
|
||||||
textarea {
|
textarea {
|
||||||
background: ${({ isError, isReadOnly }) =>
|
background: ${({ isError, isReadOnly }): string => isError ? defaults.box.error.background : (isReadOnly ? '#eee' : '#fff')};
|
||||||
isError
|
border-color: ${({ isError }): string => isError ? defaults.box.error.border : defaults.inputBorder};
|
||||||
? defaults.box.error.background
|
|
||||||
: isReadOnly
|
|
||||||
? '#eee'
|
|
||||||
: '#fff'
|
|
||||||
};
|
|
||||||
border-color: ${({ isError }) =>
|
|
||||||
isError
|
|
||||||
? defaults.box.error.border
|
|
||||||
: defaults.inputBorder
|
|
||||||
};
|
|
||||||
border-radius: ${defaults.borderRadius};
|
border-radius: ${defaults.borderRadius};
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: ${({ isError }) =>
|
color: ${({ isError }): string => isError ? defaults.box.error.border : defaults.color};
|
||||||
isError
|
|
||||||
? defaults.box.error.border
|
|
||||||
: defaults.color
|
|
||||||
};
|
|
||||||
display: block;
|
display: block;
|
||||||
font-family: ${defaults.fontFamily};
|
font-family: ${defaults.fontFamily};
|
||||||
font-size: ${defaults.fontSize};
|
font-size: ${defaults.fontSize};
|
||||||
padding: ${({ label }) =>
|
padding: ${({ label }): string => label ? defaults.inputPaddingLabel : defaults.inputPadding};
|
||||||
label
|
|
||||||
? defaults.inputPaddingLabel
|
|
||||||
: defaults.inputPadding
|
|
||||||
};
|
|
||||||
resize: none;
|
resize: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
|||||||
@ -7,26 +7,26 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
type Color = {
|
interface Color {
|
||||||
background: string,
|
background: string;
|
||||||
border: string,
|
border: string;
|
||||||
color: string
|
color: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
type Type = keyof typeof defaults.box;
|
type Type = keyof typeof defaults.box;
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
children: React.ReactNode,
|
children: React.ReactNode;
|
||||||
className?: string,
|
className?: string;
|
||||||
header?: React.ReactNode,
|
header?: React.ReactNode;
|
||||||
type?: Type
|
type?: Type;
|
||||||
};
|
}
|
||||||
|
|
||||||
function getColor ({ type }: Props): Color {
|
function getColor ({ type }: Props): Color {
|
||||||
return defaults.box[type || 'info'] || defaults.box.info;
|
return defaults.box[type || 'info'] || defaults.box.info;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Tip ({ children, className, header }: Props) {
|
function Tip ({ children, className, header }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<article className={className}>
|
<article className={className}>
|
||||||
{header && <h3>{header}</h3>}
|
{header && <h3>{header}</h3>}
|
||||||
@ -37,22 +37,14 @@ function Tip ({ children, className, header }: Props) {
|
|||||||
|
|
||||||
// box-shadow: ${defaults.boxShadow};
|
// box-shadow: ${defaults.boxShadow};
|
||||||
export default styled(Tip)`
|
export default styled(Tip)`
|
||||||
background: ${(props) =>
|
background: ${(p): string => getColor(p).background};
|
||||||
getColor(props).background
|
border-left: 0.25rem solid ${(p): string => getColor(p).border};
|
||||||
};
|
color: ${(p): string => getColor(p).color};
|
||||||
border-left: 0.25rem solid ${(props) =>
|
|
||||||
getColor(props).border
|
|
||||||
};
|
|
||||||
color: ${(props) =>
|
|
||||||
getColor(props).color
|
|
||||||
};
|
|
||||||
margin: 0.75rem -1rem;
|
margin: 0.75rem -1rem;
|
||||||
padding: 1rem 1.5rem;
|
padding: 1rem 1.5rem;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
color: ${(props) =>
|
color: ${(p): string => getColor(p).border};
|
||||||
getColor(props).border
|
|
||||||
};
|
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -7,12 +7,12 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
function View ({ children, className }: Props) {
|
function View ({ children, className }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<main className={className}>
|
<main className={className}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -6,7 +6,8 @@ import { AccountsFromCtx, OnActionFromCtx, AuthRequestsFromCtx, SignRequestsFrom
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const noop = (to?: string) => {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const noop = (to?: string): void => {
|
||||||
// do nothing
|
// do nothing
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,10 +24,11 @@ export {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function withAccounts<P extends { accounts: AccountsFromCtx }> (Component: React.ComponentType<P>): React.ComponentType<SubtractProps<P, { accounts: AccountsFromCtx }>> {
|
export function withAccounts<P extends { accounts: AccountsFromCtx }> (Component: React.ComponentType<P>): React.ComponentType<SubtractProps<P, { accounts: AccountsFromCtx }>> {
|
||||||
|
// eslint-disable-next-line react/display-name,@typescript-eslint/explicit-function-return-type
|
||||||
return (props: SubtractProps<P, { accounts: AccountsFromCtx }>) => {
|
return (props: SubtractProps<P, { accounts: AccountsFromCtx }>) => {
|
||||||
return (
|
return (
|
||||||
<AccountContext.Consumer>
|
<AccountContext.Consumer>
|
||||||
{(accounts) => (
|
{(accounts): React.ReactNode => (
|
||||||
// @ts-ignore Something here with the props are going wonky
|
// @ts-ignore Something here with the props are going wonky
|
||||||
<Component
|
<Component
|
||||||
{...props}
|
{...props}
|
||||||
@ -39,10 +41,11 @@ export function withAccounts<P extends { accounts: AccountsFromCtx }> (Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function withOnAction<P extends { onAction: OnActionFromCtx }> (Component: React.ComponentType<P>): React.ComponentType<SubtractProps<P, { onAction: OnActionFromCtx }>> {
|
export function withOnAction<P extends { onAction: OnActionFromCtx }> (Component: React.ComponentType<P>): React.ComponentType<SubtractProps<P, { onAction: OnActionFromCtx }>> {
|
||||||
|
// eslint-disable-next-line react/display-name,@typescript-eslint/explicit-function-return-type
|
||||||
return (props: SubtractProps<P, { onAction: OnActionFromCtx }>) => {
|
return (props: SubtractProps<P, { onAction: OnActionFromCtx }>) => {
|
||||||
return (
|
return (
|
||||||
<ActionContext.Consumer>
|
<ActionContext.Consumer>
|
||||||
{(onAction) => (
|
{(onAction): React.ReactNode => (
|
||||||
// @ts-ignore Something here with the props are going wonky
|
// @ts-ignore Something here with the props are going wonky
|
||||||
<Component
|
<Component
|
||||||
{...props}
|
{...props}
|
||||||
@ -55,10 +58,11 @@ export function withOnAction<P extends { onAction: OnActionFromCtx }> (Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function withAuthRequests<P extends { requests: AuthRequestsFromCtx }> (Component: React.ComponentType<P>): React.ComponentType<SubtractProps<P, { requests: AuthRequestsFromCtx }>> {
|
export function withAuthRequests<P extends { requests: AuthRequestsFromCtx }> (Component: React.ComponentType<P>): React.ComponentType<SubtractProps<P, { requests: AuthRequestsFromCtx }>> {
|
||||||
|
// eslint-disable-next-line react/display-name,@typescript-eslint/explicit-function-return-type
|
||||||
return (props: SubtractProps<P, { requests: AuthRequestsFromCtx }>) => {
|
return (props: SubtractProps<P, { requests: AuthRequestsFromCtx }>) => {
|
||||||
return (
|
return (
|
||||||
<AuthorizeContext.Consumer>
|
<AuthorizeContext.Consumer>
|
||||||
{(requests) => (
|
{(requests): React.ReactNode => (
|
||||||
// @ts-ignore Something here with the props are going wonky
|
// @ts-ignore Something here with the props are going wonky
|
||||||
<Component
|
<Component
|
||||||
{...props}
|
{...props}
|
||||||
@ -71,10 +75,11 @@ export function withAuthRequests<P extends { requests: AuthRequestsFromCtx }> (C
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function withSignRequests<P extends { requests: SignRequestsFromCtx }> (Component: React.ComponentType<P>): React.ComponentType<SubtractProps<P, { requests: SignRequestsFromCtx }>> {
|
export function withSignRequests<P extends { requests: SignRequestsFromCtx }> (Component: React.ComponentType<P>): React.ComponentType<SubtractProps<P, { requests: SignRequestsFromCtx }>> {
|
||||||
|
// eslint-disable-next-line react/display-name,@typescript-eslint/explicit-function-return-type
|
||||||
return (props: SubtractProps<P, { requests: SignRequestsFromCtx }>) => {
|
return (props: SubtractProps<P, { requests: SignRequestsFromCtx }>) => {
|
||||||
return (
|
return (
|
||||||
<SigningContext.Consumer>
|
<SigningContext.Consumer>
|
||||||
{(requests) => (
|
{(requests): React.ReactNode => (
|
||||||
// @ts-ignore Something here with the props are going wonky
|
// @ts-ignore Something here with the props are going wonky
|
||||||
<Component
|
<Component
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ const LABEL_COLOR = '#878786';
|
|||||||
const LINK_COLOR = '#3367d6';
|
const LINK_COLOR = '#3367d6';
|
||||||
const TEXT_COLOR = '#4d4e4f';
|
const TEXT_COLOR = '#4d4e4f';
|
||||||
|
|
||||||
const defaults: { [index: string]: any } = {
|
const defaults = {
|
||||||
borderRadius: '0.25rem',
|
borderRadius: '0.25rem',
|
||||||
btnBg: TEXT_COLOR, // LINK_COLOR,
|
btnBg: TEXT_COLOR, // LINK_COLOR,
|
||||||
btnBgDanger: DANGER_COLOR,
|
btnBgDanger: DANGER_COLOR,
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { KeyringJson } from '@polkadot/ui-keyring/types';
|
|||||||
export type OmitProps<T, K> = Pick<T, Exclude<keyof T, K>>;
|
export type OmitProps<T, K> = Pick<T, Exclude<keyof T, K>>;
|
||||||
export type SubtractProps<T, K> = OmitProps<T, keyof K>;
|
export type SubtractProps<T, K> = OmitProps<T, keyof K>;
|
||||||
|
|
||||||
export type AccountsFromCtx = Array<KeyringJson>;
|
export type AccountsFromCtx = KeyringJson[];
|
||||||
export type OnActionFromCtx = (to?: string) => void;
|
export type OnActionFromCtx = (to?: string) => void;
|
||||||
export type AuthRequestsFromCtx = Array<AuthorizeRequest>;
|
export type AuthRequestsFromCtx = AuthorizeRequest[];
|
||||||
export type SignRequestsFromCtx = Array<SigningRequest>;
|
export type SignRequestsFromCtx = SigningRequest[];
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
const unicode = {
|
const unicode = {
|
||||||
BACK: '\u21E6', // ⇦
|
BACK: '\u21E6', // ⇦
|
||||||
DOWN: '\u21E9', // ⇩
|
DOWN: '\u21E9', // ⇩
|
||||||
FWD: '\u21E8', // ⇨
|
FWD: '\u21E8', // ⇨
|
||||||
UP: '\u21E7' // ⇧
|
UP: '\u21E7' // ⇧
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { HashRouter } from 'react-router-dom';
|
|||||||
|
|
||||||
import { View } from './components';
|
import { View } from './components';
|
||||||
|
|
||||||
export default function createView (Entry: React.ComponentType<any>, rootId: string = 'root'): void {
|
export default function createView (Entry: React.ComponentType, rootId: string = 'root'): void {
|
||||||
const rootElement = document.getElementById(rootId);
|
const rootElement = document.getElementById(rootId);
|
||||||
|
|
||||||
if (!rootElement) {
|
if (!rootElement) {
|
||||||
|
|||||||
@ -9,22 +9,22 @@ import { KeypairType } from '@polkadot/util-crypto/types';
|
|||||||
import extension from 'extensionizer';
|
import extension from 'extensionizer';
|
||||||
import { PORT_POPUP } from '@polkadot/extension/defaults';
|
import { PORT_POPUP } from '@polkadot/extension/defaults';
|
||||||
|
|
||||||
type Handler = {
|
interface Handler {
|
||||||
resolve: (data: any) => void,
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
reject: (error: Error) => void,
|
resolve: (data: any) => void;
|
||||||
subscriber?: (data: any) => void
|
reject: (error: Error) => void;
|
||||||
};
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
subscriber?: (data: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
type Handlers = {
|
type Handlers = Record<string, Handler>;
|
||||||
[index: string]: Handler
|
|
||||||
};
|
|
||||||
|
|
||||||
const port = extension.runtime.connect({ name: PORT_POPUP });
|
const port = extension.runtime.connect({ name: PORT_POPUP });
|
||||||
const handlers: Handlers = {};
|
const handlers: Handlers = {};
|
||||||
let idCounter = 0;
|
let idCounter = 0;
|
||||||
|
|
||||||
// setup a listener for messages, any incoming resolves the promise
|
// setup a listener for messages, any incoming resolves the promise
|
||||||
port.onMessage.addListener((data) => {
|
port.onMessage.addListener((data): void => {
|
||||||
const handler = handlers[data.id];
|
const handler = handlers[data.id];
|
||||||
|
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
@ -45,8 +45,9 @@ port.onMessage.addListener((data) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function sendMessage (message: MessageTypes, request: any = {}, subscriber?: (data: any) => void): Promise<any> {
|
function sendMessage (message: MessageTypes, request: any = {}, subscriber?: (data: any) => void): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject): void => {
|
||||||
const id = `${Date.now()}.${++idCounter}`;
|
const id = `${Date.now()}.${++idCounter}`;
|
||||||
|
|
||||||
handlers[id] = { resolve, reject, subscriber };
|
handlers[id] = { resolve, reject, subscriber };
|
||||||
@ -63,11 +64,11 @@ export async function forgetAccount (address: string): Promise<boolean> {
|
|||||||
return sendMessage('accounts.forget', { address });
|
return sendMessage('accounts.forget', { address });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAccounts (): Promise<Array<KeyringJson>> {
|
export async function getAccounts (): Promise<KeyringJson[]> {
|
||||||
return sendMessage('accounts.list');
|
return sendMessage('accounts.list');
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAuthRequests (): Promise<Array<AuthorizeRequest>> {
|
export async function getAuthRequests (): Promise<AuthorizeRequest[]> {
|
||||||
return sendMessage('authorize.requests');
|
return sendMessage('authorize.requests');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ export async function approveAuthRequest (id: string): Promise<boolean> {
|
|||||||
return sendMessage('authorize.approve', { id });
|
return sendMessage('authorize.approve', { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSignRequests (): Promise<Array<SigningRequest>> {
|
export async function getSignRequests (): Promise<SigningRequest[]> {
|
||||||
return sendMessage('signing.requests');
|
return sendMessage('signing.requests');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,22 +96,22 @@ export async function createAccount (name: string, password: string, suri: strin
|
|||||||
return sendMessage('accounts.create', { name, password, suri, type });
|
return sendMessage('accounts.create', { name, password, suri, type });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createSeed (length?: number, type?: KeypairType): Promise<{ address: string, seed: string }> {
|
export async function createSeed (length?: number, type?: KeypairType): Promise<{ address: string; seed: string }> {
|
||||||
return sendMessage('seed.create', { length, type });
|
return sendMessage('seed.create', { length, type });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function subscribeAccounts (cb: (accounts: Array<KeyringJson>) => void): Promise<boolean> {
|
export async function subscribeAccounts (cb: (accounts: KeyringJson[]) => void): Promise<boolean> {
|
||||||
return sendMessage('accounts.subscribe', {}, cb);
|
return sendMessage('accounts.subscribe', {}, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function subscribeAuthorize (cb: (accounts: Array<AuthorizeRequest>) => void): Promise<boolean> {
|
export async function subscribeAuthorize (cb: (accounts: AuthorizeRequest[]) => void): Promise<boolean> {
|
||||||
return sendMessage('authorize.subscribe', {}, cb);
|
return sendMessage('authorize.subscribe', {}, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function subscribeSigning (cb: (accounts: Array<SigningRequest>) => void): Promise<boolean> {
|
export async function subscribeSigning (cb: (accounts: SigningRequest[]) => void): Promise<boolean> {
|
||||||
return sendMessage('signing.subscribe', {}, cb);
|
return sendMessage('signing.subscribe', {}, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function validateSeed (seed: string, type?: KeypairType): Promise<{ address: string, seed: string }> {
|
export async function validateSeed (seed: string, type?: KeypairType): Promise<{ address: string; seed: string }> {
|
||||||
return sendMessage('seed.validate', { seed, type });
|
return sendMessage('seed.validate', { seed, type });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,12 +7,12 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import { Link, unicode } from '../components';
|
import { Link, unicode } from '../components';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
className?: string,
|
className?: string;
|
||||||
to?: string
|
to?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
function Back ({ className, to = '/' }: Props) {
|
function Back ({ className, to = '/' }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<Link to={to}>{unicode.BACK} Back</Link>
|
<Link to={to}>{unicode.BACK} Back</Link>
|
||||||
|
|||||||
@ -8,28 +8,28 @@ import React, { useEffect, useState } from 'react';
|
|||||||
|
|
||||||
import { Input, withAccounts } from '../components';
|
import { Input, withAccounts } from '../components';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
accounts: AccountsFromCtx,
|
accounts: AccountsFromCtx;
|
||||||
address?: string,
|
address?: string;
|
||||||
className?: string,
|
className?: string;
|
||||||
defaultValue?: string | null,
|
defaultValue?: string | null;
|
||||||
isFocussed?: boolean,
|
isFocussed?: boolean;
|
||||||
label?: string | null,
|
label?: string | null;
|
||||||
onBlur?: () => void,
|
onBlur?: () => void;
|
||||||
onChange: (name: string | null) => void
|
onChange: (name: string | null) => void;
|
||||||
};
|
}
|
||||||
|
|
||||||
const MIN_LENGTH = 3;
|
const MIN_LENGTH = 3;
|
||||||
|
|
||||||
function Name ({ accounts, address, className, defaultValue, isFocussed, label = 'a descriptive name for this account', onBlur, onChange }: Props) {
|
function Name ({ accounts, address, className, defaultValue, isFocussed, label = 'a descriptive name for this account', onBlur, onChange }: Props): React.ReactElement<Props> {
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const account = accounts.find((account) => account.address === address);
|
const account = accounts.find((account): boolean => account.address === address);
|
||||||
const startValue = (account && account.meta.name) || defaultValue;
|
const startValue = (account && account.meta.name) || defaultValue;
|
||||||
const isError = !name && startValue
|
const isError = !name && startValue
|
||||||
? false
|
? false
|
||||||
: (name.length < MIN_LENGTH);
|
: (name.length < MIN_LENGTH);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect((): void => {
|
||||||
onChange(
|
onChange(
|
||||||
name && (name.length >= MIN_LENGTH)
|
name && (name.length >= MIN_LENGTH)
|
||||||
? name
|
? name
|
||||||
|
|||||||
@ -6,18 +6,18 @@ import React, { useEffect, useState } from 'react';
|
|||||||
|
|
||||||
import { Input } from '../components';
|
import { Input } from '../components';
|
||||||
|
|
||||||
type Props = {
|
interface Props {
|
||||||
isFocussed?: boolean,
|
isFocussed?: boolean;
|
||||||
onChange: (password: string | null) => void
|
onChange: (password: string | null) => void;
|
||||||
};
|
}
|
||||||
|
|
||||||
const MIN_LENGTH = 6;
|
const MIN_LENGTH = 6;
|
||||||
|
|
||||||
export default function Password ({ isFocussed, onChange }: Props) {
|
export default function Password ({ isFocussed, onChange }: Props): React.ReactElement<Props> {
|
||||||
const [pass1, setPass1] = useState('');
|
const [pass1, setPass1] = useState('');
|
||||||
const [pass2, setPass2] = useState('');
|
const [pass2, setPass2] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect((): void => {
|
||||||
onChange(
|
onChange(
|
||||||
(pass1 && pass2 && (pass1.length >= MIN_LENGTH) && (pass1 === pass2))
|
(pass1 && pass2 && (pass1.length >= MIN_LENGTH) && (pass1 === pass2))
|
||||||
? pass1
|
? pass1
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||||
import { KeyringJson } from '@polkadot/ui-keyring/types';
|
import { KeyringJson } from '@polkadot/ui-keyring/types';
|
||||||
import { AuthorizeRequest, MessageTypes, MessageAccountCreate, MessageAccountEdit, MessageAuthorizeApprove, MessageAuthorizeReject, MessageExtrinsicSignApprove, MessageExtrinsicSignCancel, MessageSeedCreate, MessageSeedCreate$Response, MessageSeedValidate, MessageSeedValidate$Response, MessageAccountForget, SigningRequest } from '../types';
|
import { AuthorizeRequest, MessageTypes, MessageAccountCreate, MessageAccountEdit, MessageAuthorizeApprove, MessageAuthorizeReject, MessageExtrinsicSignApprove, MessageExtrinsicSignCancel, MessageSeedCreate, MessageSeedCreateResponse, MessageSeedValidate, MessageSeedValidateResponse, MessageAccountForget, SigningRequest } from '../types';
|
||||||
|
|
||||||
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';
|
||||||
@ -18,14 +18,14 @@ import { createSubscription, unsubscribe } from './subscriptions';
|
|||||||
const SEED_DEFAULT_LENGTH = 12;
|
const SEED_DEFAULT_LENGTH = 12;
|
||||||
const SEED_LENGTHS = [12, 24];
|
const SEED_LENGTHS = [12, 24];
|
||||||
|
|
||||||
function transformAccounts (accounts: SubjectInfo): Array<KeyringJson> {
|
function transformAccounts (accounts: SubjectInfo): KeyringJson[] {
|
||||||
return Object.values(accounts).map(({ json }) => json);
|
return Object.values(accounts).map(({ json }): KeyringJson => json);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Extension {
|
export default class Extension {
|
||||||
state: State;
|
private state: State;
|
||||||
|
|
||||||
constructor (state: State) {
|
public constructor (state: State) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,18 +51,18 @@ export default class Extension {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private accountsList (): Array<KeyringJson> {
|
private accountsList (): KeyringJson[] {
|
||||||
return transformAccounts(accountsObservable.subject.getValue());
|
return transformAccounts(accountsObservable.subject.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME This looks very much like what we have in Tabs
|
// FIXME This looks very much like what we have in Tabs
|
||||||
private accountsSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
private accountsSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
||||||
const cb = createSubscription(id, port);
|
const cb = createSubscription(id, port);
|
||||||
const subscription = accountsObservable.subject.subscribe((accounts: SubjectInfo) =>
|
const subscription = accountsObservable.subject.subscribe((accounts: SubjectInfo): void =>
|
||||||
cb(transformAccounts(accounts))
|
cb(transformAccounts(accounts))
|
||||||
);
|
);
|
||||||
|
|
||||||
port.onDisconnect.addListener(() => {
|
port.onDisconnect.addListener((): void => {
|
||||||
unsubscribe(id);
|
unsubscribe(id);
|
||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
});
|
});
|
||||||
@ -94,18 +94,18 @@ export default class Extension {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private authorizeRequests (): Array<AuthorizeRequest> {
|
private authorizeRequests (): AuthorizeRequest[] {
|
||||||
return this.state.allAuthRequests;
|
return this.state.allAuthRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME This looks very much like what we have in accounts
|
// FIXME This looks very much like what we have in accounts
|
||||||
private authorizeSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
private authorizeSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
||||||
const cb = createSubscription(id, port);
|
const cb = createSubscription(id, port);
|
||||||
const subscription = this.state.authSubject.subscribe((requests: Array<AuthorizeRequest>) =>
|
const subscription = this.state.authSubject.subscribe((requests: AuthorizeRequest[]): void =>
|
||||||
cb(requests)
|
cb(requests)
|
||||||
);
|
);
|
||||||
|
|
||||||
port.onDisconnect.addListener(() => {
|
port.onDisconnect.addListener((): void => {
|
||||||
unsubscribe(id);
|
unsubscribe(id);
|
||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
});
|
});
|
||||||
@ -113,7 +113,7 @@ export default class Extension {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private seedCreate ({ length = SEED_DEFAULT_LENGTH, type }: MessageSeedCreate): MessageSeedCreate$Response {
|
private seedCreate ({ length = SEED_DEFAULT_LENGTH, type }: MessageSeedCreate): MessageSeedCreateResponse {
|
||||||
const seed = mnemonicGenerate(length);
|
const seed = mnemonicGenerate(length);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -122,7 +122,7 @@ export default class Extension {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private seedValidate ({ seed, type }: MessageSeedValidate): MessageSeedValidate$Response {
|
private seedValidate ({ seed, type }: MessageSeedValidate): MessageSeedValidateResponse {
|
||||||
assert(SEED_LENGTHS.includes(seed.split(' ').length), `Mnemonic needs to contain ${SEED_LENGTHS.join(', ')} words`);
|
assert(SEED_LENGTHS.includes(seed.split(' ').length), `Mnemonic needs to contain ${SEED_LENGTHS.join(', ')} words`);
|
||||||
assert(mnemonicValidate(seed), 'Not a valid mnemonic seed');
|
assert(mnemonicValidate(seed), 'Not a valid mnemonic seed');
|
||||||
|
|
||||||
@ -175,18 +175,18 @@ export default class Extension {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private signingRequests (): Array<SigningRequest> {
|
private signingRequests (): SigningRequest[] {
|
||||||
return this.state.allSignRequests;
|
return this.state.allSignRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME This looks very much like what we have in authorization
|
// FIXME This looks very much like what we have in authorization
|
||||||
private signingSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
private signingSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
||||||
const cb = createSubscription(id, port);
|
const cb = createSubscription(id, port);
|
||||||
const subscription = this.state.signSubject.subscribe((requests: Array<SigningRequest>) =>
|
const subscription = this.state.signSubject.subscribe((requests: SigningRequest[]): void =>
|
||||||
cb(requests)
|
cb(requests)
|
||||||
);
|
);
|
||||||
|
|
||||||
port.onDisconnect.addListener(() => {
|
port.onDisconnect.addListener((): void => {
|
||||||
unsubscribe(id);
|
unsubscribe(id);
|
||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
});
|
});
|
||||||
@ -194,7 +194,8 @@ export default class Extension {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handle (id: string, type: MessageTypes, request: any, port: chrome.runtime.Port): Promise<any> {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
public async handle (id: string, type: MessageTypes, request: any, port: chrome.runtime.Port): Promise<any> {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'authorize.approve':
|
case 'authorize.approve':
|
||||||
return this.authorizeApprove(request);
|
return this.authorizeApprove(request);
|
||||||
|
|||||||
@ -2,38 +2,36 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { AuthorizeRequest, MessageAuthorize, MessageExtrinsicSign, MessageExtrinsicSign$Response, SigningRequest } from '../types';
|
import { AuthorizeRequest, MessageAuthorize, MessageExtrinsicSign, MessageExtrinsicSignResponse, SigningRequest } from '../types';
|
||||||
|
|
||||||
import extension from 'extensionizer';
|
import extension from 'extensionizer';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
import { assert } from '@polkadot/util';
|
import { assert } from '@polkadot/util';
|
||||||
|
|
||||||
type AuthRequest = {
|
interface AuthRequest {
|
||||||
id: string,
|
id: string;
|
||||||
idStr: string,
|
idStr: string;
|
||||||
request: MessageAuthorize,
|
request: MessageAuthorize;
|
||||||
resolve: (result: boolean) => void,
|
resolve: (result: boolean) => void;
|
||||||
reject: (error: Error) => void,
|
reject: (error: Error) => void;
|
||||||
url: string
|
url: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
type AuthUrls = {
|
type AuthUrls = Record<string, {
|
||||||
[index: string]: {
|
count: number;
|
||||||
count: number,
|
id: string;
|
||||||
id: string,
|
isAllowed: boolean;
|
||||||
isAllowed: boolean,
|
origin: string;
|
||||||
origin: string,
|
url: string;
|
||||||
url: string
|
}>;
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
type SignRequest = {
|
interface SignRequest {
|
||||||
id: string,
|
id: string;
|
||||||
request: MessageExtrinsicSign,
|
request: MessageExtrinsicSign;
|
||||||
resolve: (result: MessageExtrinsicSign$Response) => void,
|
resolve: (result: MessageExtrinsicSignResponse) => void;
|
||||||
reject: (error: Error) => void,
|
reject: (error: Error) => void;
|
||||||
url: string
|
url: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
let idCounter = 0;
|
let idCounter = 0;
|
||||||
|
|
||||||
@ -42,44 +40,48 @@ function getId (): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class State {
|
export default class State {
|
||||||
// at the moment, we are keeping the list in memory - this should be persisted
|
|
||||||
private _authUrls: AuthUrls = {};
|
private _authUrls: AuthUrls = {};
|
||||||
private _authRequests: { [index: string]: AuthRequest } = {};
|
|
||||||
private _signRequests: { [index: string]: SignRequest } = {};
|
|
||||||
private _windows: Array<number> = [];
|
|
||||||
readonly authSubject: BehaviorSubject<Array<AuthorizeRequest>> = new BehaviorSubject([] as Array<AuthorizeRequest>);
|
|
||||||
readonly signSubject: BehaviorSubject<Array<SigningRequest>> = new BehaviorSubject([] as Array<SigningRequest>);
|
|
||||||
|
|
||||||
get hasAuthRequests (): boolean {
|
private _authRequests: Record<string, AuthRequest> = {};
|
||||||
|
|
||||||
|
private _signRequests: Record<string, SignRequest> = {};
|
||||||
|
|
||||||
|
private _windows: number[] = [];
|
||||||
|
|
||||||
|
public readonly authSubject: BehaviorSubject<AuthorizeRequest[]> = new BehaviorSubject([] as AuthorizeRequest[]);
|
||||||
|
|
||||||
|
public readonly signSubject: BehaviorSubject<SigningRequest[]> = new BehaviorSubject([] as SigningRequest[]);
|
||||||
|
|
||||||
|
public get hasAuthRequests (): boolean {
|
||||||
return this.numAuthRequests === 0;
|
return this.numAuthRequests === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasSignRequests (): boolean {
|
public get hasSignRequests (): boolean {
|
||||||
return this.numSignRequests === 0;
|
return this.numSignRequests === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get numAuthRequests (): number {
|
public get numAuthRequests (): number {
|
||||||
return Object.keys(this._authRequests).length;
|
return Object.keys(this._authRequests).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
get numSignRequests (): number {
|
public get numSignRequests (): number {
|
||||||
return Object.keys(this._signRequests).length;
|
return Object.keys(this._signRequests).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
get allAuthRequests (): Array<AuthorizeRequest> {
|
public get allAuthRequests (): AuthorizeRequest[] {
|
||||||
return Object
|
return Object
|
||||||
.values(this._authRequests)
|
.values(this._authRequests)
|
||||||
.map(({ id, request, url }) => [id, request, url]);
|
.map(({ id, request, url }): AuthorizeRequest => [id, request, url]);
|
||||||
}
|
}
|
||||||
|
|
||||||
get allSignRequests (): Array<SigningRequest> {
|
public get allSignRequests (): SigningRequest[] {
|
||||||
return Object
|
return Object
|
||||||
.values(this._signRequests)
|
.values(this._signRequests)
|
||||||
.map(({ id, request, url }) => [id, request, url]);
|
.map(({ id, request, url }): SigningRequest => [id, request, url]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private popupClose (): void {
|
private popupClose (): void {
|
||||||
this._windows.map((id: number) =>
|
this._windows.forEach((id: number): void =>
|
||||||
extension.windows.remove(id)
|
extension.windows.remove(id)
|
||||||
);
|
);
|
||||||
this._windows = [];
|
this._windows = [];
|
||||||
@ -94,14 +96,14 @@ export default class State {
|
|||||||
type: 'popup',
|
type: 'popup',
|
||||||
url: extension.extension.getURL('popup.html'),
|
url: extension.extension.getURL('popup.html'),
|
||||||
width: 480
|
width: 480
|
||||||
}, (window?: chrome.windows.Window) => {
|
}, (window?: chrome.windows.Window): void => {
|
||||||
if (window) {
|
if (window) {
|
||||||
this._windows.push(window.id);
|
this._windows.push(window.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private authComplete = (id: string, fn: Function) => {
|
private authComplete = (id: string, fn: Function): (result: boolean | Error) => void => {
|
||||||
return (result: boolean | Error): void => {
|
return (result: boolean | Error): void => {
|
||||||
const isAllowed = result === true;
|
const isAllowed = result === true;
|
||||||
const { idStr, request: { origin }, url } = this._authRequests[id];
|
const { idStr, request: { origin }, url } = this._authRequests[id];
|
||||||
@ -121,8 +123,8 @@ export default class State {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private signComplete = (id: string, fn: Function) => {
|
private signComplete = (id: string, fn: Function): (result: MessageExtrinsicSignResponse | Error) => void => {
|
||||||
return (result: MessageExtrinsicSign$Response | Error): void => {
|
return (result: MessageExtrinsicSignResponse | Error): void => {
|
||||||
delete this._signRequests[id];
|
delete this._signRequests[id];
|
||||||
this.updateIconSign(true);
|
this.updateIconSign(true);
|
||||||
|
|
||||||
@ -164,7 +166,7 @@ export default class State {
|
|||||||
this.updateIcon(shouldClose);
|
this.updateIcon(shouldClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
async authorizeUrl (url: string, request: MessageAuthorize): Promise<boolean> {
|
public async authorizeUrl (url: string, request: MessageAuthorize): Promise<boolean> {
|
||||||
const idStr = this.stripUrl(url);
|
const idStr = this.stripUrl(url);
|
||||||
|
|
||||||
if (this._authUrls[idStr]) {
|
if (this._authUrls[idStr]) {
|
||||||
@ -173,7 +175,7 @@ export default class State {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject): void => {
|
||||||
const id = getId();
|
const id = getId();
|
||||||
|
|
||||||
this._authRequests[id] = {
|
this._authRequests[id] = {
|
||||||
@ -190,7 +192,7 @@ export default class State {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isUrlAuthorized (url: string): boolean {
|
public isUrlAuthorized (url: string): boolean {
|
||||||
const entry = this._authUrls[this.stripUrl(url)];
|
const entry = this._authUrls[this.stripUrl(url)];
|
||||||
|
|
||||||
assert(entry, `The source ${url} has not been enabled yet`);
|
assert(entry, `The source ${url} has not been enabled yet`);
|
||||||
@ -199,18 +201,18 @@ export default class State {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAuthRequest (id: string): AuthRequest {
|
public getAuthRequest (id: string): AuthRequest {
|
||||||
return this._authRequests[id];
|
return this._authRequests[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
getSignRequest (id: string): SignRequest {
|
public getSignRequest (id: string): SignRequest {
|
||||||
return this._signRequests[id];
|
return this._signRequests[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
signQueue (url: string, request: MessageExtrinsicSign): Promise<MessageExtrinsicSign$Response> {
|
public signQueue (url: string, request: MessageExtrinsicSign): Promise<MessageExtrinsicSignResponse> {
|
||||||
const id = getId();
|
const id = getId();
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject): void => {
|
||||||
this._signRequests[id] = {
|
this._signRequests[id] = {
|
||||||
id,
|
id,
|
||||||
request,
|
request,
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||||
import { MessageTypes, MessageAuthorize, MessageExtrinsicSign, MessageExtrinsicSign$Response } from '../types';
|
import { MessageTypes, MessageAuthorize, MessageExtrinsicSign, MessageExtrinsicSignResponse } from '../types';
|
||||||
|
|
||||||
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';
|
||||||
@ -12,25 +12,30 @@ import { assert } from '@polkadot/util';
|
|||||||
import State from './State';
|
import State from './State';
|
||||||
import { createSubscription, unsubscribe } from './subscriptions';
|
import { createSubscription, unsubscribe } from './subscriptions';
|
||||||
|
|
||||||
type Accounts = Array<{ address: string, name?: string }>;
|
interface Account {
|
||||||
|
address: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
type Accounts = Account[];
|
||||||
|
|
||||||
function transformAccounts (accounts: SubjectInfo): Accounts {
|
function transformAccounts (accounts: SubjectInfo): Accounts {
|
||||||
return Object.values(accounts).map(({ json: { address, meta: { name } } }) => ({
|
return Object.values(accounts).map(({ json: { address, meta: { name } } }): Account => ({
|
||||||
address, name
|
address, name
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Tabs {
|
export default class Tabs {
|
||||||
state: State;
|
private state: State;
|
||||||
|
|
||||||
constructor (state: State) {
|
public constructor (state: State) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private authorize (url: string, request: MessageAuthorize) {
|
private authorize (url: string, request: MessageAuthorize): Promise<boolean> {
|
||||||
return this.state.authorizeUrl(url, request);
|
return this.state.authorizeUrl(url, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
private accountsList (url: string): Accounts {
|
private accountsList (url: string): Accounts {
|
||||||
return transformAccounts(accountsObservable.subject.getValue());
|
return transformAccounts(accountsObservable.subject.getValue());
|
||||||
}
|
}
|
||||||
@ -38,11 +43,11 @@ export default class Tabs {
|
|||||||
// FIXME This looks very much like what we have in Extension
|
// FIXME This looks very much like what we have in Extension
|
||||||
private accountsSubscribe (url: string, id: string, port: chrome.runtime.Port): boolean {
|
private accountsSubscribe (url: string, id: string, port: chrome.runtime.Port): boolean {
|
||||||
const cb = createSubscription(id, port);
|
const cb = createSubscription(id, port);
|
||||||
const subscription = accountsObservable.subject.subscribe((accounts: SubjectInfo) =>
|
const subscription = accountsObservable.subject.subscribe((accounts: SubjectInfo): void =>
|
||||||
cb(transformAccounts(accounts))
|
cb(transformAccounts(accounts))
|
||||||
);
|
);
|
||||||
|
|
||||||
port.onDisconnect.addListener(() => {
|
port.onDisconnect.addListener((): void => {
|
||||||
unsubscribe(id);
|
unsubscribe(id);
|
||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
});
|
});
|
||||||
@ -50,7 +55,7 @@ export default class Tabs {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private extrinsicSign (url: string, request: MessageExtrinsicSign): Promise<MessageExtrinsicSign$Response> {
|
private extrinsicSign (url: string, request: MessageExtrinsicSign): Promise<MessageExtrinsicSignResponse> {
|
||||||
const { address } = request;
|
const { address } = request;
|
||||||
const pair = keyring.getPair(address);
|
const pair = keyring.getPair(address);
|
||||||
|
|
||||||
@ -59,7 +64,8 @@ export default class Tabs {
|
|||||||
return this.state.signQueue(url, request);
|
return this.state.signQueue(url, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
async handle (id: string, type: MessageTypes, request: any, url: string, port: chrome.runtime.Port): Promise<any> {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
public async handle (id: string, type: MessageTypes, request: any, url: string, port: chrome.runtime.Port): Promise<any> {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'authorize.tab':
|
case 'authorize.tab':
|
||||||
return this.authorize(url, request);
|
return this.authorize(url, request);
|
||||||
|
|||||||
@ -28,12 +28,12 @@ export default function handler ({ id, message, request }: MessageRequest, port:
|
|||||||
: tabs.handle(id, message, request, from, port);
|
: tabs.handle(id, message, request, from, port);
|
||||||
|
|
||||||
promise
|
promise
|
||||||
.then((response) => {
|
.then((response): void => {
|
||||||
console.log(`[out] ${source}`); // :: ${JSON.stringify(response)}`);
|
console.log(`[out] ${source}`); // :: ${JSON.stringify(response)}`);
|
||||||
|
|
||||||
port.postMessage({ id, response });
|
port.postMessage({ id, response });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error): void => {
|
||||||
console.log(`[err] ${source}:: ${error.message}`);
|
console.log(`[err] ${source}:: ${error.message}`);
|
||||||
|
|
||||||
port.postMessage({ id, error: error.message });
|
port.postMessage({ id, error: error.message });
|
||||||
|
|||||||
@ -2,17 +2,17 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
type Subscriptions = {
|
type Subscriptions = Record<string, chrome.runtime.Port>;
|
||||||
[index: string]: chrome.runtime.Port
|
|
||||||
};
|
|
||||||
|
|
||||||
const subscriptions: Subscriptions = {};
|
const subscriptions: Subscriptions = {};
|
||||||
|
|
||||||
// return a subscription callback, that will send the data to the caller via the port
|
// return a subscription callback, that will send the data to the caller via the port
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export function createSubscription (id: string, port: chrome.runtime.Port): (data: any) => void {
|
export function createSubscription (id: string, port: chrome.runtime.Port): (data: any) => void {
|
||||||
subscriptions[id] = port;
|
subscriptions[id] = port;
|
||||||
|
|
||||||
return (subscription: any) => {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
return (subscription: any): void => {
|
||||||
if (subscriptions[id]) {
|
if (subscriptions[id]) {
|
||||||
port.postMessage({ id, subscription });
|
port.postMessage({ id, subscription });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,18 +17,18 @@ import handlers from './handlers';
|
|||||||
extension.browserAction.setBadgeBackgroundColor({ color: '#d90000' });
|
extension.browserAction.setBadgeBackgroundColor({ color: '#d90000' });
|
||||||
|
|
||||||
// listen to all messages and handle appropriately
|
// listen to all messages and handle appropriately
|
||||||
extension.runtime.onConnect.addListener((port) => {
|
extension.runtime.onConnect.addListener((port): void => {
|
||||||
// shouldn't happen, however... only listen to what we know about
|
// shouldn't happen, however... only listen to what we know about
|
||||||
assert([PORT_CONTENT, PORT_POPUP].includes(port.name), `Unknown connection from ${port.name}`);
|
assert([PORT_CONTENT, PORT_POPUP].includes(port.name), `Unknown connection from ${port.name}`);
|
||||||
|
|
||||||
// message and disconnect handlers
|
// message and disconnect handlers
|
||||||
port.onMessage.addListener((data) => handlers(data, port));
|
port.onMessage.addListener((data): void => handlers(data, port));
|
||||||
port.onDisconnect.addListener(() => console.log(`Disconnected from ${port.name}`));
|
port.onDisconnect.addListener((): void => console.log(`Disconnected from ${port.name}`));
|
||||||
});
|
});
|
||||||
|
|
||||||
// initial setup
|
// initial setup
|
||||||
cryptoWaitReady()
|
cryptoWaitReady()
|
||||||
.then(() => {
|
.then((): void => {
|
||||||
console.log('crypto initialized');
|
console.log('crypto initialized');
|
||||||
|
|
||||||
// load all the keyring data
|
// load all the keyring data
|
||||||
@ -36,6 +36,6 @@ cryptoWaitReady()
|
|||||||
|
|
||||||
console.log('initialization completed');
|
console.log('initialization completed');
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error): void => {
|
||||||
console.error('initialization failed', error);
|
console.error('initialization failed', error);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -10,55 +10,58 @@ export type AuthorizeRequest = [string, MessageAuthorize, string];
|
|||||||
|
|
||||||
export type SigningRequest = [string, MessageExtrinsicSign, string];
|
export type SigningRequest = [string, MessageExtrinsicSign, string];
|
||||||
|
|
||||||
export type MessageAuthorize = {
|
export interface MessageAuthorize {
|
||||||
origin: string
|
origin: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type MessageAuthorizeApprove = {
|
export interface MessageAuthorizeApprove {
|
||||||
id: string;
|
id: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type MessageAuthorizeReject = {
|
export interface MessageAuthorizeReject {
|
||||||
id: string;
|
id: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type MessageRequest = {
|
export interface MessageRequest {
|
||||||
id: string,
|
id: string;
|
||||||
message: MessageTypes,
|
message: MessageTypes;
|
||||||
request: any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
};
|
request: any;
|
||||||
|
}
|
||||||
|
|
||||||
export type MessageResponse = {
|
export interface MessageResponse {
|
||||||
error?: string,
|
error?: string;
|
||||||
id: string,
|
id: string;
|
||||||
response?: any,
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
subscription?: any
|
response?: any;
|
||||||
};
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
subscription?: any;
|
||||||
|
}
|
||||||
|
|
||||||
export type MessageAccountCreate = {
|
export interface MessageAccountCreate {
|
||||||
name: string,
|
name: string;
|
||||||
password: string,
|
password: string;
|
||||||
suri: string,
|
suri: string;
|
||||||
type?: KeypairType
|
type?: KeypairType;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type MessageAccountEdit= {
|
export interface MessageAccountEdit {
|
||||||
address: string,
|
address: string;
|
||||||
name: string
|
name: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type MessageAccountForget = {
|
export interface MessageAccountForget {
|
||||||
address: string
|
address: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type MessageExtrinsicSignApprove = {
|
export interface MessageExtrinsicSignApprove {
|
||||||
id: string,
|
id: string;
|
||||||
password: string
|
password: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type MessageExtrinsicSignCancel = {
|
export interface MessageExtrinsicSignCancel {
|
||||||
id: string
|
id: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface MessageExtrinsicSign {
|
export interface MessageExtrinsicSign {
|
||||||
address: string;
|
address: string;
|
||||||
@ -70,27 +73,27 @@ export interface MessageExtrinsicSign {
|
|||||||
nonce: string;
|
nonce: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MessageExtrinsicSign$Response = {
|
export interface MessageExtrinsicSignResponse {
|
||||||
id: string,
|
id: string;
|
||||||
signature: string
|
signature: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type MessageSeedCreate$Response = {
|
export interface MessageSeedCreateResponse {
|
||||||
address: string,
|
address: string;
|
||||||
seed: string
|
seed: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type MessageSeedCreate = {
|
export interface MessageSeedCreate {
|
||||||
length?: 12 | 24,
|
length?: 12 | 24;
|
||||||
type?: KeypairType
|
type?: KeypairType;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type MessageSeedValidate = {
|
export interface MessageSeedValidate {
|
||||||
seed: string,
|
seed: string;
|
||||||
type?: KeypairType
|
type?: KeypairType;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type MessageSeedValidate$Response = {
|
export interface MessageSeedValidateResponse {
|
||||||
address: string,
|
address: string;
|
||||||
seed: string
|
seed: string;
|
||||||
};
|
}
|
||||||
|
|||||||
@ -8,12 +8,12 @@ import { Metadata } from '@polkadot/types';
|
|||||||
// curl -H "Content-Type: application/json" -d '{"id":1, "jsonrpc":"2.0", "method": "state_getMetadata", "params":[]}' http://localhost:9933
|
// curl -H "Content-Type: application/json" -d '{"id":1, "jsonrpc":"2.0", "method": "state_getMetadata", "params":[]}' http://localhost:9933
|
||||||
import alexander from './alexander';
|
import alexander from './alexander';
|
||||||
|
|
||||||
type Chain = {
|
interface Chain {
|
||||||
meta?: Metadata,
|
meta?: Metadata;
|
||||||
name: string
|
name: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
const chains: { [genesisHash: string]: Chain } = {
|
const chains: Record<string, Chain> = {
|
||||||
'0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b': {
|
'0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b': {
|
||||||
name: 'Alexander',
|
name: 'Alexander',
|
||||||
meta: new Metadata(alexander.meta)
|
meta: new Metadata(alexander.meta)
|
||||||
|
|||||||
@ -10,12 +10,12 @@ import { PORT_CONTENT } from './defaults';
|
|||||||
const port = extension.runtime.connect({ name: PORT_CONTENT });
|
const port = extension.runtime.connect({ name: PORT_CONTENT });
|
||||||
|
|
||||||
// send any messages from the extension back to the page
|
// send any messages from the extension back to the page
|
||||||
port.onMessage.addListener((data) => {
|
port.onMessage.addListener((data): void => {
|
||||||
window.postMessage({ ...data, origin: 'content' }, '*');
|
window.postMessage({ ...data, origin: 'content' }, '*');
|
||||||
});
|
});
|
||||||
|
|
||||||
// all messages from the page, pass them to the extension
|
// all messages from the page, pass them to the extension
|
||||||
window.addEventListener('message', ({ data, source }) => {
|
window.addEventListener('message', ({ data, source }): void => {
|
||||||
// only allow messages from our window, by the inject
|
// only allow messages from our window, by the inject
|
||||||
if (source !== window || data.origin !== 'page') {
|
if (source !== window || data.origin !== 'page') {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -8,19 +8,20 @@ import { SendRequest } from './types';
|
|||||||
let sendRequest: SendRequest;
|
let sendRequest: SendRequest;
|
||||||
|
|
||||||
export default class Accounts implements InjectedAccounts {
|
export default class Accounts implements InjectedAccounts {
|
||||||
constructor (_sendRequest: SendRequest) {
|
public constructor (_sendRequest: SendRequest) {
|
||||||
sendRequest = _sendRequest;
|
sendRequest = _sendRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
get (): Promise<Array<InjectedAccount>> {
|
public get (): Promise<InjectedAccount[]> {
|
||||||
return sendRequest('accounts.list');
|
return sendRequest('accounts.list');
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe (cb: (accounts: Array<InjectedAccount>) => any): Unsubcall {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
public subscribe (cb: (accounts: InjectedAccount[]) => any): Unsubcall {
|
||||||
sendRequest('accounts.subscribe', null, cb)
|
sendRequest('accounts.subscribe', null, cb)
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
|
|
||||||
return () => {
|
return (): void => {
|
||||||
// FIXME we need the ability to unsubscribe
|
// FIXME we need the ability to unsubscribe
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,10 +9,11 @@ import Accounts from './Accounts';
|
|||||||
import Signer from './Signer';
|
import Signer from './Signer';
|
||||||
|
|
||||||
export default class Injected implements InjectedInjected {
|
export default class Injected implements InjectedInjected {
|
||||||
readonly accounts: Accounts;
|
public readonly accounts: Accounts;
|
||||||
readonly signer: Signer;
|
|
||||||
|
|
||||||
constructor (sendRequest: SendRequest) {
|
public readonly signer: Signer;
|
||||||
|
|
||||||
|
public constructor (sendRequest: SendRequest) {
|
||||||
this.accounts = new Accounts(sendRequest);
|
this.accounts = new Accounts(sendRequest);
|
||||||
this.signer = new Signer(sendRequest);
|
this.signer = new Signer(sendRequest);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,7 @@ export default class Signer implements InjectedSigner {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
public update (id: number, status: Hash | SubmittableResult): void {
|
public update (id: number, status: Hash | SubmittableResult): void {
|
||||||
// something
|
// something
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,19 +10,19 @@ import Injected from './Injected';
|
|||||||
// when sending a message from the injector to the extension, we
|
// when sending a message from the injector to the extension, we
|
||||||
// - create an event - this we send to the loader
|
// - create an event - this we send to the loader
|
||||||
// - the loader takes this event and uses port.postMessage to background
|
// - the loader takes this event and uses port.postMessage to background
|
||||||
// - on resposnse, the loader creates a reponse event
|
// - on response, the loader creates a reponse event
|
||||||
// - this injector, listens on the events, maps it to the original
|
// - this injector, listens on the events, maps it to the original
|
||||||
// - resolves/rejects the promise with the result (or sub data)
|
// - resolves/rejects the promise with the result (or sub data)
|
||||||
|
|
||||||
type Handler = {
|
interface Handler {
|
||||||
resolve: (data: any) => void,
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
reject: (error: Error) => void,
|
resolve: (data: any) => void;
|
||||||
subscriber?: (data: any) => void
|
reject: (error: Error) => void;
|
||||||
};
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
subscriber?: (data: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
type Handlers = {
|
type Handlers = Record<string, Handler>;
|
||||||
[index: string]: Handler
|
|
||||||
};
|
|
||||||
|
|
||||||
// small helper with the typescript types, just cast window
|
// small helper with the typescript types, just cast window
|
||||||
const windowInject = window as InjectedWindow;
|
const windowInject = window as InjectedWindow;
|
||||||
@ -31,8 +31,9 @@ let idCounter = 0;
|
|||||||
|
|
||||||
// a generic message sender that creates an event, returning a promise that will
|
// a generic message sender that creates an event, returning a promise that will
|
||||||
// resolve once the event is resolved (by the response listener just below this)
|
// resolve once the event is resolved (by the response listener just below this)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function sendMessage (message: MessageTypes, request: any = null, subscriber?: (data: any) => void): Promise<any> {
|
function sendMessage (message: MessageTypes, request: any = null, subscriber?: (data: any) => void): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject): void => {
|
||||||
const id = `${Date.now()}.${++idCounter}`;
|
const id = `${Date.now()}.${++idCounter}`;
|
||||||
|
|
||||||
handlers[id] = { resolve, reject, subscriber };
|
handlers[id] = { resolve, reject, subscriber };
|
||||||
@ -49,7 +50,7 @@ async function enable (origin: string): Promise<Injected> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setup a response listener (events created by the loader for extension responses)
|
// setup a response listener (events created by the loader for extension responses)
|
||||||
window.addEventListener('message', ({ data, source }) => {
|
window.addEventListener('message', ({ data, source }): void => {
|
||||||
// only allow messages from our window, by the loader
|
// only allow messages from our window, by the loader
|
||||||
if (source !== window || data.origin !== 'content') {
|
if (source !== window || data.origin !== 'content') {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -5,5 +5,6 @@
|
|||||||
import { MessageTypes } from '../background/types';
|
import { MessageTypes } from '../background/types';
|
||||||
|
|
||||||
export interface SendRequest {
|
export interface SendRequest {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(message: MessageTypes, request?: any, subscriber?: (data: any) => any): Promise<any>;
|
(message: MessageTypes, request?: any, subscriber?: (data: any) => any): Promise<any>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
// Copyright 2019 @polkadot/extension authors & contributors
|
// Copyright 2019 @polkadot/extension authors & contributors
|
||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
@ -68,6 +69,7 @@ function createWebpack ({ alias = {}, context }) {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
node: {
|
node: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/camelcase
|
||||||
child_process: 'empty',
|
child_process: 'empty',
|
||||||
dgram: 'empty',
|
dgram: 'empty',
|
||||||
fs: 'empty',
|
fs: 'empty',
|
||||||
@ -83,7 +85,7 @@ function createWebpack ({ alias = {}, context }) {
|
|||||||
'process.env': {
|
'process.env': {
|
||||||
NODE_ENV: JSON.stringify(ENV),
|
NODE_ENV: JSON.stringify(ENV),
|
||||||
PKG_NAME: JSON.stringify(pkgJson.name),
|
PKG_NAME: JSON.stringify(pkgJson.name),
|
||||||
PKG_VERSION: JSON.stringify(pkgJson.version),
|
PKG_VERSION: JSON.stringify(pkgJson.version)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
new CopyPlugin([{ from: 'public' }]),
|
new CopyPlugin([{ from: 'public' }]),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user