Implemented frontend configuration service and dynamic theme reloading integration. Updated sidebar to display dynamic application name.
All checks were successful
Dev Publish: Nuget / Publish Dev Packages (push) Successful in 48s

This commit was merged in pull request #6.
This commit is contained in:
2026-01-19 10:55:34 +01:00
parent d85b07bde7
commit 4187f9da4e
7 changed files with 70 additions and 9 deletions

View File

@@ -12,8 +12,9 @@
<script type="importmap"></script> <script type="importmap"></script>
<script> <script>
window.themeLoader = { window.frontendConfig = {
STYLE_TAG_ID: 'theme-variables', STYLE_TAG_ID: 'theme-variables',
configuration: {},
applyTheme: function(cssContent) { applyTheme: function(cssContent) {
// Find or create the style tag // Find or create the style tag
@@ -29,25 +30,33 @@
styleTag.textContent = cssContent; styleTag.textContent = cssContent;
}, },
loadInitialThemeSync: function() { reloadConfiguration: function (){
try { try {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/frontend/config', false); xhr.open('GET', '/api/frontend/config', false);
xhr.send(null); xhr.send(null);
if (xhr.status === 200) { if (xhr.status === 200) {
const config = JSON.parse(xhr.responseText); this.configuration = JSON.parse(xhr.responseText);
this.applyTheme(config.themeCss);
document.title = config.name;
} }
} catch (error) { } catch (error) {
console.error('Failed to load initial theme:', error); console.error('Failed to load initial theme:', error);
} }
},
getConfiguration: function (){
return this.configuration;
},
reload: function () {
this.reloadConfiguration();
document.title = this.configuration.name;
this.applyTheme(this.configuration.themeCss);
} }
}; };
window.themeLoader.loadInitialThemeSync(); window.frontendConfig.reload();
</script> </script>
</head> </head>

View File

@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;
namespace Moonlight.Frontend.Models;
public class FrontendConfiguration
{
[JsonPropertyName("name")]
public string Name { get; set; }
}

View File

@@ -0,0 +1,24 @@
using Microsoft.JSInterop;
using Moonlight.Frontend.Models;
namespace Moonlight.Frontend.Services;
public class FrontendService
{
private readonly IJSRuntime JsRuntime;
public FrontendService(IJSRuntime jsRuntime)
{
JsRuntime = jsRuntime;
}
public async Task<FrontendConfiguration> GetConfigurationAsync()
{
return await JsRuntime.InvokeAsync<FrontendConfiguration>("frontendConfig.getConfiguration");
}
public async Task ReloadAsync()
{
await JsRuntime.InvokeVoidAsync("frontendConfig.reload");
}
}

View File

@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moonlight.Frontend.Implementations; using Moonlight.Frontend.Implementations;
using Moonlight.Frontend.Interfaces; using Moonlight.Frontend.Interfaces;
using Moonlight.Frontend.Services;
using Moonlight.Frontend.UI; using Moonlight.Frontend.UI;
using ShadcnBlazor; using ShadcnBlazor;
using ShadcnBlazor.Extras; using ShadcnBlazor.Extras;
@@ -22,5 +23,7 @@ public partial class Startup
builder.Services.AddShadcnBlazorExtras(); builder.Services.AddShadcnBlazorExtras();
builder.Services.AddSingleton<ISidebarProvider, SidebarProvider>(); builder.Services.AddSingleton<ISidebarProvider, SidebarProvider>();
builder.Services.AddScoped<FrontendService>();
} }
} }

View File

