fix(wallet): improve mobile login and logout flows
This commit is contained in:
@@ -1,12 +1,20 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useConnectModal } from "@rainbow-me/rainbowkit";
|
||||
import { useAccount, useDisconnect, useSignMessage } from "wagmi";
|
||||
import { useAccount, useConnect, useDisconnect, useSignMessage } from "wagmi";
|
||||
import { bsc } from "wagmi/chains";
|
||||
import { requestWalletNonce, verifyWalletSignature } from "./api";
|
||||
import { hasWalletConnectProjectId } from "./RainbowWalletProvider";
|
||||
import type { WalletKind } from "./injected";
|
||||
import { useWallet } from "./WalletProvider";
|
||||
|
||||
export type WalletConnectLoginState = "idle" | "connecting" | "signing";
|
||||
|
||||
function isMobileDevice(): boolean {
|
||||
if (typeof navigator === "undefined") return false;
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile/i.test(
|
||||
navigator.userAgent || "",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* MetaMask / imToken QR fallback via RainbowKit + WalletConnect.
|
||||
*
|
||||
@@ -24,27 +32,90 @@ export function useWalletConnectLogin() {
|
||||
const { completeLogin } = useWallet();
|
||||
const { address, isConnected } = useAccount();
|
||||
const { signMessageAsync } = useSignMessage();
|
||||
const { connectAsync, connectors } = useConnect();
|
||||
const { disconnect } = useDisconnect();
|
||||
const { openConnectModal } = useConnectModal();
|
||||
const [state, setState] = useState<WalletConnectLoginState>("idle");
|
||||
const [error, setError] = useState("");
|
||||
const [qrUri, setQrUri] = useState("");
|
||||
const pendingRef = useRef(false);
|
||||
const cleanupMessageRef = useRef<(() => void) | null>(null);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
pendingRef.current = false;
|
||||
cleanupMessageRef.current?.();
|
||||
cleanupMessageRef.current = null;
|
||||
setState("idle");
|
||||
setError("");
|
||||
setQrUri("");
|
||||
}, []);
|
||||
|
||||
const start = useCallback(() => {
|
||||
if (!available) return;
|
||||
setError("");
|
||||
pendingRef.current = true;
|
||||
setState("connecting");
|
||||
// When already connected, openConnectModal is undefined; the effect below
|
||||
// picks up the existing account and proceeds straight to signing.
|
||||
openConnectModal?.();
|
||||
}, [available, openConnectModal]);
|
||||
const start = useCallback(
|
||||
async (preferredWallet?: WalletKind) => {
|
||||
if (!available) return;
|
||||
setError("");
|
||||
setQrUri("");
|
||||
pendingRef.current = true;
|
||||
setState("connecting");
|
||||
|
||||
const connector =
|
||||
connectors.find((item) => item.id === preferredWallet) ??
|
||||
connectors.find((item) => item.id === "walletConnect") ??
|
||||
connectors.find((item) => item.type === "walletConnect");
|
||||
|
||||
if (!connector) {
|
||||
pendingRef.current = false;
|
||||
setQrUri("");
|
||||
setState("idle");
|
||||
setError("WalletConnect is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
console.info("[wallet-login] walletconnect connector", {
|
||||
preferredWallet,
|
||||
connectorId: connector.id,
|
||||
connectorName: connector.name,
|
||||
connectorType: connector.type,
|
||||
});
|
||||
|
||||
const onMessage = (message: { type: string; data?: unknown }) => {
|
||||
if (
|
||||
message.type !== "display_uri" ||
|
||||
typeof message.data !== "string"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
console.info("[wallet-login] walletconnect display_uri", {
|
||||
preferredWallet,
|
||||
connectorId: connector.id,
|
||||
});
|
||||
setQrUri(message.data);
|
||||
if (preferredWallet === "tokenPocket" && isMobileDevice()) {
|
||||
window.location.href = `tpoutside://wc?uri=${encodeURIComponent(
|
||||
message.data,
|
||||
)}`;
|
||||
}
|
||||
};
|
||||
|
||||
cleanupMessageRef.current?.();
|
||||
connector.emitter.on("message", onMessage);
|
||||
cleanupMessageRef.current = () =>
|
||||
connector.emitter.off("message", onMessage);
|
||||
|
||||
try {
|
||||
await connector.disconnect().catch(() => undefined);
|
||||
await connectAsync({ chainId: bsc.id, connector });
|
||||
} catch (err) {
|
||||
pendingRef.current = false;
|
||||
setState("idle");
|
||||
setError(
|
||||
err instanceof Error ? err.message : "WalletConnect login failed",
|
||||
);
|
||||
cleanupMessageRef.current?.();
|
||||
cleanupMessageRef.current = null;
|
||||
}
|
||||
},
|
||||
[available, connectAsync, connectors],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pendingRef.current || !isConnected || !address) return;
|
||||
@@ -68,6 +139,7 @@ export function useWalletConnectLogin() {
|
||||
setError(
|
||||
err instanceof Error ? err.message : "WalletConnect login failed",
|
||||
);
|
||||
setQrUri("");
|
||||
setState("idle");
|
||||
}
|
||||
} finally {
|
||||
@@ -80,5 +152,5 @@ export function useWalletConnectLogin() {
|
||||
};
|
||||
}, [address, completeLogin, disconnect, isConnected, signMessageAsync]);
|
||||
|
||||
return { available, state, error, start, reset };
|
||||
return { available, state, error, qrUri, start, reset };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user