< Summary

Information
Class: Chronicis.Client.Services.Tree.TreeDataBuilder
Assembly: Chronicis.Client
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Client/Services/Tree/TreeDataBuilder.cs
Line coverage
100%
Covered lines: 237
Uncovered lines: 0
Coverable lines: 237
Total lines: 591
Line coverage: 100%
Branch coverage
100%
Covered branches: 50
Total branches: 50
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%
BuildWorldNode(...)100%2020100%
BuildCampaignNode(...)100%22100%
BuildArcNode(...)100%66100%
BuildSessionNode(...)100%44100%
BuildArticleNodeWithChildren(...)100%22100%
CreateArticleNode(...)100%11100%
CreateVirtualGroupNode(...)100%11100%
GetDocumentIcon(...)100%1616100%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Services/Tree/TreeDataBuilder.cs

#LineLine coverage
 1using Chronicis.Client.Models;
 2using Chronicis.Shared.DTOs;
 3using Chronicis.Shared.DTOs.Maps;
 4using Chronicis.Shared.DTOs.Sessions;
 5using Chronicis.Shared.Enums;
 6
 7namespace Chronicis.Client.Services.Tree;
 8
 9/// <summary>
 10/// Responsible for building the navigation tree structure from API data.
 11/// Fetches worlds, campaigns, arcs, and articles and constructs the hierarchical tree.
 12/// </summary>
 13internal sealed class TreeDataBuilder
 14{
 15    private readonly IArticleApiService _articleApi;
 16    private readonly IWorldApiService _worldApi;
 17    private readonly ICampaignApiService _campaignApi;
 18    private readonly IArcApiService _arcApi;
 19    private readonly ISessionApiService _sessionApi;
 20    private readonly IMapApiService _mapApi;
 21    private readonly ILogger _logger;
 22
 23    public TreeDataBuilder(
 24        IArticleApiService articleApi,
 25        IWorldApiService worldApi,
 26        ICampaignApiService campaignApi,
 27        IArcApiService arcApi,
 28        ISessionApiService sessionApi,
 29        IMapApiService mapApi,
 30        ILogger logger)
 31    {
 2432        _articleApi = articleApi;
 2433        _worldApi = worldApi;
 2434        _campaignApi = campaignApi;
 2435        _arcApi = arcApi;
 2436        _sessionApi = sessionApi;
 2437        _mapApi = mapApi;
 2438        _logger = logger;
 2439    }
 40
 41    /// <summary>
 42    /// Result of building the tree, containing the node index and cached articles.
 43    /// </summary>
 44    public sealed class BuildResult
 45    {
 46        public required TreeNodeIndex NodeIndex { get; init; }
 47        public required List<ArticleTreeDto> CachedArticles { get; init; }
 48    }
 49
 50    /// <summary>
 51    /// Builds the complete tree structure by fetching all data from APIs.
 52    /// </summary>
 53    public async Task<BuildResult> BuildTreeAsync()
 54    {
 55        var nodeIndex = new TreeNodeIndex();
 56        var stopwatch = System.Diagnostics.Stopwatch.StartNew();
 57
 58        // Phase 1: Fetch worlds and articles in parallel
 59        var worldsTask = _worldApi.GetWorldsAsync();
 60        var articlesTask = _articleApi.GetAllArticlesAsync();
 61
 62        await Task.WhenAll(worldsTask, articlesTask);
 63
 64        var worlds = worldsTask.Result;
 65        var allArticles = articlesTask.Result;
 66
 67        if (!worlds.Any())
 68        {
 69            return new BuildResult
 70            {
 71                NodeIndex = nodeIndex,
 72                CachedArticles = allArticles
 73            };
 74        }
 75
 76        // Phase 2: Fetch all world details in parallel
 77        _logger.LogDebug("Phase 2: Fetching world details in parallel...");
 78        var worldDetailTasks = worlds.Select(w => _worldApi.GetWorldAsync(w.Id)).ToList();
 79        var worldDetails = await Task.WhenAll(worldDetailTasks);
 80
 81        var worldDetailLookup = worldDetails
 82            .Where(wd => wd != null)
 83            .ToDictionary(wd => wd!.Id, wd => wd!);
 84
 85        _logger.LogDebug("Phase 2 complete: {Count} world details in {Ms}ms",
 86            worldDetailLookup.Count, stopwatch.ElapsedMilliseconds);
 87
 88        // Phase 3: Collect all campaigns and fetch arcs, links, documents in parallel
 89        var allCampaigns = worldDetails
 90            .Where(wd => wd != null)
 91            .SelectMany(wd => wd!.Campaigns ?? new List<CampaignDto>())
 92            .ToList();
 93
 94        _logger.LogDebug("Phase 3: Fetching arcs for {Count} campaigns, world links, and documents in parallel...", allC
 95
 96        var arcTasks = allCampaigns.Select(c => _arcApi.GetArcsByCampaignAsync(c.Id)).ToList();
 97        var linkTasks = worlds.Select(w => _worldApi.GetWorldLinksAsync(w.Id)).ToList();
 98        var documentTasks = worlds.Select(w => _worldApi.GetWorldDocumentsAsync(w.Id)).ToList();
 99        var mapTasks = worlds.Select(w => _mapApi.ListMapsForWorldAsync(w.Id)).ToList();
 100
 101        await Task.WhenAll(
 102            Task.WhenAll(arcTasks),
 103            Task.WhenAll(linkTasks),
 104            Task.WhenAll(documentTasks),
 105            Task.WhenAll(mapTasks)
 106        );
 107
 108        var arcResults = arcTasks.Select(t => t.Result).ToArray();
 109
 110        var linksByWorld = new Dictionary<Guid, List<WorldLinkDto>>();
 111        for (int i = 0; i < worlds.Count; i++)
 112        {
 113            linksByWorld[worlds[i].Id] = linkTasks[i].Result;
 114        }
 115
 116        var documentsByWorld = new Dictionary<Guid, List<WorldDocumentDto>>();
 117        for (int i = 0; i < worlds.Count; i++)
 118        {
 119            documentsByWorld[worlds[i].Id] = documentTasks[i].Result;
 120        }
 121
 122        var mapsByWorld = new Dictionary<Guid, List<MapSummaryDto>>();
 123        for (int i = 0; i < worlds.Count; i++)
 124        {
 125            mapsByWorld[worlds[i].Id] = mapTasks[i].Result;
 126        }
 127
 128        var arcsByCampaign = new Dictionary<Guid, List<ArcDto>>();
 129        for (int i = 0; i < allCampaigns.Count; i++)
 130        {
 131            arcsByCampaign[allCampaigns[i].Id] = arcResults[i];
 132        }
 133
 134        // Phase 3b: Fetch sessions for all arcs in parallel
 135        var allArcs = arcResults.SelectMany(r => r).ToList();
 136        var sessionTasks = allArcs.Select(a => _sessionApi.GetSessionsByArcAsync(a.Id)).ToList();
 137        await Task.WhenAll(sessionTasks);
 138
 139        var sessionsByArc = new Dictionary<Guid, List<SessionTreeDto>>();
 140        for (int i = 0; i < allArcs.Count; i++)
 141        {
 142            sessionsByArc[allArcs[i].Id] = sessionTasks[i].Result;
 143        }
 144
 145        _logger.LogDebug("Phase 3 complete: {Count} total arcs in {Ms}ms",
 146            arcResults.Sum(r => r.Count), stopwatch.ElapsedMilliseconds);
 147
 148        // Phase 4: Build the tree structure
 149        _logger.LogDebug("Phase 4: Building tree structure...");
 150
 151        var articleIndex = allArticles.ToDictionary(a => a.Id);
 152
 153        foreach (var world in worlds.OrderBy(w => w.Name))
 154        {
 155            if (!worldDetailLookup.TryGetValue(world.Id, out var worldDetail))
 156            {
 157                _logger.LogWarning("World detail not found for {WorldId}", world.Id);
 158                continue;
 159            }
 160
 161            var worldLinks = linksByWorld[world.Id];
 162            var worldDocuments = documentsByWorld[world.Id];
 163
 164            var worldMaps = mapsByWorld[world.Id];
 165
 166            var worldNode = BuildWorldNode(
 167                world,
 168                worldDetail,
 169                allArticles,
 170                articleIndex,
 171                arcsByCampaign,
 172                sessionsByArc,
 173                worldLinks,
 174                worldDocuments,
 175                worldMaps,
 176                nodeIndex);
 177
 178            nodeIndex.AddRootNode(worldNode);
 179        }
 180
 181        // Handle orphan articles (no world assigned)
 182        var orphanArticles = allArticles.Where(a => !a.WorldId.HasValue && a.ParentId == null).ToList();
 183        if (orphanArticles.Any())
 184        {
 185            _logger.LogWarning("{Count} articles have no world assigned", orphanArticles.Count);
 186
 187            var orphanNode = CreateVirtualGroupNode(
 188                VirtualGroupType.Uncategorized,
 189                "Unassigned Articles",
 190                null);
 191
 192            foreach (var article in orphanArticles)
 193            {
 194                var articleNode = CreateArticleNode(article);
 195                orphanNode.Children.Add(articleNode);
 196                nodeIndex.AddNode(articleNode);
 197            }
 198
 199            if (orphanNode.Children.Any())
 200            {
 201                orphanNode.ChildCount = orphanNode.Children.Count;
 202                nodeIndex.AddRootNode(orphanNode);
 203            }
 204        }
 205
 206        stopwatch.Stop();
 207        _logger.LogDebug("Tree build complete in {Ms}ms total", stopwatch.ElapsedMilliseconds);
 208
 209        return new BuildResult
 210        {
 211            NodeIndex = nodeIndex,
 212            CachedArticles = allArticles
 213        };
 214    }
 215
 216    private TreeNode BuildWorldNode(
 217        WorldDto world,
 218        WorldDetailDto worldDetail,
 219        List<ArticleTreeDto> allArticles,
 220        Dictionary<Guid, ArticleTreeDto> articleIndex,
 221        Dictionary<Guid, List<ArcDto>> arcsByCampaign,
 222        Dictionary<Guid, List<SessionTreeDto>> sessionsByArc,
 223        List<WorldLinkDto> worldLinks,
 224        List<WorldDocumentDto> worldDocuments,
 225        List<MapSummaryDto> worldMaps,
 226        TreeNodeIndex nodeIndex)
 227    {
 26228        var worldNode = new TreeNode
 26229        {
 26230            Id = world.Id,
 26231            NodeType = TreeNodeType.World,
 26232            Title = world.Name,
 26233            Slug = world.Slug,
 26234            WorldId = world.Id,
 26235            IconEmoji = "fa-solid fa-globe"
 26236        };
 237
 26238        var campaigns = worldDetail.Campaigns ?? new List<CampaignDto>();
 26239        var worldArticles = allArticles.Where(a => a.WorldId == world.Id).ToList();
 240
 241        // Create virtual groups
 26242        var campaignsGroup = CreateVirtualGroupNode(VirtualGroupType.Campaigns, "Campaigns", world.Id);
 26243        var charactersGroup = CreateVirtualGroupNode(VirtualGroupType.PlayerCharacters, "Player Characters", world.Id);
 26244        var wikiGroup = CreateVirtualGroupNode(VirtualGroupType.Wiki, "Wiki", world.Id);
 26245        var uncategorizedGroup = CreateVirtualGroupNode(VirtualGroupType.Uncategorized, "Uncategorized", world.Id);
 246
 247        // Build Campaigns group
 100248        foreach (var campaign in campaigns.OrderBy(c => c.Name))
 249        {
 24250            var arcs = arcsByCampaign[campaign.Id];
 251
 24252            var campaignNode = BuildCampaignNode(campaign, arcs, worldArticles, articleIndex, sessionsByArc, nodeIndex);
 24253            campaignsGroup.Children.Add(campaignNode);
 24254            nodeIndex.AddNode(campaignNode);
 255        }
 26256        campaignsGroup.ChildCount = campaignsGroup.Children.Count;
 257
 258        // Build Characters group
 26259        var characterArticles = worldArticles
 26260            .Where(a => a.Type == ArticleType.Character && a.ParentId == null).OrderBy(a => a.Title).ToList();
 261
 56262        foreach (var article in characterArticles)
 263        {
 2264            var articleNode = BuildArticleNodeWithChildren(article, worldArticles, articleIndex, nodeIndex);
 2265            charactersGroup.Children.Add(articleNode);
 266        }
 26267        charactersGroup.ChildCount = charactersGroup.Children.Count;
 268
 269        // Build Wiki group
 26270        var wikiArticles = worldArticles
 26271            .Where(a => a.Type == ArticleType.WikiArticle && a.ParentId == null)
 26272            .OrderBy(a => a.Title)
 26273            .ToList();
 274
 100275        foreach (var article in wikiArticles)
 276        {
 24277            var articleNode = BuildArticleNodeWithChildren(article, worldArticles, articleIndex, nodeIndex);
 24278            wikiGroup.Children.Add(articleNode);
 279        }
 26280        wikiGroup.ChildCount = wikiGroup.Children.Count;
 281
 282        // Build Links group
 26283        var linksGroup = CreateVirtualGroupNode(VirtualGroupType.Links, "External Resources", world.Id);
 54284        foreach (var link in worldLinks.OrderBy(l => l.Title))
 285        {
 1286            var linkNode = new TreeNode
 1287            {
 1288                Id = link.Id,
 1289                NodeType = TreeNodeType.ExternalLink,
 1290                Title = link.Title,
 1291                Url = link.Url,
 1292                WorldId = world.Id,
 1293                IconEmoji = "fa-solid fa-external-link-alt"
 1294            };
 1295            linksGroup.Children.Add(linkNode);
 1296            nodeIndex.AddNode(linkNode);
 297        }
 298
 54299        foreach (var document in worldDocuments.Where(d => d.ArticleId == null).OrderBy(d => d.Title))
 300        {
 1301            var documentNode = new TreeNode
 1302            {
 1303                Id = document.Id,
 1304                NodeType = TreeNodeType.ExternalLink,
 1305                Title = document.Title,
 1306                Url = null,
 1307                WorldId = world.Id,
 1308                IconEmoji = GetDocumentIcon(document.ContentType),
 1309                AdditionalData = new Dictionary<string, object>
 1310                {
 1311                    { "IsDocument", true },
 1312                    { "ContentType", document.ContentType },
 1313                    { "FileSizeBytes", document.FileSizeBytes },
 1314                    { "FileName", document.FileName }
 1315                }
 1316            };
 1317            linksGroup.Children.Add(documentNode);
 1318            nodeIndex.AddNode(documentNode);
 319        }
 26320        linksGroup.ChildCount = linksGroup.Children.Count;
 321
 322        // Build Maps group
 26323        var mapsGroup = CreateVirtualGroupNode(VirtualGroupType.Maps, "Maps", world.Id);
 26324        mapsGroup.WorldSlug = world.Slug;
 56325        foreach (var map in worldMaps.OrderBy(m => m.Name))
 326        {
 2327            var mapNode = new TreeNode
 2328            {
 2329                Id = map.WorldMapId,
 2330                NodeType = TreeNodeType.Map,
 2331                Title = map.Name,
 2332                Slug = map.Slug,
 2333                WorldId = world.Id,
 2334                WorldSlug = world.Slug
 2335            };
 2336            mapsGroup.Children.Add(mapNode);
 2337            nodeIndex.AddNode(mapNode);
 338        }
 26339        mapsGroup.ChildCount = mapsGroup.Children.Count;
 340
 341        // Build Uncategorized group
 26342        var uncategorizedArticles = worldArticles
 26343            .Where(a => a.ParentId == null &&
 26344                       a.Type != ArticleType.WikiArticle &&
 26345                       a.Type != ArticleType.Character &&
 26346                       a.Type != ArticleType.Session &&
 26347                       a.Type != ArticleType.SessionNote &&
 26348                       a.Type != ArticleType.CharacterNote).OrderBy(a => a.Title).ToList();
 349
 56350        foreach (var article in uncategorizedArticles)
 351        {
 2352            var articleNode = BuildArticleNodeWithChildren(article, worldArticles, articleIndex, nodeIndex);
 2353            uncategorizedGroup.Children.Add(articleNode);
 354        }
 26355        uncategorizedGroup.ChildCount = uncategorizedGroup.Children.Count;
 356
 357        // Add groups to world node
 26358        worldNode.Children.Add(campaignsGroup);
 26359        nodeIndex.AddNode(campaignsGroup);
 360
 26361        worldNode.Children.Add(charactersGroup);
 26362        nodeIndex.AddNode(charactersGroup);
 363
 26364        worldNode.Children.Add(wikiGroup);
 26365        nodeIndex.AddNode(wikiGroup);
 366
 26367        worldNode.Children.Add(mapsGroup);
 26368        nodeIndex.AddNode(mapsGroup);
 369
 26370        if (linksGroup.Children.Any())
 371        {
 1372            worldNode.Children.Add(linksGroup);
 1373            nodeIndex.AddNode(linksGroup);
 374        }
 375
 26376        if (uncategorizedGroup.Children.Any())
 377        {
 2378            worldNode.Children.Add(uncategorizedGroup);
 2379            nodeIndex.AddNode(uncategorizedGroup);
 380        }
 381
 26382        worldNode.ChildCount = worldNode.Children.Count;
 383
 26384        return worldNode;
 385    }
 386
 387    private TreeNode BuildCampaignNode(
 388        CampaignDto campaign,
 389        List<ArcDto> arcs,
 390        List<ArticleTreeDto> worldArticles,
 391        Dictionary<Guid, ArticleTreeDto> articleIndex,
 392        Dictionary<Guid, List<SessionTreeDto>> sessionsByArc,
 393        TreeNodeIndex nodeIndex)
 394    {
 24395        var campaignNode = new TreeNode
 24396        {
 24397            Id = campaign.Id,
 24398            NodeType = TreeNodeType.Campaign,
 24399            Title = campaign.Name,
 24400            Slug = campaign.Slug,
 24401            WorldId = campaign.WorldId,
 24402            WorldSlug = campaign.WorldSlug,
 24403            CampaignId = campaign.Id,
 24404            IconEmoji = "fa-solid fa-dungeon"
 24405        };
 406
 96407        foreach (var arc in arcs.OrderBy(a => a.SortOrder).ThenBy(a => a.Name))
 408        {
 24409            var arcNode = BuildArcNode(arc, worldArticles, articleIndex, sessionsByArc, nodeIndex);
 24410            campaignNode.Children.Add(arcNode);
 24411            nodeIndex.AddNode(arcNode);
 412        }
 413
 24414        campaignNode.ChildCount = campaignNode.Children.Count;
 415
 24416        return campaignNode;
 417    }
 418
 419    private TreeNode BuildArcNode(
 420        ArcDto arc,
 421        List<ArticleTreeDto> worldArticles,
 422        Dictionary<Guid, ArticleTreeDto> articleIndex,
 423        Dictionary<Guid, List<SessionTreeDto>> sessionsByArc,
 424        TreeNodeIndex nodeIndex)
 425    {
 26426        var arcNode = new TreeNode
 26427        {
 26428            Id = arc.Id,
 26429            NodeType = TreeNodeType.Arc,
 26430            Title = arc.Name,
 26431            Slug = arc.Slug,
 26432            WorldSlug = arc.WorldSlug,
 26433            CampaignSlug = arc.CampaignSlug,
 26434            CampaignId = arc.CampaignId,
 26435            ArcId = arc.Id,
 26436            IconEmoji = "fa-solid fa-book-open"
 26437        };
 438
 26439        var sessions = sessionsByArc.TryGetValue(arc.Id, out var sessionList)
 26440            ? sessionList
 26441            : new List<SessionTreeDto>();
 442
 26443        if (sessions.Count > 0)
 444        {
 20445            foreach (var session in sessions
 4446                .OrderBy(s => s.Name, StringComparer.OrdinalIgnoreCase)
 4447                .ThenBy(s => s.Id))
 448            {
 6449                var sessionNode = BuildSessionNode(session, arc, worldArticles, articleIndex, nodeIndex);
 6450                arcNode.Children.Add(sessionNode);
 451            }
 452        }
 453
 26454        arcNode.ChildCount = arcNode.Children.Count;
 455
 26456        return arcNode;
 457    }
 458
 459    private TreeNode BuildSessionNode(
 460        SessionTreeDto session,
 461        ArcDto arc,
 462        List<ArticleTreeDto> worldArticles,
 463        Dictionary<Guid, ArticleTreeDto> articleIndex,
 464        TreeNodeIndex nodeIndex)
 465    {
 6466        var sessionNode = new TreeNode
 6467        {
 6468            Id = session.Id,
 6469            NodeType = TreeNodeType.Session,
 6470            Title = session.Name,
 6471            Slug = session.Slug,
 6472            WorldSlug = session.WorldSlug,
 6473            CampaignSlug = session.CampaignSlug,
 6474            ArcSlug = session.ArcSlug,
 6475            CampaignId = arc.CampaignId,
 6476            ArcId = arc.Id,
 6477            IconEmoji = "fa-solid fa-calendar-day",
 6478            HasAISummary = session.HasAiSummary
 6479        };
 480
 6481        nodeIndex.AddNode(sessionNode);
 482
 6483        var sessionNotes = worldArticles
 6484            .Where(a => a.Type == ArticleType.SessionNote && a.SessionId == session.Id)
 6485            .OrderBy(a => a.Title)
 6486            .ToList();
 487
 6488        if (sessionNotes.Count == 0)
 489        {
 4490            sessionNode.ChildCount = 0;
 4491            return sessionNode;
 492        }
 493
 2494        var sessionNoteIds = sessionNotes.Select(n => n.Id).ToHashSet();
 2495        var rootSessionNotes = sessionNotes
 2496            .Where(n => !n.ParentId.HasValue || !sessionNoteIds.Contains(n.ParentId.Value))
 2497            .ToList();
 498
 8499        foreach (var note in rootSessionNotes)
 500        {
 2501            var noteNode = BuildArticleNodeWithChildren(note, sessionNotes, articleIndex, nodeIndex);
 2502            noteNode.ParentId = sessionNode.Id;
 2503            sessionNode.Children.Add(noteNode);
 504        }
 505
 2506        sessionNode.ChildCount = sessionNode.Children.Count;
 2507        return sessionNode;
 508    }
 509
 510    private TreeNode BuildArticleNodeWithChildren(
 511        ArticleTreeDto article,
 512        List<ArticleTreeDto> allArticles,
 513        Dictionary<Guid, ArticleTreeDto> articleIndex,
 514        TreeNodeIndex nodeIndex)
 515    {
 32516        var node = CreateArticleNode(article);
 32517        nodeIndex.AddNode(node);
 518
 32519        var children = allArticles
 32520            .Where(a => a.ParentId == article.Id)
 32521            .OrderBy(a => a.Title)
 32522            .ToList();
 523
 68524        foreach (var child in children)
 525        {
 2526            var childNode = BuildArticleNodeWithChildren(child, allArticles, articleIndex, nodeIndex);
 2527            childNode.ParentId = node.Id;
 2528            node.Children.Add(childNode);
 529        }
 530
 32531        node.ChildCount = node.Children.Count;
 532
 32533        return node;
 534    }
 535
 536    /// <summary>
 537    /// Creates a TreeNode from an ArticleTreeDto.
 538    /// </summary>
 539    public static TreeNode CreateArticleNode(ArticleTreeDto article)
 540    {
 34541        return new TreeNode
 34542        {
 34543            Id = article.Id,
 34544            NodeType = TreeNodeType.Article,
 34545            ArticleType = article.Type,
 34546            Title = article.Title,
 34547            Slug = article.Slug,
 34548            IconEmoji = article.IconEmoji,
 34549            ParentId = article.ParentId,
 34550            WorldId = article.WorldId,
 34551            CampaignId = article.CampaignId,
 34552            ArcId = article.ArcId,
 34553            ChildCount = article.ChildCount,
 34554            Visibility = article.Visibility,
 34555            HasAISummary = article.HasAISummary
 34556        };
 557    }
 558
 559    /// <summary>
 560    /// Creates a virtual group node.
 561    /// </summary>
 562    public static TreeNode CreateVirtualGroupNode(VirtualGroupType groupType, string title, Guid? worldId)
 563    {
 158564        return new TreeNode
 158565        {
 158566            Id = Guid.NewGuid(),
 158567            NodeType = TreeNodeType.VirtualGroup,
 158568            VirtualGroupType = groupType,
 158569            Title = title,
 158570            WorldId = worldId
 158571        };
 572    }
 573
 574    /// <summary>
 575    /// Gets the appropriate icon for a document based on its content type.
 576    /// </summary>
 577    public static string GetDocumentIcon(string contentType)
 578    {
 9579        return contentType.ToLowerInvariant() switch
 9580        {
 2581            "application/pdf" => "fa-solid fa-file-pdf",
 1582            "application/vnd.openxmlformats-officedocument.wordprocessingml.document" => "fa-solid fa-file-word",
 1583            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" => "fa-solid fa-file-excel",
 1584            "application/vnd.openxmlformats-officedocument.presentationml.presentation" => "fa-solid fa-file-powerpoint"
 1585            "text/plain" => "fa-solid fa-file-lines",
 1586            "text/markdown" => "fa-solid fa-file-lines",
 3587            string ct when ct.StartsWith("image/") => "fa-solid fa-file-image",
 1588            _ => "fa-solid fa-file"
 9589        };
 590    }
 591}

Methods/Properties

.ctor(Chronicis.Client.Services.IArticleApiService,Chronicis.Client.Services.IWorldApiService,Chronicis.Client.Services.ICampaignApiService,Chronicis.Client.Services.IArcApiService,Chronicis.Client.Services.ISessionApiService,Chronicis.Client.Services.IMapApiService,Microsoft.Extensions.Logging.ILogger)
BuildWorldNode(Chronicis.Shared.DTOs.WorldDto,Chronicis.Shared.DTOs.WorldDetailDto,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.ArticleTreeDto>,System.Collections.Generic.Dictionary`2<System.Guid,Chronicis.Shared.DTOs.ArticleTreeDto>,System.Collections.Generic.Dictionary`2<System.Guid,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.ArcDto>>,System.Collections.Generic.Dictionary`2<System.Guid,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.Sessions.SessionTreeDto>>,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.WorldLinkDto>,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.WorldDocumentDto>,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.Maps.MapSummaryDto>,Chronicis.Client.Services.Tree.TreeNodeIndex)
BuildCampaignNode(Chronicis.Shared.DTOs.CampaignDto,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.ArcDto>,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.ArticleTreeDto>,System.Collections.Generic.Dictionary`2<System.Guid,Chronicis.Shared.DTOs.ArticleTreeDto>,System.Collections.Generic.Dictionary`2<System.Guid,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.Sessions.SessionTreeDto>>,Chronicis.Client.Services.Tree.TreeNodeIndex)
BuildArcNode(Chronicis.Shared.DTOs.ArcDto,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.ArticleTreeDto>,System.Collections.Generic.Dictionary`2<System.Guid,Chronicis.Shared.DTOs.ArticleTreeDto>,System.Collections.Generic.Dictionary`2<System.Guid,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.Sessions.SessionTreeDto>>,Chronicis.Client.Services.Tree.TreeNodeIndex)
BuildSessionNode(Chronicis.Shared.DTOs.Sessions.SessionTreeDto,Chronicis.Shared.DTOs.ArcDto,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.ArticleTreeDto>,System.Collections.Generic.Dictionary`2<System.Guid,Chronicis.Shared.DTOs.ArticleTreeDto>,Chronicis.Client.Services.Tree.TreeNodeIndex)
BuildArticleNodeWithChildren(Chronicis.Shared.DTOs.ArticleTreeDto,System.Collections.Generic.List`1<Chronicis.Shared.DTOs.ArticleTreeDto>,System.Collections.Generic.Dictionary`2<System.Guid,Chronicis.Shared.DTOs.ArticleTreeDto>,Chronicis.Client.Services.Tree.TreeNodeIndex)
CreateArticleNode(Chronicis.Shared.DTOs.ArticleTreeDto)
CreateVirtualGroupNode(Chronicis.Client.Models.VirtualGroupType,System.String,System.Nullable`1<System.Guid>)
GetDocumentIcon(System.String)