Optimise components (#39)

* ui-react-app -> ui-app

* CopyButton, Input & Password components

* Convert all to use ui-app/{Input, Label}

* Change to PureComponent where we have onChange listeners

* Cleanup layouts

* Swap dropdown components, remove uselexx constructors

* Allow address/account forget

* Bump

* Searching with headers operational

* flex grow/shrink for icon + account add

* Fix balance & transaction display (account/address management)

* Adjust balance layout

* Equalise padding
This commit is contained in:
Jaco Greeff 2018-05-11 18:08:10 +02:00 committed by GitHub
parent da4807b63c
commit b11a6981a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
116 changed files with 1552 additions and 1041 deletions

View File

@ -14,4 +14,4 @@ all=warn
include_warnings=true
module.name_mapper='^@polkadot/app-\(accounts\|addresses\|explorer\|extrinsics\|home\|rpc\|settings\|storage\|vanitygen\)\(.*\)$' -> '<PROJECT_ROOT>/packages/app-\1/src\2'
module.name_mapper='^@polkadot/apps\(.*\)$' -> '<PROJECT_ROOT>/packages/apps/src\1'
module.name_mapper='^@polkadot/ui-\(keyring\|react-app\)\(.*\)$' -> '<PROJECT_ROOT>/packages/ui-\1/src\2'
module.name_mapper='^@polkadot/ui-\(app\|keyring\)\(.*\)$' -> '<PROJECT_ROOT>/packages/ui-\1/src\2'

View File

@ -1,5 +1,5 @@
// @flow
declare module 'react-copy-to-clipboard' {
declare module.exports: React$ComponentType<{ text: string }>;
declare module.exports: React$ComponentType<{ text?: string }>;
}

1
flow-typed/store.js vendored
View File

@ -4,6 +4,7 @@ declare module 'store' {
declare module.exports: {
each: (fn: (value: any, key: string) => void) => void,
get: (key: string) => any,
remove: (key: string) => any,
set: (key: string, value: any) => void,
}
}

View File

@ -10,5 +10,5 @@
"packages": [
"packages/*"
],
"version": "0.3.4"
"version": "0.4.0"
}

View File

@ -1,6 +1,6 @@
{
"name": "@polkadot/app-accounts",
"version": "0.3.4",
"version": "0.4.0",
"main": "index.js",
"repository": "https://github.com/polkadot-js/apps.git",
"author": "Jaco Greeff <jacogr@gmail.com>",
@ -11,6 +11,6 @@
"test": "jest --coverage"
},
"devDependencies": {
"@polkadot/ui-react-app": "^0.3.4"
"@polkadot/ui-app": "^0.4.0"
}
}

View File

