84 lines
2.4 KiB
TypeScript
84 lines
2.4 KiB
TypeScript
|
|
import { useEffect } from "react";
|
||
|
|
import {
|
||
|
|
connectInjectedWallet,
|
||
|
|
getInjectedWallet,
|
||
|
|
type WalletKind,
|
||
|
|
} from "./injected";
|
||
|
|
import { localWalletToken, useWallet } from "./WalletProvider";
|
||
|
|
|
||
|
|
const AUTO_LOGIN_PARAM = "autoLogin";
|
||
|
|
const ETHEREUM_WAIT_MS = 8000;
|
||
|
|
const ETHEREUM_POLL_MS = 200;
|
||
|
|
|
||
|
|
function parseKind(value: string | null): WalletKind | null {
|
||
|
|
if (value === "tokenPocket" || value === "metaMask" || value === "imToken") {
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
function stripAutoLoginParam(): void {
|
||
|
|
const url = new URL(window.location.href);
|
||
|
|
url.searchParams.delete(AUTO_LOGIN_PARAM);
|
||
|
|
const qs = url.searchParams.toString();
|
||
|
|
const next = url.pathname + (qs ? `?${qs}` : "") + url.hash;
|
||
|
|
window.history.replaceState({}, "", next);
|
||
|
|
}
|
||
|
|
|
||
|
|
function waitForInjected(kind: WalletKind): Promise<boolean> {
|
||
|
|
return new Promise((resolve) => {
|
||
|
|
const start = Date.now();
|
||
|
|
const tick = () => {
|
||
|
|
if (getInjectedWallet(kind)) {
|
||
|
|
resolve(true);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (Date.now() - start >= ETHEREUM_WAIT_MS) {
|
||
|
|
resolve(false);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
window.setTimeout(tick, ETHEREUM_POLL_MS);
|
||
|
|
};
|
||
|
|
tick();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* When the page is opened via a `?autoLogin=<wallet>` deeplink (typically from
|
||
|
|
* inside TokenPocket / imToken in-app browsers), wait for the wallet to inject
|
||
|
|
* `window.ethereum`, then complete a local wallet session automatically. Bypasses
|
||
|
|
* WalletConnect entirely so it works on networks where the WC relay is blocked.
|
||
|
|
*/
|
||
|
|
export function AutoInjectedLogin() {
|
||
|
|
const { completeLogin, status } = useWallet();
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (typeof window === "undefined") return;
|
||
|
|
const params = new URLSearchParams(window.location.search);
|
||
|
|
const kind = parseKind(params.get(AUTO_LOGIN_PARAM));
|
||
|
|
if (!kind) return;
|
||
|
|
|
||
|
|
stripAutoLoginParam();
|
||
|
|
if (status === "loggedIn") return;
|
||
|
|
|
||
|
|
let cancelled = false;
|
||
|
|
void waitForInjected(kind).then(async (ready) => {
|
||
|
|
if (cancelled || !ready) return;
|
||
|
|
try {
|
||
|
|
const address = await connectInjectedWallet(kind);
|
||
|
|
if (cancelled) return;
|
||
|
|
completeLogin(localWalletToken(address), address);
|
||
|
|
} catch (err) {
|
||
|
|
// eslint-disable-next-line no-console
|
||
|
|
console.warn("[wallet-autologin] failed", err);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
return () => {
|
||
|
|
cancelled = true;
|
||
|
|
};
|
||
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|