feat: confirm desktop wallet address
Some checks failed
Deploy Staging (terry-wallet-login) / deploy (push) Failing after 32s
Some checks failed
Deploy Staging (terry-wallet-login) / deploy (push) Failing after 32s
This commit is contained in:
@@ -16,6 +16,10 @@ function supportsDirectPull(kind: WalletKind): boolean {
|
||||
return kind === "tokenPocket" || kind === "imToken";
|
||||
}
|
||||
|
||||
function supportsDesktopExtension(kind: WalletKind): boolean {
|
||||
return kind === "tokenPocket";
|
||||
}
|
||||
|
||||
function buildAutoLoginDappUrl(kind: WalletKind): string {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set(AUTO_LOGIN_PARAM, kind);
|
||||
@@ -26,6 +30,11 @@ const wallets: WalletKind[] = ["tokenPocket", "imToken"];
|
||||
|
||||
type LoginState = "idle" | "connecting";
|
||||
|
||||
type PendingLogin = {
|
||||
kind: WalletKind;
|
||||
address: string;
|
||||
};
|
||||
|
||||
type Translate = (key: string) => string;
|
||||
|
||||
function walletErrorMessage(error: unknown, t: Translate): string {
|
||||
@@ -47,6 +56,7 @@ export function WalletLoginModal() {
|
||||
const [mobileDevice, setMobileDevice] = useState(false);
|
||||
const [state, setState] = useState<LoginState>("idle");
|
||||
const [error, setError] = useState("");
|
||||
const [pendingLogin, setPendingLogin] = useState<PendingLogin | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loginModalOpen) return;
|
||||
@@ -54,13 +64,14 @@ export function WalletLoginModal() {
|
||||
setSelected(null);
|
||||
setState("idle");
|
||||
setError("");
|
||||
setPendingLogin(null);
|
||||
}, [loginModalOpen]);
|
||||
|
||||
if (!loginModalOpen) return null;
|
||||
|
||||
const walletName = (kind: WalletKind) => t(walletNameKey(kind));
|
||||
const walletText = (key: string, kind: WalletKind) =>
|
||||
t(key).replace("{wallet}", walletName(kind));
|
||||
t(key).replaceAll("{wallet}", walletName(kind));
|
||||
const walletHint = () =>
|
||||
mobileDevice ? t("walletChooseMobile") : t("walletDesktopHint");
|
||||
const busy = state !== "idle";
|
||||
@@ -70,27 +81,49 @@ export function WalletLoginModal() {
|
||||
setSelected(null);
|
||||
setState("idle");
|
||||
setError("");
|
||||
setPendingLogin(null);
|
||||
};
|
||||
|
||||
const selectWallet = (kind: WalletKind) => {
|
||||
setSelected(kind);
|
||||
setError("");
|
||||
setPendingLogin(null);
|
||||
};
|
||||
|
||||
const loginInjected = async (kind: WalletKind) => {
|
||||
setSelected(kind);
|
||||
setState("connecting");
|
||||
setError("");
|
||||
setPendingLogin(null);
|
||||
try {
|
||||
const address = await connectInjectedWallet(kind);
|
||||
completeLogin(localWalletToken(address), address);
|
||||
setPendingLogin({ kind, address });
|
||||
setState("idle");
|
||||
} catch (err) {
|
||||
setState("idle");
|
||||
setError(walletErrorMessage(err, t));
|
||||
}
|
||||
};
|
||||
|
||||
const confirmPendingLogin = () => {
|
||||
if (!pendingLogin) return;
|
||||
completeLogin(localWalletToken(pendingLogin.address), pendingLogin.address);
|
||||
};
|
||||
|
||||
const cancelPendingLogin = () => {
|
||||
setPendingLogin(null);
|
||||
setSelected(null);
|
||||
setState("idle");
|
||||
setError("");
|
||||
};
|
||||
|
||||
const openWalletAppDirect = (kind: WalletKind) => {
|
||||
if (!mobileDevice && !supportsDesktopExtension(kind)) {
|
||||
setSelected(kind);
|
||||
setPendingLogin(null);
|
||||
setError("");
|
||||
return;
|
||||
}
|
||||
if (getInjectedWallet(kind)) {
|
||||
void loginInjected(kind);
|
||||
return;
|
||||
@@ -103,6 +136,7 @@ export function WalletLoginModal() {
|
||||
return;
|
||||
}
|
||||
setSelected(kind);
|
||||
setPendingLogin(null);
|
||||
setError(walletText("walletInstallSelected", kind));
|
||||
};
|
||||
|
||||
@@ -192,31 +226,82 @@ export function WalletLoginModal() {
|
||||
|
||||
{!mobileDevice && active ? (
|
||||
<div className="mt-3 rounded-2xl border border-white/10 bg-black/20 p-3 text-sm text-neutral-300">
|
||||
<p className="font-semibold text-neutral-100">
|
||||
{walletText("walletDesktopHelpTitle", kind)}
|
||||
</p>
|
||||
<ol className="mt-2 list-decimal space-y-1 pl-5 leading-6 text-neutral-400">
|
||||
<li>{t("walletDesktopHelpUnlock")}</li>
|
||||
<li>{t("walletDesktopHelpSelect")}</li>
|
||||
<li>{walletText("walletDesktopHelpRetry", kind)}</li>
|
||||
</ol>
|
||||
<div className="mt-3 grid gap-2 sm:grid-cols-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openWalletAppDirect(kind)}
|
||||
disabled={busy}
|
||||
className="rounded-full bg-ark-gold px-3 py-2 text-sm font-bold text-black transition hover:bg-ark-gold2 disabled:cursor-wait disabled:opacity-70"
|
||||
>
|
||||
{walletText("walletReconnectWallet", kind)}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openWalletInstall(kind)}
|
||||
className="rounded-full border border-ark-gold/50 px-3 py-2 text-sm font-semibold text-ark-gold transition hover:bg-ark-gold/10"
|
||||
>
|
||||
{walletText("walletInstallWallet", kind)}
|
||||
</button>
|
||||
</div>
|
||||
{pendingLogin?.kind === kind ? (
|
||||
<>
|
||||
<p className="font-semibold text-neutral-100">
|
||||
{walletText("walletConfirmAddressTitle", kind)}
|
||||
</p>
|
||||
<p className="mt-2 text-xs leading-5 text-neutral-400">
|
||||
{walletText("walletConfirmAddressDesc", kind)}
|
||||
</p>
|
||||
<p
|
||||
className="mt-3 break-all rounded-xl border border-ark-gold/30 bg-ark-gold/10 px-3 py-2 font-mono text-sm text-ark-gold"
|
||||
title={pendingLogin.address}
|
||||
>
|
||||
{pendingLogin.address}
|
||||
</p>
|
||||
<div className="mt-3 grid gap-2 sm:grid-cols-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={confirmPendingLogin}
|
||||
className="rounded-full bg-ark-gold px-3 py-2 text-sm font-bold text-black transition hover:bg-ark-gold2"
|
||||
>
|
||||
{t("walletConfirmLogin")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={cancelPendingLogin}
|
||||
className="rounded-full border border-white/20 px-3 py-2 text-sm font-semibold text-neutral-200 transition hover:border-ark-gold/50 hover:text-ark-gold"
|
||||
>
|
||||
{t("walletCancelLogin")}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : supportsDesktopExtension(kind) ? (
|
||||
<>
|
||||
<p className="font-semibold text-neutral-100">
|
||||
{walletText("walletDesktopHelpTitle", kind)}
|
||||
</p>
|
||||
<ol className="mt-2 list-decimal space-y-1 pl-5 leading-6 text-neutral-400">
|
||||
<li>{t("walletDesktopHelpUnlock")}</li>
|
||||
<li>{t("walletDesktopHelpSelect")}</li>
|
||||
<li>{walletText("walletDesktopHelpRetry", kind)}</li>
|
||||
</ol>
|
||||
<div className="mt-3 grid gap-2 sm:grid-cols-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openWalletAppDirect(kind)}
|
||||
disabled={busy}
|
||||
className="rounded-full bg-ark-gold px-3 py-2 text-sm font-bold text-black transition hover:bg-ark-gold2 disabled:cursor-wait disabled:opacity-70"
|
||||
>
|
||||
{walletText("walletReconnectWallet", kind)}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openWalletInstall(kind)}
|
||||
className="rounded-full border border-ark-gold/50 px-3 py-2 text-sm font-semibold text-ark-gold transition hover:bg-ark-gold/10"
|
||||
>
|
||||
{walletText("walletInstallWallet", kind)}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="font-semibold text-neutral-100">
|
||||
{walletText("walletDesktopImTokenTitle", kind)}
|
||||
</p>
|
||||
<p className="mt-2 text-sm leading-6 text-neutral-400">
|
||||
{walletText("walletDesktopImTokenDesc", kind)}
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openWalletInstall(kind)}
|
||||
className="mt-3 w-full rounded-full border border-ark-gold/50 px-3 py-2 text-sm font-semibold text-ark-gold transition hover:bg-ark-gold/10"
|
||||
>
|
||||
{walletText("walletDownloadApp", kind)}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@ export function openWalletDeepLink(kind: WalletKind): void {
|
||||
}
|
||||
|
||||
const downloadUrls: Record<WalletKind, string> = {
|
||||
tokenPocket: "https://www.tokenpocket.pro/en/download/app",
|
||||
tokenPocket: "https://extension.tokenpocket.pro/",
|
||||
metaMask: "https://metamask.io/download/",
|
||||
imToken: "https://token.im/download",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user