55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
"use client";
|
|
|
|
/** 구글이 그려주는 버튼. 우리가 모양을 흉내 내면 브랜드 규정에 걸린다 */
|
|
|
|
import { useEffect, useRef, useState } from "react";
|
|
import {
|
|
fetchConfig, loadGoogleIdentity, type GoogleButtonOptions,
|
|
} from "@/lib/auth";
|
|
|
|
export default function GoogleSignIn({ onCredential, text = "signin_with" }: {
|
|
onCredential: (credential: string) => void;
|
|
text?: GoogleButtonOptions["text"];
|
|
}) {
|
|
const host = useRef<HTMLDivElement>(null);
|
|
const [error, setError] = useState<string | null>(null);
|
|
// 콜백이 매 렌더마다 바뀌어도 구글에 다시 등록하지 않게 최신 것만 들고 있는다
|
|
const latest = useRef(onCredential);
|
|
latest.current = onCredential;
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
(async () => {
|
|
const config = await fetchConfig();
|
|
if (!config.enabled) throw new Error("구글 로그인이 설정되지 않았습니다");
|
|
|
|
const google = await loadGoogleIdentity();
|
|
if (cancelled || !host.current) return;
|
|
|
|
google.initialize({
|
|
client_id: config.client_id,
|
|
callback: (response) => {
|
|
if (response.credential) latest.current(response.credential);
|
|
},
|
|
auto_select: false,
|
|
cancel_on_tap_outside: true,
|
|
});
|
|
google.renderButton(host.current, {
|
|
type: "standard", theme: "filled_black", size: "large",
|
|
shape: "pill", text, logo_alignment: "center", width: 320,
|
|
});
|
|
})().catch((failure) => {
|
|
if (!cancelled) setError((failure as Error).message);
|
|
});
|
|
|
|
return () => { cancelled = true; };
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [text]);
|
|
|
|
if (error) {
|
|
return <p style={{ color: "#ff7a7a", fontSize: "var(--text-sm)", margin: 0 }}>{error}</p>;
|
|
}
|
|
return <div ref={host} style={{ display: "flex", justifyContent: "center" }} />;
|
|
}
|