fix(wallet): restore metamask mobile login

This commit is contained in:
TerryM
2026-06-03 00:12:50 +08:00
parent b19486e908
commit 6552b92c50
3 changed files with 192 additions and 15 deletions

View File

@@ -165,13 +165,8 @@ export function WalletLoginModal() {
) : null}
{selected && wc.qrUri ? (
<div className="mt-4 grid place-items-center gap-2 rounded-2xl bg-white p-4 text-center">
<div className="mt-4 grid place-items-center rounded-2xl bg-white p-4 text-center">
<QRCodeSVG value={wc.qrUri} size={180} level="M" />
<p className="text-xs font-medium text-neutral-700">
{mobileDevice
? t("walletTpWaiting")
: t("walletQrUseAnotherDevice")}
</p>
</div>
) : null}

View File

@@ -1,5 +1,5 @@
import { useCallback, useRef, useState } from "react";
import { useConnect, useDisconnect } from "wagmi";
import { useCallback, useEffect, useRef, useState } from "react";
import { useAccount, useConnect, useDisconnect } from "wagmi";
import { bsc } from "wagmi/chains";
import { hasWalletConnectProjectId } from "./RainbowWalletProvider";
import {
@@ -24,18 +24,39 @@ function currentUrl(): string {
return window.location.href;
}
function isWalletConnectUri(uri: string): boolean {
return uri.startsWith("wc:");
}
function metaMaskWalletConnectLink(uri: string): string {
if (!isWalletConnectUri(uri)) return uri;
return `https://metamask.app.link/wc?uri=${encodeURIComponent(uri)}`;
}
function walletConnectQrValue(
kind: WalletKind | undefined,
uri: string,
): string {
if (kind === "metaMask") return metaMaskWalletConnectLink(uri);
return uri;
}
function walletConnectDeeplink(
kind: WalletKind | undefined,
uri: string,
): string | null {
if (kind === "tokenPocket") {
return `tpoutside://wc?uri=${encodeURIComponent(uri)}`;
return isWalletConnectUri(uri)
? `tpoutside://wc?uri=${encodeURIComponent(uri)}`
: uri;
}
if (kind === "metaMask") {
return `https://metamask.app.link/wc?uri=${encodeURIComponent(uri)}`;
return metaMaskWalletConnectLink(uri);
}
if (kind === "imToken") {
return `imtokenv2://wc?uri=${encodeURIComponent(uri)}`;
return isWalletConnectUri(uri)
? `imtokenv2://wc?uri=${encodeURIComponent(uri)}`
: uri;
}
return null;
}
@@ -61,6 +82,26 @@ function openWalletDeeplink(
}, 1500);
}
function connectorMatchesWallet(
connector: { id: string; name?: string; type?: string },
kind: WalletKind | undefined,
): boolean {
if (!kind) return false;
const id = connector.id.toLowerCase();
const name = connector.name?.toLowerCase() ?? "";
const type = connector.type?.toLowerCase() ?? "";
if (kind === "metaMask") {
return id === "metamask" || type === "metamask" || name === "metamask";
}
if (kind === "tokenPocket") {
return id === "tokenpocket" || name === "tokenpocket";
}
if (kind === "imToken") {
return id === "imtoken" || name === "imtoken";
}
return false;
}
/**
* MetaMask / imToken QR fallback via RainbowKit + WalletConnect.
*
@@ -74,7 +115,8 @@ function openWalletDeeplink(
*/
export function useWalletConnectLogin() {
const available = hasWalletConnectProjectId();
const { completeLogin } = useWallet();
const { address: localAddress, completeLogin } = useWallet();
const { address: wagmiAddress, isConnected: wagmiConnected } = useAccount();
const { connectAsync, connectors } = useConnect();
const { disconnect, disconnectAsync } = useDisconnect();
const [state, setState] = useState<WalletConnectLoginState>("idle");
@@ -82,10 +124,12 @@ export function useWalletConnectLogin() {
const [qrUri, setQrUri] = useState("");
const [connectedAddress, setConnectedAddress] = useState("");
const pendingRef = useRef(false);
const completedAddressRef = useRef<string | null>(null);
const cleanupMessageRef = useRef<(() => void) | null>(null);
const reset = useCallback(() => {
pendingRef.current = false;
completedAddressRef.current = null;
cleanupMessageRef.current?.();
cleanupMessageRef.current = null;
setState("idle");
@@ -94,6 +138,33 @@ export function useWalletConnectLogin() {
setConnectedAddress("");
}, []);
useEffect(() => {
if (!wagmiConnected || !wagmiAddress) return;
const alreadyCompleted =
completedAddressRef.current?.toLowerCase() === wagmiAddress.toLowerCase();
if (alreadyCompleted) return;
completedAddressRef.current = wagmiAddress;
pendingRef.current = false;
setConnectedAddress(wagmiAddress);
setQrUri("");
setState("idle");
if (localAddress?.toLowerCase() !== wagmiAddress.toLowerCase()) {
console.info("[wallet-login] wagmi account connected", {
address: wagmiAddress,
chain: "BNB Chain",
chainId: bsc.id,
});
completeLogin(localWalletToken(wagmiAddress), wagmiAddress);
console.info("[wallet-login] local wallet session completed", {
address: wagmiAddress,
});
}
void disconnect();
}, [completeLogin, disconnect, localAddress, wagmiAddress, wagmiConnected]);
const start = useCallback(
async (
preferredWallet?: WalletKind,
@@ -103,6 +174,7 @@ export function useWalletConnectLogin() {
setError("");
setQrUri("");
setConnectedAddress("");
completedAddressRef.current = null;
pendingRef.current = true;
setState("connecting");
@@ -126,9 +198,13 @@ export function useWalletConnectLogin() {
}
}
// This modal is QR/WalletConnect-only. RainbowKit also exposes wallet-
// specific injected connectors (for example `tokenPocket`) when an
// Prefer the connector RainbowKit created for the selected wallet. This
// is especially important for MetaMask Mobile: RainbowKit/Wagmi use the
// MetaMask SDK connector there instead of the generic WalletConnect one.
const connector =
connectors.find((item) =>
connectorMatchesWallet(item, preferredWallet),
) ??
connectors.find((item) => item.type === "walletConnect") ??
connectors.find((item) => item.id === "walletConnect");
@@ -158,7 +234,9 @@ export function useWalletConnectLogin() {
preferredWallet,
connectorId: connector.id,
});
if (mode === "qr") setQrUri(message.data);
if (mode === "qr") {
setQrUri(walletConnectQrValue(preferredWallet, message.data));
}
const deeplink = walletConnectDeeplink(preferredWallet, message.data);
if (mode === "deeplink" && deeplink && isMobileDevice()) {
openWalletDeeplink(preferredWallet, deeplink);
@@ -178,6 +256,7 @@ export function useWalletConnectLogin() {
if (!connectedAddress)
throw new Error("Wallet connected without an account");
pendingRef.current = false;
completedAddressRef.current = connectedAddress;
setConnectedAddress(connectedAddress);
console.info("[wallet-login] walletconnect connected", {
address: connectedAddress,