Synchronise les parties entre les deux devices

This commit is contained in:
2026-04-15 23:40:18 +02:00
parent 3b88b9abe6
commit e0c3a41ccd
11 changed files with 527 additions and 18 deletions

View File

@@ -3,6 +3,7 @@
@implements IAsyncDisposable
@inject MatchStore Store
@inject NavigationManager Navigation
@inject SocialRealtimeService Realtime
<PageTitle>ChessCubing Arena | Phase Chrono</PageTitle>
<PageBody Page="chrono" BodyClass="@ChronoBodyClass" />
@@ -156,6 +157,7 @@ else if (Match is not null && summary is not null && blackZone is not null && wh
private CancellationTokenSource? _tickerCancellation;
private bool _ready;
private bool ShowArbiterModal;
private long _knownCollaborativeRevision;
private MatchState? Match => Store.Current;
@@ -171,7 +173,16 @@ else if (Match is not null && summary is not null && blackZone is not null && wh
return;
}
Realtime.Changed += HandleRealtimeChanged;
await Realtime.EnsureStartedAsync();
await Store.EnsureLoadedAsync();
if (!string.IsNullOrWhiteSpace(Store.Current?.CollaborationSessionId))
{
await Realtime.EnsureJoinedPlaySessionAsync(Store.Current.CollaborationSessionId);
await ApplyCollaborativeSyncAsync();
}
_ready = true;
if (Match is null)
@@ -200,9 +211,19 @@ else if (Match is not null && summary is not null && blackZone is not null && wh
_tickerCancellation.Dispose();
}
Realtime.Changed -= HandleRealtimeChanged;
await Store.FlushIfDueAsync(0);
}
private void HandleRealtimeChanged()
=> _ = InvokeAsync(HandleRealtimeChangedAsync);
private async Task HandleRealtimeChangedAsync()
{
await ApplyCollaborativeSyncAsync();
StateHasChanged();
}
private async Task RunTickerAsync(CancellationToken cancellationToken)
{
try
@@ -385,7 +406,14 @@ else if (Match is not null && summary is not null && blackZone is not null && wh
private async Task ResetMatchAsync()
{
var sessionId = Match?.CollaborationSessionId;
await Store.ClearAsync();
if (!string.IsNullOrWhiteSpace(sessionId))
{
await Realtime.PublishMatchStateAsync(null, "/application.html");
}
Navigation.NavigateTo("/application.html", replace: true);
}
@@ -394,6 +422,12 @@ else if (Match is not null && summary is not null && blackZone is not null && wh
Store.MarkDirty();
await Store.SaveAsync();
var route = Match is not null && string.IsNullOrEmpty(Match.Result) && Match.Phase == MatchEngine.PhaseCube
? "/cube.html"
: "/chrono.html";
await Realtime.PublishMatchStateAsync(Match, route);
if (Match is not null && string.IsNullOrEmpty(Match.Result) && Match.Phase == MatchEngine.PhaseCube)
{
Navigation.NavigateTo("/cube.html", replace: true);
@@ -574,6 +608,31 @@ else if (Match is not null && summary is not null && blackZone is not null && wh
private static string BoolString(bool value)
=> value ? "true" : "false";
private async Task ApplyCollaborativeSyncAsync()
{
var snapshot = Realtime.CollaborativeSnapshot;
if (snapshot is null || snapshot.Revision <= _knownCollaborativeRevision)
{
return;
}
_knownCollaborativeRevision = snapshot.Revision;
Store.ReplaceCurrent(snapshot.Match);
await Store.SaveAsync();
var route = NormalizeRoute(snapshot.Route);
if (!string.Equals(route, "/chrono.html", StringComparison.OrdinalIgnoreCase))
{
Navigation.NavigateTo(route, replace: true);
}
}
private static string NormalizeRoute(string? route)
{
var normalized = string.IsNullOrWhiteSpace(route) ? "/application.html" : route.Trim();
return normalized.StartsWith('/') ? normalized : $"/{normalized}";
}
private sealed record ChronoSummaryView(
string Title,
string Subtitle,