Skip to content

Commit

Permalink
feat: copy address flow changes
Browse files Browse the repository at this point in the history
  • Loading branch information
0xKheops committed Jan 15, 2025
1 parent 85a144a commit a046be0
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 30 deletions.
67 changes: 51 additions & 16 deletions apps/extension/src/ui/domains/CopyAddress/CopyAddressChainForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ChainId } from "@talismn/chaindata-provider"
import { CopyIcon, QrIcon } from "@talismn/icons"
import { isEthereumAddress } from "@talismn/util"
import { SubstrateLedgerAppType } from "extension-core"
Expand All @@ -16,36 +15,65 @@ import { useAccountByAddress, useBalancesByAddress, useChains, useSetting } from
import { AccountIcon } from "../Account/AccountIcon"
import { ChainLogo } from "../Asset/ChainLogo"
import { CopyAddressExchangeWarning } from "./CopyAddressExchangeWarning"
import {
ChainFormat,
CopyAddressFormatPickerDrawer,
isMigratedFormat,
MigratedChainFormat,
} from "./CopyAddressFormatPickerDrawer"
import { CopyAddressLayout } from "./CopyAddressLayout"
import { useCopyAddressWizard } from "./useCopyAddressWizard"

type ChainFormat = {
key: string
chainId: ChainId | null
prefix: number | null
name: string
address: string
}

const ChainFormatButton = ({ format }: { format: ChainFormat }) => {
const { t } = useTranslation()
const { setChainId, copySpecific } = useCopyAddressWizard()
const { open: openWarning, isOpen: isWarningOpen, close: closeWarning } = useOpenClose()

const handleQrClick = useCallback(() => {
setChainId(format.chainId)
}, [format.chainId, setChainId])
const [migratedFormatPicker, setMigratedFormatPicker] = useState<{
format: MigratedChainFormat
mode: "copy" | "qr"
}>()

const { open: openWarning, isOpen: isWarningOpen, close: closeWarning } = useOpenClose()
const handleQrClick = useCallback(() => {
if (isMigratedFormat(format)) setMigratedFormatPicker({ format, mode: "qr" })
else setChainId(format.chainId)
}, [format, setChainId])

const handleCopyClick = useCallback(() => {
if (format.chainId === null && !isEthereumAddress(format.address)) openWarning()
else copySpecific(format.address, format.chainId)
}, [copySpecific, format.address, format.chainId, openWarning])
if (format.chainId === null && !isEthereumAddress(format.address)) {
openWarning()
} else if (isMigratedFormat(format)) {
setMigratedFormatPicker({ format, mode: "copy" })
} else {
copySpecific(format.address, format.chainId)
}
}, [copySpecific, format, openWarning])

const handleWarningContinueClick = useCallback(() => {
copySpecific(format.address, format.chainId)
}, [copySpecific, format.address, format.chainId])

const handleFormatPickerSelect = useCallback(
(legacyFormat: boolean) => {
if (!migratedFormatPicker) return
const { format, mode } = migratedFormatPicker

if (mode === "copy")
copySpecific(
legacyFormat ? format.oldAddress : format.address,
format.chainId,
legacyFormat,
)
if (mode === "qr") {
setChainId(format.chainId, legacyFormat)
}

// close drawer
setMigratedFormatPicker(undefined)
},
[copySpecific, migratedFormatPicker, setChainId],
)

return (
<div className="text-body-secondary hover:text-body hover:bg-grey-800 flex h-32 w-full items-center gap-6 px-12">
{format.chainId ? (
Expand Down Expand Up @@ -91,6 +119,11 @@ const ChainFormatButton = ({ format }: { format: ChainFormat }) => {
onDismiss={closeWarning}
onContinue={handleWarningContinueClick}
/>
<CopyAddressFormatPickerDrawer
format={migratedFormatPicker?.format}
onDismiss={() => setMigratedFormatPicker(undefined)}
onSelect={handleFormatPickerSelect}
/>
</div>
)
}
Expand Down Expand Up @@ -149,8 +182,10 @@ export const CopyAddressChainForm = () => {
key: chain.id,
chainId: chain.id,
prefix: chain.prefix,
oldPrefix: chain.oldPrefix,
name: chain.name ?? "unknown",
address: convertAddress(address, chain.prefix),
oldAddress: chain.oldPrefix ? convertAddress(address, chain.oldPrefix) : undefined,
})),
].filter((f) => !accountChain || accountChain.id === f.chainId)
}, [address, chains, SUBSTRATE_FORMAT, account?.ledgerApp, balancesPerNetwork, accountChain])
Expand Down
38 changes: 36 additions & 2 deletions apps/extension/src/ui/domains/CopyAddress/CopyAddressCopyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,22 @@ export const CopyAddressCopyForm = () => {
logo,
chain,
isLogoLoaded,
legacyFormat,
goToAddressPage,
goToNetworkOrTokenPage,
goToNetworkPage,
} = useCopyAddressWizard()

const isEthereum = useMemo(
() => !chain && formattedAddress && isEthereumAddress(formattedAddress),
[chain, formattedAddress],
)

const isMigratedChain = useMemo(() => {
if (!chain) return false
const { oldPrefix, prefix } = chain
return typeof oldPrefix === "number" && typeof prefix === "number" && oldPrefix !== prefix
}, [chain])

const genesisHash = chain?.genesisHash

