diff --git a/Moonlight.Client/Implementations/AuthenticationUiHandler.cs b/Moonlight.Client/Implementations/AuthenticationUiHandler.cs new file mode 100644 index 00000000..fef8e2ad --- /dev/null +++ b/Moonlight.Client/Implementations/AuthenticationUiHandler.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Components; +using MoonCore.Blazor.Helpers; +using Moonlight.Client.Interfaces; +using Moonlight.Client.Services; +using Moonlight.Client.UI.Screens; + +namespace Moonlight.Client.Implementations; + +public class AuthenticationUiHandler : IAppLoader, IAppScreen +{ + public int Priority => 0; + + public Task ShouldRender(IServiceProvider serviceProvider) + { + var identityService = serviceProvider.GetRequiredService(); + + return Task.FromResult(identityService.IsLoggedIn); + } + + public RenderFragment Render() => ComponentHelper.FromType(); + + public async Task Load(IServiceProvider serviceProvider) + { + var identityService = serviceProvider.GetRequiredService(); + await identityService.Check(); + } +} \ No newline at end of file diff --git a/Moonlight.Client/Interfaces/IAppLoader.cs b/Moonlight.Client/Interfaces/IAppLoader.cs new file mode 100644 index 00000000..f1e2b5e0 --- /dev/null +++ b/Moonlight.Client/Interfaces/IAppLoader.cs @@ -0,0 +1,7 @@ +namespace Moonlight.Client.Interfaces; + +public interface IAppLoader +{ + public int Priority { get; } + public Task Load(IServiceProvider serviceProvider); +} \ No newline at end of file diff --git a/Moonlight.Client/Interfaces/IAppScreen.cs b/Moonlight.Client/Interfaces/IAppScreen.cs new file mode 100644 index 00000000..2301a9f7 --- /dev/null +++ b/Moonlight.Client/Interfaces/IAppScreen.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Components; + +namespace Moonlight.Client.Interfaces; + +public interface IAppScreen +{ + public int Priority { get; } + public Task ShouldRender(IServiceProvider serviceProvider); + public RenderFragment Render(); +} \ No newline at end of file diff --git a/Moonlight.Client/Moonlight.Client.csproj b/Moonlight.Client/Moonlight.Client.csproj index 6c6bb562..bc3f6ec7 100644 --- a/Moonlight.Client/Moonlight.Client.csproj +++ b/Moonlight.Client/Moonlight.Client.csproj @@ -25,7 +25,6 @@ - diff --git a/Moonlight.Client/Program.cs b/Moonlight.Client/Program.cs index f4bce8c9..b947e06c 100644 --- a/Moonlight.Client/Program.cs +++ b/Moonlight.Client/Program.cs @@ -55,6 +55,10 @@ var implementationService = new ImplementationService(); implementationService.Register(); +var authUiHandler = new AuthenticationUiHandler(); +implementationService.Register(authUiHandler); +implementationService.Register(authUiHandler); + builder.Services.AddSingleton(implementationService); await builder.Build().RunAsync(); \ No newline at end of file diff --git a/Moonlight.Client/Services/IdentityService.cs b/Moonlight.Client/Services/IdentityService.cs new file mode 100644 index 00000000..46fdb9ff --- /dev/null +++ b/Moonlight.Client/Services/IdentityService.cs @@ -0,0 +1,57 @@ +using MoonCore.Blazor.Tailwind.Services; +using MoonCore.Exceptions; +using MoonCore.Helpers; +using Moonlight.Shared.Http.Responses.Auth; + +namespace Moonlight.Client.Services; + +public class IdentityService +{ + public string Username { get; private set; } + public string Email { get; private set; } + public string[] Permissions { get; private set; } + public bool IsLoggedIn { get; private set; } + + public event Func OnStateChanged; + + private readonly CookieService CookieService; + private readonly HttpApiClient ApiClient; + + public IdentityService(CookieService cookieService, HttpApiClient apiClient) + { + CookieService = cookieService; + ApiClient = apiClient; + } + + public async Task Check() + { + try + { + var response = await ApiClient.GetJson("api/auth/check"); + + Username = response.Username; + Email = response.Email; + Permissions = response.Permissions; + + IsLoggedIn = true; + } + catch (HttpApiException) + { + IsLoggedIn = false; + } + + await OnStateChanged(); + } + + public async Task Login(string token) + { + await CookieService.SetValue("token", token, 30); + await Check(); + } + + public async Task Logout() + { + await CookieService.SetValue("token", "", 30); + await Check(); + } +} \ No newline at end of file diff --git a/Moonlight.Client/UI/Layouts/MainLayout.razor b/Moonlight.Client/UI/Layouts/MainLayout.razor index 5805a534..9a37c48f 100644 --- a/Moonlight.Client/UI/Layouts/MainLayout.razor +++ b/Moonlight.Client/UI/Layouts/MainLayout.razor @@ -1,30 +1,53 @@ @using MoonCore.Helpers +@using MoonCore.PluginFramework.Services +@using Moonlight.Client.Interfaces @using Moonlight.Client.UI.Partials @inherits LayoutComponentBase -
- - -
- +@inject ImplementationService ImplementationService +@inject IServiceProvider ServiceProvider +@inject ILogger Logger - -
-
- @Body -
-
-
+@if (IsLoading) +{ + - - + +} +else if (CurrentScreen != null) +{ + @CurrentScreen +} +else +{ +
+ + +
+ + + +
+
+ + @Body + +
+
+
+
-
+} + + + + @code { - public event Func OnStateChanged; + // Mobile navigation + public event Func OnStateChanged; public bool ShowMobileNavigation { get; private set; } = false; public async Task ToggleMobileNavigation() @@ -32,4 +55,64 @@ ShowMobileNavigation = !ShowMobileNavigation; await OnStateChanged(); } -} + + // App loaders & screens + private bool IsLoading = true; + private RenderFragment? CurrentScreen; + + private async Task Load(LazyLoader lazyLoader) + { + await lazyLoader.UpdateText("Retrieving data"); + await RunLoaders(); + + await RenderScreens(); + + IsLoading = false; + await InvokeAsync(StateHasChanged); + } + + public async Task Reload() + { + IsLoading = true; + await InvokeAsync(StateHasChanged); + } + + private async Task RunLoaders() + { + var appLoaders = ImplementationService + .Get() + .OrderBy(x => x.Priority); + + foreach (var loader in appLoaders) //TODO: Measure performance of every loader? + { + try + { + await loader.Load(ServiceProvider); + } + catch (Exception e) + { + Logger.LogCritical("An app loader threw an unhandled exception: {e}", e); + } + } + } + + public async Task RenderScreens() + { + CurrentScreen = null; + + var appScreens = ImplementationService + .Get() + .OrderBy(x => x.Priority); + + foreach (var screen in appScreens) + { + if (!await screen.ShouldRender(ServiceProvider)) + continue; + + CurrentScreen = screen.Render(); + await InvokeAsync(StateHasChanged); + + return; + } + } +} \ No newline at end of file diff --git a/Moonlight.Client/UI/Screens/AuthenticationScreen.razor b/Moonlight.Client/UI/Screens/AuthenticationScreen.razor new file mode 100644 index 00000000..3b1cbbf6 --- /dev/null +++ b/Moonlight.Client/UI/Screens/AuthenticationScreen.razor @@ -0,0 +1,18 @@ +@page "/auth/register" +@page "/auth/login" + +@inject NavigationManager Navigation + +@{ + var url = new Uri(Navigation.Uri); + var isRegister = url.LocalPath.StartsWith("/auth/register"); +} + +@if (isRegister) +{ + +} +else +{ + +} \ No newline at end of file