using JSMR.Application.Common.Search; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Routing; using Microsoft.JSInterop; namespace JSMR.UI.Blazor.Shared; public abstract class SearchPageBase : ComponentBase, IAsyncDisposable where TState : notnull { [Inject] protected NavigationManager Nav { get; set; } = default!; [Inject] protected IJSRuntime JS { get; set; } = default!; protected TState State { get; private set; } = default!; protected SearchResult? Result { get; private set; } protected bool IsLoading { get; private set; } private bool _suppressNextLocation; private bool _isAlive = true; private CancellationTokenSource _cancellationTokenSource = new(); protected override async Task OnInitializedAsync() { Nav.LocationChanged += OnLocationChanged; State = ParseStateFromUri(Nav.Uri); await RunSearchAsync(); } protected async Task UpdateAsync(TState next) { if (Equals(next, State)) return; State = next; NavigateToState(next); _ = RunSearchAsync(); } private void NavigateToState(TState next) { string uri = BuildUri(next); _suppressNextLocation = true; Nav.NavigateTo(uri); } private async void OnLocationChanged(object? sender, LocationChangedEventArgs e) { if (_suppressNextLocation) { _suppressNextLocation = false; return; } if (!_isAlive) return; if (!IsThisPage(e.Location)) return; TState next = ParseStateFromUri(e.Location); if (Equals(next, State)) return; State = next; await InvokeAsync(StateHasChanged); _ = RunSearchAsync(); } private async Task RunSearchAsync() { await JS.InvokeVoidAsync("pageHelpers.scrollToTop"); try { IsLoading = true; //StateHasChanged(); _cancellationTokenSource.Cancel(); _cancellationTokenSource = new(); Result = await ExecuteSearchAsync(State, _cancellationTokenSource.Token); } catch (OperationCanceledException) { } finally { if (_isAlive) { IsLoading = false; await InvokeAsync(StateHasChanged); } } } protected abstract TState ParseStateFromUri(string absoluteUri); protected abstract string BuildUri(TState state); protected abstract bool IsThisPage(string absoluteUri); protected abstract Task> ExecuteSearchAsync(TState state, CancellationToken cancellationToken); public async ValueTask DisposeAsync() { _isAlive = false; Nav.LocationChanged -= OnLocationChanged; _cancellationTokenSource.Cancel(); _cancellationTokenSource.Dispose(); GC.SuppressFinalize(this); await Task.CompletedTask; } }