Skip to content

Commit c935d15

Browse files
“Chat2DB-Pro”claude
andcommitted
feat: add Google One Tap sign-in component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent dccfa36 commit c935d15

3 files changed

Lines changed: 1425 additions & 1354 deletions

File tree

components/GoogleOneTap.jsx

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { useEffect, useState } from "react";
2+
import Script from "next/script";
3+
4+
const ONE_TAP_SESSION_KEY = "chat2db_google_one_tap_prompted";
5+
const GOOGLE_ONE_TAP_CLIENT_ID =
6+
"1082889006101-vvt2hjqb1ic211d3dinruc6lj3jurapp.apps.googleusercontent.com";
7+
8+
const getApiBaseUrl = () => {
9+
if (typeof window === "undefined") return "https://app.chat2db-ai.com";
10+
const hostname = window.location.hostname;
11+
if (hostname.includes("chat2db.ai") && !hostname.includes("chat2db-ai.com")) {
12+
return "https://app.chat2db.ai";
13+
}
14+
return "https://app.chat2db-ai.com";
15+
};
16+
17+
const isChromeBrowser = () => {
18+
if (typeof window === "undefined") return false;
19+
const ua = window.navigator.userAgent;
20+
return /Chrome/.test(ua) && !/Edg/.test(ua) && !/OPR/.test(ua);
21+
};
22+
23+
export default function GoogleOneTap() {
24+
const [scriptReady, setScriptReady] = useState(false);
25+
26+
useEffect(() => {
27+
if (!scriptReady || !isChromeBrowser()) return;
28+
if (sessionStorage.getItem(ONE_TAP_SESSION_KEY) === "1") return;
29+
30+
const googleIdentity = window.google?.accounts?.id;
31+
if (!googleIdentity) return;
32+
33+
googleIdentity.initialize({
34+
client_id: GOOGLE_ONE_TAP_CLIENT_ID,
35+
callback: async ({ credential }) => {
36+
if (!credential) return;
37+
try {
38+
await fetch(`${getApiBaseUrl()}/api/oauth/google_one_tap`, {
39+
method: "POST",
40+
headers: { "Content-Type": "application/json" },
41+
credentials: "include",
42+
body: JSON.stringify({ credential, appCode: "chat2db" }),
43+
});
44+
sessionStorage.setItem(ONE_TAP_SESSION_KEY, "1");
45+
} catch (error) {
46+
console.error("[GoogleOneTap] login failed", error);
47+
}
48+
},
49+
auto_select: false,
50+
cancel_on_tap_outside: true,
51+
context: "signin",
52+
itp_support: true,
53+
});
54+
55+
googleIdentity.prompt();
56+
57+
return () => {
58+
window.google?.accounts?.id?.cancel();
59+
};
60+
}, [scriptReady]);
61+
62+
return (
63+
<Script
64+
src="https://accounts.google.com/gsi/client"
65+
strategy="afterInteractive"
66+
onLoad={() => setScriptReady(true)}
67+
/>
68+
);
69+
}

pages/_app.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import '../styles/global.css';
22
import { GoogleAnalytics } from "@next/third-parties/google";
33
import { useLanguageDetection } from '../hooks/useLanguageDetection';
4+
import GoogleOneTap from '../components/GoogleOneTap';
45
export default function Nextra({ Component, pageProps }) {
56
useLanguageDetection();
67

78
return (
89
<>
910
<Component {...pageProps} />
1011
<GoogleAnalytics gaId="G-99S9GRT0LE" />
12+
<GoogleOneTap />
1113
</>
1214
);
1315
}

0 commit comments

Comments
 (0)