From 3c415038270b5774a06ef9cf7a04f49e7be30153 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sun, 15 Mar 2026 03:36:51 +0800 Subject: [PATCH] add: Archive Others/All and Delete Others/All for session management --- .../session/sidebar/SessionNodeItem.tsx | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/ui/src/components/session/sidebar/SessionNodeItem.tsx b/ui/src/components/session/sidebar/SessionNodeItem.tsx index 83f5f95..2cd9067 100644 --- a/ui/src/components/session/sidebar/SessionNodeItem.tsx +++ b/ui/src/components/session/sidebar/SessionNodeItem.tsx @@ -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 (
+ {!archivedBucket && ( + <> + + + Archive Others + + + + Archive All + + + + )} + {archivedBucket && ( + <> + + + Delete Others + + + + Delete All + + + + )} handleDeleteSession(session, { archivedBucket })}> {archivedBucket ? 'Delete' : 'Archive'}