"use client"; import { useQueryClient } from "@tanstack/react-query"; import { useAtomValue, useSetAtom } from "jotai"; import { Globe, Link2, User, Users } from "lucide-react"; import { useCallback, useState } from "react"; import { toast } from "sonner"; import { togglePublicShareMutationAtom } 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 setCurrentThreadState = useSetAtom(currentThreadAtom); const setThreadVisibility = useSetAtom(setThreadVisibilityAtom); // Public share mutation const { mutateAsync: togglePublicShare, isPending: isTogglingPublic } = useAtomValue( togglePublicShareMutationAtom ); // Use Jotai visibility if available (synced from chat page), otherwise fall back to thread prop const currentVisibility = currentThreadState.visibility ?? thread?.visibility ?? "PRIVATE"; const isPublicEnabled = currentThreadState.publicShareEnabled ?? thread?.public_share_enabled ?? false; const publicShareToken = currentThreadState.publicShareToken ?? null; 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 handlePublicShareToggle = useCallback(async () => { if (!thread) return; try { const response = await togglePublicShare({ thread_id: thread.id, enabled: !isPublicEnabled, }); // Update atom state with response setCurrentThreadState((prev) => ({ ...prev, publicShareEnabled: response.enabled, publicShareToken: response.share_token, })); } catch (error) { console.error("Failed to toggle public share:", error); } }, [thread, isPublicEnabled, togglePublicShare, setCurrentThreadState]); const handleCopyPublicLink = useCallback(async () => { if (!publicShareToken) return; const publicUrl = `${window.location.origin}/public/${publicShareToken}`; await navigator.clipboard.writeText(publicUrl); toast.success("Public link copied to clipboard"); }, [publicShareToken]); // Don't show if no thread (new chat that hasn't been created yet) if (!thread) { return null; } const CurrentIcon = isPublicEnabled ? Globe : currentVisibility === "PRIVATE" ? User : Users; const buttonLabel = isPublicEnabled ? "Public" : 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 Share Option */}
); }