Files
speckle-sharp-connectors/DUI3/Speckle.Connectors.DUI/Models/DocumentModelStore.cs
T
Adam Hathcock 83efebfed9 Introduce Event aggregator (#417)
* Remove Dataflow usage

* format and remove dep

* merge fixes

* Fix serializer

* Add MainThreadContext

* add some main context detection

* add RevitMainThreadContext

* remove revit async?

* formatting

* use mainThreadContext

* Use more MainThreadContext

* some rearranging

* renaming

* Revit needs new run async

* merge fixes

* gather on worker, convert on main

* operations know threading but not host apps

* rhino options

* revit can receive

* autocad in progress

* need to yield for UI thread async

* revamp yield

* Found APIContext and removed it

* ArcGIS runs all workers on MCT thread

* Refactor ThreadContext and ArcGIS saving is always on a worker

* Revit threading is simplier?

* ArcGIS can not always go to the queued task

* format

* fix tekla compile errors

* Use EventAggregator to decouple exception handler and UI

* it's ALIVE

* regenerate locks

* Add Prism Evening to DUI

* clean up

* always run on background thread

* Clean up to be specific

* update etabs

* thread context

* autocad threading?

* merge fixes

* remove more async

* clean up

* fix build issues

* Do top level handling in event aggregator

* add some rhino events

* add more Rhino events and do Idle as OneTime with Id

* fix up rhino idle usages

* fmt

* can build agian

* Use valuetask

* fmt

* fix up some bridge execution to be sync

* cleanup

* add some non async paths for progress

* format

* remove needless selection

* Fixes

* Convert tekla

* selection event is used without idle

* Build fixes from merge

* Fix tests and clean up

* Add new events

* Properly dispose one time events

* Minor tekla updates
2025-01-21 14:20:01 +00:00

140 lines
3.4 KiB
C#

using System.Diagnostics;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.Connectors.DUI.Utils;
using Speckle.Sdk;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.DUI.Models;
/// <summary>
/// Encapsulates the state Speckle needs to persist in the host app's document.
/// </summary>
public abstract class DocumentModelStore(IJsonSerializer serializer)
{
private readonly List<ModelCard> _models = new();
//needed for javascript UI
public IReadOnlyList<ModelCard> Models
{
get
{
lock (_models)
{
return _models.AsReadOnly();
}
}
}
public virtual Task OnDocumentStoreInitialized() => Task.CompletedTask;
public virtual bool IsDocumentInit { get; set; }
// TODO: not sure about this, throwing an exception, needs some thought...
// Further note (dim): If we reach to the stage of throwing an exception here because a model is not found, there's a huge misalignment between the UI's list of model cards and the host app's.
// In theory this should never really happen, but if it does
public ModelCard GetModelById(string id)
{
var model = _models.First(model => model.ModelCardId == id) ?? throw new ModelNotFoundException();
return model;
}
public void AddModel(ModelCard model)
{
lock (_models)
{
_models.Add(model);
SaveState();
}
}
public void ClearAndSave()
{
lock (_models)
{
_models.Clear();
SaveState();
}
}
public void UpdateModel(ModelCard model)
{
lock (_models)
{
var index = _models.FindIndex(m => m.ModelCardId == model.ModelCardId);
if (index == -1)
{
throw new ModelNotFoundException("Model card not found to update.");
}
_models[index] = model;
SaveState();
}
}
public void RemoveModel(ModelCard model)
{
lock (_models)
{
var index = _models.FindIndex(m => m.ModelCardId == model.ModelCardId);
if (index == -1)
{
throw new ModelNotFoundException("Model card not found to update.");
}
_models.RemoveAt(index);
SaveState();
}
}
public IEnumerable<SenderModelCard> GetSenders()
{
lock (_models)
{
return _models
.Where(model => model.TypeDiscriminator == nameof(SenderModelCard))
.Cast<SenderModelCard>()
.ToList();
}
}
protected string Serialize() => serializer.Serialize(Models);
// POC: this seemms more like a IModelsDeserializer?, seems disconnected from this class
protected List<ModelCard> Deserialize(string models) => serializer.Deserialize<List<ModelCard>>(models).NotNull();
protected void SaveState()
{
lock (_models)
{
var state = Serialize();
HostAppSaveState(state);
}
}
/// <summary>
/// Implement this method according to the host app's specific ways of reading custom data from its file.
/// </summary>
protected abstract void HostAppSaveState(string modelCardState);
protected abstract void LoadState();
protected void LoadFromString(string? models)
{
try
{
lock (_models)
{
_models.Clear();
if (string.IsNullOrEmpty(models))
{
return;
}
_models.AddRange(Deserialize(models.NotNull()).NotNull());
}
}
catch (Exception ex) when (!ex.IsFatal())
{
ClearAndSave();
Debug.WriteLine(ex.Message); // POC: Log here error and notify UI that cards not read succesfully
}
}
}