terry-wallet-login #15
@@ -33,14 +33,6 @@ function metaMaskWalletConnectLink(uri: string): string {
|
|||||||
return `https://metamask.app.link/wc?uri=${encodeURIComponent(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(
|
function walletConnectDeeplink(
|
||||||
kind: WalletKind | undefined,
|
kind: WalletKind | undefined,
|
||||||
uri: string,
|
uri: string,
|
||||||
@@ -118,7 +110,7 @@ export function useWalletConnectLogin() {
|
|||||||
const { address: localAddress, completeLogin } = useWallet();
|
const { address: localAddress, completeLogin } = useWallet();
|
||||||
const { address: wagmiAddress, isConnected: wagmiConnected } = useAccount();
|
const { address: wagmiAddress, isConnected: wagmiConnected } = useAccount();
|
||||||
const { connectAsync, connectors } = useConnect();
|
const { connectAsync, connectors } = useConnect();
|
||||||
const { disconnect, disconnectAsync } = useDisconnect();
|
const { disconnectAsync } = useDisconnect();
|
||||||
const [state, setState] = useState<WalletConnectLoginState>("idle");
|
const [state, setState] = useState<WalletConnectLoginState>("idle");
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [qrUri, setQrUri] = useState("");
|
const [qrUri, setQrUri] = useState("");
|
||||||
@@ -126,12 +118,15 @@ export function useWalletConnectLogin() {
|
|||||||
const pendingRef = useRef(false);
|
const pendingRef = useRef(false);
|
||||||
const completedAddressRef = useRef<string | null>(null);
|
const completedAddressRef = useRef<string | null>(null);
|
||||||
const cleanupMessageRef = useRef<(() => void) | null>(null);
|
const cleanupMessageRef = useRef<(() => void) | null>(null);
|
||||||
|
const cleanupPollingRef = useRef<(() => void) | null>(null);
|
||||||
|
|
||||||
const reset = useCallback(() => {
|
const reset = useCallback(() => {
|
||||||
pendingRef.current = false;
|
pendingRef.current = false;
|
||||||
completedAddressRef.current = null;
|
completedAddressRef.current = null;
|
||||||
cleanupMessageRef.current?.();
|
cleanupMessageRef.current?.();
|
||||||
cleanupMessageRef.current = null;
|
cleanupMessageRef.current = null;
|
||||||
|
cleanupPollingRef.current?.();
|
||||||
|
cleanupPollingRef.current = null;
|
||||||
setState("idle");
|
setState("idle");
|
||||||
setError("");
|
setError("");
|
||||||
setQrUri("");
|
setQrUri("");
|
||||||
@@ -161,9 +156,7 @@ export function useWalletConnectLogin() {
|
|||||||
address: wagmiAddress,
|
address: wagmiAddress,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}, [completeLogin, localAddress, wagmiAddress, wagmiConnected]);
|
||||||
void disconnect();
|
|
||||||
}, [completeLogin, disconnect, localAddress, wagmiAddress, wagmiConnected]);
|
|
||||||
|
|
||||||
const start = useCallback(
|
const start = useCallback(
|
||||||
async (
|
async (
|
||||||
@@ -178,7 +171,11 @@ export function useWalletConnectLogin() {
|
|||||||
pendingRef.current = true;
|
pendingRef.current = true;
|
||||||
setState("connecting");
|
setState("connecting");
|
||||||
|
|
||||||
if (preferredWallet && getInjectedWallet(preferredWallet)) {
|
if (
|
||||||
|
mode === "deeplink" &&
|
||||||
|
preferredWallet &&
|
||||||
|
getInjectedWallet(preferredWallet)
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const injectedAddress = await connectInjectedWallet(preferredWallet);
|
const injectedAddress = await connectInjectedWallet(preferredWallet);
|
||||||
console.info("[wallet-login] injected connected", {
|
console.info("[wallet-login] injected connected", {
|
||||||
@@ -198,15 +195,20 @@ export function useWalletConnectLogin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the connector RainbowKit created for the selected wallet. This
|
// QR mode must always use a WalletConnect-compatible connector so the
|
||||||
// is especially important for MetaMask Mobile: RainbowKit/Wagmi use the
|
// desktop page can render a scannable `wc:` URI instead of opening a
|
||||||
// MetaMask SDK connector there instead of the generic WalletConnect one.
|
// local browser extension. Deeplink mode can prefer wallet-specific
|
||||||
const connector =
|
// connectors (notably MetaMask SDK on mobile).
|
||||||
connectors.find((item) =>
|
const walletConnectConnector =
|
||||||
connectorMatchesWallet(item, preferredWallet),
|
|
||||||
) ??
|
|
||||||
connectors.find((item) => item.type === "walletConnect") ??
|
connectors.find((item) => item.type === "walletConnect") ??
|
||||||
connectors.find((item) => item.id === "walletConnect");
|
connectors.find((item) => item.id === "walletConnect");
|
||||||
|
const walletSpecificConnector = connectors.find((item) =>
|
||||||
|
connectorMatchesWallet(item, preferredWallet),
|
||||||
|
);
|
||||||
|
const connector =
|
||||||
|
mode === "qr"
|
||||||
|
? walletConnectConnector
|
||||||
|
: (walletSpecificConnector ?? walletConnectConnector);
|
||||||
|
|
||||||
if (!connector) {
|
if (!connector) {
|
||||||
pendingRef.current = false;
|
pendingRef.current = false;
|
||||||
@@ -235,7 +237,7 @@ export function useWalletConnectLogin() {
|
|||||||
connectorId: connector.id,
|
connectorId: connector.id,
|
||||||
});
|
});
|
||||||
if (mode === "qr") {
|
if (mode === "qr") {
|
||||||
setQrUri(walletConnectQrValue(preferredWallet, message.data));
|
setQrUri(message.data);
|
||||||
}
|
}
|
||||||
const deeplink = walletConnectDeeplink(preferredWallet, message.data);
|
const deeplink = walletConnectDeeplink(preferredWallet, message.data);
|
||||||
if (mode === "deeplink" && deeplink && isMobileDevice()) {
|
if (mode === "deeplink" && deeplink && isMobileDevice()) {
|
||||||
@@ -248,6 +250,43 @@ export function useWalletConnectLogin() {
|
|||||||
cleanupMessageRef.current = () =>
|
cleanupMessageRef.current = () =>
|
||||||
connector.emitter.off("message", onMessage);
|
connector.emitter.off("message", onMessage);
|
||||||
|
|
||||||
|
cleanupPollingRef.current?.();
|
||||||
|
const finishFromAddress = (address: string, source: string) => {
|
||||||
|
const alreadyCompleted =
|
||||||
|
completedAddressRef.current?.toLowerCase() === address.toLowerCase();
|
||||||
|
if (alreadyCompleted) return;
|
||||||
|
pendingRef.current = false;
|
||||||
|
completedAddressRef.current = address;
|
||||||
|
setConnectedAddress(address);
|
||||||
|
setQrUri("");
|
||||||
|
setState("idle");
|
||||||
|
cleanupMessageRef.current?.();
|
||||||
|
cleanupMessageRef.current = null;
|
||||||
|
cleanupPollingRef.current?.();
|
||||||
|
cleanupPollingRef.current = null;
|
||||||
|
console.info("[wallet-login] wallet account connected", {
|
||||||
|
source,
|
||||||
|
preferredWallet,
|
||||||
|
address,
|
||||||
|
chain: "BNB Chain",
|
||||||
|
chainId: bsc.id,
|
||||||
|
});
|
||||||
|
completeLogin(localWalletToken(address), address);
|
||||||
|
console.info("[wallet-login] local wallet session completed", {
|
||||||
|
address,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const pollId = window.setInterval(() => {
|
||||||
|
void connector
|
||||||
|
.getAccounts()
|
||||||
|
.then((accounts) => {
|
||||||
|
const account = accounts[0];
|
||||||
|
if (account) finishFromAddress(account, "connector-poll");
|
||||||
|
})
|
||||||
|
.catch(() => undefined);
|
||||||
|
}, 1000);
|
||||||
|
cleanupPollingRef.current = () => window.clearInterval(pollId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await disconnectAsync().catch(() => undefined);
|
await disconnectAsync().catch(() => undefined);
|
||||||
await connector.disconnect().catch(() => undefined);
|
await connector.disconnect().catch(() => undefined);
|
||||||
@@ -255,23 +294,9 @@ export function useWalletConnectLogin() {
|
|||||||
const connectedAddress = result.accounts[0];
|
const connectedAddress = result.accounts[0];
|
||||||
if (!connectedAddress)
|
if (!connectedAddress)
|
||||||
throw new Error("Wallet connected without an account");
|
throw new Error("Wallet connected without an account");
|
||||||
pendingRef.current = false;
|
finishFromAddress(connectedAddress, "connectAsync");
|
||||||
completedAddressRef.current = connectedAddress;
|
|
||||||
setConnectedAddress(connectedAddress);
|
|
||||||
console.info("[wallet-login] walletconnect connected", {
|
|
||||||
address: connectedAddress,
|
|
||||||
chain: "BNB Chain",
|
|
||||||
chainId: bsc.id,
|
|
||||||
});
|
|
||||||
window.alert(`扫码成功,已拿到钱包地址:\n${connectedAddress}`);
|
|
||||||
completeLogin(localWalletToken(connectedAddress), connectedAddress);
|
|
||||||
console.info("[wallet-login] local wallet session completed", {
|
|
||||||
address: connectedAddress,
|
|
||||||
});
|
|
||||||
setQrUri("");
|
|
||||||
setState("idle");
|
|
||||||
disconnect();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (completedAddressRef.current) return;
|
||||||
pendingRef.current = false;
|
pendingRef.current = false;
|
||||||
setState("idle");
|
setState("idle");
|
||||||
setError(
|
setError(
|
||||||
@@ -279,16 +304,11 @@ export function useWalletConnectLogin() {
|
|||||||
);
|
);
|
||||||
cleanupMessageRef.current?.();
|
cleanupMessageRef.current?.();
|
||||||
cleanupMessageRef.current = null;
|
cleanupMessageRef.current = null;
|
||||||
|
cleanupPollingRef.current?.();
|
||||||
|
cleanupPollingRef.current = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[available, completeLogin, connectAsync, connectors, disconnectAsync],
|
||||||
available,
|
|
||||||
completeLogin,
|
|
||||||
connectAsync,
|
|
||||||
connectors,
|
|
||||||
disconnect,
|
|
||||||
disconnectAsync,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user