From 384626fc0e14954ad112e2d354f3cc10b42715df Mon Sep 17 00:00:00 2001 From: Rupesh Date: Fri, 17 Apr 2026 16:45:21 +0530 Subject: [PATCH 1/4] commit --- src/services/geminiService.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/services/geminiService.js b/src/services/geminiService.js index b375565..c210173 100644 --- a/src/services/geminiService.js +++ b/src/services/geminiService.js @@ -1,6 +1,6 @@ import { GEMINI_MODEL } from '../config/env'; -export const callGeminiWithRetry = async (payload, apiKey, maxRetries = 3) => { +export const callGeminiWithRetry = async (payload, apiKey, maxRetries = 6) => { let retries = 0; while (retries < maxRetries) { try { @@ -31,11 +31,14 @@ export const callGeminiWithRetry = async (payload, apiKey, maxRetries = 3) => { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error?.message || `API request failed with status ${response.status}`); } catch (error) { - if (error.message.includes("Rate limit") || error.message.includes("Service unavailable") || (retries < maxRetries && error.name === 'TypeError')) { - // If we already handled 429/503 and exhausted retries, throw it - if (error.message.includes("Rate limit") || error.message.includes("Service unavailable")) throw error; - + if (error.message.includes("Rate limit") || error.message.includes("Service unavailable")) { + throw error; + } + if (error.name === 'TypeError') { retries++; + if (retries >= maxRetries) { + throw new Error(`Network error: Failed after ${maxRetries} attempts`); + } const waitTime = Math.pow(2, retries) * 1000; await new Promise(resolve => setTimeout(resolve, waitTime)); continue; @@ -43,4 +46,5 @@ export const callGeminiWithRetry = async (payload, apiKey, maxRetries = 3) => { throw error; } } + throw new Error(`Failed to generate content after ${maxRetries} attempts`); }; From 13eee1b190f68ed13b9d79419458b4a4a167bc3f Mon Sep 17 00:00:00 2001 From: Rupesh Date: Fri, 17 Apr 2026 17:53:40 +0530 Subject: [PATCH 2/4] commit --- package.json | 2 ++ public/background.js | 3 +++ public/manifest.extension.json | 35 ++++++++++++++++++++++++++++++++ src/contexts/AuthContext.jsx | 23 +++++++++++++++++++-- src/features/sidebar/Sidebar.jsx | 13 ++++++++---- 5 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 public/background.js create mode 100644 public/manifest.extension.json diff --git a/package.json b/package.json index e626573..192d97c 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "my-productivity-hub", "version": "0.1.0", "private": true, + "homepage": ".", "dependencies": { "@react-oauth/google": "^0.12.2", "@testing-library/dom": "^10.4.0", @@ -18,6 +19,7 @@ "scripts": { "start": "react-scripts start", "build": "react-scripts build", + "build:extension": "react-scripts build && node -e \"require('fs').copyFileSync('./build/manifest.extension.json', './build/manifest.json')\"", "test": "react-scripts test", "eject": "react-scripts eject" }, diff --git a/public/background.js b/public/background.js new file mode 100644 index 0000000..d7262b8 --- /dev/null +++ b/public/background.js @@ -0,0 +1,3 @@ +chrome.sidePanel + .setPanelBehavior({ openPanelOnActionClick: true }) + .catch((error) => console.error(error)); diff --git a/public/manifest.extension.json b/public/manifest.extension.json new file mode 100644 index 0000000..3fa3cae --- /dev/null +++ b/public/manifest.extension.json @@ -0,0 +1,35 @@ +{ + "name": "ProdHub Chrome Extension", + "version": "1.0", + "manifest_version": 3, + "description": "Your side-panel productivity hub.", + "background": { + "service_worker": "background.js" + }, + "permissions": [ + "sidePanel", + "identity" + ], + "oauth2": { + "client_id": "357244560688-4ibqpt07g54ns83qtimb2rtsa3p42uur.apps.googleusercontent.com", + "scopes": [ + "profile", + "email", + "openid" + ] + }, + "host_permissions": [ + "" + ], + "action": { + "default_title": "Open ProdHub Panel", + "default_icon": "logo192.png" + }, + "side_panel": { + "default_path": "index.html" + }, + "icons": { + "192": "logo192.png", + "512": "logo512.png" + } +} \ No newline at end of file diff --git a/src/contexts/AuthContext.jsx b/src/contexts/AuthContext.jsx index e94dbd1..08b6a9f 100644 --- a/src/contexts/AuthContext.jsx +++ b/src/contexts/AuthContext.jsx @@ -5,6 +5,7 @@ import { onAuthStateChanged, signInWithEmailAndPassword, signInWithPopup, + signInWithCredential, signOut, } from 'firebase/auth'; import { auth } from '../config/firebase'; @@ -37,8 +38,26 @@ export function AuthProvider({ children }) { const signInWithGoogle = async () => { if (!auth) throw new Error('Auth not configured'); - const provider = new GoogleAuthProvider(); - await signInWithPopup(auth, provider); + + if (window.chrome && window.chrome.identity) { + return new Promise((resolve, reject) => { + window.chrome.identity.getAuthToken({ interactive: true }, async (token) => { + if (window.chrome.runtime.lastError || !token) { + return reject(window.chrome.runtime.lastError || new Error('No OAuth token returned.')); + } + try { + const credential = GoogleAuthProvider.credential(null, token); + await signInWithCredential(auth, credential); + resolve(); + } catch (error) { + reject(error); + } + }); + }); + } else { + const provider = new GoogleAuthProvider(); + await signInWithPopup(auth, provider); + } }; const emailSignIn = async (email, password) => { diff --git a/src/features/sidebar/Sidebar.jsx b/src/features/sidebar/Sidebar.jsx index f39736d..9b86878 100644 --- a/src/features/sidebar/Sidebar.jsx +++ b/src/features/sidebar/Sidebar.jsx @@ -1,5 +1,5 @@ import React, { useMemo, useState } from 'react'; -import { Book, Calendar, CheckSquare, ChevronDown, ChevronRight, Edit2, LogOut, Plus, Repeat, Save, Sparkles, Trash2, TrendingUp } from 'lucide-react'; +import { Book, Calendar, CheckSquare, ChevronDown, ChevronRight, Edit2, LogOut, Plus, Repeat, Save, Sparkles, Trash2, TrendingUp, X } from 'lucide-react'; import { collection, doc, addDoc, updateDoc, writeBatch, getDocs, query, where } from 'firebase/firestore'; import { db } from '../../config/firebase'; import { appId } from '../../config/env'; @@ -58,9 +58,14 @@ export default function Sidebar({ onViewChange, projects, goals, userId, handleS setShowGoalModal(false)} userId={userId} />