< Summary

Information
Class: Chronicis.Client.ViewModels.SearchViewModel
Assembly: Chronicis.Client
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Client/ViewModels/SearchViewModel.cs
Line coverage
100%
Covered lines: 22
Uncovered lines: 0
Coverable lines: 22
Total lines: 97
Line coverage: 100%
Branch coverage
100%
Covered branches: 4
Total branches: 4
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_IsLoading()100%11100%
set_IsLoading(...)100%11100%
get_Results()100%11100%
set_Results(...)100%11100%
NavigateToArticle(...)100%44100%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/ViewModels/SearchViewModel.cs

#LineLine coverage
 1using Chronicis.Client.Abstractions;
 2using Chronicis.Client.Services;
 3using Chronicis.Shared.DTOs;
 4using Chronicis.Shared.Extensions;
 5
 6namespace Chronicis.Client.ViewModels;
 7
 8/// <summary>
 9/// ViewModel for the Search page.
 10/// Handles content search, result state, and navigation to matched articles.
 11/// </summary>
 12public sealed class SearchViewModel : ViewModelBase
 13{
 14    private readonly ISearchApiService _searchApi;
 15    private readonly IBreadcrumbService _breadcrumbService;
 16    private readonly ITreeStateService _treeState;
 17    private readonly IAppNavigator _navigator;
 18    private readonly ILogger<SearchViewModel> _logger;
 19
 20    private bool _isLoading;
 21    private GlobalSearchResultsDto? _results;
 22
 1523    public SearchViewModel(
 1524        ISearchApiService searchApi,
 1525        IBreadcrumbService breadcrumbService,
 1526        ITreeStateService treeState,
 1527        IAppNavigator navigator,
 1528        ILogger<SearchViewModel> logger)
 29    {
 1530        _searchApi = searchApi;
 1531        _breadcrumbService = breadcrumbService;
 1532        _treeState = treeState;
 1533        _navigator = navigator;
 1534        _logger = logger;
 1535    }
 36
 37    /// <summary>Whether a search request is in flight.</summary>
 38    public bool IsLoading
 39    {
 1240        get => _isLoading;
 1141        private set => SetField(ref _isLoading, value);
 42    }
 43
 44    /// <summary>The results of the most recent search, or <c>null</c> if no search has been performed.</summary>
 45    public GlobalSearchResultsDto? Results
 46    {
 2447        get => _results;
 1248        private set => SetField(ref _results, value);
 49    }
 50
 51    /// <summary>
 52    /// Executes a content search for the given query.
 53    /// Clears results and does nothing when <paramref name="query"/> is null or whitespace.
 54    /// </summary>
 55    public async Task SearchAsync(string? query)
 56    {
 57        if (string.IsNullOrWhiteSpace(query))
 58        {
 59            Results = null;
 60            return;
 61        }
 62
 63        IsLoading = true;
 64
 65        try
 66        {
 67            Results = await _searchApi.SearchContentAsync(query);
 68        }
 69        catch (Exception ex)
 70        {
 71            _logger.LogErrorSanitized(ex, "Search error for query: {Query}", query);
 72            Results = null;
 73        }
 74        finally
 75        {
 76            IsLoading = false;
 77        }
 78    }
 79
 80    /// <summary>
 81    /// Expands the tree to the article and navigates to it.
 82    /// </summary>
 83    public void NavigateToArticle(ArticleSearchResultDto result)
 84    {
 385        _treeState.ExpandPathToAndSelect(result.Id);
 86
 387        if (result.AncestorPath != null && result.AncestorPath.Any())
 88        {
 189            var path = _breadcrumbService.BuildArticleUrl(result.AncestorPath);
 190            _navigator.NavigateTo(path);
 91        }
 92        else
 93        {
 294            _navigator.NavigateTo($"/article/{result.Slug}");
 95        }
 296    }
 97}