Files
OpenNest/OpenNest.Desktop/Document.cs
T
aj 38de7ab3a3 refactor(desktop): move Document and undo recording into OpenNest.Desktop
Document and Undo/ (ChangeScope, NestFingerprint, NestUndoState) use
only Core, Data, IO and Engine, so they move into the toolkit-neutral
library both desktop front ends share. Namespaces are unchanged, so the
WinForms app compiles as before; it keeps using the internal scope
factories and fingerprint through InternalsVisibleTo.

The scope, fingerprint and SaveAs/save-point rules now have Linux tests
in OpenNest.Desktop.Tests; before, only the Windows-only WinForms suite
covered them.
2026-10-29 18:40:49 -04:00

92 lines
3.6 KiB
C#

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using OpenNest.Data;
using OpenNest.Engine.BestFit;
using OpenNest.IO;
using OpenNest.Undo;
namespace OpenNest
{
public class Document
{
public Nest Nest { get; set; }
/// <summary>
/// This document's undo history: whole-nest states, reset with the first one when the
/// nest window opens. Saving marks a save point; it never clears the history.
/// </summary>
public UndoHistory<NestUndoState> History { get; } = new();
public DateTime LastSaveDate { get; private set; }
public string LastSavePath { get; private set; }
private readonly NestSaveSession remoteSession = new();
public Guid RemoteId => remoteSession.RemoteId;
public Guid PendingCreateId => remoteSession.PendingCreateId;
public bool PendingCreateRequiresReconciliation => remoteSession.PendingCreateId != Guid.Empty && remoteSession.RequiresReload;
public bool PendingCreateWasCopy => remoteSession.PendingCreateWasCopy;
public bool IsFirstDatabaseSave(string serverUrl) => remoteSession.IsFirstSave(serverUrl);
/// <summary>
/// A duplicate name the user already accepted in Nest Info, so the first Save
/// does not ask about it again.
/// </summary>
public string AcceptedDuplicateName { get; set; }
public void BindRemote(Guid id, string serverUrl) => remoteSession.Bind(id, serverUrl);
public void BindRemote(Guid id, string serverUrl, int revision) => remoteSession.Bind(id, serverUrl, revision);
public Task<NestRecord> ReconcilePendingCreateAsync(INestRepository repository, string serverUrl,
CancellationToken cancellationToken = default) =>
remoteSession.ReconcilePendingCreateAsync(repository, serverUrl, cancellationToken);
public int RemoteRevision => remoteSession.Revision;
public string Name => Nest?.Name;
public bool HasSavePath => File.Exists(LastSavePath);
public void SaveAs(string path)
{
var name = Path.GetFileNameWithoutExtension(path);
Nest.Name = name;
LastSaveDate = DateTime.Now;
LastSavePath = path;
var writer = new NestWriter(Nest) { BestFitStore = BestFitCacheStore.Shared };
writer.Write(path);
if (History.Current != null)
History.MarkSaved();
}
/// <summary>Serializes the current nest to a .nest archive in memory.</summary>
public byte[] Serialize()
{
using var stream = new MemoryStream();
new NestWriter(Nest) { BestFitStore = BestFitCacheStore.Shared }.Write(stream);
return stream.ToArray();
}
/// <summary>Uploads this nest, preserving its server identity only on success.</summary>
public async Task<NestRecord> SaveToDatabaseAsync(
INestRepository repository, string serverUrl, bool saveCopy = false,
CancellationToken cancellationToken = default)
{
var bytes = Serialize();
// The save point is the state serialized here, unless an edit was recorded since.
var state = History.Current;
var saved = await remoteSession.SaveAsync(
repository, serverUrl, Nest, bytes, saveCopy, cancellationToken);
LastSaveDate = DateTime.Now;
if (state != null && ReferenceEquals(History.Current, state))
History.MarkSaved();
return saved;
}
}
}