// Copyright 2017-2023 @polkadot/app-extrinsics authors & contributors // SPDX-License-Identifier: Apache-2.0 import type { SubmittableExtrinsic, SubmittableExtrinsicFunction } from '@polkadot/api/types'; import type { ComponentMap, RawParam } from '@polkadot/react-params/types'; import type { TypeDef } from '@polkadot/types/types'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import Params from '@polkadot/react-params'; import { getTypeDef } from '@polkadot/types/create'; import { isUndefined, objectSpread } from '@polkadot/util'; import InputExtrinsic from './InputExtrinsic/index.js'; import paramComponents from './Params/index.js'; import { balanceCalls, balanceCallsOverrides } from './constants.js'; interface Props { className?: string; defaultArgs?: RawParam[]; defaultValue: SubmittableExtrinsicFunction<'promise'>; filter?: (section: string, method?: string) => boolean; isDisabled?: boolean; isError?: boolean; isPrivate?: boolean; label?: React.ReactNode; onChange: (method?: SubmittableExtrinsic<'promise'>) => void; onEnter?: () => void; onError?: (error?: Error | null) => void; onEscape?: () => void; withLabel?: boolean; } interface ParamDef { name: string; type: TypeDef; } interface CallState { extrinsic: { fn: SubmittableExtrinsicFunction<'promise'>; params: ParamDef[]; }, values: RawParam[]; } const allComponents = objectSpread({}, paramComponents, balanceCallsOverrides); 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: { ...getTypeDef(type.toString()), ...(typeName.isSome ? { typeName: typeName.unwrap().toString() } : {} ) } })); } function getCallState (fn: SubmittableExtrinsicFunction<'promise'>, values: RawParam[] = []): CallState { return { extrinsic: { fn, params: getParams(fn) }, values }; } function ExtrinsicDisplay ({ defaultArgs, defaultValue, filter, isDisabled, isError, isPrivate, label, onChange, onEnter, onError, onEscape, withLabel }: Props): React.ReactElement { const [{ extrinsic, values }, setDisplay] = useState(() => getCallState(defaultValue, defaultArgs)); useEffect((): void => { const isValid = isValuesValid(extrinsic.params, values); let method; if (isValid) { try { method = extrinsic.fn(...values.map(({ value }) => value)); } catch (error) { onError && onError(error as Error); } } else { onError && onError(null); } onChange(method); }, [extrinsic, onChange, onError, values]); const overrides = useMemo( () => balanceCalls.includes(`${extrinsic.fn.section}.${extrinsic.fn.method}`) ? allComponents : paramComponents, [extrinsic] ); const _onChangeMethod = useCallback( (fn: SubmittableExtrinsicFunction<'promise'>) => setDisplay((prev): CallState => fn.section === prev.extrinsic.fn.section && fn.method === prev.extrinsic.fn.method ? prev : getCallState(fn) ), [] ); const _setValues = useCallback( (values: RawParam[]) => setDisplay(({ extrinsic }) => ({ extrinsic, values })), [] ); const { fn: { method, section }, params } = extrinsic; return (
); } export default React.memo(ExtrinsicDisplay);