From 1da3969f1b47cc1c4e7331ef129b53a7c3fb5ac1 Mon Sep 17 00:00:00 2001 From: Aj Wazzan Date: Thu, 15 May 2025 22:13:36 -0700 Subject: [PATCH] refactor: streamline AIChat and AISidebar components by removing unused variables and optimizing imports --- apps/mail/components/create/ai-chat.tsx | 90 +++---------------- apps/mail/components/ui/ai-sidebar.tsx | 112 +++++++++++++++++------- 2 files changed, 89 insertions(+), 113 deletions(-) diff --git a/apps/mail/components/create/ai-chat.tsx b/apps/mail/components/create/ai-chat.tsx index 37955ad3d..2b91658a5 100644 --- a/apps/mail/components/create/ai-chat.tsx +++ b/apps/mail/components/create/ai-chat.tsx @@ -164,76 +164,8 @@ export function AIChat({ const messagesEndRef = useRef(null); const messagesContainerRef = useRef(null); const inputRef = useRef(null); - const { refetch, chatMessages } = useBilling(); const [threadId] = useQueryState('threadId'); - const { refetch: refetchLabels } = useLabels(); - const { refetch: refetchStats } = useStats(); - const trpc = useTRPC(); - const queryClient = useQueryClient(); - const { refetch: refetchThread } = useThread(threadId); - const { folder } = useParams<{ folder: string }>(); - const [searchValue] = useSearchValue(); - const { attach, track, refetch: refetchBilling } = useBilling(); - - // Use internal chat state if external state is not provided - const { - messages: internalMessages, - input: internalInput, - setInput: internalSetInput, - error: internalError, - handleSubmit: internalHandleSubmit, - status: internalStatus, - stop: internalStop - } = useChat({ - api: `${env.NEXT_PUBLIC_BACKEND_URL}/api/chat`, - fetch: (url, options) => fetch(url, { ...options, credentials: 'include' }), - maxSteps: 5, - body: { - threadId: threadId ?? undefined, - currentFolder: folder ?? undefined, - currentFilter: searchValue.value ?? undefined, - }, - onError(error) { - console.error('Error in useChat', error); - toast.error('Error, please try again later'); - }, - onResponse: (response) => { - if (!response.ok) { - throw new Error('Failed to send message'); - } - }, - onFinish: () => {}, - async onToolCall({ toolCall }) { - console.warn('toolCall', toolCall); - switch (toolCall.toolName) { - case Tools.CreateLabel: - case Tools.DeleteLabel: - await refetchLabels(); - break; - case Tools.SendEmail: - await queryClient.invalidateQueries({ - queryKey: trpc.mail.listThreads.queryKey({ folder: 'sent' }), - }); - break; - case Tools.MarkThreadsRead: - case Tools.MarkThreadsUnread: - case Tools.ModifyLabels: - case Tools.BulkDelete: - console.log('modifyLabels', toolCall.args); - await refetchLabels(); - await Promise.all( - (toolCall.args as { threadIds: string[] }).threadIds.map((id) => - queryClient.invalidateQueries({ - queryKey: trpc.mail.get.queryKey({ id }), - }), - ), - ); - break; - } - await track({ featureId: 'chat-messages', value: 1 }); - await refetchBilling(); - }, - }); + const { attach, chatMessages } = useBilling(); const scrollToBottom = useCallback(() => { if (messagesEndRef.current) { @@ -307,18 +239,17 @@ export function AIChat({ {/* Threads below the bubble */} {toolParts.map((part, idx) => - part.toolInvocation && 'result' in part.toolInvocation && - part.toolInvocation.result && 'threads' in part.toolInvocation.result ? ( - + part.toolInvocation && + 'result' in part.toolInvocation && + part.toolInvocation.result && + 'threads' in part.toolInvocation.result ? ( + ) : part.toolInvocation && 'result' in part.toolInvocation ? ( Used tool: {part.toolInvocation.toolName} - ) : null + ) : null, )} {textParts.length > 0 && (
- {textParts.map((part) => ( - part.text && {part.text || ' '} - ))} + {textParts.map( + (part) => + part.text && {part.text || ' '}, + )}
)} diff --git a/apps/mail/components/ui/ai-sidebar.tsx b/apps/mail/components/ui/ai-sidebar.tsx index 3cf209c89..24d8362e7 100644 --- a/apps/mail/components/ui/ai-sidebar.tsx +++ b/apps/mail/components/ui/ai-sidebar.tsx @@ -8,29 +8,42 @@ import { DialogTitle, DialogTrigger, } from './dialog'; +import { + useState, + useEffect, + useContext, + createContext, + useCallback, + useMemo, + useRef, +} from 'react'; import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/components/ui/tooltip'; import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/ui/resizable'; -import { useState, useEffect, useContext, createContext, useCallback, useMemo, useRef } from 'react'; import { AI_SIDEBAR_COOKIE_NAME, SIDEBAR_COOKIE_MAX_AGE } from '@/lib/constants'; import { StyledEmailAssistantSystemPrompt, AiChatPrompt } from '@/lib/prompts'; -import { useEditor } from '@/components/providers/editor-provider'; +import { usePathname, useSearchParams, useParams } from 'next/navigation'; +import { useSearchValue } from '@/hooks/use-search-value'; +import { useQueryClient } from '@tanstack/react-query'; import { AIChat } from '@/components/create/ai-chat'; -import { useChat } from '@ai-sdk/react'; +import { useTRPC } from '@/providers/query-provider'; import { X, Paper } from '@/components/icons/icons'; import { GitBranchPlus, Plus } from 'lucide-react'; +import { Tools } from '../../../server/src/types'; import { useBilling } from '@/hooks/use-billing'; import { Button } from '@/components/ui/button'; import { useHotkeys } from 'react-hotkeys-hook'; +import { useLabels } from '@/hooks/use-labels'; import { Gauge } from '@/components/ui/gauge'; -import { usePathname, useSearchParams, useParams } from 'next/navigation'; import { useCustomer } from 'autumn-js/next'; +import { useChat } from '@ai-sdk/react'; import { getCookie } from '@/lib/utils'; import { Textarea } from './textarea'; +import { useQueryState } from 'nuqs'; import { cn } from '@/lib/utils'; -import Link from 'next/link'; -import Image from 'next/image'; import { env } from '@/lib/env'; +import Image from 'next/image'; import { toast } from 'sonner'; +import Link from 'next/link'; interface AISidebarProps { className?: string; @@ -82,10 +95,14 @@ export function AISidebar({ children, className }: AISidebarProps & { children: const { open, setOpen } = useAISidebar(); const [resetKey, setResetKey] = useState(0); const pathname = usePathname(); - const params = useSearchParams(); + const { attach, customer, chatMessages, track, refetch: refetchBilling } = useBilling(); + const queryClient = useQueryClient(); + const trpc = useTRPC(); + const [threadId] = useQueryState('threadId'); const { folder } = useParams<{ folder: string }>(); - const { chatMessages, attach, customer } = useBilling(); - + const { refetch: refetchLabels } = useLabels(); + const [searchValue] = useSearchValue(); + // Initialize shared chat state that will be used by both desktop and mobile views // This ensures conversation continuity when switching between viewport sizes const chatState = useChat({ @@ -93,9 +110,9 @@ export function AISidebar({ children, className }: AISidebarProps & { children: fetch: (url, options) => fetch(url, { ...options, credentials: 'include' }), maxSteps: 5, body: { - threadId: params.get('threadId') ?? undefined, + threadId: threadId ?? undefined, currentFolder: folder ?? undefined, - currentFilter: params.get('q') ?? undefined, + currentFilter: searchValue.value ?? undefined, }, onError(error) { console.error('Error in useChat', error); @@ -106,6 +123,37 @@ export function AISidebar({ children, className }: AISidebarProps & { children: throw new Error('Failed to send message'); } }, + onFinish: () => {}, + async onToolCall({ toolCall }) { + console.warn('toolCall', toolCall); + switch (toolCall.toolName) { + case Tools.CreateLabel: + case Tools.DeleteLabel: + await refetchLabels(); + break; + case Tools.SendEmail: + await queryClient.invalidateQueries({ + queryKey: trpc.mail.listThreads.queryKey({ folder: 'sent' }), + }); + break; + case Tools.MarkThreadsRead: + case Tools.MarkThreadsUnread: + case Tools.ModifyLabels: + case Tools.BulkDelete: + console.log('modifyLabels', toolCall.args); + await refetchLabels(); + await Promise.all( + (toolCall.args as { threadIds: string[] }).threadIds.map((id) => + queryClient.invalidateQueries({ + queryKey: trpc.mail.get.queryKey({ id }), + }), + ), + ); + break; + } + await track({ featureId: 'chat-messages', value: 1 }); + await refetchBilling(); + }, }); const isPro = useMemo(() => { @@ -151,27 +199,29 @@ export function AISidebar({ children, className }: AISidebarProps & { children: const searchParams = useSearchParams(); const previousPathRef = useRef(pathname); const previousSearchParamsRef = useRef(searchParams.toString()); - + // Close the popup when URL changes in any way (path or search params) useEffect(() => { const currentPath = pathname; const currentSearchParams = searchParams.toString(); - + // Check if we're on small screens and if the URL has changed in any way - if (open && - (previousPathRef.current !== currentPath || - previousSearchParamsRef.current !== currentSearchParams)) { + if ( + open && + (previousPathRef.current !== currentPath || + previousSearchParamsRef.current !== currentSearchParams) + ) { // Only close if we're on small screens (sm or smaller) if (typeof window !== 'undefined' && window.innerWidth < 640) { setOpen(false); } } - + // Update refs with current values previousPathRef.current = currentPath; previousSearchParamsRef.current = currentSearchParams; }, [pathname, searchParams, open, setOpen]); - + if (!isMailPage) { return <>{children}; } @@ -191,7 +241,7 @@ export function AISidebar({ children, className }: AISidebarProps & { children: defaultSize={20} minSize={20} maxSize={35} - className="bg-panelLight dark:bg-panelDark mr-1.5 mt-1 h-[calc(98vh+12px)] border-[#E7E7E7] shadow-sm md:rounded-2xl md:border md:shadow-sm dark:border-[#252525] hidden xl:block" + className="bg-panelLight dark:bg-panelDark mr-1.5 mt-1 hidden h-[calc(98vh+12px)] border-[#E7E7E7] shadow-sm md:rounded-2xl md:border md:shadow-sm xl:block dark:border-[#252525]" >
@@ -313,9 +363,9 @@ export function AISidebar({ children, className }: AISidebarProps & { children:
-
@@ -323,16 +373,10 @@ export function AISidebar({ children, className }: AISidebarProps & { children: {/* Mobile popup - only visible on smaller screens */} -
+
{/* Chat popup container */} -
-
+
+
@@ -451,9 +495,9 @@ export function AISidebar({ children, className }: AISidebarProps & { children:
-