import { useState, useEffect, useRef } from 'react' import { Search, Grid, List, SlidersHorizontal, FolderOpen, Upload, Settings, Menu, Trash2, X, ShoppingBasket, } from 'lucide-react' import clsx from 'clsx' import { usePhotoStore } from '../../store/photoStore' import { useFilterStore, hasActiveFilters } from '../../store/filterStore' import { photos } from '../../services/api' import { toast } from '../ToastContainer' import { useMutation, useQueryClient } from '@tanstack/react-query' import { useHeapsQuery } from '../../hooks/useHeapsQuery' import muliLogo from '../../assets/muli-logo.png' const SEARCH_DEBOUNCE_MS = 300 export function TopBar() { // Filter store is the source of truth for search; the input has a local // mirror so typing stays responsive while we debounce store updates. const storeQ = useFilterStore((s) => s.q) const setStoreQ = useFilterStore((s) => s.setQ) const filterBarOpen = useFilterStore((s) => s.filterBarOpen) const toggleFilterBar = useFilterStore((s) => s.toggleFilterBar) const filterState = useFilterStore() const filtersActive = hasActiveFilters(filterState) || filterBarOpen const [searchQuery, setSearchQuery] = useState(storeQ) // Keep local input in sync if the store is changed externally (URL hydrate, // active-chip removal, clear-all). useEffect(() => { setSearchQuery(storeQ) }, [storeQ]) // Debounce local input -> store. const debounceRef = useRef(null) useEffect(() => { if (searchQuery === storeQ) return if (debounceRef.current) window.clearTimeout(debounceRef.current) debounceRef.current = window.setTimeout(() => { setStoreQ(searchQuery) }, SEARCH_DEBOUNCE_MS) return () => { if (debounceRef.current) window.clearTimeout(debounceRef.current) } }, [searchQuery, storeQ, setStoreQ]) const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid') const selectedPhotos = usePhotoStore((state) => state.selectedPhotos) const clearSelection = usePhotoStore((state) => state.clearSelection) const selectedCount = selectedPhotos.length const queryClient = useQueryClient() // Currently active heap (for the T shortcut). Shown as a pill so the user // always knows where their next T-press will land. const { data: heapsList = [] } = useHeapsQuery() const activeHeap = heapsList.find((h) => h.is_active) // Mutation for discarding selected photos const discardPhotosMutation = useMutation({ mutationFn: async () => { await photos.bulkUpdate(selectedPhotos, { discard: true }) }, onSuccess: () => { toast.success('Discarded', `${selectedCount} photo${selectedCount > 1 ? 's' : ''} discarded`) clearSelection() queryClient.invalidateQueries({ queryKey: ['photos'] }) }, onError: (error: any) => { toast.error('Failed to discard', error.message || 'An error occurred') }, }) return (
{/* Left Section - Menu and App Name */}
Mulita

Mulita

{activeHeap && ( {activeHeap.name} )} {selectedCount > 0 && ( <> {selectedCount} selected )}
{/* Center Section - Search */}
setSearchQuery(e.target.value)} onKeyDown={(e) => { if (e.key === 'Escape') { setSearchQuery('') setStoreQ('') e.currentTarget.blur() } }} placeholder="Search photos..." className="w-full rounded-md border border-border bg-bg py-1.5 pl-9 pr-9 text-sm text-text placeholder-text-muted focus:border-primary focus:outline-none" /> {searchQuery && ( )}
{/* Right Section - View Controls and Actions */}
{/* View Mode Toggle */}
{/* Filter Button */}
{/* Action Buttons */}
) }