Improve the logged out share experience

This commit is contained in:
sabaimran 2024-07-11 20:11:21 +05:30
parent 6f1d799759
commit bea0aa5445
6 changed files with 102 additions and 51 deletions

View file

@ -39,11 +39,15 @@ import {
AlertDialogAction,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle
} from '@/components/ui/alert-dialog';
import { Popover, PopoverContent } from '@/components/ui/popover';
import { PopoverTrigger } from '@radix-ui/react-popover';
import Link from 'next/link';
import { AlertDialogCancel } from '@radix-ui/react-alert-dialog';
import LoginPrompt from '../loginPrompt/loginPrompt';
export interface ChatOptions {
[key: string]: string
@ -66,6 +70,8 @@ export default function ChatInputArea(props: ChatInputProps) {
const [warning, setWarning] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const [uploading, setUploading] = useState(false);
const [loginRedirectMessage, setLoginRedirectMessage] = useState<string | null>(null);
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
const [progressValue, setProgressValue] = useState(0);
@ -87,7 +93,15 @@ export default function ChatInputArea(props: ChatInputProps) {
}, [uploading]);
function onSendMessage() {
props.sendMessage(message);
if (!message.trim()) return;
if (!props.isLoggedIn) {
setLoginRedirectMessage('Hey there, you need to be signed in to send messages to Khoj AI');
setShowLoginPrompt(true);
return;
}
props.sendMessage(message.trim());
setMessage('');
}
@ -103,6 +117,12 @@ export default function ChatInputArea(props: ChatInputProps) {
function handleFileChange(event: React.ChangeEvent<HTMLInputElement>) {
if (!event.target.files) return;
if (!props.isLoggedIn) {
setLoginRedirectMessage('Whoa! You need to login to upload files');
setShowLoginPrompt(true);
return;
}
uploadDataForIndexing(
event.target.files,
setWarning,
@ -153,6 +173,13 @@ export default function ChatInputArea(props: ChatInputProps) {
return (
<>
{
showLoginPrompt && loginRedirectMessage && (
<LoginPrompt
onOpenChange={setShowLoginPrompt}
loginRedirectMessage={loginRedirectMessage} />
)
}
{
uploading && (
<AlertDialog

View file

@ -1,24 +0,0 @@
/* HTML: <div class="loader"></div> */
.loader {
--c: conic-gradient(from -90deg, hsla(var(--secondary)) 90deg, #0000 0);
background: var(--c), var(--c);
background-size: 40% 40%;
animation: l19 1s infinite alternate;
}
@keyframes l19 {
0%,
10% {
background-position: 0 0, 0 calc(100%/3)
}
50% {
background-position: 0 0, calc(100%/3) calc(100%/3)
}
90%,
100% {
background-position: 0 0, calc(100%/3) 0
}
}

View file

@ -1,10 +1,11 @@
import styles from './loading.module.css';
import { CircleNotch } from '@phosphor-icons/react';
export default function Loading() {
return (
<div className={`${styles.loader} h-[100vh] flex items-center justify-center`}></div>
// NOTE: We can display usage tips here for casual learning moments.
<div className={`bg-background opacity-50 flex items-center justify-center h-screen`}>
<div>Loading <span><CircleNotch className={`inline animate-spin h-5 w-5`} /></span></div>
</div>
);
}

View file

@ -0,0 +1,42 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle
} from '@/components/ui/alert-dialog';
import Link from 'next/link';
export interface LoginPromptProps {
loginRedirectMessage: string;
onOpenChange: (open: boolean) => void;
}
export default function LoginPrompt(props: LoginPromptProps) {
return (
<AlertDialog
open={true}
onOpenChange={props.onOpenChange}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Sign in to Khoj to continue</AlertDialogTitle>
</AlertDialogHeader>
<AlertDialogDescription>{props.loginRedirectMessage}. By logging in, you agree to our <Link href="https://khoj.dev/terms-of-service">Terms of Service.</Link></AlertDialogDescription>
<AlertDialogFooter>
<AlertDialogCancel>Dismiss</AlertDialogCancel>
<AlertDialogAction className='bg-slate-400 hover:bg-slate-500'
onClick={() => {
window.location.href = `/login?next=${encodeURIComponent(window.location.pathname)}`;
}}>
<Link href={`/login?next=${encodeURIComponent(window.location.pathname)}`}> {/* Redirect to login page */}
Login
</Link>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}

View file

@ -4,7 +4,7 @@ import styles from "./sidePanel.module.css";
import { Suspense, useEffect, useState } from "react";
import { UserProfile } from "@/app/common/auth";
import { UserProfile, useAuthenticatedData } from "@/app/common/auth";
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
import Link from "next/link";
import useSWR from "swr";
@ -45,7 +45,7 @@ import {
import { ScrollArea } from "@/components/ui/scroll-area";
import { ArrowRight, ArrowLeft, ArrowDown, Spinner, Check, FolderPlus, DotsThreeVertical } from "@phosphor-icons/react";
import { ArrowRight, ArrowLeft, ArrowDown, Spinner, Check, FolderPlus, DotsThreeVertical, House, StackPlus, UserCirclePlus } from "@phosphor-icons/react";
interface ChatHistory {
conversation_id: string;
@ -496,7 +496,7 @@ function ChatSessionActionMenu(props: ChatSessionActionMenuProps) {
<DropdownMenu
onOpenChange={(open) => setIsOpen(open)}
open={isOpen}>
<DropdownMenuTrigger><DotsThreeVertical className="h-4 w-4"/></DropdownMenuTrigger>
<DropdownMenuTrigger><DotsThreeVertical className="h-4 w-4" /></DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<Button className="p-0 text-sm h-auto" variant={'ghost'} onClick={() => setIsRenaming(true)}>
@ -656,9 +656,9 @@ export default function SidePanel(props: SidePanelProps) {
const [subsetOrganizedData, setSubsetOrganizedData] = useState<GroupedChatHistory | null>(null);
const [enabled, setEnabled] = useState(false);
const [userProfile, setUserProfile] = useState<UserProfile | null>(null);
const authenticatedData = useAuthenticatedData();
const { data: chatSessions } = useChatSessionsFetchRequest(authenticatedData ? `/api/chat/sessions` : '');
const { data: chatSessions } = useChatSessionsFetchRequest('/api/chat/sessions');
const [isMobileWidth, setIsMobileWidth] = useState(false);
@ -707,16 +707,6 @@ export default function SidePanel(props: SidePanelProps) {
window.addEventListener('resize', () => {
setIsMobileWidth(window.innerWidth < 768);
});
fetch('/api/v1/user', { method: 'GET' })
.then(response => response.json())
.then((data: UserProfile) => {
setUserProfile(data);
})
.catch(err => {
console.error(err);
return;
});
}, []);
return (
@ -728,6 +718,7 @@ export default function SidePanel(props: SidePanelProps) {
height={40}
/>
{
authenticatedData &&
isMobileWidth ?
<Drawer>
<DrawerTrigger><ArrowRight className="h-4 w-4 mx-2" /></DrawerTrigger>
@ -744,7 +735,7 @@ export default function SidePanel(props: SidePanelProps) {
organizedData={organizedData}
data={data}
uploadedFiles={props.uploadedFiles}
userProfile={userProfile}
userProfile={authenticatedData}
conversationId={props.conversationId}
isMobileWidth={isMobileWidth}
/>
@ -763,7 +754,7 @@ export default function SidePanel(props: SidePanelProps) {
}
</div>
{
enabled &&
authenticatedData && enabled &&
<div className={`${styles.panelWrapper}`}>
<SessionsAndFiles
webSocketConnected={props.webSocketConnected}
@ -772,13 +763,26 @@ export default function SidePanel(props: SidePanelProps) {
organizedData={organizedData}
data={data}
uploadedFiles={props.uploadedFiles}
userProfile={userProfile}
userProfile={authenticatedData}
conversationId={props.conversationId}
isMobileWidth={isMobileWidth}
/>
</div>
}
{
!authenticatedData && enabled &&
<div className={`${styles.panelWrapper}`}>
<Link href="/">
<Button variant="ghost"><House className="h-4 w-4 mr-1" />Home</Button>
</Link>
<Link href="/">
<Button variant="ghost"><StackPlus className="h-4 w-4 mr-1" />New Conversation</Button>
</Link>
<Link href={`/login?next=${encodeURIComponent(window.location.pathname)}`}> {/* Redirect to login page */}
<Button variant="default"><UserCirclePlus className="h-4 w-4 mr-1"/>Sign Up</Button>
</Link>
</div>
}
</div>
);
}

View file

@ -76,7 +76,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
<ChatInputArea
isLoggedIn={props.isLoggedIn}
sendMessage={(message) => setMessage(message)}
sendDisabled={!props.isLoggedIn || processingMessage}
sendDisabled={processingMessage}
chatOptionsData={props.chatOptionsData}
conversationId={props.conversationId}
isMobileWidth={props.isMobileWidth}
@ -280,7 +280,7 @@ export default function SharedChat({ params }: { params: { slug: string } }) {
return (
<div className={`${styles.main} ${authenticatedData ? styles.chatLayout : ''}`}>
<div className={`${styles.main} ${styles.chatLayout}`}>
<title>
{title}
</title>
@ -290,6 +290,7 @@ export default function SharedChat({ params }: { params: { slug: string } }) {
conversationId={conversationId ?? null}
uploadedFiles={uploadedFiles} />
</div>
<div className={styles.chatBox}>
<NavMenu selected="Chat" title={title} />
<div className={styles.chatBoxBody}>