Params support for non-native AccountId{20, 32} (#8752)
This commit is contained in:
parent
e239e8e1df
commit
b2e8b6444a
@ -18,6 +18,7 @@ import RoboHash from './RoboHash';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
forceIconType?: 'ethereum' | 'substrate';
|
||||
prefix?: IdentityProps['prefix'];
|
||||
size?: number;
|
||||
theme?: IdentityProps['theme'] | 'robohash';
|
||||
@ -32,11 +33,12 @@ function isCodec (value?: AccountId | AccountIndex | Address | string | Uint8Arr
|
||||
return !!(value && (value as AccountId).toHuman);
|
||||
}
|
||||
|
||||
function IdentityIcon ({ className = '', prefix, size = 24, theme, value }: Props): React.ReactElement<Props> {
|
||||
function IdentityIcon ({ className = '', forceIconType, prefix, size = 24, theme, value }: Props): React.ReactElement<Props> {
|
||||
const { isEthereum, specName, systemName } = useApi();
|
||||
const { t } = useTranslation();
|
||||
const { queueAction } = useQueue();
|
||||
const thisTheme = theme || getIdentityTheme(systemName, specName);
|
||||
|
||||
const Custom = thisTheme === 'robohash'
|
||||
? RoboHash
|
||||
: undefined;
|
||||
@ -58,7 +60,7 @@ function IdentityIcon ({ className = '', prefix, size = 24, theme, value }: Prop
|
||||
onCopy={_onCopy}
|
||||
prefix={prefix}
|
||||
size={size}
|
||||
theme={isEthereum ? 'ethereum' : thisTheme as 'substrate'}
|
||||
theme={forceIconType || (isEthereum ? 'ethereum' : thisTheme as 'substrate')}
|
||||
value={isCodec(value) ? value.toString() : value}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -13,6 +13,7 @@ interface Props {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
defaultValue?: string | null;
|
||||
forceIconType?: 'ethereum' | 'substrate';
|
||||
help?: React.ReactNode;
|
||||
isDisabled?: boolean;
|
||||
isError?: boolean;
|
||||
@ -24,7 +25,7 @@ interface Props {
|
||||
onEscape?: () => void;
|
||||
}
|
||||
|
||||
function InputAddressSimple ({ autoFocus, children, className = '', defaultValue, help, isDisabled, isError, isFull, label, noConvert, onChange, onEnter, onEscape }: Props): React.ReactElement<Props> {
|
||||
function InputAddressSimple ({ autoFocus, children, className = '', defaultValue, forceIconType, help, isDisabled, isError, isFull, label, noConvert, onChange, onEnter, onEscape }: Props): React.ReactElement<Props> {
|
||||
const [address, setAddress] = useState<string | null>(defaultValue || null);
|
||||
|
||||
const _onChange = useCallback(
|
||||
@ -43,7 +44,7 @@ function InputAddressSimple ({ autoFocus, children, className = '', defaultValue
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className={`${className} ui--InputAddressSimple`}>
|
||||
<Input
|
||||
autoFocus={autoFocus}
|
||||
defaultValue={defaultValue}
|
||||
@ -60,6 +61,7 @@ function InputAddressSimple ({ autoFocus, children, className = '', defaultValue
|
||||
</Input>
|
||||
<IdentityIcon
|
||||
className='ui--InputAddressSimpleIcon'
|
||||
forceIconType={forceIconType}
|
||||
size={32}
|
||||
value={address}
|
||||
/>
|
||||
|
||||
@ -9,9 +9,7 @@ import { KeyringCtx } from './ctx/Keyring';
|
||||
import { createNamedHook } from './createNamedHook';
|
||||
|
||||
function useAccountsImpl (): Accounts {
|
||||
const { accounts } = useContext(KeyringCtx);
|
||||
|
||||
return accounts;
|
||||
return useContext(KeyringCtx).accounts;
|
||||
}
|
||||
|
||||
export const useAccounts = createNamedHook('useAccounts', useAccountsImpl);
|
||||
|
||||
@ -9,9 +9,7 @@ import { KeyringCtx } from './ctx/Keyring';
|
||||
import { createNamedHook } from './createNamedHook';
|
||||
|
||||
function useAddressesImpl (): Addresses {
|
||||
const { addresses } = useContext(KeyringCtx);
|
||||
|
||||
return addresses;
|
||||
return useContext(KeyringCtx).addresses;
|
||||
}
|
||||
|
||||
export const useAddresses = createNamedHook('useAddresses', useAddressesImpl);
|
||||
|
||||
@ -41,10 +41,13 @@ export default React.memo(styled(Holder)`
|
||||
padding-left: 4rem;
|
||||
}
|
||||
|
||||
.ui--Param-composite .ui--row > .ui--Labelled > label {
|
||||
text-transform: none !important;
|
||||
font: var(--font-mono);
|
||||
font-size: var(--font-size-label);
|
||||
.ui--Param-composite .ui--row,
|
||||
.ui--Param-composite .ui--row .ui--InputAddressSimple {
|
||||
& > .ui--Labelled > label {
|
||||
text-transform: none !important;
|
||||
font: var(--font-mono);
|
||||
font-size: var(--font-size-label);
|
||||
}
|
||||
}
|
||||
|
||||
.ui--row {
|
||||
|
||||
19
packages/react-params/src/Param/BasicAccountId20.tsx
Normal file
19
packages/react-params/src/Param/BasicAccountId20.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2017-2023 @polkadot/react-params authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Props } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import BasicAccountIdBase from './BasicAccountIdBase';
|
||||
|
||||
function BasicAccountId20 (props: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<BasicAccountIdBase
|
||||
{...props}
|
||||
bytesLength={20}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(BasicAccountId20);
|
||||
19
packages/react-params/src/Param/BasicAccountId32.tsx
Normal file
19
packages/react-params/src/Param/BasicAccountId32.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2017-2023 @polkadot/react-params authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Props } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import BasicAccountIdBase from './BasicAccountIdBase';
|
||||
|
||||
function BasicAccountId32 (props: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<BasicAccountIdBase
|
||||
{...props}
|
||||
bytesLength={32}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(BasicAccountId32);
|
||||
70
packages/react-params/src/Param/BasicAccountIdBase.tsx
Normal file
70
packages/react-params/src/Param/BasicAccountIdBase.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright 2017-2023 @polkadot/react-params authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Props as BaseProps } from '../types';
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { InputAddressSimple } from '@polkadot/react-components';
|
||||
import { useApi } from '@polkadot/react-hooks';
|
||||
import { isEthereumAddress, validateAddress } from '@polkadot/util-crypto';
|
||||
|
||||
import Bare from './Bare';
|
||||
|
||||
interface Props extends BaseProps {
|
||||
bytesLength: 20 | 32;
|
||||
}
|
||||
|
||||
function isValidAddress (value: string | null | undefined, isEthereum: boolean): boolean {
|
||||
if (value) {
|
||||
try {
|
||||
if (isEthereum) {
|
||||
return isEthereumAddress(value);
|
||||
} else {
|
||||
return validateAddress(value);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function BasicAccountIdBase (props: Props): React.ReactElement<Props> {
|
||||
const { isEthereum } = useApi();
|
||||
const { bytesLength, className = '', defaultValue: { value }, isDisabled, isError, isInOption, label, onChange, withLabel } = props;
|
||||
const [defaultValue] = useState(() => (value as string)?.toString());
|
||||
|
||||
const _onChange = useCallback(
|
||||
(value?: string | null) =>
|
||||
onChange && onChange({
|
||||
isValid: isValidAddress(value, bytesLength === 20),
|
||||
value
|
||||
}),
|
||||
[bytesLength, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<Bare className={className}>
|
||||
<InputAddressSimple
|
||||
className='full'
|
||||
defaultValue={defaultValue}
|
||||
forceIconType={bytesLength === 20 ? 'ethereum' : 'substrate'}
|
||||
hideAddress={isInOption}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
isInput
|
||||
label={label}
|
||||
noConvert
|
||||
onChange={_onChange}
|
||||
placeholder={isEthereum ? '5...' : '0x1...'}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
</Bare>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(BasicAccountIdBase);
|
||||
@ -11,6 +11,8 @@ import { isBn } from '@polkadot/util';
|
||||
import Account from './Account';
|
||||
import Amount from './Amount';
|
||||
import Balance from './Balance';
|
||||
import AccountId20 from './BasicAccountId20';
|
||||
import AccountId32 from './BasicAccountId32';
|
||||
import Bool from './Bool';
|
||||
import Bytes from './Bytes';
|
||||
import Call from './Call';
|
||||
@ -43,12 +45,12 @@ interface TypeToComponent {
|
||||
t: string[];
|
||||
}
|
||||
|
||||
const SPECIAL_TYPES = ['AccountId', 'AccountId32', 'AccountIndex', 'Address', 'Balance', 'BalanceOf', 'Vec<KeyValue>'];
|
||||
const SPECIAL_TYPES = ['AccountId', 'AccountId20', 'AccountId32', 'AccountIndex', 'Address', 'Balance', 'BalanceOf', 'Vec<KeyValue>'];
|
||||
|
||||
const DISPATCH_ERROR = ['DispatchError', 'SpRuntimeDispatchError'];
|
||||
|
||||
const componentDef: TypeToComponent[] = [
|
||||
{ c: Account, t: ['AccountId', 'AccountId32', 'Address', 'LookupSource', 'MultiAddress'] },
|
||||
{ c: Account, t: ['AccountId', 'Address', 'LookupSource', 'MultiAddress'] },
|
||||
{ c: Amount, t: ['AccountIndex', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128', 'u256'] },
|
||||
{ c: Balance, t: ['Amount', 'Balance', 'BalanceOf'] },
|
||||
{ c: Bool, t: ['bool'] },
|
||||
@ -142,6 +144,20 @@ function fromDef ({ displayName, info, lookupName, sub, type }: TypeDef): string
|
||||
}
|
||||
|
||||
export default function findComponent (registry: Registry, def: TypeDef, overrides: ComponentMap = {}): React.ComponentType<Props> {
|
||||
// Explicit/special handling for Account20/32 types where they don't match
|
||||
// the actual chain we are connected to
|
||||
if (['AccountId20', 'AccountId32'].includes(def.type)) {
|
||||
const defType = `AccountId${registry.createType('AccountId').length}`;
|
||||
|
||||
if (def.type !== defType) {
|
||||
if (def.type === 'AccountId20') {
|
||||
return AccountId20;
|
||||
} else {
|
||||
return AccountId32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const findOne = (type?: string): React.ComponentType<Props> | null =>
|
||||
type
|
||||
? overrides[type] || components[type]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user