diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index 6156023..1e28f10 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -26,6 +26,7 @@ jobs: project_name: "crosschain-ui" script_run: false dist_path: . + enable_notify_comment: true enable_notify_slack: true slack_channel: public-darwinia-websites-apps slack_webhook: ${{ secrets.SLACK_INCOMING_WEBHOOK_URL }} diff --git a/.github/workflows/deploy-stg.yml b/.github/workflows/deploy-stg.yml index 2ee6ee3..4496360 100644 --- a/.github/workflows/deploy-stg.yml +++ b/.github/workflows/deploy-stg.yml @@ -27,6 +27,7 @@ jobs: project_name: "crosschain-ui" script_run: false dist_path: . + enable_notify_comment: true enable_notify_slack: true slack_channel: public-darwinia-websites-apps slack_webhook: ${{ secrets.SLACK_INCOMING_WEBHOOK_URL }} diff --git a/src/components/address-input.tsx b/src/components/address-input.tsx index d897796..fb5d900 100644 --- a/src/components/address-input.tsx +++ b/src/components/address-input.tsx @@ -2,9 +2,11 @@ import InputSelect from "@/ui/input-select"; import { ChangeEventHandler, useCallback, useEffect, useMemo, useRef } from "react"; import ConnectWallet from "./connect-wallet"; import { WalletAccount } from "@talismn/connect-wallets"; -import { isValidAddress } from "@/utils"; -import { useTransfer } from "@/hooks"; +import { isValidAddress, toShortAdrress } from "@/utils"; +import { useAccountName, useTransfer } from "@/hooks"; import AddressIdenticon from "./address-identicon"; +import { ApiPromise } from "@polkadot/api"; +import { AddressType } from "@/types"; interface Value { name?: string; @@ -14,6 +16,7 @@ interface Value { interface Props { canInput?: boolean; + api: ApiPromise | undefined; who: "sender" | "recipient"; value?: Value; options?: Pick[]; @@ -25,6 +28,7 @@ interface Props { } export default function AddressInput({ + api, who, value, options, @@ -78,26 +82,33 @@ export default function AddressInput({ childClassName="flex flex-col py-middle bg-component border-primary border border-radius max-h-60 overflow-y-auto" placeholder={placeholder ?? "Address"} value={value?.address ?? ""} // Keep it as a controlled component + inputChildren={ + value?.name ? ( +
+ {value.name} +  ({toShortAdrress(value.address)}) +
+ ) : undefined + } alert={value?.valid === false ? "* Unavailable address" : ""} onClear={handleClear} onChange={handleChange} > {options?.length ? ( options.map((account, index) => ( - + /> )) ) : (
@@ -107,3 +118,42 @@ export default function AddressInput({ ); } + +function Option({ + api, + account, + disabled, + addressType, + onClick, + onChange, +}: { + account: Pick; + api: ApiPromise | undefined; + disabled?: boolean; + addressType: AddressType; + onClick: () => void; + onChange: (value: Value | undefined) => void; +}) { + const name = useAccountName(account.address, api); + + return ( + + ); +} diff --git a/src/components/transfer.tsx b/src/components/transfer.tsx index f7d6ae1..6effe74 100644 --- a/src/components/transfer.tsx +++ b/src/components/transfer.tsx @@ -26,6 +26,8 @@ const { export default function Transfer() { const { + sourceApi, + targetApi, assetLimit, targetAssetDetails, sender, @@ -243,6 +245,7 @@ export default function Transfer() { { + const known = KNOWN.find(([address]) => address.eq(accountAddress)); + if (known) { + return known[1]; + } + return defaultName || accountAddress; +}; + +const extractIdentity = (cacheAddress: string, identity: DeriveAccountRegistration) => { + const judgements = identity.judgements.filter(([, judgement]) => !judgement.isFeePaid); + const isGood = judgements.some(([, judgement]) => judgement.isKnownGood || judgement.isReasonable); + // const isBad = judgements.some(([, judgement]) => judgement.isErroneous || judgement.isLowQuality); + const displayName = isGood ? identity.display : (identity.display || "").replace(/[^\x20-\x7E]/g, ""); + const displayParent = + identity.displayParent && (isGood ? identity.displayParent : identity.displayParent.replace(/[^\x20-\x7E]/g, "")); + return displayParent || displayName || cacheAddress; +}; + +export const useAccountName = (address: string, api: ApiPromise | undefined) => { + const [name, setName] = useState(address); + + useEffect(() => { + let sub$$: Subscription | undefined; + + if (api) { + sub$$ = from(api.derive.accounts.info(address)).subscribe({ + next: ({ nickname, identity }) => { + if (typeof api.query.identity?.identityOf === "function") { + setName(identity.display ? extractIdentity(address, identity) : extractName(address)); + } else if (nickname) { + setName(nickname); + } else { + setName(extractName(address)); + } + }, + error: console.error, + }); + } + + return () => { + sub$$?.unsubscribe(); + }; + }, [address, api]); + + return name; +}; diff --git a/src/providers/transfer-provider.tsx b/src/providers/transfer-provider.tsx index 0089fba..510f5f0 100644 --- a/src/providers/transfer-provider.tsx +++ b/src/providers/transfer-provider.tsx @@ -11,6 +11,7 @@ import { WalletAccount } from "@talismn/connect-wallets"; import { Signer } from "@polkadot/api/types"; import { notifyError, notifyTransaction, parseCross, signAndSendExtrinsic } from "@/utils"; import { useApi, useAssetDetails, useAssetLimit, useBalance } from "@/hooks"; +import { ApiPromise } from "@polkadot/api"; interface TransferCtx { assetLimit: BN | undefined; @@ -30,6 +31,8 @@ interface TransferCtx { activeRecipientAccount: WalletAccount | undefined; activeSenderWallet: WalletID | undefined; activeRecipientWallet: WalletID | undefined; + sourceApi: ApiPromise | undefined; + targetApi: ApiPromise | undefined; setTransferAmount: Dispatch>; setSourceChain: Dispatch>; @@ -81,6 +84,8 @@ const defaultValue: TransferCtx = { activeRecipientAccount: undefined, activeSenderWallet: undefined, activeRecipientWallet: undefined, + sourceApi: undefined, + targetApi: undefined, setTransferAmount: () => undefined, setSourceChain: () => undefined, @@ -195,6 +200,8 @@ export default function TransferProvider({ children }: PropsWithChildren void; onChange?: ChangeEventHandler; } @@ -51,6 +52,7 @@ export default function InputSelect({ placement, canInput, sameWidth, + inputChildren, wrapClassName, inputClassName, childClassName, @@ -113,6 +115,7 @@ export default function InputSelect({ disabled={disabled} onChange={onChange} readOnly={!canInput} + inputChildren={inputChildren} /> {suffix === true ? (
diff --git a/src/ui/input.tsx b/src/ui/input.tsx index 865f071..708fe72 100644 --- a/src/ui/input.tsx +++ b/src/ui/input.tsx @@ -1,12 +1,23 @@ import { InputHTMLAttributes, forwardRef } from "react"; -interface Props {} +interface Props { + inputChildren?: JSX.Element; +} export default forwardRef & Props>(function Input( - { className, ...rest }, + { className, inputChildren, ...rest }, ref, ) { return ( - +
+ + {inputChildren &&
{inputChildren}
} +
); });