import { DndContext, closestCenter, useSensor, useSensors, PointerSensor, KeyboardSensor, } from '@dnd-kit/core' import { SortableContext, verticalListSortingStrategy, arrayMove, useSortable, } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { IconDots, IconStarFilled, IconTrash, IconEdit, IconStar, } from '@tabler/icons-react' import { useThreads } from '@/hooks/useThreads' import { cn } from '@/lib/utils' import { route } from '@/constants/routes' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { useTranslation } from 'react-i18next' import { DialogClose, DialogFooter, DialogHeader } from '@/components/ui/dialog' import { Dialog, DialogTrigger, DialogContent, DialogTitle, DialogDescription, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { memo, useMemo, useState } from 'react' import { useNavigate, useMatches } from '@tanstack/react-router' import { toast } from 'sonner' import { Input } from '@/components/ui/input' const SortableItem = memo(({ thread }: { thread: Thread }) => { const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id: thread.id }) const style = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.5 : 1, } const { toggleFavorite, deleteThread, renameThread } = useThreads() const { t } = useTranslation() const [openDropdown, setOpenDropdown] = useState(false) const navigate = useNavigate() // Check if current route matches this thread's detail page const matches = useMatches() const isActive = matches.some( (match) => match.routeId === '/threads/$threadId' && 'threadId' in match.params && match.params.threadId === thread.id ) const handleClick = () => { if (!isDragging) { navigate({ to: route.threadsDetail, params: { threadId: thread.id } }) } } const [title, setTitle] = useState(thread.title || 'New Thread') return (
{thread.title || 'New Thread'}
setOpenDropdown(open)} > { e.preventDefault() e.stopPropagation() }} /> {thread.isFavorite ? ( { e.stopPropagation() toggleFavorite(thread.id) }} > {t('common.unstar')} ) : ( { e.stopPropagation() toggleFavorite(thread.id) }} > {t('common.star')} )} { if (!open) { setOpenDropdown(false) setTitle(thread.title) } }} > e.preventDefault()}> {t('common.rename')} Rename Title { setTitle(e.target.value) }} className="mt-2" onKeyDown={(e) => { // Prevent key from being captured by parent components e.stopPropagation() }} /> { if (!open) setOpenDropdown(false) }} > e.preventDefault()}> {t('common.delete')} Delete Thread Are you sure you want to delete this thread? This action cannot be undone.
) }) type ThreadListProps = { threads: Thread[] isFavoriteSection?: boolean } function ThreadList({ threads, isFavoriteSection = false }: ThreadListProps) { const { setThreads } = useThreads() const sortedThreads = useMemo(() => { return threads.sort((a, b) => { if (a.order && b.order) return a.order - b.order // Later on top return (b.updated || 0) - (a.updated || 0) }) }, [threads]) const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { delay: 200, tolerance: 5, }, }), useSensor(KeyboardSensor) ) return ( { const { active, over } = event if (active.id !== over?.id) { const oldIndex = threads.findIndex((t) => t.id === active.id) const newIndex = threads.findIndex((t) => t.id === over?.id) // Create a new array with the reordered threads from this section only const reorderedSectionThreads = arrayMove(threads, oldIndex, newIndex) // Split all threads into favorites and non-favorites const favThreads = sortedThreads.filter((t) => t.isFavorite) const nonFavThreads = sortedThreads.filter((t) => !t.isFavorite) // Replace the appropriate section with the reordered threads let updatedThreads if (isFavoriteSection) { // If we're in the favorites section, update favorites and keep non-favorites as is updatedThreads = [...reorderedSectionThreads, ...nonFavThreads] } else { // If we're in the non-favorites section, update non-favorites and keep favorites as is updatedThreads = [...favThreads, ...reorderedSectionThreads] } setThreads(updatedThreads) } }} > t.id)} strategy={verticalListSortingStrategy} > {sortedThreads.map((thread, index) => ( ))} ) } export default memo(ThreadList)