Add globe indicator for chats with public links

This commit is contained in:
CREDO23 2026-02-04 18:26:38 +02:00
parent f5aa520743
commit fb371d09f5

View file

@ -1,8 +1,9 @@
"use client"; "use client";
import { useQueryClient } from "@tanstack/react-query"; import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useAtomValue, useSetAtom } from "jotai"; import { useAtomValue, useSetAtom } from "jotai";
import { Globe, User, Users } from "lucide-react"; import { Globe, User, Users } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { currentThreadAtom, setThreadVisibilityAtom } from "@/atoms/chat/current-thread.atom"; import { currentThreadAtom, setThreadVisibilityAtom } from "@/atoms/chat/current-thread.atom";
@ -11,6 +12,7 @@ import { createPublicChatSnapshotMutationAtom } from "@/atoms/public-chat-snapsh
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { chatThreadsApiService } from "@/lib/apis/chat-threads-api.service";
import { import {
type ChatVisibility, type ChatVisibility,
type ThreadRecord, type ThreadRecord,
@ -46,6 +48,8 @@ const visibilityOptions: {
export function ChatShareButton({ thread, onVisibilityChange, className }: ChatShareButtonProps) { export function ChatShareButton({ thread, onVisibilityChange, className }: ChatShareButtonProps) {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const router = useRouter();
const params = useParams();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
// Use Jotai atom for visibility (single source of truth) // Use Jotai atom for visibility (single source of truth)
@ -65,6 +69,16 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
return access.permissions?.includes("public_sharing:create") ?? false; return access.permissions?.includes("public_sharing:create") ?? false;
}, [access]); }, [access]);
// Query to check if thread has public snapshots
const { data: snapshotsData } = useQuery({
queryKey: ["thread-snapshots", thread?.id],
queryFn: () => chatThreadsApiService.listPublicChatSnapshots({ thread_id: thread!.id }),
enabled: !!thread?.id,
staleTime: 30000, // Cache for 30 seconds
});
const hasPublicSnapshots = (snapshotsData?.snapshots?.length ?? 0) > 0;
const snapshotCount = snapshotsData?.snapshots?.length ?? 0;
// Use Jotai visibility if available (synced from chat page), otherwise fall back to thread prop // Use Jotai visibility if available (synced from chat page), otherwise fall back to thread prop
const currentVisibility = currentThreadState.visibility ?? thread?.visibility ?? "PRIVATE"; const currentVisibility = currentThreadState.visibility ?? thread?.visibility ?? "PRIVATE";
@ -106,11 +120,13 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
try { try {
await createSnapshot({ thread_id: thread.id }); await createSnapshot({ thread_id: thread.id });
// Refetch snapshots to show the globe indicator
await queryClient.invalidateQueries({ queryKey: ["thread-snapshots", thread.id] });
setOpen(false); setOpen(false);
} catch (error) { } catch (error) {
console.error("Failed to create public link:", error); console.error("Failed to create public link:", error);
} }
}, [thread, createSnapshot]); }, [thread, createSnapshot, queryClient]);
// Don't show if no thread (new chat that hasn't been created yet) // Don't show if no thread (new chat that hasn't been created yet)
if (!thread) { if (!thread) {
@ -121,6 +137,7 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
const buttonLabel = currentVisibility === "PRIVATE" ? "Private" : "Shared"; const buttonLabel = currentVisibility === "PRIVATE" ? "Private" : "Shared";
return ( return (
<div className={cn("flex items-center gap-1", className)}>
<Popover open={open} onOpenChange={setOpen}> <Popover open={open} onOpenChange={setOpen}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
@ -128,10 +145,7 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
<Button <Button
variant="outline" variant="outline"
size="icon" size="icon"
className={cn( className="h-8 w-8 md:w-auto md:px-3 md:gap-2 relative bg-muted hover:bg-muted/80 border-0"
"h-8 w-8 md:w-auto md:px-3 md:gap-2 relative bg-muted hover:bg-muted/80 border-0",
className
)}
> >
<CurrentIcon className="h-4 w-4" /> <CurrentIcon className="h-4 w-4" />
<span className="hidden md:inline text-sm">{buttonLabel}</span> <span className="hidden md:inline text-sm">{buttonLabel}</span>
@ -228,5 +242,26 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
</div> </div>
</PopoverContent> </PopoverContent>
</Popover> </Popover>
{/* Globe indicator when public snapshots exist - clicks to settings */}
{hasPublicSnapshots && (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={() => router.push(`/dashboard/${params.search_space_id}/settings`)}
className="flex items-center justify-center h-8 w-8 rounded-md bg-muted/50 hover:bg-muted transition-colors"
>
<Globe className="h-4 w-4 text-muted-foreground" />
</button>
</TooltipTrigger>
<TooltipContent>
{snapshotCount === 1
? "This chat has a public link - Click to manage"
: `This chat has ${snapshotCount} public links - Click to manage`}
</TooltipContent>
</Tooltip>
)}
</div>
); );
} }