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