Skip to content

refactor(source-control): slim SourceControlViewModel via SyncQueue rename, DiffStatProvider, SelectionController #136

Description

@ClaudiaFang

Background

Clean-code review of PR #135 found the overall direction sound (domain boundary, ChangePresentation adapter, RefreshState) but flagged that SourceControlViewModel is starting to absorb workflow state. This issue tracks the three "must-fix" items before the ViewModel becomes a god object — the review notes "now is easy, two-three PRs later it starts to hurt."

Must-fix

1. Rename selectedItemssyncQueue

selectedItems is a UI-interaction term; the domain concept is "the pending sync operation queue." Rename the SourceControlViewState.selectedItems projection field to syncQueue (aligns with the SYNC QUEUE UI label already landed). Touches SourceControlViewModel, SourceControlView, and tests. Purely a rename.

2. Extract DiffStatProvider

The +/- diff-stat cache (Map<ChangeId, ChangeStat | null>), eager local-only load, eager selected load, lazy two-sided load, and clear-on-refresh currently all live inside SourceControlView.ts. Extract them into a DiffStatProvider (get / eagerLocal / eagerSelected / lazyLoad / clear) so the view stops owning cache+load+invalidate and the data concern is isolated. UI-layer class (the loader reads view-side diff content).

3. Extract SelectionController

PushSelectionStore already exists and is clean, but SourceControlView holds a direct reference to it and reaches past the ViewModel (this.selection.includeForPush/excludeFromPush/getSelectedChangeIds), and the View implements batch ops (clearSelection, toggleFolderSelect) inline. Consolidate selection behind a SelectionController exposed via viewModel.selection so the view no longer bypasses the ViewModel and batch ops live in one domain-named controller. (Exact shape — wrap vs rename vs minimal-expose — to be confirmed; preserves the buildSummary/matchesFilter isIncluded contract.)

Constraints

  • Domain-untouched invariant must hold: src/logic/source-control/ may only gain the new SelectionController/DiffStatProvider-related files and ViewModel edits; no sync-pipeline changes.
  • TDD: update/extend Vitest tests alongside source.
  • DoD: npx eslint . 0 errors; npm run build passes (tsc + Obsidian 1.11.0 compat + esbuild); npx vitest run passes.

Out of scope (next PR)

Notification UX, full operation history, diff viewer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions