Bumps w/ eslint (#338)

* Bumps w/ eslint

* Additional

* Additional

* Additional

* Additional

* Additional

* Linting done, tsc fixes remain

* Final round
This commit is contained in:
Jaco Greeff 2020-05-24 12:17:53 +02:00 committed by GitHub
parent f313d13a44
commit 837fb52a3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 970 additions and 758 deletions

View File

@ -6,11 +6,11 @@
import chrome from 'sinon-chrome'; import chrome from 'sinon-chrome';
class MessagingFake { class MessagingFake {
private listeners: any = []; private listeners: ((...params: unknown[]) => unknown)[] = [];
get onMessage (): any { get onMessage (): any {
return { return {
addListener: (cb: any): any => this.listeners.push(cb) addListener: (cb: (...params: unknown[]) => unknown) => this.listeners.push(cb)
}; };
} }
@ -20,8 +20,8 @@ class MessagingFake {
}; };
} }
postMessage (data: any): void { postMessage (data: unknown): void {
this.listeners.forEach((cb: any) => cb.call(this, data)); this.listeners.forEach((cb) => cb.call(this, data));
} }
} }
@ -29,7 +29,7 @@ const messagingFake = new MessagingFake();
chrome.runtime.connect.returns(messagingFake); chrome.runtime.connect.returns(messagingFake);
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access
(window as any).chrome = chrome; (window as any).chrome = chrome;
export default chrome; export default chrome;

View File

@ -7,9 +7,9 @@
"packages/*" "packages/*"
], ],
"resolutions": { "resolutions": {
"@polkadot/api": "^1.14.1", "@polkadot/api": "^1.15.1",
"@polkadot/keyring": "^2.10.1", "@polkadot/keyring": "^2.10.1",
"@polkadot/types": "^1.14.1", "@polkadot/types": "^1.15.1",
"@polkadot/util": "^2.10.1", "@polkadot/util": "^2.10.1",
"@polkadot/util-crypto": "^2.10.1", "@polkadot/util-crypto": "^2.10.1",
"babel-core": "^7.0.0-bridge.0" "babel-core": "^7.0.0-bridge.0"
@ -30,12 +30,12 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.9.6", "@babel/core": "^7.9.6",
"@polkadot/dev": "^0.53.5", "@polkadot/dev": "^0.54.1",
"@polkadot/typegen": "^1.14.1", "@polkadot/typegen": "^1.15.1",
"@types/jest": "^25.2.2", "@types/jest": "^25.2.3",
"copy-webpack-plugin": "^5.1.1", "copy-webpack-plugin": "^5.1.1",
"sinon-chrome": "^3.0.1", "sinon-chrome": "^3.0.1",
"stylelint": "^13.3.3", "stylelint": "^13.5.0",
"stylelint-config-recommended": "^3.0.0", "stylelint-config-recommended": "^3.0.0",
"stylelint-config-styled-components": "^0.1.1" "stylelint-config-styled-components": "^0.1.1"
} }

View File

@ -6,9 +6,9 @@
"license": "Apache-2", "license": "Apache-2",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.9.6", "@babel/runtime": "^7.9.6",
"@polkadot/api": "^1.14.1", "@polkadot/api": "^1.15.1",
"@polkadot/extension-inject": "0.26.0-beta.2", "@polkadot/extension-inject": "0.26.0-beta.2",
"@polkadot/keyring": "^2.10.1", "@polkadot/keyring": "^2.10.1",
"@polkadot/ui-keyring": "^0.53.1" "@polkadot/ui-keyring": "^0.54.0-beta.1"
} }
} }

View File

