diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js new file mode 100644 index 00000000..981518ac --- /dev/null +++ b/docs/.vuepress/config.js @@ -0,0 +1,22 @@ +module.exports = { + base: '/ui/', + title: 'polkadot-js/ui', + description: 'UI libraries and utilities in-use accross @polkadot projects', + markdown: { + lineNumbers: true + }, + themeConfig: { + displayAllHeaders: true, + lastUpdated: 'Last Updated', + nav: [ + { text: 'GitHub', link: 'https://github.com/polkadot-js/ui' } + ], + sidebar: [ + ['/ui-identicon/', '@polkadot/ui-identicon'], + ['/ui-keyring/', '@polkadot/ui-keyring'], + ['/ui-settings/', '@polkadot/ui-settings'], + ['/ui-util/', '@polkadot/ui-util'], + '/CONTRIBUTING.md' + ] + } +}; diff --git a/docs/.vuepress/styles/palette.styl b/docs/.vuepress/styles/palette.styl new file mode 100644 index 00000000..37ea71e5 --- /dev/null +++ b/docs/.vuepress/styles/palette.styl @@ -0,0 +1 @@ +$accentColor = #fd8824 diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 00000000..947952e8 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,45 @@ +# Contributing + +## What? + +Individuals making significant and valuable contributions are given commit-access to a project to contribute as they see fit. +A project is more like an open wiki than a standard guarded open source project. + +## Rules + +There are a few basic ground-rules for contributors (including the maintainer(s) of the project): + +1. **No `--force` pushes** or modifying the Git history in any way. If you need to rebase, ensure you do it in your own repo. +2. **Non-master branches**, prefixed with a short name moniker (e.g. `-`) must be used for ongoing work. +3. **All modifications** must be made in a **pull-request** to solicit feedback from other contributors. +4. A pull-request *must not be merged until CI* has finished successfully. + +#### Merging pull requests once CI is successful: +- A pull request with no large change to logic that is an urgent fix may be merged after a non-author contributor has reviewed it well. +- No PR should be merged until all reviews' comments are addressed. + +#### Reviewing pull requests: +When reviewing a pull request, the end-goal is to suggest useful changes to the author. Reviews should finish with approval unless there are issues that would result in: + +- Buggy behaviour. +- Undue maintenance burden. +- Breaking with house coding style. +- Pessimisation (i.e. reduction of speed as measured in the projects benchmarks). +- Feature reduction (i.e. it removes some aspect of functionality that a significant minority of users rely on). +- Uselessness (i.e. it does not strictly add a feature or fix a known issue). + +#### Reviews may not be used as an effective veto for a PR because: +- There exists a somewhat cleaner/better/faster way of accomplishing the same feature/fix. +- It does not fit well with some other contributors' longer-term vision for the project. + +## Releases + +Declaring formal releases remains the prerogative of the project maintainer(s). + +## Changes to this arrangement + +This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change. + +## Heritage + +These contributing guidelines are modified from the "OPEN Open Source Project" guidelines for the Level project: [https://github.com/Level/community/blob/master/CONTRIBUTING.md](https://github.com/Level/community/blob/master/CONTRIBUTING.md) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..48c35bb5 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,21 @@ +# Polkadot Javascript UI libraries + +This is a collection of UI (Rwact) libraries, utlities and classes that allows for easier use of the Polkadot JS infrstaructure inside your apps. + +## The Polkadot Project + +You can read more about the Polkadot Network at [https://polkadot.network/](https://polkadot.network/) and more about the polkadot-js projects at [https://polkadot.js.org](https://polkadot.js.org) + +## Github repositories + +You can find the Polkadot repositories at: + +- [https://github.com/polkadot-js](https://github.com/polkadot-js) +- [https://github.com/paritytech/polkadot](https://github.com/paritytech/polkadot) +- [https://github.com/paritytech/substrate](https://github.com/paritytech/substrate) + +This documentation is generated from [https://github.com/polkadot-js/ui](https://github.com/polkadot-js/ui) + +## Contributing + +Contribution to the Polkadot JS projects are more than welcome. You can [report issues](https://github.com/polkadot-js/ui/issues/new) and [log feature requests](https://github.com/polkadot-js/ui/issues/new). diff --git a/package.json b/package.json index 988bccd7..5260d022 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "typescript": "^3.3.3333" }, "scripts": { - "build": "polkadot-dev-build-ts", + "build": "polkadot-dev-build-ts && polkadot-dev-build-docs", "check": "tslint --project . && tsc --noEmit --pretty", "clean": "polkadot-dev-clean-build", "postinstall": "polkadot-dev-yarn-only", diff --git a/packages/ui-assets/.nodoc b/packages/ui-assets/.nodoc new file mode 100644 index 00000000..e69de29b diff --git a/packages/ui-identicon/README.md b/packages/ui-identicon/README.md index 6853cb7a..2c9c6448 100644 --- a/packages/ui-identicon/README.md +++ b/packages/ui-identicon/README.md @@ -2,7 +2,7 @@ A generic identity icon that can render icons based on the theme, be it Substrate or Polkadot -## Usage +## Usage Examples To install the component, do `yarn add @polkadot/ui-identicon` @@ -17,7 +17,8 @@ render () { const { address } = this.props; // size (optional) is a number, indicating the size (in pixels, 64 as default) const size = 32; - // theme (optional), depicts the type of icon, either 'polkadot' or 'substrate' (default) + // theme (optional), depicts the type of icon, one of + // 'polkadot', 'substrate' (default), 'beachball' or 'jdenticon' const theme = 'polkadot'; // standard className & style props are also available diff --git a/packages/ui-identicon/src/index.tsx b/packages/ui-identicon/src/Identicon.tsx similarity index 100% rename from packages/ui-identicon/src/index.tsx rename to packages/ui-identicon/src/Identicon.tsx diff --git a/packages/ui-identicon/src/beachball/colors.spec.js b/packages/ui-identicon/src/beachball/colors.spec.ts similarity index 95% rename from packages/ui-identicon/src/beachball/colors.spec.js rename to packages/ui-identicon/src/beachball/colors.spec.ts index b091b218..47b20504 100644 --- a/packages/ui-identicon/src/beachball/colors.spec.js +++ b/packages/ui-identicon/src/beachball/colors.spec.ts @@ -2,11 +2,13 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { ColorGen } from './types'; + import newSeeder from './seeder'; import newColors from './colors'; describe('colors', () => { - let colors; + let colors: ColorGen; beforeEach(() => { colors = newColors(newSeeder()); diff --git a/packages/ui-identicon/src/beachball/colors.ts b/packages/ui-identicon/src/beachball/colors.ts index dcef9cd7..42b91abf 100644 --- a/packages/ui-identicon/src/beachball/colors.ts +++ b/packages/ui-identicon/src/beachball/colors.ts @@ -3,16 +3,12 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Seeder } from './types'; +import { ColorGen, Seeder } from './types'; import Color from 'color'; import { COLORS } from './defaults'; -type ColorGen = { - (alpha?: number): string -}; - const WOBBLE = 30; export default function colors (seeder: Seeder): ColorGen { diff --git a/packages/ui-identicon/src/beachball/container.spec.js b/packages/ui-identicon/src/beachball/container.spec.ts similarity index 89% rename from packages/ui-identicon/src/beachball/container.spec.js rename to packages/ui-identicon/src/beachball/container.spec.ts index 9a967648..8b4fc28a 100644 --- a/packages/ui-identicon/src/beachball/container.spec.js +++ b/packages/ui-identicon/src/beachball/container.spec.ts @@ -7,7 +7,7 @@ import container from './container'; describe('container', () => { it('applies default styles', () => { expect( - container(100).style._values + (container(100).style as any)._values ).toMatchObject({ 'background': 'white', 'border-radius': '50px', @@ -22,7 +22,7 @@ describe('container', () => { it('overrides with supplied styles', () => { expect( - container(50, 'black', '', { display: 'block' }).style._values + (container(50, 'black', '', { display: 'block' }).style as any)._values ).toMatchObject({ 'background': 'black', 'border-radius': '25px', diff --git a/packages/ui-identicon/src/beachball/index.spec.js b/packages/ui-identicon/src/beachball/index.spec.ts similarity index 100% rename from packages/ui-identicon/src/beachball/index.spec.js rename to packages/ui-identicon/src/beachball/index.spec.ts diff --git a/packages/ui-identicon/src/beachball/index.ts b/packages/ui-identicon/src/beachball/index.ts index bc5c615c..88be3c63 100644 --- a/packages/ui-identicon/src/beachball/index.ts +++ b/packages/ui-identicon/src/beachball/index.ts @@ -10,7 +10,7 @@ import newShape from './shape/circle'; import newElement from './svg/element'; import { SHAPE_COUNT } from './defaults'; -export default function identicon (seed: string, diameter: number = 256, className: string = '', style?: { [index: string]: string }): HTMLElement { +export default function identicon (seed: string | Uint8Array, diameter: number = 256, className: string = '', style?: { [index: string]: string }): HTMLElement { const seeder = newSeeder(seed); const colorGen = colors(seeder); const outer = newContainer(diameter, 'white', className, style); diff --git a/packages/ui-identicon/src/beachball/seeder.spec.js b/packages/ui-identicon/src/beachball/seeder.spec.ts similarity index 91% rename from packages/ui-identicon/src/beachball/seeder.spec.js rename to packages/ui-identicon/src/beachball/seeder.spec.ts index 33809eba..66b70c19 100644 --- a/packages/ui-identicon/src/beachball/seeder.spec.js +++ b/packages/ui-identicon/src/beachball/seeder.spec.ts @@ -2,10 +2,12 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Seeder } from './types'; + import newSeeder from './seeder'; describe('seeder', () => { - let seeder; + let seeder: Seeder; beforeEach(() => { seeder = newSeeder(new Uint8Array([1, 2, 3, 4])); diff --git a/packages/ui-identicon/src/beachball/shape/circle.spec.js b/packages/ui-identicon/src/beachball/shape/circle.spec.ts similarity index 100% rename from packages/ui-identicon/src/beachball/shape/circle.spec.js rename to packages/ui-identicon/src/beachball/shape/circle.spec.ts diff --git a/packages/ui-identicon/src/beachball/svg/circle.spec.js b/packages/ui-identicon/src/beachball/svg/circle.spec.ts similarity index 100% rename from packages/ui-identicon/src/beachball/svg/circle.spec.js rename to packages/ui-identicon/src/beachball/svg/circle.spec.ts diff --git a/packages/ui-identicon/src/beachball/svg/element.spec.js b/packages/ui-identicon/src/beachball/svg/element.spec.ts similarity index 100% rename from packages/ui-identicon/src/beachball/svg/element.spec.js rename to packages/ui-identicon/src/beachball/svg/element.spec.ts diff --git a/packages/ui-identicon/src/beachball/svg/rect.spec.js b/packages/ui-identicon/src/beachball/svg/rect.spec.ts similarity index 100% rename from packages/ui-identicon/src/beachball/svg/rect.spec.js rename to packages/ui-identicon/src/beachball/svg/rect.spec.ts diff --git a/packages/ui-identicon/src/beachball/svg/svg.spec.js b/packages/ui-identicon/src/beachball/svg/svg.spec.ts similarity index 100% rename from packages/ui-identicon/src/beachball/svg/svg.spec.js rename to packages/ui-identicon/src/beachball/svg/svg.spec.ts diff --git a/packages/ui-identicon/src/beachball/types.ts b/packages/ui-identicon/src/beachball/types.ts index af4e7155..f8661835 100644 --- a/packages/ui-identicon/src/beachball/types.ts +++ b/packages/ui-identicon/src/beachball/types.ts @@ -3,3 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. export type Seeder = () => number; + +export type ColorGen = { + (alpha?: number): string +}; diff --git a/packages/ui-identicon/src/beachball/xmlserializer.d.ts b/packages/ui-identicon/src/beachball/xmlserializer.d.ts new file mode 100644 index 00000000..da1ce8d1 --- /dev/null +++ b/packages/ui-identicon/src/beachball/xmlserializer.d.ts @@ -0,0 +1,5 @@ +// Copyright 2017-2019 @polkadot/ui-identicon authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +declare module 'xmlserializer'; diff --git a/packages/ui-identicon/src/index.ts b/packages/ui-identicon/src/index.ts new file mode 100644 index 00000000..865e98dc --- /dev/null +++ b/packages/ui-identicon/src/index.ts @@ -0,0 +1,8 @@ +// Copyright 2017-2019 @polkadot/ui-identicon 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 { default as Identicon } from './Identicon'; +export * from './icons'; + +export default Identicon; diff --git a/packages/ui-keyring/README.md b/packages/ui-keyring/README.md index a48d356d..63adb1e8 100644 --- a/packages/ui-keyring/README.md +++ b/packages/ui-keyring/README.md @@ -4,10 +4,12 @@ A wrapper extending the base @polkadot/keyring interface for usage in the browse Key management of user accounts including generation and retrieval of keyring pairs from a variety of input combinations. ## Usage Examples + All module methods are exposed through a single default export. ### Regular -``` + +```js import keyring from @polkadot/ui-keyring render () { @@ -28,9 +30,12 @@ render () { keyring.saveAddress(address, { ...meta }); } ``` + ## Observables -### Option 1: Declarative subscribe/unsubscribe w/ react-with-observable (recommended 'React' way) -``` + +Option 1: Declarative subscribe/unsubscribe w/ react-with-observable (recommended 'React' way) + +```js import accountObservable from '@polkadot/ui-keyring/observable/accounts'; import { SingleAddress, SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import React from 'react'; @@ -61,8 +66,9 @@ class MyReactComponent extends React.PureComponent { ``` -### Option 2: Imperative subscribe/unsubscribe -``` +Option 2: Imperative subscribe/unsubscribe + +```js import accountObservable from '@polkadot/ui-keyring/observable/accounts'; import { SingleAddress, SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import React from 'react'; @@ -110,6 +116,7 @@ class MyReactComponent extends React.PureComponent { ``` ## FAQ + - Difference between Keyring Accounts and Addresses? - From the perspective of the keyring, it saves a particular user's unlocked identities as an account, a la keyring.saveAccount(pair, password). So with these accounts you are able to send and sign transactions. - To save addresses without unlocking them (i.e. because a user might want to have easy access to addresses they frequently transact with), use keyring.saveAddress(address, meta) @@ -121,10 +128,9 @@ class MyReactComponent extends React.PureComponent { **If you have any unanswered/undocumented questions, please raise an issue [here](https://github.com/polkadot-js/ui/issues).** - ## Users + Keyring is core to many polkadot/substrate apps. * [polkadot-js/apps](https://github.com/polkadot-js/apps) -* [polkadot-js/api](https://github.com/polkadot-js/api) * [paritytech/substrate-light-ui](https://github.com/paritytech/substrate-light-ui) diff --git a/packages/ui-keyring/src/Keyring.ts b/packages/ui-keyring/src/Keyring.ts new file mode 100644 index 00000000..17b19d84 --- /dev/null +++ b/packages/ui-keyring/src/Keyring.ts @@ -0,0 +1,248 @@ +// Copyright 2017-2019 @polkadot/ui-keyring 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 { KeyringPair, KeyringPair$Meta, KeyringPair$Json } from '@polkadot/keyring/types'; +import { SingleAddress } from './observable/types'; +import { KeyringAddress, KeyringJson, KeyringJson$Meta, KeyringOptions, KeyringStruct } from './types'; + +import store from 'store'; +import createPair from '@polkadot/keyring/pair'; +import { hexToU8a, isHex, isString } from '@polkadot/util'; + +import env from './observable/development'; +import Base from './Base'; +import { accountKey, addressKey, accountRegex, addressRegex } from './defaults'; +import keyringOption from './options'; + +// No accounts (or test accounts) should be loaded until after the chain determination. +// Chain determination occurs outside of Keyring. Loading `keyring.loadAll({ type: 'ed25519' | 'sr25519' })` is triggered +// from the API after the chain is received +export class Keyring extends Base implements KeyringStruct { + addAccountPair (pair: KeyringPair, password: string): KeyringPair { + this.keyring.addPair(pair); + this.saveAccount(pair, password); + + return pair; + } + + backupAccount (pair: KeyringPair, password: string): KeyringPair$Json { + if (!pair.isLocked()) { + pair.lock(); + } + + pair.decodePkcs8(password); + + return pair.toJson(password); + } + + createAccount (seed: Uint8Array, password?: string, meta: KeyringPair$Meta = {}): KeyringPair { + const pair = this.keyring.addFromSeed(seed, meta); + + this.saveAccount(pair, password); + + return pair; + } + + createAccountExternal (publicKey: Uint8Array, meta: KeyringPair$Meta = {}): KeyringPair { + const pair = this.keyring.addFromAddress(publicKey, { ...meta, isExternal: true }, null); + + this.saveAccount(pair); + + return pair; + } + + createAccountMnemonic (seed: string, password?: string, meta: KeyringPair$Meta = {}): KeyringPair { + const pair = this.keyring.addFromMnemonic(seed, meta); + + this.saveAccount(pair, password); + + return pair; + } + + encryptAccount (pair: KeyringPair, password: string): void { + const json = pair.toJson(password); + + json.meta.whenEdited = Date.now(); + + this.keyring.addFromJson(json); + this.accounts.add(json.address, json); + } + + forgetAccount (address: string): void { + this.keyring.removePair(address); + this.accounts.remove(address); + } + + forgetAddress (address: string): void { + this.addresses.remove(address); + } + + getAccount (address: string | Uint8Array): KeyringAddress { + return this.getAddress(address, 'account'); + } + + getAccounts (): Array { + const available = this.accounts.subject.getValue(); + + return Object + .keys(available) + .map((address) => this.getAddress(address, 'account')) + .filter((account) => env.isDevelopment() || account.getMeta().isTesting !== true); + } + + getAddress (_address: string | Uint8Array, type: 'account' | 'address' = 'address'): KeyringAddress { + const address = isString(_address) + ? _address + : this.encodeAddress(_address); + const publicKey = this.decodeAddress(address); + const subject = type === 'account' + ? this.accounts.subject + : this.addresses.subject; + + return { + address: (): string => + address, + isValid: (): boolean => + !!subject.getValue()[address], + publicKey: (): Uint8Array => + publicKey, + getMeta: (): KeyringJson$Meta => + subject.getValue()[address].json.meta + }; + } + + getAddresses (): Array { + const available = this.addresses.subject.getValue(); + + return Object + .keys(available) + .map((address) => this.getAddress(address)); + } + + private rewriteKey (json: KeyringJson, key: string, hexAddr: string, creator: (addr: string) => string) { + if (hexAddr.substr(0, 2) === '0x') { + return; + } + + store.remove(key); + store.set(creator(hexAddr), json); + } + + private loadAccount (json: KeyringJson, key: string) { + if (!json.meta.isTesting && (json as KeyringPair$Json).encoded) { + const pair = this.keyring.addFromJson(json as KeyringPair$Json); + + this.accounts.add(pair.address(), json); + } + + const [, hexAddr] = key.split(':'); + + this.rewriteKey(json, key, hexAddr, accountKey); + } + + private loadAddress (json: KeyringJson, key: string) { + const address = this.encodeAddress( + isHex(json.address) + ? hexToU8a(json.address) + : this.decodeAddress(json.address) + ); + const [, hexAddr] = key.split(':'); + + this.addresses.add(address, json); + this.rewriteKey(json, key, hexAddr, addressKey); + } + + loadAll (options: KeyringOptions): void { + super.initKeyring(options); + + store.each((json: KeyringJson, key: string) => { + if (accountRegex.test(key)) { + this.loadAccount(json, key); + } else if (addressRegex.test(key)) { + this.loadAddress(json, key); + } + }); + + keyringOption.init(this); + } + + restoreAccount (json: KeyringPair$Json, password: string): KeyringPair { + const pair = createPair( + this.keyring.type, + { + publicKey: this.decodeAddress(json.address) + }, + json.meta, + hexToU8a(json.encoded) + ); + + // unlock, save account and then lock (locking cleans secretKey, so needs to be last) + pair.decodePkcs8(password); + this.addAccountPair(pair, password); + pair.lock(); + + return pair; + } + + saveAccount (pair: KeyringPair, password?: string): void { + this.addTimestamp(pair); + + const json = pair.toJson(password); + + this.keyring.addFromJson(json); + this.accounts.add(json.address, json); + } + + saveAccountMeta (pair: KeyringPair, meta: KeyringPair$Meta): void { + const address = pair.address(); + const json = store.get(accountKey(address)); + + pair.setMeta(meta); + json.meta = pair.getMeta(); + + this.accounts.add(json.address, json); + } + + saveAddress (address: string, meta: KeyringPair$Meta): void { + const available = this.addresses.subject.getValue(); + + const json = (available[address] && available[address].json) || { + address, + meta: { + isRecent: void 0, + whenCreated: Date.now() + } + }; + + Object.keys(meta).forEach((key) => { + json.meta[key] = meta[key]; + }); + + delete json.meta.isRecent; + + this.addresses.add(address, json); + } + + saveRecent (address: string): SingleAddress { + const available = this.addresses.subject.getValue(); + + if (!available[address]) { + const json = { + address, + meta: { + isRecent: true, + whenCreated: Date.now() + } + }; + + this.addresses.add(address, (json as KeyringJson)); + } + + return this.addresses.subject.getValue()[address]; + } +} + +const keyringInstance = new Keyring(); + +export default keyringInstance; diff --git a/packages/ui-keyring/src/index.ts b/packages/ui-keyring/src/index.ts index d95a89ad..c96c23dd 100644 --- a/packages/ui-keyring/src/index.ts +++ b/packages/ui-keyring/src/index.ts @@ -2,247 +2,10 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { KeyringPair, KeyringPair$Meta, KeyringPair$Json } from '@polkadot/keyring/types'; -import { SingleAddress } from './observable/types'; -import { KeyringAddress, KeyringJson, KeyringJson$Meta, KeyringOptions, KeyringStruct } from './types'; +import keyring, { Keyring } from './Keyring'; -import store from 'store'; -import createPair from '@polkadot/keyring/pair'; -import { hexToU8a, isHex, isString } from '@polkadot/util'; +export default keyring; -import env from './observable/development'; -import Base from './Base'; -import { accountKey, addressKey, accountRegex, addressRegex } from './defaults'; -import keyringOption from './options'; - -// No accounts (or test accounts) should be loaded until after the chain determination. -// Chain determination occurs outside of Keyring. Loading `keyring.loadAll({ type: 'ed25519' | 'sr25519' })` is triggered -// from the API after the chain is received -class Keyring extends Base implements KeyringStruct { - addAccountPair (pair: KeyringPair, password: string): KeyringPair { - this.keyring.addPair(pair); - this.saveAccount(pair, password); - - return pair; - } - - backupAccount (pair: KeyringPair, password: string): KeyringPair$Json { - if (!pair.isLocked()) { - pair.lock(); - } - - pair.decodePkcs8(password); - - return pair.toJson(password); - } - - createAccount (seed: Uint8Array, password?: string, meta: KeyringPair$Meta = {}): KeyringPair { - const pair = this.keyring.addFromSeed(seed, meta); - - this.saveAccount(pair, password); - - return pair; - } - - createAccountExternal (publicKey: Uint8Array, meta: KeyringPair$Meta = {}): KeyringPair { - const pair = this.keyring.addFromAddress(publicKey, { ...meta, isExternal: true }, null); - - this.saveAccount(pair); - - return pair; - } - - createAccountMnemonic (seed: string, password?: string, meta: KeyringPair$Meta = {}): KeyringPair { - const pair = this.keyring.addFromMnemonic(seed, meta); - - this.saveAccount(pair, password); - - return pair; - } - - encryptAccount (pair: KeyringPair, password: string): void { - const json = pair.toJson(password); - - json.meta.whenEdited = Date.now(); - - this.keyring.addFromJson(json); - this.accounts.add(json.address, json); - } - - forgetAccount (address: string): void { - this.keyring.removePair(address); - this.accounts.remove(address); - } - - forgetAddress (address: string): void { - this.addresses.remove(address); - } - - getAccount (address: string | Uint8Array): KeyringAddress { - return this.getAddress(address, 'account'); - } - - getAccounts (): Array { - const available = this.accounts.subject.getValue(); - - return Object - .keys(available) - .map((address) => this.getAddress(address, 'account')) - .filter((account) => env.isDevelopment() || account.getMeta().isTesting !== true); - } - - getAddress (_address: string | Uint8Array, type: 'account' | 'address' = 'address'): KeyringAddress { - const address = isString(_address) - ? _address - : this.encodeAddress(_address); - const publicKey = this.decodeAddress(address); - const subject = type === 'account' - ? this.accounts.subject - : this.addresses.subject; - - return { - address: (): string => - address, - isValid: (): boolean => - !!subject.getValue()[address], - publicKey: (): Uint8Array => - publicKey, - getMeta: (): KeyringJson$Meta => - subject.getValue()[address].json.meta - }; - } - - getAddresses (): Array { - const available = this.addresses.subject.getValue(); - - return Object - .keys(available) - .map((address) => this.getAddress(address)); - } - - private rewriteKey (json: KeyringJson, key: string, hexAddr: string, creator: (addr: string) => string) { - if (hexAddr.substr(0, 2) === '0x') { - return; - } - - store.remove(key); - store.set(creator(hexAddr), json); - } - - private loadAccount (json: KeyringJson, key: string) { - if (!json.meta.isTesting && (json as KeyringPair$Json).encoded) { - const pair = this.keyring.addFromJson(json as KeyringPair$Json); - - this.accounts.add(pair.address(), json); - } - - const [, hexAddr] = key.split(':'); - - this.rewriteKey(json, key, hexAddr, accountKey); - } - - private loadAddress (json: KeyringJson, key: string) { - const address = this.encodeAddress( - isHex(json.address) - ? hexToU8a(json.address) - : this.decodeAddress(json.address) - ); - const [, hexAddr] = key.split(':'); - - this.addresses.add(address, json); - this.rewriteKey(json, key, hexAddr, addressKey); - } - - loadAll (options: KeyringOptions): void { - super.initKeyring(options); - - store.each((json: KeyringJson, key: string) => { - if (accountRegex.test(key)) { - this.loadAccount(json, key); - } else if (addressRegex.test(key)) { - this.loadAddress(json, key); - } - }); - - keyringOption.init(this); - } - - restoreAccount (json: KeyringPair$Json, password: string): KeyringPair { - const pair = createPair( - this.keyring.type, - { - publicKey: this.decodeAddress(json.address) - }, - json.meta, - hexToU8a(json.encoded) - ); - - // unlock, save account and then lock (locking cleans secretKey, so needs to be last) - pair.decodePkcs8(password); - this.addAccountPair(pair, password); - pair.lock(); - - return pair; - } - - saveAccount (pair: KeyringPair, password?: string): void { - this.addTimestamp(pair); - - const json = pair.toJson(password); - - this.keyring.addFromJson(json); - this.accounts.add(json.address, json); - } - - saveAccountMeta (pair: KeyringPair, meta: KeyringPair$Meta): void { - const address = pair.address(); - const json = store.get(accountKey(address)); - - pair.setMeta(meta); - json.meta = pair.getMeta(); - - this.accounts.add(json.address, json); - } - - saveAddress (address: string, meta: KeyringPair$Meta): void { - const available = this.addresses.subject.getValue(); - - const json = (available[address] && available[address].json) || { - address, - meta: { - isRecent: void 0, - whenCreated: Date.now() - } - }; - - Object.keys(meta).forEach((key) => { - json.meta[key] = meta[key]; - }); - - delete json.meta.isRecent; - - this.addresses.add(address, json); - } - - saveRecent (address: string): SingleAddress { - const available = this.addresses.subject.getValue(); - - if (!available[address]) { - const json = { - address, - meta: { - isRecent: true, - whenCreated: Date.now() - } - }; - - this.addresses.add(address, (json as KeyringJson)); - } - - return this.addresses.subject.getValue()[address]; - } -} - -const keyringInstance = new Keyring(); - -export default keyringInstance; +export { + Keyring +}; diff --git a/packages/ui-settings/README.md b/packages/ui-settings/README.md index 7887372a..58e4e775 100644 --- a/packages/ui-settings/README.md +++ b/packages/ui-settings/README.md @@ -2,9 +2,11 @@ Manages app settings including endpoints, themes and prefixes -### Usage Example +## Usage Examples + User preferences are set as a settings object in the browser's local storage. -``` + +```js import settings from '@polkadot/ui-settings'; render () { @@ -30,7 +32,9 @@ render () { } ``` -### Used by +## Used by + Apps that currently use the settings package -* [polkadot-js/apps]('https://www.github.com/polkadot-js/apps') + +* [polkadot-js/apps](https://www.github.com/polkadot-js/apps) * [paritytech/substrate-light-ui](https://github.com/paritytech/substrate-light-ui) diff --git a/packages/ui-settings/src/Settings.ts b/packages/ui-settings/src/Settings.ts new file mode 100644 index 00000000..96286793 --- /dev/null +++ b/packages/ui-settings/src/Settings.ts @@ -0,0 +1,78 @@ +// Copyright 2017-2019 @polkadot/ui-settings 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 store from 'store'; + +import { ENDPOINTS, LANGUAGES, UIMODES, UITHEMES } from './defaults'; +import { Options, SettingsStruct } from './types'; + +export class Settings implements SettingsStruct { + private _apiUrl: string; + private _i18nLang: string; + private _uiMode: string; + private _uiTheme: string; + + constructor () { + const settings = store.get('settings') || {}; + + this._apiUrl = settings.apiUrl || process.env.WS_URL || ENDPOINTS[0].value; + this._i18nLang = settings.i18nLang || LANGUAGES[0].value; + this._uiMode = settings.uiMode || process.env.UI_MODE || UIMODES[0].value; + this._uiTheme = settings.uiTheme || process.env.UI_THEME || UITHEMES[0].value; + } + + get apiUrl (): string { + return this._apiUrl; + } + + get i18nLang (): string { + return this._i18nLang; + } + + get uiMode (): string { + return this._uiMode; + } + + get uiTheme (): string { + return this._uiTheme; + } + + get availableNodes (): Options { + return ENDPOINTS; + } + + get availableLanguages (): Options { + return LANGUAGES; + } + + get availableUIModes (): Options { + return UIMODES; + } + + get availableUIThemes (): Options { + return UITHEMES; + } + + get (): SettingsStruct { + return { + apiUrl: this._apiUrl, + i18nLang: this._i18nLang, + uiMode: this._uiMode, + uiTheme: this._uiTheme + }; + } + + set (settings: Partial): void { + this._apiUrl = settings.apiUrl || this._apiUrl; + this._i18nLang = settings.i18nLang || this._i18nLang; + this._uiMode = settings.uiMode || this._uiMode; + this._uiTheme = settings.uiTheme || this._uiTheme; + + store.set('settings', this.get()); + } +} + +const settings = new Settings(); + +export default settings; diff --git a/packages/ui-settings/src/index.ts b/packages/ui-settings/src/index.ts index d132b6ed..45a92c52 100644 --- a/packages/ui-settings/src/index.ts +++ b/packages/ui-settings/src/index.ts @@ -2,77 +2,10 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import store from 'store'; - -import { ENDPOINTS, LANGUAGES, UIMODES, UITHEMES } from './defaults'; -import { Options, SettingsStruct } from './types'; - -class Settings implements SettingsStruct { - private _apiUrl: string; - private _i18nLang: string; - private _uiMode: string; - private _uiTheme: string; - - constructor () { - const settings = store.get('settings') || {}; - - this._apiUrl = settings.apiUrl || process.env.WS_URL || ENDPOINTS[0].value; - this._i18nLang = settings.i18nLang || LANGUAGES[0].value; - this._uiMode = settings.uiMode || process.env.UI_MODE || UIMODES[0].value; - this._uiTheme = settings.uiTheme || process.env.UI_THEME || UITHEMES[0].value; - } - - get apiUrl (): string { - return this._apiUrl; - } - - get i18nLang (): string { - return this._i18nLang; - } - - get uiMode (): string { - return this._uiMode; - } - - get uiTheme (): string { - return this._uiTheme; - } - - get availableNodes (): Options { - return ENDPOINTS; - } - - get availableLanguages (): Options { - return LANGUAGES; - } - - get availableUIModes (): Options { - return UIMODES; - } - - get availableUIThemes (): Options { - return UITHEMES; - } - - get (): SettingsStruct { - return { - apiUrl: this._apiUrl, - i18nLang: this._i18nLang, - uiMode: this._uiMode, - uiTheme: this._uiTheme - }; - } - - set (settings: Partial): void { - this._apiUrl = settings.apiUrl || this._apiUrl; - this._i18nLang = settings.i18nLang || this._i18nLang; - this._uiMode = settings.uiMode || this._uiMode; - this._uiTheme = settings.uiTheme || this._uiTheme; - - store.set('settings', this.get()); - } -} - -const settings = new Settings(); +import settings, { Settings } from './Settings'; export default settings; + +export { + Settings +}; diff --git a/packages/ui-util/README.md b/packages/ui-util/README.md index e28680f2..b87941dc 100644 --- a/packages/ui-util/README.md +++ b/packages/ui-util/README.md @@ -18,6 +18,6 @@ formatBalance('12345'); // 12.345z DOT ## calcSi ```js -// calculates the SI unit applucable +// calculates the SI unit applicable formatBalance.calcSi('12345'); // { power: 3, value: 'k', text: 'Kilo' } ``` diff --git a/typedoc.js b/typedoc.js new file mode 100644 index 00000000..a3fc3ba3 --- /dev/null +++ b/typedoc.js @@ -0,0 +1,16 @@ +module.exports = { + name: 'Polkadot JS UI libraries', + exclude: '**/*+(index|e2e|spec).ts', + excludeExternals: true, + excludeNotExported: true, + excludeProtected: true, + excludePrivate: true, + hideGenerator: true, + includeDeclarations: false, + out: 'docs', + module: 'commonjs', + moduleResolution: 'node', + mdEngine: 'gitbook', + stripInternal: 'true', + theme: 'markdown' +};