add: Archive Others/All and Delete Others/All for session management

This commit is contained in:
2026-03-15 03:36:51 +08:00
parent c313646d60
commit 3c41503827

View File

@@ -14,6 +14,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip
import { GridLoader } from '@/components/ui/grid-loader';
import {
RiAddLine,
RiArchiveLine,
RiArrowDownSLine,
RiArrowRightSLine,
RiChat4Line,
@@ -34,6 +35,8 @@ import {
RiUnpinLine,
} from '@remixicon/react';
import { cn } from '@/lib/utils';
import { useSessionStore } from '@/stores/useSessionStore';
import { toast } from '@/components/ui';
import { DraggableSessionRow } from './sessionFolderDnd';
import type { SessionNode, SessionSummaryMeta } from './types';
import { formatSessionDateLabel, normalizePath, renderHighlightedText, resolveSessionDiffStats } from './utils';
@@ -159,6 +162,99 @@ export function SessionNodeItem(props: Props): React.ReactNode {
const sessionSummary = session.summary as SessionSummaryMeta | undefined;
const sessionDiffStats = resolveSessionDiffStats(sessionSummary);
const archiveSessions = useSessionStore((state) => state.archiveSessions);
const sessionsByDirectory = useSessionStore((state) => state.sessionsByDirectory);
const deleteSessions = useSessionStore((state) => state.deleteSessions);
const archivedSessions = useSessionStore((state) => state.archivedSessions);
const handleArchiveOthers = React.useCallback(async () => {
if (!sessionDirectory) return;
const sessionsInDir = sessionsByDirectory.get(sessionDirectory) ?? [];
const otherSessions = sessionsInDir.filter(s => s.id !== session.id && !s.time?.archived);
const ids = otherSessions.map(s => s.id);
if (ids.length === 0) {
toast.message('No other sessions to archive in this directory');
return;
}
const result = await archiveSessions(ids, { silent: true });
if (result.archivedIds.length > 0) {
toast.success(`Archived ${result.archivedIds.length} session${result.archivedIds.length === 1 ? '' : 's'}`);
}
if (result.failedIds.length > 0) {
toast.error(`Failed to archive ${result.failedIds.length} session${result.failedIds.length === 1 ? '' : 's'}`);
}
}, [sessionDirectory, session.id, sessionsByDirectory, archiveSessions]);
const handleArchiveAll = React.useCallback(async () => {
if (!sessionDirectory) return;
const sessionsInDir = sessionsByDirectory.get(sessionDirectory) ?? [];
const allSessions = sessionsInDir.filter(s => !s.time?.archived);
const ids = allSessions.map(s => s.id);
if (ids.length === 0) {
toast.message('No sessions to archive in this directory');
return;
}
const result = await archiveSessions(ids, { silent: true });
if (result.archivedIds.length > 0) {
toast.success(`Archived ${result.archivedIds.length} session${result.archivedIds.length === 1 ? '' : 's'}`);
}
if (result.failedIds.length > 0) {
toast.error(`Failed to archive ${result.failedIds.length} session${result.failedIds.length === 1 ? '' : 's'}`);
}
}, [sessionDirectory, sessionsByDirectory, archiveSessions]);
const handleDeleteOthers = React.useCallback(async () => {
if (!sessionDirectory) return;
const otherSessions = archivedSessions.filter(s => {
const sessionDir = normalizePath((s as Session & { directory?: string | null }).directory ?? null);
return sessionDir === sessionDirectory && s.id !== session.id;
});
const ids = otherSessions.map(s => s.id);
if (ids.length === 0) {
toast.message('No other sessions to delete in this directory');
return;
}
const result = await deleteSessions(ids, { silent: true });
if (result.deletedIds.length > 0) {
toast.success(`Deleted ${result.deletedIds.length} session${result.deletedIds.length === 1 ? '' : 's'}`);
}
if (result.failedIds.length > 0) {
toast.error(`Failed to delete ${result.failedIds.length} session${result.failedIds.length === 1 ? '' : 's'}`);
}
}, [sessionDirectory, session.id, archivedSessions, deleteSessions]);
const handleDeleteAll = React.useCallback(async () => {
if (!sessionDirectory) return;
const allSessions = archivedSessions.filter(s => {
const sessionDir = normalizePath((s as Session & { directory?: string | null }).directory ?? null);
return sessionDir === sessionDirectory;
});
const ids = allSessions.map(s => s.id);
if (ids.length === 0) {
toast.message('No sessions to delete in this directory');
return;
}
const result = await deleteSessions(ids, { silent: true });
if (result.deletedIds.length > 0) {
toast.success(`Deleted ${result.deletedIds.length} session${result.deletedIds.length === 1 ? '' : 's'}`);
}
if (result.failedIds.length > 0) {
toast.error(`Failed to delete ${result.failedIds.length} session${result.failedIds.length === 1 ? '' : 's'}`);
}
}, [sessionDirectory, archivedSessions, deleteSessions]);
if (editingId === session.id) {
return (
<div
@@ -481,6 +577,32 @@ export function SessionNodeItem(props: Props): React.ReactNode {
</DropdownMenuItem>
<DropdownMenuSeparator />
{!archivedBucket && (
<>
<DropdownMenuItem onClick={handleArchiveOthers}>
<RiArchiveLine className="mr-1 h-4 w-4" />
Archive Others
</DropdownMenuItem>
<DropdownMenuItem onClick={handleArchiveAll}>
<RiArchiveLine className="mr-1 h-4 w-4" />
Archive All
</DropdownMenuItem>
<DropdownMenuSeparator />
</>
)}
{archivedBucket && (
<>
<DropdownMenuItem onClick={handleDeleteOthers}>
<RiDeleteBinLine className="mr-1 h-4 w-4" />
Delete Others
</DropdownMenuItem>
<DropdownMenuItem onClick={handleDeleteAll}>
<RiDeleteBinLine className="mr-1 h-4 w-4" />
Delete All
</DropdownMenuItem>
<DropdownMenuSeparator />
</>
)}
<DropdownMenuItem className="text-destructive focus:text-destructive [&>svg]:mr-1" onClick={() => handleDeleteSession(session, { archivedBucket })}>
<RiDeleteBinLine className="mr-1 h-4 w-4" />
{archivedBucket ? 'Delete' : 'Archive'}