99 lines
3.7 KiB
C#
99 lines
3.7 KiB
C#
using System.ComponentModel;
|
|||
|
|
using System.Text.Json;
|
||
|
|
using NucleicApp.Services;
|
||
|
|
using NucleicProtocol.Interop;
|
||
|
|
|
||
|
|
namespace NucleicApp.ViewModels;
|
||
|
|
|
||
|
|
/// Presentation and intent surface for one session detail screen. Transcript rows remain owned
|
||
|
|
/// by the shared Swift projector; this model forwards only that session's splice diffs.
|
||
|
|
public sealed class SessionDetailViewModel : ViewModelBase, IDisposable
|
||
|
|
{
|
||
|
|
private readonly RendererStore store;
|
||
|
|
private readonly HostConnection connection;
|
||
|
|
private ProjectionDiff? lastProjection;
|
||
|
|
private bool active;
|
||
|
|
private bool disposed;
|
||
|
|
|
||
|
|
public SessionDetailViewModel(
|
||
|
|
string sessionId, RendererStore store, HostConnection connection)
|
||
|
|
{
|
||
|
|
SessionId = sessionId;
|
||
|
|
this.store = store;
|
||
|
|
this.connection = connection;
|
||
|
|
store.PropertyChanged += StoreChanged;
|
||
|
|
store.ProjectionChanged += ProjectionChanged;
|
||
|
|
}
|
||
|
|
|
||
|
|
public string SessionId { get; }
|
||
|
|
public SessionSummary? Session => store.Sessions.FirstOrDefault(item => item.Id == SessionId);
|
||
|
|
public IReadOnlyList<ApprovalRequest> PendingApprovals => store.PendingApprovals
|
||
|
|
.Where(item => item.SessionId == SessionId).ToArray();
|
||
|
|
public ProjectionDiff? LastProjection
|
||
|
|
{
|
||
|
|
get => lastProjection;
|
||
|
|
private set => Set(ref lastProjection, value);
|
||
|
|
}
|
||
|
|
|
||
|
|
public ProtocolResult Activate(ulong? sinceSequence = null)
|
||
|
|
{
|
||
|
|
ObjectDisposedException.ThrowIf(disposed, this);
|
||
|
|
if (active) return ProtocolResult.Ok;
|
||
|
|
var result = connection.SendIntent(ClientIntents.Subscribe(SessionId, sinceSequence));
|
||
|
|
if (result == ProtocolResult.Ok) active = true;
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
public ProtocolResult SendText(string text)
|
||
|
|
{
|
||
|
|
ObjectDisposedException.ThrowIf(disposed, this);
|
||
|
|
if (string.IsNullOrWhiteSpace(text)) return ProtocolResult.InvalidArgument;
|
||
|
|
return connection.SendIntent(ClientIntents.SendInput(SessionId, text));
|
||
|
|
}
|
||
|
|
|
||
|
|
public ProtocolResult Allow(ApprovalRequest approval, JsonElement? updatedInput = null) =>
|
||
|
|
Respond(approval, ClientIntents.ApprovalAllow(approval.Id, updatedInput));
|
||
|
|
|
||
|
|
public ProtocolResult Deny(ApprovalRequest approval, string? reason = null) =>
|
||
|
|
Respond(approval, ClientIntents.ApprovalDeny(approval.Id, reason));
|
||
|
|
|
||
|
|
public ProtocolResult AllowAlways(ApprovalRequest approval, ApprovalAlwaysScope scope) =>
|
||
|
|
Respond(approval, ClientIntents.ApprovalAllowAlways(approval.Id, scope));
|
||
|
|
|
||
|
|
public ProtocolResult CancelRun(ApprovalRequest approval) =>
|
||
|
|
Respond(approval, ClientIntents.ApprovalCancelRun(approval.Id));
|
||
|
|
|
||
|
|
private ProtocolResult Respond(ApprovalRequest approval, string intent)
|
||
|
|
{
|
||
|
|
ObjectDisposedException.ThrowIf(disposed, this);
|
||
|
|
if (approval.SessionId != SessionId) return ProtocolResult.InvalidArgument;
|
||
|
|
return connection.SendIntent(intent);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void StoreChanged(object? sender, PropertyChangedEventArgs args)
|
||
|
|
{
|
||
|
|
if (args.PropertyName == nameof(RendererStore.Sessions)) Raise(nameof(Session));
|
||
|
|
else if (args.PropertyName == nameof(RendererStore.PendingApprovals))
|
||
|
|
Raise(nameof(PendingApprovals));
|
||
|
|
}
|
||
|
|
|
||
|
|
private void ProjectionChanged(ProjectionDiff diff)
|
||
|
|
{
|
||
|
|
if (diff.Root.TryGetProperty("sessionID", out var session) &&
|
||
|
|
session.ValueKind == JsonValueKind.String && session.GetString() == SessionId)
|
||
|
|
{
|
||
|
|
LastProjection = diff;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
if (disposed) return;
|
||
|
|
disposed = true;
|
||
|
|
store.PropertyChanged -= StoreChanged;
|
||
|
|
store.ProjectionChanged -= ProjectionChanged;
|
||
|
|
if (active) _ = connection.SendIntent(ClientIntents.Unsubscribe(SessionId));
|
||
|
|
active = false;
|
||
|
|
}
|
||
|
|
}
|