Skip to content

Add message-bus redesign plan for MainWindowViewModel - #236

Merged
niklaslundberg merged 6 commits into
mainfrom
claude/mainwindowviewmodel-refactor-f4ehp2
Jun 15, 2026
Merged

niklaslundberg merged 6 commits into
mainfrom
claude/mainwindowviewmodel-refactor-f4ehp2

Conversation

@niklaslundberg

Copy link
Copy Markdown
Owner

Capture a new holistic target architecture for moving the remaining
Explorer/Request panel state out of MainWindowViewModel: feature VMs own
their state, the ILeftPanelContext/IRequestPanelContext facades are
retired, and cross-feature notifications flow over a UI-agnostic custom
Rx message bus in Core. Command flows stay direct (returning outcome
records) by default, with a mediator used selectively. Delivered
incrementally, one slice per PR.

Cross-link the new plan from the two existing architecture plans.

https://claude.ai/code/session_01GSb4ZCyb4wFsk3wjcZgQSf

claude added 2 commits June 15, 2026 07:41
Capture a new holistic target architecture for moving the remaining
Explorer/Request panel state out of MainWindowViewModel: feature VMs own
their state, the ILeftPanelContext/IRequestPanelContext facades are
retired, and cross-feature notifications flow over a UI-agnostic custom
Rx message bus in Core. Command flows stay direct (returning outcome
records) by default, with a mediator used selectively. Delivered
incrementally, one slice per PR.

Cross-link the new plan from the two existing architecture plans.

https://claude.ai/code/session_01GSb4ZCyb4wFsk3wjcZgQSf
Introduce IMessageBus and a Subject-backed MessageBus in
Arbor.HttpClient.Core/Messaging as the foundation for cross-feature
notifications in the MainWindowViewModel redesign. The bus is
framework-agnostic (depends only on System.Reactive, already pinned) and
scheduler-agnostic; UI consumers marshal to their own scheduler at the
subscription site.

Nothing is wired to the bus yet. Covered by MessageBusTests
(delivery, type isolation, multi-subscriber, dispose, ordering,
no-replay) and the full solution suite stays green.

https://claude.ai/code/session_01GSb4ZCyb4wFsk3wjcZgQSf
claude added 3 commits June 15, 2026 08:16
Move the History tab's state out of MainWindowViewModel into a dedicated
HistoryPanelViewModel that owns the request-history list, its search
filter, and the reload/load commands. Loading a history entry now
publishes a HistoryRequestLoadRequested message on the bus instead of
mutating the request editor directly, so the History panel no longer
depends on the request panel; Main subscribes and applies the entry to
the active editor.

- ILeftPanelContext drops HistorySearchQuery/History/LoadHistoryRequestCommand
  in favour of a single HistoryPanel child VM; LeftPanelView rebinds the
  History tab through it.
- App composes one application-wide MessageBus and injects it into Main,
  which passes it to the History panel.
- Add a project-wide global-using alias so the Core bus names resolve
  unambiguously alongside ReactiveUI's unused IMessageBus/MessageBus.
- DI slice deferred as premature; plan doc updated to reflect progress.

Covered by new HistoryPanelViewModelTests and updated UI tests; full
solution suite green (Core 242, Integration 9, Sqlite 32, E2E 602).

https://claude.ai/code/session_01GSb4ZCyb4wFsk3wjcZgQSf
Move the Scheduled Jobs tab's state out of MainWindowViewModel into a
dedicated ScheduledJobsPanelViewModel that owns the job list and the
add/remove commands (wrapping ScheduledJobsWorkflow). Application-options
values it needs (default interval, follow-redirects, auto-start) are
supplied as delegates, and the add-job tab switch is preserved via an
onJobAdded callback, so the panel does not depend on the options or
tab-selector state that remains in Main.

- ILeftPanelContext drops ScheduledJobs/AddScheduledJobCommand/
  RemoveScheduledJobCommand in favour of a single ScheduledJobsPanel child
  VM; LeftPanelView and the LeftPanelViewModel proxy rebind through it.
- Main drops the workflow field, the Add/Remove commands, and routes
  load/dispose through the panel VM.

Re-sequenced the Collections slice (3) to after the request panel (5):
its load/add/inherited-header flows are bound to the request tabs and
editor, so a clean extraction is blocked until the request panel owns
them. Plan doc updated.

Covered by new ScheduledJobsPanelViewModelTests and updated UI/decoupling/
screenshot-generator tests; full solution suite green (Core 242,
Integration 9, Sqlite 32, E2E 606).

https://claude.ai/code/session_01GSb4ZCyb4wFsk3wjcZgQSf
Move the integrated response panel's display state (status, timing,
body/raw/headers tabs, web-view preview) and the project/snapshot/restore/
clear logic out of MainWindowViewModel into a dedicated, independently
tested ResponseStateViewModel.

To keep this step low-risk, MainWindowViewModel retains thin delegating
response properties and forwards the sub-VM's PropertyChanged as its own,
so IRequestPanelContext/IResponseActionsContext, both response views, and
their code-behind subscriptions are untouched. Rebinding views to
App.Response and retiring the contract members is deferred to a later
sub-step of the request-panel slice.

Covered by new ResponseStateViewModelTests; response-state setters in the
deadlock/shortcuts/execution UI tests now go through viewModel.Response.
Full solution suite green (Core 242, Integration 9, Sqlite 32, E2E 611);
response screenshots unchanged.

https://claude.ai/code/session_01GSb4ZCyb4wFsk3wjcZgQSf
@niklaslundberg

Copy link
Copy Markdown
Owner Author

@copilot fix review comment

…pper

Co-authored-by: niklaslundberg <1242006+niklaslundberg@users.noreply.github.com>

Copilot AI commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

@copilot fix review comment

Fixed in the latest commit. Subject.Synchronize returns AnonymousSubject<T> which is not IDisposable, so the inner Subject<TMessage> was never being disposed. The fix introduces a private SubjectEntry<TMessage> wrapper that holds the inner Subject<T> reference and disposes it when MessageBus.Dispose() is called. MessageBus now implements IDisposable and throws ObjectDisposedException on any post-dispose call. CodeQL scan is clean.

@niklaslundberg
niklaslundberg merged commit 47642f2 into main Jun 15, 2026
23 checks passed
@niklaslundberg
niklaslundberg deleted the claude/mainwindowviewmodel-refactor-f4ehp2 branch June 15, 2026 09:36
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.

4 participants