KeyValueArray.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // Copyright 2017-2020 @polkadot/react-components authors & contributors
  2. // This software may be modified and distributed under the terms
  3. // of the Apache-2.0 license. See the LICENSE file for details.
  4. import { KeyValue as Pair } from '@polkadot/types/interfaces';
  5. import { Props as BaseProps, RawParam } from '../types';
  6. import React, { useCallback, useState } from 'react';
  7. import { WithTranslation } from 'react-i18next';
  8. import { Vec } from '@polkadot/types';
  9. import { assert, isHex, u8aToHex, u8aToString } from '@polkadot/util';
  10. import { useTranslation } from '../translate';
  11. import Base from './Base';
  12. import Bytes from './Bytes';
  13. import File from './File';
  14. import { createParam } from './KeyValue';
  15. interface Props extends BaseProps, WithTranslation {}
  16. interface Parsed {
  17. isValid: boolean;
  18. value: [Uint8Array, Uint8Array][];
  19. }
  20. const BYTES_TYPE = {
  21. type: 'Bytes',
  22. info: 0
  23. };
  24. const EMPTY_PLACEHOLDER = 'click to select or drag and drop JSON key/value (hex-encoded) file';
  25. function parseFile (raw: Uint8Array): Parsed {
  26. const json = JSON.parse(u8aToString(raw));
  27. const keys = Object.keys(json);
  28. let isValid = keys.length !== 0;
  29. const value = keys.map((key): [Uint8Array, Uint8Array] => {
  30. const value = json[key];
  31. assert(isHex(key) && isHex(value), `Non-hex key/value pair found in ${key.toString()} => ${value.toString()}`);
  32. const encKey = createParam(key);
  33. const encValue = createParam(value);
  34. isValid = isValid && encKey.isValid && encValue.isValid;
  35. return [encKey.u8a, encValue.u8a];
  36. });
  37. return {
  38. isValid,
  39. value
  40. };
  41. }
  42. function KeyValueArray ({ className, defaultValue, isDisabled, isError, label, onChange, onEnter, onEscape, style, withLabel }: Props): React.ReactElement<Props> {
  43. const { t } = useTranslation();
  44. const [placeholder, setPlaceholder] = useState<string>(t(EMPTY_PLACEHOLDER));
  45. const _onChange = useCallback(
  46. (raw: Uint8Array): void => {
  47. let encoded: Parsed = { isValid: false, value: [] };
  48. try {
  49. encoded = parseFile(raw);
  50. setPlaceholder(t('{{count}} key/value pairs encoded for submission', {
  51. replace: {
  52. count: encoded.value.length
  53. }
  54. }));
  55. } catch (error) {
  56. console.error('Error converting json k/v', error);
  57. setPlaceholder(t(EMPTY_PLACEHOLDER));
  58. }
  59. onChange && onChange(encoded);
  60. },
  61. []
  62. );
  63. if (isDisabled) {
  64. const pairs = defaultValue.value as Vec<Pair>;
  65. return (
  66. <>
  67. <Base
  68. className={className}
  69. label={label}
  70. style={style}
  71. >
  72. <div />
  73. </Base>
  74. <div className='ui--Params'>
  75. {pairs.map(([key, value]): React.ReactNode => {
  76. const keyHex = u8aToHex(key.toU8a(true));
  77. return (
  78. <Bytes
  79. defaultValue={{ value } as unknown as RawParam}
  80. isDisabled
  81. key={keyHex}
  82. label={keyHex}
  83. name={keyHex}
  84. onEnter={onEnter}
  85. onEscape={onEscape}
  86. type={BYTES_TYPE}
  87. />
  88. );
  89. })}
  90. </div>
  91. </>
  92. );
  93. }
  94. return (
  95. <File
  96. className={className}
  97. isDisabled={isDisabled}
  98. isError={isError}
  99. label={label}
  100. onChange={_onChange}
  101. placeholder={placeholder}
  102. style={style}
  103. withLabel={withLabel}
  104. />
  105. );
  106. }
  107. export default React.memo(KeyValueArray);