diff --git a/CHANGELOG.md b/CHANGELOG.md index aba0e39..9f2d777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V ## [Unreleased] +## [0.59.0] - 2026-11-02 + +### Added +- Aufgaben: Jede Aufgabenliste merkt sich, ob sie als Liste oder als Kanban-Board angezeigt wird, „Alle Aufgaben“ ebenso (pro Gerät). + ## [0.58.0] - 2026-11-02 ### Added @@ -848,7 +853,8 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V - Geteilte Listen (Einkaufs-, Pack- und Checklisten) mit `#Kategorie`-Gruppierung, Mengenangaben und Echtzeit-Abhaken. - Benutzer-, Gruppen- und Admin-Verwaltung (Ersteinrichtungs-Assistent, Einladungscodes, Profilbilder, persönliche API-Keys & Webhook-Ingest `/api/v1/webhooks/ingest`, JSON-Datenexport) sowie CSS-Theme-Engine mit 4 Themes (`Dark`, `Light`, `High Contrast`, `Soft`) und PWA-Unterstützung. -[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.58.0...dev +[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.59.0...dev +[0.59.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.58.0...v0.59.0 [0.58.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.57.0...v0.58.0 [0.57.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.56.0...v0.57.0 [0.56.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.55.0...v0.56.0 diff --git a/README.md b/README.md index a367a2b..f8c9bba 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Self-Hosted](https://img.shields.io/badge/100%25-Self--Hosted-2563eb?style=flat-square)](#) [![Docker](https://img.shields.io/badge/Deployment-Single%20Container-2496ed?style=flat-square&logo=docker&logoColor=white)](#-schnellstart) [![SQLite](https://img.shields.io/badge/Daten-SQLite-003b57?style=flat-square&logo=sqlite&logoColor=white)](#-deine-daten-gehören-dir) -[![Version](https://img.shields.io/badge/Version-v0.58.0-16a34a?style=flat-square)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/Version-v0.59.0-16a34a?style=flat-square)](CHANGELOG.md) **MindSift** ist eine selbst gehostete Schaltstelle für spontane Gedanken, Aufgaben mit Kanban-Board, hierarchische Outliner-Notizen, die mehrere Personen gleichzeitig bearbeiten können, und gemeinsame Listen. Alles läuft als ein einzelner Container mit eingebetteter SQLite-Datenbank, auf deinem eigenen Server. diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3b2978f..5448e8d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "mindsift-frontend", - "version": "0.58.0", + "version": "0.59.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mindsift-frontend", - "version": "0.58.0", + "version": "0.59.0", "license": "AGPL-3.0-only", "dependencies": { "@fontsource-variable/inter": "5.3.0", diff --git a/frontend/package.json b/frontend/package.json index 7588054..9936cd2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "mindsift-frontend", "private": true, - "version": "0.58.0", + "version": "0.59.0", "license": "AGPL-3.0-only", "type": "module", "scripts": { 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,