|
@@ -0,0 +1,137 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.IO;
|
|
|
+using System.Linq;
|
|
|
+using System.Runtime.CompilerServices;
|
|
|
+using System.Threading;
|
|
|
+using System.Threading.Tasks;
|
|
|
+using InABox.Clients;
|
|
|
+using InABox.Core;
|
|
|
+using Newtonsoft.Json.Linq;
|
|
|
+
|
|
|
+namespace InABox.Mobile
|
|
|
+{
|
|
|
+
|
|
|
+ public interface IQueueUpdater
|
|
|
+ {
|
|
|
+ void ProcessQueue();
|
|
|
+ }
|
|
|
+
|
|
|
+ public interface IQueueUpdater<TEntity> : IQueueUpdater
|
|
|
+ where TEntity : Entity, IPersistent, IRemotable, new()
|
|
|
+ {
|
|
|
+ void Add(TEntity entity);
|
|
|
+
|
|
|
+ TEntity[] Items { get; }
|
|
|
+ }
|
|
|
+
|
|
|
+ public class QueuedUpdaterState
|
|
|
+ {
|
|
|
+ public Guid ID { get; set; }
|
|
|
+ public string OriginalState { get; set; }
|
|
|
+ public Dictionary<string,object> Changes { get; set; } = new();
|
|
|
+
|
|
|
+ public bool DisableUpdates { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ public class QueueUpdater<TEntity> : IDisposable, IQueueUpdater<TEntity>
|
|
|
+ where TEntity : Entity, IPersistent, IRemotable, new()
|
|
|
+ {
|
|
|
+
|
|
|
+ private static String StateFile(Guid id) => $"{id}.{typeof(TEntity).Name.Split('.').Last()}";
|
|
|
+
|
|
|
+ private Dictionary<TEntity, QueuedUpdaterState> _cache = new();
|
|
|
+
|
|
|
+ private string _path;
|
|
|
+ private IModelHost _host;
|
|
|
+
|
|
|
+ public QueueUpdater(IModelHost host, string path)
|
|
|
+ {
|
|
|
+ _host = host;
|
|
|
+ _path = path;
|
|
|
+ LoadCache();
|
|
|
+ _host.AddUpdateQueue(this);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Add(TEntity entity)
|
|
|
+ {
|
|
|
+ var _state = new QueuedUpdaterState();
|
|
|
+ _state.ID = Guid.NewGuid();
|
|
|
+ _state.OriginalState = Serialization.Serialize(entity);
|
|
|
+ Save(_state);
|
|
|
+ _cache[entity] = _state;
|
|
|
+
|
|
|
+ entity.PropertyChanged += (sender, args) =>
|
|
|
+ {
|
|
|
+ var state = _cache[entity];
|
|
|
+ state.Changes[args.PropertyName] = CoreUtils.GetPropertyValue(entity,args.PropertyName);
|
|
|
+ Save(state);
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Update(TEntity entity, Action<TEntity> action)
|
|
|
+ {
|
|
|
+ var state = _cache[entity];
|
|
|
+ state.DisableUpdates = true;
|
|
|
+ action(entity);
|
|
|
+ Save(state);
|
|
|
+ state.DisableUpdates = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Save(QueuedUpdaterState state)
|
|
|
+ {
|
|
|
+ var _text = Serialization.Serialize(state);
|
|
|
+ File.WriteAllText(StateFile(state.ID), _text);
|
|
|
+ }
|
|
|
+
|
|
|
+ public TEntity[] Items => _cache.Keys.ToArray();
|
|
|
+
|
|
|
+ private void LoadCache()
|
|
|
+ {
|
|
|
+ _cache.Clear();
|
|
|
+ var files = Directory.GetFiles(_path, $"*.{typeof(TEntity).Name.Split('.').Last()}");
|
|
|
+ foreach (var file in files)
|
|
|
+ {
|
|
|
+ if (!Guid.TryParse(Path.GetFileNameWithoutExtension(file), out Guid id))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ var _text = File.ReadAllText(file);
|
|
|
+ if (string.IsNullOrWhiteSpace(_text))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ var _state = Serialization.Deserialize<QueuedUpdaterState>(_text);
|
|
|
+ if (_state == null)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ var _entity = Serialization.Deserialize<TEntity>(_state.OriginalState);
|
|
|
+ if (_entity == null)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ foreach (var change in _state.Changes)
|
|
|
+ CoreUtils.SetPropertyValue(_entity,change.Key,change.Value);
|
|
|
+
|
|
|
+ _cache[_entity] = _state;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void ProcessQueue()
|
|
|
+ {
|
|
|
+ var item = Items.FirstOrDefault();
|
|
|
+ if (item != null)
|
|
|
+ {
|
|
|
+ if (item.IsChanged() && _host.IsConnected())
|
|
|
+ {
|
|
|
+ new Client<TEntity>().Save(item, "Updated from Mobile Device");
|
|
|
+ var state = Serialization.Serialize(item);
|
|
|
+ File.WriteAllText(StateFile(_cache[item].ID),state);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Dispose()
|
|
|
+ {
|
|
|
+ _host.RemoveUpdateQueue(this);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|