const { t } = useTranslation()
Expand All @@ -204,12 +212,20 @@ export const CopyAddressCopyForm = () => {
<div>
<NetworkPillButton
chainId={networkId}
onClick={goToNetworkOrTokenPage}
onClick={goToNetworkPage}
address={formattedAddress}
/>
</div>
</div>
)}
{isMigratedChain && (
<div className="text-body-secondary flex h-16 w-full items-center justify-between">
<div>{t("Format")}</div>
<div>
<FormatIndicator legacyFormat={legacyFormat} />
</div>
</div>
)}
</div>
<div className="flex w-full grow flex-col items-center justify-center gap-12">
<div className="h-[21rem] w-[21rem] rounded-lg bg-[#ffffff] p-8">
Expand Down Expand Up @@ -334,3 +350,21 @@ export const CopyAddressCopyForm = () => {
</CopyAddressLayout>
)
}

const FormatIndicator: FC<{ legacyFormat?: boolean }> = ({ legacyFormat }) => {
const { t } = useTranslation()

return (
<Tooltip>
<TooltipTrigger asChild>
<div className="text-body flex items-center gap-2">
<span>{legacyFormat ? t("Legacy format") : t("New format")}</span>
<InfoIcon />
</div>
</TooltipTrigger>
<TooltipContent>
{t("You may need to use legacy format when sending from some exchanges.")}
</TooltipContent>
</Tooltip>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { InfoIcon } from "@talismn/icons"
import { ChainId } from "extension-core"
import { UNIFIED_ADDRESS_FORMAT_DOCS_URL } from "extension-shared"
import { FC, useCallback, useEffect, useState } from "react"
import { useTranslation } from "react-i18next"
import { Button, Drawer } from "talisman-ui"

import { shortenAddress } from "@talisman/util/shortenAddress"
import { useChain } from "@ui/state"

import { ChainLogo } from "../Asset/ChainLogo"

// should only be used here and in CopyAddressChainForm
export type ChainFormat = {
key: string
chainId: ChainId | null
prefix: number | null
oldPrefix?: number
name: string
address: string
oldAddress?: string
}

export type MigratedChainFormat = {
key: string
chainId: ChainId
prefix: number
oldPrefix: number
name: string
address: string
oldAddress: string
}

export const isMigratedFormat = (format: ChainFormat): format is MigratedChainFormat => {
const { prefix, oldPrefix } = format
return typeof oldPrefix === "number" && typeof prefix === "number" && oldPrefix !== prefix
}

export const CopyAddressFormatPickerDrawer: FC<{
format?: MigratedChainFormat
onDismiss: () => void
onSelect: (legacyFormat: boolean) => void
}> = ({ format, onDismiss, onSelect }) => {
// keep a copy here to be able to keep rendering content while drawer is closing
const [data, setData] = useState<MigratedChainFormat>()

useEffect(() => {
if (format) setData(format)
}, [format])

return (
<Drawer
containerId="copy-address-modal"
isOpen={!!format}
anchor="bottom"
onDismiss={onDismiss}
>
{!!data && <DrawerContent format={data} onSelect={onSelect} />}
</Drawer>
)
}

const DrawerContent: FC<{
format: MigratedChainFormat
onSelect: (legacyFormat: boolean) => void
}> = ({ format, onSelect }) => {
const { t } = useTranslation()
const chain = useChain(format.chainId)

const handleSelect = useCallback(
(legacyFormat: boolean) => () => {
onSelect(legacyFormat)
},
[onSelect],
)

return (
<div className="bg-grey-800 flex w-full flex-col items-center gap-6 rounded-t-xl p-12">
<div className="text-md text-body font-bold">{t("Select Address Format")}</div>
<div className="text-body-secondary text-center text-sm">
{t("Legacy format may be needed when sending from some exchanges.")} <LearnMore />
</div>
<div></div>
<FormatRow
chainId={format.chainId}
chainName={chain?.name ?? t("Unknown network")}
address={format.address}
label={t("New format")}
onSelect={handleSelect(false)}
/>
<FormatRow
chainId={format.chainId}
chainName={chain?.name ?? t("Unknown network")}
address={format.oldAddress}
label={t("Legacy format")}
onSelect={handleSelect(true)}
/>
</div>
)
}

const LearnMore = () => {
const { t } = useTranslation()

const handleClick = () => {
window.open(UNIFIED_ADDRESS_FORMAT_DOCS_URL, "_blank", "nooppener noreferrer")
}

return (
<button
type="button"
className="text-body bg-grey-750 hover:bg-grey-700 inline-flex h-10 items-center gap-2 rounded-full px-3 text-xs"
onClick={handleClick}
>
<InfoIcon />
<span>{t("Learn more")}</span>
</button>
)
}

const FormatRow: FC<{
chainId: ChainId
chainName: string
address: string
label: string
onSelect: () => void
}> = ({ chainId, chainName, address, label, onSelect }) => {
const { t } = useTranslation()

return (
<div className="border-grey-700 flex h-[6.8rem] w-full items-center gap-6 rounded-lg border px-10">
<div className="size-16 shrink-0">
<ChainLogo id={chainId} className="shrink-0 text-xl" />
</div>
<div className="flex grow flex-col gap-2 overflow-hidden">
<div className="flex items-center gap-4 overflow-hidden">
<div className="text-body truncate text-sm">{chainName}</div>
<div className="text-body-inactive text-tiny rounded-xs border-body-inactive shrink-0 border px-2 py-1">
{label}
</div>
</div>
<div className="text-body-secondary text-xs">{shortenAddress(address, 10, 10)}</div>
</div>
<Button primary small onClick={onSelect}>
{t("Select")}
</Button>
</div>
)
}
1 change: 1 addition & 0 deletions apps/extension/src/ui/domains/CopyAddress/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export type CopyAddressWizardInputs = {
networkId?: ChainId | EvmNetworkId | null
address?: Address
qr?: boolean
legacyFormat?: boolean
addresses?: Address[]
}
Loading

0 comments on commit a046be0

Please sign in to comment.