feat: bulk actions for email lists

Select multiple rows (checkbox per row) in FolderView, SearchResults,
and the Senders email panel. A toolbar appears with mark read/unread,
star, archive, and trash, applied to the whole selection via
CleanupService.ExecuteAsync (already scoped to UserId).

Shared via useSelection hook and BulkToolbar component to avoid
duplicating selection state across the three list views.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
cesnimda
2026-06-30 21:53:43 +02:00
parent 8c92263cc3
commit c11d747919
7 changed files with 135 additions and 5 deletions
+18 -1
View File
@@ -2,6 +2,8 @@ import { useEffect, useState, useCallback, useRef } from 'react';
import { useParams } from 'react-router-dom';
import { SearchApi } from '../api/client.js';
import EmailRow from '../components/EmailRow.jsx';
import BulkToolbar from '../components/BulkToolbar.jsx';
import useSelection from '../hooks/useSelection.js';
const FOLDER_META = {
inbox: { icon: '📥', label: 'Inbox' },
@@ -43,6 +45,7 @@ export default function FolderView() {
const [hasMore, setHasMore] = useState(true);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const { selected, toggle, clear, removeIds, selectedIds } = useSelection();
// Reset when the folder changes
useEffect(() => {
@@ -51,7 +54,8 @@ export default function FolderView() {
setPage(1);
setHasMore(true);
setError(null);
}, [slug]);
clear();
}, [slug, clear]);
// Fetch a page and append results
const fetchPage = useCallback((p) => {
@@ -98,6 +102,17 @@ export default function FolderView() {
{error && <div className="fv-error">{error}</div>}
<BulkToolbar
selectedIds={selectedIds}
onClear={clear}
onDone={(action, ids) => {
if (action === 'trash' || action === 'archive') {
setEmails((prev) => prev.filter((x) => !ids.includes(x.id)));
removeIds(ids);
}
}}
/>
{!loading && !error && emails.length === 0 && (
<div className="fv-empty">No emails in this folder.</div>
)}
@@ -109,6 +124,8 @@ export default function FolderView() {
<EmailRow
key={e.id}
email={e}
selected={selected.has(e.id)}
onToggleSelect={toggle}
onRemove={(id) => setEmails((prev) => prev.filter((x) => x.id !== id))}
/>
))}