diff --git a/packages/apps/public/locales/en/app-accounts.json b/packages/apps/public/locales/en/app-accounts.json index 795d2f8659..8f5a21bf67 100644 --- a/packages/apps/public/locales/en/app-accounts.json +++ b/packages/apps/public/locales/en/app-accounts.json @@ -212,6 +212,7 @@ "You should not setup proxies to act as a self-proxy.": "You should not setup proxies to act as a self-proxy.", "You will remove any delegation made by this acccount": "You will remove any delegation made by this acccount", "Your password contains at least one \"/\" character. Disregard this warning if it is intended.": "Your password contains at least one \"/\" character. Disregard this warning if it is intended.", + "YourDiscordHandle": "YourDiscordHandle", "account forgotten": "account forgotten", "account name": "account name", "account restored": "account restored", @@ -250,6 +251,7 @@ "derive root account": "derive root account", "development": "development", "development seed": "development seed", + "discord": "discord", "display name": "display name", "email": "email", "ethereum private key": "ethereum private key", diff --git a/packages/apps/public/locales/en/translation.json b/packages/apps/public/locales/en/translation.json index 7fb5a94016..15f890360b 100644 --- a/packages/apps/public/locales/en/translation.json +++ b/packages/apps/public/locales/en/translation.json @@ -204,6 +204,7 @@ "Yes, allow nominations": "", "You need to sign an attestation for the following account:": "", "You need to sign an attestation for the following accounts:": "", + "YourDiscordHandle": "", "account": "", "account type": "", "accounts": "", diff --git a/packages/page-accounts/src/modals/IdentityMain.tsx b/packages/page-accounts/src/modals/IdentityMain.tsx index 6d84250d17..be8a930a6a 100644 --- a/packages/page-accounts/src/modals/IdentityMain.tsx +++ b/packages/page-accounts/src/modals/IdentityMain.tsx @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import type { Data, Option } from '@polkadot/types'; -import type { Registration } from '@polkadot/types/interfaces'; +import type { IdentityInfo, Registration } from '@polkadot/types/interfaces'; import React, { useEffect, useState } from 'react'; +import { ApiPromise } from '@polkadot/api'; import { Input, InputBalance, Modal, Toggle, TxButton } from '@polkadot/react-components'; import { getAddressMeta } from '@polkadot/react-components/util'; import { useApi, useCall } from '@polkadot/react-hooks'; @@ -22,6 +23,7 @@ interface Props { interface ValueState { info: Record; okAll: boolean; + okDiscord?: boolean; okDisplay?: boolean; okEmail?: boolean; okLegal?: boolean; @@ -31,6 +33,7 @@ interface ValueState { } const WHITESPACE = [' ', '\t']; +const ADDITIONAL_FIELD_DISCORD = 'Discord'; function setData (data: Data, setActive: null | ((isActive: boolean) => void), setVal: (val: string) => void): void { if (data.isRaw) { @@ -39,6 +42,19 @@ function setData (data: Data, setActive: null | ((isActive: boolean) => void), s } } +function setAdditionalFieldData (api: ApiPromise, info: IdentityInfo, key: string, setActive: null | ((isActive: boolean) => void), setVal: (val: string) => void): Data { + const dataKey = api.registry.createType('Data', api.registry.createType('Data', { Raw: key })); + const dataNone = api.registry.createType('Data', ''); + + const value = info.additional.find((v) => v[0].eq(dataKey))?.[1] || dataNone; + + if (value.isRaw) { + setData(value, setActive, setVal); + } + + return value; +} + function checkValue (hasValue: boolean, value: string | null | undefined, minLength: number, includes: string[], excludes: string[], starting: string[], notStarting: string[] = WHITESPACE, notEnding: string[] = WHITESPACE): boolean { return !hasValue || ( !!value && @@ -55,17 +71,19 @@ function IdentityMain ({ address, className = '', onClose }: Props): React.React const { t } = useTranslation(); const { api } = useApi(); const identityOpt = useCall>(api.query.identity.identityOf, [address]); - const [{ info, okAll, okDisplay, okEmail, okLegal, okRiot, okTwitter, okWeb }, setInfo] = useState({ info: {}, okAll: false }); + const [{ info, okAll, okDiscord, okDisplay, okEmail, okLegal, okRiot, okTwitter, okWeb }, setInfo] = useState({ info: {}, okAll: false }); const [hasEmail, setHasEmail] = useState(false); const [hasLegal, setHasLegal] = useState(false); const [hasRiot, setHasRiot] = useState(false); const [hasTwitter, setHasTwitter] = useState(false); + const [hasDiscord, setHasDiscord] = useState(false); const [hasWeb, setHasWeb] = useState(false); const [valDisplay, setValDisplay] = useState(() => (getAddressMeta(address).name || '').replace(/\(.*\)/, '').trim()); const [valEmail, setValEmail] = useState(''); const [valLegal, setValLegal] = useState(''); const [valRiot, setValRiot] = useState(''); const [valTwitter, setValTwitter] = useState(''); + const [valDiscord, setValDiscord] = useState(''); const [valWeb, setValWeb] = useState(''); const [gotPreviousIdentity, setGotPreviousIdentity] = useState(false); @@ -78,9 +96,11 @@ function IdentityMain ({ address, className = '', onClose }: Props): React.React setData(info.legal, setHasLegal, setValLegal); setData(info.riot, setHasRiot, setValRiot); setData(info.twitter, setHasTwitter, setValTwitter); + const infoDiscord = setAdditionalFieldData(api, info, ADDITIONAL_FIELD_DISCORD, setHasDiscord, setValDiscord); + setData(info.web, setHasWeb, setValWeb); - [info.display, info.email, info.legal, info.riot, info.twitter, info.web].some((info: Data) => { + [info.display, info.email, info.legal, info.riot, info.twitter, infoDiscord, info.web].some((info: Data) => { if (info.isRaw) { setGotPreviousIdentity(true); @@ -90,7 +110,7 @@ function IdentityMain ({ address, className = '', onClose }: Props): React.React } }); } - }, [identityOpt]); + }, [identityOpt, api]); useEffect((): void => { const okDisplay = checkValue(true, valDisplay, 1, [], [], []); @@ -98,10 +118,14 @@ function IdentityMain ({ address, className = '', onClose }: Props): React.React const okLegal = checkValue(hasLegal, valLegal, 1, [], [], []); const okRiot = checkValue(hasRiot, valRiot, 6, [':'], WHITESPACE, ['@', '~']); const okTwitter = checkValue(hasTwitter, valTwitter, 3, [], WHITESPACE, ['@']); + const okDiscord = checkValue(hasDiscord, valDiscord, 3, [], WHITESPACE, []); const okWeb = checkValue(hasWeb, valWeb, 8, ['.'], WHITESPACE, ['https://', 'http://']); setInfo({ info: { + additional: [ + (okDiscord && hasDiscord ? [{ raw: ADDITIONAL_FIELD_DISCORD }, { raw: valDiscord }] : null) + ].filter((item) => !!item), display: { [okDisplay ? 'raw' : 'none']: valDisplay || null }, email: { [okEmail && hasEmail ? 'raw' : 'none']: okEmail && hasEmail ? valEmail : null }, legal: { [okLegal && hasLegal ? 'raw' : 'none']: okLegal && hasLegal ? valLegal : null }, @@ -109,7 +133,8 @@ function IdentityMain ({ address, className = '', onClose }: Props): React.React twitter: { [okTwitter && hasTwitter ? 'raw' : 'none']: okTwitter && hasTwitter ? valTwitter : null }, web: { [okWeb && hasWeb ? 'raw' : 'none']: okWeb && hasWeb ? valWeb : null } }, - okAll: okDisplay && okEmail && okLegal && okRiot && okTwitter && okWeb, + okAll: okDisplay && okEmail && okLegal && okRiot && okTwitter && okDiscord && okWeb, + okDiscord, okDisplay, okEmail, okLegal, @@ -117,7 +142,7 @@ function IdentityMain ({ address, className = '', onClose }: Props): React.React okTwitter, okWeb }); - }, [hasEmail, hasLegal, hasRiot, hasTwitter, hasWeb, valDisplay, valEmail, valLegal, valRiot, valTwitter, valWeb]); + }, [hasEmail, hasLegal, hasRiot, hasTwitter, hasDiscord, hasWeb, valDisplay, valEmail, valLegal, valRiot, valTwitter, valDiscord, valWeb]); return ( ('@YourTwitterName')} value={hasTwitter ? valTwitter : ''} /> + ('discord')} + labelExtra={ + ('include field')} + onChange={setHasDiscord} + value={hasDiscord} + /> + } + onChange={setValDiscord} + placeholder={t('YourDiscordHandle')} + value={hasDiscord ? valDiscord : ''} + />