8000 feat: add Google One Tap modal by darcyYe · Pull Request #1117 · logto-io/docs · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: add Google One Tap modal #1117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docusaurus.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// eslint-disable-next-line import/no-unassigned-import
import 'dotenv/config';
import type { Config } from '@docusaurus/types';
import { yes } from '@silverhand/essentials';

import {
addAliasPlugin,
Expand Down Expand Up @@ -35,6 +36,14 @@ const config: Config = {
organizationName: 'logto-io',
projectName: 'docs',

scripts: [
{
src: 'https://accounts.google.com/gsi/client',
async: true,
defer: true,
},
],

i18n: {
defaultLocale,
locales: ['de', 'en', 'es', 'fr', 'ja', 'ko', 'pt-BR', 'zh-CN', 'zh-TW'],
Expand All @@ -43,6 +52,8 @@ const config: Config = {

customFields: {
inkeepApiKey: process.env.INKEEP_API_KEY,
logtoApiBaseUrl: process.env.LOGTO_API_BASE_URL,
isDevFeatureEnabled: yes(process.env.IS_DEV_FEATURE_ENABLED),
},

staticDirectories: ['static', 'static-localized/' + currentLocale],
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,8 @@
"core-js-pure",
"sharp"
]
},
"dependencies": {
"zod": "^3.25.13"
}
}
29 changes: 20 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

107 changes: 107 additions & 0 deletions src/theme/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import type { WrapperProps } from '@docusaurus/types';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import type LayoutType from '@theme/Layout';
import Layout from '@theme-original/Layout';
import type { ReactNode } from 'react';
import { useEffect, useState } from 'react';
import { z } from 'zod';

type Props = WrapperProps<typeof LayoutType>;

const >
.object({
isEnabled: z.boolean().optional(),
autoSelect: z.boolean().optional(),
closeOnTapOutside: z.boolean().optional(),
itpSupport: z.boolean().optional(),
})
.optional();

const googleOneTapConfigSchema = z.object({
clientId: z.string(),
oneTap: oneTapSchema,
});

type GoogleOneTapConfig = z.infer<typeof googleOneTapConfigSchema>;

const CACHE_KEY = '_logto_google_one_tap_config';
const CACHE_EXPIRY_KEY = '_logto_google_one_tap_config_expiry';
const CACHE_EXPIRY_TIME = 1 * 60 * 60 * 1000; // 1 hour

// Default API base URL
const DEFAULT_API_BASE_PROD_URL = 'https://auth.logto.io';
const DEFAULT_API_BASE_DEV_URL = 'https://auth.logto.dev';

export default function LayoutWrapper(props: Props): ReactNode {
const [config, setConfig] = useState<GoogleOneTapConfig | undefined>(undefined);
const { siteConfig } = useDocusaurusContext();

// Get the API base URL from customFields, or use the default value if it doesn't exist
const logtoApiBaseUrl = siteConfig.customFields?.logtoApiBaseUrl;
const apiBaseUrl =
typeof logtoApiBaseUrl === 'string'
? logtoApiBaseUrl
: siteConfig.customFields?.isDevFeatureEnabled
? DEFAULT_API_BASE_DEV_URL
: DEFAULT_API_BASE_PROD_URL;

useEffect(() => {
const fetchConfig = async () => {
try {
const cachedConfig = localStorage.getItem(CACHE_KEY);
const cachedExpiry = localStorage.getItem(CACHE_EXPIRY_KEY);

if (cachedConfig && cachedExpiry && Number(cachedExpiry) > Date.now()) {
try {
const parsedConfig = googleOneTapConfigSchema.parse(JSON.parse(cachedConfig));
setConfig(parsedConfig);
return;
} catch (parseError) {
console.error('Cached config validation failed:', parseError);
}
}

const response = await fetch(`${apiBaseUrl}/api/google-one-tap/config`, {
headers: {
Origin: window.location.origin,
},
});

if (!response.ok) {
throw new Error('Failed to fetch Google One Tap config');
}

const data = await response.json();

const validatedConfig = googleOneTapConfigSchema.parse(data);

localStorage.setItem(CACHE_KEY, JSON.stringify(validatedConfig));
localStorage.setItem(CACHE_EXPIRY_KEY, String(Date.now() + CACHE_EXPIRY_TIME));

setConfig(validatedConfig);
} catch (error) {
console.error('Error fetching or validating Google One Tap config:', error);
Copy link
Preview
Copilot AI May 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider implementing a graceful fallback UI or user notification in case the config fetch fails rather than only logging the error.

Suggested change
console.error('Error fetching or validating Google One Tap config:', error);
console.error('Error fetching or validating Google One Tap config:', error);
setFetchError(true);

Copilot uses AI. Check for mistakes.

}
};

void fetchConfig();
}, [apiBaseUrl]);

return (
<>
{config && config.oneTap?.isEnabled && (
<div
data-itp_support={Boolean(config.oneTap.itpSupport)}
data-auto_select={Boolean(config.oneTap.autoSelect)}
data-cancel_on_tap_outside={Boolean(config.oneTap.closeOnTapOutside)}
id="g_id_onload"
data-client_id={config.clientId}
// TODO: implement handleCredentialResponse page

Check warning on line 99 in src/theme/Layout/index.tsx

View workflow job for this annotation

GitHub Actions / main-lint

Unexpected 'todo' comment: 'TODO: implement handleCredentialResponse...'
data-callback="handleCredentialResponse"
Copy link
Preview
Copilot AI May 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider adding documentation or clarifying the TODO for the 'handleCredentialResponse' callback to guide future implementations.

Copilot uses AI. Check for mistakes.

data-context="signin"
></div>
)}
<Layout {...props} />
</>
);
}
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
},
"include": [
"src",
"src/theme",
"docs",
"i18n",
"plugins",
Expand Down
Loading
0