Rework query components (no refs) (#1688)

* Rework query components (no refs)

* Fix raw storage queries

* Alphabetical
This commit is contained in:
Jaco Greeff 2019-10-03 09:54:27 +02:00 committed by GitHub
parent 4f2c1f679e
commit 69bc18d053
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 181 additions and 286 deletions

View File

@ -2,20 +2,20 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { RenderFn, DefaultProps, ComponentRenderer } from '@polkadot/react-api/with/types';
import { I18nProps } from '@polkadot/react-components/types';
import { ConstValue } from '@polkadot/react-components/InputConsts/types';
import { QueryTypes, StorageEntryPromise, StorageModuleQuery } from './types';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { Compact } from '@polkadot/types';
import { Button, Labelled } from '@polkadot/react-components';
import { withCallDiv } from '@polkadot/react-api';
import valueToText from '@polkadot/react-params/valueToText';
import { Compact, Data, Option } from '@polkadot/types';
import { isU8a, u8aToHex, u8aToString } from '@polkadot/util';
import translate from './translate';
import { RenderFn, DefaultProps, ComponentRenderer } from '@polkadot/react-api/with/types';
import { ConstValue } from '@polkadot/react-components/InputConsts/types';
interface Props extends I18nProps {
onRemove: (id: number) => void;
@ -65,16 +65,14 @@ function createComponent (type: string, Component: React.ComponentType<any>, def
return {
Component,
// In order to replace the default component during runtime we can provide a RenderFn to create a new 'plugged' component
render: (createComponent: RenderFn): React.ComponentType<any> => {
return renderHelper(createComponent, defaultProps);
},
render: (createComponent: RenderFn): React.ComponentType<any> =>
renderHelper(createComponent, defaultProps),
// In order to modify the parameters which are used to render the default component, we can use this method
refresh: (swallowErrors: boolean, contentShorten: boolean): React.ComponentType<any> => {
return renderHelper(
refresh: (swallowErrors: boolean, contentShorten: boolean): React.ComponentType<any> =>
renderHelper(
(value: any): React.ReactNode => valueToText(type, value, swallowErrors, contentShorten),
defaultProps
);
}
)
};
}
@ -93,12 +91,23 @@ function getCachedComponent (query: QueryTypes): CacheInstance {
} else {
const values: any[] = params.map(({ value }): any => value);
// render function to create an element for the query results which is plugged to the api
renderHelper = withCallDiv('subscribe', {
paramName: 'params',
paramValid: true,
params: [key, ...values]
});
if (isU8a(key)) {
// subscribe to the raw key here
renderHelper = withCallDiv('rpc.state.subscribeStorage', {
paramName: 'params',
paramValid: true,
params: [[key]],
transform: ([data]: Option<Data>[]): Option<Data> => data
});
} else {
// render function to create an element for the query results which is plugged to the api
renderHelper = withCallDiv('subscribe', {
paramName: 'params',
paramValid: true,
params: [key, ...values]
});
}
type = key.creator && key.creator.meta
? typeToString(key)
: 'Data';

View File

@ -5,84 +5,54 @@
import { ConstantCodec } from '@polkadot/api-metadata/consts/types';
import { I18nProps } from '@polkadot/react-components/types';
import { ConstValue } from '@polkadot/react-components/InputConsts/types';
import { ApiProps } from '@polkadot/react-api/types';
import { ComponentProps } from '../types';
import React from 'react';
import { Button, InputConsts, TxComponent } from '@polkadot/react-components';
import { withApi, withMulti } from '@polkadot/react-api';
import React, { useContext, useState } from 'react';
import { ApiContext } from '@polkadot/react-api';
import { Button, InputConsts } from '@polkadot/react-components';
import translate from '../translate';
interface Props extends ComponentProps, ApiProps, I18nProps {}
interface Props extends ComponentProps, I18nProps {}
interface State {
value: ConstValue;
}
class Consts extends TxComponent<Props, State> {
private defaultValue: ConstValue;
public state: State;
public constructor (props: Props) {
super(props);
const { api } = this.props;
function Consts ({ onAdd, t }: Props): React.ReactElement<Props> {
const { api } = useContext(ApiContext);
const [defaultValue] = useState<ConstValue>((): ConstValue => {
const section = Object.keys(api.consts)[0];
const method = Object.keys(api.consts[section])[0];
this.defaultValue = {
return {
meta: (api.consts[section][method] as ConstantCodec).meta,
method,
section
};
this.state = {
value: this.defaultValue
};
}
});
const [value, setValue] = useState(defaultValue);
public render (): React.ReactNode {
const { api, t } = this.props;
const { value: { method, section } } = this.state;
const meta = (api.consts[section][method] as ConstantCodec).meta;
const _onAdd = (): void => onAdd({ isConst: true, key: value });
return (
<section className='storage--actionrow'>
<div className='storage--actionrow-value'>
<InputConsts
defaultValue={this.defaultValue}
label={t('selected constant query')}
onChange={this.onChangeConst}
help={meta && meta.documentation && meta.documentation.join(' ')}
/>
</div>
<div className='storage--actionrow-buttons'>
<Button
icon='plus'
isPrimary
onClick={this.onAdd}
ref={this.button}
/>
</div>
</section>
);
}
const { method, section } = value;
const meta = (api.consts[section][method] as ConstantCodec).meta;
private onAdd = (): void => {
const { onAdd } = this.props;
const { value } = this.state;
onAdd({ isConst: true, key: value });
}
private onChangeConst = (value: ConstValue): void => {
this.setState({ value });
}
return (
<section className='storage--actionrow'>
<div className='storage--actionrow-value'>
<InputConsts
defaultValue={defaultValue}
label={t('selected constant query')}
onChange={setValue}
help={meta && meta.documentation && meta.documentation.join(' ')}
/>
</div>
<div className='storage--actionrow-buttons'>
<Button
icon='plus'
isPrimary
onClick={_onAdd}
/>
</div>
</section>
);
}
export default withMulti(
Consts,
translate,
withApi
);
export default translate(Consts);

View File

@ -5,181 +5,115 @@
import { TypeDef } from '@polkadot/types/types';
import { I18nProps } from '@polkadot/react-components/types';
import { RawParams } from '@polkadot/react-params/types';
import { ApiProps } from '@polkadot/react-api/types';
import { ComponentProps, StorageEntryPromise } from '../types';
import React from 'react';
import React, { useContext, useState } from 'react';
import { getTypeDef } from '@polkadot/types';
import { Button, InputStorage, TxComponent } from '@polkadot/react-components';
import { Button, InputStorage } from '@polkadot/react-components';
import Params from '@polkadot/react-params';
import { withApi, withMulti } from '@polkadot/react-api';
import { ApiContext } from '@polkadot/react-api';
import { isNull, isUndefined } from '@polkadot/util';
import translate from '../translate';
interface Props extends ComponentProps, ApiProps, I18nProps {}
interface Props extends ComponentProps, I18nProps {}
interface State {
isLinked: boolean;
isValid: boolean;
key: StorageEntryPromise;
defaultValues?: RawParams | null;
values: RawParams;
params: { type: TypeDef }[];
type ParamsType = { type: TypeDef }[];
function areParamsValid (values: RawParams): boolean {
return values.reduce(
(isValid: boolean, value): boolean => (
isValid &&
!isUndefined(value) &&
!isUndefined(value.value) &&
value.isValid),
true
);
}
class Modules extends TxComponent<Props, State> {
private defaultValue: any;
function Modules ({ onAdd, t }: Props): React.ReactElement<Props> {
const { api } = useContext(ApiContext);
const [{ defaultValues, isLinked, key, params }, setKey] = useState<{ defaultValues: RawParams | undefined | null; isLinked: boolean; key: StorageEntryPromise; params: ParamsType }>({ defaultValues: undefined, isLinked: false, key: api.query.timestamp.now, params: [] });
const [{ isValid, values }, setValues] = useState<{ isValid: boolean; values: RawParams }>({ isValid: true, values: [] });
public state: State;
public constructor (props: Props) {
super(props);
const { api } = this.props;
this.defaultValue = api.query.timestamp.now;
this.state = {
isLinked: false,
isValid: true,
key: this.defaultValue,
values: [],
params: []
};
}
public render (): React.ReactNode {
const { t } = this.props;
const { isValid, key: { creator: { method, section, meta } }, defaultValues, params } = this.state;
return (
<section className='storage--actionrow'>
<div className='storage--actionrow-value'>
<InputStorage
defaultValue={this.defaultValue}
label={t('selected state query')}
onChange={this.onChangeKey}
help={meta && meta.documentation && meta.documentation.join(' ')}
/>
<Params
key={`${section}.${method}:params` /* force re-render on change */}
onChange={this.onChangeParams}
onEnter={this.submit}
params={params}
values={defaultValues}
/>
</div>
<div className='storage--actionrow-buttons'>
<Button
icon='plus'
isDisabled={!isValid}
isPrimary
onClick={this.onAdd}
ref={this.button}
/>
</div>
</section>
);
}
private nextState (newState: Partial<State>): void {
this.setState(
(prevState: State): Pick<State, never> => {
const { key = prevState.key, values = prevState.values } = newState;
const areParamsValid = (): boolean => {
return values.reduce(
(isValid: boolean, value): boolean => (
isValid &&
!isUndefined(value) &&
!isUndefined(value.value) &&
value.isValid),
true
);
};
if (key.creator.meta.type.isDoubleMap) {
const key1 = key.creator.meta.type.asDoubleMap.key1.toString();
const key2 = key.creator.meta.type.asDoubleMap.key2.toString();
return {
defaultValues: this.getDefaultValues(),
isValid: values.length === 2 && areParamsValid(),
key,
params: [
{ type: getTypeDef(key1) },
{ type: getTypeDef(key2) }
],
values
};
}
const isMap = key.creator.meta.type.isMap;
const isLinked = isMap && key.creator.meta.type.asMap.linked.isTrue;
const isValid = values.length === (isMap ? 1 : 0) && areParamsValid();
return {
defaultValues: null,
isLinked,
isValid,
key,
params: isMap
? [{
type: getTypeDef(
isLinked
? `Option<${key.creator.meta.type.asMap.key.toString()}>`
: key.creator.meta.type.asMap.key.toString()
)
}]
: [],
values
};
}
);
}
private getDefaultValues = (): RawParams | null => {
const { api } = this.props;
const { key } = this.state;
if (key.creator.section === 'session') {
return [
{
isValid: true,
value: api.consts.session.dedupKeyPrefix.toHex()
}
];
}
return null;
}
private onAdd = (): void => {
const { onAdd } = this.props;
const { isLinked, key, values } = this.state;
onAdd({
const _onAdd = (): void => {
isValid && onAdd({
isConst: false,
key,
params: values.filter(({ value }): boolean => !isLinked || !isNull(value))
});
}
private onChangeKey = (key: StorageEntryPromise): void => {
this.nextState({
isValid: false,
key,
values: [],
params: []
};
const _onChangeValues = (values: RawParams): void => {
setValues({
isValid: (
key.creator.meta.type.isDoubleMap
? values.length === 2
: values.length === (key.creator.meta.type.isMap ? 1 : 0)
) && areParamsValid(values),
values
});
}
};
const _onChangeKey = (key: StorageEntryPromise): void => {
const isMap = key.creator.meta.type.isMap;
const isLinked = isMap && key.creator.meta.type.asMap.linked.isTrue;
private onChangeParams = (values: RawParams = []): void => {
this.nextState({ values });
}
setKey({
defaultValues: key.creator.section === 'session'
? [{
isValid: true,
value: api.consts.session.dedupKeyPrefix.toHex()
}]
: null,
isLinked,
key,
params: key.creator.meta.type.isDoubleMap
? [
{ type: getTypeDef(key.creator.meta.type.asDoubleMap.key1.toString()) },
{ type: getTypeDef(key.creator.meta.type.asDoubleMap.key2.toString()) }
]
: isMap
? [{
type: getTypeDef(
isLinked
? `Option<${key.creator.meta.type.asMap.key.toString()}>`
: key.creator.meta.type.asMap.key.toString()
)
}]
: []
});
_onChangeValues([]);
};
const { creator: { method, section, meta } } = key;
return (
<section className='storage--actionrow'>
<div className='storage--actionrow-value'>
<InputStorage
defaultValue={api.query.timestamp.now}
label={t('selected state query')}
onChange={_onChangeKey}
help={meta && meta.documentation && meta.documentation.join(' ')}
/>
<Params
key={`${section}.${method}:params` /* force re-render on change */}
onChange={_onChangeValues}
onEnter={_onAdd}
params={params}
values={defaultValues}
/>
</div>
<div className='storage--actionrow-buttons'>
<Button
icon='plus'
isDisabled={!isValid}
isPrimary
onClick={_onAdd}
/>
</div>
</section>
);
}
export default withMulti(
Modules,
translate,
withApi
);
export default translate(Modules);

View File

@ -5,8 +5,8 @@
import { I18nProps } from '@polkadot/react-components/types';
import { ComponentProps } from '../types';
import React from 'react';
import { Button, Input, TxComponent } from '@polkadot/react-components';
import React, { useState } from 'react';
import { Button, Input } from '@polkadot/react-components';
import translate from '../translate';
import { u8aToU8a } from '@polkadot/util';
@ -14,60 +14,42 @@ import { Compact } from '@polkadot/types';
interface Props extends ComponentProps, I18nProps {}
interface State {
isValid: boolean;
key: Uint8Array;
}
function Raw ({ onAdd, t }: Props): React.ReactElement<Props> {
const [{ isValid, key }, setValue] = useState<{ isValid: boolean; key: Uint8Array }>({ isValid: false, key: new Uint8Array([]) });
class Raw extends TxComponent<Props, State> {
public state: State = {
isValid: false,
key: new Uint8Array([])
const _onAdd = (): void => {
isValid && onAdd({ isConst: false, key });
};
public render (): React.ReactNode {
const { t } = this.props;
const { isValid } = this.state;
return (
<section className='storage--actionrow'>
<div className='storage--actionrow-value'>
<Input
autoFocus
label={t('hex-encoded storage key')}
onChange={this.onChangeKey}
onEnter={this.submit}
/>
</div>
<div className='storage--actionrow-buttons'>
<Button
icon='plus'
isDisabled={!isValid}
isPrimary
onClick={this.onAdd}
ref={this.button}
/>
</div>
</section>
);
}
private onAdd = (): void => {
const { onAdd } = this.props;
const { key } = this.state;
onAdd({ isConst: false, key });
}
private onChangeKey = (key: string): void => {
const _onChangeKey = (key: string): void => {
const u8a = u8aToU8a(key);
const isValid = u8a.length !== 0;
this.setState({
setValue({
isValid,
key: Compact.addLengthPrefix(u8a)
});
}
};
return (
<section className='storage--actionrow'>
<div className='storage--actionrow-value'>
<Input
autoFocus
label={t('hex-encoded storage key')}
onChange={_onChangeKey}
onEnter={_onAdd}
/>
</div>
<div className='storage--actionrow-buttons'>
<Button
icon='plus'
isDisabled={!isValid}
isPrimary
onClick={_onAdd}
/>
</div>
</section>
);
}
export default translate(Raw);

View File

@ -18,7 +18,7 @@ interface Props extends AppProps, I18nProps {}
function StorageApp ({ basePath }: Props): React.ReactElement<Props> {
const [queue, setQueue] = useState<QueryTypes[]>([]);
const _onAdd = (query: QueryTypes): void => setQueue([query].concat(queue));
const _onAdd = (query: QueryTypes): void => setQueue([query, ...queue]);
const _onRemove = (id: number): void => setQueue(queue.filter((item): boolean => item.id !== id));
return (