Tasks: per-vault persistence for the Flagged filter, Hide done, and sort mode (issue #94)

This commit is contained in:
Yuri Karamian
2026-06-11 15:50:18 +02:00
parent ee16ef07c5
commit 784248f984
5 changed files with 39 additions and 12 deletions
+13 -1
View File
@@ -38,6 +38,9 @@
viewMode,
sortMode,
tasksLayout,
tasksHideCompleted,
tasksOnlyFlagged,
tasksSort,
activeTag,
updateAvailable as globalUpdateAvailable,
settingsTab,
@@ -202,7 +205,10 @@
last_view_mode: $viewMode,
last_notebook: $activeNotebook?.relative_path ?? null,
last_tag: $activeTag,
tasks_layout: $tasksLayout
tasks_layout: $tasksLayout,
tasks_hide_completed: $tasksHideCompleted,
tasks_only_flagged: $tasksOnlyFlagged,
tasks_sort: $tasksSort
};
try {
await saveVaultState(state);
@@ -504,6 +510,9 @@
$effect(() => {
$sortMode;
$tasksLayout;
$tasksHideCompleted;
$tasksOnlyFlagged;
$tasksSort;
persistState();
});
@@ -529,6 +538,9 @@
$notebookOrder = state.notebook_order ?? {};
if (state.sort_mode === 'created' || state.sort_mode === 'title' || state.sort_mode === 'modified') $sortMode = state.sort_mode;
if (state.tasks_layout === 'calendar' || state.tasks_layout === 'list') $tasksLayout = state.tasks_layout;
if (typeof state.tasks_hide_completed === 'boolean') $tasksHideCompleted = state.tasks_hide_completed;
if (typeof state.tasks_only_flagged === 'boolean') $tasksOnlyFlagged = state.tasks_only_flagged;
if (state.tasks_sort === 'due' || state.tasks_sort === 'priority' || state.tasks_sort === 'note') $tasksSort = state.tasks_sort;
lastNotePath = state.last_open_note ?? null;
lastViewMode = state.last_view_mode ?? '';
lastNotebook = state.last_notebook ?? null;
+11 -11
View File
@@ -3,7 +3,7 @@
import { listen } from '@tauri-apps/api/event';
import { getTasks } from '$lib/api';
import { debounce } from '$lib/utils/debounce';
import { tasksLayout, appConfig } from '$lib/stores/app';
import { tasksLayout, tasksHideCompleted, tasksOnlyFlagged, tasksSort, appConfig } from '$lib/stores/app';
import { isAndroid } from '$lib/platform';
import type { TaskItem, FileEvent } from '$lib/types';
@@ -16,8 +16,6 @@
let tasks = $state<TaskItem[]>([]);
let loading = $state(true);
let hideCompleted = $state(true);
let sortBy = $state<'due' | 'priority' | 'note'>('due');
let unlisten: (() => void) | null = null;
async function load() {
@@ -46,13 +44,14 @@
const filtered = $derived.by(() => {
let list = tasks.slice();
if (hideCompleted) list = list.filter((t) => !t.completed);
if ($tasksHideCompleted) list = list.filter((t) => !t.completed);
if ($tasksOnlyFlagged) list = list.filter((t) => !!t.due || !!t.priority);
list.sort((a, b) => {
if (a.completed !== b.completed) return a.completed ? 1 : -1; // done last
if (sortBy === 'priority') {
if ($tasksSort === 'priority') {
const d = prank(a.priority) - prank(b.priority);
if (d) return d;
} else if (sortBy === 'note') {
} else if ($tasksSort === 'note') {
const d = a.note_title.localeCompare(b.note_title);
if (d) return d;
} else {
@@ -110,7 +109,7 @@
const calDayNames = $derived([...DOW.slice(weekStartsOn), ...DOW.slice(0, weekStartsOn)]);
// Calendar tasks share the list's hide-completed filter (no sort).
const calBase = $derived(tasks.filter((t) => !hideCompleted || !t.completed));
const calBase = $derived(tasks.filter((t) => (!$tasksHideCompleted || !t.completed) && (!$tasksOnlyFlagged || !!t.due || !!t.priority)));
const tasksByDate = $derived.by(() => {
const m = new Map<string, TaskItem[]>();
@@ -186,7 +185,8 @@
<button class="prio-backdrop" onclick={() => { prioMenuFor = null; dueMenuFor = null; }} aria-label="Close menu"></button>
{/if}
<div class="tasks-controls">
<button class="tasks-ctl" class:active={hideCompleted} onclick={() => (hideCompleted = !hideCompleted)} title="Hide completed tasks">Hide done</button>
<button class="tasks-ctl" class:active={$tasksHideCompleted} onclick={() => ($tasksHideCompleted = !$tasksHideCompleted)} title="Hide completed tasks">Hide done</button>
<button class="tasks-ctl" class:active={$tasksOnlyFlagged} onclick={() => ($tasksOnlyFlagged = !$tasksOnlyFlagged)} title="Only show tasks with a due date or priority">Flagged</button>
{#if !isAndroid}
<div class="tasks-layout">
<button class="tasks-ctl" class:active={viewLayout === 'list'} onclick={() => ($tasksLayout = 'list')} title="List view">List</button>
@@ -196,7 +196,7 @@
{#if viewLayout === 'list'}
<div class="tasks-sort">
{#each [['due', 'Due'], ['priority', 'Priority'], ['note', 'Note']] as [v, label]}
<button class="tasks-ctl" class:active={sortBy === v} onclick={() => (sortBy = v as 'due' | 'priority' | 'note')}>{label}</button>
<button class="tasks-ctl" class:active={$tasksSort === v} onclick={() => ($tasksSort = v as 'due' | 'priority' | 'note')}>{label}</button>
{/each}
</div>
{/if}
@@ -458,7 +458,7 @@
background: transparent;
color: var(--text-tertiary);
border: 1px dashed var(--border-color);
opacity: 0;
opacity: 0.4;
transition: opacity 0.1s;
}
.task-row:hover .task-prio.prio-none { opacity: 1; }
@@ -529,7 +529,7 @@
background: transparent;
border-style: dashed;
color: var(--text-tertiary);
opacity: 0;
opacity: 0.4;
transition: opacity 0.1s;
}
.task-row:hover .task-due:not(.set) { opacity: 1; }
+3
View File
@@ -17,6 +17,9 @@ export const vaultReady = writable(false);
export const viewMode = writable<ViewMode>("all");
export const sortMode = writable<SortMode>("modified");
export const tasksLayout = writable<"list" | "calendar">("list");
export const tasksHideCompleted = writable<boolean>(true);
export const tasksOnlyFlagged = writable<boolean>(false);
export const tasksSort = writable<"due" | "priority" | "note">("due");
export const sidebarCollapsed = writable(false);
export const sidebarWidth = writable(220);
export const notelistWidth = writable(280);
+3
View File
@@ -111,6 +111,9 @@ export interface VaultState {
last_notebook?: string | null;
last_tag?: string | null;
tasks_layout?: string;
tasks_hide_completed?: boolean;
tasks_only_flagged?: boolean;
tasks_sort?: string;
}
export interface SearchResult {