From 8dff4e9bc57412e996ff7cb60aff733fec29c4f1 Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Fri, 18 Sep 2026 04:40:47 -0700 Subject: [PATCH] fix(desktop): unarchive from Settings into the session's own sidebar slice The Settings unarchive prepended the row into $sessions regardless of source, so a messaging or cron session showed under SESSIONS until the next refresh. Route it through restoreListedSession, which picks the slice from the row's source. --- .../app/settings/sessions-settings.test.tsx | 60 +++++++++++++++++++ .../src/app/settings/sessions-settings.tsx | 5 +- 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 apps/desktop/src/app/settings/sessions-settings.test.tsx diff --git a/apps/desktop/src/app/settings/sessions-settings.test.tsx b/apps/desktop/src/app/settings/sessions-settings.test.tsx new file mode 100644 index 0000000000..73f94b8ce9 --- /dev/null +++ b/apps/desktop/src/app/settings/sessions-settings.test.tsx @@ -0,0 +1,60 @@ +// @vitest-environment jsdom +import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +import { listAllProfileSessions, setSessionArchived } from '@/hermes' +import { en } from '@/i18n/en' +import { $messagingSessions, $sessions, setMessagingSessions, setSessions } from '@/store/session' +import type { SessionInfo } from '@/types/hermes' + +import { SessionsSettings } from './sessions-settings' + +vi.mock('@/i18n', () => ({ useI18n: () => ({ t: en }) })) + +vi.mock('@/hermes', async importOriginal => ({ + ...(await importOriginal>()), + getHermesConfigRecord: vi.fn().mockResolvedValue({ config: {} }), + listAllProfileSessions: vi.fn(), + setSessionArchived: vi.fn().mockResolvedValue(undefined) +})) + +const archivedMatrixSession = { + archived: true, + ended_at: null, + id: 'matrix-1', + input_tokens: 0, + is_active: false, + last_active: 1, + message_count: 2, + model: null, + output_tokens: 0, + preview: null, + source: 'matrix', + started_at: 1, + title: 'archived room', + tool_call_count: 0 +} as SessionInfo + +beforeEach(() => { + setSessions([]) + setMessagingSessions([]) + vi.mocked(listAllProfileSessions).mockResolvedValue({ sessions: [archivedMatrixSession], total: 1 } as never) +}) + +afterEach(() => { + cleanup() +}) + +describe('SessionsSettings unarchive', () => { + it('restores a messaging-source session into $messagingSessions, not $sessions', async () => { + render() + const button = await screen.findByRole('button', { name: en.settings.sessions.unarchive }) + + await act(async () => fireEvent.click(button)) + + await waitFor(() => expect(setSessionArchived).toHaveBeenCalledWith('matrix-1', false, undefined)) + expect($messagingSessions.get().map(session => session.id)).toEqual(['matrix-1']) + expect($messagingSessions.get()[0]?.archived).toBe(false) + expect($sessions.get()).toEqual([]) + }) +}) diff --git a/apps/desktop/src/app/settings/sessions-settings.tsx b/apps/desktop/src/app/settings/sessions-settings.tsx index d33e37b81f..68587193cb 100644 --- a/apps/desktop/src/app/settings/sessions-settings.tsx +++ b/apps/desktop/src/app/settings/sessions-settings.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useState } from 'react' +import { restoreListedSession } from '@/app/session/hooks/use-session-actions/utils' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Tip } from '@/components/ui/tooltip' @@ -17,7 +18,7 @@ import { triggerHaptic } from '@/lib/haptics' import { Archive, ArchiveOff, FolderOpen, Loader2, Trash2 } from '@/lib/icons' import { confirm } from '@/store/confirm' import { notify, notifyError } from '@/store/notifications' -import { applyConfiguredDefaultProjectDir, ensureDefaultWorkspaceCwd, setSessions } from '@/store/session' +import { applyConfiguredDefaultProjectDir, ensureDefaultWorkspaceCwd } from '@/store/session' import { untombstoneSessions } from '@/store/session-removal' import { forgetSessionUnread } from '@/store/session-unread' import type { HermesConfigRecord, SessionInfo } from '@/types/hermes' @@ -63,7 +64,7 @@ export function SessionsSettings() { // Surface it again in the sidebar without waiting for a full refresh, and // lift any optimistic eviction so the grouped tree shows it again too. untombstoneSessions([session.id, session._lineage_root_id]) - setSessions(prev => [{ ...session, archived: false }, ...prev.filter(s => s.id !== session.id)]) + restoreListedSession({ ...session, archived: false }) triggerHaptic('selection') notify({ durationMs: 2_000, kind: 'success', message: s.restored }) } catch (err) {