< Summary

Information
Class: Chronicis.Client.Infrastructure.AppNavigator
Assembly: Chronicis.Client
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Client/Infrastructure/AppNavigator.cs
Line coverage
100%
Covered lines: 84
Uncovered lines: 0
Coverable lines: 84
Total lines: 206
Line coverage: 100%
Branch coverage
100%
Covered branches: 44
Total branches: 44
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_BaseUri()100%11100%
get_Uri()100%11100%
NavigateTo(...)100%11100%
GoToWorldAsync(...)100%11100%
GoToCampaignAsync(...)100%11100%
GoToCampaignAsync(...)100%11100%
GoToArcAsync(...)100%11100%
GoToArcAsync(...)100%11100%
GoToSessionAsync(...)100%11100%
GoToSessionAsync(...)100%11100%
GoToSessionNoteAsync(...)100%11100%
GoToMapListingAsync(...)100%11100%
GoToMapAsync(...)100%11100%
GoToWikiArticleAsync(...)100%11100%
GoToTutorialAsync(...)100%11100%
GoToArticleAsync(...)100%66100%
GoToSearchResultAsync(...)100%1818100%
BuildSessionNoteUrl(...)100%44100%
BuildWikiArticleUrl(...)100%1616100%
LogDeprecatedAndReturnDashboard(...)100%11100%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Infrastructure/AppNavigator.cs

