feat: Search pagination and UX improvements

- Add pagination support to search (previously limited to 50 results)
- Search now scoped to current mailbox/folder
- Support shared mailbox folders in search
- Display total results count (e.g., "50 of 1400 conversations")
- Add clear (X) button to search input
- Re-run search when changing folders during active search
- Remove unused sidebar screenshot
This commit is contained in:
Matthieu MALVACHE
2025-12-17 18:39:49 +01:00
committed by Matthieu MALVACHE
parent ec9b322726
commit 93834511c0
7 changed files with 117 additions and 34 deletions
+8 -1
View File
@@ -60,6 +60,7 @@ export function EmailList({
loadMoreEmails,
hasMoreEmails,
isLoadingMore,
totalEmails,
mailboxes,
selectedMailbox,
expandedThreadIds,
@@ -260,7 +261,13 @@ export function EmailList({
)}
</button>
<h2 className="text-sm font-medium text-foreground">
{isLoading ? 'Loading...' : threadGroups.length > 0 ? `${threadGroups.length} conversations` : 'No conversations'}
{isLoading ? 'Loading...' : threadGroups.length > 0
? (totalEmails > threadGroups.length
? `${threadGroups.length} of ${totalEmails} conversations`
: hasMoreEmails
? `${threadGroups.length}+ conversations`
: `${threadGroups.length} conversations`)
: 'No conversations'}
</h2>
</div>
</div>
+24 -1
View File
@@ -24,6 +24,7 @@ import {
ChevronUp,
Users,
User,
X,
} from "lucide-react";
import { cn, buildMailboxTree, MailboxNode, formatFileSize } from "@/lib/utils";
import { Mailbox } from "@/lib/jmap/types";
@@ -37,6 +38,8 @@ interface SidebarProps {
onCompose?: () => void;
onLogout?: () => void;
onSearch?: (query: string) => void;
onClearSearch?: () => void;
activeSearchQuery?: string;
quota?: { used: number; total: number } | null;
isPushConnected?: boolean;
className?: string;
@@ -206,6 +209,8 @@ export function Sidebar({
onCompose,
onLogout,
onSearch,
onClearSearch,
activeSearchQuery = "",
quota,
isPushConnected = false,
className,
@@ -215,6 +220,11 @@ export function Sidebar({
const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set());
const [showMenu, setShowMenu] = useState(false);
const t = useTranslations('sidebar');
// Sync local search query with store's active search query
useEffect(() => {
setSearchQuery(activeSearchQuery);
}, [activeSearchQuery]);
const params = useParams();
const router = useRouter();
@@ -334,9 +344,22 @@ export function Sidebar({
placeholder={t("search_placeholder")}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-9"
className={cn("pl-9", searchQuery && "pr-8")}
data-search-input
/>
{searchQuery && (
<button
type="button"
onClick={() => {
setSearchQuery("");
onClearSearch?.();
}}
className="absolute right-2 top-1/2 transform -translate-y-1/2 p-1 rounded-full hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
aria-label="Clear search"
>
<X className="w-4 h-4" />
</button>
)}
</form>
</div>
)}