Merge dev into main (v0.59.0)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 30s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 30s
This commit is contained in:
commit
1ce9d67e00
5 files changed
+49
-13
No files matched your search
+7
-1
@@ -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
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[](#)
|
||||
[](#-schnellstart)
|
||||
[](#-deine-daten-gehören-dir)
|
||||
[](CHANGELOG.md)
|
||||
[](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.
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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<string, ViewMode>;
|
||||
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<TasksViewProps> = ({
|
||||
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<TasksSession>('tasks'), []);
|
||||
const [viewMode, setViewMode] = useState<'list' | 'kanban'>(() =>
|
||||
remembered.viewMode === 'kanban' ? 'kanban' : 'list'
|
||||
);
|
||||
const [viewModes, setViewModes] = useState<Record<string, ViewMode>>(() => {
|
||||
const out: Record<string, ViewMode> = {};
|
||||
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<GroupBy>(() =>
|
||||
remembered.groupAll && remembered.groupAll in GROUP_LABELS ? remembered.groupAll : DEFAULT_GROUP_ALL
|
||||
);
|
||||
@@ -153,13 +170,25 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
// only a convenience
|
||||
}
|
||||
};
|
||||
const isKanbanWide = viewMode === 'kanban' && kanbanWide;
|
||||
const [filter, setFilter] = useState<'open' | 'done' | 'discarded'>('open');
|
||||
const [tasks, setTasks] = useState<Thought[]>([]);
|
||||
const [taskLists, setTaskLists] = useState<TaskList[]>([]);
|
||||
const [selectedListId, setSelectedListId] = useState<string>(
|
||||
() => 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<TasksViewProps> = ({
|
||||
.filter((id) => isAllTasks || id !== 'list')
|
||||
.map((id) => ({ id, label: GROUP_LABELS[id] }));
|
||||
useEffect(() => {
|
||||
writeViewMemory<TasksSession>('tasks', { listId: selectedListId, viewMode, groupAll, groupList });
|
||||
}, [selectedListId, viewMode, groupAll, groupList]);
|
||||
writeViewMemory<TasksSession>('tasks', { listId: selectedListId, viewModes, groupAll, groupList });
|
||||
}, [selectedListId, viewModes, groupAll, groupList]);
|
||||
const [teams, setTeams] = useState<Team[]>([]);
|
||||
const [collaborators, setCollaborators] = useState<User[]>([]);
|
||||
// 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<TasksViewProps> = ({
|
||||
selectedTag,
|
||||
groupBy,
|
||||
defaultGroupBy,
|
||||
setViewMode,
|
||||
sortBy,
|
||||
openTasks.length,
|
||||
doneTasks.length,
|
||||
|
||||
Reference in new issue
Block a user