@ -13,12 +13,15 @@ import chrome from '@polkadot/extension-inject/chrome';
import { MetadataStore } from '../../stores'; import { MetadataStore } from '../../stores';
interface AuthRequest { interface Resolver <T> {
reject: (error: Error) => void;
resolve: (result: T) => void;
}
interface AuthRequest extends Resolver<boolean> {
id: string; id: string;
idStr: string; idStr: string;
request: RequestAuthorizeTab; request: RequestAuthorizeTab;
resolve: (result: boolean) => void;
reject: (error: Error) => void;
url: string; url: string;
} }
@ -30,11 +33,9 @@ type AuthUrls = Record<string, {
url: string; url: string;
}>; }>;
interface MetaRequest { interface MetaRequest extends Resolver<boolean> {
id: string; id: string;
request: MetadataDef; request: MetadataDef;
resolve: (result: boolean) => void;
reject: (error: Error) => void;
url: string; url: string;
} }
@ -47,12 +48,10 @@ type Providers = Record<string, {
start: () => ProviderInterface; start: () => ProviderInterface;
}> }>
interface SignRequest { interface SignRequest extends Resolver<ResponseSigning> {
account: AccountJson; account: AccountJson;
id: string; id: string;
request: RequestSign; request: RequestSign;
resolve: (result: ResponseSigning) => void;
reject: (error: Error) => void;
url: string; url: string;
} }
@ -81,7 +80,7 @@ export default class State {
readonly #metaStore = new MetadataStore(); readonly #metaStore = new MetadataStore();
// Map of providers currently injected in tabs // Map of providers currently injected in tabs
readonly #injectedProviders: Map<chrome.runtime.Port, ProviderInterface> = new Map(); readonly #injectedProviders = new Map<chrome.runtime.Port, ProviderInterface>();
readonly #metaRequests: Record<string, MetaRequest> = {}; readonly #metaRequests: Record<string, MetaRequest> = {};
@ -155,8 +154,8 @@ export default class State {
}); });
} }
private authComplete = (id: string, fn: Function): (result: boolean | Error) => void => { private authComplete = (id: string, resolve: (result: boolean) => void, reject: (error: Error) => void): Resolver<boolean> => {
return (result: boolean | Error): void => { const complete = (result: boolean | Error) => {
const isAllowed = result === true; const isAllowed = result === true;
const { idStr, request: { origin }, url } = this.#authRequests[id]; const { idStr, request: { origin }, url } = this.#authRequests[id];
@ -170,26 +169,53 @@ export default class State {
delete this.#authRequests[id]; delete this.#authRequests[id];
this.updateIconAuth(true); this.updateIconAuth(true);
};
fn(result); return {
reject: (error: Error): void => {
complete(error);
reject(error);
},
resolve: (result: boolean): void => {
complete(result);
resolve(result);
}
}; };
} }
private metaComplete = (id: string, fn: Function): (result: boolean | Error) => void => { private metaComplete = (id: string, resolve: (result: boolean) => void, reject: (error: Error) => void): Resolver<boolean> => {
return (result: boolean | Error): void => { const complete = (): void => {
delete this.#metaRequests[id]; delete this.#metaRequests[id];
this.updateIconMeta(true); this.updateIconMeta(true);
};
fn(result); return {
reject: (error: Error): void => {
complete();
reject(error);
},
resolve: (result: boolean): void => {
complete();
resolve(result);
}
}; };
} }
private signComplete = (id: string, fn: Function): (result: ResponseSigning | Error) => void => { private signComplete = (id: string, resolve: (result: ResponseSigning) => void, reject: (error: Error) => void): Resolver<ResponseSigning> => {
return (result: ResponseSigning | Error): void => { const complete = (): void => {
delete this.#signRequests[id]; delete this.#signRequests[id];
this.updateIconSign(true); this.updateIconSign(true);
};
fn(result); return {
reject: (error: Error): void => {
complete();
reject(error);
},
resolve: (result: ResponseSigning): void => {
complete();
resolve(result);
}
}; };
} }
@ -248,11 +274,10 @@ export default class State {
const id = getId(); const id = getId();
this.#authRequests[id] = { this.#authRequests[id] = {
...this.authComplete(id, resolve, reject),
id, id,
idStr, idStr,
reject: this.authComplete(id, reject),
request, request,
resolve: this.authComplete(id, resolve),
url url
}; };
@ -275,10 +300,9 @@ export default class State {
const id = getId(); const id = getId();
this.#metaRequests[id] = { this.#metaRequests[id] = {
...this.metaComplete(id, resolve, reject),
id, id,
reject: this.metaComplete(id, reject),
request, request,
resolve: this.metaComplete(id, resolve),
url url
}; };
@ -313,7 +337,7 @@ export default class State {
assert(provider, 'Cannot call pub(rpc.subscribe) before provider is set'); assert(provider, 'Cannot call pub(rpc.subscribe) before provider is set');
return provider.send(request.method, request.params); return provider.send(request.method, request.params) as Promise<JsonRpcResponse>;
} }
// Start a provider, return its meta // Start a provider, return its meta
@ -378,11 +402,10 @@ export default class State {
return new Promise((resolve, reject): void => { return new Promise((resolve, reject): void => {
this.#signRequests[id] = { this.#signRequests[id] = {
...this.signComplete(id, resolve, reject),
account, account,
id, id,
reject: this.signComplete(id, reject),
request, request,
resolve: this.signComplete(id, resolve),
url url
}; };

View File

@ -108,7 +108,7 @@ export default class Tabs {
port.onDisconnect.addListener((): void => { port.onDisconnect.addListener((): void => {
unsubscribe(id); unsubscribe(id);
this.rpcUnsubscribe({ ...request, subscriptionId }, port); this.rpcUnsubscribe({ ...request, subscriptionId }, port).catch(console.error);
}); });
return true; return true;

View File

@ -39,7 +39,7 @@ export default function handler<TMessageType extends MessageTypes> ({ id, messag
port.postMessage({ id, response }); port.postMessage({ id, response });
}) })
.catch((error): void => { .catch((error: Error): void => {
console.log(`[err] ${source}:: ${error.message}`); console.log(`[err] ${source}:: ${error.message}`);
// only send message back to port if it's still connected // only send message back to port if it's still connected

View File

@ -12,8 +12,7 @@ const subscriptions: Subscriptions = {};
export function createSubscription<TMessageType extends MessageTypesWithSubscriptions> (id: string, port: chrome.runtime.Port): (data: SubscriptionMessageTypes[TMessageType]) => void { export function createSubscription<TMessageType extends MessageTypesWithSubscriptions> (id: string, port: chrome.runtime.Port): (data: SubscriptionMessageTypes[TMessageType]) => void {
subscriptions[id] = port; subscriptions[id] = port;
// eslint-disable-next-line @typescript-eslint/no-explicit-any return (subscription: unknown): void => {
return (subscription: any): void => {
if (subscriptions[id]) { if (subscriptions[id]) {
port.postMessage({ id, subscription }); port.postMessage({ id, subscription });
} }

View File

@ -199,8 +199,7 @@ export type RequestAccountSubscribe = null;
export interface RequestRpcSend { export interface RequestRpcSend {
method: string; method: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any params: unknown[];
params: any[];
} }
export interface RequestRpcSubscribe extends RequestRpcSend { export interface RequestRpcSubscribe extends RequestRpcSend {

View File

@ -17,8 +17,7 @@ export default class Accounts implements InjectedAccounts {
return sendRequest('pub(accounts.list)'); return sendRequest('pub(accounts.list)');
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any public subscribe (cb: (accounts: InjectedAccount[]) => unknown): Unsubcall {
public subscribe (cb: (accounts: InjectedAccount[]) => any): Unsubcall {
sendRequest('pub(accounts.subscribe)', null, cb) sendRequest('pub(accounts.subscribe)', null, cb)
.catch((error: Error) => console.error(error)); .catch((error: Error) => console.error(error));

View File

@ -12,8 +12,7 @@ import { ProviderList, ProviderMeta, InjectedProvider } from '@polkadot/extensio
const l = logger('PostMessageProvider'); const l = logger('PostMessageProvider');
// eslint-disable-next-line @typescript-eslint/no-explicit-any type CallbackHandler = (error?: null | Error, value?: unknown) => void;
type CallbackHandler = (error?: null | Error, value?: any) => void;
// Same as https://github.com/polkadot-js/api/blob/57ca9a9c3204339e1e1f693fcacc33039868dc27/packages/rpc-provider/src/ws/Provider.ts#L17 // Same as https://github.com/polkadot-js/api/blob/57ca9a9c3204339e1e1f693fcacc33039868dc27/packages/rpc-provider/src/ws/Provider.ts#L17
interface SubscriptionHandler { interface SubscriptionHandler {
@ -100,24 +99,20 @@ export default class PostMessageProvider implements InjectedProvider {
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
public send (method: string, params: any[], subscription?: SubscriptionHandler): Promise<any> { public async send (method: string, params: unknown[], subscription?: SubscriptionHandler): Promise<any> {
if (subscription) { if (subscription) {
const { callback, type } = subscription; const { callback, type } = subscription;
return sendRequest( const id = await sendRequest('pub(rpc.subscribe)', { method, params, type }, (res): void => {
'pub(rpc.subscribe)', subscription.callback(null, res);
{ method, params, type },
(res) => {
subscription.callback(null, res);
}
).then((id): number => {
this.#subscriptions[`${type}::${id}`] = callback;
return id;
}); });
} else {
return sendRequest('pub(rpc.send)', { method, params }); this.#subscriptions[`${type}::${id}`] = callback;
return id;
} }
return sendRequest('pub(rpc.send)', { method, params });
} }
/** /**
@ -130,6 +125,7 @@ export default class PostMessageProvider implements InjectedProvider {
const meta = await sendRequest('pub(rpc.startProvider)', key); const meta = await sendRequest('pub(rpc.startProvider)', key);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
sendRequest('pub(rpc.subscribeConnected)', null, (connected) => { sendRequest('pub(rpc.subscribeConnected)', null, (connected) => {
this.#isConnected = connected; this.#isConnected = connected;
@ -145,11 +141,8 @@ export default class PostMessageProvider implements InjectedProvider {
return meta; return meta;
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any public subscribe (type: string, method: string, params: unknown[], callback: AnyFunction): Promise<number> {
public async subscribe (type: string, method: string, params: any[], callback: AnyFunction): Promise<number> { return this.send(method, params, { callback, type }) as Promise<number>;
const id = await this.send(method, params, { callback, type });
return id as number;
} }
/** /**
@ -170,8 +163,6 @@ export default class PostMessageProvider implements InjectedProvider {
delete this.#subscriptions[subscription]; delete this.#subscriptions[subscription];
const result = await this.send(method, [id]); return this.send(method, [id]) as Promise<boolean>;
return result as boolean;
} }
} }

View File

@ -15,7 +15,7 @@ import Injected from './Injected';
export interface Handler { export interface Handler {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
resolve: (data: any) => void; resolve: (data?: any) => void;
reject: (error: Error) => void; reject: (error: Error) => void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
subscriber?: (data: any) => void; subscriber?: (data: any) => void;
@ -32,8 +32,7 @@ export function sendMessage<TMessageType extends MessageTypesWithNullRequest>(me
export function sendMessage<TMessageType extends MessageTypesWithNoSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType]): Promise<ResponseTypes[TMessageType]>; export function sendMessage<TMessageType extends MessageTypesWithNoSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType]): Promise<ResponseTypes[TMessageType]>;
export function sendMessage<TMessageType extends MessageTypesWithSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise<ResponseTypes[TMessageType]>; export function sendMessage<TMessageType extends MessageTypesWithSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise<ResponseTypes[TMessageType]>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any export function sendMessage<TMessageType extends MessageTypes> (message: TMessageType, request?: RequestTypes[TMessageType], subscriber?: (data: unknown) => void): Promise<ResponseTypes[TMessageType]> {
export function sendMessage<TMessageType extends MessageTypes> (message: TMessageType, request?: RequestTypes[TMessageType], subscriber?: (data: any) => void): Promise<ResponseTypes[TMessageType]> {
return new Promise((resolve, reject): void => { return new Promise((resolve, reject): void => {
const id = `${Date.now()}.${++idCounter}`; const id = `${Date.now()}.${++idCounter}`;
@ -57,8 +56,7 @@ export async function enable (origin: string): Promise<Injected> {
return new Injected(sendMessage); return new Injected(sendMessage);
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any export function handleResponse<TMessageType extends MessageTypes> (data: TransportResponseMessage<TMessageType> & { subscription?: string }): void {
export function handleResponse<TMessageType extends MessageTypes> (data: TransportResponseMessage<TMessageType> & { subscription?: any }): void {
const handler = handlers[data.id]; const handler = handlers[data.id];
if (!handler) { if (!handler) {
@ -72,6 +70,7 @@ export function handleResponse<TMessageType extends MessageTypes> (data: Transpo
} }
if (data.subscription) { if (data.subscription) {
// eslint-disable-next-line @typescript-eslint/ban-types
(handler.subscriber as Function)(data.subscription); (handler.subscriber as Function)(data.subscription);
} else if (data.error) { } else if (data.error) {
handler.reject(new Error(data.error)); handler.reject(new Error(data.error));

View File

@ -6,7 +6,7 @@ import { KeyringStore, KeyringJson } from '@polkadot/ui-keyring/types';
import BaseStore from './Base'; import BaseStore from './Base';
export default class AccountsStore extends BaseStore implements KeyringStore { export default class AccountsStore extends BaseStore<KeyringJson> implements KeyringStore {
constructor () { constructor () {
super(null); super(null);
} }
@ -15,22 +15,22 @@ export default class AccountsStore extends BaseStore implements KeyringStore {
super.all(cb); super.all(cb);
} }
public get (key: string, cb: (value: KeyringJson) => void): void { public get (key: string, update: (value: KeyringJson) => void): void {
super.get(key, cb); super.get(key, update);
} }
public remove (key: string, cb?: () => void): void { public remove (key: string, update?: () => void): void {
super.remove(key, cb); super.remove(key, update);
} }
public set (key: string, value: KeyringJson, cb?: () => void): void { public set (key: string, value: KeyringJson, update?: () => void): void {
// shortcut, don't save testing accounts in extension storage // shortcut, don't save testing accounts in extension storage
if (key.startsWith('account:') && value.meta && value.meta.isTesting) { if (key.startsWith('account:') && value.meta && value.meta.isTesting) {
cb && cb(); update && update();
return; return;
} }
super.set(key, value, cb); super.set(key, value, update);
} }
} }

View File

@ -4,8 +4,7 @@
import chrome from '@polkadot/extension-inject/chrome'; import chrome from '@polkadot/extension-inject/chrome';
// eslint-disable-next-line @typescript-eslint/no-explicit-any type StoreValue = Record<string, unknown>;
type StoreValue = Record<string, any>;
const lastError = (type: string): void => { const lastError = (type: string): void => {
const error = chrome.runtime.lastError; const error = chrome.runtime.lastError;
@ -15,15 +14,14 @@ const lastError = (type: string): void => {
} }
}; };
export default abstract class BaseStore { export default abstract class BaseStore <T> {
#prefix: string; #prefix: string;
constructor (prefix: string | null) { constructor (prefix: string | null) {
this.#prefix = prefix ? `${prefix}:` : ''; this.#prefix = prefix ? `${prefix}:` : '';
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any public all (update: (key: string, value: T) => void): void {
public all (cb: (key: string, value: any) => void): void {
chrome.storage.local.get(null, (result: StoreValue): void => { chrome.storage.local.get(null, (result: StoreValue): void => {
lastError('all'); lastError('all');
@ -31,41 +29,38 @@ export default abstract class BaseStore {
.entries(result) .entries(result)
.filter(([key]) => key.startsWith(this.#prefix)) .filter(([key]) => key.startsWith(this.#prefix))
.forEach(([key, value]): void => { .forEach(([key, value]): void => {
cb(key.replace(this.#prefix, ''), value); update(key.replace(this.#prefix, ''), value as T);
}); });
}); });
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any public get (_key: string, update: (value: T) => void): void {
public get (_key: string, cb: (value: any) => void): void {
const key = `${this.#prefix}${_key}`; const key = `${this.#prefix}${_key}`;
chrome.storage.local.get([key], (result: StoreValue): void => { chrome.storage.local.get([key], (result: StoreValue): void => {
lastError('get'); lastError('get');
cb(result[key]); update(result[key] as T);
}); });
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any public remove (_key: string, update?: () => void): void {
public remove (_key: string, cb?: () => void): void {
const key = `${this.#prefix}${_key}`; const key = `${this.#prefix}${_key}`;
chrome.storage.local.remove(key, (): void => { chrome.storage.local.remove(key, (): void => {
lastError('remove'); lastError('remove');
cb && cb(); update && update();
}); });
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any public set (_key: string, value: T, update?: () => void): void {
public set (_key: string, value: any, cb?: () => void): void {
const key = `${this.#prefix}${_key}`; const key = `${this.#prefix}${_key}`;
chrome.storage.local.set({ [key]: value }, (): void => { chrome.storage.local.set({ [key]: value }, (): void => {
lastError('set'); lastError('set');
cb && cb(); update && update();
}); });
} }
} }

View File

@ -6,7 +6,7 @@ import { MetadataDef } from '@polkadot/extension-inject/types';
import BaseStore from './Base'; import BaseStore from './Base';
export default class MetadataStore extends BaseStore { export default class MetadataStore extends BaseStore<MetadataDef> {
constructor () { constructor () {
super('metadata'); super('metadata');
} }
@ -15,15 +15,15 @@ export default class MetadataStore extends BaseStore {
super.all(cb); super.all(cb);
} }
public get (key: string, cb: (value: MetadataDef) => void): void { public get (key: string, update: (value: MetadataDef) => void): void {
super.get(key, cb); super.get(key, update);
} }
public remove (key: string, cb?: () => void): void { public remove (key: string, update?: () => void): void {
super.remove(key, cb); super.remove(key, update);
} }
public set (key: string, value: MetadataDef, cb?: () => void): void { public set (key: string, value: MetadataDef, update?: () => void): void {
super.set(key, value, cb); super.set(key, value, update);
} }
} }

View File

@ -0,0 +1,13 @@
// Copyright 2019-2020 @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.
export interface Message extends MessageEvent {
data: {
error?: string;
id: string;
origin: string;
response?: string;
subscription?: string;
}
}

View File

@ -19,8 +19,7 @@ function metadataExpand (definition: MetadataDef): Chain {
const { chain, genesisHash, icon, metaCalls, specVersion, ss58Format, tokenDecimals, tokenSymbol, types } = definition; const { chain, genesisHash, icon, metaCalls, specVersion, ss58Format, tokenDecimals, tokenSymbol, types } = definition;
const registry = new TypeRegistry(); const registry = new TypeRegistry();
// eslint-disable-next-line @typescript-eslint/no-explicit-any registry.register(types);
registry.register(types as any);
const isUnknown = genesisHash === '0x'; const isUnknown = genesisHash === '0x';
const metadata = metaCalls const metadata = metaCalls

View File

@ -58,9 +58,8 @@ export function web3Enable (originName: string): Promise<InjectedExtension[]> {
.map(([info, ext]): InjectedExtension => { .map(([info, ext]): InjectedExtension => {
// if we don't have an accounts subscriber, add a single-shot version // if we don't have an accounts subscriber, add a single-shot version
if (ext && !ext.accounts.subscribe) { if (ext && !ext.accounts.subscribe) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any ext.accounts.subscribe = (cb: (accounts: InjectedAccount[]) => void | Promise<void>): Unsubcall => {
ext.accounts.subscribe = (cb: (accounts: InjectedAccount[]) => any): Unsubcall => { ext.accounts.get().then(cb).catch(console.error);
ext.accounts.get().then(cb).catch((error: Error) => console.error(error));
return (): void => { return (): void => {
// no ubsubscribe needed, this is a single-shot // no ubsubscribe needed, this is a single-shot
@ -119,25 +118,28 @@ export async function web3Accounts (): Promise<InjectedAccountWithMeta[]> {
return accounts; return accounts;
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any export async function web3AccountsSubscribe (cb: (accounts: InjectedAccountWithMeta[]) => void | Promise<void>): Promise<Unsubcall> {
export async function web3AccountsSubscribe (cb: (accounts: InjectedAccountWithMeta[]) => any): Promise<Unsubcall> {
if (!web3EnablePromise) { if (!web3EnablePromise) {
return throwError('web3AccountsSubscribe'); return throwError('web3AccountsSubscribe');
} }
const accounts: Record<string, InjectedAccount[]> = {}; const accounts: Record<string, InjectedAccount[]> = {};
const triggerUpdate = (): void => { const triggerUpdate = (): void | Promise<void> => cb(
cb(Object.entries(accounts).reduce((result, [source, list]): InjectedAccountWithMeta[] => { Object
result.push(...mapAccounts(source, list)); .entries(accounts)
.reduce((result: InjectedAccountWithMeta[], [source, list]): InjectedAccountWithMeta[] => {
result.push(...mapAccounts(source, list));
return result; return result;
}, [] as InjectedAccountWithMeta[])); }, [])
}; );
const unsubs = (await web3EnablePromise).map(({ accounts: { subscribe }, name: source }): Unsubcall => const unsubs = (await web3EnablePromise).map(({ accounts: { subscribe }, name: source }): Unsubcall =>
subscribe((result): void => { subscribe((result): void => {
accounts[source] = result; accounts[source] = result;
// eslint-disable-next-line @typescript-eslint/no-floating-promises
triggerUpdate(); triggerUpdate();
}) })
); );

View File

@ -8,7 +8,7 @@
"@babel/runtime": "^7.9.6" "@babel/runtime": "^7.9.6"
}, },
"devDependencies": { "devDependencies": {
"@types/chrome": "^0.0.112", "@types/chrome": "^0.0.114",
"@types/firefox-webext-browser": "^70.0.1" "@types/firefox-webext-browser": "^70.0.1"
}, },
"peerDependencies": { "peerDependencies": {

View File

@ -27,8 +27,7 @@ export interface InjectedAccountWithMeta {
export interface InjectedAccounts { export interface InjectedAccounts {
get: () => Promise<InjectedAccount[]>; get: () => Promise<InjectedAccount[]>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any subscribe: (cb: (accounts: InjectedAccount[]) => void | Promise<void>) => Unsubcall;
subscribe: (cb: (accounts: InjectedAccount[]) => any) => Unsubcall;
} }
export interface InjectedExtensionInfo { export interface InjectedExtensionInfo {
@ -56,7 +55,7 @@ export interface MetadataDef {
ss58Format: number; ss58Format: number;
tokenDecimals: number; tokenDecimals: number;
tokenSymbol: string; tokenSymbol: string;
types: Record<string, object | string>; types: Record<string, Record<string, string> | string>;
metaCalls?: string; metaCalls?: string;
} }

View File

@ -9,8 +9,8 @@
"@polkadot/extension-base": "0.26.0-beta.2", "@polkadot/extension-base": "0.26.0-beta.2",
"@polkadot/extension-inject": "0.26.0-beta.2", "@polkadot/extension-inject": "0.26.0-beta.2",
"@polkadot/keyring": "^2.10.1", "@polkadot/keyring": "^2.10.1",
"@polkadot/react-identicon": "^0.53.1", "@polkadot/react-identicon": "^0.54.0-beta.1",
"@polkadot/react-qr": "^0.53.1", "@polkadot/react-qr": "^0.54.0-beta.1",
"@polkadot/util": "^2.10.1", "@polkadot/util": "^2.10.1",
"@polkadot/util-crypto": "^2.10.1", "@polkadot/util-crypto": "^2.10.1",
"react": "^16.13.1", "react": "^16.13.1",
@ -28,7 +28,7 @@
"@types/react-dom": "^16.9.8", "@types/react-dom": "^16.9.8",
"@types/react-router": "^5.1.7", "@types/react-router": "^5.1.7",
"@types/react-router-dom": "^5.1.5", "@types/react-router-dom": "^5.1.5",
"@types/sinon-chrome": "^2.2.8", "@types/sinon-chrome": "^2.2.9",
"@types/styled-components": "^5.1.0", "@types/styled-components": "^5.1.0",
"enzyme": "^3.11.0", "enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2", "enzyme-adapter-react-16": "^1.15.2",

View File

@ -18,8 +18,8 @@ configure({ adapter: new Adapter() });
describe('Account component', () => { describe('Account component', () => {
let wrapper: ReactWrapper; let wrapper: ReactWrapper;
const VALID_ADDRESS = 'HjoBp62cvsWDA3vtNMWxz6c9q13ReEHi9UGHK7JbZweH5g5'; const VALID_ADDRESS = 'HjoBp62cvsWDA3vtNMWxz6c9q13ReEHi9UGHK7JbZweH5g5';
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-unsafe-return
const mountAccountComponent = (additionalAccountProperties: any, theme: Theme = themes.dark): ReactWrapper => mount( const mountAccountComponent = (additionalAccountProperties: Record<string, unknown>, theme: Theme = themes.dark): ReactWrapper => mount(
<MemoryRouter> <MemoryRouter>
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<Account <Account

View File

@ -3,6 +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 { AccountJson } from '@polkadot/extension-base/background/types'; import { AccountJson } from '@polkadot/extension-base/background/types';
import { ThemeProps } from '../../types';
import React, { useCallback, useContext, useState } from 'react'; import React, { useCallback, useContext, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -82,7 +83,7 @@ function Account ({ address, className, isExternal, parentName }: Props): React.
const MenuGroup = styled.div` const MenuGroup = styled.div`
padding-bottom: 16px; padding-bottom: 16px;
margin-bottom: 16px; margin-bottom: 16px;
border-bottom: 1px solid ${({ theme }): string => theme.boxBorderColor}; border-bottom: 1px solid ${({ theme }: ThemeProps): string => theme.boxBorderColor};
`; `;
const MenuItem = styled(Link)` const MenuItem = styled(Link)`

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../../types';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -10,9 +12,7 @@ import { AddAccount, Header } from '../../partials';
import AccountsTree from './AccountsTree'; import AccountsTree from './AccountsTree';
import qrIcon from '../../assets/qr.svg'; import qrIcon from '../../assets/qr.svg';
type Props = {}; export default function Accounts (): React.ReactElement {
export default function Accounts (): React.ReactElement<Props> {
const { hierarchy, master } = useContext(AccountContext); const { hierarchy, master } = useContext(AccountContext);
const mediaAllowed = useContext(MediaContext); const mediaAllowed = useContext(MediaContext);
@ -64,7 +64,7 @@ const QrButton = styled(Button)`
${Svg} { ${Svg} {
width: 20px; width: 20px;
height: 20px; height: 20px;
background: ${({ theme }): string => theme.buttonTextColor}; background: ${({ theme }: ThemeProps): string => theme.buttonTextColor};
} }
`; `;

View File

@ -19,6 +19,7 @@ import Request from './Request';
configure({ adapter: new Adapter() }); configure({ adapter: new Adapter() });
describe('Authorize', () => { describe('Authorize', () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
const mountAuthorize = (authorizeRequests: AuthorizeRequest[] = []): ReactWrapper => mount( const mountAuthorize = (authorizeRequests: AuthorizeRequest[] = []): ReactWrapper => mount(
<AuthorizeReqContext.Provider value={authorizeRequests}> <AuthorizeReqContext.Provider value={authorizeRequests}>
<ThemeProvider theme={themes.dark}> <ThemeProvider theme={themes.dark}>

View File

@ -3,6 +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 { RequestAuthorizeTab } from '@polkadot/extension-base/background/types'; import { RequestAuthorizeTab } from '@polkadot/extension-base/background/types';
import { ThemeProps } from '../../types';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -72,7 +73,7 @@ const RequestInfo = styled.div`
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-bottom: 8px; margin-bottom: 8px;
background: ${({ theme }): string => theme.highlightedAreaBackground}; background: ${({ theme }: ThemeProps): string => theme.highlightedAreaBackground};
`; `;
const Info = styled.div` const Info = styled.div`
@ -99,7 +100,7 @@ const RejectButton = styled(ActionBar)`
export default styled(Request)` export default styled(Request)`
.icon { .icon {
background: ${({ theme }): string => theme.buttonBackgroundDanger}; background: ${({ theme }: ThemeProps): string => theme.buttonBackgroundDanger};
color: white; color: white;
min-width: 18px; min-width: 18px;
width: 14px; width: 14px;
@ -118,7 +119,7 @@ export default styled(Request)`
.tab-name, .tab-name,
.tab-url { .tab-url {
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps): string => theme.textColor};
display: inline-block; display: inline-block;
max-width: 20rem; max-width: 20rem;
overflow: hidden; overflow: hidden;

View File

@ -9,7 +9,7 @@ import { AuthorizeReqContext } from '../../components';
import { Header } from '../../partials'; import { Header } from '../../partials';
import Request from './Request'; import Request from './Request';
export default function Authorize (): React.ReactElement<{}> { export default function Authorize (): React.ReactElement {
const requests = useContext(AuthorizeReqContext); const requests = useContext(AuthorizeReqContext);
return ( return (

View File

@ -3,7 +3,8 @@
// 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 React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { Address, BackButton, ButtonArea, NextStepButton, VerticalSpace } from '../..//components';
import { Address, BackButton, ButtonArea, NextStepButton, VerticalSpace } from '../../components';
import { Name, Password } from '../../partials'; import { Name, Password } from '../../partials';
interface Props { interface Props {

View File

@ -25,6 +25,7 @@ describe('Create Account', () => {
address: 'HjoBp62cvsWDA3vtNMWxz6c9q13ReEHi9UGHK7JbZweH5g5', address: 'HjoBp62cvsWDA3vtNMWxz6c9q13ReEHi9UGHK7JbZweH5g5',
seed: 'horse battery staple correct' seed: 'horse battery staple correct'
}; };
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
const mountComponent = (): ReactWrapper => mount( const mountComponent = (): ReactWrapper => mount(
<ActionContext.Provider value={onActionStub}> <ActionContext.Provider value={onActionStub}>
<ThemeProvider theme={themes.dark}> <ThemeProvider theme={themes.dark}>

View File

@ -2,9 +2,12 @@
// 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 { ThemeProps } from '../../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { ActionText, Title } from '@polkadot/extension-ui/components';
import { ActionText, Title } from '../../components';
interface Props { interface Props {
step: number; step: number;
@ -36,16 +39,16 @@ const CreateAnAccount = styled(Title).attrs(() => ({
`; `;
const CurrentStep = styled.span` const CurrentStep = styled.span`
font-size: ${({ theme }): string => theme.labelFontSize}; font-size: ${({ theme }: ThemeProps): string => theme.labelFontSize};
line-height: ${({ theme }): string => theme.labelLineHeight}; line-height: ${({ theme }: ThemeProps): string => theme.labelLineHeight};
color: ${({ theme }): string => theme.primaryColor}; color: ${({ theme }: ThemeProps): string => theme.primaryColor};
font-weight: 600; font-weight: 600;
`; `;
const TotalSteps = styled.span` const TotalSteps = styled.span`
font-size: ${({ theme }): string => theme.labelFontSize}; font-size: ${({ theme }: ThemeProps): string => theme.labelFontSize};
line-height: ${({ theme }): string => theme.labelLineHeight}; line-height: ${({ theme }: ThemeProps): string => theme.labelLineHeight};
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps): string => theme.textColor};
font-weight: 600; font-weight: 600;
`; `;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../../types';
import React, { useCallback, useRef, useState } from 'react'; import React, { useCallback, useRef, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -54,11 +56,11 @@ const HiddenOptions = styled.div<{ visible: boolean }>`
visibility: ${({ visible }): string => (visible ? 'visible' : 'hidden')}; visibility: ${({ visible }): string => (visible ? 'visible' : 'hidden')};
width: 430px; width: 430px;
z-index: 100; z-index: 100;
background: ${({ theme }): string => theme.bodyColor}; background: ${({ theme }: ThemeProps): string => theme.bodyColor};
max-height: ${({ visible }): string => (visible ? '200px' : '0')}; max-height: ${({ visible }): string => (visible ? '200px' : '0')};
overflow: scroll; overflow: scroll;
padding: 5px; padding: 5px;
border: 1px solid ${({ theme }): string => theme.boxBorderColor}; border: 1px solid ${({ theme }: ThemeProps): string => theme.boxBorderColor};
box-sizing: border-box; box-sizing: border-box;
border-radius: 4px; border-radius: 4px;
margin-top: -8px; margin-top: -8px;
@ -81,10 +83,10 @@ export default styled(AddressDropdown)`
width: 30px; width: 30px;
height: 30px; height: 30px;
background: url(${arrow}) center no-repeat; background: url(${arrow}) center no-repeat;
background-color: ${({ theme }): string => theme.inputBackground}; background-color: ${({ theme }: ThemeProps): string => theme.inputBackground};
pointer-events: none; pointer-events: none;
border-radius: 4px; border-radius: 4px;
border: 1px solid ${({ theme }): string => theme.boxBorderColor}; border: 1px solid ${({ theme }: ThemeProps): string => theme.boxBorderColor};
} }
${Address} ${Svg} { ${Address} ${Svg} {

View File

@ -30,6 +30,7 @@ describe('Export component', () => {
beforeEach(() => { beforeEach(() => {
jest.spyOn(messaging, 'exportAccount').mockResolvedValue({ exportedJson: '{ "meta": { "name": "account_name" } }' }); jest.spyOn(messaging, 'exportAccount').mockResolvedValue({ exportedJson: '{ "meta": { "name": "account_name" } }' });
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
wrapper = mount( wrapper = mount(
<MemoryRouter initialEntries={ [`/account/export/${VALID_ADDRESS}`] }> <MemoryRouter initialEntries={ [`/account/export/${VALID_ADDRESS}`] }>
<ThemeProvider theme={themes.dark}> <ThemeProvider theme={themes.dark}>

View File

@ -29,9 +29,10 @@ function Export ({ match: { params: { address } } }: Props): React.ReactElement<
exportAccount(address, pass) exportAccount(address, pass)
.then(({ exportedJson }) => { .then(({ exportedJson }) => {
const element = document.createElement('a'); const element = document.createElement('a');
const { meta } = JSON.parse(exportedJson) as { meta: { name: string } };
element.href = `data:text/plain;charset=utf-8,${exportedJson}`; element.href = `data:text/plain;charset=utf-8,${exportedJson}`;
element.download = `${JSON.parse(exportedJson).meta.name}_exported_account_${Date.now()}.json`; element.download = `${meta.name}_exported_account_${Date.now()}.json`;
element.click(); element.click();
onAction('/'); onAction('/');

View File

@ -9,9 +9,7 @@ import { ActionContext, Address, NextStepButton, ButtonArea, VerticalSpace } fro
import { createAccountExternal } from '../messaging'; import { createAccountExternal } from '../messaging';
import { Header, Name } from '../partials'; import { Header, Name } from '../partials';
type Props = {}; export default function ImportQr (): React.ReactElement {
export default function ImportQr (): React.ReactElement<Props> {
const onAction = useContext(ActionContext); const onAction = useContext(ActionContext);
const [account, setAccount] = useState<null | { address: string; genesisHash: string }>(null); const [account, setAccount] = useState<null | { address: string; genesisHash: string }>(null);
const [name, setName] = useState<string | null>(null); const [name, setName] = useState<string | null>(null);

View File

@ -10,24 +10,22 @@ import { allOf, isNotShorterThan, Result } from '../validators';
import { createAccountSuri, validateSeed } from '../messaging'; import { createAccountSuri, validateSeed } from '../messaging';
import { Header, Name, Password } from '../partials'; import { Header, Name, Password } from '../partials';
type Props = {}; async function validate (suri: string): Promise<Result<string>> {
const validate = async (suri: string): Promise<Result<string>> => {
try { try {
await validateSeed(suri); await validateSeed(suri);
return Result.ok(suri); return Result.ok(suri);
} catch (err) { } catch (error) {
return Result.error(err.message); return Result.error((error as Error).message);
} }
}; }
const isSeedValid = allOf( const isSeedValid = allOf(
isNotShorterThan(1, 'Seed is empty'), isNotShorterThan(1, 'Seed is empty'),
validate validate
); );
export default function Import (): React.ReactElement<Props> { export default function Import (): React.ReactElement {
const onAction = useContext(ActionContext); const onAction = useContext(ActionContext);
const [account, setAccount] = useState<null | { address: string; suri: string }>(null); const [account, setAccount] = useState<null | { address: string; suri: string }>(null);
const [name, setName] = useState<string | null>(null); const [name, setName] = useState<string | null>(null);
@ -49,8 +47,8 @@ export default function Import (): React.ReactElement<Props> {
// this should always be the case // this should always be the case
if (name && password && account) { if (name && password && account) {
createAccountSuri(name, password, account.suri) createAccountSuri(name, password, account.suri)
.then((): void => onAction('/')) .then(() => onAction('/'))
.catch((error: Error) => console.error(error)); .catch(console.error);
} }
}, [account, name, onAction, password]); }, [account, name, onAction, password]);

View File

@ -3,6 +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 { MetadataDef } from '@polkadot/extension-inject/types'; import { MetadataDef } from '@polkadot/extension-inject/types';
import { ThemeProps } from '../../types';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -10,6 +11,7 @@ import styled from 'styled-components';
import { ActionBar, ActionContext, Button, Link, Table, Warning } from '../../components'; import { ActionBar, ActionContext, Button, Link, Table, Warning } from '../../components';
import useMetadata from '../../hooks/useMetadata'; import useMetadata from '../../hooks/useMetadata';
import { approveMetaRequest, rejectMetaRequest } from '../../messaging'; import { approveMetaRequest, rejectMetaRequest } from '../../messaging';
interface Props { interface Props {
className?: string; className?: string;
request: MetadataDef; request: MetadataDef;
@ -78,7 +80,7 @@ const RequestInfo = styled.div`
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-bottom: 8px; margin-bottom: 8px;
background: ${({ theme }): string => theme.highlightedAreaBackground}; background: ${({ theme }: ThemeProps): string => theme.highlightedAreaBackground};
`; `;
const AcceptButton = styled(Button)` const AcceptButton = styled(Button)`
@ -99,7 +101,7 @@ const RejectButton = styled(ActionBar)`
export default styled(Request)` export default styled(Request)`
.icon { .icon {
background: ${({ theme }): string => theme.buttonBackgroundDanger}; background: ${({ theme }: ThemeProps): string => theme.buttonBackgroundDanger};
color: white; color: white;
min-width: 18px; min-width: 18px;
width: 14px; width: 14px;
@ -118,7 +120,7 @@ export default styled(Request)`
.tab-name, .tab-name,
.tab-url { .tab-url {
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps): string => theme.textColor};
display: inline-block; display: inline-block;
max-width: 20rem; max-width: 20rem;
overflow: hidden; overflow: hidden;

View File

@ -8,7 +8,7 @@ import { Loading, MetadataReqContext } from '../../components';
import { Header } from '../../partials'; import { Header } from '../../partials';
import Request from './Request'; import Request from './Request';
export default function Metadata (): React.ReactElement<{}> { export default function Metadata (): React.ReactElement {
const requests = useContext(MetadataReqContext); const requests = useContext(MetadataReqContext);
return ( return (

View File

@ -33,8 +33,7 @@ function decodeMethod (data: string, isDecoded: boolean, chain: Chain | null, sp
try { try {
if (isDecoded && chain && chain.hasMetadata && specVersion.eqn(chain.specVersion)) { if (isDecoded && chain && chain.hasMetadata && specVersion.eqn(chain.specVersion)) {
method = chain.registry.createType('Call', data); method = chain.registry.createType('Call', data);
// eslint-disable-next-line @typescript-eslint/no-explicit-any args = (method.toHuman() as { args: AnyJson }).args;
args = (method.toHuman() as any).args;
} }
} catch (error) { } catch (error) {
args = null; args = null;

View File

@ -43,6 +43,7 @@ describe.skip('Signing requests', () => {
} }
const mountComponent = async (): Promise<void> => { const mountComponent = async (): Promise<void> => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
wrapper = mount( wrapper = mount(
<ActionContext.Provider value={onActionStub}> <ActionContext.Provider value={onActionStub}>
<ThemeProvider theme={themes.dark}> <ThemeProvider theme={themes.dark}>

View File

@ -2,8 +2,11 @@
// 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 { ThemeProps } from '../../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { Svg } from '../../components'; import { Svg } from '../../components';
import ArrowLeftImage from '../../assets/arrowLeft.svg'; import ArrowLeftImage from '../../assets/arrowLeft.svg';
@ -15,6 +18,10 @@ interface Props {
className?: string; className?: string;
} }
interface ArrowProps extends ThemeProps {
isActive: boolean;
}
function TransactionIndex ({ index, onNextClick, onPreviousClick, totalItems }: Props): React.ReactElement<Props> { function TransactionIndex ({ index, onNextClick, onPreviousClick, totalItems }: Props): React.ReactElement<Props> {
const previousClickActive = index !== 0; const previousClickActive = index !== 0;
const nextClickActive = index < totalItems - 1; const nextClickActive = index < totalItems - 1;
@ -48,29 +55,23 @@ const CreationSteps = styled.div`
`; `;
const CurrentStep = styled.span` const CurrentStep = styled.span`
font-size: ${({ theme }): string => theme.labelFontSize}; font-size: ${({ theme }: ThemeProps): string => theme.labelFontSize};
line-height: ${({ theme }): string => theme.labelLineHeight}; line-height: ${({ theme }: ThemeProps): string => theme.labelLineHeight};
color: ${({ theme }): string => theme.primaryColor}; color: ${({ theme }: ThemeProps): string => theme.primaryColor};
font-weight: 600; font-weight: 600;
margin-left: 10px; margin-left: 10px;
`; `;
const TotalSteps = styled.span` const TotalSteps = styled.span`
font-size: ${({ theme }): string => theme.labelFontSize}; font-size: ${({ theme }: ThemeProps): string => theme.labelFontSize};
line-height: ${({ theme }): string => theme.labelLineHeight}; line-height: ${({ theme }: ThemeProps): string => theme.labelLineHeight};
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps): string => theme.textColor};
font-weight: 600; font-weight: 600;
`; `;
interface ArrowProps { const ArrowLeft = styled(Svg).attrs(() => ({ src: ArrowLeftImage }))<ArrowProps>`
isActive: boolean;
}
const ArrowLeft = styled(Svg).attrs(() => ({
src: ArrowLeftImage
}))<ArrowProps>`
display: inline-block; display: inline-block;
background: ${({ isActive, theme }): string => isActive ? theme.primaryColor : theme.iconNeutralColor}; background: ${({ isActive, theme }: ArrowProps): string => isActive ? theme.primaryColor : theme.iconNeutralColor};
cursor: ${({ isActive }): string => isActive ? 'pointer' : 'default'}; cursor: ${({ isActive }): string => isActive ? 'pointer' : 'default'};
width: 12px; width: 12px;
height: 12px; height: 12px;

View File

@ -25,7 +25,7 @@ export default function Unlock ({ buttonText = 'Sign the transaction', className
); );
const _onClick = useCallback( const _onClick = useCallback(
(): Promise<void> => (): Promise<void> =>
onSign(password).catch((error): void => setError(error.message)), onSign(password).catch((error: Error) => setError(error.message)),
[onSign, password] [onSign, password]
); );

View File

@ -15,7 +15,7 @@ function isInnerTransaction (signingRequest: SigningRequest): boolean {
return 'blockNumber' in signingRequest.request.payload; return 'blockNumber' in signingRequest.request.payload;
} }
export default function Signing (): React.ReactElement<{}> { export default function Signing (): React.ReactElement {
const requests = useContext(SigningReqContext); const requests = useContext(SigningReqContext);
const [requestIndex, setRequestIndex] = useState(0); const [requestIndex, setRequestIndex] = useState(0);
const isTransaction = useMemo( const isTransaction = useMemo(

View File

@ -2,14 +2,14 @@
// 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 { ThemeProps as Props } from '../types';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { ActionContext, Box, Button, ButtonArea, List, VerticalSpace } from '../components'; import { ActionContext, Box, Button, ButtonArea, List, VerticalSpace } from '../components';
import { Header } from '../partials'; import { Header } from '../partials';
type Props = {};
export default function Welcome (): React.ReactElement<Props> { export default function Welcome (): React.ReactElement<Props> {
const onAction = useContext(ActionContext); const onAction = useContext(ActionContext);
@ -41,11 +41,11 @@ export default function Welcome (): React.ReactElement<Props> {
const Note = styled.p` const Note = styled.p`
margin-bottom: 6px; margin-bottom: 6px;
margin-top: 0; margin-top: 0;
color: ${({ theme }): string => theme.subTextColor}; color: ${({ theme }: Props): string => theme.subTextColor};
`; `;
const TextBox = styled(Box)` const TextBox = styled(Box)`
margin: 0.75rem 24px; margin: 0.75rem 24px;
border: 1px solid ${({ theme }): string => theme.inputBorderColor}; border: 1px solid ${({ theme }: Props): string => theme.inputBorderColor};
color: ${({ theme }): string => theme.subTextColor}; color: ${({ theme }: Props): string => theme.subTextColor};
`; `;

View File

@ -44,7 +44,7 @@ async function requestMediaAccess (cameraOn: boolean): Promise<boolean> {
return true; return true;
} catch (error) { } catch (error) {
console.error('Permission for video declined', error.message); console.error('Permission for video declined', (error as Error).message);
} }
return false; return false;
@ -62,7 +62,7 @@ function initAccountContext (accounts: AccountJson[]): AccountsContext {
}; };
} }
export default function Popup (): React.ReactElement<{}> { export default function Popup (): React.ReactElement {
const [accounts, setAccounts] = useState<null | AccountJson[]>(null); const [accounts, setAccounts] = useState<null | AccountJson[]>(null);
const [authRequests, setAuthRequests] = useState<null | AuthorizeRequest[]>(null); const [authRequests, setAuthRequests] = useState<null | AuthorizeRequest[]>(null);
const [cameraOn, setCameraOn] = useState(settings.get().camera === 'on'); const [cameraOn, setCameraOn] = useState(settings.get().camera === 'on');
@ -93,7 +93,7 @@ export default function Popup (): React.ReactElement<{}> {
}, []); }, []);
useEffect((): void => { useEffect((): void => {
requestMediaAccess(cameraOn).then(setMediaAllowed); requestMediaAccess(cameraOn).then(setMediaAllowed).catch(console.error);
}, [cameraOn]); }, [cameraOn]);
const Root = isWelcomeDone const Root = isWelcomeDone

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React, { MouseEventHandler } from 'react'; import React, { MouseEventHandler } from 'react';
import Svg from '@polkadot/extension-ui/components/Svg'; import Svg from '@polkadot/extension-ui/components/Svg';
import styled from 'styled-components'; import styled from 'styled-components';
@ -29,14 +31,14 @@ export default styled(ActionText)`
cursor: pointer; cursor: pointer;
span { span {
font-size: ${({ theme }): string => theme.labelFontSize}; font-size: ${({ theme }: ThemeProps): string => theme.labelFontSize};
line-height: ${({ theme }): string => theme.labelLineHeight}; line-height: ${({ theme }: ThemeProps): string => theme.labelLineHeight};
text-decoration-line: underline; text-decoration-line: underline;
color: ${({ theme }): string => theme.labelColor} color: ${({ theme }: ThemeProps): string => theme.labelColor}
} }
${Svg} { ${Svg} {
background: ${({ theme }): string => theme.iconNeutralColor}; background: ${({ theme }: ThemeProps): string => theme.iconNeutralColor};
display: inline-block; display: inline-block;
position: relative; position: relative;
top: 2px; top: 2px;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import { AccountJson, AccountWithChildren } from '@polkadot/extension-base/background/types'; import { AccountJson, AccountWithChildren } from '@polkadot/extension-base/background/types';
import { Chain } from '@polkadot/extension-chains/types'; import { Chain } from '@polkadot/extension-chains/types';
@ -168,9 +170,9 @@ const CopyAddress = styled.div`
width: 14px; width: 14px;
height: 14px; height: 14px;
margin-right: 10px; margin-right: 10px;
background: ${({ theme }): string => theme.accountDotsIconColor}; background: ${({ theme }: ThemeProps): string => theme.accountDotsIconColor};
&:hover { &:hover {
background: ${({ theme }): string => theme.labelColor}; background: ${({ theme }: ThemeProps): string => theme.labelColor};
cursor: pointer; cursor: pointer;
} }
} }
@ -207,7 +209,7 @@ const FullAddress = styled.div`
width: 270px; width: 270px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
color: ${({ theme }): string => theme.labelColor}; color: ${({ theme }: ThemeProps): string => theme.labelColor};
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
`; `;
@ -234,12 +236,12 @@ const Settings = styled.div`
top: 25%; top: 25%;
bottom: 25%; bottom: 25%;
width: 1px; width: 1px;
background: ${({ theme }): string => theme.boxBorderColor}; background: ${({ theme }: ThemeProps): string => theme.boxBorderColor};
} }
&:hover { &:hover {
cursor: pointer; cursor: pointer;
background: ${({ theme }): string => theme.readonlyInputBackground}; background: ${({ theme }: ThemeProps): string => theme.readonlyInputBackground};
} }
`; `;
@ -248,13 +250,13 @@ Settings.displayName = 'Details';
const ActionsIcon = styled(Svg).attrs(() => ({ const ActionsIcon = styled(Svg).attrs(() => ({
src: details src: details
}))` }))`
background: ${({ theme }): string => theme.accountDotsIconColor}; background: ${({ theme }: ThemeProps): string => theme.accountDotsIconColor};
`; `;
const ActiveActionsIcon = styled(Svg).attrs(() => ({ const ActiveActionsIcon = styled(Svg).attrs(() => ({
src: details src: details
}))` }))`
background: ${({ theme }): string => theme.primaryColor}; background: ${({ theme }: ThemeProps): string => theme.primaryColor};
`; `;
const Banner = styled.div` const Banner = styled.div`
@ -269,7 +271,7 @@ const Banner = styled.div`
const ChainBanner = styled(Banner)` const ChainBanner = styled(Banner)`
padding: 0.1rem 0.5rem; padding: 0.1rem 0.5rem;
right: 40px; right: 40px;
background: ${({ theme }): string => theme.primaryColor}; background: ${({ theme }: ThemeProps): string => theme.primaryColor};
`; `;
const ParentName = styled.div` const ParentName = styled.div`
@ -277,7 +279,7 @@ const ParentName = styled.div`
font-weight: 600; font-weight: 600;
font-size: 10px; font-size: 10px;
line-height: 14px; line-height: 14px;
color: ${({ theme }): string => theme.labelColor}; color: ${({ theme }: ThemeProps): string => theme.labelColor};
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
width: 270px; width: 270px;
@ -290,7 +292,7 @@ const ArrowLabel = styled(Svg).attrs(() => ({
top: 5px; top: 5px;
width: 9px; width: 9px;
height: 9px; height: 9px;
background: ${({ theme }): string => theme.labelColor}; background: ${({ theme }: ThemeProps): string => theme.labelColor};
`; `;
const MovableMenu = styled(Menu) <{ isMoved: boolean }>` const MovableMenu = styled(Menu) <{ isMoved: boolean }>`
@ -302,8 +304,8 @@ export default styled(Address)`
margin-bottom: 8px; margin-bottom: 8px;
& > div { & > div {
background: ${({ theme }): string => theme.accountBackground}; background: ${({ theme }: ThemeProps): string => theme.accountBackground};
border: 1px solid ${({ theme }): string => theme.boxBorderColor}; border: 1px solid ${({ theme }: ThemeProps): string => theme.boxBorderColor};
box-sizing: border-box; box-sizing: border-box;
border-radius: 4px; border-radius: 4px;
} }

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -24,7 +26,7 @@ export default function BackButton ({ onClick }: Props): React.ReactElement<Prop
const SmallButton = styled(Button)` const SmallButton = styled(Button)`
margin-right: 11px; margin-right: 11px;
width: 42px; width: 42px;
background: ${({ theme }): string => theme.backButtonBackground}; background: ${({ theme }: ThemeProps): string => theme.backButtonBackground};
`; `;
const ArrowLeft = styled(Svg).attrs(() => ({ const ArrowLeft = styled(Svg).attrs(() => ({
@ -34,5 +36,5 @@ const ArrowLeft = styled(Svg).attrs(() => ({
height: 12px; height: 12px;
display: block; display: block;
margin: auto; margin: auto;
background: ${({ theme }): string => theme.backButtonTextColor}; background: ${({ theme }: ThemeProps): string => theme.backButtonTextColor};
`; `;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -21,19 +23,19 @@ function Box ({ banner, children, className }: Props): React.ReactElement<Props>
} }
export default styled(Box)` export default styled(Box)`
background: ${({ theme }): string => theme.readonlyInputBackground}; background: ${({ theme }: ThemeProps): string => theme.readonlyInputBackground};
border: none; border: none;
border-radius: ${({ theme }): string => theme.borderRadius}; border-radius: ${({ theme }: ThemeProps): string => theme.borderRadius};
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps): string => theme.textColor};
font-family: ${({ theme }): string => theme.fontFamily}; font-family: ${({ theme }: ThemeProps): string => theme.fontFamily};
font-size: ${({ theme }): string => theme.fontSize}; font-size: ${({ theme }: ThemeProps): string => theme.fontSize};
margin: ${({ theme }): string => theme.boxMargin}; margin: ${({ theme }: ThemeProps): string => theme.boxMargin};
padding: ${({ theme }): string => theme.boxPadding}; padding: ${({ theme }: ThemeProps): string => theme.boxPadding};
position: relative; position: relative;
.banner { .banner {
background: darkorange; background: darkorange;
border-radius: 0 ${({ theme }): string => theme.borderRadius} 0 ${({ theme }): string => theme.borderRadius}; border-radius: 0 ${({ theme }: ThemeProps): string => theme.borderRadius} 0 ${({ theme }: ThemeProps): string => theme.borderRadius};
color: white; color: white;
font-size: 0.75rem; font-size: 0.75rem;
padding: 0.25rem 0.5rem; padding: 0.25rem 0.5rem;

View File

@ -2,10 +2,12 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
interface Props { interface Props extends ThemeProps {
className?: string; className?: string;
children?: React.ReactNode; children?: React.ReactNode;
isDanger?: boolean; isDanger?: boolean;
@ -41,17 +43,17 @@ function Button ({ children, className, isDisabled, onClick, to }: Props): React
export default styled(Button)` export default styled(Button)`
display: block; display: block;
width: 100%; width: 100%;
height: ${({ isDanger }): string => isDanger ? '40px' : '48px'}; height: ${({ isDanger }: Props): string => isDanger ? '40px' : '48px'};
box-sizing: border-box; box-sizing: border-box;
border: none; border: none;
border-radius: ${({ theme }): string => theme.borderRadius}; border-radius: ${({ theme }: Props): string => theme.borderRadius};
color: ${({ theme }): string => theme.buttonTextColor}; color: ${({ theme }: Props): string => theme.buttonTextColor};
font-size: 15px; font-size: 15px;
font-weight: 800; font-weight: 800;
line-height: 20px; line-height: 20px;
padding: 0 1rem; padding: 0 1rem;
text-align: center; text-align: center;
background: ${({ isDanger, theme }): string => isDanger ? theme.buttonBackgroundDanger : theme.buttonBackground}; background: ${({ isDanger, theme }: Props): string => isDanger ? theme.buttonBackgroundDanger : theme.buttonBackground};
cursor: pointer; cursor: pointer;
&:disabled { &:disabled {
@ -60,6 +62,6 @@ export default styled(Button)`
} }
&:not(:disabled):hover { &:not(:disabled):hover {
background: ${({ isDanger, theme }): string => isDanger ? theme.buttonBackgroundDangerHover : theme.buttonBackgroundHover}; background: ${({ isDanger, theme }: Props): string => isDanger ? theme.buttonBackgroundDangerHover : theme.buttonBackgroundHover};
} }
`; `;

View File

@ -2,14 +2,16 @@
// 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 { ThemeProps } from '../types';
import styled from 'styled-components'; import styled from 'styled-components';
import Button from './Button'; import Button from './Button';
export default styled.div` export default styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
background: ${({ theme }): string => theme.highlightedAreaBackground}; background: ${({ theme }: ThemeProps): string => theme.highlightedAreaBackground};
border-top: 1px solid ${({ theme }): string => theme.inputBorderColor}; border-top: 1px solid ${({ theme }: ThemeProps): string => theme.inputBorderColor};
padding: 12px 24px; padding: 12px 24px;
&&& { &&& {

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import Checkmark from '../assets/checkmark.svg'; import Checkmark from '../assets/checkmark.svg';
@ -30,7 +32,7 @@ function Checkbox ({ checked, className, label, onChange }: Props): React.ReactE
} }
export default styled(Checkbox)` export default styled(Checkbox)`
margin: ${({ theme }): string => theme.boxMargin}; margin: ${({ theme }: ThemeProps): string => theme.boxMargin};
label { label {
display: block; display: block;
@ -39,9 +41,9 @@ export default styled(Checkbox)`
user-select: none; user-select: none;
padding-left: 24px; padding-left: 24px;
padding-top: 1px; padding-top: 1px;
color: ${({ theme }): string => theme.subTextColor}; color: ${({ theme }: ThemeProps): string => theme.subTextColor};
font-size: ${({ theme }): string => theme.fontSize}; font-size: ${({ theme }: ThemeProps): string => theme.fontSize};
line-height: ${({ theme }): string => theme.lineHeight}; line-height: ${({ theme }: ThemeProps): string => theme.lineHeight};
& input { & input {
position: absolute; position: absolute;
@ -57,10 +59,10 @@ export default styled(Checkbox)`
left: 0; left: 0;
height: 16px; height: 16px;
width: 16px; width: 16px;
border-radius: ${({ theme }): string => theme.borderRadius}; border-radius: ${({ theme }: ThemeProps): string => theme.borderRadius};
background-color: ${({ theme }): string => theme.readonlyInputBackground}; background-color: ${({ theme }: ThemeProps): string => theme.readonlyInputBackground};
border: 1px solid ${({ theme }): string => theme.inputBorderColor}; border: 1px solid ${({ theme }: ThemeProps): string => theme.inputBorderColor};
border: 1px solid ${({ theme }): string => theme.inputBorderColor}; border: 1px solid ${({ theme }: ThemeProps): string => theme.inputBorderColor};
&:after { &:after {
content: ''; content: '';
display: none; display: none;
@ -71,12 +73,12 @@ export default styled(Checkbox)`
top: 2px; top: 2px;
mask: url(${Checkmark}); mask: url(${Checkmark});
mask-size: cover; mask-size: cover;
background: ${({ theme }): string => theme.primaryColor}; background: ${({ theme }: ThemeProps): string => theme.primaryColor};
} }
} }
&:hover input ~ span { &:hover input ~ span {
background-color: ${({ theme }): string => theme.inputBackground}; background-color: ${({ theme }: ThemeProps): string => theme.inputBackground};
} }
input:checked ~ span:after { input:checked ~ span:after {

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -13,7 +15,7 @@ interface DropdownOption {
value: string; value: string;
} }
interface Props { interface Props extends ThemeProps {
className?: string; className?: string;
defaultValue?: string | null; defaultValue?: string | null;
isError?: boolean; isError?: boolean;
@ -64,16 +66,16 @@ export default styled(Dropdown)`
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
appearance: none; appearance: none;
background: ${({ theme }): string => theme.readonlyInputBackground}; background: ${({ theme }: Props): string => theme.readonlyInputBackground};
border-color: ${({ isError, theme }): string => isError ? theme.errorBorderColor : theme.inputBorderColor}; border-color: ${({ isError, theme }: Props): string => isError ? theme.errorBorderColor : theme.inputBorderColor};
border-radius: ${({ theme }): string => theme.borderRadius}; border-radius: ${({ theme }: ThemeProps): string => theme.borderRadius};
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
box-sizing: border-box; box-sizing: border-box;
color: ${({ isError, theme }): string => isError ? theme.errorBorderColor : theme.textColor}; color: ${({ isError, theme }: Props): string => isError ? theme.errorBorderColor : theme.textColor};
display: block; display: block;
font-family: ${({ theme }): string => theme.fontFamily}; font-family: ${({ theme }: Props): string => theme.fontFamily};
font-size: ${({ theme }): string => theme.fontSize}; font-size: ${({ theme }: Props): string => theme.fontSize};
font-weight: 600; font-weight: 600;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
width: 100%; width: 100%;

View File

@ -11,7 +11,7 @@ interface Props {
onClick?: () => void | Promise<void>; onClick?: () => void | Promise<void>;
} }
function Icon ({ className, icon, onClick }: Props): React.ReactElement<Props> { function Icon ({ className = '', icon, onClick }: Props): React.ReactElement<Props> {
return ( return (
<div <div
className={`${className} icon`} className={`${className} icon`}

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import Icon from '@polkadot/react-identicon'; import Icon from '@polkadot/react-identicon';
@ -35,7 +37,7 @@ export default styled(Identicon)`
.container:before { .container:before {
box-shadow: none; box-shadow: none;
background: ${({ theme }): string => theme.identiconBackground}; background: ${({ theme }: ThemeProps): string => theme.identiconBackground};
} }
svg { svg {

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -21,7 +23,7 @@ function Label ({ children, className, label }: Props): React.ReactElement<Props
} }
export default styled(Label)` export default styled(Label)`
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps): string => theme.textColor};
label { label {
font-size: 10px; font-size: 10px;

View File

@ -2,11 +2,13 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { Link as RouterLink } from 'react-router-dom'; import { Link as RouterLink } from 'react-router-dom';
interface Props { interface Props extends ThemeProps {
children?: React.ReactNode; children?: React.ReactNode;
className?: string; className?: string;
isDanger?: boolean; isDanger?: boolean;
@ -15,8 +17,8 @@ interface Props {
} }
function Link ({ children, className, onClick, to }: Props): React.ReactElement<Props> { function Link ({ children, className, onClick, to }: Props): React.ReactElement<Props> {
return ( return to
to ? ( ? (
<RouterLink <RouterLink
className={className} className={className}
onClick={onClick} onClick={onClick}
@ -24,7 +26,8 @@ function Link ({ children, className, onClick, to }: Props): React.ReactElement<
> >
{children} {children}
</RouterLink> </RouterLink>
) : ( )
: (
<a <a
className={className} className={className}
href='#' href='#'
@ -32,24 +35,23 @@ function Link ({ children, className, onClick, to }: Props): React.ReactElement<
> >
{children} {children}
</a> </a>
) );
);
} }
export default styled(Link)` export default styled(Link)`
display: flex; display: flex;
align-items: center; align-items: center;
color: ${({ isDanger, theme }): string => isDanger ? theme.textColorDanger : theme.textColor}; color: ${({ isDanger, theme }: Props): string => isDanger ? theme.textColorDanger : theme.textColor};
opacity: 0.9; opacity: 0.9;
text-decoration: none; text-decoration: none;
vertical-align: middle; vertical-align: middle;
&:hover { &:hover {
color: ${({ isDanger, theme }): string => isDanger ? theme.textColorDanger : theme.textColor}; color: ${({ isDanger, theme }: Props): string => isDanger ? theme.textColorDanger : theme.textColor};
opacity: 1.0; opacity: 1.0;
} }
&:visited { &:visited {
color: ${({ isDanger, theme }): string => isDanger ? theme.textColorDanger : theme.textColor}; color: ${({ isDanger, theme }: Props): string => isDanger ? theme.textColorDanger : theme.textColor};
} }
`; `;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import styled from 'styled-components'; import styled from 'styled-components';
export default styled.ul` export default styled.ul`
@ -17,7 +19,7 @@ export default styled.ul`
li::before { li::before {
content: '\\2022'; content: '\\2022';
color: ${({ theme }): string => theme.primaryColor}; color: ${({ theme }: ThemeProps): string => theme.primaryColor};
font-size: 30px; font-size: 30px;
font-weight: bold; font-weight: bold;
margin-right: 10px; margin-right: 10px;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -27,10 +29,10 @@ export default styled(Menu)`
right: 0; right: 0;
margin-top: 90px; margin-top: 90px;
padding: 16px 0; padding: 16px 0;
background: ${({ theme }): string => theme.popupBackground}; background: ${({ theme }: ThemeProps): string => theme.popupBackground};
border-radius: 4px; border-radius: 4px;
border: 1px solid ${({ theme }): string => theme.boxBorderColor}; border: 1px solid ${({ theme }: ThemeProps): string => theme.boxBorderColor};
box-sizing: border-box; box-sizing: border-box;
box-shadow: 0 0 32px ${({ theme }): string => theme.boxShadow}; box-shadow: 0 0 32px ${({ theme }: ThemeProps): string => theme.boxShadow};
z-index: 1; z-index: 1;
`; `;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import styled from 'styled-components'; import styled from 'styled-components';
import React, { MouseEventHandler } from 'react'; import React, { MouseEventHandler } from 'react';
import TextAreaWithLabel from './TextAreaWithLabel'; import TextAreaWithLabel from './TextAreaWithLabel';
@ -35,13 +37,13 @@ const MnemonicText = styled(TextAreaWithLabel).attrs(() => ({
label: 'Generated 12-word mnemonic seed:' label: 'Generated 12-word mnemonic seed:'
}))` }))`
textarea { textarea {
font-size: ${({ theme }): string => theme.fontSize}; font-size: ${({ theme }: ThemeProps): string => theme.fontSize};
line-height: ${({ theme }): string => theme.lineHeight}; line-height: ${({ theme }: ThemeProps): string => theme.lineHeight};
height: unset; height: unset;
letter-spacing: -0.01em; letter-spacing: -0.01em;
padding: 14px; padding: 14px;
margin-bottom: 10px; margin-bottom: 10px;
color: ${({ theme }): string => theme.primaryColor}; color: ${({ theme }: ThemeProps): string => theme.primaryColor};
} }
`; `;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -28,5 +30,5 @@ const ArrowRight = styled(Svg).attrs(() => ({
height: 12px; height: 12px;
margin: 4px 1px 0 0; margin: 4px 1px 0 0;
transform: rotate(180deg); transform: rotate(180deg);
background: ${({ theme }): string => theme.buttonTextColor}; background: ${({ theme }: ThemeProps): string => theme.buttonTextColor};
`; `;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import styled from 'styled-components'; import styled from 'styled-components';
interface Props { interface Props {
@ -12,5 +14,5 @@ export default styled.span<Props>`
display: block; display: block;
mask: ${({ src }): string => `url(${src})`}; mask: ${({ src }): string => `url(${src})`};
mask-size: cover; mask-size: cover;
background: ${({ theme }): string => theme.textColor}; background: ${({ theme }: ThemeProps): string => theme.textColor};
`; `;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -15,7 +17,7 @@ interface Props {
function Switch ({ checked, checkedLabel, className, onChange, uncheckedLabel }: Props): React.ReactElement<Props> { function Switch ({ checked, checkedLabel, className, onChange, uncheckedLabel }: Props): React.ReactElement<Props> {
const _onChange = useCallback( const _onChange = useCallback(
(event) => onChange(event.target.checked), (event: React.ChangeEvent<HTMLInputElement>) => onChange(event.target.checked),
[onChange] [onChange]
); );
@ -41,10 +43,10 @@ const Slider = styled.span`
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background-color: ${({ theme }): string => theme.readonlyInputBackground}; background-color: ${({ theme }: ThemeProps): string => theme.readonlyInputBackground};
transition: 0.2s; transition: 0.2s;
border-radius: 100px; border-radius: 100px;
border: 1px solid ${({ theme }): string => theme.inputBorderColor}; border: 1px solid ${({ theme }: ThemeProps): string => theme.inputBorderColor};
&:before { &:before {
position: absolute; position: absolute;
@ -53,7 +55,7 @@ const Slider = styled.span`
width: 16px; width: 16px;
left: 4px; left: 4px;
bottom: 3px; bottom: 3px;
background-color: ${({ theme }): string => theme.primaryColor}; background-color: ${({ theme }: ThemeProps): string => theme.primaryColor};
transition: 0.4s; transition: 0.4s;
border-radius: 50%; border-radius: 50%;
} }

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -11,9 +13,9 @@ interface Props {
isFull?: boolean; isFull?: boolean;
} }
function Table ({ children, className, isFull }: Props): React.ReactElement<Props> { function Table ({ children, className = '', isFull }: Props): React.ReactElement<Props> {
return ( return (
<table className={`${className} ${isFull && 'isFull'}`}> <table className={`${className} ${isFull ? 'isFull' : ''}`}>
<tbody> <tbody>
{children} {children}
</tbody> </tbody>
@ -24,8 +26,8 @@ function Table ({ children, className, isFull }: Props): React.ReactElement<Prop
export default React.memo(styled(Table)` export default React.memo(styled(Table)`
border: 0; border: 0;
display: block; display: block;
font-size: ${({ theme }): string => theme.labelFontSize}; font-size: ${({ theme }: ThemeProps): string => theme.labelFontSize};
line-height: ${({ theme }): string => theme.labelLineHeight}; line-height: ${({ theme }: ThemeProps): string => theme.labelLineHeight};
margin-bottom: 1rem; margin-bottom: 1rem;
&.isFull { &.isFull {

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import styled, { css } from 'styled-components'; import styled, { css } from 'styled-components';
interface Props { interface Props {
@ -9,25 +11,25 @@ interface Props {
} }
const DefaultTextInputColors = css` const DefaultTextInputColors = css`
background: ${({ theme }): string => theme.inputBackground}; background: ${({ theme }: ThemeProps): string => theme.inputBackground};
border-color: ${({ theme }): string => theme.inputBorderColor}; border-color: ${({ theme }: ThemeProps): string => theme.inputBorderColor};
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps): string => theme.textColor};
`; `;
const ErroredTextInputColors = css` const ErroredTextInputColors = css`
background: ${({ theme }): string => theme.inputBackground}; background: ${({ theme }: ThemeProps): string => theme.inputBackground};
border-color: ${({ theme }): string => theme.errorBorderColor}; border-color: ${({ theme }: ThemeProps): string => theme.errorBorderColor};
color: ${({ theme }): string => theme.errorColor}; color: ${({ theme }: ThemeProps): string => theme.errorColor};
`; `;
const TextInput = css<Props>` const TextInput = css<Props>`
border-radius: ${({ theme }): string => theme.borderRadius}; border-radius: ${({ theme }: ThemeProps): string => theme.borderRadius};
border: ${({ theme }): string => `1px solid ${theme.inputBorderColor}`}; border: ${({ theme }: ThemeProps): string => `1px solid ${theme.inputBorderColor}`};
outline: none; outline: none;
box-sizing: border-box; box-sizing: border-box;
display: block; display: block;
font-family: ${({ theme }): string => theme.fontFamily}; font-family: ${({ theme }: ThemeProps): string => theme.fontFamily};
font-size: ${({ theme }): string => theme.fontSize}; font-size: ${({ theme }: ThemeProps): string => theme.fontSize};
font-weight: 600; font-weight: 600;
height: 40px; height: 40px;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
@ -36,7 +38,7 @@ const TextInput = css<Props>`
${({ withError }): typeof ErroredTextInputColors => (withError ? ErroredTextInputColors : DefaultTextInputColors)}; ${({ withError }): typeof ErroredTextInputColors => (withError ? ErroredTextInputColors : DefaultTextInputColors)};
&:read-only { &:read-only {
background: ${({ theme }): string => theme.readonlyInputBackground}; background: ${({ theme }: ThemeProps): string => theme.readonlyInputBackground};
box-shadow: none; box-shadow: none;
outline: none; outline: none;
} }

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import styled from 'styled-components'; import styled from 'styled-components';
export default styled.div` export default styled.div`
@ -13,6 +15,6 @@ export default styled.div`
line-height: 14px; line-height: 14px;
letter-spacing: 0.04em; letter-spacing: 0.04em;
text-transform: uppercase; text-transform: uppercase;
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps): string => theme.textColor};
opacity: 0.65; opacity: 0.65;
`; `;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -30,6 +32,6 @@ export default styled(Toast)<{visible: boolean}>`
&& { && {
margin: auto; margin: auto;
border-radius: 25px; border-radius: 25px;
background: ${({ theme }): string => theme.highlightedAreaBackground}; background: ${({ theme }: ThemeProps): string => theme.highlightedAreaBackground};
} }
`; `;

View File

@ -2,9 +2,11 @@
// 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 React, { useState, useEffect } from 'react'; import { ThemeProps } from '../types';
import React, { useState, useEffect } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { Result, Validator } from '../validators'; import { Result, Validator } from '../validators';
interface BasicProps { interface BasicProps {
@ -21,7 +23,7 @@ type Props<T extends BasicProps> = T & {
defaultValue?: string; defaultValue?: string;
} }
function ValidatedInput<T extends object> ({ className, component: Input, defaultValue, onValidatedChange, validator, ...props }: Props<T>): React.ReactElement<Props<T>> { function ValidatedInput<T extends Record<string, unknown>> ({ className, component: Input, defaultValue, onValidatedChange, validator, ...props }: Props<T>): React.ReactElement<Props<T>> {
const [value, setValue] = useState(defaultValue || ''); const [value, setValue] = useState(defaultValue || '');
const [wasMounted, setWasMounted] = useState(false); const [wasMounted, setWasMounted] = useState(false);
const [validationResult, setValidationResult] = useState<Result<string>>(Result.ok('')); const [validationResult, setValidationResult] = useState<Result<string>>(Result.ok(''));
@ -31,6 +33,7 @@ function ValidatedInput<T extends object> ({ className, component: Input, defaul
setWasMounted(true); setWasMounted(true);
} }
// eslint-disable-next-line @typescript-eslint/no-floating-promises
(async (): Promise<void> => { (async (): Promise<void> => {
const result = await validator(value); const result = await validator(value);
@ -43,7 +46,7 @@ function ValidatedInput<T extends object> ({ className, component: Input, defaul
return ( return (
<div className={className}> <div className={className}>
<Input <Input
{...props as T} {...props as unknown as T}
isError={Result.isError(validationResult)} isError={Result.isError(validationResult)}
onChange={setValue} onChange={setValue}
value={value} value={value}
@ -56,9 +59,9 @@ function ValidatedInput<T extends object> ({ className, component: Input, defaul
const ErrorMessage = styled.span` const ErrorMessage = styled.span`
display: block; display: block;
margin-top: -10px; margin-top: -10px;
font-size: ${({ theme }): string => theme.labelFontSize}; font-size: ${({ theme }: ThemeProps): string => theme.labelFontSize};
line-height: ${({ theme }): string => theme.labelLineHeight}; line-height: ${({ theme }: ThemeProps): string => theme.labelLineHeight};
color: ${({ theme }): string => theme.errorColor}; color: ${({ theme }: ThemeProps): string => theme.errorColor};
`; `;
ErrorMessage.displayName = 'ErrorMessage'; ErrorMessage.displayName = 'ErrorMessage';

View File

@ -2,9 +2,13 @@
// 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 { ThemeProps } from '../types';
import React, { useState } from 'react'; import React, { useState } from 'react';
import styled, { ThemeProvider, createGlobalStyle } from 'styled-components'; import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';
import { AvailableThemes, chooseTheme, themes, ThemeSwitchContext, Theme } from '.';
// FIXME We should not import from index when this one is imported there as well
import { AvailableThemes, chooseTheme, themes, ThemeSwitchContext } from '.';
interface Props { interface Props {
children: React.ReactNode; children: React.ReactNode;
@ -32,9 +36,9 @@ function View ({ children, className }: Props): React.ReactElement<Props> {
); );
} }
const BodyTheme = createGlobalStyle<{ theme: Theme }>` const BodyTheme = createGlobalStyle<ThemeProps>`
body { body {
background-color: ${({ theme }): string => theme.bodyColor}; background-color: ${({ theme }: ThemeProps): string => theme.bodyColor};
} }
html { html {
@ -50,14 +54,14 @@ const Main = styled.main`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background: ${({ theme }): string => theme.background}; background: ${({ theme }: ThemeProps): string => theme.background};
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps): string => theme.textColor};
font-size: ${({ theme }): string => theme.fontSize}; font-size: ${({ theme }: ThemeProps): string => theme.fontSize};
line-height: ${({ theme }): string => theme.lineHeight}; line-height: ${({ theme }: ThemeProps): string => theme.lineHeight};
border: 1px solid ${({ theme }): string => theme.inputBorderColor}; border: 1px solid ${({ theme }: ThemeProps): string => theme.inputBorderColor};
* { * {
font-family: ${({ theme }): string => theme.fontFamily}; font-family: ${({ theme }: ThemeProps): string => theme.fontFamily};
} }
> * { > * {

View File

@ -2,45 +2,46 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import WarningImageSrc from '../assets/warning.svg';
import Svg from '@polkadot/extension-ui/components/Svg';
interface Props { import warningImageSrc from '../assets/warning.svg';
import Svg from './Svg';
interface Props extends ThemeProps {
children: React.ReactNode; children: React.ReactNode;
danger?: boolean; danger?: boolean;
className?: string; className?: string;
} }
function Warning ({ children, className, danger }: Props): React.ReactElement<Props> { function Warning ({ children, className }: Props): React.ReactElement<Props> {
return ( return (
<div className={className}> <div className={className}>
<div> <div>
<WarningImage <Svg
danger={danger} className='warningImage'
src={WarningImageSrc} src={warningImageSrc}
/> />
</div> </div>
<div> <div>{children}</div>
{children}
</div>
</div> </div>
); );
} }
const WarningImage = styled(Svg)<Pick<Props, 'danger'>>` export default React.memo(styled(Warning)`
width: 16px;
height: 14px;
margin: 5px 10px 5px 0;
background: ${({ danger, theme }): string => danger ? theme.iconDangerColor : theme.iconWarningColor};
`;
export default styled(Warning)`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
padding-left: ${({ danger }): string => danger ? '18px' : ''}; padding-left: ${({ danger }): string => danger ? '18px' : ''};
color: ${({ theme }): string => theme.subTextColor}; color: ${({ theme }: Props): string => theme.subTextColor};
margin-right: 20px; margin-right: 20px;
border-left: ${({ danger, theme }): string => danger ? `0.25rem solid ${theme.buttonBackgroundDanger}` : ''}; border-left: ${({ danger, theme }: Props): string => danger ? `0.25rem solid ${theme.buttonBackgroundDanger}` : ''};
`;
.warningImage {
width: 16px;
height: 14px;
margin: 5px 10px 5px 0;
background: ${({ danger, theme }: Props): string => danger ? theme.iconDangerColor : theme.iconWarningColor};
}
`);

View File

@ -76,12 +76,14 @@ export function chooseTheme (): AvailableThemes {
const preferredTheme = localStorage.getItem('theme'); const preferredTheme = localStorage.getItem('theme');
if (preferredTheme) { if (preferredTheme) {
return preferredTheme === 'dark' ? 'dark' : 'light'; return preferredTheme === 'dark'
? 'dark'
: 'light';
} }
const isLightColorSchemePreferred = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches; return window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches
? 'light'
return isLightColorSchemePreferred ? 'light' : 'dark'; : 'dark';
} }
export declare type Theme = typeof darkTheme; export declare type Theme = typeof darkTheme;

View File

@ -12,7 +12,7 @@ export default function useMetadata (genesisHash?: string | null): Chain | null
const [chain, setChain] = useState<Chain | null>(null); const [chain, setChain] = useState<Chain | null>(null);
useEffect((): void => { useEffect((): void => {
getMetadata(genesisHash).then(setChain); getMetadata(genesisHash).then(setChain).catch(console.error);
}, [genesisHash]); }, [genesisHash]);
return chain; return chain;

View File

@ -17,7 +17,7 @@ describe('messaging sends message to background via extension port for', () => {
const callback = jest.fn(); const callback = jest.fn();
chrome.runtime.connect().onMessage.addListener(callback); chrome.runtime.connect().onMessage.addListener(callback);
exportAccount('HjoBp62cvsWDA3vtNMWxz6c9q13ReEHi9UGHK7JbZweH5g5', 'passw0rd'); exportAccount('HjoBp62cvsWDA3vtNMWxz6c9q13ReEHi9UGHK7JbZweH5g5', 'passw0rd').catch(console.error);
expect(callback).toHaveBeenCalledWith( expect(callback).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({

View File

@ -2,6 +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 { Message } from '@polkadot/extension-base/types';
import { AccountJson, AuthorizeRequest, SigningRequest, RequestTypes, MessageTypes, ResponseTypes, SeedLengths, SubscriptionMessageTypes, MetadataRequest, MessageTypesWithNullRequest, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions, ResponseDeriveValidate } from '@polkadot/extension-base/background/types'; import { AccountJson, AuthorizeRequest, SigningRequest, RequestTypes, MessageTypes, ResponseTypes, SeedLengths, SubscriptionMessageTypes, MetadataRequest, MessageTypesWithNullRequest, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions, ResponseDeriveValidate } from '@polkadot/extension-base/background/types';
import { Chain } from '@polkadot/extension-chains/types'; import { Chain } from '@polkadot/extension-chains/types';
import { KeypairType } from '@polkadot/util-crypto/types'; import { KeypairType } from '@polkadot/util-crypto/types';
@ -25,7 +26,7 @@ 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): void => { port.onMessage.addListener((data: Message['data']): void => {
const handler = handlers[data.id]; const handler = handlers[data.id];
if (!handler) { if (!handler) {
@ -39,6 +40,7 @@ port.onMessage.addListener((data): void => {
} }
if (data.subscription) { if (data.subscription) {
// eslint-disable-next-line @typescript-eslint/ban-types
(handler.subscriber as Function)(data.subscription); (handler.subscriber as Function)(data.subscription);
} else if (data.error) { } else if (data.error) {
handler.reject(new Error(data.error)); handler.reject(new Error(data.error));
@ -50,8 +52,7 @@ port.onMessage.addListener((data): void => {
function sendMessage<TMessageType extends MessageTypesWithNullRequest>(message: TMessageType): Promise<ResponseTypes[TMessageType]>; function sendMessage<TMessageType extends MessageTypesWithNullRequest>(message: TMessageType): Promise<ResponseTypes[TMessageType]>;
function sendMessage<TMessageType extends MessageTypesWithNoSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType]): Promise<ResponseTypes[TMessageType]>; function sendMessage<TMessageType extends MessageTypesWithNoSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType]): Promise<ResponseTypes[TMessageType]>;
function sendMessage<TMessageType extends MessageTypesWithSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise<ResponseTypes[TMessageType]>; function sendMessage<TMessageType extends MessageTypesWithSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise<ResponseTypes[TMessageType]>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any function sendMessage<TMessageType extends MessageTypes> (message: TMessageType, request?: RequestTypes[TMessageType], subscriber?: (data: unknown) => void): Promise<ResponseTypes[TMessageType]> {
function sendMessage<TMessageType extends MessageTypes> (message: TMessageType, request?: RequestTypes[TMessageType], subscriber?: (data: any) => void): Promise<ResponseTypes[TMessageType]> {
return new Promise((resolve, reject): void => { return new Promise((resolve, reject): void => {
const id = `${Date.now()}.${++idCounter}`; const id = `${Date.now()}.${++idCounter}`;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React, { useCallback, useContext } from 'react'; import React, { useCallback, useContext } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -43,7 +45,7 @@ const TipText = styled.p`
font-size: 16px; font-size: 16px;
line-height: 26px; line-height: 26px;
margin: 0 30px; margin: 0 30px;
color: ${({ theme }): string => theme.subTextColor}; color: ${({ theme }: ThemeProps): string => theme.subTextColor};
`; `;
const Image = styled.div` const Image = styled.div`
@ -52,11 +54,11 @@ const Image = styled.div`
`; `;
export default styled(AddAccount)` export default styled(AddAccount)`
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps): string => theme.textColor};
height: 100%; height: 100%;
h3 { h3 {
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps): string => theme.textColor};
margin-top: 0; margin-top: 0;
font-weight: normal; font-weight: normal;
font-size: 24px; font-size: 24px;

View File

@ -8,10 +8,12 @@
/* eslint-disable react/jsx-sort-props */ /* eslint-disable react/jsx-sort-props */
/* eslint-disable react/jsx-max-props-per-line */ /* eslint-disable react/jsx-max-props-per-line */
import { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
interface Props { interface Props extends ThemeProps {
className?: string; className?: string;
onClick: () => void; onClick: () => void;
} }
@ -73,11 +75,11 @@ export default styled(AddAccountImage)`
} }
path { path {
fill: ${({ theme }): string => theme.textColor}; fill: ${({ theme }: Props): string => theme.textColor};
} }
& > g > circle { & > g > circle {
stroke: ${({ theme }): string => theme.inputBorderColor}; stroke: ${({ theme }: Props): string => theme.inputBorderColor};
fill: ${({ theme }): string => theme.addAccountImageBackground}; fill: ${({ theme }: Props): string => theme.addAccountImageBackground};
} }
`; `;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -28,22 +30,22 @@ const ArrowLeft = styled(Svg).attrs(() => ({ src: ArrowLeftImage }))`
width: 12px; width: 12px;
height: 12px; height: 12px;
margin-right: 13px; margin-right: 13px;
background: ${({ theme }): string => theme.labelColor}; background: ${({ theme }: ThemeProps) => theme.labelColor};
`; `;
const BackLink = styled(Link)` const BackLink = styled(Link)`
width: min-content; width: min-content;
text-decoration: underline; text-decoration: underline;
color: ${({ theme }): string => theme.labelColor}; color: ${({ theme }: ThemeProps) => theme.labelColor};
&:visited { &:visited {
color: ${({ theme }): string => theme.labelColor}; color: ${({ theme }: ThemeProps) => theme.labelColor};
} }
`; `;
export default styled(Back)` export default styled(Back)`
margin: 0; margin: 0;
line-height: 52px; line-height: 52px;
border-bottom: 1px solid ${({ theme }): string => theme.inputBorderColor}; border-bottom: 1px solid ${({ theme }: ThemeProps): string => theme.inputBorderColor};
font-size: ${({ theme }): string => theme.labelFontSize}; font-size: ${({ theme }: ThemeProps): string => theme.labelFontSize};
`; `;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -90,7 +92,7 @@ const LockIcon = styled(Svg).attrs(() => ({
}))` }))`
width: 11px; width: 11px;
height: 14px; height: 14px;
background: ${({ theme }): string => theme.iconNeutralColor}; background: ${({ theme }: ThemeProps) => theme.iconNeutralColor};
`; `;
const UnlockIcon = styled(Svg).attrs(() => ({ const UnlockIcon = styled(Svg).attrs(() => ({
@ -98,7 +100,7 @@ const UnlockIcon = styled(Svg).attrs(() => ({
}))` }))`
width: 11px; width: 11px;
height: 14px; height: 14px;
background: ${({ theme }): string => theme.iconNeutralColor}; background: ${({ theme }: ThemeProps) => theme.iconNeutralColor};
`; `;
const LockButton = styled(Button)` const LockButton = styled(Button)`

View File

@ -18,6 +18,7 @@ configure({ adapter: new Adapter() });
describe('Header component', () => { describe('Header component', () => {
let wrapper: ReactWrapper; let wrapper: ReactWrapper;
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
const mountHeader = (props: React.ComponentProps<typeof Header> = {}): ReactWrapper => mount( const mountHeader = (props: React.ComponentProps<typeof Header> = {}): ReactWrapper => mount(
<MemoryRouter> <MemoryRouter>
<ThemeProvider theme={themes.dark}> <ThemeProvider theme={themes.dark}>

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React, { useCallback, useRef, useState } from 'react'; import React, { useCallback, useRef, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -62,28 +64,26 @@ function Header ({ children, className, showBackArrow, showSettings, text }: Pro
const BackLink = styled(Link)` const BackLink = styled(Link)`
width: min-content; width: min-content;
text-decoration: underline; text-decoration: underline;
color: ${({ theme }): string => theme.labelColor}; color: ${({ theme }: ThemeProps) => theme.labelColor};
min-height: 52px; min-height: 52px;
&:visited { &:visited {
color: ${({ theme }): string => theme.labelColor}; color: ${({ theme }: ThemeProps) => theme.labelColor};
} }
`; `;
const ArrowLeft = styled(Svg).attrs(() => ({ const ArrowLeft = styled(Svg).attrs(() => ({ src: ArrowLeftImage }))`
src: ArrowLeftImage
}))`
width: 12px; width: 12px;
height: 12px; height: 12px;
margin-right: 13px; margin-right: 13px;
background: ${({ theme }): string => theme.labelColor}; background: ${({ theme }: ThemeProps) => theme.labelColor};
`; `;
const Container = styled.div` const Container = styled.div`
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
border-bottom: 1px solid ${({ theme }): string => theme.inputBorderColor}; border-bottom: 1px solid ${({ theme }: ThemeProps) => theme.inputBorderColor};
min-height: 70px; min-height: 70px;
`; `;
@ -91,8 +91,8 @@ const Branding = styled.div`
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
color: ${({ theme }): string => theme.labelColor}; color: ${({ theme }: ThemeProps) => theme.labelColor};
font-family: ${({ theme }): string => theme.fontFamily}; font-family: ${({ theme }: ThemeProps) => theme.fontFamily};
text-align: center; text-align: center;
margin-left: 24px; margin-left: 24px;
`; `;
@ -107,21 +107,19 @@ interface GearProps {
isSelected: boolean; isSelected: boolean;
} }
const Gear = styled(Svg).attrs(() => ({ const Gear = styled(Svg).attrs(() => ({ src: gear }))<GearProps>`
src: gear
}))<GearProps>`
height: 18px; height: 18px;
width: 18px; width: 18px;
margin-right: 24px; margin-right: 24px;
align-self: center; align-self: center;
background: ${({ isSelected, theme }): string => isSelected ? theme.primaryColor : theme.iconNeutralColor}; background: ${({ isSelected, theme }: ThemeProps & GearProps): string => isSelected ? theme.primaryColor : theme.iconNeutralColor};
`; `;
Gear.displayName = 'Gear'; Gear.displayName = 'Gear';
const LogoText = styled.span` const LogoText = styled.span`
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps) => theme.textColor};
font-family: ${({ theme }): string => theme.fontFamily}; font-family: ${({ theme }: ThemeProps) => theme.fontFamily};
font-size: 20px; font-size: 20px;
line-height: 27px; line-height: 27px;
`; `;

View File

@ -2,6 +2,8 @@
// 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 { ThemeProps } from '../types';
import React, { useCallback, useContext } from 'react'; import React, { useCallback, useContext } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@ -46,15 +48,15 @@ const CreationSteps = styled.div`
`; `;
const CurrentStep = styled.span` const CurrentStep = styled.span`
font-size: ${({ theme }): string => theme.labelFontSize}; font-size: ${({ theme }: ThemeProps) => theme.labelFontSize};
line-height: ${({ theme }): string => theme.labelLineHeight}; line-height: ${({ theme }: ThemeProps) => theme.labelLineHeight};
color: ${({ theme }): string => theme.primaryColor}; color: ${({ theme }: ThemeProps) => theme.primaryColor};
font-weight: 600; font-weight: 600;
`; `;
const TotalSteps = styled.span` const TotalSteps = styled.span`
font-size: ${({ theme }): string => theme.labelFontSize}; font-size: ${({ theme }: ThemeProps) => theme.labelFontSize};
line-height: ${({ theme }): string => theme.labelLineHeight}; line-height: ${({ theme }: ThemeProps) => theme.labelLineHeight};
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps) => theme.textColor};
font-weight: 600; font-weight: 600;
`; `;

View File

@ -2,6 +2,8 @@
// 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 { Theme, ThemeProps } from '../types';
import React, { useCallback, useContext, useState, useEffect } from 'react'; import React, { useCallback, useContext, useState, useEffect } from 'react';
import styled, { ThemeContext } from 'styled-components'; import styled, { ThemeContext } from 'styled-components';
import { setSS58Format } from '@polkadot/util-crypto'; import { setSS58Format } from '@polkadot/util-crypto';
@ -16,6 +18,11 @@ interface Option {
value: string; value: string;
} }
interface Props extends ThemeProps {
className?: string;
reference: React.MutableRefObject<null>;
}
const isPopup = window.innerWidth <= 480; const isPopup = window.innerWidth <= 480;
const prefixOptions = settings.availablePrefixes.map(({ text, value }): Option => ({ const prefixOptions = settings.availablePrefixes.map(({ text, value }): Option => ({
text: value === -1 text: value === -1
@ -24,10 +31,10 @@ const prefixOptions = settings.availablePrefixes.map(({ text, value }): Option =
value: `${value}` value: `${value}`
})); }));
export default function Settings ({ reference }: { reference: React.MutableRefObject<null> }): React.ReactElement { function Settings ({ className, reference }: Props): React.ReactElement<Props> {
const [camera, setCamera] = useState(settings.camera === 'on'); const [camera, setCamera] = useState(settings.camera === 'on');
const [prefix, setPrefix] = useState(`${settings.prefix}`); const [prefix, setPrefix] = useState(`${settings.prefix}`);
const themeContext = useContext(ThemeContext); const themeContext = useContext<Theme>(ThemeContext);
const setTheme = useContext(ThemeSwitchContext); const setTheme = useContext(ThemeSwitchContext);
useEffect(() => { useEffect(() => {
@ -56,7 +63,10 @@ export default function Settings ({ reference }: { reference: React.MutableRefOb
); );
return ( return (
<SettingsMenu reference={reference}> <Menu
className={className}
reference={reference}
>
<Setting> <Setting>
<SettingTitle>Theme</SettingTitle> <SettingTitle>Theme</SettingTitle>
<Switch <Switch
@ -92,28 +102,22 @@ export default function Settings ({ reference }: { reference: React.MutableRefOb
/> />
</Setting> </Setting>
)} )}
</SettingsMenu> </Menu>
); );
} }
const SettingsMenu = styled(Menu)`
margin-top: 56px;
right: 24px;
user-select: none;
`;
const CheckboxSetting = styled(Checkbox)` const CheckboxSetting = styled(Checkbox)`
font-size: 15px; font-size: 15px;
font-weight: 600; font-weight: 600;
line-height: 20px; line-height: 20px;
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps) => theme.textColor};
label { label {
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps) => theme.textColor};
} }
`; `;
const DropdownSetting = styled(Dropdown)` const DropdownSetting = styled(Dropdown)`
background: ${({ theme }): string => theme.background}; background: ${({ theme }: ThemeProps) => theme.background};
margin-top: 9px; margin-top: 9px;
margin-bottom: 12px; margin-bottom: 12px;
width : 100%; width : 100%;
@ -125,27 +129,38 @@ const SettingTitle = styled(Title)`
`; `;
const Setting = styled.div` const Setting = styled.div`
padding: 0 16px;
max-width: 100%;
& + & {
padding-top: 18px;
border-top: 1px solid ${({ theme }): string => theme.inputBorderColor};
}
`; `;
const OpenInNewWindowButton = styled(ActionText)` const OpenInNewWindowButton = styled(ActionText)`
span { span {
color: ${({ theme }): string => theme.textColor}; color: ${({ theme }: ThemeProps) => theme.textColor};
text-decoration: none; text-decoration: none;
font-weight: 600; font-weight: 600;
font-size: ${({ theme }): string => theme.fontSize}; font-size: ${({ theme }: ThemeProps) => theme.fontSize};
line-height: ${({ theme }): string => theme.lineHeight}; line-height: ${({ theme }: ThemeProps) => theme.lineHeight};
} }
${Svg} { ${Svg} {
width: 20px; width: 20px;
height: 20px; height: 20px;
top: 4px; top: 4px;
background: ${({ theme }): string => theme.textColor}; background: ${({ theme }: ThemeProps) => theme.textColor};
} }
`; `;
export default React.memo(styled(Settings)`
margin-top: 56px;
right: 24px;
user-select: none;
.setting {
padding: 0 16px;
max-width: 100%;
& + & {
padding-top: 18px;
border-top: 1px solid ${({ theme }: Props): string => theme.inputBorderColor};
}
}
`);

View File

@ -0,0 +1,11 @@
// Copyright 2019-2020 @polkadot/extension-ui authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Theme } from './components/themes';
export { Theme };
export interface ThemeProps {
theme: Theme;
}

View File

@ -11,7 +11,7 @@
"@polkadot/extension-ui": "0.26.0-beta.2" "@polkadot/extension-ui": "0.26.0-beta.2"
}, },
"devDependencies": { "devDependencies": {
"@polkadot/dev": "^0.53.5", "@polkadot/dev": "^0.54.1",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"extract-loader": "^5.0.1", "extract-loader": "^5.0.1",
"file-loader": "^6.0.0", "file-loader": "^6.0.0",

View File

@ -2,6 +2,8 @@
// 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 { Message } from '@polkadot/extension-base/types';
import { PORT_CONTENT } from '@polkadot/extension-base/defaults'; import { PORT_CONTENT } from '@polkadot/extension-base/defaults';
import chrome from '@polkadot/extension-inject/chrome'; import chrome from '@polkadot/extension-inject/chrome';
@ -14,7 +16,7 @@ port.onMessage.addListener((data): void => {
}); });
// 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 }): void => { window.addEventListener('message', ({ data, source }: Message): 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;

View File

@ -2,18 +2,21 @@
// 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 { Message } from '@polkadot/extension-base/types';
import { enable, handleResponse } from '@polkadot/extension-base/page'; import { enable, handleResponse } from '@polkadot/extension-base/page';
import { injectExtension } from '@polkadot/extension-inject'; import { injectExtension } from '@polkadot/extension-inject';
// 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 }): void => { window.addEventListener('message', ({ data, source }: Message): 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;
} }
if (data.id) { if (data.id) {
handleResponse(data); // eslint-disable-next-line @typescript-eslint/no-explicit-any
handleResponse(data as any);
} else { } else {
console.error('Missing id for response.'); console.error('Missing id for response.');
} }

View File

@ -61,7 +61,6 @@ 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',

823
yarn.lock

File diff suppressed because it is too large Load Diff