logged out chat modal, call to action
This commit is contained in:
@@ -5,15 +5,9 @@ import ChatInterface from '~/app/chat/_components/ChatInterface'
|
|||||||
import { useMessages } from '~/app/_providers/MessagesProvider';
|
import { useMessages } from '~/app/_providers/MessagesProvider';
|
||||||
import { Spinner } from '~/components/ui/spinner';
|
import { Spinner } from '~/components/ui/spinner';
|
||||||
|
|
||||||
type DBMessage = {
|
|
||||||
id: string
|
|
||||||
role: 'user' | 'assistant'
|
|
||||||
content: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ChatModal() {
|
export default function ChatModal() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const {messages,session,clearChat,clearingChat,isLoading,error,refetchMessages} = useMessages()
|
const {messages,session,isLoading,error} = useMessages()
|
||||||
return (
|
return (
|
||||||
<Dialog modal={true} open onOpenChange={() => router.back()}>
|
<Dialog modal={true} open onOpenChange={() => router.back()}>
|
||||||
<DialogContent className="w-full max-w-full rounded-none sm:max-w-full h-[100svh] lg:max-w-3xl lg:rounded-xl lg:h-[80vh] flex flex-col p-0 gap-0">
|
<DialogContent className="w-full max-w-full rounded-none sm:max-w-full h-[100svh] lg:max-w-3xl lg:rounded-xl lg:h-[80vh] flex flex-col p-0 gap-0">
|
||||||
@@ -21,8 +15,8 @@ export default function ChatModal() {
|
|||||||
<DialogTitle>Talk To My AI-Assistant</DialogTitle>
|
<DialogTitle>Talk To My AI-Assistant</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="flex-1 overflow-hidden min-h-0">
|
<div className="flex-1 overflow-hidden min-h-0">
|
||||||
{messages && session?.id &&
|
{!isLoading &&
|
||||||
<ChatInterface sessionId={session.id} dbMessages={messages}/>
|
<ChatInterface sessionId={session?.id} dbMessages={messages ?? []}/>
|
||||||
}
|
}
|
||||||
{isLoading &&
|
{isLoading &&
|
||||||
<><Spinner/> Loading Messages...</>
|
<><Spinner/> Loading Messages...</>
|
||||||
|
|||||||
@@ -1,16 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { Skeleton } from '~/components/ui/skeleton';
|
|
||||||
import ChatModal from './_components/ChatModal'
|
import ChatModal from './_components/ChatModal'
|
||||||
import { trpc } from '~/app/_trpc/Client'
|
|
||||||
import { useTimeLine } from '~/app/_providers/GsapProvicer';
|
|
||||||
|
|
||||||
export default function AssistantModalPage() {
|
export default function AssistantModalPage() {
|
||||||
const { data: session, error, isLoading } = trpc.chat.getSession.useQuery();
|
|
||||||
return (
|
return (
|
||||||
<>
|
<ChatModal/>
|
||||||
<ChatModal/>
|
|
||||||
{error && <div>{error.message}</div>}
|
|
||||||
{isLoading && <Skeleton />}
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { MessageCircle } from 'lucide-react'
|
import { MessageCircle } from 'lucide-react'
|
||||||
import { Show } from '@clerk/nextjs'
|
|
||||||
import { Button } from '~/components/ui/button'
|
import { Button } from '~/components/ui/button'
|
||||||
import { usePathname } from 'next/navigation'
|
import { usePathname } from 'next/navigation'
|
||||||
export default function ChatFAB() {
|
export default function ChatFAB() {
|
||||||
@@ -10,15 +9,13 @@ export default function ChatFAB() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!isChat &&
|
{!isChat &&
|
||||||
<Show when="signed-in">
|
<div className="fixed bottom-6 right-6 z-50">
|
||||||
<div className="fixed bottom-6 right-6 z-50">
|
<Button asChild size="icon" className="h-14 w-14 rounded-full shadow-lg">
|
||||||
<Button asChild size="icon" className="h-14 w-14 rounded-full shadow-lg">
|
<Link href="/assistant">
|
||||||
<Link href="/assistant">
|
<MessageCircle className="h-6 w-6" />
|
||||||
<MessageCircle className="h-6 w-6" />
|
</Link>
|
||||||
</Link>
|
</Button>
|
||||||
</Button>
|
</div>
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { inferRouterOutputs } from '@trpc/server';
|
import type { inferRouterOutputs } from '@trpc/server';
|
||||||
|
import { useUser } from '@clerk/nextjs'
|
||||||
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'
|
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'
|
||||||
import { trpc } from '~/app/_trpc/Client'
|
import { trpc } from '~/app/_trpc/Client'
|
||||||
import { type ChatRouter } from '~/server/routers/chat'
|
import { type ChatRouter } from '~/server/routers/chat'
|
||||||
@@ -26,15 +27,29 @@ export const useMessages = () => useContext(MessageContext)
|
|||||||
export const MessagesProvider = ({children}:{children:ReactNode}) => {
|
export const MessagesProvider = ({children}:{children:ReactNode}) => {
|
||||||
const [error,setError] = useState<string|null>(null)
|
const [error,setError] = useState<string|null>(null)
|
||||||
const [isLoading,setIsLoading] = useState<boolean>(true)
|
const [isLoading,setIsLoading] = useState<boolean>(true)
|
||||||
const { data: session,error:sessionError,isLoading:sessionLoading} = trpc.chat.getSession.useQuery()
|
const { isLoaded, isSignedIn } = useUser()
|
||||||
const { data: messages, refetch, error:messageError, isLoading:messagesLoading } = trpc.chat.getMessages.useQuery(session?.id ? session.id : "")
|
const { data: session,error:sessionError,isLoading:sessionLoading} = trpc.chat.getSession.useQuery(undefined, {
|
||||||
|
enabled: isSignedIn === true,
|
||||||
|
})
|
||||||
|
const { data: messages, refetch, error:messageError, isLoading:messagesLoading } = trpc.chat.getMessages.useQuery(session?.id ? session.id : "", {
|
||||||
|
enabled: isSignedIn === true && session?.id != undefined,
|
||||||
|
})
|
||||||
const { mutate ,isPending:clearingChat,isSuccess:clearedChat } = trpc.chat.clearChat.useMutation()
|
const { mutate ,isPending:clearingChat,isSuccess:clearedChat } = trpc.chat.clearChat.useMutation()
|
||||||
const utils = trpc.useUtils()
|
const utils = trpc.useUtils()
|
||||||
const refetchMessages = () => {
|
const refetchMessages = () => {
|
||||||
|
if (!isSignedIn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
utils.chat.getMessages.invalidate()
|
utils.chat.getMessages.invalidate()
|
||||||
refetch()
|
refetch()
|
||||||
}
|
}
|
||||||
const clearChat = (callback?: () => void) => {
|
const clearChat = (callback?: () => void) => {
|
||||||
|
if (!isSignedIn) {
|
||||||
|
if (callback) {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
mutate(undefined,{onSuccess: () => {
|
mutate(undefined,{onSuccess: () => {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback()
|
callback()
|
||||||
@@ -43,18 +58,29 @@ export const MessagesProvider = ({children}:{children:ReactNode}) => {
|
|||||||
}})
|
}})
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (isSignedIn !== true) {
|
||||||
|
setError(null)
|
||||||
|
return;
|
||||||
|
}
|
||||||
messageError && setError(messageError.message)
|
messageError && setError(messageError.message)
|
||||||
sessionError && setError(sessionError.message)
|
sessionError && setError(sessionError.message)
|
||||||
},[messageError,sessionError])
|
},[messageError,sessionError,isSignedIn])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
!sessionLoading && !messagesLoading && setIsLoading(false)
|
if (!isLoaded) {
|
||||||
sessionLoading || messagesLoading && setIsLoading(true)
|
setIsLoading(true)
|
||||||
},[sessionLoading,messagesLoading])
|
return;
|
||||||
|
}
|
||||||
|
if (isSignedIn !== true) {
|
||||||
|
setIsLoading(false)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setIsLoading(sessionLoading || messagesLoading)
|
||||||
|
},[isLoaded,isSignedIn,sessionLoading,messagesLoading])
|
||||||
return (
|
return (
|
||||||
<MessageContext.Provider value={
|
<MessageContext.Provider value={
|
||||||
{
|
{
|
||||||
session,
|
session: isSignedIn === true ? session : undefined,
|
||||||
messages,
|
messages: isSignedIn === true ? messages : undefined,
|
||||||
refetchMessages,
|
refetchMessages,
|
||||||
clearChat,
|
clearChat,
|
||||||
error,
|
error,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useChat } from '@ai-sdk/react'
|
|||||||
import { DefaultChatTransport, type UIMessage } from 'ai'
|
import { DefaultChatTransport, type UIMessage } from 'ai'
|
||||||
import { Button } from '~/components/ui/button'
|
import { Button } from '~/components/ui/button'
|
||||||
import { Textarea } from '~/components/ui/textarea'
|
import { Textarea } from '~/components/ui/textarea'
|
||||||
|
import { SignInButton } from '@clerk/nextjs'
|
||||||
import {
|
import {
|
||||||
useGsapContext,
|
useGsapContext,
|
||||||
} from '~/app/_providers/GsapProvicer';
|
} from '~/app/_providers/GsapProvicer';
|
||||||
@@ -18,10 +19,26 @@ interface DBMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ChatInterfaceProps {
|
interface ChatInterfaceProps {
|
||||||
sessionId: string,
|
sessionId?: string,
|
||||||
dbMessages: DBMessage[],
|
dbMessages: DBMessage[],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SignInChatPrompt() {
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full flex-col items-center justify-center gap-4 text-center">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<h2 className="text-xl font-semibold">Sign in to use the chat</h2>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
You need to be signed in before you can talk to Gregor's AI assistant.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<SignInButton mode="modal">
|
||||||
|
<Button type="button">Sign in</Button>
|
||||||
|
</SignInButton>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function toUIMessages(dbMessages: DBMessage[]): UIMessage[] {
|
function toUIMessages(dbMessages: DBMessage[]): UIMessage[] {
|
||||||
return dbMessages.map((m) => ({
|
return dbMessages.map((m) => ({
|
||||||
id: m.id,
|
id: m.id,
|
||||||
@@ -43,7 +60,7 @@ function addInitMessage(messageArray: UIMessage[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ChatInterface({ dbMessages, sessionId }: ChatInterfaceProps) {
|
function AuthenticatedChatInterface({ dbMessages, sessionId }: ChatInterfaceProps & { sessionId: string }) {
|
||||||
const [input, setInput] = useState('')
|
const [input, setInput] = useState('')
|
||||||
const { clearingChat, clearChat, refetchMessages } = useMessages();
|
const { clearingChat, clearChat, refetchMessages } = useMessages();
|
||||||
const initialMessages = toUIMessages(dbMessages)
|
const initialMessages = toUIMessages(dbMessages)
|
||||||
@@ -115,7 +132,7 @@ export default function ChatInterface({ dbMessages, sessionId }: ChatInterfacePr
|
|||||||
<div className='flex flex-col gap-2'>
|
<div className='flex flex-col gap-2'>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
disabled={status != "ready" || !input.trim() || sessionId == undefined}
|
disabled={status != "ready" || !input.trim()}
|
||||||
>
|
>
|
||||||
Send
|
Send
|
||||||
</Button>
|
</Button>
|
||||||
@@ -140,3 +157,10 @@ export default function ChatInterface({ dbMessages, sessionId }: ChatInterfacePr
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function ChatInterface({ dbMessages, sessionId }: ChatInterfaceProps) {
|
||||||
|
if (sessionId == undefined) {
|
||||||
|
return <SignInChatPrompt />
|
||||||
|
}
|
||||||
|
return <AuthenticatedChatInterface sessionId={sessionId} dbMessages={dbMessages} />
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import ChatInterface from './_components/ChatInterface'
|
import ChatInterface from './_components/ChatInterface'
|
||||||
import { trpc } from '../_trpc/Client';
|
|
||||||
import { Skeleton } from '~/components/ui/skeleton';
|
|
||||||
import AnimatedPageTitle from '../_components/Animated/AnimatedPageTitle';
|
import AnimatedPageTitle from '../_components/Animated/AnimatedPageTitle';
|
||||||
import { useTimeLine } from '../_providers/GsapProvicer';
|
import { useTimeLine } from '../_providers/GsapProvicer';
|
||||||
import { useMessages } from '../_providers/MessagesProvider';
|
import { useMessages } from '../_providers/MessagesProvider';
|
||||||
import { Spinner } from '~/components/ui/spinner';
|
import { Spinner } from '~/components/ui/spinner';
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
export default function ChatPage() {
|
export default function ChatPage() {
|
||||||
const {messages,session,clearChat,clearingChat,isLoading,error,refetchMessages} = useMessages()
|
const {messages,session,isLoading,error} = useMessages()
|
||||||
useTimeLine(messages)
|
useTimeLine(messages)
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col px-10 lg:px-0 w-full h-full max-w-4xl mx-auto pt-10">
|
<div className="flex flex-col px-10 lg:px-0 w-full h-full max-w-4xl mx-auto pt-10">
|
||||||
@@ -17,8 +14,8 @@ export default function ChatPage() {
|
|||||||
<span>Talk To My </span> <span> AI-Assistant</span>
|
<span>Talk To My </span> <span> AI-Assistant</span>
|
||||||
</AnimatedPageTitle>
|
</AnimatedPageTitle>
|
||||||
<div className='flex items-center h-[80%] w-full my-auto w-full'>
|
<div className='flex items-center h-[80%] w-full my-auto w-full'>
|
||||||
{messages && session?.id &&
|
{!isLoading &&
|
||||||
<ChatInterface sessionId={session.id} dbMessages={messages}/>
|
<ChatInterface sessionId={session?.id} dbMessages={messages ?? []}/>
|
||||||
}
|
}
|
||||||
{isLoading &&
|
{isLoading &&
|
||||||
<><Spinner/> Loading Messages...</>
|
<><Spinner/> Loading Messages...</>
|
||||||
|
|||||||
Reference in New Issue
Block a user