Files
Nexus.Reader/src/NexusReader.UI.Shared/Services/SyncService.cs
T

113 lines
3.3 KiB
C#

using FluentResults;
using Microsoft.AspNetCore.SignalR.Client;
using NexusReader.Application.Abstractions.Services;
using System.Net.Http;
namespace NexusReader.UI.Shared.Services;
public class SyncService : ISyncService, IAsyncDisposable
{
private readonly HttpClient _httpClient;
private readonly INativeStorageService _storageService;
private readonly IPlatformService _platformService;
private HubConnection? _hubConnection;
private bool _isInitialized;
private CancellationTokenSource? _debounceCts;
public event Action<string, DateTime>? OnProgressReceived;
public SyncService(
HttpClient httpClient,
INativeStorageService storageService,
IPlatformService platformService)
{
_httpClient = httpClient;
_storageService = storageService;
_platformService = platformService;
}
public async Task<Result> InitializeAsync()
{
if (_isInitialized) return Result.Ok();
var tokenResult = await _storageService.GetSecureString("nexus_auth_token");
if (tokenResult.IsFailed) return Result.Fail("Not authenticated");
var baseUrl = _httpClient.BaseAddress?.ToString() ?? "http://localhost:5000/";
var hubUrl = new Uri(new Uri(baseUrl), "synchub").ToString();
_hubConnection = new HubConnectionBuilder()
.WithUrl(hubUrl, options =>
{
options.AccessTokenProvider = () => Task.FromResult<string?>(tokenResult.Value);
})
.WithAutomaticReconnect()
.Build();
_hubConnection.On<string, DateTime>("ProgressUpdated", (pageId, timestamp) =>
{
OnProgressReceived?.Invoke(pageId, timestamp);
});
try
{
await _hubConnection.StartAsync();
_isInitialized = true;
return Result.Ok();
}
catch (Exception ex)
{
return Result.Fail(ex.Message);
}
}
private string? _lastSentPageId;
public async Task<Result> UpdateProgressAsync(string pageId)
{
if (pageId == _lastSentPageId) return Result.Ok();
// Proper trailing-edge debounce
_debounceCts?.Cancel();
_debounceCts = new CancellationTokenSource();
var token = _debounceCts.Token;
_ = Task.Run(async () =>
{
try
{
await Task.Delay(2000, token);
if (!_isInitialized) await InitializeAsync();
if (_hubConnection?.State == HubConnectionState.Connected)
{
await _hubConnection.SendAsync("UpdateProgress", pageId, token);
_lastSentPageId = pageId;
}
}
catch (TaskCanceledException) { /* Ignored, user kept scrolling */ }
catch (Exception ex)
{
Console.WriteLine($"[SyncService] Error sending progress: {ex.Message}");
}
}, token);
return Result.Ok();
}
public async Task DisposeAsync()
{
_debounceCts?.Cancel();
if (_hubConnection != null)
{
await _hubConnection.DisposeAsync();
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
await DisposeAsync();
}
}