< Summary

Information
Class: Chronicis.Client.Services.AppContextService
Assembly: Chronicis.Client
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Client/Services/AppContextService.cs
Line coverage
100%
Covered lines: 4
Uncovered lines: 0
Coverable lines: 4
Total lines: 171
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Services/AppContextService.cs

#LineLine coverage
 1using Blazored.LocalStorage;
 2using Chronicis.Shared.DTOs;
 3
 4namespace Chronicis.Client.Services;
 5
 6/// <summary>
 7/// Service for managing application context (current World/Campaign)
 8/// </summary>
 9public class AppContextService : IAppContextService
 10{
 11    private readonly IWorldApiService _worldApi;
 12    private readonly ILocalStorageService _localStorage;
 13    private readonly ILogger<AppContextService> _logger;
 14
 15    private const string WorldIdKey = "chronicis_current_world_id";
 16    private const string CampaignIdKey = "chronicis_current_campaign_id";
 17
 18    public Guid? CurrentWorldId { get; private set; }
 19    public Guid? CurrentCampaignId { get; private set; }
 20    public WorldDetailDto? CurrentWorld { get; private set; }
 21    public CampaignDto? CurrentCampaign { get; private set; }
 22    public List<WorldDto> Worlds { get; private set; } = new();
 23    public bool IsInitialized { get; private set; }
 24
 25    public event Action? OnContextChanged;
 26
 27    public AppContextService(
 28        IWorldApiService worldApi,
 29        ILocalStorageService localStorage,
 30        ILogger<AppContextService> logger)
 31    {
 2132        _worldApi = worldApi;
 2133        _localStorage = localStorage;
 2134        _logger = logger;
 2135    }
 36
 37    public async Task InitializeAsync()
 38    {
 39        if (IsInitialized)
 40            return;
 41
 42        _logger.LogDebug("Initializing app context");
 43
 44        // Load all worlds
 45        Worlds = await _worldApi.GetWorldsAsync();
 46
 47        if (Worlds.Count == 0)
 48        {
 49            _logger.LogWarning("No worlds found for user");
 50            IsInitialized = true;
 51            return;
 52        }
 53
 54        // Try to restore saved selection
 55        Guid? savedWorldId = null;
 56        Guid? savedCampaignId = null;
 57
 58        try
 59        {
 60            var savedWorldIdStr = await _localStorage.GetItemAsStringAsync(WorldIdKey);
 61            var savedCampaignIdStr = await _localStorage.GetItemAsStringAsync(CampaignIdKey);
 62
 63            if (!string.IsNullOrEmpty(savedWorldIdStr) && Guid.TryParse(savedWorldIdStr.Trim('"'), out var parsedWorldId
 64                savedWorldId = parsedWorldId;
 65
 66            if (!string.IsNullOrEmpty(savedCampaignIdStr) && Guid.TryParse(savedCampaignIdStr.Trim('"'), out var parsedC
 67                savedCampaignId = parsedCampaignId;
 68        }
 69        catch (Exception ex)
 70        {
 71            _logger.LogWarning(ex, "Failed to read saved context from localStorage");
 72        }
 73
 74        // Validate saved world exists
 75        if (savedWorldId.HasValue && Worlds.Any(w => w.Id == savedWorldId.Value))
 76        {
 77            await SelectWorldAsync(savedWorldId.Value, savedCampaignId);
 78        }
 79        else
 80        {
 81            // Default to first world
 82            await SelectWorldAsync(Worlds[0].Id);
 83        }
 84
 85        IsInitialized = true;
 86        _logger.LogDebug("App context initialized. World: {WorldId}, Campaign: {CampaignId}",
 87            CurrentWorldId, CurrentCampaignId);
 88    }
 89
 90    public async Task SelectWorldAsync(Guid worldId, Guid? campaignId = null)
 91    {
 92        _logger.LogDebug("Selecting world {WorldId}", worldId);
 93
 94        // Load world details
 95        var world = await _worldApi.GetWorldAsync(worldId);
 96        if (world == null)
 97        {
 98            _logger.LogWarning("Failed to load world {WorldId}", worldId);
 99            return;
 100        }
 101
 102        CurrentWorldId = worldId;
 103        CurrentWorld = world;
 104
 105        // Save to localStorage
 106        await _localStorage.SetItemAsStringAsync(WorldIdKey, worldId.ToString());
 107
 108        // Select campaign
 109        if (campaignId.HasValue && world.Campaigns.Any(c => c.Id == campaignId.Value))
 110        {
 111            await SelectCampaignAsync(campaignId.Value);
 112        }
 113        else if (world.Campaigns.Count > 0)
 114        {
 115            // Default to first campaign if available
 116            await SelectCampaignAsync(world.Campaigns[0].Id);
 117        }
 118        else
 119        {
 120            await SelectCampaignAsync(null);
 121        }
 122
 123        OnContextChanged?.Invoke();
 124    }
 125
 126    public async Task SelectCampaignAsync(Guid? campaignId)
 127    {
 128        _logger.LogDebug("Selecting campaign {CampaignId}", campaignId);
 129
 130        CurrentCampaignId = campaignId;
 131
 132        if (campaignId.HasValue && CurrentWorld != null)
 133        {
 134            CurrentCampaign = CurrentWorld.Campaigns.FirstOrDefault(c => c.Id == campaignId.Value);
 135            await _localStorage.SetItemAsStringAsync(CampaignIdKey, campaignId.Value.ToString());
 136        }
 137        else
 138        {
 139            CurrentCampaign = null;
 140            await _localStorage.RemoveItemAsync(CampaignIdKey);
 141        }
 142
 143        OnContextChanged?.Invoke();
 144    }
 145
 146    public async Task RefreshCurrentWorldAsync()
 147    {
 148        if (!CurrentWorldId.HasValue)
 149            return;
 150
 151        var world = await _worldApi.GetWorldAsync(CurrentWorldId.Value);
 152        if (world != null)
 153        {
 154            CurrentWorld = world;
 155
 156            // Update campaign reference if still valid
 157            if (CurrentCampaignId.HasValue)
 158            {
 159                CurrentCampaign = world.Campaigns.FirstOrDefault(c => c.Id == CurrentCampaignId.Value);
 160            }
 161
 162            OnContextChanged?.Invoke();
 163        }
 164    }
 165
 166    public async Task RefreshWorldsAsync()
 167    {
 168        Worlds = await _worldApi.GetWorldsAsync();
 169        OnContextChanged?.Invoke();
 170    }
 171}