diff --git a/packages/page-accounts/src/modals/IdentityMain.tsx b/packages/page-accounts/src/modals/IdentityMain.tsx index 167c91f7df..80a40c4a0a 100644 --- a/packages/page-accounts/src/modals/IdentityMain.tsx +++ b/packages/page-accounts/src/modals/IdentityMain.tsx @@ -2,8 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import type { ApiPromise } from '@polkadot/api'; -import type { Data, Option } from '@polkadot/types'; -import type { IdentityInfo, Registration } from '@polkadot/types/interfaces'; +import type { Bytes, Data, Option, Struct } from '@polkadot/types'; +import type { H160, IdentityInfo, Registration } from '@polkadot/types/interfaces'; +import type { PalletIdentityRegistration } from '@polkadot/types/lookup'; +import type { ITuple } from '@polkadot/types-codec/types'; import React, { useEffect, useState } from 'react'; @@ -22,21 +24,36 @@ interface Props { } interface ValueState { - info: Record; okAll: boolean; okDiscord?: boolean; okDisplay?: boolean; okEmail?: boolean; + okGithub?: boolean; okLegal?: boolean; + okMatrix?: boolean; okRiot?: boolean; okTwitter?: boolean; okWeb?: boolean; } +// With the Identity pallet being transferred to the people chain, the types are also changed. +interface PeopleIdentityInfo extends Struct { + display: Data; + legal: Data; + web: Data; + matrix: Data; + email: Data; + pgpFingerprint: Option; + image: Data; + twitter: Data; + github: Data; + discord: Data; +} + const WHITESPACE = [' ', '\t']; function setData (data: Data, setActive: null | ((isActive: boolean) => void), setVal: (val: string) => void): void { - if (data.isRaw) { + if (data && data.isRaw) { setActive && setActive(true); setVal(u8aToString(data.asRaw.toU8a(true))); } @@ -45,16 +62,24 @@ 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) { + if (value && value.isRaw) { setData(value, setActive, setVal); } return value; } +function setDiscordFieldData (info: PeopleIdentityInfo, setActive: null | ((isActive: boolean) => void), setVal: (val: string) => void): Data { + if (info.discord && !info.discord.isNone) { + setActive && setActive(true); + setVal(u8aToString(info.discord.asRaw.toU8a(true))); + } + + return info.discord; +} + 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 && @@ -69,12 +94,16 @@ function checkValue (hasValue: boolean, value: string | null | undefined, minLen function IdentityMain ({ address, className = '', onClose }: Props): React.ReactElement { const { t } = useTranslation(); - const { api } = useApi(); - const identityOpt = useCall>(api.query.identity.identityOf, [address]); - const [{ info, okAll, okDiscord, okDisplay, okEmail, okLegal, okRiot, okTwitter, okWeb }, setInfo] = useState({ info: {}, okAll: false }); + const { api, specName } = useApi(); + const identityOpt = useCall]>>>(api.query.identity.identityOf, [address]); + const [{ okAll, okDiscord, okDisplay, okEmail, okGithub, okLegal, okMatrix, okRiot, okTwitter, okWeb }, setOkInfo] = useState({ okAll: false }); + const [legacyInfo, setLegacyInfo] = useState>({}); + const [info, setInfo] = useState>({}); const [hasEmail, setHasEmail] = useState(false); const [hasLegal, setHasLegal] = useState(false); const [hasRiot, setHasRiot] = useState(false); + const [hasMatrix, setHasMatrix] = useState(false); + const [hasGithub, setHasGithub] = useState(false); const [hasTwitter, setHasTwitter] = useState(false); const [hasDiscord, setHasDiscord] = useState(false); const [hasWeb, setHasWeb] = useState(false); @@ -82,47 +111,83 @@ function IdentityMain ({ address, className = '', onClose }: Props): React.React const [valEmail, setValEmail] = useState(''); const [valLegal, setValLegal] = useState(''); const [valRiot, setValRiot] = useState(''); + const [valMatrix, setValMatrix] = useState(''); + const [valGithub, setValGithub] = useState(''); const [valTwitter, setValTwitter] = useState(''); const [valDiscord, setValDiscord] = useState(''); const [valWeb, setValWeb] = useState(''); const [gotPreviousIdentity, setGotPreviousIdentity] = useState(false); + // HACK This ensures we can deal with legacy indentity information for chains while + // also giving support for the new identity logic inside of the Peoples chain. + // The new logic is specific to people system parachains. + const [isLegacyIdentity, setIsLegacyIdentity] = useState(!specName.includes('people-')); + const [isPalletChecked, setIsPalletChecked] = useState(false); useEffect((): void => { if (identityOpt && identityOpt.isSome) { - const { info } = identityOpt.unwrap(); + const identity = identityOpt.unwrap(); + const foundInfo = Array.isArray(identity) ? identity[0].info : (identity as Registration).info; - setData(info.display, null, setValDisplay); - setData(info.email, setHasEmail, setValEmail); - setData(info.legal, setHasLegal, setValLegal); - setData(info.riot, setHasRiot, setValRiot); - setData(info.twitter, setHasTwitter, setValTwitter); - const infoDiscord = setAdditionalFieldData(api, info, AddressIdentityOtherDiscordKey, setHasDiscord, setValDiscord); + // Ensure we set the state before ever jumping into the logic below. + if (!isPalletChecked) { + // riot was replaced with matrix, so if we see riot as part of the structure + // we know its part of the legacy identity logic + foundInfo.riot ? setIsLegacyIdentity(true) : setIsLegacyIdentity(false); + setIsPalletChecked(true); + } else { + setData(foundInfo.display, null, setValDisplay); + setData(foundInfo.email, setHasEmail, setValEmail); + setData(foundInfo.legal, setHasLegal, setValLegal); + setData(foundInfo.riot, setHasRiot, setValRiot); + setData(foundInfo.twitter, setHasTwitter, setValTwitter); + setData((foundInfo as unknown as PeopleIdentityInfo).matrix, setHasMatrix, setValMatrix); + setData((foundInfo as unknown as PeopleIdentityInfo).github, setHasGithub, setValGithub); + setData(foundInfo.web, setHasWeb, setValWeb); + const infoDiscord = isLegacyIdentity ? setAdditionalFieldData(api, foundInfo, AddressIdentityOtherDiscordKey, setHasDiscord, setValDiscord) : setDiscordFieldData((foundInfo as unknown as PeopleIdentityInfo), setHasDiscord, setValDiscord); - setData(info.web, setHasWeb, setValWeb); + const previousKeys = isLegacyIdentity + ? [foundInfo.display, foundInfo.email, foundInfo.legal, foundInfo.riot, foundInfo.twitter, infoDiscord, foundInfo.web] + : [foundInfo.display, foundInfo.email, (foundInfo as unknown as PeopleIdentityInfo).github, foundInfo.legal, (foundInfo as unknown as PeopleIdentityInfo).matrix, foundInfo.twitter, infoDiscord, foundInfo.web]; - [info.display, info.email, info.legal, info.riot, info.twitter, infoDiscord, info.web].some((info: Data) => { - if (info.isRaw) { - setGotPreviousIdentity(true); + previousKeys.some((info: Data | null) => { + if (info && info.isRaw) { + setGotPreviousIdentity(true); - return true; - } else { - return false; - } - }); + return true; + } else { + return false; + } + }); + } } - }, [identityOpt, api]); + }, [identityOpt, api, isLegacyIdentity, isPalletChecked]); useEffect((): void => { const okDisplay = checkValue(true, valDisplay, 1, [], [], []); const okEmail = checkValue(hasEmail, valEmail, 3, ['@'], WHITESPACE, []); const okLegal = checkValue(hasLegal, valLegal, 1, [], [], []); + const okMatrix = checkValue(hasMatrix, valMatrix, 6, [':'], WHITESPACE, ['@', '~']); const okRiot = checkValue(hasRiot, valRiot, 6, [':'], WHITESPACE, ['@', '~']); const okTwitter = checkValue(hasTwitter, valTwitter, 3, [], WHITESPACE, ['@']); const okDiscord = checkValue(hasDiscord, valDiscord, 3, [], WHITESPACE, []); + const okGithub = checkValue(hasGithub, valGithub, 3, [], WHITESPACE, []); const okWeb = checkValue(hasWeb, valWeb, 8, ['.'], WHITESPACE, ['https://', 'http://']); - setInfo({ - info: { + setOkInfo({ + okAll: okDisplay && okEmail && okLegal && okRiot && okTwitter && okDiscord && okWeb, + okDiscord, + okDisplay, + okEmail, + okGithub, + okLegal, + okMatrix, + okRiot, + okTwitter, + okWeb + }); + + isLegacyIdentity + ? setLegacyInfo({ additional: [ (okDiscord && hasDiscord ? [{ raw: AddressIdentityOtherDiscordKey }, { raw: valDiscord }] : null) ].filter((item) => !!item), @@ -132,17 +197,18 @@ function IdentityMain ({ address, className = '', onClose }: Props): React.React riot: { [okRiot && hasRiot ? 'raw' : 'none']: okRiot && hasRiot ? valRiot : null }, twitter: { [okTwitter && hasTwitter ? 'raw' : 'none']: okTwitter && hasTwitter ? valTwitter : null }, web: { [okWeb && hasWeb ? 'raw' : 'none']: okWeb && hasWeb ? valWeb : null } - }, - okAll: okDisplay && okEmail && okLegal && okRiot && okTwitter && okDiscord && okWeb, - okDiscord, - okDisplay, - okEmail, - okLegal, - okRiot, - okTwitter, - okWeb - }); - }, [hasEmail, hasLegal, hasRiot, hasTwitter, hasDiscord, hasWeb, valDisplay, valEmail, valLegal, valRiot, valTwitter, valDiscord, valWeb]); + }) + : setInfo({ + discord: { [okDiscord && hasDiscord ? 'raw' : 'none']: okDiscord && hasDiscord ? valDiscord : null }, + display: { [okDisplay ? 'raw' : 'none']: valDisplay || null }, + email: { [okEmail && hasEmail ? 'raw' : 'none']: okEmail && hasEmail ? valEmail : null }, + github: { [okGithub && hasGithub ? 'raw' : 'none']: okGithub && hasGithub ? valGithub : null }, + legal: { [okLegal && hasLegal ? 'raw' : 'none']: okLegal && hasLegal ? valLegal : null }, + matrix: { [okMatrix && hasMatrix ? 'raw' : 'none']: okMatrix && hasMatrix ? valMatrix : null }, + twitter: { [okTwitter && hasTwitter ? 'raw' : 'none']: okTwitter && hasTwitter ? valTwitter : null }, + web: { [okWeb && hasWeb ? 'raw' : 'none']: okWeb && hasWeb ? valWeb : null } + }); + }, [hasEmail, hasLegal, hasRiot, hasTwitter, hasDiscord, hasWeb, hasMatrix, hasGithub, valDisplay, valEmail, valLegal, valRiot, valTwitter, valDiscord, valWeb, valMatrix, valGithub, isLegacyIdentity]); return ( '} /> - - } - maxLength={32} - onChange={setValRiot} - placeholder={t('@yourname:matrix.org')} - value={hasRiot ? valRiot : ''} - /> + { + !isLegacyIdentity + ? ( + + } + maxLength={32} + onChange={setValMatrix} + placeholder={t('@yourname:matrix.org')} + value={hasMatrix ? valMatrix : ''} + /> + ) + : ( + + } + maxLength={32} + onChange={setValRiot} + placeholder={t('@yourname:matrix.org')} + value={hasRiot ? valRiot : ''} + /> + ) + } + { + !isLegacyIdentity + ? ( + + } + maxLength={32} + onChange={setValGithub} + placeholder={t('YourGithubHandle')} + value={hasGithub ? valGithub : ''} + /> + ) + :
+ }