fix(todo): drop stale filter selections when source values disappear
Some checks failed
CI / verify (push) Failing after 7s

If a user filters by a project/context/priority and then edits tasks until
that value no longer exists, the chip is gone but the active set still
contains it — leaving the list filtered to empty with no chip to toggle
off. Watch each available-list and prune orphaned entries from the
matching active set. Also tightens the task-input placeholder and reflows
some template whitespace.
This commit is contained in:
Julien Calixte
2026-06-13 22:11:31 +02:00
parent 4c29151f7c
commit af7c87a18c

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, defineAsyncComponent, ref, watch } from "vue" import { computed, defineAsyncComponent, Ref, ref, watch } from "vue"
import TodoTxtItem from "@/components/TodoTxtItem.vue" import TodoTxtItem from "@/components/TodoTxtItem.vue"
import { useGitHubContent } from "@/hooks/useGitHubContent.hook" import { useGitHubContent } from "@/hooks/useGitHubContent.hook"
@@ -69,7 +69,9 @@ const taskEntries = computed(() => {
if (!isBlank(line)) out.push({ line, index }) if (!isBlank(line)) out.push({ line, index })
}) })
// Stable sort: equal priorities preserve file order. // Stable sort: equal priorities preserve file order.
out.sort((a, b) => priorityRank(a.line.priority) - priorityRank(b.line.priority)) out.sort(
(a, b) => priorityRank(a.line.priority) - priorityRank(b.line.priority)
)
return out return out
}) })
@@ -114,6 +116,23 @@ const setStatusFilter = (next: Exclude<StatusFilter, "all">) => {
statusFilter.value = statusFilter.value === next ? "all" : next statusFilter.value = statusFilter.value === next ? "all" : next
} }
// Drop entries from `active` that no longer appear in `available`. Without
// this, editing tasks until a selected project/context/priority disappears
// leaves the filter "stuck on" — hasFilters stays true with no visible chip
// to toggle off.
const pruneActive = <T>(active: Ref<Set<T>>, available: ReadonlyArray<T>) => {
const valid = new Set(available)
const pruned = new Set<T>()
active.value.forEach((v) => {
if (valid.has(v)) pruned.add(v)
})
if (pruned.size !== active.value.size) active.value = pruned
}
watch(allProjects, (next) => pruneActive(activeProjects, next))
watch(allContexts, (next) => pruneActive(activeContexts, next))
watch(allPriorities, (next) => pruneActive(activePriorities, next))
const toggleProject = (p: string) => { const toggleProject = (p: string) => {
const next = new Set(activeProjects.value) const next = new Set(activeProjects.value)
if (next.has(p)) next.delete(p) if (next.has(p)) next.delete(p)
@@ -262,16 +281,12 @@ const createTodoFile = async () => {
</div> </div>
<template v-else> <template v-else>
<form <form v-if="canPush" class="mb-4 join w-full" @submit.prevent="addTask">
v-if="canPush"
class="mb-4 join w-full"
@submit.prevent="addTask"
>
<input <input
v-model="newTaskInput" v-model="newTaskInput"
type="text" type="text"
class="input input-bordered input-sm join-item flex-1" class="input input-bordered input-sm join-item flex-1"
placeholder="Add a task ((A) priority +project @context)…" placeholder="(A) task +project @context…"
/> />
<button <button
type="submit" type="submit"
@@ -310,10 +325,7 @@ const createTodoFile = async () => {
</button> </button>
</div> </div>
</section> </section>
<section <section v-if="allPriorities.length" class="todo-filter-column">
v-if="allPriorities.length"
class="todo-filter-column"
>
<h4 class="todo-filter-label">Priorities</h4> <h4 class="todo-filter-label">Priorities</h4>
<div class="todo-filter-chips"> <div class="todo-filter-chips">
<button <button
@@ -331,10 +343,7 @@ const createTodoFile = async () => {
</button> </button>
</div> </div>
</section> </section>
<section <section v-if="allProjects.length" class="todo-filter-column">
v-if="allProjects.length"
class="todo-filter-column"
>
<h4 class="todo-filter-label">Projects</h4> <h4 class="todo-filter-label">Projects</h4>
<div class="todo-filter-chips"> <div class="todo-filter-chips">
<button <button
@@ -349,10 +358,7 @@ const createTodoFile = async () => {
</button> </button>
</div> </div>
</section> </section>
<section <section v-if="allContexts.length" class="todo-filter-column">
v-if="allContexts.length"
class="todo-filter-column"
>
<h4 class="todo-filter-label">Contexts</h4> <h4 class="todo-filter-label">Contexts</h4>
<div class="todo-filter-chips"> <div class="todo-filter-chips">
<button <button
@@ -502,6 +508,5 @@ const createTodoFile = async () => {
.todo-filter-chip { .todo-filter-chip {
cursor: pointer; cursor: pointer;
} }
} }
</style> </style>