#LineLine coverage
 1using Chronicis.Client.Abstractions;
 2using Chronicis.Client.Services.Routing;
 3using Chronicis.Shared.DTOs;
 4using Chronicis.Shared.DTOs.Sessions;
 5using Chronicis.Shared.Enums;
 6using Chronicis.Shared.Extensions;
 7using Microsoft.AspNetCore.Components;
 8using Microsoft.Extensions.Logging;
 9
 10namespace Chronicis.Client.Infrastructure;
 11
 12/// <summary>
 13/// Wraps <see cref="NavigationManager"/> to implement <see cref="IAppNavigator"/>.
 14/// </summary>
 15public sealed class AppNavigator : IAppNavigator
 16{
 17    private readonly NavigationManager _navigation;
 18    private readonly IAppUrlBuilder _urlBuilder;
 19    private readonly ILogger<AppNavigator> _logger;
 20
 21    public AppNavigator(NavigationManager navigation, IAppUrlBuilder urlBuilder, ILogger<AppNavigator> logger)
 22    {
 3523        _navigation = navigation;
 3524        _urlBuilder = urlBuilder;
 3525        _logger = logger;
 3526    }
 27
 28    /// <inheritdoc />
 129    public string BaseUri => _navigation.BaseUri;
 30
 31    /// <inheritdoc />
 132    public string Uri => _navigation.Uri;
 33
 34    /// <inheritdoc />
 35    public void NavigateTo(string url, bool replace = false) =>
 236        _navigation.NavigateTo(url, replace);
 37
 38    public Task GoToWorldAsync(string worldSlug, bool replace = false)
 39    {
 240        _navigation.NavigateTo(_urlBuilder.ForWorld(worldSlug), replace);
 241        return Task.CompletedTask;
 42    }
 43
 44    public Task GoToCampaignAsync(string worldSlug, string campaignSlug, bool replace = false)
 45    {
 246        _navigation.NavigateTo(_urlBuilder.ForCampaign(worldSlug, campaignSlug), replace);
 247        return Task.CompletedTask;
 48    }
 49
 50    public Task GoToCampaignAsync(CampaignDto campaign, bool replace = false) =>
 151        GoToCampaignAsync(campaign.WorldSlug, campaign.Slug, replace);
 52
 53    public Task GoToArcAsync(string worldSlug, string campaignSlug, string arcSlug, bool replace = false)
 54    {
 355        _navigation.NavigateTo(_urlBuilder.ForArc(worldSlug, campaignSlug, arcSlug), replace);
 356        return Task.CompletedTask;
 57    }
 58
 59    public Task GoToArcAsync(ArcDto arc, bool replace = false) =>
 160        GoToArcAsync(arc.WorldSlug, arc.CampaignSlug, arc.Slug, replace);
 61
 62    public Task GoToSessionAsync(string worldSlug, string campaignSlug, string arcSlug, string sessionSlug, bool replace
 63    {
 264        _navigation.NavigateTo(_urlBuilder.ForSession(worldSlug, campaignSlug, arcSlug, sessionSlug), replace);
 265        return Task.CompletedTask;
 66    }
 67
 68    public Task GoToSessionAsync(SessionTreeDto session, bool replace = false) =>
 169        GoToSessionAsync(session.WorldSlug, session.CampaignSlug, session.ArcSlug, session.Slug, replace);
 70
 71    public Task GoToSessionNoteAsync(string worldSlug, string campaignSlug, string arcSlug, string sessionSlug, string n
 72    {
 173        _navigation.NavigateTo(_urlBuilder.ForSessionNote(worldSlug, campaignSlug, arcSlug, sessionSlug, noteSlug), repl
 174        return Task.CompletedTask;
 75    }
 76
 77    public Task GoToMapListingAsync(string worldSlug, bool replace = false)
 78    {
 179        _navigation.NavigateTo(_urlBuilder.ForMapListing(worldSlug), replace);
 180        return Task.CompletedTask;
 81    }
 82
 83    public Task GoToMapAsync(string worldSlug, string mapSlug, bool replace = false)
 84    {
 185        _navigation.NavigateTo(_urlBuilder.ForMap(worldSlug, mapSlug), replace);
 186        return Task.CompletedTask;
 87    }
 88
 89    public Task GoToWikiArticleAsync(string worldSlug, IReadOnlyList<string> articleSlugSegments, bool replace = false)
 90    {
 191        _navigation.NavigateTo(_urlBuilder.ForWikiArticle(worldSlug, articleSlugSegments), replace);
 192        return Task.CompletedTask;
 93    }
 94
 95    public Task GoToTutorialAsync(string tutorialSlug, bool replace = false)
 96    {
 297        _navigation.NavigateTo(_urlBuilder.ForTutorial(tutorialSlug), replace);
 298        return Task.CompletedTask;
 99    }
 100
 101    public Task GoToArticleAsync(ArticleDto article, bool replace = false)
 102    {
 9103        var url = article.Type switch
 9104        {
 1105            ArticleType.Tutorial => _urlBuilder.ForTutorial(article.Slug),
 3106            ArticleType.SessionNote => BuildSessionNoteUrl(article),
 1107            ArticleType.Session => LogDeprecatedAndReturnDashboard(article.Id),
 4108            _ => BuildWikiArticleUrl(article)
 9109        };
 9110        _navigation.NavigateTo(url, replace);
 9111        return Task.CompletedTask;
 112    }
 113
 114    public Task GoToSearchResultAsync(ArticleSearchResultDto result, bool replace = false)
 115    {
 116        string url;
 6117        switch (result.Type)
 118        {
 119            case ArticleType.SessionNote:
 2120                if (result.ArticleSlugChain.Count == 0
 2121                    || string.IsNullOrEmpty(result.CampaignSlug)
 2122                    || string.IsNullOrEmpty(result.ArcSlug)
 2123                    || string.IsNullOrEmpty(result.SessionSlug))
 124                {
 1125                    _logger.LogErrorSanitized("Search result missing session slug context for navigation");
 1126                    url = "/dashboard";
 127                }
 128                else
 129                {
 1130                    url = _urlBuilder.ForSessionNote(
 1131                        result.WorldSlug,
 1132                        result.CampaignSlug,
 1133                        result.ArcSlug,
 1134                        result.SessionSlug,
 1135                        result.ArticleSlugChain[0]);
 136                }
 1137                break;
 138
 139            case ArticleType.Tutorial:
 2140                if (result.ArticleSlugChain.Count == 0)
 141                {
 1142                    _logger.LogErrorSanitized("Search result has empty slug chain for tutorial navigation");
 1143                    url = "/dashboard";
 144                }
 145                else
 146                {
 1147                    url = _urlBuilder.ForTutorial(result.ArticleSlugChain[0]);
 148                }
 1149                break;
 150
 151            default:
 2152                if (string.IsNullOrEmpty(result.WorldSlug) || result.ArticleSlugChain.Count == 0)
 153                {
 1154                    _logger.LogErrorSanitized("Search result missing world slug or slug chain for navigation");
 1155                    url = "/dashboard";
 156                }
 157                else
 158                {
 1159                    url = _urlBuilder.ForWikiArticle(result.WorldSlug, result.ArticleSlugChain);
 160                }
 161                break;
 162        }
 6163        _navigation.NavigateTo(url, replace);
 6164        return Task.CompletedTask;
 165    }
 166
 167    private string BuildSessionNoteUrl(ArticleDto article)
 168    {
 3169        if (article.Breadcrumbs == null || article.Breadcrumbs.Count < 5)
 170        {
 2171            _logger.LogWarningSanitized("Session note article missing breadcrumbs for slug navigation");
 2172            return "/dashboard";
 173        }
 1174        return _urlBuilder.ForSessionNote(
 1175            article.Breadcrumbs[0].Slug,
 1176            article.Breadcrumbs[1].Slug,
 1177            article.Breadcrumbs[2].Slug,
 1178            article.Breadcrumbs[3].Slug,
 1179            article.Breadcrumbs[4].Slug);
 180    }
 181
 182    private string BuildWikiArticleUrl(ArticleDto article)
 183    {
 4184        var worldSlug = article.WorldSlug;
 4185        if (string.IsNullOrEmpty(worldSlug) && article.Breadcrumbs != null)
 2186            worldSlug = article.Breadcrumbs.FirstOrDefault(b => b.IsWorld)?.Slug ?? string.Empty;
 187
 4188        var slugChain = article.Breadcrumbs?
 4189            .Where(b => !b.IsWorld)
 4190            .Select(b => b.Slug)
 4191            .ToList() ?? new List<string>();
 192
 4193        if (string.IsNullOrEmpty(worldSlug) || slugChain.Count == 0)
 194        {
 2195            _logger.LogWarningSanitized("Article missing world slug or slug chain for navigation");
 2196            return "/dashboard";
 197        }
 2198        return _urlBuilder.ForWikiArticle(worldSlug, slugChain);
 199    }
 200
 201    private string LogDeprecatedAndReturnDashboard(Guid articleId)
 202    {
 1203        _logger.LogWarningSanitized("Deprecated Session article type encountered during navigation");
 1204        return "/dashboard";
 205    }
 206}