From 7065bcb2bab4cf8f46cb6b0880069495bdbe0bf6 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Tue, 2 Jun 2026 16:20:12 +0300 Subject: [PATCH] feat: archive sessions from the mobile session row Add a two-step archive affordance to each mobile session row, mirroring the project remove confirmation: tapping the archive icon arms an inline confirm (with cancel), and confirming archives the session. Only one row can be in the confirming state at a time. Also relabel the project remove confirmation from "Delete" to "Close" across all locales, since removing a project closes it from the app rather than deleting anything. --- packages/ui/src/apps/MobileSessionsSheet.tsx | 74 +++++++++++++++++++- packages/ui/src/lib/i18n/messages/en.ts | 4 +- packages/ui/src/lib/i18n/messages/es.ts | 4 +- packages/ui/src/lib/i18n/messages/ko.ts | 4 +- packages/ui/src/lib/i18n/messages/pl.ts | 4 +- packages/ui/src/lib/i18n/messages/pt-BR.ts | 4 +- packages/ui/src/lib/i18n/messages/uk.ts | 4 +- packages/ui/src/lib/i18n/messages/zh-CN.ts | 4 +- packages/ui/src/lib/i18n/messages/zh-TW.ts | 4 +- 9 files changed, 95 insertions(+), 11 deletions(-) diff --git a/packages/ui/src/apps/MobileSessionsSheet.tsx b/packages/ui/src/apps/MobileSessionsSheet.tsx index c873fadf..9849987f 100644 --- a/packages/ui/src/apps/MobileSessionsSheet.tsx +++ b/packages/ui/src/apps/MobileSessionsSheet.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { RiAddLine, + RiArchiveLine, RiArrowDownSLine, RiArrowUpSLine, RiCheckLine, @@ -245,8 +246,22 @@ const SessionRow: React.FC<{ indent: number; /** When provided, shown as a small second-line subtitle below the title (e.g. "Project · branch"). */ contextLabel?: string; + /** When true, the row shows the two-step archive confirmation. */ + confirmingArchive?: boolean; onSelect: () => void; -}> = ({ session, active, indent, contextLabel, onSelect }) => { + /** When provided, an archive affordance is shown; first tap arms confirm, X cancels. */ + onRequestArchive?: () => void; + onConfirmArchive?: () => void; +}> = ({ + session, + active, + indent, + contextLabel, + confirmingArchive = false, + onSelect, + onRequestArchive, + onConfirmArchive, +}) => { const { t } = useI18n(); const time = formatRelativeShort(getSessionTimestamp(session)); const title = session.title?.trim() || t('mobile.sessions.untitled'); @@ -254,14 +269,19 @@ const SessionRow: React.FC<{
+ {onRequestArchive ? ( + <> + {confirmingArchive ? ( + + ) : null} + + + ) : null}
); }; @@ -429,6 +478,7 @@ export const MobileSessionsSheet: React.FC = ({ open, const currentDirectory = useDirectoryStore((state) => state.currentDirectory); const currentSessionId = useSessionUIStore((state) => state.currentSessionId); const setCurrentSession = useSessionUIStore((state) => state.setCurrentSession); + const archiveSession = useSessionUIStore((state) => state.archiveSession); const openNewSessionDraft = useSessionUIStore((state) => state.openNewSessionDraft); const setActiveProject = useProjectsStore((state) => state.setActiveProject); const setActiveProjectIdOnly = useProjectsStore((state) => state.setActiveProjectIdOnly); @@ -441,6 +491,7 @@ export const MobileSessionsSheet: React.FC = ({ open, const worktreeOrderByProject = useWorktreeOrderStore((state) => state.orderByProject); const [query, setQuery] = React.useState(''); const [editingProjectId, setEditingProjectId] = React.useState(null); + const [confirmingArchiveSessionId, setConfirmingArchiveSessionId] = React.useState(null); // Bumped to force a re-list of worktrees (e.g. after one is deleted in the editor). const [worktreeRefreshKey, setWorktreeRefreshKey] = React.useState(0); const [directoryDialogOpen, setDirectoryDialogOpen] = React.useState(false); @@ -463,6 +514,7 @@ export const MobileSessionsSheet: React.FC = ({ open, setConfirmingDeleteId(null); setVisibleCountByBucket(new Map()); setEditingProjectId(null); + setConfirmingArchiveSessionId(null); return; } void refreshGlobalSessions(liveSessions); @@ -663,7 +715,10 @@ export const MobileSessionsSheet: React.FC = ({ open, session={session} active={currentSessionId === session.id} indent={indent} + confirmingArchive={confirmingArchiveSessionId === session.id} onSelect={() => handleSelectSession(session)} + onRequestArchive={() => handleRequestArchive(session.id)} + onConfirmArchive={() => void handleConfirmArchive(session)} /> ))} {remaining > 0 ? ( @@ -702,6 +757,19 @@ export const MobileSessionsSheet: React.FC = ({ open, onOpenChange(false); }; + // Two-step archive: first tap arms the confirm on that row, second confirms. + // Only one row can be in the confirming state at a time. + const handleRequestArchive = (sessionId: string) => { + setConfirmingArchiveSessionId((current) => (current === sessionId ? null : sessionId)); + }; + + const handleConfirmArchive = async (session: Session) => { + setConfirmingArchiveSessionId(null); + const ok = await archiveSession(session.id); + if (ok) toast.success(t('sessions.sidebar.session.archive.success')); + else toast.error(t('sessions.sidebar.session.archive.error')); + }; + const handleStartNewChat = () => { openNewSessionDraft(); onOpenChange(false); diff --git a/packages/ui/src/lib/i18n/messages/en.ts b/packages/ui/src/lib/i18n/messages/en.ts index 3abd3e17..df1fb1d7 100644 --- a/packages/ui/src/lib/i18n/messages/en.ts +++ b/packages/ui/src/lib/i18n/messages/en.ts @@ -64,9 +64,11 @@ export const dict = { 'mobile.sessions.moveDownAria': 'Move {label} down', 'mobile.sessions.removeProjectAria': 'Remove {label}', 'mobile.sessions.cancelRemoveProjectAria': 'Cancel removing {label}', - 'mobile.sessions.confirmRemoveProject': 'Delete', + 'mobile.sessions.confirmRemoveProject': 'Close', 'mobile.sessions.confirmRemoveProjectAria': 'Confirm removing {label}', 'mobile.sessions.toast.projectRemoved': 'Removed {label}', + 'mobile.sessions.archiveSessionAria': 'Archive {title}', + 'mobile.sessions.cancelArchiveAria': 'Cancel archiving {title}', 'mobile.projectEdit.worktreesTitle': 'Worktrees', 'mobile.projectEdit.worktreesEmpty': 'No worktrees in this project yet.', 'mobile.projectEdit.reorderHint': 'Drag to reorder worktrees.', diff --git a/packages/ui/src/lib/i18n/messages/es.ts b/packages/ui/src/lib/i18n/messages/es.ts index d9306b41..d17afaca 100644 --- a/packages/ui/src/lib/i18n/messages/es.ts +++ b/packages/ui/src/lib/i18n/messages/es.ts @@ -64,9 +64,11 @@ export const dict: Record = { "mobile.sessions.moveDownAria": "Mover {label} abajo", "mobile.sessions.removeProjectAria": "Eliminar {label}", "mobile.sessions.cancelRemoveProjectAria": "Cancelar eliminación de {label}", - "mobile.sessions.confirmRemoveProject": "Eliminar", + "mobile.sessions.confirmRemoveProject": "Cerrar", "mobile.sessions.confirmRemoveProjectAria": "Confirmar eliminación de {label}", "mobile.sessions.toast.projectRemoved": "Se eliminó {label}", + "mobile.sessions.archiveSessionAria": "Archivar {title}", + "mobile.sessions.cancelArchiveAria": "Cancelar archivar {title}", "mobile.sessions.editProjectAria": "Editar {label}", "mobile.projectEdit.worktreesTitle": "Worktrees", "mobile.projectEdit.worktreesEmpty": "Este proyecto aún no tiene worktrees.", diff --git a/packages/ui/src/lib/i18n/messages/ko.ts b/packages/ui/src/lib/i18n/messages/ko.ts index cc02c091..37d49a54 100644 --- a/packages/ui/src/lib/i18n/messages/ko.ts +++ b/packages/ui/src/lib/i18n/messages/ko.ts @@ -64,9 +64,11 @@ export const dict: Record = { 'mobile.sessions.moveDownAria': '{label} 아래로 이동', 'mobile.sessions.removeProjectAria': '{label} 제거', 'mobile.sessions.cancelRemoveProjectAria': '{label} 제거 취소', - 'mobile.sessions.confirmRemoveProject': '삭제', + 'mobile.sessions.confirmRemoveProject': '닫기', 'mobile.sessions.confirmRemoveProjectAria': '{label} 제거 확인', 'mobile.sessions.toast.projectRemoved': '{label} 제거됨', + 'mobile.sessions.archiveSessionAria': '{title} 보관', + 'mobile.sessions.cancelArchiveAria': '{title} 보관 취소', 'mobile.sessions.editProjectAria': '{label} 편집', 'mobile.projectEdit.worktreesTitle': '워크트리', 'mobile.projectEdit.worktreesEmpty': '이 프로젝트에는 아직 워크트리가 없습니다.', diff --git a/packages/ui/src/lib/i18n/messages/pl.ts b/packages/ui/src/lib/i18n/messages/pl.ts index 6dea2fac..827f19ff 100644 --- a/packages/ui/src/lib/i18n/messages/pl.ts +++ b/packages/ui/src/lib/i18n/messages/pl.ts @@ -65,9 +65,11 @@ export const dict: Record = { 'mobile.sessions.moveDownAria': 'Przenieś {label} w dół', 'mobile.sessions.removeProjectAria': 'Usuń {label}', 'mobile.sessions.cancelRemoveProjectAria': 'Anuluj usuwanie {label}', - 'mobile.sessions.confirmRemoveProject': 'Usuń', + 'mobile.sessions.confirmRemoveProject': 'Zamknij', 'mobile.sessions.confirmRemoveProjectAria': 'Potwierdź usunięcie {label}', 'mobile.sessions.toast.projectRemoved': 'Usunięto {label}', + 'mobile.sessions.archiveSessionAria': 'Zarchiwizuj {title}', + 'mobile.sessions.cancelArchiveAria': 'Anuluj archiwizację {title}', 'mobile.sessions.editProjectAria': 'Edytuj {label}', 'mobile.projectEdit.worktreesTitle': 'Worktree', 'mobile.projectEdit.worktreesEmpty': 'Ten projekt nie ma jeszcze worktree.', diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.ts b/packages/ui/src/lib/i18n/messages/pt-BR.ts index c945adf9..0794411c 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.ts @@ -64,9 +64,11 @@ export const dict: Record = { "mobile.sessions.moveDownAria": "Mover {label} para baixo", "mobile.sessions.removeProjectAria": "Remover {label}", "mobile.sessions.cancelRemoveProjectAria": "Cancelar remoção de {label}", - "mobile.sessions.confirmRemoveProject": "Excluir", + "mobile.sessions.confirmRemoveProject": "Fechar", "mobile.sessions.confirmRemoveProjectAria": "Confirmar remoção de {label}", "mobile.sessions.toast.projectRemoved": "Removido {label}", + "mobile.sessions.archiveSessionAria": "Arquivar {title}", + "mobile.sessions.cancelArchiveAria": "Cancelar arquivamento de {title}", "mobile.sessions.editProjectAria": "Editar {label}", "mobile.projectEdit.worktreesTitle": "Worktrees", "mobile.projectEdit.worktreesEmpty": "Este projeto ainda não tem worktrees.", diff --git a/packages/ui/src/lib/i18n/messages/uk.ts b/packages/ui/src/lib/i18n/messages/uk.ts index a821c6e4..cc316794 100644 --- a/packages/ui/src/lib/i18n/messages/uk.ts +++ b/packages/ui/src/lib/i18n/messages/uk.ts @@ -64,9 +64,11 @@ export const dict: Record = { "mobile.sessions.moveDownAria": "Перемістити {label} вниз", "mobile.sessions.removeProjectAria": "Видалити {label}", "mobile.sessions.cancelRemoveProjectAria": "Скасувати видалення {label}", - "mobile.sessions.confirmRemoveProject": "Видалити", + "mobile.sessions.confirmRemoveProject": "Закрити", "mobile.sessions.confirmRemoveProjectAria": "Підтвердити видалення {label}", "mobile.sessions.toast.projectRemoved": "Видалено {label}", + "mobile.sessions.archiveSessionAria": "Архівувати {title}", + "mobile.sessions.cancelArchiveAria": "Скасувати архівування {title}", "mobile.sessions.editProjectAria": "Редагувати {label}", "mobile.projectEdit.worktreesTitle": "Ворктрі", "mobile.projectEdit.worktreesEmpty": "У цьому проєкті ще немає ворктрі.", diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.ts b/packages/ui/src/lib/i18n/messages/zh-CN.ts index 37ff2824..e5faedd5 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.ts @@ -64,9 +64,11 @@ export const dict: Record = { 'mobile.sessions.moveDownAria': '将 {label} 下移', 'mobile.sessions.removeProjectAria': '移除 {label}', 'mobile.sessions.cancelRemoveProjectAria': '取消移除 {label}', - 'mobile.sessions.confirmRemoveProject': '删除', + 'mobile.sessions.confirmRemoveProject': '关闭', 'mobile.sessions.confirmRemoveProjectAria': '确认移除 {label}', 'mobile.sessions.toast.projectRemoved': '已移除 {label}', + 'mobile.sessions.archiveSessionAria': '归档 {title}', + 'mobile.sessions.cancelArchiveAria': '取消归档 {title}', 'mobile.sessions.editProjectAria': '编辑 {label}', 'mobile.projectEdit.worktreesTitle': '工作树', 'mobile.projectEdit.worktreesEmpty': '此项目还没有工作树。', diff --git a/packages/ui/src/lib/i18n/messages/zh-TW.ts b/packages/ui/src/lib/i18n/messages/zh-TW.ts index 249ca51d..47de0e1c 100644 --- a/packages/ui/src/lib/i18n/messages/zh-TW.ts +++ b/packages/ui/src/lib/i18n/messages/zh-TW.ts @@ -64,9 +64,11 @@ export const dict: Record = { 'mobile.sessions.moveDownAria': '將 {label} 下移', 'mobile.sessions.removeProjectAria': '移除 {label}', 'mobile.sessions.cancelRemoveProjectAria': '取消移除 {label}', - 'mobile.sessions.confirmRemoveProject': '刪除', + 'mobile.sessions.confirmRemoveProject': '關閉', 'mobile.sessions.confirmRemoveProjectAria': '確認移除 {label}', 'mobile.sessions.toast.projectRemoved': '已移除 {label}', + 'mobile.sessions.archiveSessionAria': '封存 {title}', + 'mobile.sessions.cancelArchiveAria': '取消封存 {title}', 'mobile.sessions.editProjectAria': '編輯 {label}', 'mobile.projectEdit.worktreesTitle': '工作樹', 'mobile.projectEdit.worktreesEmpty': '此專案還沒有工作樹。',