Pass last decoded extrinsic to submission (#6964)
* Pass last decoded extrinsic to submission (partial) * Adjust * Vector * Adjust * Adjust * Adjust * Fix enum state * Tweak enum * Non-crashing enum * Tuple * Cleanups * Cleanups * CHANGELOG
This commit is contained in:
parent
2550860722
commit
661ed704db
@ -6,6 +6,7 @@ Changes:
|
||||
|
||||
- Adjust teleport to only cater for latest XCM
|
||||
- Add blocktime latency tab to Explorer
|
||||
- Allow decoded extrinsics to populate submission
|
||||
|
||||
|
||||
## 0.106.1 Feb 14, 2022
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
import type { SubmittableExtrinsic, SubmittableExtrinsicFunction } from '@polkadot/api/types';
|
||||
import type { Call } from '@polkadot/types/interfaces';
|
||||
import type { DecodedExtrinsic } from './types';
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
@ -17,6 +18,7 @@ import { useTranslation } from './translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
setLast: (value: DecodedExtrinsic | null) => void;
|
||||
}
|
||||
|
||||
interface ExtrinsicInfo {
|
||||
@ -37,7 +39,7 @@ const DEFAULT_INFO: ExtrinsicInfo = {
|
||||
extrinsicHex: null
|
||||
};
|
||||
|
||||
function Decoder ({ className }: Props): React.ReactElement<Props> {
|
||||
function Decoder ({ className, setLast }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [{ decoded, extrinsic, extrinsicCall, extrinsicError, extrinsicFn }, setExtrinsicInfo] = useState<ExtrinsicInfo>(DEFAULT_INFO);
|
||||
@ -68,11 +70,13 @@ function Decoder ({ className }: Props): React.ReactElement<Props> {
|
||||
}
|
||||
|
||||
setExtrinsicInfo({ ...DEFAULT_INFO, decoded, extrinsic, extrinsicCall, extrinsicFn, extrinsicHex });
|
||||
setLast({ call: extrinsicCall, fn: extrinsicFn });
|
||||
} catch (e) {
|
||||
setExtrinsicInfo({ ...DEFAULT_INFO, extrinsicError: (e as Error).message });
|
||||
setLast(null);
|
||||
}
|
||||
},
|
||||
[api]
|
||||
[api, setLast]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
// Copyright 2017-2022 @polkadot/app-extrinsics authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { SubmittableExtrinsic } from '@polkadot/api/types';
|
||||
import type { SubmittableExtrinsic, SubmittableExtrinsicFunction } from '@polkadot/api/types';
|
||||
import type { RawParam } from '@polkadot/react-params/types';
|
||||
import type { DecodedExtrinsic } from './types';
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
@ -14,22 +16,45 @@ import { useTranslation } from './translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
defaultValue: DecodedExtrinsic | null;
|
||||
}
|
||||
|
||||
function Selection ({ className }: Props): React.ReactElement<Props> {
|
||||
interface DefaultExtrinsic {
|
||||
defaultArgs?: RawParam[];
|
||||
defaultFn: SubmittableExtrinsicFunction<'promise'>;
|
||||
}
|
||||
|
||||
function extractDefaults (value: DecodedExtrinsic | null, defaultFn: SubmittableExtrinsicFunction<'promise'>): DefaultExtrinsic {
|
||||
if (!value) {
|
||||
return { defaultFn };
|
||||
}
|
||||
|
||||
return {
|
||||
defaultArgs: value.call.args.map((value) => ({
|
||||
isValid: true,
|
||||
value
|
||||
})),
|
||||
defaultFn: value.fn
|
||||
};
|
||||
}
|
||||
|
||||
function Selection ({ className, defaultValue }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { apiDefaultTxSudo } = useApi();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [extrinsic, setExtrinsic] = useState<SubmittableExtrinsic<'promise'> | null>(null);
|
||||
const [{ defaultArgs, defaultFn }] = useState<DefaultExtrinsic>(() => extractDefaults(defaultValue, apiDefaultTxSudo));
|
||||
|
||||
const _onExtrinsicChange = useCallback(
|
||||
(method?: SubmittableExtrinsic<'promise'>) => setExtrinsic(() => method || null),
|
||||
(method?: SubmittableExtrinsic<'promise'>) =>
|
||||
setExtrinsic(() => method || null),
|
||||
[]
|
||||
);
|
||||
|
||||
const _onExtrinsicError = useCallback(
|
||||
(error?: Error | null) => setError(error ? error.message : null),
|
||||
(error?: Error | null) =>
|
||||
setError(error ? error.message : null),
|
||||
[]
|
||||
);
|
||||
|
||||
@ -47,7 +72,8 @@ function Selection ({ className }: Props): React.ReactElement<Props> {
|
||||
type='account'
|
||||
/>
|
||||
<Extrinsic
|
||||
defaultValue={apiDefaultTxSudo}
|
||||
defaultArgs={defaultArgs}
|
||||
defaultValue={defaultFn}
|
||||
label={t<string>('submit the following extrinsic')}
|
||||
onChange={_onExtrinsicChange}
|
||||
onError={_onExtrinsicError}
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { AppProps as Props } from '@polkadot/react-components/types';
|
||||
import type { DecodedExtrinsic } from './types';
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Route, Switch } from 'react-router';
|
||||
|
||||
import { Tabs } from '@polkadot/react-components';
|
||||
@ -14,6 +15,7 @@ import { useTranslation } from './translate';
|
||||
|
||||
function ExtrinsicsApp ({ basePath }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [decoded, setDecoded] = useState<DecodedExtrinsic | null>(null);
|
||||
|
||||
const itemsRef = useRef([
|
||||
{
|
||||
@ -34,9 +36,11 @@ function ExtrinsicsApp ({ basePath }: Props): React.ReactElement<Props> {
|
||||
items={itemsRef.current}
|
||||
/>
|
||||
<Switch>
|
||||
<Route path={`${basePath}/decode`}><Decoder /></Route>
|
||||
<Route path={`${basePath}/decode`}>
|
||||
<Decoder setLast={setDecoded} />
|
||||
</Route>
|
||||
<Route>
|
||||
<Submission />
|
||||
<Submission defaultValue={decoded} />
|
||||
</Route>
|
||||
</Switch>
|
||||
</main>
|
||||
|
||||
10
packages/page-extrinsics/src/types.ts
Normal file
10
packages/page-extrinsics/src/types.ts
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright 2017-2022 @polkadot/app-extrinsics authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { SubmittableExtrinsicFunction } from '@polkadot/api/types';
|
||||
import type { Call } from '@polkadot/types/interfaces';
|
||||
|
||||
export interface DecodedExtrinsic {
|
||||
call: Call;
|
||||
fn: SubmittableExtrinsicFunction<'promise'>;
|
||||
}
|
||||
@ -16,6 +16,7 @@ import paramComponents from './Params';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
defaultArgs?: RawParam[];
|
||||
defaultValue: SubmittableExtrinsicFunction<'promise'>;
|
||||
isDisabled?: boolean;
|
||||
isError?: boolean;
|
||||
@ -28,15 +29,29 @@ interface Props {
|
||||
withLabel?: boolean;
|
||||
}
|
||||
|
||||
interface CallState {
|
||||
fn: SubmittableExtrinsicFunction<'promise'>;
|
||||
params: {
|
||||
name: string;
|
||||
type: TypeDef
|
||||
}[];
|
||||
interface ParamDef {
|
||||
name: string;
|
||||
type: TypeDef;
|
||||
}
|
||||
|
||||
function getParams ({ meta }: SubmittableExtrinsicFunction<'promise'>): { name: string; type: TypeDef }[] {
|
||||
interface CallState {
|
||||
extrinsic: {
|
||||
fn: SubmittableExtrinsicFunction<'promise'>;
|
||||
params: ParamDef[];
|
||||
},
|
||||
values: RawParam[];
|
||||
}
|
||||
|
||||
function isValuesValid (params: ParamDef[], values: RawParam[]): boolean {
|
||||
return values.reduce((isValid, value): boolean =>
|
||||
isValid &&
|
||||
!isUndefined(value) &&
|
||||
!isUndefined(value.value) &&
|
||||
value.isValid, params.length === values.length
|
||||
);
|
||||
}
|
||||
|
||||
function getParams ({ meta }: SubmittableExtrinsicFunction<'promise'>): ParamDef[] {
|
||||
return meta.args.map(({ name, type, typeName }): { name: string; type: TypeDef } => ({
|
||||
name: name.toString(),
|
||||
type: {
|
||||
@ -49,21 +64,21 @@ function getParams ({ meta }: SubmittableExtrinsicFunction<'promise'>): { name:
|
||||
}));
|
||||
}
|
||||
|
||||
function ExtrinsicDisplay ({ defaultValue, isDisabled, isError, isPrivate, label, onChange, onEnter, onError, onEscape, withLabel }: Props): React.ReactElement<Props> {
|
||||
const [extrinsic, setCall] = useState<CallState>(() => ({ fn: defaultValue, params: getParams(defaultValue) }));
|
||||
const [values, setValues] = useState<RawParam[]>([]);
|
||||
function getCallState (fn: SubmittableExtrinsicFunction<'promise'>, values: RawParam[] = []): CallState {
|
||||
return {
|
||||
extrinsic: {
|
||||
fn,
|
||||
params: getParams(fn)
|
||||
},
|
||||
values
|
||||
};
|
||||
}
|
||||
|
||||
function ExtrinsicDisplay ({ defaultArgs, defaultValue, isDisabled, isError, isPrivate, label, onChange, onEnter, onError, onEscape, withLabel }: Props): React.ReactElement<Props> {
|
||||
const [{ extrinsic, values }, setDisplay] = useState<CallState>(() => getCallState(defaultValue, defaultArgs));
|
||||
|
||||
useEffect((): void => {
|
||||
setValues([]);
|
||||
}, [extrinsic]);
|
||||
|
||||
useEffect((): void => {
|
||||
const isValid = values.reduce((isValid, value): boolean =>
|
||||
isValid &&
|
||||
!isUndefined(value) &&
|
||||
!isUndefined(value.value) &&
|
||||
value.isValid, extrinsic.params.length === values.length
|
||||
);
|
||||
const isValid = isValuesValid(extrinsic.params, values);
|
||||
|
||||
let method;
|
||||
|
||||
@ -81,7 +96,14 @@ function ExtrinsicDisplay ({ defaultValue, isDisabled, isError, isPrivate, label
|
||||
}, [extrinsic, onChange, onError, values]);
|
||||
|
||||
const _onChangeMethod = useCallback(
|
||||
(fn: SubmittableExtrinsicFunction<'promise'>): void => setCall({ fn, params: getParams(fn) }),
|
||||
(fn: SubmittableExtrinsicFunction<'promise'>) =>
|
||||
setDisplay(getCallState(fn)),
|
||||
[]
|
||||
);
|
||||
|
||||
const _setValues = useCallback(
|
||||
(values: RawParam[]) =>
|
||||
setDisplay(({ extrinsic }) => ({ extrinsic, values })),
|
||||
[]
|
||||
);
|
||||
|
||||
@ -101,11 +123,12 @@ function ExtrinsicDisplay ({ defaultValue, isDisabled, isError, isPrivate, label
|
||||
/>
|
||||
<Params
|
||||
key={`${section}.${method}:params` /* force re-render on change */}
|
||||
onChange={setValues}
|
||||
onChange={_setValues}
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
overrides={paramComponents}
|
||||
params={params}
|
||||
values={values}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -10,28 +10,29 @@ import { keyring } from '@polkadot/ui-keyring';
|
||||
|
||||
import Bare from './Bare';
|
||||
|
||||
function isValidAddress (value?: string | null): boolean {
|
||||
if (value) {
|
||||
try {
|
||||
keyring.decodeAddress(value);
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function Account ({ className = '', defaultValue: { value }, isDisabled, isError, isInOption, label, onChange, withLabel }: Props): React.ReactElement<Props> {
|
||||
const [defaultValue] = useState(() => (value as string)?.toString());
|
||||
|
||||
const _onChange = useCallback(
|
||||
(value?: string | null): void => {
|
||||
let isValid = false;
|
||||
|
||||
if (value) {
|
||||
try {
|
||||
keyring.decodeAddress(value);
|
||||
|
||||
isValid = true;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
(value?: string | null) =>
|
||||
onChange && onChange({
|
||||
isValid,
|
||||
isValid: isValidAddress(value),
|
||||
value
|
||||
});
|
||||
},
|
||||
}),
|
||||
[onChange]
|
||||
);
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// Copyright 2017-2022 @polkadot/react-params authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Registry, TypeDef } from '@polkadot/types/types';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { Props } from '../types';
|
||||
|
||||
@ -11,6 +12,14 @@ import { bnToBn, formatNumber, isUndefined } from '@polkadot/util';
|
||||
|
||||
import Bare from './Bare';
|
||||
|
||||
function getBitLength (registry: Registry, { type }: TypeDef): number {
|
||||
try {
|
||||
return registry.createType(type as 'u32').bitLength();
|
||||
} catch (error) {
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
|
||||
function Amount ({ className = '', defaultValue: { value }, isDisabled, isError, label, onChange, onEnter, registry, type, withLabel }: Props): React.ReactElement<Props> {
|
||||
const defaultValue = useMemo(
|
||||
() => isDisabled
|
||||
@ -22,21 +31,16 @@ function Amount ({ className = '', defaultValue: { value }, isDisabled, isError,
|
||||
);
|
||||
|
||||
const bitLength = useMemo(
|
||||
(): number => {
|
||||
try {
|
||||
return registry.createType(type.type as 'u32').bitLength();
|
||||
} catch (error) {
|
||||
return 32;
|
||||
}
|
||||
},
|
||||
() => getBitLength(registry, type),
|
||||
[registry, type]
|
||||
);
|
||||
|
||||
const _onChange = useCallback(
|
||||
(value?: BN) => onChange && onChange({
|
||||
isValid: !isUndefined(value),
|
||||
value
|
||||
}),
|
||||
(value?: BN) =>
|
||||
onChange && onChange({
|
||||
isValid: !isUndefined(value),
|
||||
value
|
||||
}),
|
||||
[onChange]
|
||||
);
|
||||
|
||||
|
||||
@ -78,7 +78,10 @@ function BaseBytes ({ asHex, children, className = '', defaultValue: { value },
|
||||
: u8aToHex(value as Uint8Array, isDisabled ? 256 : -1)
|
||||
: undefined
|
||||
);
|
||||
const [{ isAddress, isValid, lastValue }, setValidity] = useState<Validity>(() => ({ isAddress: false, isValid: false }));
|
||||
const [{ isAddress, isValid, lastValue }, setValidity] = useState<Validity>(() => ({
|
||||
isAddress: false,
|
||||
isValid: isHex(defaultValue)
|
||||
}));
|
||||
|
||||
const _onChange = useCallback(
|
||||
(hex: string): void => {
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// Copyright 2017-2022 @polkadot/react-params authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { TypeDef } from '@polkadot/types/types';
|
||||
import type { Registry, TypeDef } from '@polkadot/types/types';
|
||||
import type { ParamDef, Props, RawParam } from '../types';
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { Dropdown } from '@polkadot/react-components';
|
||||
import { Enum, getTypeDef } from '@polkadot/types';
|
||||
@ -23,40 +23,61 @@ interface Options {
|
||||
subTypes: TypeDef[];
|
||||
}
|
||||
|
||||
interface Initial {
|
||||
initialEnum: string | null;
|
||||
initialValues: RawParam[] | undefined;
|
||||
}
|
||||
|
||||
function getSubTypes (registry: Registry, type: TypeDef): TypeDef[] {
|
||||
const rawType = registry.createType(type.type as 'u32').toRawType();
|
||||
|
||||
return getTypeDef(rawType).sub as TypeDef[];
|
||||
}
|
||||
|
||||
function getOptions (registry: Registry, type: TypeDef): Options {
|
||||
const subTypes = getSubTypes(registry, type).filter(({ name }) => !!name && !name.startsWith('__Unused'));
|
||||
|
||||
return {
|
||||
options: subTypes.map(({ name }): Option => ({
|
||||
text: name,
|
||||
value: name
|
||||
})),
|
||||
subTypes
|
||||
};
|
||||
}
|
||||
|
||||
function getInitial (defaultValue: RawParam): Initial {
|
||||
return {
|
||||
initialEnum: defaultValue && defaultValue.value
|
||||
? defaultValue.value instanceof Enum
|
||||
? defaultValue.value.type
|
||||
: Object.keys(defaultValue.value as Record<string, unknown>)[0]
|
||||
: null,
|
||||
initialValues: defaultValue && defaultValue.value
|
||||
? defaultValue.value instanceof Enum
|
||||
? [{ isValid: true, value: defaultValue.value.inner }]
|
||||
: undefined
|
||||
: undefined
|
||||
};
|
||||
}
|
||||
|
||||
function getCurrent (registry: Registry, type: TypeDef, defaultValue: RawParam, subTypes: TypeDef[]): ParamDef[] | null {
|
||||
const subs = getSubTypes(registry, type);
|
||||
|
||||
return defaultValue.value instanceof Enum
|
||||
? [{ name: defaultValue.value.type, type: subs[defaultValue.value.index] }]
|
||||
: [{ name: subTypes[0].name, type: subTypes[0] }];
|
||||
}
|
||||
|
||||
function EnumParam (props: Props): React.ReactElement<Props> {
|
||||
const { className = '', defaultValue, isDisabled, isError, label, onChange, overrides, registry, type, withLabel } = props;
|
||||
const [current, setCurrent] = useState<ParamDef[] | null>(null);
|
||||
const [initialValue, setInitialValue] = useState<string | null>(null);
|
||||
const [{ options, subTypes }, setOptions] = useState<Options>({ options: [], subTypes: [] });
|
||||
|
||||
useEffect((): void => {
|
||||
const rawType = registry.createType(type.type as 'u32').toRawType();
|
||||
const typeDef = getTypeDef(rawType);
|
||||
const subTypes = (typeDef.sub as TypeDef[]).filter(({ name }) => !!name && !name.startsWith('__Unused'));
|
||||
|
||||
setOptions({
|
||||
options: subTypes.map(({ name }): Option => ({
|
||||
text: name,
|
||||
value: name
|
||||
})),
|
||||
subTypes
|
||||
});
|
||||
setCurrent([{ name: subTypes[0].name, type: subTypes[0] }]);
|
||||
}, [registry, type]);
|
||||
|
||||
useEffect((): void => {
|
||||
setInitialValue(
|
||||
defaultValue && defaultValue.value
|
||||
? defaultValue.value instanceof Enum
|
||||
? defaultValue.value.type
|
||||
: Object.keys(defaultValue.value as Record<string, unknown>)[0]
|
||||
: null
|
||||
);
|
||||
}, [defaultValue]);
|
||||
const [{ options, subTypes }] = useState<Options>(() => getOptions(registry, type));
|
||||
const [current, setCurrent] = useState<ParamDef[] | null>(() => getCurrent(registry, type, defaultValue, subTypes));
|
||||
const [{ initialEnum, initialValues }] = useState<Initial>(() => getInitial(defaultValue));
|
||||
|
||||
const _onChange = useCallback(
|
||||
(value: string): void => {
|
||||
const newType = subTypes.find(({ name }): boolean => name === value) || null;
|
||||
const newType = subTypes.find(({ name }) => name === value) || null;
|
||||
|
||||
setCurrent(
|
||||
newType
|
||||
@ -85,7 +106,7 @@ function EnumParam (props: Props): React.ReactElement<Props> {
|
||||
<Bare className={className}>
|
||||
<Dropdown
|
||||
className='full'
|
||||
defaultValue={initialValue}
|
||||
defaultValue={initialEnum}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
label={label}
|
||||
@ -100,6 +121,7 @@ function EnumParam (props: Props): React.ReactElement<Props> {
|
||||
overrides={overrides}
|
||||
params={current}
|
||||
registry={registry}
|
||||
values={initialValues}
|
||||
/>
|
||||
)}
|
||||
</Bare>
|
||||
|
||||
@ -3,16 +3,26 @@
|
||||
|
||||
import type { Props, RawParam } from '../types';
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { Struct } from '@polkadot/types';
|
||||
import { isCodec } from '@polkadot/util';
|
||||
|
||||
import Params from '../';
|
||||
import Base from './Base';
|
||||
import Static from './Static';
|
||||
import useParamDefs from './useParamDefs';
|
||||
|
||||
function extractValues ({ isValid, value }: RawParam): RawParam[] | undefined {
|
||||
return (isValid && isCodec(value) && value instanceof Struct)
|
||||
? value.toArray().map((value) => ({ isValid: true, value }))
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function StructParam (props: Props): React.ReactElement<Props> {
|
||||
const params = useParamDefs(props.registry, props.type);
|
||||
const { className = '', isDisabled, label, onChange, overrides, withLabel } = props;
|
||||
const { className = '', defaultValue, isDisabled, label, onChange, overrides, withLabel } = props;
|
||||
const [values] = useState(() => extractValues(defaultValue));
|
||||
|
||||
const _onChangeParams = useCallback(
|
||||
(values: RawParam[]): void => {
|
||||
@ -44,6 +54,7 @@ function StructParam (props: Props): React.ReactElement<Props> {
|
||||
overrides={overrides}
|
||||
params={params}
|
||||
registry={props.registry}
|
||||
values={values}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -3,21 +3,30 @@
|
||||
|
||||
import type { Props, RawParam } from '../types';
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { Tuple } from '@polkadot/types';
|
||||
|
||||
import Params from '../';
|
||||
import Base from './Base';
|
||||
import Static from './Static';
|
||||
import useParamDefs from './useParamDefs';
|
||||
|
||||
function Tuple (props: Props): React.ReactElement<Props> {
|
||||
function getInitialValues ({ value }: RawParam): RawParam[] {
|
||||
return value instanceof Tuple
|
||||
? value.map((value) => ({ isValid: true, value: value as unknown }))
|
||||
: value as RawParam[];
|
||||
}
|
||||
|
||||
function TupleDisplay (props: Props): React.ReactElement<Props> {
|
||||
const params = useParamDefs(props.registry, props.type);
|
||||
const { className = '', isDisabled, label, onChange, overrides, withLabel } = props;
|
||||
const { className = '', defaultValue, isDisabled, label, onChange, overrides, withLabel } = props;
|
||||
const [values] = useState<RawParam[]>(() => getInitialValues(defaultValue));
|
||||
|
||||
const _onChangeParams = useCallback(
|
||||
(values: RawParam[]): void => {
|
||||
onChange && onChange({
|
||||
isValid: values.reduce((result: boolean, { isValid }) => result && isValid, true),
|
||||
isValid: values.reduce<boolean>((result, { isValid }) => result && isValid, true),
|
||||
value: values.map(({ value }) => value)
|
||||
});
|
||||
},
|
||||
@ -40,9 +49,10 @@ function Tuple (props: Props): React.ReactElement<Props> {
|
||||
overrides={overrides}
|
||||
params={params}
|
||||
registry={props.registry}
|
||||
values={values}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Tuple);
|
||||
export default React.memo(TupleDisplay);
|
||||
|
||||
@ -14,32 +14,48 @@ import Params from '../';
|
||||
import Base from './Base';
|
||||
import useParamDefs from './useParamDefs';
|
||||
|
||||
function generateParam ([{ name, type }]: ParamDef[], index: number): ParamDef {
|
||||
function getParam ([{ name, type }]: ParamDef[], index: number): ParamDef {
|
||||
return {
|
||||
name: `${index}: ${name || type.type}`,
|
||||
type
|
||||
};
|
||||
}
|
||||
|
||||
export function getParams (inputParams: ParamDef[], prev: ParamDef[], max: number): ParamDef[] {
|
||||
if (prev.length === max) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
const params: ParamDef[] = [];
|
||||
|
||||
for (let index = 0; index < max; index++) {
|
||||
params.push(getParam(inputParams, index));
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
export function getValues ({ value }: RawParam): RawParam[] {
|
||||
return (value as RawParam[] || []).map((value: RawParam) =>
|
||||
isUndefined(value) || isUndefined(value.isValid)
|
||||
? { isValid: !isUndefined(value), value }
|
||||
: value
|
||||
);
|
||||
}
|
||||
|
||||
function Vector ({ className = '', defaultValue, isDisabled = false, label, onChange, overrides, registry, type, withLabel }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const inputParams = useParamDefs(registry, type);
|
||||
const [count, setCount] = useState(0);
|
||||
const [params, setParams] = useState<ParamDef[]>([]);
|
||||
const [values, setValues] = useState<RawParam[]>([]);
|
||||
const [values, setValues] = useState<RawParam[]>(() => getValues(defaultValue));
|
||||
const [count, setCount] = useState(() => values.length);
|
||||
const [params, setParams] = useState<ParamDef[]>(() => getParams(inputParams, [], count));
|
||||
|
||||
// build up the list of parameters we are using
|
||||
useEffect((): void => {
|
||||
if (inputParams.length) {
|
||||
const max = isDisabled ? (defaultValue.value as RawParam[] || []).length : count;
|
||||
const params: ParamDef[] = [];
|
||||
|
||||
for (let index = 0; index < max; index++) {
|
||||
params.push(generateParam(inputParams, index));
|
||||
}
|
||||
|
||||
setParams(params);
|
||||
}
|
||||
inputParams.length &&
|
||||
setParams((prev) =>
|
||||
getParams(inputParams, prev, isDisabled ? (defaultValue.value as RawParam[] || []).length : count)
|
||||
);
|
||||
}, [count, defaultValue, isDisabled, inputParams]);
|
||||
|
||||
// when !isDisable, generating an input list based on count
|
||||
@ -58,24 +74,12 @@ function Vector ({ className = '', defaultValue, isDisabled = false, label, onCh
|
||||
|
||||
return values.slice(0, count);
|
||||
});
|
||||
}, [count, inputParams, isDisabled, registry]);
|
||||
|
||||
// when isDisabled, set the values based on the defaultValue input
|
||||
useEffect((): void => {
|
||||
isDisabled &&
|
||||
setValues(
|
||||
(defaultValue.value as RawParam[] || []).map((value: RawParam) =>
|
||||
isUndefined(value) || isUndefined(value.isValid)
|
||||
? { isValid: !isUndefined(value), value }
|
||||
: value
|
||||
)
|
||||
);
|
||||
}, [defaultValue, isDisabled]);
|
||||
}, [count, defaultValue, inputParams, isDisabled, registry]);
|
||||
|
||||
// when our values has changed, alert upstream
|
||||
useEffect((): void => {
|
||||
onChange && onChange({
|
||||
isValid: values.reduce((result: boolean, { isValid }) => result && isValid, true),
|
||||
isValid: values.reduce<boolean>((result, { isValid }) => result && isValid, true),
|
||||
value: values.map(({ value }) => value)
|
||||
});
|
||||
}, [values, onChange]);
|
||||
|
||||
@ -5,39 +5,25 @@ import type { ParamDef, Props, RawParam } from '../types';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { VecFixed } from '@polkadot/types';
|
||||
import { isUndefined } from '@polkadot/util';
|
||||
|
||||
import getInitValue from '../initValue';
|
||||
import Params from '../';
|
||||
import Base from './Base';
|
||||
import useParamDefs from './useParamDefs';
|
||||
import { getParams, getValues } from './Vector';
|
||||
|
||||
function generateParam ([{ name, type }]: ParamDef[], index: number): ParamDef {
|
||||
return {
|
||||
name: `${index}: ${name || type.type}`,
|
||||
type
|
||||
};
|
||||
function getInitialValues (defaultValue: RawParam): RawParam[] {
|
||||
return defaultValue.value instanceof VecFixed
|
||||
? defaultValue.value.map((value) => ({ isValid: true, value: value as unknown }))
|
||||
: getValues(defaultValue);
|
||||
}
|
||||
|
||||
function VectorFixed ({ className = '', defaultValue, isDisabled = false, label, onChange, overrides, registry, type, withLabel }: Props): React.ReactElement<Props> | null {
|
||||
const inputParams = useParamDefs(registry, type);
|
||||
const [params, setParams] = useState<ParamDef[]>([]);
|
||||
const [values, setValues] = useState<RawParam[]>([]);
|
||||
|
||||
// build up the list of parameters we are using
|
||||
useEffect((): void => {
|
||||
if (inputParams.length) {
|
||||
const count = (inputParams[0].length || 1);
|
||||
const max = isDisabled ? (defaultValue.value as RawParam[] || []).length : count;
|
||||
const params: ParamDef[] = [];
|
||||
|
||||
for (let index = 0; index < max; index++) {
|
||||
params.push(generateParam(inputParams, index));
|
||||
}
|
||||
|
||||
setParams(params);
|
||||
}
|
||||
}, [defaultValue, isDisabled, inputParams]);
|
||||
const [params] = useState<ParamDef[]>(() => getParams(inputParams, [], (inputParams[0].length || 1)));
|
||||
const [values, setValues] = useState<RawParam[]>(() => getInitialValues(defaultValue));
|
||||
|
||||
// when !isDisable, generating an input list based on count
|
||||
useEffect((): void => {
|
||||
@ -59,18 +45,6 @@ function VectorFixed ({ className = '', defaultValue, isDisabled = false, label,
|
||||
});
|
||||
}, [inputParams, isDisabled, registry]);
|
||||
|
||||
// when isDisabled, set the values based on the defaultValue input
|
||||
useEffect((): void => {
|
||||
isDisabled &&
|
||||
setValues(
|
||||
(defaultValue.value as RawParam[] || []).map((value: RawParam) =>
|
||||
isUndefined(value) || isUndefined(value.isValid)
|
||||
? { isValid: !isUndefined(value), value }
|
||||
: value
|
||||
)
|
||||
);
|
||||
}, [defaultValue, isDisabled]);
|
||||
|
||||
// when our values has changed, alert upstream
|
||||
useEffect((): void => {
|
||||
onChange && onChange({
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
import type { Registry, TypeDef } from '@polkadot/types/types';
|
||||
import type { ParamDef } from '../types';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { createNamedHook } from '@polkadot/react-hooks';
|
||||
import { getTypeDef } from '@polkadot/types/create';
|
||||
@ -19,26 +19,23 @@ function expandDef (registry: Registry, td: TypeDef): TypeDef {
|
||||
}
|
||||
}
|
||||
|
||||
function getDefs (registry: Registry, type: TypeDef): ParamDef[] {
|
||||
const typeDef = expandDef(registry, type);
|
||||
|
||||
return typeDef.sub
|
||||
? (Array.isArray(typeDef.sub) ? typeDef.sub : [typeDef.sub]).map((td): ParamDef => ({
|
||||
length: typeDef.length,
|
||||
name: td.name,
|
||||
type: td
|
||||
}))
|
||||
: [];
|
||||
}
|
||||
|
||||
function useParamDefsImpl (registry: Registry, type: TypeDef): ParamDef[] {
|
||||
const [params, setParams] = useState<ParamDef[]>([]);
|
||||
|
||||
useEffect((): void => {
|
||||
const typeDef = expandDef(registry, type);
|
||||
|
||||
if (!typeDef.sub) {
|
||||
return setParams([]);
|
||||
}
|
||||
|
||||
setParams(
|
||||
(Array.isArray(typeDef.sub) ? typeDef.sub : [typeDef.sub]).map((td): ParamDef => ({
|
||||
length: typeDef.length,
|
||||
name: td.name,
|
||||
type: td // expandDef(td)
|
||||
}))
|
||||
);
|
||||
}, [registry, type]);
|
||||
|
||||
return params;
|
||||
return useMemo(
|
||||
() => getDefs(registry, type),
|
||||
[registry, type]
|
||||
);
|
||||
}
|
||||
|
||||
export default createNamedHook('useParamDefs', useParamDefsImpl);
|
||||
|
||||
@ -50,13 +50,15 @@ class Params extends React.PureComponent<Props, State> {
|
||||
return {
|
||||
params,
|
||||
values: params.reduce(
|
||||
(result: RawParams, param, index): RawParams => [
|
||||
...result,
|
||||
values && values[index]
|
||||
? values[index]
|
||||
: createValue(registry, param)
|
||||
],
|
||||
[]
|
||||
(result: RawParams, param, index): RawParams => {
|
||||
result.push(
|
||||
values && values[index]
|
||||
? values[index]
|
||||
: createValue(registry, param)
|
||||
);
|
||||
|
||||
return result;
|
||||
}, []
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user