terry-wallet-login #15
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: "Remove wallet address verification popup — Quick Fix"
|
||||||
|
type: quick-fix
|
||||||
|
date: 2026-06-04
|
||||||
|
---
|
||||||
|
|
||||||
|
# Remove wallet address verification popup — Quick Fix
|
||||||
|
|
||||||
|
## Bug
|
||||||
|
|
||||||
|
The in-app-browser wallet address verification popup added friction and interfered with imToken login. The requested behavior is to remove that popup and keep the deeplink login flow direct.
|
||||||
|
|
||||||
|
## Root Cause
|
||||||
|
|
||||||
|
`AutoInjectedLogin` rendered a blocking confirmation dialog for `?autoLogin=` deeplink sessions before calling the injected wallet signature flow. That UI was unnecessary for the current wallet-login flow and could block or confuse imToken users.
|
||||||
|
|
||||||
|
## Fix
|
||||||
|
|
||||||
|
Removed the verification dialog and restored direct deeplink behavior: after the wallet in-app browser injects `window.ethereum`, the app calls `signInWithInjectedWallet()` and completes backend-verified login. Existing logged-in sessions still skip auto-login after stripping the deeplink parameter.
|
||||||
|
|
||||||
|
### Files Modified
|
||||||
|
|
||||||
|
- `src/wallet/AutoInjectedLogin.tsx` — removes the verification popup UI and auto-runs signature login for logged-out deeplink sessions.
|
||||||
|
- `src/wallet/injected.ts` — removes now-unused connected-address helper.
|
||||||
|
- `src/locales/zh-CN.ts` — removes unused verification popup copy.
|
||||||
|
- `src/locales/en.ts` — removes unused verification popup copy.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `rg -n "walletVerifyAddress|walletDetectedAddress|getConnectedInjectedAddress|wallet-verify" src || true`
|
||||||
|
- `npx tsc --noEmit`
|
||||||
|
- `npm run format:check`
|
||||||
|
- `npm test`
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
The imToken injected-provider fallback remains in place; only the confirmation popup and its supporting copy/helper were removed.
|
||||||
@@ -197,13 +197,6 @@ export const enDict: Dict = {
|
|||||||
walletTpLoginBtn: "Log in with TokenPocket",
|
walletTpLoginBtn: "Log in with TokenPocket",
|
||||||
walletTpWaiting: "Waiting for your signature in TokenPocket…",
|
walletTpWaiting: "Waiting for your signature in TokenPocket…",
|
||||||
walletTpReopen: "Reopen TokenPocket",
|
walletTpReopen: "Reopen TokenPocket",
|
||||||
walletVerifyAddressTitle: "Verify wallet address",
|
|
||||||
walletVerifyAddressDesc:
|
|
||||||
"Confirm the wallet address you want to use. After you tap the button below, your wallet app will ask you to sign and verify the address.",
|
|
||||||
walletDetectedAddress: "Detected wallet address",
|
|
||||||
walletVerifyAddressUnknown:
|
|
||||||
"The wallet address will be requested after verification",
|
|
||||||
walletVerifyAddressButton: "Verify this address",
|
|
||||||
favoritesFilters: "Filters",
|
favoritesFilters: "Filters",
|
||||||
favoriteSessionExpired: "Your session expired. Please sign in again.",
|
favoriteSessionExpired: "Your session expired. Please sign in again.",
|
||||||
loadFailed: "Could not load your favorites.",
|
loadFailed: "Could not load your favorites.",
|
||||||
|
|||||||
@@ -186,12 +186,6 @@ export const zhDict: Dict = {
|
|||||||
walletTpLoginBtn: "使用 TokenPocket 登录",
|
walletTpLoginBtn: "使用 TokenPocket 登录",
|
||||||
walletTpWaiting: "等待你在 TokenPocket 中完成签名…",
|
walletTpWaiting: "等待你在 TokenPocket 中完成签名…",
|
||||||
walletTpReopen: "重新打开 TokenPocket",
|
walletTpReopen: "重新打开 TokenPocket",
|
||||||
walletVerifyAddressTitle: "验证钱包地址",
|
|
||||||
walletVerifyAddressDesc:
|
|
||||||
"请确认将使用当前钱包地址登录。点击下方按钮后,钱包 App 会要求你签名验证地址。",
|
|
||||||
walletDetectedAddress: "检测到的钱包地址",
|
|
||||||
walletVerifyAddressUnknown: "点击验证后将请求钱包地址",
|
|
||||||
walletVerifyAddressButton: "验证此地址",
|
|
||||||
favoritesFilters: "筛选",
|
favoritesFilters: "筛选",
|
||||||
favoriteSessionExpired: "登录已过期,请重新登录。",
|
favoriteSessionExpired: "登录已过期,请重新登录。",
|
||||||
loadFailed: "无法加载收藏,请稍后重试。",
|
loadFailed: "无法加载收藏,请稍后重试。",
|
||||||
|
|||||||
@@ -1,25 +1,15 @@
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { useEffect } from "react";
|
||||||
import { useI18n } from "../i18n";
|
|
||||||
import {
|
import {
|
||||||
getConnectedInjectedAddress,
|
|
||||||
getInjectedWallet,
|
getInjectedWallet,
|
||||||
signInWithInjectedWallet,
|
signInWithInjectedWallet,
|
||||||
type WalletKind,
|
type WalletKind,
|
||||||
} from "./injected";
|
} from "./injected";
|
||||||
import { shortenAddress, useWallet } from "./WalletProvider";
|
import { useWallet } from "./WalletProvider";
|
||||||
|
|
||||||
const AUTO_LOGIN_PARAM = "autoLogin";
|
const AUTO_LOGIN_PARAM = "autoLogin";
|
||||||
const ETHEREUM_WAIT_MS = 8000;
|
const ETHEREUM_WAIT_MS = 8000;
|
||||||
const ETHEREUM_POLL_MS = 200;
|
const ETHEREUM_POLL_MS = 200;
|
||||||
|
|
||||||
type AutoLoginRequest = {
|
|
||||||
kind: WalletKind;
|
|
||||||
ready: boolean;
|
|
||||||
address: string | null;
|
|
||||||
signing: boolean;
|
|
||||||
error: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
function parseKind(value: string | null): WalletKind | null {
|
function parseKind(value: string | null): WalletKind | null {
|
||||||
if (value === "tokenPocket" || value === "metaMask" || value === "imToken") {
|
if (value === "tokenPocket" || value === "metaMask" || value === "imToken") {
|
||||||
return value;
|
return value;
|
||||||
@@ -55,15 +45,13 @@ function waitForInjected(kind: WalletKind): Promise<boolean> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* When the page is opened via a `?autoLogin=<wallet>` deeplink (typically from
|
* When the page is opened via a `?autoLogin=<wallet>` deeplink (typically from
|
||||||
* inside TokenPocket / imToken in-app browsers), show an explicit verification
|
* inside TokenPocket / imToken in-app browsers), wait for the wallet to inject
|
||||||
* prompt first. The wallet signature and backend verification only start after
|
* `window.ethereum`, then require a wallet signature and complete a verified
|
||||||
* the user taps the verification button, so Chrome -> wallet handoff never logs
|
* backend wallet session. Bypasses WalletConnect entirely so it works on
|
||||||
* in silently from a cached in-app-browser session.
|
* networks where the WC relay is blocked.
|
||||||
*/
|
*/
|
||||||
export function AutoInjectedLogin() {
|
export function AutoInjectedLogin() {
|
||||||
const { t } = useI18n();
|
|
||||||
const { completeLogin, status } = useWallet();
|
const { completeLogin, status } = useWallet();
|
||||||
const [request, setRequest] = useState<AutoLoginRequest | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window === "undefined") return;
|
if (typeof window === "undefined") return;
|
||||||
@@ -73,133 +61,24 @@ export function AutoInjectedLogin() {
|
|||||||
if (status === "loading") return;
|
if (status === "loading") return;
|
||||||
|
|
||||||
stripAutoLoginParam();
|
stripAutoLoginParam();
|
||||||
if (status === "loggedIn") {
|
if (status === "loggedIn") return;
|
||||||
setRequest(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
setRequest({
|
|
||||||
kind,
|
|
||||||
ready: false,
|
|
||||||
address: null,
|
|
||||||
signing: false,
|
|
||||||
error: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
void waitForInjected(kind).then(async (ready) => {
|
void waitForInjected(kind).then(async (ready) => {
|
||||||
|
if (cancelled || !ready) return;
|
||||||
|
try {
|
||||||
|
const res = await signInWithInjectedWallet(kind);
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
if (!ready) {
|
completeLogin(res.token, res.wallet);
|
||||||
setRequest((current) =>
|
} catch (err) {
|
||||||
current?.kind === kind
|
// eslint-disable-next-line no-console
|
||||||
? { ...current, ready: false, error: t("walletNoBrowserWallet") }
|
console.warn("[wallet-autologin] failed", err);
|
||||||
: current,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const address = await getConnectedInjectedAddress(kind);
|
|
||||||
if (cancelled) return;
|
|
||||||
setRequest((current) =>
|
|
||||||
current?.kind === kind
|
|
||||||
? { ...current, ready: true, address, error: "" }
|
|
||||||
: current,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [status, t]);
|
}, [completeLogin, status]);
|
||||||
|
|
||||||
const verifyAddress = useCallback(async () => {
|
return null;
|
||||||
if (!request || !request.ready || request.signing) return;
|
|
||||||
|
|
||||||
setRequest((current) =>
|
|
||||||
current ? { ...current, signing: true, error: "" } : current,
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
const res = await signInWithInjectedWallet(request.kind);
|
|
||||||
completeLogin(res.token, res.wallet);
|
|
||||||
setRequest(null);
|
|
||||||
} catch (err) {
|
|
||||||
const message =
|
|
||||||
err instanceof Error ? err.message : t("walletLoginFailed");
|
|
||||||
setRequest((current) =>
|
|
||||||
current
|
|
||||||
? {
|
|
||||||
...current,
|
|
||||||
signing: false,
|
|
||||||
error: message || t("walletLoginFailed"),
|
|
||||||
}
|
|
||||||
: current,
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.warn("[wallet-autologin] verification failed", err);
|
|
||||||
}
|
|
||||||
}, [completeLogin, request, t]);
|
|
||||||
|
|
||||||
if (!request) return null;
|
|
||||||
|
|
||||||
const addressLabel = request.address
|
|
||||||
? shortenAddress(request.address)
|
|
||||||
: t("walletVerifyAddressUnknown");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="fixed inset-0 z-[130] flex items-end justify-center bg-black/75 px-3 pb-3 pt-10 backdrop-blur-sm md:items-center md:p-6"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
aria-labelledby="wallet-verify-title"
|
|
||||||
>
|
|
||||||
<div className="w-full max-w-[420px] rounded-3xl border border-white/10 bg-[#17171d] p-5 shadow-2xl shadow-black/70 md:p-6">
|
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-ark-gold">
|
|
||||||
{t(walletNameKey(request.kind))}
|
|
||||||
</p>
|
|
||||||
<h2
|
|
||||||
id="wallet-verify-title"
|
|
||||||
className="mt-3 text-xl font-semibold text-white"
|
|
||||||
>
|
|
||||||
{t("walletVerifyAddressTitle")}
|
|
||||||
</h2>
|
|
||||||
<p className="mt-2 text-sm leading-6 text-neutral-400">
|
|
||||||
{t("walletVerifyAddressDesc")}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="mt-5 rounded-2xl border border-white/10 bg-white/[0.03] px-4 py-3">
|
|
||||||
<p className="text-xs text-neutral-500">
|
|
||||||
{t("walletDetectedAddress")}
|
|
||||||
</p>
|
|
||||||
<p className="mt-1 break-all font-mono text-sm text-neutral-100">
|
|
||||||
{addressLabel}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{request.error ? (
|
|
||||||
<p className="mt-4 rounded-2xl border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-200">
|
|
||||||
{request.error}
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={verifyAddress}
|
|
||||||
disabled={!request.ready || request.signing}
|
|
||||||
className="mt-5 w-full rounded-full bg-ark-gold px-4 py-3 text-sm font-bold text-black transition hover:bg-ark-gold2 disabled:cursor-wait disabled:opacity-70"
|
|
||||||
>
|
|
||||||
{request.signing
|
|
||||||
? t("walletSigning")
|
|
||||||
: request.ready
|
|
||||||
? t("walletVerifyAddressButton")
|
|
||||||
: t("walletConnecting")}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function walletNameKey(kind: WalletKind): string {
|
|
||||||
if (kind === "tokenPocket") return "walletTokenPocket";
|
|
||||||
if (kind === "metaMask") return "walletMetaMask";
|
|
||||||
return "walletImToken";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,17 +166,6 @@ export function getInjectedWallet(kind?: WalletKind): EthereumProvider | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getConnectedInjectedAddress(
|
|
||||||
kind?: WalletKind,
|
|
||||||
): Promise<string | null> {
|
|
||||||
const ethereum = getInjectedWallet(kind);
|
|
||||||
if (!ethereum) return null;
|
|
||||||
const accounts = await ethereum
|
|
||||||
.request<unknown[]>({ method: "eth_accounts" })
|
|
||||||
.catch((): unknown[] => []);
|
|
||||||
return accounts.find(isAddress) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Diagnostic: log what injected providers the browser exposes. */
|
/** Diagnostic: log what injected providers the browser exposes. */
|
||||||
export function logWalletProviders(): void {
|
export function logWalletProviders(): void {
|
||||||
const ethereum = getInjectedEthereum();
|
const ethereum = getInjectedEthereum();
|
||||||
|
|||||||
Reference in New Issue
Block a user