@ -3,63 +3,97 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from '@polkadot/ui-react-app/types';
import type { BareProps } from '@polkadot/ui-app/types';
import React from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import CopyButton from '@polkadot/ui-app/src/CopyButton';
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
import Balance from '@polkadot/ui-react-rx/Balance';
import Nonce from '@polkadot/ui-react-rx/Nonce';
import addressDecode from '@polkadot/util-keyring/address/decode';
type Props = BareProps & {
value: null | {
address: string,
publicKey: Uint8Array
};
value: string | null;
}
export default function Address ({ className, style, value }: Props): React$Node {
if (!value) {
return null;
type State = {
address: string,
isValid: boolean,
publicKey: Uint8Array | null,
shortValue: string
}
const DEFAULT_ADDR = '5'.padEnd(16, 'x');
const DEFAULT_SHORT = `${DEFAULT_ADDR.slice(0, 7)}…${DEFAULT_ADDR.slice(-7)}`;
export default class Address extends React.PureComponent<Props, State> {
state: State = ({}: $Shape<State>);
static getDerivedStateFromProps ({ value }: Props, { publicKey, shortValue }: State): State {
// flowlint-next-line sketchy-null-string:off
const address = value || DEFAULT_ADDR;
try {
// $FlowFixMe yes, we expect a throw when invalid
publicKey = addressDecode(value);
shortValue = `${address.slice(0, 7)}…${address.slice(-7)}`;
} catch (error) {
publicKey = null;
}
const isValid = !!publicKey && publicKey.length === 32;
console.log('publicKey', publicKey);
return {
address: isValid ? address : DEFAULT_ADDR,
isValid,
publicKey,
shortValue: isValid ? shortValue : DEFAULT_SHORT
};
}
const { address, publicKey } = value;
const short = `${address.slice(0, 7)}…${address.slice(-7)}`;
render (): React$Node {
const { className, style } = this.props;
const { address, isValid, publicKey, shortValue } = this.state;
return (
<div
className={['accounts--Address', className].join(' ')}
style={style}
>
<IdentityIcon
className='accounts--Address-icon'
size={96}
value={address}
/>
<div className='accounts--Address-data'>
<div className='accounts--Address-address'>
{short}
</div>
<CopyToClipboard text={address}>
<Button
icon='copy'
primary
size='tiny'
/>
</CopyToClipboard>
</div>
<Balance
className='accounts--Address-balance'
label='balance '
value={publicKey}
/>
<Nonce
className='accounts--Address-nonce'
value={publicKey}
return (
<div
className={['accounts--Address', isValid ? '' : 'invalid', className].join(' ')}
style={style}
>
{' transactions'}
</Nonce>
</div>
);
<IdentityIcon
className='accounts--Address-icon'
size={96}
value={address}
/>
<div className='accounts--Address-data'>
<div className='accounts--Address-address'>
{shortValue}
</div>
<CopyButton value={address} />
</div>
{
!isValid
? null
: [
<Balance
className='accounts--Address-balance'
key='balance'
label='balance '
params={publicKey}
/>,
<Nonce
className='accounts--Address-nonce'
key='nonce'
params={publicKey}
>
{' transactions'}
</Nonce>
]
}
</div>
);
}
}

View File

@ -3,12 +3,13 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import Input from 'semantic-ui-react/dist/es/elements/Input';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import Input from '@polkadot/ui-app/src/Input';
import Password from '@polkadot/ui-app/src/Password';
import keyring from '@polkadot/ui-keyring/src';
import isHex from '@polkadot/util/is/hex';
import hexToU8a from '@polkadot/util/hex/toU8a';
@ -26,13 +27,11 @@ type Props = I18nProps & {
};
type State = {
address: string | null,
publicKey: Uint8Array | null,
address: string,
fieldName: string,
isNameValid: boolean,
isSeedValid: boolean,
isPassValid: boolean,
isPassVisible: boolean,
isValid: boolean,
name: string,
password: string,
@ -45,14 +44,12 @@ function formatSeed (seed: string): Uint8Array {
: u8aFromString(seed.padEnd(32, ' '));
}
function infoFromSeed (seed: string): { address: string, publicKey: Uint8Array } {
const { publicKey } = keypairFromSeed(formatSeed(seed));
const address = addressEncode(publicKey);
return {
address,
publicKey
};
function addressFromSeed (seed: string): string {
return addressEncode(
keypairFromSeed(
formatSeed(seed)
).publicKey
);
}
class Creator extends React.PureComponent<Props, State> {
@ -66,7 +63,7 @@ class Creator extends React.PureComponent<Props, State> {
render (): React$Node {
const { className, style, t } = this.props;
const { address, fieldName, isNameValid, isPassValid, isPassVisible, isSeedValid, isValid, name, password, publicKey, seed } = this.state;
const { address, fieldName, isNameValid, isPassValid, isSeedValid, isValid, name, password, seed } = this.state;
return (
<div
@ -74,68 +71,52 @@ class Creator extends React.PureComponent<Props, State> {
style={style}
>
<div className='ui--grid'>
<div className='medium'>
<div className='ui--row'>
<div className='full'>
<Label>{t('creator.seed', {
defaultValue: 'create from the following seed (hex or string)'
})}</Label>
<Input
error={!isSeedValid}
name={`${fieldName}_seed`}
onChange={this.onChangeSeed}
value={seed}
/>
</div>
</div>
<div className='ui--row'>
<div className='full'>
<Label>{t('creator.name', {
defaultValue: 'name the account'
})}</Label>
<Input
error={!isNameValid}
name={`${fieldName}_name`}
onChange={this.onChangeName}
value={name}
/>
</div>
</div>
<div className='ui--row'>
<div className='full'>
<Label>{t('creator.pass1', {
defaultValue: 'encrypt it using the password'
})}</Label>
<Input
action
error={!isPassValid}
name={`${fieldName}_pass`}
onChange={this.onChangePass}
type={isPassVisible ? 'text' : 'password'}
value={password}
>
<input />
<Button
icon='eye'
primary
onClick={this.togglePassword}
/>
</Input>
</div>
</div>
</div>
<Address
className='medium'
className='shrink'
value={
// flowlint-next-line sketchy-null-string:off
!address || !publicKey
? null
: {
address,
publicKey
}
isSeedValid
? address
: null
}
/>
<div className='grow'>
<div className='ui--row'>
<Input
className='full'
isError={!isSeedValid}
label={t('creator.seed', {
defaultValue: 'create from the following seed (hex or string)'
})}
name={`${fieldName}_seed`}
onChange={this.onChangeSeed}
value={seed}
/>
</div>
<div className='ui--row'>
<Input
className='full'
isError={!isNameValid}
label={t('creator.name', {
defaultValue: 'name the account'
})}
name={`${fieldName}_name`}
onChange={this.onChangeName}
value={name}
/>
</div>
<div className='ui--row'>
<Password
className='full'
isError={!isPassValid}
label={t('creator.password', {
defaultValue: 'encrypt it using the password'
})}
name={`${fieldName}_pass`}
onChange={this.onChangePass}
value={password}
/>
</div>
</div>
</div>
<div className='ui--row-buttons'>
<Button
@ -161,19 +142,17 @@ class Creator extends React.PureComponent<Props, State> {
emptyState (): State {
const seed = u8aToHex(randomBytes());
const { address, publicKey } = infoFromSeed(seed);
const address = addressFromSeed(seed);
return {
address,
fieldName: `field_${Date.now()}`,
isNameValid: true,
isPassValid: false,
isPassVisible: false,
isSeedValid: true,
isValid: false,
name: 'new keypair',
password: '',
publicKey,
seed
};
}
@ -183,7 +162,6 @@ class Creator extends React.PureComponent<Props, State> {
(prevState: State, props: Props): $Shape<State> => {
const { name = prevState.name, password = prevState.password, seed = prevState.seed } = newState;
let address = prevState.address;
let publicKey = prevState.publicKey;
const isNameValid = !!name;
const isSeedValid = isHex(seed)
? seed.length === 66
@ -191,10 +169,7 @@ class Creator extends React.PureComponent<Props, State> {
const isPassValid = password.length > 0 && password.length <= 32;
if (isSeedValid && seed !== prevState.seed) {
const info = infoFromSeed(seed);
address = info.address;
publicKey = info.publicKey;
address = addressFromSeed(seed);
}
return {
@ -205,25 +180,22 @@ class Creator extends React.PureComponent<Props, State> {
isValid: isNameValid && isPassValid && isSeedValid,
name,
password,
publicKey,
seed
};
}
);
}
// eslint-disable-next-line no-unused-vars
onChangeSeed = (event: SyntheticEvent<*>, { value }): void => {
this.nextState({ seed: value });
onChangeSeed = (seed: string): void => {
this.nextState({ seed });
}
// eslint-disable-next-line no-unused-vars
onChangeName = (event: SyntheticEvent<*>, { value }): void => {
this.nextState({ name: value });
onChangeName = (name: string): void => {
this.nextState({ name });
}
onChangePass = (event: SyntheticEvent<*>, { value }): void => {
this.nextState({ password: value });
onChangePass = (password: string): void => {
this.nextState({ password });
}
onCommit = (): void => {
@ -240,10 +212,6 @@ class Creator extends React.PureComponent<Props, State> {
onDiscard = (): void => {
this.setState(this.emptyState());
}
togglePassword = (): void => {
this.setState({ isPassVisible: !this.state.isPassVisible });
}
}
export default translate(Creator);

View File

@ -4,14 +4,14 @@
// @flow
import type { KeyringPair } from '@polkadot/util-keyring/types';
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import Input from 'semantic-ui-react/dist/es/elements/Input';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import Input from '@polkadot/ui-app/src/Input';
import InputAddress from '@polkadot/ui-app/src/InputAddress';
import keyring from '@polkadot/ui-keyring/src';
import InputAddress from '@polkadot/ui-react-app/src/InputAddress';
import Address from './Address';
import translate from './translate';
@ -21,8 +21,8 @@ type Props = I18nProps & {
};
type State = {
currentPair: KeyringPair,
defaultPublicKey: Uint8Array,
currentPair: KeyringPair | null,
defaultPublicKey?: Uint8Array,
editedName: string,
isEdited: boolean
}
@ -34,55 +34,69 @@ class Editor extends React.PureComponent<Props, State> {
super(props);
const pairs = keyring.getPairs();
const currentPair = pairs[pairs.length - 1];
const currentPair = pairs[pairs.length - 1] || null;
this.state = this.createState(currentPair);
this.state.defaultPublicKey = currentPair.publicKey();
this.state.defaultPublicKey = currentPair
? currentPair.publicKey()
: void 0;
}
render (): React$Node {
const { className, style, t } = this.props;
const { defaultPublicKey, currentPair, editedName, isEdited } = this.state;
if (!currentPair) {
return (
<div
className={['accounts--Editor', className].join(' ')}
style={style}
>
{t('editor.none', {
defaultValue: 'There are no saved accounts. Add some first.'
})}
</div>
);
}
const address = currentPair.address();
return (
<div
className={['accounts--Editor', className].join(' ')}
style={style}
>
<div className='ui--grid'>
<div className='medium'>
<Address
className='shrink'
value={address}
/>
<div className='grow'>
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
hideAddress
isInput={false}
isSUIInput={false}
label={t('editor.select', {
defaultValue: 'using my account'
})}
onChange={this.onChangeAccount}
type='account'
value={address}
/>
</div>
<div className='ui--row'>
<div className='full'>
<Label>{t('editor.name', {
<Input
className='full'
label={t('editor.name', {
defaultValue: 'identified by the name'
})}</Label>
<Input
onChange={this.onChangeName}
value={editedName}
/>
</div>
})}
onChange={this.onChangeName}
value={editedName}
/>
</div>
</div>
<Address
className='medium'
value={{
address: currentPair.address(),
publicKey: currentPair.publicKey()
}}
/>
</div>
<div className='ui--row-buttons'>
<Button
@ -102,17 +116,26 @@ class Editor extends React.PureComponent<Props, State> {
defaultValue: 'Save'
})}
</Button>
<Button
negative
onClick={this.onForget}
primary
>
{t('editor.forget', {
defaultValue: 'Forget'
})}
</Button>
</div>
</div>
);
}
createState (currentPair: KeyringPair): $Shape<State> {
const { name = '' } = currentPair.getMeta();
createState (currentPair: KeyringPair | null): $Shape<State> {
return {
currentPair,
editedName: name,
editedName: currentPair
? currentPair.getMeta().name || ''
: '',
isEdited: false
};
}
@ -121,13 +144,19 @@ class Editor extends React.PureComponent<Props, State> {
this.setState(
(prevState: State): $Shape<State> => {
let { currentPair = prevState.currentPair, editedName = prevState.editedName } = newState;
const previous = prevState.currentPair || { address: () => null };
let isEdited = false;
if (currentPair.address() !== prevState.currentPair.address()) {
editedName = currentPair.getMeta().name || '';
if (currentPair) {
if (currentPair.address() !== previous.address()) {
editedName = currentPair.getMeta().name || '';
} else if (editedName !== currentPair.getMeta().name) {
isEdited = true;
}
} else {
editedName = '';
}
const isEdited = editedName !== currentPair.getMeta().name;
return {
currentPair,
editedName,
@ -145,14 +174,17 @@ class Editor extends React.PureComponent<Props, State> {
});
}
// eslint-disable-next-line no-unused-vars
onChangeName = (event: SyntheticEvent<*>, { value }): void => {
this.nextState({ editedName: value });
onChangeName = (editedName: string): void => {
this.nextState({ editedName });
}
onCommit = (): void => {
const { currentPair, editedName } = this.state;
if (!currentPair) {
return;
}
keyring.saveAccountMeta(currentPair, {
name: editedName,
whenEdited: Date.now()
@ -164,10 +196,38 @@ class Editor extends React.PureComponent<Props, State> {
onDiscard = (): void => {
const { currentPair } = this.state;
if (!currentPair) {
return;
}
this.nextState({
editedName: currentPair.getMeta().name
});
}
onForget = (): void => {
const { currentPair } = this.state;
if (!currentPair) {
return;
}
const address = currentPair.address();
const pairs = keyring.getPairs().filter((item) =>
item.address() !== address
);
const nextPair = pairs[pairs.length - 1] || null;
const defaultPublicKey = nextPair
? nextPair.publicKey()
: void 0;
keyring.forgetAccount(address);
this.nextState({
currentPair: nextPair,
defaultPublicKey
});
}
}
export default translate(Editor);

View File

@ -15,6 +15,15 @@
text-align: center;
}
.accounts--Address .rx--updated {
background: transparent !important;
}
.accounts--Address.invalid {
filter: grayscale(100);
opacity: 0.45;
}
.accounts--Address-icon {
display: block
}
@ -24,7 +33,14 @@
opacity: 0.45;
}
.accounts--Address-data {}
.accounts--Address-balance.rx--updated,
.accounts--Address-nonce.rx--updated {
opacity: 1;
}
.accounts--Address-data {
padding: 0.5em 2em;
}
.accounts--Address-data * {
margin: 0 0.25rem !important;

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import './index.css';
@ -19,8 +19,7 @@ type Props = I18nProps & {};
type Actions = 'create' | 'edit';
type State = {
currentAction: Actions,
currentAddress: string | null
action: Actions
}
// FIXME React-router would probably be the best route, not home-grown
@ -30,19 +29,12 @@ const Components: { [Actions]: React$ComponentType<*> } = {
};
class App extends React.PureComponent<Props, State> {
constructor (props: Props) {
super(props);
this.state = {
currentAction: 'edit',
currentAddress: null
};
}
state: State = { action: 'edit' };
render (): React$Node {
const { className, style, t } = this.props;
const { currentAction } = this.state;
const Component = Components[currentAction];
const { action } = this.state;
const Component = Components[action];
return (
<div
@ -53,7 +45,7 @@ class App extends React.PureComponent<Props, State> {
<Button.Group>
<Button
onClick={this.selectEdit}
primary={currentAction === 'edit'}
primary={action === 'edit'}
>
{t('app.edit', {
defaultValue: 'Edit account'
@ -66,7 +58,7 @@ class App extends React.PureComponent<Props, State> {
/>
<Button
onClick={this.selectCreate}
primary={currentAction === 'create'}
primary={action === 'create'}
>
{t('app.create', {
defaultValue: 'Create account'
@ -79,16 +71,12 @@ class App extends React.PureComponent<Props, State> {
);
}
setCurrentAction (currentAction: Actions): void {
this.setState({ currentAction });
}
selectCreate = (): void => {
this.setCurrentAction('create');
this.setState({ action: 'create' });
}
selectEdit = (): void => {
this.setCurrentAction('edit');
this.setState({ action: 'edit' });
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@polkadot/app-addresses",
"version": "0.3.4",
"version": "0.4.0",
"main": "index.js",
"repository": "https://github.com/polkadot-js/apps.git",
"author": "Jaco Greeff <jacogr@gmail.com>",
@ -11,7 +11,7 @@
"test": "jest --coverage"
},
"devDependencies": {
"@polkadot/app-accounts": "^0.3.4",
"@polkadot/ui-react-app": "^0.3.4"
"@polkadot/app-accounts": "^0.4.0",
"@polkadot/ui-app": "^0.4.0"
}
}

View File

@ -3,12 +3,12 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import Input from 'semantic-ui-react/dist/es/elements/Input';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import Input from '@polkadot/ui-app/src/Input';
import keyring from '@polkadot/ui-keyring/src';
import addressDecode from '@polkadot/util-keyring/address/decode';
import addressEncode from '@polkadot/util-keyring/address/encode';
@ -21,8 +21,7 @@ type Props = I18nProps & {
};
type State = {
address: string | null,
publicKey: Uint8Array | null,
address: string,
fieldName: string,
isAddressValid: boolean,
isNameValid: boolean,
@ -41,7 +40,7 @@ class Creator extends React.PureComponent<Props, State> {
render (): React$Node {
const { className, style, t } = this.props;
const { address, fieldName, isAddressValid, isNameValid, isValid, name, publicKey } = this.state;
const { address, fieldName, isAddressValid, isNameValid, isValid, name } = this.state;
return (
<div
@ -49,46 +48,36 @@ class Creator extends React.PureComponent<Props, State> {
style={style}
>
<div className='ui--grid'>
<div className='medium'>
<Address
className='shrink'
value={address}
/>
<div className='grow'>
<div className='ui--row'>
<div className='full'>
<Label>{t('creator.address', {
<Input
className='full'
isError={!isAddressValid}
label={t('creator.address', {
defaultValue: 'add the following address'
})}</Label>
<Input
error={!isAddressValid}
name={`${fieldName}_address`}
onChange={this.onChangeAddress}
value={address}
/>
</div>
})}
name={`${fieldName}_address`}
onChange={this.onChangeAddress}
value={address}
/>
</div>
<div className='ui--row'>
<div className='full'>
<Label>{t('creator.name', {
<Input
className='full'
isError={!isNameValid}
label={t('creator.name', {
defaultValue: 'name the entry'
})}</Label>
<Input
error={!isNameValid}
name={`${fieldName}_name`}
onChange={this.onChangeName}
value={name}
/>
</div>
})}
name={`${fieldName}_name`}
onChange={this.onChangeName}
value={name}
/>
</div>
</div>
<Address
className='medium'
value={
// flowlint-next-line sketchy-null-string:off
!address || !publicKey
? null
: {
address,
publicKey
}
}
/>
</div>
<div className='ui--row-buttons'>
<Button
@ -119,8 +108,7 @@ class Creator extends React.PureComponent<Props, State> {
isAddressValid: false,
isNameValid: true,
isValid: false,
name: 'new address',
publicKey: null
name: 'new address'
};
}
@ -129,51 +117,42 @@ class Creator extends React.PureComponent<Props, State> {
(prevState: State, props: Props): $Shape<State> => {
const { address = prevState.address, name = prevState.name } = newState;
let nextAddress;
let publicKey;
let isAddressValid = true;
try {
// $FlowFixMe we do expect failures... sometimes
publicKey = addressDecode(address);
nextAddress = addressEncode(publicKey);
addressEncode(
addressDecode(address)
);
} catch (error) {
nextAddress = void 0;
publicKey = void 0;
isAddressValid = false;
}
const isNameValid = !!name;
const isAddressValid = !!nextAddress;
return {
address: nextAddress,
address,
isAddressValid,
isNameValid,
isValid: isAddressValid && isNameValid,
name,
publicKey
name
};
}
);
}
// eslint-disable-next-line no-unused-vars
onChangeAddress = (event: SyntheticEvent<*>, { value }): void => {
this.nextState({ address: value });
onChangeAddress = (address: string): void => {
this.nextState({ address });
}
// eslint-disable-next-line no-unused-vars
onChangeName = (event: SyntheticEvent<*>, { value }): void => {
this.nextState({ name: value });
onChangeName = (name: string): void => {
this.nextState({ name });
}
onCommit = (): void => {
const { onBack } = this.props;
const { address, name } = this.state;
// flowlint-next-line sketchy-null-string:off
if (address) {
keyring.saveAddress(address, { name });
}
keyring.saveAddress(address, { name });
onBack();
}

View File

@ -4,14 +4,14 @@
// @flow
import type { KeyringAddress } from '@polkadot/ui-keyring/types';
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import Input from 'semantic-ui-react/dist/es/elements/Input';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import Input from '@polkadot/ui-app/src/Input';
import InputAddress from '@polkadot/ui-app/src/InputAddress';
import keyring from '@polkadot/ui-keyring/src';
import InputAddress from '@polkadot/ui-react-app/src/InputAddress';
import Address from '@polkadot/app-accounts/src/Address';
import translate from './translate';
@ -21,7 +21,7 @@ type Props = I18nProps & {
};
type State = {
currentAddress?: KeyringAddress,
currentAddress: KeyringAddress | null,
defaultPublicKey?: Uint8Array,
editedName: string,
isEdited: boolean,
@ -35,7 +35,7 @@ class Editor extends React.PureComponent<Props, State> {
super(props);
const addresses = keyring.getAddresses();
const currentAddress = addresses[addresses.length - 1];
const currentAddress = addresses[addresses.length - 1] || null;
this.state = this.createState(currentAddress);
this.state.defaultPublicKey = currentAddress
@ -60,13 +60,19 @@ class Editor extends React.PureComponent<Props, State> {
);
}
const address = currentAddress.address();
return (
<div
className={['addresses--Editor', className].join(' ')}
style={style}
>
<div className='ui--grid'>
<div className='medium'>
<Address
className='shrink'
value={address}
/>
<div className='grow'>
<div className='ui--row'>
<InputAddress
className='full'
@ -78,27 +84,20 @@ class Editor extends React.PureComponent<Props, State> {
})}
onChange={this.onChangeAddress}
type='address'
value={address}
/>
</div>
<div className='ui--row'>
<div className='full'>
<Label>{t('editor.name', {
<Input
className='full'
label={t('editor.name', {
defaultValue: 'identified by the name'
})}</Label>
<Input
onChange={this.onChangeName}
value={editedName}
/>
</div>
})}
onChange={this.onChangeName}
value={editedName}
/>
</div>
</div>
<Address
className='medium'
value={{
address: currentAddress.address(),
publicKey: currentAddress.publicKey()
}}
/>
</div>
<div className='ui--row-buttons'>
<Button
@ -118,12 +117,21 @@ class Editor extends React.PureComponent<Props, State> {
defaultValue: 'Save'
})}
</Button>
<Button
negative
onClick={this.onForget}
primary
>
{t('editor.forget', {
defaultValue: 'Forget'
})}
</Button>
</div>
</div>
);
}
createState (currentAddress?: KeyringAddress): $Shape<State> {
createState (currentAddress: KeyringAddress | null): $Shape<State> {
const { name = '' } = currentAddress
? currentAddress.getMeta()
: {};
@ -140,13 +148,18 @@ class Editor extends React.PureComponent<Props, State> {
(prevState: State): $Shape<State> => {
let { currentAddress = prevState.currentAddress, editedName = prevState.editedName } = newState;
const previous = prevState.currentAddress || { address: () => null };
let isEdited = false;
if (currentAddress && currentAddress.address() !== previous.address()) {
editedName = currentAddress.getMeta().name || '';
if (currentAddress && currentAddress.isValid()) {
if (currentAddress.address() !== previous.address()) {
editedName = currentAddress.getMeta().name || '';
} else if (editedName !== currentAddress.getMeta().name) {
isEdited = true;
}
} else {
editedName = '';
}
const isEdited = !!currentAddress && editedName !== currentAddress.getMeta().name;
return {
currentAddress,
editedName,
@ -157,16 +170,15 @@ class Editor extends React.PureComponent<Props, State> {
}
onChangeAddress = (publicKey: Uint8Array): void => {
const currentAddress = keyring.getAddress(publicKey);
const currentAddress = keyring.getAddress(publicKey) || null;
this.nextState({
currentAddress
});
}
// eslint-disable-next-line no-unused-vars
onChangeName = (event: SyntheticEvent<*>, { value }): void => {
this.nextState({ editedName: value });
onChangeName = (editedName: string): void => {
this.nextState({ editedName });
}
onCommit = (): void => {
@ -195,6 +207,30 @@ class Editor extends React.PureComponent<Props, State> {
editedName: currentAddress.getMeta().name
});
}
onForget = (): void => {
const { currentAddress } = this.state;
if (!currentAddress) {
return;
}
const address = currentAddress.address();
const addresses = keyring.getAddresses().filter((item) =>
item.address() !== address
);
const nextAddress = addresses[0] || null;
const defaultPublicKey = nextAddress
? nextAddress.publicKey()
: void 0;
keyring.forgetAddress(address);
this.nextState({
currentAddress: nextAddress,
defaultPublicKey
});
}
}
export default translate(Editor);

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import './index.css';
@ -19,8 +19,7 @@ type Props = I18nProps & {};
type Actions = 'create' | 'edit';
type State = {
currentAction: Actions,
currentAddress: string | null
action: Actions
}
// FIXME React-router would probably be the best route, not home-grown
@ -30,19 +29,12 @@ const Components: { [Actions]: React$ComponentType<*> } = {
};
class App extends React.PureComponent<Props, State> {
constructor (props: Props) {
super(props);
this.state = {
currentAction: 'edit',
currentAddress: null
};
}
state: State = { action: 'edit' };
render (): React$Node {
const { className, style, t } = this.props;
const { currentAction } = this.state;
const Component = Components[currentAction];
const { action } = this.state;
const Component = Components[action];
return (
<div
@ -53,7 +45,7 @@ class App extends React.PureComponent<Props, State> {
<Button.Group>
<Button
onClick={this.selectEdit}
primary={currentAction === 'edit'}
primary={action === 'edit'}
>
{t('app.edit', {
defaultValue: 'Edit address'
@ -66,7 +58,7 @@ class App extends React.PureComponent<Props, State> {
/>
<Button
onClick={this.selectCreate}
primary={currentAction === 'create'}
primary={action === 'create'}
>
{t('app.create', {
defaultValue: 'Add address'
@ -79,16 +71,12 @@ class App extends React.PureComponent<Props, State> {
);
}
setCurrentAction (currentAction: Actions): void {
this.setState({ currentAction });
}
selectCreate = (): void => {
this.setCurrentAction('create');
this.setState({ action: 'create' });
}
selectEdit = (): void => {
this.setCurrentAction('edit');
this.setState({ action: 'edit' });
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@polkadot/app-explorer",
"version": "0.3.4",
"version": "0.4.0",
"main": "index.js",
"repository": "https://github.com/polkadot-js/apps.git",
"author": "Jaco Greeff <jacogr@gmail.com>",
@ -14,7 +14,7 @@
"@polkadot/primitives": "^0.12.9",
"@polkadot/primitives-codec": "^0.12.9",
"@polkadot/primitives-json": "^0.12.9",
"@polkadot/ui-react-app": "^0.3.4",
"@polkadot/ui-app": "^0.4.0",
"@polkadot/util-crypto": "^0.19.6"
}
}

View File

@ -4,11 +4,12 @@
// @flow
import type { Header } from '@polkadot/primitives/header';
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import './BlockHeader.css';
import React from 'react';
import headerHash from '@polkadot/primitives-codec/header/hash';
import u8aToHex from '@polkadot/util/u8a/toHex';

View File

@ -6,6 +6,7 @@
import type { Header } from '@polkadot/primitives/header';
import React from 'react';
import withApiDiv from '@polkadot/ui-react-rx/with/apiDiv';
import BlockHeader from './BlockHeader';

View File

@ -3,11 +3,12 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import './index.css';
import React from 'react';
import BestNumber from '@polkadot/ui-react-rx/BestNumber';
import BestHash from './BestHash';

View File

@ -1,6 +1,6 @@
{
"name": "@polkadot/app-extrinsics",
"version": "0.3.4",
"version": "0.4.0",
"main": "index.js",
"repository": "https://github.com/polkadot-js/apps.git",
"author": "Jaco Greeff <jacogr@gmail.com>",
@ -13,7 +13,7 @@
"devDependencies": {
"@polkadot/extrinsics": "^0.12.9",
"@polkadot/extrinsics-codec": "^0.12.9",
"@polkadot/ui-react-app": "^0.3.4",
"@polkadot/ui-app": "^0.4.0",
"react-dropzone": "^4.2.9"
}
}

View File

@ -4,12 +4,13 @@
// @flow
import type { KeyringOption$Type } from '@polkadot/ui-keyring/types';
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import React from 'react';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import InputAddress from '@polkadot/ui-app/src/InputAddress';
import Labelled from '@polkadot/ui-app/src/Labelled';
import Balance from '@polkadot/ui-react-rx/Balance';
import InputAddress from '@polkadot/ui-react-app/src/InputAddress';
import translate from './translate';
@ -57,17 +58,17 @@ class Account extends React.PureComponent<Props, State> {
type={type}
/>
</div>
<div className='small'>
<Label>
{t('account.balance', {
defaultValue: 'with an available balance of'
})}
</Label>
<Labelled
className='small'
label={t('account.balance', {
defaultValue: 'with an available balance of'
})}
>
<Balance
className='ui disabled dropdown selection'
params={publicKey}
/>
</div>
</Labelled>
</div>
);
}

View File

@ -4,14 +4,15 @@
// @flow
import type { Extrinsic } from '@polkadot/extrinsics/types';
import type { BareProps } from '@polkadot/ui-react-app/types';
import type { RawParam } from '@polkadot/ui-react-app/Params/types';
import type { BareProps } from '@polkadot/ui-app/types';
import type { RawParam } from '@polkadot/ui-app/Params/types';
import type { EncodedMessage } from './types';
import React from 'react';
import encode from '@polkadot/extrinsics-codec/encode/extrinsic';
import InputExtrinsic from '@polkadot/ui-react-app/src/InputExtrinsic';
import Params from '@polkadot/ui-react-app/src/Params';
import InputExtrinsic from '@polkadot/ui-app/src/InputExtrinsic';
import Params from '@polkadot/ui-app/src/Params';
import isUndefined from '@polkadot/util/is/undefined';
import paramComponents from './Params';

View File

@ -4,10 +4,11 @@
// @flow
import type BN from 'bn.js';
import type { BareProps } from '@polkadot/ui-react-app/types';
import type { BareProps } from '@polkadot/ui-app/types';
import React from 'react';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import Labelled from '@polkadot/ui-app/src/Labelled';
import RxNonce from '@polkadot/ui-react-rx/Nonce';
type Props = BareProps & {
@ -22,14 +23,16 @@ export default function Nonce ({ className, label, style, onChange, value }: Pro
className={['ui--row', className].join(' ')}
style={style}
>
<div className='small'>
<Label>{label}</Label>
<Labelled
className='small'
label={label}
>
<RxNonce
className='ui disabled dropdown selection'
onChange={onChange}
params={value}
/>
</div>
</Labelled>
</div>
);
}

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Props as BaseProps } from '@polkadot/ui-react-app/Params/types';
import type { Props as BaseProps } from '@polkadot/ui-app/Params/types';
import React from 'react';
@ -13,22 +13,29 @@ type Props = BaseProps & {
defaultValue?: Uint8Array
};
export default function Account ({ className, defaultValue, index, isError, onChange, label, style }: Props): React$Node {
const _onChange = (value?: Uint8Array): void =>
export default class Account extends React.PureComponent<Props> {
render (): React$Node {
const { className, defaultValue, isError, label, style } = this.props;
return (
<BaseAccount
className={className}
defaultValue={defaultValue}
isError={isError}
isInput
label={label}
onChange={this.onChange}
style={style}
/>
);
}
onChange = (value?: Uint8Array): void => {
const { index, onChange } = this.props;
onChange(index, {
isValid: !!value && value.length === 32,
value
});
return (
<BaseAccount
className={className}
defaultValue={defaultValue}
isError={isError}
isInput
label={label}
onChange={_onChange}
style={style}
/>
);
}
}

View File

@ -3,11 +3,12 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Props as BaseProps } from '@polkadot/ui-react-app/Params/types';
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { Props as BaseProps } from '@polkadot/ui-app/Params/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { EncodedMessage } from '../types';
import React from 'react';
import extrinsics from '@polkadot/extrinsics-substrate';
import Extrinsic from '../Extrinsic';
@ -17,35 +18,42 @@ type Props = BaseProps & I18nProps;
const defaultValue = extrinsics.consensus.methods.private.setCode;
function Proposal ({ className, index, isError, onChange, label, style, t }: Props): React$Node {
const _onChange = ({ isValid, value }: EncodedMessage): void =>
class Proposal extends React.PureComponent<Props> {
render (): React$Node {
const { className, isError, label, style, t } = this.props;
return (
<Extrinsic
className={className}
defaultValue={defaultValue}
isError={isError}
isPrivate
labelMethod={t('proposal.method', {
defaultValue: '{{label}} (extrinsic)',
replace: {
label
}
})}
labelSection={t('proposal.method', {
defaultValue: '{{label}} (section)',
replace: {
label
}
})}
onChange={this.onChange}
style={style}
/>
);
}
onChange = ({ isValid, value }: EncodedMessage): void => {
const { index, onChange } = this.props;
onChange(index, {
isValid,
value
});
return (
<Extrinsic
className={className}
defaultValue={defaultValue}
isError={isError}
isPrivate
labelMethod={t('proposal.method', {
defaultValue: '{{label}} (extrinsic)',
replace: {
label
}
})}
labelSection={t('proposal.method', {
defaultValue: '{{label}} (section)',
replace: {
label
}
})}
onChange={_onChange}
style={style}
/>
);
}
}
export default translate(Proposal);

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { ComponentMap } from '@polkadot/ui-react-app/Params/types';
import type { ComponentMap } from '@polkadot/ui-app/Params/types';
import Account from './Account';
import Proposal from './Proposal';

View File

@ -4,7 +4,7 @@
// @flow
import type BN from 'bn.js';
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { EncodedMessage, QueueTx } from './types';
import React from 'react';
@ -32,15 +32,9 @@ const defaultExtrinsic = extrinsics.staking.methods.public.transfer;
let id = 0;
class Selection extends React.PureComponent<Props, State> {
state: State;
constructor (props: Props) {
super(props);
this.state = ({
isValid: false
}: $Shape<State>);
}
state: State = ({
isValid: false
}: $Shape<State>);
render () {
const { className, style, t } = this.props;

View File

@ -3,12 +3,13 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { QueueTx } from '../types';
import BN from 'bn.js';
import React from 'react';
import { Trans } from 'react-i18next';
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
import u8aToHexShort from '@polkadot/util/u8a/toHexShort';
import addressEncode from '@polkadot/util-keyring/address/encode';

View File

@ -11,8 +11,16 @@
line-height: 1.5em;
}
.extrinsics--Signer-Decoded {
padding-top: 2rem;
}
.extrinsics--Signer-Unlock {
padding-top: 1.5em;
padding-top: 1.5rem;
}
.extrinsics--Signer-Content {
padding: 1.5rem 3rem !important;
}
.extrinsics--Signer-Content .expanded {

View File

@ -3,13 +3,12 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { KeyringPair } from '@polkadot/util-keyring/types';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import Input from 'semantic-ui-react/dist/es/elements/Input';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import Password from '@polkadot/ui-app/src/Password';
import keyring from '@polkadot/ui-keyring/src';
import translate from '../translate';
@ -26,20 +25,13 @@ type State = {
fieldName: string,
isError: boolean,
isLocked: boolean,
isVisible: boolean,
pair: KeyringPair
}
class Unlock extends React.PureComponent<Props, State> {
constructor (props: Props) {
super(props);
state: State = ({}: $Shape<State>);
this.state = ({
isVisible: false
}: $Shape<State>);
}
static getDerivedStateFromProps ({ error, password, value }: Props, { isVisible }: State): $Shape<State> {
static getDerivedStateFromProps ({ error, password, value }: Props): $Shape<State> {
const pair = keyring.getPair(value);
const isLocked = !pair.hasSecretKey();
@ -47,16 +39,13 @@ class Unlock extends React.PureComponent<Props, State> {
fieldName: `pass_${Date.now()}`,
isError: !!error,
isLocked,
isVisible: password.length === 0
? false
: isVisible,
pair
};
}
render (): React$Node {
const { className, password, style, t } = this.props;
const { fieldName, isError, isLocked, isVisible } = this.state;
const { className, onChange, password, style, t } = this.props;
const { fieldName, isError, isLocked } = this.state;
if (!isLocked) {
return null;
@ -67,42 +56,21 @@ class Unlock extends React.PureComponent<Props, State> {
className={['extrinsics--Signer-Unlock', className].join(' ')}
style={style}
>
<div className='expanded ui--row'>
<div className='medium'>
<Label>{t('unlock.password', {
<div className='ui--row'>
<Password
className='medium'
isError={isError}
label={t('unlock.password', {
defaultValue: 'unlock account using'
})}</Label>
<Input
action
error={isError}
name={fieldName}
onChange={this.onChange}
type={isVisible ? 'text' : 'password'}
value={password}
>
<input />
<Button
icon='eye'
primary
onClick={this.togglePassword}
/>
</Input>
</div>
})}
name={fieldName}
onChange={onChange}
value={password}
/>
</div>
</div>
);
}
// eslint-disable-next-line no-unused-vars
onChange = (event: SyntheticEvent<*>, { value }): void => {
this.props.onChange(value);
}
togglePassword = (): void => {
this.setState({
isVisible: !this.state.isVisible
});
}
}
export default translate(Unlock);

View File

@ -4,7 +4,7 @@
// @flow
import type { ApiProps } from '@polkadot/ui-react-rx/types';
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { QueueTx } from '../types';
import './Signer.css';
@ -12,6 +12,7 @@ import './Signer.css';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import Modal from 'semantic-ui-react/dist/es/modules/Modal';
import keyring from '@polkadot/ui-keyring/src';
import withApi from '@polkadot/ui-react-rx/with/api';
@ -37,14 +38,10 @@ type State = {
};
class Signer extends React.PureComponent<Props, State> {
constructor (props: Props) {
super(props);
this.state = {
password: '',
unlockError: null
};
}
state: State = {
password: '',
unlockError: null
};
static getDerivedStateFromProps ({ queue }: Props, { currentItem, password, unlockError }: State): $Shape<State> {
const nextItem = queue.find(({ status }) =>

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from '@polkadot/ui-react-app/types';
import type { BareProps } from '@polkadot/ui-app/types';
import type { QueueTx } from './types';
import React from 'react';

View File

@ -7,16 +7,16 @@
}
.extrinsics--Status {
bottom: 0;
display: inline-block;
position: fixed;
right: 0.125rem;
right: 0.25rem;
top: 0.125rem;
z-index: 1001;
}
.extrinsics--Status-Item {
background: #bcdaf0;
margin-bottom: 0.125rem;
margin: 0.125rem;
padding: 0.5rem 1rem;
text-align: center;
}

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from '@polkadot/ui-react-app/types';
import type { BareProps } from '@polkadot/ui-app/types';
import type { QueueTx, QueueTx$Status } from './types';
import './index.css';
@ -21,15 +21,9 @@ type State = {
};
export default class App extends React.PureComponent<Props, State> {
state: State;
constructor (props: Props) {
super(props);
this.state = {
queue: []
};
}
state: State = {
queue: []
};
render (): React$Node {
const { className, style } = this.props;

View File

@ -1,6 +1,6 @@
{
"name": "@polkadot/app-storage",
"version": "0.3.4",
"version": "0.4.0",
"main": "index.js",
"repository": "https://github.com/polkadot-js/apps.git",
"author": "Jaco Greeff <jacogr@gmail.com>",
@ -11,6 +11,6 @@
"test": "jest --coverage"
},
"devDependencies": {
"@polkadot/ui-react-app": "^0.3.4"
"@polkadot/ui-app": "^0.4.0"
}
}

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { StorageQuery } from './types';
import React from 'react';

View File

@ -6,6 +6,7 @@
import type { Param$Type, Param$TypeArray } from '@polkadot/primitives/param';
import React from 'react';
// import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
import addressEncode from '@polkadot/util-keyring/address/encode';
import u8aToBn from '@polkadot/util/u8a/toBn';

View File

@ -3,13 +3,14 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { StorageQuery } from '../types';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import typeToText from '@polkadot/ui-react-app/src/Params/typeToText';
import Labelled from '@polkadot/ui-app/src/Labelled';
import typeToText from '@polkadot/ui-app/src/Params/typeToText';
import withStorageDiv from '@polkadot/ui-react-rx/with/storageDiv';
import translate from '../translate';
@ -20,61 +21,82 @@ type Props = I18nProps & {
value: StorageQuery
};
type State = {
inputs: Array<React$Node>,
Component: React$ComponentType<*>;
};
const cache = [];
function Query ({ className, onRemove, style, value: { id, key, params } }: Props): React$Node {
const CachedQuery = (() => {
if (!cache[id]) {
const values = params.map(({ value }) => value);
class Query extends React.PureComponent<Props, State> {
state: State = ({}: $Shape<State>);
cache[id] = withStorageDiv(key, { params: values })(
(value) =>
format(key.type, value),
{ className: 'ui disabled dropdown selection' }
);
}
static getDerivedStateFromProps ({ value: { id, key, params } }: Props, prevState: State): State | null {
const Component = ((): React$ComponentType<*> => {
if (!cache[id]) {
const values = params.map(({ value }) => value);
return cache[id];
})();
cache[id] = withStorageDiv(key, { params: values })(
(value) =>
format(key.type, value),
{ className: 'ui disabled dropdown selection' }
);
}
return cache[id];
})();
const inputs = Object
.keys(key.params || {})
.map((name, index) => {
// $FlowFixMe key.params exists
const formatted = format(key.params[name].type, params[index].value, 12);
return (
<span key={`param_${name}_${index}`}>{name}={formatted}</span>
);
});
return {
Component,
inputs
};
}
render (): React$Node {
const { className, style, value: { key } } = this.props;
const { Component, inputs } = this.state;
return (
<div
className={['storage--Query', 'storage--actionrow', className].join(' ')}
style={style}
>
<Labelled
className='storage--actionrow-value'
label={
<div>{key.section}.{key.name}({inputs}): {typeToText(key.type)}</div>
}
>
<Component />
</Labelled>
<Labelled className='storage--actionrow-button'>
<Button
icon='close'
negative
onClick={this.onRemove}
/>
</Labelled>
</div>
);
}
onRemove = (): void => {
const { onRemove, value: { id } } = this.props;
const _onRemove = (): void => {
delete cache[id];
onRemove(id);
};
const inputs = Object
.keys(key.params || {})
.map((name, index) => {
// $FlowFixMe key.params exists
const formatted = format(key.params[name].type, params[index].value, 12);
return (
<span>{name}={formatted}</span>
);
});
return (
<div
className={['storage--Query', 'storage--actionrow', className].join(' ')}
style={style}
>
<div className='storage--actionrow-value'>
<Label>
{key.section}.{key.name}({inputs}): {typeToText(key.type)}
</Label>
<CachedQuery />
</div>
<div className='storage--actionrow-button'>
<Label>&nbsp;</Label>
<Button
icon='close'
negative
onClick={_onRemove}
/>
</div>
</div>
);
}
}
export default translate(Query);

View File

@ -4,17 +4,17 @@
// @flow
import type { StorageDef$Key } from '@polkadot/storage/types';
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { RawParams } from '@polkadot/ui-react-app/Params/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { RawParams } from '@polkadot/ui-app/Params/types';
import type { StorageQuery } from './types';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import Label from 'semantic-ui-react/dist/es/elements/Label';
// import encode from '@polkadot/extrinsics-codec/encode/params';
import storage from '@polkadot/storage-substrate/keys';
import InputStorage from '@polkadot/ui-react-app/src/InputStorage';
import Params from '@polkadot/ui-react-app/src/Params';
import InputStorage from '@polkadot/ui-app/src/InputStorage';
import Labelled from '@polkadot/ui-app/src/Labelled';
import Params from '@polkadot/ui-app/src/Params';
import isUndefined from '@polkadot/util/is/undefined';
import translate from './translate';
@ -33,13 +33,9 @@ const defaultValue = storage.timestamp.keys.current;
let id = -1;
class Selection extends React.PureComponent<Props, State> {
constructor (props: Props) {
super(props);
this.state = ({
key: defaultValue
}: $Shape<State>);
}
state: State = ({
key: defaultValue
}: $Shape<State>);
render (): React$Node {
const { className, style, t } = this.props;
@ -63,15 +59,14 @@ class Selection extends React.PureComponent<Props, State> {
onChange={this.onChangeParams}
/>
</div>
<div className='storage--actionrow-button'>
<Label>&nbsp;</Label>
<Labelled className='storage--actionrow-button'>
<Button
disabled={!isValid}
icon='plus'
onClick={this.onAdd}
primary
/>
</div>
</Labelled>
</div>
);
}

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { StorageQuery } from './types';
import './index.css';
@ -21,15 +21,9 @@ type State = {
}
class App extends React.PureComponent<Props, State> {
state: State;
constructor (props: Props) {
super(props);
this.state = {
queue: []
};
}
state: State = {
queue: []
};
render (): React$Node {
const { className, style } = this.props;

View File

@ -1,6 +1,6 @@
{
"name": "@polkadot/app-vanitygen",
"version": "0.3.4",
"version": "0.4.0",
"main": "index.js",
"repository": "https://github.com/polkadot-js/apps.git",
"author": "Jaco Greeff <jacogr@gmail.com>",
@ -11,9 +11,8 @@
"test": "jest --coverage"
},
"devDependencies": {
"@polkadot/ui-react-app": "^0.3.4",
"@polkadot/ui-app": "^0.4.0",
"chalk": "^2.4.1",
"react-copy-to-clipboard": "^5.0.1",
"yargs": "10.1.2"
},
"optionalDependencies": {

View File

@ -3,11 +3,12 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from '@polkadot/ui-react-app/types';
import type { BareProps } from '@polkadot/ui-app/types';
import React from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import CopyButton from '@polkadot/ui-app/src/CopyButton';
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
import u8aToHex from '@polkadot/util/u8a/toHex';
@ -19,46 +20,59 @@ type Props = BareProps & {
seed: Uint8Array;
}
export default function Match ({ address, className, count, offset, onRemove, seed, style }: Props): React$Node {
const _onRemove = (): void =>
onRemove(address);
const hexSeed = u8aToHex(seed);
type State = {
hexSeed: string
};
return (
<div
className={['vanity--Match', className].join(' ')}
style={style}
>
<div className='vanity--Match-item'>
<IdentityIcon
className='vanity--Match-icon'
size={48}
value={address}
/>
<div className='vanity--Match-data'>
<div className='vanity--Match-addr'>
<span className='no'>{address.slice(0, offset)}</span><span className='yes'>{address.slice(offset, count + offset)}</span><span className='no'>{address.slice(count + offset)}</span>
export default class Match extends React.PureComponent<Props, State> {
state: State = ({}: $Shape<State>);
static getDerivedStateFromProps ({ seed }: Props): State {
return {
hexSeed: u8aToHex(seed)
};
}
render (): React$Node {
const { address, className, count, offset, style } = this.props;
const { hexSeed } = this.state;
return (
<div
className={['vanity--Match', className].join(' ')}
style={style}
>
<div className='vanity--Match-item'>
<IdentityIcon
className='vanity--Match-icon'
size={48}
value={address}
/>
<div className='vanity--Match-data'>
<div className='vanity--Match-addr'>
<span className='no'>{address.slice(0, offset)}</span><span className='yes'>{address.slice(offset, count + offset)}</span><span className='no'>{address.slice(count + offset)}</span>
</div>
<div className='vanity--Match-seed'>
{hexSeed}
</div>
</div>
<div className='vanity--Match-seed'>
{hexSeed}
</div>
</div>
<div className='vanity--Match-buttons'>
<CopyToClipboard text={hexSeed}>
<div className='vanity--Match-buttons'>
<CopyButton value={hexSeed} />
<Button
icon='copy'
primary
icon='close'
negative
onClick={this.onRemove}
size='tiny'
/>
</CopyToClipboard>
<Button
icon='close'
negative
onClick={_onRemove}
size='tiny'
/>
</div>
</div>
</div>
</div>
);
);
}
onRemove = (): void => {
const { address, onRemove } = this.props;
onRemove(address);
}
}

View File

@ -3,16 +3,17 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { Generator$Matches, Generator$Result } from './generator/types';
import './index.css';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import Input from 'semantic-ui-react/dist/es/elements/Input';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import Dropdown from 'semantic-ui-react/dist/es/modules/Dropdown';
import Dropdown from '@polkadot/ui-app/src/Dropdown';
import Input from '@polkadot/ui-app/src/Input';
import Labelled from '@polkadot/ui-app/src/Labelled';
import Match from './Match';
import generator from './generator';
@ -41,25 +42,18 @@ const BOOL_OPTIONS = [
];
class App extends React.PureComponent<Props, State> {
results: Array<Generator$Result>;
state: State;
constructor (props: Props) {
super(props);
this.results = [];
this.state = {
elapsed: 0,
isMatchValid: true,
isRunning: false,
keyCount: 0,
keyTime: 0,
match: DEFAULT_MATCH,
matches: [],
startAt: 0,
withCase: true
};
}
results: Array<Generator$Result> = [];
state: State = {
elapsed: 0,
isMatchValid: true,
isRunning: false,
keyCount: 0,
keyTime: 0,
match: DEFAULT_MATCH,
matches: [],
startAt: 0,
withCase: true
};
render (): React$Node {
const { className, style, t } = this.props;
@ -71,38 +65,37 @@ class App extends React.PureComponent<Props, State> {
style={style}
>
<div className='ui--row'>
<div className='medium'>
<Label>{t('vanity.matching', {
<Input
className='medium'
isDisable={isRunning}
isError={!isMatchValid}
label={t('vanity.matching', {
defaultValue: 'generate address containing (? wildcard)'
})}</Label>
<Input
disabled={isRunning}
error={!isMatchValid}
onChange={this.onChangeMatch}
value={match}
/>
</div>
<div className='small'>
<Label>{t('vanity.case', {
})}
onChange={this.onChangeMatch}
value={match}
/>
<Dropdown
className='small'
label={t('vanity.case', {
defaultValue: 'case sensitive match'
})}</Label>
<Dropdown
selection
options={BOOL_OPTIONS}
onChange={this.onChangeCase}
value={withCase}
/>
</div>
<div className='small'>
<Label>{t('vanity.offset', {
})}
options={BOOL_OPTIONS}
onChange={this.onChangeCase}
value={withCase}
/>
<Labelled
className='small'
label={t('vanity.offset', {
defaultValue: 'exact offset'
})}</Label>
})}
>
<div className='ui dropdown selection disabled'>
{t('vanity.offset.off', {
defaultValue: 'No'
})}
</div>
</div>
</Labelled>
</div>
<div className='ui--row-buttons'>
<Button
@ -205,21 +198,17 @@ class App extends React.PureComponent<Props, State> {
}, 0);
}
// eslint-disable-next-line no-unused-vars
onChangeCase = (event: SyntheticEvent<*>, { value }): void => {
this.setState({
withCase: value
});
onChangeCase = (withCase: boolean): void => {
this.setState({ withCase });
}
// eslint-disable-next-line no-unused-vars
onChangeMatch = (event: SyntheticEvent<*>, { value }): void => {
onChangeMatch = (match: string): void => {
this.setState({
isMatchValid:
matchRegex.test(value) &&
(value.length !== 0) &&
(value.length < 31),
match: value
matchRegex.test(match) &&
(match.length !== 0) &&
(match.length < 31),
match
});
}

View File

@ -1,6 +1,6 @@
{
"name": "@polkadot/apps",
"version": "0.3.4",
"version": "0.4.0",
"description": "An Apps portal into the Polkadot network",
"main": "index.js",
"homepage": ".",
@ -14,12 +14,12 @@
"test": "react-scripts test --env=jsdom --coverage"
},
"devDependencies": {
"@polkadot/app-accounts": "^0.3.4",
"@polkadot/app-addresses": "^0.3.4",
"@polkadot/app-explorer": "^0.3.4",
"@polkadot/app-extrinsics": "^0.3.4",
"@polkadot/app-storage": "^0.3.4",
"@polkadot/ui-react-app": "^0.3.4",
"@polkadot/app-accounts": "^0.4.0",
"@polkadot/app-addresses": "^0.4.0",
"@polkadot/app-explorer": "^0.4.0",
"@polkadot/app-extrinsics": "^0.4.0",
"@polkadot/app-storage": "^0.4.0",
"@polkadot/ui-app": "^0.4.0",
"react-remarkable": "^1.1.3"
}
}

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import './App.css';

View File

@ -3,11 +3,12 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import './Connecting.css';
import React from 'react';
import withApiCall from '@polkadot/ui-react-rx/with/apiCall';
import translate from '../translate';

View File

@ -4,7 +4,7 @@
// @flow
import type { Location } from 'react-router-dom';
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import './Content.css';

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import './NotFound.css';

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { Route } from '../types';
import './Item.css';

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-react-app/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import './SideBar.css';

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import createApp from '@polkadot/ui-react-app/src';
import createApp from '@polkadot/ui-app/src';
import App from './App';
import NotFound from './NotFound';

View File

@ -0,0 +1 @@
# @polkadot/ui-app

View File

@ -1,6 +1,6 @@
{
"name": "@polkadot/ui-react-app",
"version": "0.3.4",
"name": "@polkadot/ui-app",
"version": "0.4.0",
"main": "index.js",
"repository": "https://github.com/polkadot-js/apps.git",
"author": "Jaco Greeff <jacogr@gmail.com>",
@ -13,7 +13,7 @@
"dependencies": {
"@polkadot/extrinsics-substrate": "^0.12.9",
"@polkadot/storage-substrate": "^0.12.9",
"@polkadot/ui-keyring": "^0.3.4",
"@polkadot/ui-keyring": "^0.4.0",
"@polkadot/ui-react": "^0.11.3",
"@polkadot/ui-react-rx": "^0.11.3",
"@polkadot/util": "^0.19.6",
@ -21,6 +21,7 @@
"i18next-browser-languagedetector": "^2.2.0",
"i18next-xhr-backend": "^1.5.1",
"react": "^16.3.1",
"react-copy-to-clipboard": "^5.0.1",
"react-dom": "^16.3.1",
"react-i18next": "^7.5.1",
"react-router-dom": "^4.2.2",

View File

@ -0,0 +1,28 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from './types';
import React from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import Button from 'semantic-ui-react/dist/es/elements/Button';
type Props = BareProps & {
value?: string
}
export default function CopyButton ({ className, style, value }: Props): React$Node {
return (
<CopyToClipboard text={value}>
<Button
className={className}
icon='copy'
primary
size='tiny'
style={style}
/>
</CopyToClipboard>
);
}

View File

@ -0,0 +1,66 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from './types';
import React from 'react';
import SUIDropdown from 'semantic-ui-react/dist/es/modules/Dropdown';
import Labelled from './Labelled';
type Props = BareProps & {
defaultValue?: mixed,
isError?: boolean,
label?: string,
// flowlint-next-line unclear-type:off
onChange: (value: any) => void,
// flowlint-next-line unclear-type:off
transform?: (value: any) => any
};
type SUIEvent = {
// flowlint-next-line unclear-type:off
value: any
};
export default class Dropdown extends React.PureComponent<Props> {
render (): React$Node {
const { className, isError, label, style } = this.props;
const _props = {...this.props};
delete _props.className;
delete _props.isError;
delete _props.label;
delete _props.onChange;
delete _props.style;
delete _props.transform;
return (
<Labelled
className={['ui--Dropdown', className].join(' ')}
label={label}
style={style}
>
<SUIDropdown
key='dropdown'
selection
{..._props}
error={isError}
onChange={this.onChange}
/>
</Labelled>
);
}
onChange = (event: SyntheticEvent<*>, { value }: SUIEvent): void => {
const { onChange, transform } = this.props;
onChange(
transform
? transform(value)
: value
);
}
}

View File

@ -0,0 +1,69 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from './types';
import React from 'react';
import SUIInput from 'semantic-ui-react/dist/es/elements/Input';
import Labelled from './Labelled';
type Props = BareProps & {
children?: React$Node,
defaultValue?: mixed,
isAction?: boolean,
isDisabled?: boolean,
isError?: boolean,
label?: string,
max?: mixed,
min?: mixed,
name?: string,
// flowlint-next-line unclear-type:off
onChange: (value: any) => void,
placeholder?: string,
type?: string,
value?: mixed
};
type SUIEvent = {
// flowlint-next-line unclear-type:off
value: any
};
export default class Input extends React.PureComponent<Props> {
render (): React$Node {
const { children, className, defaultValue, isAction = false, isDisabled = false, isError = false, label, max, min, name, placeholder, style, type = 'text', value } = this.props;
return (
<Labelled
className={className}
label={label}
style={style}
>
<SUIInput
action={isAction}
defaultValue={defaultValue}
disabled={isDisabled}
error={isError}
max={max}
min={min}
name={name}
onChange={this.onChange}
placeholder={placeholder}
type={type}
value={value}
>
<input />
{children}
</SUIInput>
</Labelled>
);
}
// eslint-disable-next-line no-unused-var
onChange = (event: SyntheticEvent<*>, { value }: SUIEvent): void => {
this.props.onChange(value);
}
}

View File

@ -0,0 +1,20 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import addressDecode from '@polkadot/util-keyring/address/decode';
import addressEncode from '@polkadot/util-keyring/address/encode';
export default function addressToAddress (value?: string | Uint8Array): ?string {
// flowlint-next-line sketchy-null-string:off
if (value) {
try {
return addressEncode(
addressDecode(value)
);
} catch (error) {
console.error('Unable to encode address', value);
}
}
}

View File

@ -9,26 +9,32 @@ import type { BareProps } from '../types';
import './InputAddress.css';
import React from 'react';
import keyring from '@polkadot/ui-keyring/src';
import addressDecode from '@polkadot/util-keyring/address/decode';
import addressEncode from '@polkadot/util-keyring/address/encode';
import RxDropdown from '../RxDropdown';
import keyring from '@polkadot/ui-keyring/src';
import createOptionHeader from '@polkadot/ui-keyring/src/options/header';
import addressDecode from '@polkadot/util-keyring/address/decode';
import Dropdown from '../Dropdown';
import addressToAddress from './addressToAddress';
type Props = BareProps & {
defaultValue?: Uint8Array,
defaultValue?: string | Uint8Array,
hideAddress?: boolean;
isError?: boolean,
isInput?: boolean,
label?: string,
onChange: (value: Uint8Array) => void,
type?: KeyringOption$Type
type?: KeyringOption$Type,
value?: string | Uint8Array
};
type State = {
defaultValue?: string;
defaultValue: ?string;
value: ?string;
}
const RECENT_KEY = 'header-recent';
const transform = (value: string): Uint8Array => {
try {
return addressDecode(value);
@ -42,28 +48,25 @@ export default class InputAddress extends React.Component<Props, State> {
constructor (props: Props) {
super(props);
let defaultValue;
if (props.defaultValue) {
try {
defaultValue = addressEncode(props.defaultValue);
} catch (error) {
console.error('Unable to encode defaultValue address', props.defaultValue);
}
}
this.state = {
defaultValue
defaultValue: addressToAddress(props.defaultValue),
value: addressToAddress(props.value)
};
}
static getDerivedStateFromProps ({ value }: Props): $Shape<State> {
return {
value: addressToAddress(value)
};
}
render (): React$Node {
const { className, hideAddress = false, isError, label, onChange, style, type = 'all' } = this.props;
const { defaultValue } = this.state;
const { defaultValue, value } = this.state;
const options = keyring.getOptions(type);
return (
<RxDropdown
<Dropdown
className={['ui--InputAddress', hideAddress ? 'flag--hideAddress' : '', className].join(' ')}
defaultValue={defaultValue}
isError={isError}
@ -73,6 +76,7 @@ export default class InputAddress extends React.Component<Props, State> {
search={this.onSearch}
style={style}
transform={transform}
value={value}
/>
);
}
@ -81,9 +85,8 @@ export default class InputAddress extends React.Component<Props, State> {
const { isInput = true } = this.props;
const queryLower = query.toLowerCase();
const matches = filteredOptions.filter((item) => {
// flowlint-next-line sketchy-null-string:off
if (!item.key) {
return false;
if (!item.value) {
return true;
}
const { name, value } = item;
@ -93,17 +96,32 @@ export default class InputAddress extends React.Component<Props, State> {
return hasMatch;
});
// see if we should add a new item, i.e. valid address found
if (isInput && matches.length === 0) {
const valueMatches = matches.filter((item) => item.value);
// see if we should add a new item, i.e. no valid address found
if (isInput && valueMatches.length === 0) {
const publicKey = transform(query);
if (publicKey.length === 32) {
if (!matches.find((item) => item.key === RECENT_KEY)) {
matches.push(
createOptionHeader('Recent')
);
}
matches.push(
keyring.saveRecent(query)
);
}
}
return matches;
return matches.filter((item, index) => {
// it is a value entry or header followed by a value entry
if (item.value || (index !== matches.length - 1 && matches[index + 1].value)) {
return true;
}
return false;
});
};
}

View File

@ -2,11 +2,11 @@
/* This software may be modified and distributed under the terms
/* of the ISC license. See the LICENSE file for details. */
.ui--RxDropdownLinked {}
.ui--DropdownLinked {}
.ui--RxDropdownLinked-Items {}
.ui--DropdownLinked-Items {}
.ui--RxDropdownLinked-Items .text {
.ui--DropdownLinked-Items .text {
box-sizing: border-box;
display: flex !important;
flex-wrap: nowrap;
@ -16,16 +16,16 @@
white-space: nowrap;
}
.ui--RxDropdownLinked-Item > .text {
.ui--DropdownLinked-Item > .text {
padding-left: 1em;
}
.ui--RxDropdownLinked-Item-text,
.ui--RxDropdownLinked-Item-call {
.ui--DropdownLinked-Item-text,
.ui--DropdownLinked-Item-call {
display: inline-block;
}
.ui--RxDropdownLinked-Item-call {
.ui--DropdownLinked-Item-call {
flex: 1;
font-family: monospace;
margin-left: 1rem;
@ -35,8 +35,8 @@
text-overflow: ellipsis;
}
.ui--RxDropdownLinked-Item-text {
.ui--DropdownLinked-Item-text {
flex: 1 0;
}
.ui--RxDropdownLinked-Sections {}
.ui--DropdownLinked-Sections {}

View File

@ -7,9 +7,10 @@ import type { Extrinsic } from '@polkadot/extrinsics/types';
import type { I18nProps } from '../types';
import React from 'react';
import extrinsics from '@polkadot/extrinsics-substrate';
import RxDropdown from '../RxDropdown';
import Dropdown from '../Dropdown';
import translate from '../translate';
import createOptions from './options/method';
@ -33,8 +34,8 @@ function SelectMethod ({ className, isError, label = '', onChange, style, t, typ
const options = createOptions(section, type);
return (
<RxDropdown
className={['ui--RxDropdownLinked-Items', className].join(' ')}
<Dropdown
className={['ui--DropdownLinked-Items', className].join(' ')}
isError={isError}
label={label || t('input.extrinsic.method', {
defaultValue: 'with the extrinsic'

View File

@ -8,7 +8,7 @@ import type { I18nProps } from '../types';
import React from 'react';
import RxDropdown from '../RxDropdown';
import Dropdown from '../Dropdown';
import translate from '../translate';
import createOptions from './options/section';
@ -25,8 +25,8 @@ function SelectSection ({ className, defaultValue, isError, label = '', onChange
const options = createOptions(type);
return (
<RxDropdown
className={['ui--RxDropdownLinked-Sections', className].join(' ')}
<Dropdown
className={['ui--DropdownLinked-Sections', className].join(' ')}
defaultValue={defaultValue}
isError={isError}
label={label || t('input.extrinsic.section', {

View File

@ -49,25 +49,23 @@ class InputExtrinsic extends React.PureComponent<Props, State> {
return (
<div
className={['ui--RxDropdownLinked', 'ui--row', className].join(' ')}
className={['ui--DropdownLinked', 'ui--row', className].join(' ')}
style={style}
>
<div className='small'>
<SelectSection
label={labelSection}
onChange={this.onSectionChange}
type={type}
value={value}
/>
</div>
<div className='large'>
<SelectMethod
label={labelMethod}
onChange={this.onKeyChange}
value={value}
type={type}
/>
</div>
<SelectSection
className='small'
label={labelSection}
onChange={this.onSectionChange}
type={type}
value={value}
/>
<SelectMethod
className='large'
label={labelMethod}
onChange={this.onKeyChange}
value={value}
type={type}
/>
</div>
);
}

View File

@ -6,6 +6,7 @@
import type { ExtrinsicSectionName } from '@polkadot/extrinsics/types';
import React from 'react';
import map from '@polkadot/extrinsics-substrate';
// flowlint-next-line unclear-type:off
@ -26,17 +27,17 @@ export default function createOptions (sectionName: any, type: 'private' | 'publ
const inputs = Object.keys(params).join(', ');
return {
className: 'ui--RxDropdownLinked-Item',
className: 'ui--DropdownLinked-Item',
key: `${sectionName}_${name}`,
text: [
<div
className='ui--RxDropdownLinked-Item-text'
className='ui--DropdownLinked-Item-text'
key={`${sectionName}_${name}:text`}
>
{description || name}
</div>,
<div
className='ui--RxDropdownLinked-Item-call'
className='ui--DropdownLinked-Item-call'
key={`${sectionName}_${name}:call`}
>
{name}({inputs})

View File

@ -7,9 +7,10 @@ import type { StorageDef$Key } from '@polkadot/storage/types';
import type { I18nProps } from '../types';
import React from 'react';
import map from '@polkadot/storage-substrate/keys';
import RxDropdown from '../RxDropdown';
import Dropdown from '../Dropdown';
import translate from '../translate';
import createOptions from './options/key';
@ -32,8 +33,8 @@ function SelectKey ({ className, isError, label = '', onChange, style, t, value:
const options = createOptions(section);
return (
<RxDropdown
className={['ui--RxDropdownLinked-Items', className].join(' ')}
<Dropdown
className={['ui--DropdownLinked-Items', className].join(' ')}
isError={isError}
label={label || t('input.storage.key', {
defaultValue: 'with storage key'

View File

@ -8,7 +8,7 @@ import type { I18nProps } from '../types';
import React from 'react';
import RxDropdown from '../RxDropdown';
import Dropdown from '../Dropdown';
import translate from '../translate';
import createOptions from './options/section';
@ -24,8 +24,8 @@ const options = createOptions();
function SelectSection ({ className, defaultValue, isError, label, onChange, style, t, value: { section } }: Props): React$Node {
return (
<RxDropdown
className={['ui--RxDropdownLinked-Sections', className].join(' ')}
<Dropdown
className={['ui--DropdownLinked-Sections', className].join(' ')}
defaultValue={defaultValue}
isError={isError}
label={label || t('input.storage.section', {

View File

@ -47,23 +47,21 @@ class InputStorage extends React.PureComponent<Props, State> {
return (
<div
className={['ui--RxDropdownLinked', 'ui--row', className].join(' ')}
className={['ui--DropdownLinked', 'ui--row', className].join(' ')}
style={style}
>
<div className='small'>
<SelectSection
label={labelSection}
onChange={this.onSectionChange}
value={value}
/>
</div>
<div className='large'>
<SelectKey
label={labelMethod}
onChange={this.onKeyChange}
value={value}
/>
</div>
<SelectSection
className='small'
label={labelSection}
onChange={this.onSectionChange}
value={value}
/>
<SelectKey
className='large'
label={labelMethod}
onChange={this.onKeyChange}
value={value}
/>
</div>
);
}

View File

@ -6,6 +6,7 @@
import type { StateDb$SectionNames } from '@polkadot/storage/types';
import React from 'react';
import map from '@polkadot/storage-substrate/keys';
// flowlint-next-line unclear-type:off
@ -29,17 +30,17 @@ export default function createOptions (sectionName: any): Array<*> {
const inputs = Object.keys(params).join(', ');
return {
className: 'ui--RxDropdownLinked-Item',
className: 'ui--DropdownLinked-Item',
key: `${sectionName}_${name}`,
text: [
<div
className='ui--RxDropdownLinked-Item-text'
className='ui--DropdownLinked-Item-text'
key={`${sectionName}_${name}:text`}
>
{description || name}
</div>,
<div
className='ui--RxDropdownLinked-Item-call'
className='ui--DropdownLinked-Item-call'
key={`${sectionName}_${name}:call`}
>
{name}({inputs})

View File

@ -0,0 +1,32 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from './types';
import React from 'react';
import Label from 'semantic-ui-react/dist/es/elements/Label';
type Props = BareProps & {
label?: React$Node,
children: React$Node
};
export default function Labelled ({ className, children, label, style }: Props): React$Node {
console.log('label', label);
return (
<div
className={className}
style={style}
>
<Label>{
typeof label === 'undefined'
? <div>&nbsp;</div>
: label
}</Label>
{children}
</div>
);
}

View File

@ -0,0 +1,46 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Props } from '../types';
import React from 'react';
import InputAddress from '../../InputAddress';
import Bare from './Bare';
export default class Account extends React.PureComponent<Props> {
render (): React$Node {
const { className, isError, label, style, value: { options: { initValue } = {} } } = this.props;
// flowlint-next-line unclear-type:off
const defaultValue = ((initValue: any): Uint8Array);
return (
<Bare
className={className}
style={style}
>
<InputAddress
className='large'
defaultValue={defaultValue}
isError={isError}
isInput
label={label}
onChange={this.onChange}
placeholder='5...'
/>
</Bare>
);
}
onChange = (value?: Uint8Array): void => {
const { index, onChange } = this.props;
onChange(index, {
isValid: !!value && value.length === 32,
value
});
}
}

View File

@ -0,0 +1,46 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
// flowlint sketchy-null-mixed:off,sketchy-null-number:off
import type { Props } from '../types';
import BN from 'bn.js';
import React from 'react';
import Input from '../../Input';
import Bare from './Bare';
export default class Amount extends React.PureComponent<Props> {
render (): React$Node {
const { className, isError, label, style, value: { options: { initValue = 0, maxValue, minValue = 0 } = {} } } = this.props;
return (
<Bare
className={className}
style={style}
>
<Input
className='small'
defaultValue={initValue || minValue}
isError={isError}
label={label}
max={maxValue}
min={minValue}
onChange={this.onChange}
type='number'
/>
</Bare>
);
}
onChange = (value: number): void => {
const { index, onChange, value: { options: { minValue = 0 } = {} } } = this.props;
onChange(index, {
isValid: true,
value: new BN(value || minValue || 0)
});
}
}

View File

@ -0,0 +1,23 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from '../../types';
import React from 'react';
type Props = BareProps & {
children: React$Node
};
export default function Bare ({ children, className, style }: Props): React$Node {
return (
<div
className={['ui--row', className].join(' ')}
style={style}
>
{children}
</div>
);
}

View File

@ -7,7 +7,9 @@ import type { BareProps } from '../../types';
import type { Size } from '../types';
import React from 'react';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import Labelled from '../../Labelled';
import Bare from './Bare';
type Props = BareProps & {
children: React$Node,
@ -17,14 +19,16 @@ type Props = BareProps & {
export default function Base ({ children, className, label, size = 'medium', style }: Props): React$Node {
return (
<div
className={['ui--row', className].join(' ')}
<Bare
className={className}
style={style}
>
<div className={size}>
<Label>{label}</Label>
<Labelled
className={size}
label={label}
>
{children}
</div>
</div>
</Labelled>
</Bare>
);
}

View File

@ -0,0 +1,47 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Props } from '../types';
import React from 'react';
import Dropdown from '../../Dropdown';
import Bare from './Bare';
const options = [
{ text: 'No', value: false },
{ text: 'Yes', value: true }
];
export default class Bool extends React.PureComponent<Props> {
render (): React$Node {
const { className, isError, label, style, value: { options: { initValue = false } = {} } } = this.props;
return (
<Bare
className={className}
style={style}
>
<Dropdown
className='small'
isError={isError}
defaultValue={initValue}
label={label}
options={options}
onChange={this.onChange}
/>
</Bare>
);
}
onChange = (value: boolean): void => {
const { index, onChange } = this.props;
onChange(index, {
isValid: true,
value
});
}
}

View File

@ -6,10 +6,11 @@
import type { Props as BaseProps, Size } from '../types';
import React from 'react';
import Input from 'semantic-ui-react/dist/es/elements/Input';
import hexToU8a from '@polkadot/util/hex/toU8a';
import Base from './Base';
import Input from '../../Input';
import Bare from './Bare';
type Props = BaseProps & {
length?: number,
@ -20,13 +21,34 @@ type Props = BaseProps & {
const defaultValidate = (u8a: Uint8Array): boolean =>
true;
export default function Bytes ({ index, isError, length = -1, label, onChange, size = 'full', validate = defaultValidate }: Props): React$Node {
// eslint-disable-next-line no-unused-vars
const _onChange = (event: SyntheticEvent<*>, { value }) => {
export default class Bytes extends React.PureComponent<Props> {
render (): React$Node {
const { className, isError, label, size = 'full', style } = this.props;
return (
<Bare
className={className}
style={style}
>
<Input
className={size}
isError={isError}
label={label}
onChange={this.onChange}
placeholder='0x...'
type='text'
/>
</Bare>
);
}
onChange = (hex: string): void => {
const { index, length = -1, onChange, validate = defaultValidate } = this.props;
let u8a;
try {
u8a = hexToU8a(value);
u8a = hexToU8a(hex);
} catch (error) {
u8a = new Uint8Array([]);
}
@ -40,18 +62,4 @@ export default function Bytes ({ index, isError, length = -1, label, onChange, s
value: u8a
});
};
return (
<Base
label={label}
size={size}
>
<Input
error={isError}
onChange={_onChange}
placeholder='0x...'
type='text'
/>
</Base>
);
}

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from '@polkadot/ui-react-app/types';
import type { BareProps } from '@polkadot/ui-app/types';
import React from 'react';
import Dropzone from 'react-dropzone';
@ -26,13 +26,7 @@ type State = {
};
class BytesFile extends React.PureComponent<Props, State> {
state: State;
constructor (props: Props) {
super(props);
this.state = {};
}
state: State = {};
render (): React$Node {
const { className, isError = false, label, t } = this.props;

View File

@ -0,0 +1,49 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
// flowlint sketchy-null-mixed:off
import type { Props } from '../types';
import React from 'react';
import Dropdown from '../../Dropdown';
import Bare from './Bare';
const options = [
{ text: 'Super majority approval', value: 0 },
{ text: 'Super majority rejection', value: 1 },
{ text: 'Simple majority', value: 2 }
];
export default class VoteThreshold extends React.PureComponent<Props> {
render (): React$Node {
const { className, label, style, value: { options: { initValue = 0 } = {} } } = this.props;
const defaultValue = initValue || 0;
return (
<Bare
className={className}
style={style}
>
<Dropdown
className='small'
defaultValue={defaultValue}
label={label}
options={options}
onChange={this.onChange}
/>
</Bare>
);
}
onChange = (value: number): void => {
const { index, onChange } = this.props;
onChange(index, {
isValid: true,
value
});
}
}

View File

@ -6,24 +6,34 @@
import type { Props } from '../types';
import React from 'react';
import bnToU8a from '@polkadot/util/bn/toU8a';
import u8aConcat from '@polkadot/util/u8a/concat';
import BytesFile from './File';
export default function Wasm ({ index, isError, label, onChange }: Props): React$Node {
export default class Wasm extends React.PureComponent<Props> {
render (): React$Node {
const { className, isError, label, style } = this.props;
return (
<BytesFile
className={className}
isError={isError}
label={label}
onChange={this.onChange}
style={style}
/>
);
}
// TODO: Validate that we have actual proper WASM code
const _onChange = (value: Uint8Array) =>
onChange = (value: Uint8Array): void => {
const { index, onChange } = this.props;
onChange(index, {
isValid: value.length !== 0,
value: u8aConcat(bnToU8a(value.length, 32, true), value)
});
return (
<BytesFile
isError={isError}
label={label}
onChange={_onChange}
/>
);
}
}

View File

@ -22,13 +22,37 @@ type Props = I18nProps & {
};
};
function ParamComponent ({ className, index, onChange, overrides, style, value: { name, type, options = {} } = {} }: Props): React$Node {
if (!type) {
return null;
type State = {
Component: React$ComponentType<*> | Array<React$ComponentType<*>> | null
}
class ParamComponent extends React.PureComponent<Props, State> {
state: State = {
Component: null
};
static getDerivedStateFromProps ({ overrides, value: { type } = {} }: Props): State {
return {
Component: !type
? null
: findComponent(type, overrides)
};
}
const Component = findComponent(type, overrides);
const renderComponent = (Component: React$ComponentType<*>, sub: number = -1) => {
render (): React$Node {
const { Component } = this.state;
if (!Component) {
return null;
}
return Array.isArray(Component)
? Component.map(this.renderComponent)
: this.renderComponent(Component);
}
renderComponent = (Component: React$ComponentType<*>, sub: number = -1): React$Node => {
const { className, index, onChange, style, value: { name, type, options = {} } = {} } = this.props;
const _type: Param$Type = Array.isArray(type)
? type[sub]
: type;
@ -39,23 +63,20 @@ function ParamComponent ({ className, index, onChange, overrides, style, value:
return (
<Component
className='ui--Param'
className={['ui--Param', className].join(' ')}
index={index}
key={`${name}:${text}:${index}}`}
label={`${name}: ${text}${labelExtra}`}
// FIXME subjects are not for array components (as defined here)
onChange={onChange}
style={style}
value={{
options,
type: _type
}}
/>
);
};
return Array.isArray(Component)
? Component.map(renderComponent)
: renderComponent(Component);
}
}
export default translate(ParamComponent);

View File

@ -27,14 +27,7 @@ type State = {
};
class Params extends React.PureComponent<Props, State> {
state: State;
constructor (props: Props) {
super(props);
// wed ont' set anything, getDerivedStateFromProps will update
this.state = ({}: $Shape<State>);
}
state: State = ({}: $Shape<State>);
static getDerivedStateFromProps ({ item, onChange }: Props, { item: { name, section } = {} }: State): $Shape<State> | null {
if (name === item.name && section === item.section) {

View File

@ -7,6 +7,7 @@
import type { Param } from '@polkadot/primitives/param';
import BN from 'bn.js';
import isUndefined from '@polkadot/util/is/undefined';
export default function getInitValue ({ options: { initValue, minValue } = {}, type }: Param): mixed {

View File

@ -0,0 +1,69 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from './types';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import Input from './Input';
type Props = BareProps & {
defaultValue?: mixed,
children?: React$Node,
isDisabled?: boolean,
isError?: boolean,
label?: string,
name?: string,
onChange: (value: string) => void,
value: mixed
};
type State = {
isVisible: boolean
}
export default class Password extends React.PureComponent<Props, State> {
state: State = {
isVisible: false
};
render (): React$Node {
const { children, className, defaultValue, isDisabled, isError, label, name, onChange, style, value } = this.props;
const { isVisible } = this.state;
const type = isVisible
? 'text'
: 'password';
return (
<Input
className={className}
defaultValue={defaultValue}
isAction
isDisabled={isDisabled}
isError={isError}
label={label}
name={name}
onChange={onChange}
style={style}
type={type}
value={value}
>
<Button
icon='eye'
primary
onClick={this.onToggleVisible}
/>
{children}
</Input>
);
}
onToggleVisible = (): void => {
this.setState({
isVisible: !this.state.isVisible
});
}
}

View File

@ -13,6 +13,7 @@ import './styles';
import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter } from 'react-router-dom';
import Api from '@polkadot/ui-react-rx/Api';
type Props = BareProps & {

View File

@ -20,15 +20,29 @@
.ui--row > div {
box-sizing: border-box;
min-width: 0;
}
.ui--grid > div:not(.grow):not(.shrink),
.ui--row > div:not(.grow):not(.shrink) {
width: 100%;
}
.ui--grid > div,
.ui--grid > div:not(.shrink),
.ui--grid > div.full,
.ui--row > div.full {
flex: 0 100%;
}
.ui--grid > div.shrink,
.ui--row > div.shrink {
flex: 0 1 auto;
}
.ui--grid > div.grow,
.ui--row > div.grow {
flex: 1 1 auto;
}
.ui--grid > div.large,
.ui--row > div.large {
flex: 0 75%;
@ -65,7 +79,7 @@
.ui--row-buttons {
margin: 1em 0;
text-align: center;
text-align: right;
}
.ui--row-buttons .button {

View File

@ -43,7 +43,7 @@
.ui.modal > .actions {
background: transparent;
border-top: none;
text-align: center;
text-align: right;
}
.ui.modal > .actions .button {

Some files were not shown because too many files have changed in this diff Show More