Displays conversation history and allows management (new, select, rename, delete).

Overview

The ChatSidebar component provides:

  • Conversation list with real-time updates
  • New conversation button
  • Conversation selection
  • Rename conversation functionality
  • Delete conversation functionality
  • Subscription status display (integrated)

Basic Usage

import ChatSidebar from "@/components/ai/ChatSidebar";

export default function ChatPage() {
  const [currentConversationId, setCurrentConversationId] = useState<string | null>(null);

  return (
    <div className="flex h-screen">
      <ChatSidebar
        currentConversationId={currentConversationId}
        onSelectConversation={(id) => setCurrentConversationId(id)}
        onNewConversation={() => {
          // Create new conversation logic
          setCurrentConversationId(null);
        }}
      />
      {/* Main chat interface */}
    </div>
  );
}

Props

interface ChatSidebarProps {
  currentConversationId: string | null;
  onSelectConversation: (conversationId: string) => void;
  onNewConversation: () => void;
}

Features

Real-time Updates

The component automatically syncs with Firestore:

// Uses Firestore onSnapshot for real-time updates
const q = query(
  collection(db, "users", userId, "conversations"),
  orderBy("updatedAt", "desc")
);

onSnapshot(q, (snapshot) => {
  // Updates conversation list automatically
});

Conversation Management

  • New Conversation - Creates a new conversation document
  • Select Conversation - Loads conversation messages
  • Rename Conversation - Updates conversation title
  • Delete Conversation - Removes conversation and messages

Path-Based Security

Conversations are stored using path-based security:

users/{userId}/conversations/{conversationId}

This ensures users can only access their own conversations.

Integration Example

Full Chat Page Integration

"use client";

import { useState, useEffect } from "react";
import ChatSidebar from "@/components/ai/ChatSidebar";
import ChatInterface from "@/components/ai/ChatInterface";
import { getAuthInstance, getFirestoreInstance } from "@core/src/lib/firebase/client";
import { onAuthStateChanged } from "firebase/auth";

export default function ChatPage() {
  const [user, setUser] = useState<any>(null);
  const [currentConversationId, setCurrentConversationId] = useState<string | null>(null);
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const auth = getAuthInstance();
    const unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
      setUser(firebaseUser);
    });
    return () => unsubscribe();
  }, []);

  const handleSelectConversation = (conversationId: string) => {
    setCurrentConversationId(conversationId);
    // Load messages for this conversation
  };

  const handleNewConversation = async () => {
    // Create new conversation logic
    const newId = await createNewConversation();
    setCurrentConversationId(newId);
  };

  return (
    <div className="flex h-screen">
      <ChatSidebar
        currentConversationId={currentConversationId}
        onSelectConversation={handleSelectConversation}
        onNewConversation={handleNewConversation}
      />
      <div className="flex-1">
        <ChatInterface messages={messages} />
      </div>
    </div>
  );
}

Conversation Data Structure

Conversations are stored in Firestore with this structure:

interface Conversation {
  id: string;
  title: string;
  createdAt: Timestamp;
  updatedAt: Timestamp;
  lastMessage?: string;
  messageCount?: number;
}

Styling

The component uses DaisyUI classes:

  • bg-base-200 - Sidebar background
  • border-r border-base-content/10 - Right border
  • btn btn-primary - New chat button
  • badge - Active conversation indicator

Customization

Custom Styling

<ChatSidebar
  className="w-80" // Custom width
  currentConversationId={currentConversationId}
  onSelectConversation={handleSelect}
  onNewConversation={handleNew}
/>

Custom New Conversation Button

The component includes a "New Chat" button, but you can customize the behavior:

const handleNewConversation = async () => {
  // Your custom logic
  const newConversation = await createConversation({
    title: "New Conversation",
    userId: user.uid,
  });
  setCurrentConversationId(newConversation.id);
};

Error Handling

The component handles:

  • Loading states (shows spinner while loading)
  • Empty states (shows message when no conversations)
  • Firestore errors (displays error message)
  • Authentication errors (handles unauthenticated state)

Best Practices

  1. Real-time Updates - Component automatically syncs, no manual refresh needed
  2. Path-Based Security - Conversations are scoped to user via Firestore path
  3. Optimistic Updates - Update UI immediately, sync with Firestore
  4. Error Handling - Always handle Firestore errors gracefully
  5. Performance - Use Firestore indexes for efficient queries

Firestore Security Rules

Ensure your Firestore rules allow conversation access:

match /users/{userId}/conversations/{conversationId} {
  allow read, write: if request.auth != null && request.auth.uid == userId;
}

Next Steps