Skip to content

fix(bases): keep Task List groups collapsed on first render - #21

Merged
renatomen merged 25 commits into
mainfrom
fix/tasklist-collapsed-default-lost-on-first-render
Aug 1, 2026
Merged

fix(bases): keep Task List groups collapsed on first render#21
renatomen merged 25 commits into
mainfrom
fix/tasklist-collapsed-default-lost-on-first-render

Conversation

@renatomen

Copy link
Copy Markdown
Owner

Problem

A Task List base view configured with Default collapsed state: Collapsed renders every group collapsed, but the first chevron click does the opposite of what you ask: the clicked group stays collapsed and every other group expands.

Root cause

The collapse default is seeded during render, then overwritten by a state snapshot captured before that render.

  1. The first render() returns early because Bases has not delivered data yet (the !this.data?.data guard in src/bases/TaskListView.ts). collapsedGroups is still empty.

  2. Data arrives and onDataUpdated() runs the debounced render wrapper in src/bases/BasesViewBase.ts:

    const savedState = this.getEphemeralState();    // collapsedGroups: []  — captured BEFORE render
    try { await this.render(); }                    // seeds every group key
    finally { this.setEphemeralState(savedState); } // writes [] back over the seed
  3. That render seeds the collapse default through initializeCollapseStateForSnapshot and paints the DOM all-collapsed.

  4. The finally replaces collapsedGroups with the stale empty array and does not re-render — so the DOM shows collapsed groups while the view's state says nothing is collapsed.

  5. The wipe is permanent: re-seeding is gated on initializedPrimaryGroupKeys, which already holds every key, so initializeCollapseStateForSnapshot never seeds again.

  6. handleGroupToggle then reads collapsedGroups.has(key) === false and collapses the clicked group. The re-render expands all the others.

Fix

Restore collapse state from ephemeral state only before the view has built its first grouping snapshot; afterwards the view's own sets are authoritative.

hasInitializedCollapseState() and restoreCollapsedStateFromEphemeral() are extracted so the new guard is visible in review — the moved block is otherwise unchanged.

Tests

Two regression tests in tests/unit/ui/TaskListView.groupCollapse.test.ts replay the ordering the render wrapper actually produces (seed, then restore a pre-render snapshot): one asserts the collapse default survives it, one asserts a toggle then expands only the clicked group.

The suite already covered the opposite, safe ordering (restore-then-seed). The ordering production actually produces was untested, which is how this shipped.

Verification

  • TaskListView.groupCollapse.test.ts: 9/9 pass — 2 of them fail on the current base without the fix, with exactly the reported symptom.
  • Full suite: 3872 pass. The 17 failures across 7 suites are pre-existing on this base, verified by re-running them on an unmodified checkout.
  • tsc --noEmit and eslint clean.
  • Manually verified in a test vault with a full Obsidian restart: a chevron click now expands that group and leaves the others collapsed.

Known trade-off

If Obsidian ever calls setEphemeralState after the view's first render, the collapse portion of that payload is now ignored. The only observed post-render caller today is the render wrapper's own save/restore round trip.

Note on the broader design

The underlying coupling remains: get/setEphemeralState serves both Obsidian's cross-reload persistence and an intra-render scroll round trip, so correctness depends on call ordering relative to render(). Separating those concerns in BasesViewBase would eliminate the whole class of bug, but it touches KanbanView and CalendarView, which are not affected today. Deliberately left out of this change so the fix stays small and reviewable.

mcuste and others added 6 commits July 11, 2026 12:35
Keep calendar credentials and tokens out of data.json, where they
could be exposed through version control, backups, or vault sync.
Completing a materialized occurrence wrote the occurrence date
(occurrence_date) into completedDate, losing the actual completion
date. The parent's complete_instances already tracks which occurrence
was fulfilled, so completedDate can record when the user actually
completed it — matching non-recurring task behavior.

Fixes callumalpass#2125 (callumalpass/tasknotes)

Co-Authored-By: Claude Fable 5 <[email protected]>
A Task List view configured with defaultCollapsedState "Collapsed" seeds its
collapsed-group sets during render. The debounced data-update render in
BasesViewBase captures ephemeral state before that render and restores it in a
finally block, so the pre-render (empty) snapshot overwrote the seed. Nothing
re-seeded afterwards, because the group keys were already marked initialized.

The result: the DOM still showed every group collapsed while the view's state
said none were, so the first chevron click collapsed the clicked group and
expanded all the others.

Restore collapse state from ephemeral state only before the view has built its
first grouping snapshot; afterwards the live sets are authoritative.
@renatomen renatomen closed this Jul 29, 2026
@renatomen renatomen reopened this Jul 29, 2026
@renatomen renatomen closed this Jul 29, 2026
@renatomen renatomen reopened this Jul 29, 2026
abbiefalls90 and others added 19 commits August 1, 2026 08:26
* Fix recurring all-day ICS event ends

* Clean up ICS recurrence PR notes

---------

Co-authored-by: callumalpass <[email protected]>
* fix(kanban): add helper to hide empty columns across swimlanes

* fix(kanban): hide empty columns in swimlane mode

* test(kanban): guard hide-empty-columns swimlane behavior

* Add release note for Kanban swimlane fix

---------

Co-authored-by: callumalpass <[email protected]>
Respect the expandable-subtasks setting, keep the project badge non-interactive when expansion is disabled, synchronize translated tooltips, and document the fix in unreleased notes.
…#2041)

Gate task-creation sync on the master Google Calendar export setting and prevent defensive sync calls from persisting retry work while sync is intentionally disabled. Add regression coverage for issue callumalpass#2040 and document the fix in unreleased notes.
Make keyboard-driven date and time quick actions focus the natural-language field when it is available, retain the native date-input fallback, add regression coverage for issue callumalpass#2046, and document the fix in unreleased notes.
Keep manually adjusted recurring tasks tied to their underlying occurrence, preserve due-date offsets and scheduled times, handle future and long-interval rules and moved Google Calendar exceptions, add regression coverage, and document the fix for callumalpass#2064.
Keep task-card and relationship widgets mounted through reading-mode DOM rebuilds, refresh reused leaves and visible metadata changes, distinguish transient cache misses from confirmed non-task notes, add regression coverage, and document the fix for callumalpass#2081.
Add {{projectFolder}} and {{projectFolders}} folder-template variables for placing tasks alongside linked project notes, document their use, add focused tests, and include the feature in unreleased notes with contributor credit.
…orage

Move calendar OAuth secrets to Obsidian Secret Storage
…l-completed-date

fix: record real completion date on materialized occurrences
…urrence-filename-template

feat: configurable filename template for materialized occurrences (callumalpass#2126)
@sonarqubecloud

sonarqubecloud Bot commented Aug 1, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
7.0% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@renatomen
renatomen merged commit 4e80934 into main Aug 1, 2026
2 of 3 checks passed
@renatomen
renatomen deleted the fix/tasklist-collapsed-default-lost-on-first-render branch August 1, 2026 20:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants