fix(wpf): MainViewModel subscription via direct Subscribe + Dispatcher marshal

The .ObserveOn(SynchronizationContextScheduler(SyncContext.Current)) path captured a synchronization context at subscribe time that didn't pump subsequent OnNext emissions in WPF startup, leaving the Participants collection empty even though the engine's discovery was firing. Console probe confirmed engine sees Teams sources; only the GUI consumer was broken.

Switched to direct Subscribe + Dispatcher.InvokeAsync inside the callback (same pattern proven by Console.Program.cs). Subscribe-time context capture is gone; every emission marshals to the UI thread on its own.
This commit is contained in:
Zac Gaetano 2026-05-15 14:27:17 -04:00
parent d282e1b0f8
commit 209b643cd5

View file

@ -346,10 +346,18 @@ public sealed class MainViewModel : ObservableObject, IDisposable
// Apply the operator's saved sort preference, if any.
ApplySortFromPrefs();
// Subscribe directly (no ObserveOn) and marshal to the UI thread inside
// the callback via Dispatcher.InvokeAsync. The previous ObserveOn(
// SynchronizationContextScheduler) path captured SynchronizationContext
// .Current at subscribe time — fragile in WPF startup ordering, where
// the UI thread's SyncContext can be in a transitional state during
// App.OnStartup and the captured context never pumps subsequent
// OnNext calls. Direct subscribe + explicit dispatcher marshal is the
// pattern proven by Console.Program.cs (engine emits, consumer marshals).
_participantsSub = controller.Participants
.ObserveOn(new SynchronizationContextScheduler(
System.Threading.SynchronizationContext.Current ?? new DispatcherSynchronizationContext(_dispatcher)))
.Subscribe(OnParticipantsChanged);
.Subscribe(snapshot => _dispatcher.InvokeAsync(
() => OnParticipantsChanged(snapshot),
DispatcherPriority.Background));
_alertsSub = controller.Alerts
.ObserveOn(new SynchronizationContextScheduler(