"use client"; import styles from "./loginPrompt.module.css"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import Autoplay from "embla-carousel-autoplay"; import { ArrowLeft, ArrowsClockwise, LineVertical, PaperPlaneTilt, PencilSimple, Spinner, } from "@phosphor-icons/react"; import Link from "next/link"; import { useEffect, useRef, useState } from "react"; import useSWR from "swr"; import { GoogleSignIn } from "./GoogleSignIn"; import { Drawer, DrawerContent } from "@/components/ui/drawer"; import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, } from "@/components/ui/carousel"; import { Card, CardContent } from "@/components/ui/card"; import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp"; export interface LoginPromptProps { loginRedirectMessage: string; onOpenChange: (open: boolean) => void; isMobileWidth?: boolean; } const fetcher = (url: string) => fetch(url).then((res) => res.json()); const ALLOWED_OTP_ATTEMPTS = 5; interface Provider { client_id: string; redirect_uri: string; } interface CredentialsData { [provider: string]: Provider; } export default function LoginPrompt(props: LoginPromptProps) { const { data, error, isLoading } = useSWR("/auth/oauth/metadata", fetcher); const [useEmailSignIn, setUseEmailSignIn] = useState(false); const [email, setEmail] = useState(""); const [checkEmail, setCheckEmail] = useState(false); const [recheckEmail, setRecheckEmail] = useState(false); useEffect(() => { const google = (window as any).google; if (!google) return; // Initialize Google Sign In after script loads google.accounts.id.initialize({ client_id: data?.google?.client_id, callback: handleGoogleSignIn, auto_select: false, login_uri: data?.google?.redirect_uri, }); // Render the button google.accounts.id.renderButton(document.getElementById("g_id_signin")!, { theme: "outline", size: "large", width: "100%", }); }, [data]); const handleGoogleSignIn = () => { if (!data?.google?.client_id || !data?.google?.redirect_uri) return; // Create full redirect URL using current origin const fullRedirectUri = `${window.location.origin}${data.google.redirect_uri}`; const params = new URLSearchParams({ client_id: data.google.client_id, redirect_uri: fullRedirectUri, response_type: "code", scope: "email profile openid", state: window.location.pathname, access_type: "offline", prompt: "consent select_account", include_granted_scopes: "true", }); window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?${params}`; }; const handleGoogleScriptLoad = () => { const google = (window as any).google; if (!data?.google?.client_id || !data?.google?.redirect_uri) return; // Initialize Google Sign In after script loads google.accounts.id.initialize({ client_id: data?.google?.client_id, callback: handleGoogleSignIn, auto_select: false, login_uri: data?.google?.redirect_uri, }); // Render the button google.accounts.id.renderButton(document.getElementById("g_id_signin")!, { theme: "outline", size: "large", width: "100%", }); }; function handleMagicLinkSignIn() { fetch("/auth/magic", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ email: email }), }) .then((res) => { if (res.ok) { setCheckEmail(true); if (checkEmail) { setRecheckEmail(true); } return res.json(); } else { throw new Error("Failed to send magic link"); } }) .then((data) => { console.log(data); }) .catch((err) => { console.error(err); }); } if (props.isMobileWidth) { return (
{useEmailSignIn && ( )} {!useEmailSignIn && ( )}
); } return (
{useEmailSignIn && ( )} {!useEmailSignIn && ( )}
); } function EmailSignInContext({ email, setEmail, checkEmail, setCheckEmail, setUseEmailSignIn, recheckEmail, handleMagicLinkSignIn, }: { email: string; setEmail: (email: string) => void; checkEmail: boolean; setCheckEmail: (checkEmail: boolean) => void; setUseEmailSignIn: (useEmailSignIn: boolean) => void; recheckEmail: boolean; setRecheckEmail: (recheckEmail: boolean) => void; handleMagicLinkSignIn: () => void; }) { const [otp, setOTP] = useState(""); const [otpError, setOTPError] = useState(""); const [numFailures, setNumFailures] = useState(0); function checkOTPAndRedirect() { const verifyUrl = `/auth/magic?code=${otp}&email=${email}`; if (numFailures >= ALLOWED_OTP_ATTEMPTS) { setOTPError("Too many failed attempts. Please try again tomorrow."); return; } fetch(verifyUrl, { method: "GET", headers: { "Content-Type": "application/json", }, }) .then((res) => { if (res.ok) { // Check if the response is a redirect if (res.redirected) { window.location.href = res.url; } } else if (res.status === 401) { setOTPError("Invalid OTP."); setNumFailures(numFailures + 1); if (numFailures + 1 >= ALLOWED_OTP_ATTEMPTS) { setOTPError("Too many failed attempts. Please try again tomorrow."); } } else if (res.status === 429) { setOTPError("Too many failed attempts. Please try again tomorrow."); setNumFailures(ALLOWED_OTP_ATTEMPTS); } else { throw new Error("Failed to verify OTP"); } }) .catch((err) => { console.error(err); }); } return (
Get started with Khoj
{checkEmail ? recheckEmail ? `A new link has been sent to ${email}.` : `A one time sign in code has been sent to ${email}.` : "You will receive a sign-in code on the email address you provide below"}
{!checkEmail && ( <> { if (e.key === "Enter") { handleMagicLinkSignIn(); } }} onChange={(e) => setEmail(e.target.value)} /> )} {checkEmail && (
= ALLOWED_OTP_ATTEMPTS} onComplete={() => setTimeout(() => { checkOTPAndRedirect(); }, 1000) } > {otpError && (
{otpError} {ALLOWED_OTP_ATTEMPTS - numFailures} remaining attempts.
)}
)} {checkEmail && (
)}
); } function MainSignInContext({ handleGoogleScriptLoad, handleGoogleSignIn, isLoading, data, setUseEmailSignIn, isMobileWidth, }: { handleGoogleScriptLoad: () => void; handleGoogleSignIn: () => void; isLoading: boolean; data: CredentialsData | undefined; setUseEmailSignIn: (useEmailSignIn: boolean) => void; isMobileWidth: boolean; }) { const plugin = useRef(Autoplay({ delay: 4000, stopOnInteraction: true })); const [showArrows, setShowArrows] = useState(false); const tips = [ { src: "https://assets.khoj.dev/sign_in_demos/research_mode.gif", alt: "Research tip", description: "Perform deeper research to get informed, accurate answers.", }, { src: "https://assets.khoj.dev/sign_in_demos/custom_agents.gif", alt: "Personalize tip", description: "Create AI agents and customize their personality to discuss homework, office work or a hobby.", }, { src: "https://assets.khoj.dev/sign_in_demos/docment_questions.gif", alt: "Document tip", description: "Get verifiable answers from across the internet and your documents.", }, ]; return (
{!isMobileWidth && ( { plugin.current.stop(); setShowArrows(true); }} onMouseLeave={() => { plugin.current.play(); setShowArrows(false); }} > {tips.map((tip, index) => (
{tip.alt}
{tip.description}
))}
{showArrows && ( <> )}
)}
Sign in to unlock your second brain
{/*
*/} )} Continue with Google
By logging in, you agree to our{" "} Terms of Service. See{" "} Privacy Policy.
); }