"use client"; import { useQueryClient } from "@tanstack/react-query"; import { useAtomValue, useSetAtom } from "jotai"; import { Globe, User, Users } from "lucide-react"; import { useCallback, useState } from "react"; import { toast } from "sonner"; import { createSnapshotMutationAtom } from "@/atoms/chat/chat-thread-mutation.atoms"; import { currentThreadAtom, setThreadVisibilityAtom } from "@/atoms/chat/current-thread.atom"; import { Button } from "@/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { type ChatVisibility, type ThreadRecord, updateThreadVisibility, } from "@/lib/chat/thread-persistence"; import { cn } from "@/lib/utils"; interface ChatShareButtonProps { thread: ThreadRecord | null; onVisibilityChange?: (visibility: ChatVisibility) => void; className?: string; } const visibilityOptions: { value: ChatVisibility; label: string; description: string; icon: typeof User; }[] = [ { value: "PRIVATE", label: "Private", description: "Only you can access this chat", icon: User, }, { value: "SEARCH_SPACE", label: "Search Space", description: "All members of this search space can access", icon: Users, }, ]; export function ChatShareButton({ thread, onVisibilityChange, className }: ChatShareButtonProps) { const queryClient = useQueryClient(); const [open, setOpen] = useState(false); // Use Jotai atom for visibility (single source of truth) const currentThreadState = useAtomValue(currentThreadAtom); const setThreadVisibility = useSetAtom(setThreadVisibilityAtom); // Snapshot creation mutation const { mutateAsync: createSnapshot, isPending: isCreatingSnapshot } = useAtomValue( createSnapshotMutationAtom ); // Use Jotai visibility if available (synced from chat page), otherwise fall back to thread prop const currentVisibility = currentThreadState.visibility ?? thread?.visibility ?? "PRIVATE"; const handleVisibilityChange = useCallback( async (newVisibility: ChatVisibility) => { if (!thread || newVisibility === currentVisibility) { setOpen(false); return; } // Update Jotai atom immediately for instant UI feedback setThreadVisibility(newVisibility); try { await updateThreadVisibility(thread.id, newVisibility); // Refetch threads list to update sidebar await queryClient.refetchQueries({ predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "threads", }); onVisibilityChange?.(newVisibility); toast.success( newVisibility === "SEARCH_SPACE" ? "Chat shared with search space" : "Chat is now private" ); setOpen(false); } catch (error) { console.error("Failed to update visibility:", error); // Revert Jotai state on error setThreadVisibility(thread.visibility ?? "PRIVATE"); toast.error("Failed to update sharing settings"); } }, [thread, currentVisibility, onVisibilityChange, queryClient, setThreadVisibility] ); const handleCreatePublicLink = useCallback(async () => { if (!thread) return; try { await createSnapshot({ thread_id: thread.id }); setOpen(false); } catch (error) { console.error("Failed to create public link:", error); } }, [thread, createSnapshot]); // Don't show if no thread (new chat that hasn't been created yet) if (!thread) { return null; } const CurrentIcon = currentVisibility === "PRIVATE" ? User : Users; const buttonLabel = currentVisibility === "PRIVATE" ? "Private" : "Shared"; return ( Share settings e.preventDefault()} >
{/* Visibility Options */} {visibilityOptions.map((option) => { const isSelected = currentVisibility === option.value; const Icon = option.icon; return ( ); })} {/* Divider */}
{/* Public Link Option */}
); }