using NucleicApp.Services; using NucleicProtocol.Interop; namespace NucleicApp.ViewModels; public enum ShellSection { Home, Sessions, Approvals, Projects, Settings, } /// Navigation state for the WinUI split shell. The future XAML layer binds to this class and /// swaps views; session activation remains an ordinary protocol subscription intent. public sealed class ShellViewModel : ViewModelBase, IDisposable { private readonly RendererStore store; private readonly HostConnection connection; private ShellSection section = ShellSection.Home; private SessionDetailViewModel? selectedSession; private HostConnectionState connectionState; private bool disposed; public ShellViewModel(RendererStore store, HostConnection connection) { this.store = store; this.connection = connection; connectionState = connection.State; store.PropertyChanged += StoreChanged; connection.StateChanged += ConnectionChanged; Home = new HomeViewModel(store); } public HomeViewModel Home { get; } public ShellSection Section { get => section; private set => Set(ref section, value); } public SessionDetailViewModel? SelectedSession { get => selectedSession; private set => Set(ref selectedSession, value); } public HostConnectionState ConnectionState { get => connectionState; private set => Set(ref connectionState, value); } public int PendingApprovalCount => store.PendingApprovals.Count; public void Navigate(ShellSection destination) { ObjectDisposedException.ThrowIf(disposed, this); if (destination != ShellSection.Sessions) CloseSession(); Section = destination; } public ProtocolResult OpenSession(string sessionId, ulong? sinceSequence = null) { ObjectDisposedException.ThrowIf(disposed, this); if (SelectedSession?.SessionId != sessionId) { SelectedSession?.Dispose(); SelectedSession = new SessionDetailViewModel(sessionId, store, connection); } Section = ShellSection.Sessions; return SelectedSession.Activate(sinceSequence); } public void CloseSession() { SelectedSession?.Dispose(); SelectedSession = null; } private void StoreChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs args) { if (args.PropertyName == nameof(RendererStore.PendingApprovals)) Raise(nameof(PendingApprovalCount)); } private void ConnectionChanged(HostConnectionState state) => ConnectionState = state; public void Dispose() { if (disposed) return; disposed = true; CloseSession(); Home.Dispose(); store.PropertyChanged -= StoreChanged; connection.StateChanged -= ConnectionChanged; } }