Files
gitlab-instance-0a899031_cn…/app/components/GoogleLoginButton.tsx
root 283d65d1d1 ;s
2026-06-07 12:48:17 +08:00

105 lines
3.3 KiB
TypeScript

"use client";
import { useEffect, useRef, useState } from "react";
declare global {
interface Window {
google?: {
accounts?: {
id?: {
initialize: (options: {
client_id: string;
callback: (response: { credential?: string }) => void;
}) => void;
renderButton: (
parent: HTMLElement,
options: {
theme?: "outline" | "filled_blue" | "filled_black";
size?: "large" | "medium" | "small";
width?: number;
text?: "signin_with" | "signup_with" | "continue_with" | "signin";
shape?: "rectangular" | "pill" | "circle" | "square";
}
) => void;
};
};
};
}
}
const GOOGLE_SCRIPT_ID = "google-identity-services";
const GOOGLE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || "";
function loadGoogleScript(): Promise<void> {
if (typeof window === "undefined") return Promise.resolve();
if (window.google?.accounts?.id) return Promise.resolve();
const existing = document.getElementById(GOOGLE_SCRIPT_ID) as HTMLScriptElement | null;
if (existing) {
return new Promise((resolve, reject) => {
existing.addEventListener("load", () => resolve(), { once: true });
existing.addEventListener("error", () => reject(new Error("Google 登录加载失败")), { once: true });
});
}
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.id = GOOGLE_SCRIPT_ID;
script.src = "https://accounts.google.com/gsi/client";
script.async = true;
script.defer = true;
script.onload = () => resolve();
script.onerror = () => reject(new Error("Google 登录加载失败"));
document.head.appendChild(script);
});
}
export default function GoogleLoginButton({
onCredential,
onError,
disabled = false,
}: {
onCredential: (credential: string) => void;
onError?: (message: string) => void;
disabled?: boolean;
}) {
const ref = useRef<HTMLDivElement>(null);
const [ready, setReady] = useState(false);
useEffect(() => {
if (!GOOGLE_CLIENT_ID || !ref.current) return;
let cancelled = false;
loadGoogleScript()
.then(() => {
if (cancelled || !ref.current || !window.google?.accounts?.id) return;
ref.current.innerHTML = "";
window.google.accounts.id.initialize({
client_id: GOOGLE_CLIENT_ID,
callback: (response) => {
if (response.credential) onCredential(response.credential);
else onError?.("Google 登录失败");
},
});
window.google.accounts.id.renderButton(ref.current, {
theme: "outline",
size: "large",
width: 304,
text: "continue_with",
shape: "rectangular",
});
setReady(true);
})
.catch((error) => onError?.(error instanceof Error ? error.message : "Google 登录加载失败"));
return () => {
cancelled = true;
};
}, [onCredential, onError]);
if (!GOOGLE_CLIENT_ID) return null;
return (
<div className={disabled ? "pointer-events-none opacity-60" : ""}>
<div ref={ref} className="flex min-h-10 justify-center" />
{!ready && <div className="h-10 rounded-lg border border-gray-200 dark:border-gray-700" />}
</div>
);
}