@@ -3,6 +3,7 @@
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Moonlight.Shared @using Moonlight.Shared
@using LucideBlazor @using LucideBlazor
@using Moonlight.Frontend.Services
@using Moonlight.Shared.Http.Requests.Themes @using Moonlight.Shared.Http.Requests.Themes
@using ShadcnBlazor.Buttons @using ShadcnBlazor.Buttons
@using ShadcnBlazor.Labels @using ShadcnBlazor.Labels
@@ -18,6 +19,7 @@
@inject HttpClient HttpClient @inject HttpClient HttpClient
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject ToastService ToastService @inject ToastService ToastService
@inject FrontendService FrontendService
<div class="flex flex-row justify-between"> <div class="flex flex-row justify-between">
<div class="flex flex-col"> <div class="flex flex-col">
@@ -129,6 +131,8 @@
$"Successfully created theme {Request.Name}" $"Successfully created theme {Request.Name}"
); );
await FrontendService.ReloadAsync();
Navigation.NavigateTo("/admin/system?tab=themes"); Navigation.NavigateTo("/admin/system?tab=themes");
} }
} }

View File

@@ -4,6 +4,7 @@
@using Moonlight.Shared @using Moonlight.Shared
@using LucideBlazor @using LucideBlazor
@using Moonlight.Frontend.Mappers @using Moonlight.Frontend.Mappers
@using Moonlight.Frontend.Services
@using Moonlight.Shared.Http.Requests.Themes @using Moonlight.Shared.Http.Requests.Themes
@using Moonlight.Shared.Http.Responses.Themes @using Moonlight.Shared.Http.Responses.Themes
@using ShadcnBlazor.Buttons @using ShadcnBlazor.Buttons
@@ -21,6 +22,7 @@
@inject HttpClient HttpClient @inject HttpClient HttpClient
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject ToastService ToastService @inject ToastService ToastService
@inject FrontendService FrontendService
<div class="flex flex-row justify-between"> <div class="flex flex-row justify-between">
<div class="flex flex-col"> <div class="flex flex-col">
@@ -142,6 +144,8 @@
$"Successfully updated theme {Request.Name}" $"Successfully updated theme {Request.Name}"
); );
await FrontendService.ReloadAsync();
Navigation.NavigateTo("/admin/system?tab=themes"); Navigation.NavigateTo("/admin/system?tab=themes");
} }
} }

View File

@@ -1,11 +1,14 @@
@using Microsoft.AspNetCore.Authorization @using System.Text.Json
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.Authorization
@using Moonlight.Frontend.Interfaces @using Moonlight.Frontend.Interfaces
@using Moonlight.Frontend.Models @using Moonlight.Frontend.Models
@using Moonlight.Frontend.Services
@using ShadcnBlazor.Sidebars @using ShadcnBlazor.Sidebars
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject IAuthorizationService AuthorizationService @inject IAuthorizationService AuthorizationService
@inject FrontendService FrontendService
@inject IEnumerable<ISidebarProvider> Providers @inject IEnumerable<ISidebarProvider> Providers
@implements IDisposable @implements IDisposable
@@ -21,7 +24,7 @@
<SidebarMenuButton> <SidebarMenuButton>
<a href="/" class="flex flex-row items-center"> <a href="/" class="flex flex-row items-center">
<img alt="Logo" src="/_content/Moonlight.Frontend/logo.svg" class="size-6"/> <img alt="Logo" src="/_content/Moonlight.Frontend/logo.svg" class="size-6"/>
<span class="ms-2.5 text-lg font-semibold">Moonlight</span> <span class="ms-2.5 text-lg font-semibold">@FrontendConfiguration?.Name</span>
</a> </a>
</SidebarMenuButton> </SidebarMenuButton>
</SidebarMenuItem> </SidebarMenuItem>
@@ -74,6 +77,7 @@
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; } [CascadingParameter] public Task<AuthenticationState> AuthState { get; set; }
private readonly List<SidebarItem> Items = new(); private readonly List<SidebarItem> Items = new();
private FrontendConfiguration? FrontendConfiguration;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@@ -98,6 +102,10 @@
} }
Navigation.LocationChanged += OnLocationChanged; Navigation.LocationChanged += OnLocationChanged;
FrontendConfiguration = await FrontendService.GetConfigurationAsync();
Console.WriteLine(JsonSerializer.Serialize(FrontendConfiguration));
} }
private async void OnLocationChanged(object? sender, LocationChangedEventArgs e) private async void OnLocationChanged(object? sender, LocationChangedEventArgs e)