< Summary

Information
Class: Chronicis.Client.Pages.PublicWorldPage
Assembly: Chronicis.Client
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Client/Pages/PublicWorldPage.razor
Line coverage
100%
Covered lines: 12
Uncovered lines: 0
Coverable lines: 12
Total lines: 240
Line coverage: 100%
Branch coverage
100%
Covered branches: 18
Total branches: 18
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
BuildRenderTree(...)100%1818100%
OnInitialized()100%11100%
OnVmPropertyChanged(...)100%11100%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Pages/PublicWorldPage.razor

#LineLine coverage
 1@page "/w/{PublicSlug}"
 2@page "/w/{PublicSlug}/{*ArticlePath}"
 3@layout PublicLayout
 4@using Chronicis.Shared.DTOs
 5@using Chronicis.Shared.Enums
 6@using Chronicis.Client.Components.Maps
 7@using Chronicis.Client.Components.Shared
 8@using Microsoft.JSInterop
 9@inject PublicWorldPageViewModel VM
 10@inject IMarkdownService MarkdownService
 11@inject IJSRuntime JSRuntime
 12
 13@implements IAsyncDisposable
 14
 15<PageTitle>@VM.GetPageTitle()</PageTitle>
 16
 3717@if (VM.IsLoading)
 18{
 19    <div class="public-world-loading">
 20        <MudProgressCircular Color="Color.Primary" Indeterminate="true" />
 21        <MudText Typo="Typo.body1" Class="mt-3">Loading world...</MudText>
 22    </div>
 23}
 3024else if (VM.World == null)
 25{
 26    <div class="public-world-not-found">
 27        <MudPaper Elevation="2" Class="pa-6 text-center">
 28            <MudIcon Icon="@Icons.Material.Filled.SearchOff"
 29                     Style="font-size: 64px; color: var(--chronicis-beige-gold);" />
 30            <MudText Typo="Typo.h5" Class="mt-4 mb-2">World Not Found</MudText>
 31            <MudText Typo="Typo.body1" Class="mud-text-secondary mb-4">
 32                This world doesn't exist or isn't publicly shared.
 33            </MudText>
 34            <MudButton Variant="Variant.Filled"
 35                       Color="Color.Primary"
 36                       Href="/"
 37                       StartIcon="@Icons.Material.Filled.Home">
 38                Go Home
 39            </MudButton>
 40        </MudPaper>
 41    </div>
 42}
 43else
 44{
 45    <div class="public-world-container">
 46        <!-- Sidebar with article tree -->
 47        <aside class="public-world-sidebar">
 48            <div class="public-world-sidebar-header">
 49                <a href="/w/@PublicSlug" class="public-world-title-link">
 50                    <MudText Typo="Typo.subtitle1" Style="color: var(--chronicis-beige-gold); font-weight: 600;">
 51                        @VM.World.Name
 52                    </MudText>
 53                </a>
 54            </div>
 55
 56            <MudDivider Class="my-2" Style="opacity: 0.3;" />
 57
 2758            @if (VM.ArticleTree.Count == 0)
 59            {
 60                <div class="pa-3">
 61                    <MudText Typo="Typo.body2" Class="mud-text-secondary">
 62                        No public articles in this world yet.
 63                    </MudText>
 64                </div>
 65            }
 66            else
 67            {
 68                <div class="public-article-tree">
 469                    @foreach (var article in VM.ArticleTree)
 70                    {
 71                        <PublicArticleTreeItem Article="@article"
 72                                               PublicSlug="@PublicSlug"
 73                                               CurrentPath="@ArticlePath"
 74                                               OnArticleSelected="@(path => VM.NavigateToArticle(PublicSlug, path))" />
 75                    }
 76                </div>
 77            }
 78        </aside>
 79
 80        <!-- Main content area -->
 81        <main class="public-world-main">
 2782            @if (string.IsNullOrEmpty(ArticlePath))
 83            {
 84                <!-- World landing view -->
 85                <MudPaper Elevation="2" Class="pa-6">
 86                    <div class="public-world-hero">
 87                        <MudIcon Icon="@Icons.Material.Filled.Public"
 88                                 Style="font-size: 48px; color: var(--chronicis-beige-gold);" />
 89                        <MudText Typo="Typo.h4" Class="mt-3">@VM.World.Name</MudText>
 90                        @if (!string.IsNullOrEmpty(VM.World.Description))
 91                        {
 92                            <MudText Typo="Typo.body1" Class="mud-text-secondary mt-2">
 93                                @VM.World.Description
 94                            </MudText>
 95                        }
 96                        <MudText Typo="Typo.caption" Class="mud-text-secondary mt-3">
 97                            Created by @VM.World.OwnerName
 98                        </MudText>
 99                    </div>
 100
 101                    @if (VM.ArticleTree.Count > 0)
 102                    {
 103                        <MudDivider Class="my-4" />
 104                        <MudText Typo="Typo.body2" Class="mud-text-secondary">
 105                            Select an article from the sidebar to start reading.
 106                        </MudText>
 107                    }
 108                </MudPaper>
 109            }
 15110            else if (VM.CurrentArticle == null && !VM.IsLoadingArticle)
 111            {
 112                <!-- Article not found -->
 113                <MudPaper Elevation="2" Class="pa-6 text-center">
 114                    <MudIcon Icon="@Icons.Material.Filled.Article"
 115                             Style="font-size: 48px; color: var(--chronicis-beige-gold);" />
 116                    <MudText Typo="Typo.h6" Class="mt-3">Article Not Found</MudText>
 117                    <MudText Typo="Typo.body2" Class="mud-text-secondary mt-2">
 118                        This article doesn't exist or isn't publicly visible.
 119                    </MudText>
 120                    <MudButton Variant="Variant.Text"
 121                               Color="Color.Primary"
 122                               Class="mt-3"
 123                               OnClick="() => VM.NavigateToArticle(PublicSlug, string.Empty)">
 124                        Back to World Overview
 125                    </MudButton>
 126                </MudPaper>
 127            }
 11128            else if (VM.IsLoadingArticle)
 129            {
 130                <MudPaper Elevation="2" Class="pa-6">
 131                    <MudSkeleton Width="60%" Height="32px" />
 132                    <MudSkeleton Width="100%" Height="20px" Class="mt-4" />
 133                    <MudSkeleton Width="100%" Height="20px" Class="mt-2" />
 134                    <MudSkeleton Width="80%" Height="20px" Class="mt-2" />
 135                </MudPaper>
 136            }
 137            else
 138            {
 139                <!-- Article view -->
 140                <MudPaper Elevation="2" Class="pa-6">
 141                    <!-- Breadcrumbs -->
 142                    @if (VM.CurrentArticle!.Breadcrumbs?.Any() == true)
 143                    {
 144                        <ChroniclsBreadcrumbs Items="@VM.GetBreadcrumbItems(PublicSlug)" UseCustomLinks="true" Class="mb
 145                    }
 146
 147                    <!-- Article header -->
 148                    <div class="d-flex align-center gap-3 mb-4">
 149                        @if (!string.IsNullOrEmpty(VM.CurrentArticle.IconEmoji))
 150                        {
 151                            <IconDisplay Icon="@VM.CurrentArticle.IconEmoji" DefaultIcon="" Style="font-size: 2rem;" />
 152                        }
 153                        <div>
 154                            <MudText Typo="Typo.h4">@VM.CurrentArticle.Title</MudText>
 155                            <MudText Typo="Typo.caption" Class="mud-text-secondary">
 156                                @PublicWorldPageViewModel.GetArticleTypeLabel(VM.CurrentArticle.Type)
 157                                @if (VM.CurrentArticle.ModifiedAt.HasValue)
 158                                {
 159                                    <text> · Updated @VM.CurrentArticle.ModifiedAt.Value.ToString("MMM d, yyyy")</text>
 160                                }
 161                            </MudText>
 162                        </div>
 163                    </div>
 164
 165                    <!-- AI Summary (if available) -->
 166                    @if (!string.IsNullOrEmpty(VM.CurrentArticle.AISummary))
 167                    {
 168                        <MudAlert Severity="Severity.Info" Class="mb-4 summary-text" Dense="true">
 169                            <strong>AI Summary:</strong> @VM.CurrentArticle.AISummary
 170                        </MudAlert>
 171                    }
 172
 173                    <!-- Article body -->
 174                    <div id="public-article-body" class="public-article-body chronicis-editor-content">
 175                        @if (!string.IsNullOrEmpty(VM.CurrentArticle.Body))
 176                        {
 177                            @((MarkupString)VM.GetRenderedArticleBodyHtml(MarkdownService))
 178                        }
 179                        else
 180                        {
 181                            <MudText Typo="Typo.body2" Class="mud-text-secondary">
 182                                <em>No content available.</em>
 183                            </MudText>
 184                        }
 185                    </div>
 186                </MudPaper>
 187            }
 188        </main>
 189    </div>
 190
 27191    @if (VM.IsMapModalOpen)
 192    {
 193        <SessionMapViewerModal IsOpen="VM.IsMapModalOpen"
 194                               WorldId="@VM.World!.Id"
 195                               MapId="VM.SelectedMapId"
 196                               TargetFeatureId="VM.SelectedMapFeatureId"
 197                               MapName="@VM.SelectedMapName"
 198                               PublicSlug="@PublicSlug"
 199                               OnClose="VM.CloseMapModalAsync" />
 200    }
 201}
 202
 203@code {
 204    [Parameter]
 205    public string PublicSlug { get; set; } = string.Empty;
 206
 207    [Parameter]
 208    public string? ArticlePath { get; set; }
 209
 210    protected override void OnInitialized()
 211    {
 9212        VM.PropertyChanged += OnVmPropertyChanged;
 9213    }
 214
 215    protected override async Task OnParametersSetAsync()
 216    {
 217        await VM.LoadWorldAsync(PublicSlug, ArticlePath);
 218    }
 219
 220    protected override async Task OnAfterRenderAsync(bool firstRender)
 221    {
 222        if (VM.CurrentArticle != null
 223            && !string.IsNullOrEmpty(VM.CurrentArticle.Body)
 224            && !VM.WikiLinksInitialized)
 225        {
 226            await VM.InitializeWikiLinksAsync(JSRuntime);
 227        }
 228    }
 229
 230    private void OnVmPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
 231    {
 68232        InvokeAsync(StateHasChanged);
 68233    }
 234
 235    public async ValueTask DisposeAsync()
 236    {
 237        VM.PropertyChanged -= OnVmPropertyChanged;
 238        await VM.DisposeAsync();
 239    }
 240}