135 lines
3.7 KiB
C#
135 lines
3.7 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using ChessCubing.App.Models;
|
|
|
|
namespace ChessCubing.App.Services;
|
|
|
|
public sealed class MatchStore(BrowserBridge browser, UserSession userSession)
|
|
{
|
|
public const string StorageKeyPrefix = "chesscubing-arena-state-v3";
|
|
public const string WindowNameKeyPrefix = "chesscubing-arena-state-v3";
|
|
|
|
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
|
|
{
|
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
|
};
|
|
|
|
private bool _dirty;
|
|
private long _lastPersistedAt;
|
|
private string? _activeStorageKey;
|
|
private string? _activeWindowNameKey;
|
|
|
|
public MatchState? Current { get; private set; }
|
|
|
|
public bool IsLoaded { get; private set; }
|
|
|
|
public async Task EnsureLoadedAsync()
|
|
{
|
|
var storageScope = await SyncStorageScopeAsync();
|
|
if (IsLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var raw = await browser.ReadMatchJsonAsync(storageScope.StorageKey, storageScope.WindowNameKey);
|
|
if (!string.IsNullOrWhiteSpace(raw))
|
|
{
|
|
var parsed = JsonSerializer.Deserialize<MatchState>(raw, JsonOptions);
|
|
if (parsed is not null && MatchEngine.IsSupportedSchemaVersion(parsed.SchemaVersion))
|
|
{
|
|
MatchEngine.NormalizeRecoveredMatch(parsed);
|
|
Current = parsed;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
Current = null;
|
|
}
|
|
|
|
IsLoaded = true;
|
|
_lastPersistedAt = MatchEngine.NowUnixMs();
|
|
}
|
|
|
|
public void SetCurrent(MatchState? match)
|
|
{
|
|
Current = match;
|
|
MarkDirty();
|
|
}
|
|
|
|
public void ReplaceCurrent(MatchState? match)
|
|
{
|
|
Current = match;
|
|
IsLoaded = true;
|
|
_dirty = false;
|
|
}
|
|
|
|
public void MarkDirty()
|
|
=> _dirty = true;
|
|
|
|
public async Task SaveAsync()
|
|
{
|
|
var storageScope = await SyncStorageScopeAsync();
|
|
if (!IsLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Current is null)
|
|
{
|
|
await browser.ClearMatchAsync(storageScope.StorageKey, storageScope.WindowNameKey);
|
|
_dirty = false;
|
|
_lastPersistedAt = MatchEngine.NowUnixMs();
|
|
return;
|
|
}
|
|
|
|
var json = JsonSerializer.Serialize(Current, JsonOptions);
|
|
await browser.WriteMatchJsonAsync(storageScope.StorageKey, storageScope.WindowNameKey, json);
|
|
_dirty = false;
|
|
_lastPersistedAt = MatchEngine.NowUnixMs();
|
|
}
|
|
|
|
public async Task FlushIfDueAsync(long minimumIntervalMs = 1_000)
|
|
{
|
|
if (!_dirty)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (MatchEngine.NowUnixMs() - _lastPersistedAt < minimumIntervalMs)
|
|
{
|
|
return;
|
|
}
|
|
|
|
await SaveAsync();
|
|
}
|
|
|
|
public async Task ClearAsync()
|
|
{
|
|
var storageScope = await SyncStorageScopeAsync();
|
|
Current = null;
|
|
IsLoaded = true;
|
|
_dirty = false;
|
|
_lastPersistedAt = MatchEngine.NowUnixMs();
|
|
await browser.ClearMatchAsync(storageScope.StorageKey, storageScope.WindowNameKey);
|
|
}
|
|
|
|
private async ValueTask<UserStorageScope> SyncStorageScopeAsync()
|
|
{
|
|
var storageScope = await userSession.GetStorageScopeAsync();
|
|
if (_activeStorageKey is not null &&
|
|
(_activeStorageKey != storageScope.StorageKey || _activeWindowNameKey != storageScope.WindowNameKey))
|
|
{
|
|
Current = null;
|
|
IsLoaded = false;
|
|
_dirty = false;
|
|
}
|
|
|
|
_activeStorageKey = storageScope.StorageKey;
|
|
_activeWindowNameKey = storageScope.WindowNameKey;
|
|
return storageScope;
|
|
}
|
|
}
|