From 886fb2c7117d7f6b397524f0f0be9a2569290b15 Mon Sep 17 00:00:00 2001 From: Sandro Date: Mon, 2 Nov 2026 16:52:30 +0100 Subject: [PATCH] feat(tasks): remember list or Kanban per task list Each task list (and "Alle Aufgaben") keeps its own view on this device; a single remembered Kanban view from before carries over to its list. Co-Authored-By: Claude Opus 5.5 --- frontend/src/features/triage/TasksView.tsx | 46 ++++++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/frontend/src/features/triage/TasksView.tsx b/frontend/src/features/triage/TasksView.tsx index a908465..950e950 100644 --- a/frontend/src/features/triage/TasksView.tsx +++ b/frontend/src/features/triage/TasksView.tsx @@ -92,7 +92,15 @@ const kanbanBoardStyle = (wide: boolean, columns: number): React.CSSProperties = }; type GroupBy = 'none' | 'list' | 'tag' | 'status' | 'priority' | 'due'; -type TasksSession = { listId: string; viewMode: 'list' | 'kanban'; groupAll: GroupBy; groupList: GroupBy }; +type ViewMode = 'list' | 'kanban'; +// viewModes: list or Kanban per task list ("all" for Alle Aufgaben); viewMode is the old single value +type TasksSession = { + listId: string; + viewModes: Record; + viewMode?: ViewMode; + groupAll: GroupBy; + groupList: GroupBy; +}; // "Alle Aufgaben" is grouped by task list unless the user picks something else; a single list has no // grouping by default (grouping it by list would give one group) @@ -123,11 +131,20 @@ export const TasksView: React.FC = ({ onInitialTaskShown, openTasksCount, }) => { - // Open list, view and grouping are remembered on this device (new tab, restart of the installed app) + // Open list, view per list and grouping are remembered on this device (new tab, restart of the + // installed app) const remembered = useMemo(() => readViewMemory('tasks'), []); - const [viewMode, setViewMode] = useState<'list' | 'kanban'>(() => - remembered.viewMode === 'kanban' ? 'kanban' : 'list' - ); + const [viewModes, setViewModes] = useState>(() => { + const out: Record = {}; + if (remembered.viewModes && typeof remembered.viewModes === 'object') { + for (const [id, mode] of Object.entries(remembered.viewModes)) { + if (mode === 'kanban') out[id] = 'kanban'; + } + } else if (remembered.viewMode === 'kanban' && remembered.listId) { + out[remembered.listId] = 'kanban'; // stored before views were kept per list + } + return out; + }); const [groupAll, setGroupAll] = useState(() => remembered.groupAll && remembered.groupAll in GROUP_LABELS ? remembered.groupAll : DEFAULT_GROUP_ALL ); @@ -153,13 +170,25 @@ export const TasksView: React.FC = ({ // only a convenience } }; - const isKanbanWide = viewMode === 'kanban' && kanbanWide; const [filter, setFilter] = useState<'open' | 'done' | 'discarded'>('open'); const [tasks, setTasks] = useState([]); const [taskLists, setTaskLists] = useState([]); const [selectedListId, setSelectedListId] = useState( () => remembered.listId || 'all' ); + const viewMode: ViewMode = viewModes[selectedListId] || 'list'; + // Only Kanban is stored; the list view is the default + const setViewMode = useCallback( + (mode: ViewMode) => + setViewModes((prev) => { + const next = { ...prev }; + if (mode === 'kanban') next[selectedListId] = 'kanban'; + else delete next[selectedListId]; + return next; + }), + [selectedListId] + ); + const isKanbanWide = viewMode === 'kanban' && kanbanWide; const isAllTasks = selectedListId === 'all'; const groupBy: GroupBy = isAllTasks ? groupAll : groupList; const setGroupBy = (g: GroupBy) => (isAllTasks ? setGroupAll(g) : setGroupList(g)); @@ -169,8 +198,8 @@ export const TasksView: React.FC = ({ .filter((id) => isAllTasks || id !== 'list') .map((id) => ({ id, label: GROUP_LABELS[id] })); useEffect(() => { - writeViewMemory('tasks', { listId: selectedListId, viewMode, groupAll, groupList }); - }, [selectedListId, viewMode, groupAll, groupList]); + writeViewMemory('tasks', { listId: selectedListId, viewModes, groupAll, groupList }); + }, [selectedListId, viewModes, groupAll, groupList]); const [teams, setTeams] = useState([]); const [collaborators, setCollaborators] = useState([]); // Task trash of the selected list (or all): deleted tasks and cleared or cleaned-up completed ones @@ -1538,6 +1567,7 @@ export const TasksView: React.FC = ({ selectedTag, groupBy, defaultGroupBy, + setViewMode, sortBy, openTasks.length, doneTasks.length,