diff --git a/TorchRemote/Services/ApiClientService.cs b/TorchRemote/Services/ApiClientService.cs index 0984711..082fcd9 100644 --- a/TorchRemote/Services/ApiClientService.cs +++ b/TorchRemote/Services/ApiClientService.cs @@ -1,13 +1,17 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Net.WebSockets; +using System.Runtime.CompilerServices; +using System.Threading; using System.Threading.Tasks; using Refit; using Websocket.Client; namespace TorchRemote.Services; -public class ApiClientService +public class ApiClientService : IDisposable { public const string Version = "v1"; public string BearerToken @@ -16,37 +20,32 @@ public class ApiClientService set => _client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse($"Bearer {value}"); } private readonly HttpClient _client = new(); + private readonly CancellationTokenSource _tokenSource = new(); public string BaseUrl { get => _client.BaseAddress?.ToString() ?? "http://localhost"; set => _client.BaseAddress = new($"{value}/api/{Version}/"); } - public event EventHandler? Connected; + public IObservable Connected { get; } public ApiClientService() { Api = RestService.For(_client); - Task.Run(ConnectionTimer); + Connected = ConnectionTimer(_tokenSource.Token).ToObservable(); } public IRemoteApi Api { get; } - private async Task ConnectionTimer() + private async IAsyncEnumerable ConnectionTimer([EnumeratorCancellation] CancellationToken token) { - while (true) + while (!token.IsCancellationRequested) { - await Task.Delay(1000); - try - { - await Api.GetServerStatus(); - break; - } - catch - { - } + await Task.Delay(1000, token); + var success = (await Api.GetServerStatus()).Error is null; + yield return success; + if (success) + await Task.Delay(TimeSpan.FromSeconds(30), token); } - - Connected?.Invoke(this, EventArgs.Empty); } public Task WatchChatAsync() => StartWebsocketConnectionAsync("live/chat"); @@ -72,4 +71,10 @@ public class ApiClientService await client.Start(); return client; } + public void Dispose() + { + _client.Dispose(); + _tokenSource.Cancel(); + _tokenSource.Dispose(); + } } diff --git a/TorchRemote/TorchRemote.csproj b/TorchRemote/TorchRemote.csproj index 52b1e40..8221c9c 100644 --- a/TorchRemote/TorchRemote.csproj +++ b/TorchRemote/TorchRemote.csproj @@ -27,6 +27,7 @@ + diff --git a/TorchRemote/ViewModels/RemoteServerViewModel.cs b/TorchRemote/ViewModels/RemoteServerViewModel.cs index 0569b6f..60c6ed4 100644 --- a/TorchRemote/ViewModels/RemoteServerViewModel.cs +++ b/TorchRemote/ViewModels/RemoteServerViewModel.cs @@ -39,9 +39,10 @@ public class RemoteServerViewModel : TabViewModelBase, IScreen .Select(b => b.ViewModel) .InvokeCommand(Router, x => x.Navigate); - Observable.FromEventPattern(_clientService, nameof(_clientService.Connected)) + _clientService.Connected + .DistinctUntilChanged() .ObserveOn(RxApp.MainThreadScheduler) - .Subscribe(_ => Connected = true); + .ToPropertyEx(this, x => x.Connected); this.WhenAnyValue(x => x.Connected) .Where(b => b) @@ -57,6 +58,5 @@ public class RemoteServerViewModel : TabViewModelBase, IScreen public RoutingState Router { get; set; } = new(); [JsonIgnore] - [Reactive] - public bool Connected { get; set; } + public extern bool Connected { [ObservableAsProperty] get; } }