From 4ce06168f38dd4dcaaed39fa88f89f44e71bccf2 Mon Sep 17 00:00:00 2001 From: kuekhaoyang Date: Sat, 29 Nov 2025 18:20:54 +0800 Subject: [PATCH] feat: Dynamically load Google API client, remove `gapi-script` dependency, and mark sync settings as client-side. --- components/settings/SyncSettings.tsx | 2 ++ lib/hooks/useGoogleDrive.ts | 20 ++++++++++++++++++-- package-lock.json | 11 +++++++++++ package.json | 1 + 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/components/settings/SyncSettings.tsx b/components/settings/SyncSettings.tsx index c595bf7..be56200 100644 --- a/components/settings/SyncSettings.tsx +++ b/components/settings/SyncSettings.tsx @@ -1,3 +1,5 @@ +'use client'; + import { useSync } from '@/lib/hooks/useSync'; import { Icons } from '@/components/ui/Icon'; import { Button } from '@/components/ui/Button'; diff --git a/lib/hooks/useGoogleDrive.ts b/lib/hooks/useGoogleDrive.ts index 434d113..e86d247 100644 --- a/lib/hooks/useGoogleDrive.ts +++ b/lib/hooks/useGoogleDrive.ts @@ -1,10 +1,12 @@ import { useState, useEffect, useCallback } from 'react'; -import { gapi } from 'gapi-script'; const DISCOVERY_DOCS = ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest']; const SCOPES = 'https://www.googleapis.com/auth/drive.appdata'; const APP_DATA_FILENAME = 'kvideo_data.json'; +// Type declarations for gapi +declare const gapi: any; + export interface GoogleUser { name: string; email: string; @@ -24,9 +26,23 @@ export function useGoogleDrive() { const [error, setError] = useState(null); useEffect(() => { + // Only run on client side + if (typeof window === 'undefined') return; + const initClient = async () => { try { - await new Promise((resolve, reject) => { + // Dynamically load gapi script + if (!window.gapi) { + await new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = 'https://apis.google.com/js/api.js'; + script.onload = () => resolve(); + script.onerror = () => reject(new Error('Failed to load gapi script')); + document.body.appendChild(script); + }); + } + + await new Promise((resolve) => { gapi.load('client:auth2', () => { resolve(); }); diff --git a/package-lock.json b/package-lock.json index 9a58b10..7717de8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", + "@types/gapi.auth2": "^0.0.61", "@types/node": "^24", "@types/react": "^19", "@types/react-dom": "^19", @@ -1620,6 +1621,16 @@ "integrity": "sha512-/ZsLuq6BffMgbKMtZyDZ8vwQvTyKhKQ1G2K6VyWCgtHHhfSSXbk4+4JwImZiTjWNXfI2q1ZStAwFFHSkNoTkHA==", "license": "MIT" }, + "node_modules/@types/gapi.auth2": { + "version": "0.0.61", + "resolved": "https://registry.npmjs.org/@types/gapi.auth2/-/gapi.auth2-0.0.61.tgz", + "integrity": "sha512-cn+omiRoE/LTxZncnVl1QhcLggOT0sJ8Yz9RXIsw5R2zLyRf+0o6kaZzJ/Gr3Sxz6i7J/+PbXAF8yeZipCaiWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/gapi": "*" + } + }, "node_modules/@types/gapi.client": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/gapi.client/-/gapi.client-1.0.8.tgz", diff --git a/package.json b/package.json index 07d9412..b165e8f 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", + "@types/gapi.auth2": "^0.0.61", "@types/node": "^24", "@types/react": "^19", "@types/react-dom": "^19",