From c458d84d1769544ba0c48745163fdf1b0f7ccf8f Mon Sep 17 00:00:00 2001 From: Dharrnn Date: Thu, 29 May 2025 18:15:46 +0530 Subject: [PATCH] Feat: Added Continue with Github using Supabase sign in OAuth --- frontend/src/app/auth/github-popup/page.tsx | 80 +++++++++++++++++ frontend/src/app/auth/page.tsx | 13 ++- frontend/src/components/GithubSignIn.tsx | 90 +++++++++++++++++++ frontend/src/components/home/icons.tsx | 23 ++++- .../components/home/sections/hero-section.tsx | 6 +- 5 files changed, 206 insertions(+), 6 deletions(-) create mode 100644 frontend/src/app/auth/github-popup/page.tsx create mode 100644 frontend/src/components/GithubSignIn.tsx diff --git a/frontend/src/app/auth/github-popup/page.tsx b/frontend/src/app/auth/github-popup/page.tsx new file mode 100644 index 00000000..72938071 --- /dev/null +++ b/frontend/src/app/auth/github-popup/page.tsx @@ -0,0 +1,80 @@ +'use client'; + +import { useEffect } from 'react'; +import { createClient } from '@/lib/supabase/client'; + +export default function GitHubOAuthPopup() { + useEffect(() => { + const supabase = createClient(); + const returnUrl = sessionStorage.getItem('returnUrl') || '/dashboard'; + + const finish = (type: 'success' | 'error', message?: string) => { + try { + if (window.opener) { + window.opener.postMessage( + type === 'success' + ? { type: 'github-auth-success', returnUrl } + : { + type: 'github-auth-error', + message: message || 'GitHub sign-in failed', + }, + window.opener.origin || '*', + ); + } + } catch (err) { + console.warn('Failed to post message to opener:', err); + } + + setTimeout(() => window.close(), 150); // Give time for message delivery + }; + + const handleOAuth = async () => { + const isOAuthCallback = new URLSearchParams(window.location.search).has( + 'code', + ); + + if (isOAuthCallback) { + try { + const { + data: { session }, + } = await supabase.auth.getSession(); + + if (session) { + return finish('success'); + } + + // Fallback if session is not yet populated + supabase.auth.onAuthStateChange((_event, session) => { + if (session) finish('success'); + }); + + return; + } catch (err: any) { + console.error('Session error:', err); + return finish('error', err.message); + } + } + + // Start the GitHub OAuth flow + try { + await supabase.auth.signInWithOAuth({ + provider: 'github', + options: { + redirectTo: `${window.location.origin}/auth/github-popup`, + }, + }); + } catch (err: any) { + console.error('OAuth start error:', err); + finish('error', err.message); + } + }; + + handleOAuth(); + }, []); + + return ( +
+ Completing GitHub sign-in... +
+ ); +} diff --git a/frontend/src/app/auth/page.tsx b/frontend/src/app/auth/page.tsx index b90c143b..c3599390 100644 --- a/frontend/src/app/auth/page.tsx +++ b/frontend/src/app/auth/page.tsx @@ -28,6 +28,7 @@ import { DialogDescription, DialogFooter, } from '@/components/ui/dialog'; +import GitHubSignIn from '@/components/GithubSignIn'; function LoginContent() { const router = useRouter(); @@ -387,11 +388,17 @@ function LoginContent() { )} - {/* Google Sign In */} -
- + {/* OAuth Sign In */} +
+
+ +
+
+ +
+ {/* Divider */}
diff --git a/frontend/src/components/GithubSignIn.tsx b/frontend/src/components/GithubSignIn.tsx new file mode 100644 index 00000000..9dfc52f8 --- /dev/null +++ b/frontend/src/components/GithubSignIn.tsx @@ -0,0 +1,90 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useTheme } from 'next-themes'; +import { toast } from 'sonner'; +import { Icons } from './home/icons'; + +interface GitHubSignInProps { + returnUrl?: string; +} + +export default function GitHubSignIn({ returnUrl }: GitHubSignInProps) { + const [isLoading, setIsLoading] = useState(false); + const { resolvedTheme } = useTheme(); + + useEffect(() => { + const handler = (event: MessageEvent) => { + if (event.origin !== window.location.origin) return; + + if (event.data?.type === 'github-auth-success') { + sessionStorage.removeItem('isGitHubAuthInProgress'); + setIsLoading(false); + window.location.href = + event.data.returnUrl || returnUrl || '/dashboard'; + } + + if (event.data?.type === 'github-auth-error') { + sessionStorage.removeItem('isGitHubAuthInProgress'); + setIsLoading(false); + toast.error(event.data.message || 'GitHub sign-in failed.'); + } + }; + + window.addEventListener('message', handler); + return () => window.removeEventListener('message', handler); + }, [returnUrl]); + + const handleGitHubSignIn = () => { + const popup = window.open( + `${window.location.origin}/auth/github-popup`, + 'GitHubOAuth', + 'width=500,height=600', + ); + + if (!popup) { + toast.error('Popup was blocked. Please enable popups and try again.'); + return; + } + + setTimeout(() => { + sessionStorage.setItem('isGitHubAuthInProgress', '1'); + setIsLoading(true); + + const interval = setInterval(() => { + if (popup.closed) { + clearInterval(interval); + setIsLoading(false); + + // Delay toast to allow success postMessage to clear the flag + setTimeout(() => { + if (sessionStorage.getItem('isGitHubAuthInProgress')) { + sessionStorage.removeItem('isGitHubAuthInProgress'); + toast.error('GitHub sign-in was not completed.'); + } + }, 300); + } + }, 500); + }, 0); + }; + + return ( + + + ); +} diff --git a/frontend/src/components/home/icons.tsx b/frontend/src/components/home/icons.tsx index 7e4e693a..44612fd8 100644 --- a/frontend/src/components/home/icons.tsx +++ b/frontend/src/components/home/icons.tsx @@ -2461,4 +2461,25 @@ export const Icons = { ), -}; + github: ({ + className, + color = 'currentColor', + }: { + className?: string; + color?: string; + }) => ( + + ), +}; \ No newline at end of file diff --git a/frontend/src/components/home/sections/hero-section.tsx b/frontend/src/components/home/sections/hero-section.tsx index 1025834b..8c70a39b 100644 --- a/frontend/src/components/home/sections/hero-section.tsx +++ b/frontend/src/components/home/sections/hero-section.tsx @@ -34,6 +34,7 @@ import { useAccounts } from '@/hooks/use-accounts'; import { isLocalMode, config } from '@/lib/config'; import { toast } from 'sonner'; import { useModal } from '@/hooks/use-modal-store'; +import GitHubSignIn from '@/components/GithubSignIn'; // Custom dialog overlay with blur effect const BlurredDialogOverlay = () => ( @@ -145,7 +146,7 @@ export function HeroSection() { if (error instanceof BillingError) { console.log('Handling BillingError from hero section:', error.detail); // Open the payment required dialog modal instead of showing the alert - onOpen("paymentRequiredDialog"); + onOpen('paymentRequiredDialog'); // Don't show toast for billing errors } else { // Handle other errors (e.g., network, other API errors) @@ -380,9 +381,10 @@ export function HeroSection() {
)} - {/* Google Sign In */} + {/* OAuth Sign In */}
+
{/* Divider */}