Changed CascadingDependencies to be more modular.
This commit is contained in:
parent
d57a61d5ca
commit
3957d65370
@ -1,5 +1,5 @@
|
||||
<CascadingAuthenticationState>
|
||||
<CascadingDependencies>
|
||||
<CascadingDependencies Dependencies="@dependencies">
|
||||
<Content>
|
||||
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
|
||||
<Found Context="routeData">
|
||||
|
45
src/MultiShop/Client/App.razor.cs
Normal file
45
src/MultiShop/Client/App.razor.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MultiShop.Client.Module;
|
||||
using MultiShop.Client.Shared;
|
||||
using MultiShop.Shared.Models;
|
||||
using MultiShop.Shop.Framework;
|
||||
|
||||
namespace MultiShop.Client
|
||||
{
|
||||
public partial class App
|
||||
{
|
||||
private ICollection<RuntimeDependencyManager.Dependency> dependencies = new List<RuntimeDependencyManager.Dependency>();
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
dependencies.Add(new RuntimeDependencyManager.Dependency(typeof(IReadOnlyDictionary<string, IShop>), "Shops", DownloadShops));
|
||||
dependencies.Add(new RuntimeDependencyManager.Dependency(typeof(ApplicationProfile), "Application Profile", DownloadApplicationProfile));
|
||||
}
|
||||
|
||||
private async ValueTask<object> DownloadShops(HttpClient publicHttp, HttpClient http, AuthenticationState authState, ILogger logger)
|
||||
{
|
||||
ShopModuleLoader loader = new ShopModuleLoader(publicHttp, logger);
|
||||
return await loader.GetShops();
|
||||
}
|
||||
|
||||
private async ValueTask<object> DownloadApplicationProfile(HttpClient publicHttp, HttpClient http, AuthenticationState authState, ILogger logger)
|
||||
{
|
||||
if (authState.User.Identity.IsAuthenticated)
|
||||
{
|
||||
logger.LogDebug($"User is logged in. Attempting to fetch application profile.");
|
||||
HttpResponseMessage response = await http.GetAsync("Profile/Application");
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadFromJsonAsync<ApplicationProfile>();
|
||||
}
|
||||
}
|
||||
return new ApplicationProfile();
|
||||
}
|
||||
}
|
||||
}
|
@ -25,7 +25,9 @@ namespace MultiShop.Client.Pages
|
||||
[CascadingParameter]
|
||||
private Task<AuthenticationState> AuthenticationStateTask { get; set; }
|
||||
|
||||
[CascadingParameter(Name = "ApplicationProfile")]
|
||||
[CascadingParameter(Name = "RuntimeDependencyManager")]
|
||||
private RuntimeDependencyManager RuntimeDependencyManager { get; set; }
|
||||
|
||||
private ApplicationProfile ApplicationProfile { get; set; }
|
||||
|
||||
private bool collapseNavMenu;
|
||||
@ -50,6 +52,12 @@ namespace MultiShop.Client.Pages
|
||||
Section.Search
|
||||
};
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
ApplicationProfile = RuntimeDependencyManager.Get<ApplicationProfile>();
|
||||
}
|
||||
|
||||
private string GetNavItemCssClass(Section section)
|
||||
{
|
||||
return "nav-item" + ((section == currentSection) ? " active" : null);
|
||||
|
@ -29,8 +29,11 @@ namespace MultiShop.Client.Pages
|
||||
[CascadingParameter]
|
||||
private Task<AuthenticationState> AuthenticationStateTask { get; set; }
|
||||
|
||||
[CascadingParameter(Name = "Shops")]
|
||||
public IReadOnlyDictionary<string, IShop> Shops { get; set; }
|
||||
[CascadingParameter(Name = "RuntimeDependencyManager")]
|
||||
public RuntimeDependencyManager RuntimeDependencyManager { get; set; }
|
||||
|
||||
private IReadOnlyDictionary<string, IShop> Shops { get; set; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
public string Query { get; set; }
|
||||
@ -51,6 +54,7 @@ namespace MultiShop.Client.Pages
|
||||
{
|
||||
base.OnInitialized();
|
||||
LayoutStateChangeNotifier.Notify += UpdateState;
|
||||
Shops = RuntimeDependencyManager.Get<IReadOnlyDictionary<string, IShop>>();
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
|
83
src/MultiShop/Client/RuntimeDependencyManager.cs
Normal file
83
src/MultiShop/Client/RuntimeDependencyManager.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MultiShop.Client
|
||||
{
|
||||
public class RuntimeDependencyManager : IAsyncDisposable
|
||||
{
|
||||
private bool disposedValue;
|
||||
private HttpClient authenticatedHttp;
|
||||
private HttpClient publicHttp;
|
||||
private Task<AuthenticationState> authenticationStateTask;
|
||||
private ILogger logger;
|
||||
private Dictionary<Type, Dictionary<string, object>> RuntimeLoadedDependencies = new Dictionary<Type, Dictionary<string, object>>();
|
||||
|
||||
public RuntimeDependencyManager(HttpClient publicHttp, HttpClient authenticatedHttp, Task<AuthenticationState> authenticationStateTask, ILogger logger)
|
||||
{
|
||||
this.publicHttp = publicHttp;
|
||||
this.authenticatedHttp = authenticatedHttp;
|
||||
this.authenticationStateTask = authenticationStateTask;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
||||
public async ValueTask SetupDependency(Dependency dependency)
|
||||
{
|
||||
logger.LogDebug($"Setting up dependency of type \"{dependency.Type}\" named \"{dependency.Name}\".");
|
||||
Dictionary<string, object> dependencies = RuntimeLoadedDependencies.GetValueOrDefault(dependency.Type, new Dictionary<string, object>());
|
||||
dependencies.Add(dependency.Name, await dependency.LoadDependency.Invoke(publicHttp, authenticatedHttp, await authenticationStateTask, logger));
|
||||
RuntimeLoadedDependencies[dependency.Type] = dependencies;
|
||||
}
|
||||
|
||||
public T Get<T>(string name = "") {
|
||||
Type type = typeof(T);
|
||||
if (!RuntimeLoadedDependencies.ContainsKey(typeof(T))) throw new InvalidOperationException($"No dependency of type {type}.");
|
||||
if (!RuntimeLoadedDependencies[type].ContainsKey(name)) throw new InvalidOperationException($"No dependency of type {type} with name {name}.");
|
||||
return (T) RuntimeLoadedDependencies[type][name];
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (!disposedValue) {
|
||||
foreach (Dictionary<string, object> dependencies in RuntimeLoadedDependencies.Values)
|
||||
{
|
||||
foreach (object dependency in dependencies.Values)
|
||||
{
|
||||
IDisposable disposableDep = dependency as IDisposable;
|
||||
if (disposableDep != null) {
|
||||
disposableDep.Dispose();
|
||||
} else {
|
||||
IAsyncDisposable asyncDisposableDep = dependency as IAsyncDisposable;
|
||||
if (asyncDisposableDep != null) {
|
||||
await asyncDisposableDep.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RuntimeLoadedDependencies.Clear();
|
||||
disposedValue = true;
|
||||
}
|
||||
|
||||
|
||||
public class Dependency
|
||||
{
|
||||
public Type Type { get; }
|
||||
public string Name { get; }
|
||||
public string DisplayName { get; }
|
||||
public Func<HttpClient, HttpClient, AuthenticationState, ILogger, ValueTask<object>> LoadDependency { get; }
|
||||
|
||||
public Dependency(Type type, string displayName, Func<HttpClient, HttpClient, AuthenticationState, ILogger, ValueTask<object>> LoadDependencyFunc, string name = null)
|
||||
{
|
||||
this.Type = type;
|
||||
this.DisplayName = displayName;
|
||||
this.Name = name ?? "";
|
||||
this.LoadDependency = LoadDependencyFunc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,10 +4,8 @@
|
||||
|
||||
@if (loadingDisplay == null)
|
||||
{
|
||||
<CascadingValue Value="@applicationProfile" Name="ApplicationProfile">
|
||||
<CascadingValue Value="@shops" Name="Shops">
|
||||
@Content
|
||||
</CascadingValue>
|
||||
<CascadingValue Value="@manager" Name="RuntimeDependencyManager">
|
||||
@Content
|
||||
</CascadingValue>
|
||||
} else {
|
||||
@LoadingContent(loadingDisplay)
|
||||
|
@ -1,18 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MultiShop.Client.Module;
|
||||
using MultiShop.Shared.Models;
|
||||
using MultiShop.Shop.Framework;
|
||||
|
||||
namespace MultiShop.Client.Shared
|
||||
{
|
||||
public partial class CascadingDependencies : IDisposable
|
||||
public partial class CascadingDependencies : IAsyncDisposable
|
||||
{
|
||||
[Inject]
|
||||
private ILogger<CascadingDependencies> Logger { get; set; }
|
||||
@ -29,65 +27,36 @@ namespace MultiShop.Client.Shared
|
||||
[Parameter]
|
||||
public RenderFragment Content { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public ICollection<RuntimeDependencyManager.Dependency> Dependencies { get; set; }
|
||||
|
||||
private RuntimeDependencyManager manager;
|
||||
private bool disposedValue;
|
||||
|
||||
private string loadingDisplay;
|
||||
|
||||
private IReadOnlyDictionary<string, IShop> shops;
|
||||
|
||||
private ApplicationProfile applicationProfile;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
loadingDisplay = "";
|
||||
loadingDisplay = "stuff";
|
||||
await base.OnInitializedAsync();
|
||||
await DownloadShops(HttpClientFactory.CreateClient("Public-MultiShop.ServerAPI"));
|
||||
await DownloadApplicationProfile(HttpClientFactory.CreateClient("MultiShop.ServerAPI"));
|
||||
manager = new RuntimeDependencyManager(HttpClientFactory.CreateClient("Public-MultiShop.ServerAPI"), HttpClientFactory.CreateClient("MultiShop.ServerAPI"), AuthenticationStateTask, Logger);
|
||||
foreach (RuntimeDependencyManager.Dependency dep in Dependencies)
|
||||
{
|
||||
loadingDisplay = dep.DisplayName;
|
||||
await manager.SetupDependency(dep);
|
||||
}
|
||||
loadingDisplay = null;
|
||||
}
|
||||
|
||||
private async Task DownloadShops(HttpClient http)
|
||||
{
|
||||
loadingDisplay = "shops";
|
||||
ShopModuleLoader loader = new ShopModuleLoader(http, Logger);
|
||||
shops = await loader.GetShops();
|
||||
}
|
||||
|
||||
|
||||
private async Task DownloadApplicationProfile(HttpClient http)
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
loadingDisplay = "profile";
|
||||
AuthenticationState authState = await AuthenticationStateTask;
|
||||
if (authState.User.Identity.IsAuthenticated)
|
||||
{
|
||||
Logger.LogDebug($"User is logged in. Attempting to fetch application profile.");
|
||||
HttpResponseMessage response = await http.GetAsync("Profile/Application");
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
applicationProfile = await response.Content.ReadFromJsonAsync<ApplicationProfile>();
|
||||
}
|
||||
if (!disposedValue) {
|
||||
await manager.DisposeAsync();
|
||||
}
|
||||
if (applicationProfile == null) applicationProfile = new ApplicationProfile();
|
||||
disposedValue = true;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
foreach (string shopName in shops.Keys)
|
||||
{
|
||||
shops[shopName].Dispose();
|
||||
}
|
||||
}
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
@implements IDisposable
|
||||
@inject LayoutStateChangeNotifier LayoutStateChangeNotifier
|
||||
|
||||
<nav class=@ApplicationProfile.GetNavCssClass("navbar navbar-expand-lg")>
|
||||
<nav class=@(RuntimeDependencyManager.Get<ApplicationProfile>().GetNavCssClass("navbar navbar-expand-lg"))>
|
||||
<a class="navbar-brand" href="">
|
||||
@BrandContent
|
||||
</a>
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
<div class=@NavMenuCssClass>
|
||||
<ul class="navbar-nav mr-auto">
|
||||
@foreach (string item in Items)
|
||||
@foreach (string item in Places)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href=@item Match=@(string.IsNullOrEmpty(item) ? NavLinkMatch.All : NavLinkMatch.Prefix)>
|
||||
@ -28,15 +28,15 @@
|
||||
</nav>
|
||||
|
||||
@code {
|
||||
[CascadingParameter(Name = "ApplicationProfile")]
|
||||
private ApplicationProfile ApplicationProfile { get; set; }
|
||||
[CascadingParameter(Name = "RuntimeDependencyManager")]
|
||||
private RuntimeDependencyManager RuntimeDependencyManager { get; set; }
|
||||
|
||||
private bool collapseNavMenu = true;
|
||||
|
||||
private string NavMenuCssClass => (collapseNavMenu ? "collapse " : " ") + "navbar-collapse";
|
||||
|
||||
[Parameter]
|
||||
public IList<string> Items { get; set; }
|
||||
public IList<string> Places { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment BrandContent { get; set; }
|
||||
|
@ -5,7 +5,7 @@
|
||||
@inject SignOutSessionStateManager SignOutManager
|
||||
@inject IConfiguration Configuration
|
||||
|
||||
<HorizontalNavMenuTemplate Items=@PlaceOrder>
|
||||
<HorizontalNavMenuTemplate Places=@PlaceOrder>
|
||||
<BrandContent>
|
||||
<img src="images/100x100.png" width="30" height="30" class="d-inline-block align-top">
|
||||
MultiShop
|
||||
|
@ -5,7 +5,7 @@
|
||||
@implements IDisposable
|
||||
@inject LayoutStateChangeNotifier LayoutStateChangeNotifier
|
||||
|
||||
<div class=@ApplicationProfile.GetPageCssClass("page")>
|
||||
<div class=@(dependencyManager.Get<ApplicationProfile>("").GetPageCssClass("page"))>
|
||||
<HorizontalSiteNav />
|
||||
<div class="content">
|
||||
@Body
|
||||
@ -13,14 +13,16 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[CascadingParameter(Name = "ApplicationProfile")]
|
||||
private ApplicationProfile ApplicationProfile { get; set; }
|
||||
[CascadingParameter(Name = "RuntimeDependencyManager")]
|
||||
private RuntimeDependencyManager dependencyManager { get; set; }
|
||||
|
||||
private bool disposed;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
LayoutStateChangeNotifier.Notify += UpdateState;
|
||||
|
||||
}
|
||||
|
||||
private async Task UpdateState()
|
||||
|
Loading…
Reference in New Issue
Block a user