Changed CascadingDependencies to be more modular.
This commit is contained in:
parent
d57a61d5ca
commit
3957d65370
@ -1,5 +1,5 @@
|
|||||||
<CascadingAuthenticationState>
|
<CascadingAuthenticationState>
|
||||||
<CascadingDependencies>
|
<CascadingDependencies Dependencies="@dependencies">
|
||||||
<Content>
|
<Content>
|
||||||
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
|
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
|
||||||
<Found Context="routeData">
|
<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]
|
[CascadingParameter]
|
||||||
private Task<AuthenticationState> AuthenticationStateTask { get; set; }
|
private Task<AuthenticationState> AuthenticationStateTask { get; set; }
|
||||||
|
|
||||||
[CascadingParameter(Name = "ApplicationProfile")]
|
[CascadingParameter(Name = "RuntimeDependencyManager")]
|
||||||
|
private RuntimeDependencyManager RuntimeDependencyManager { get; set; }
|
||||||
|
|
||||||
private ApplicationProfile ApplicationProfile { get; set; }
|
private ApplicationProfile ApplicationProfile { get; set; }
|
||||||
|
|
||||||
private bool collapseNavMenu;
|
private bool collapseNavMenu;
|
||||||
@ -50,6 +52,12 @@ namespace MultiShop.Client.Pages
|
|||||||
Section.Search
|
Section.Search
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
base.OnInitialized();
|
||||||
|
ApplicationProfile = RuntimeDependencyManager.Get<ApplicationProfile>();
|
||||||
|
}
|
||||||
|
|
||||||
private string GetNavItemCssClass(Section section)
|
private string GetNavItemCssClass(Section section)
|
||||||
{
|
{
|
||||||
return "nav-item" + ((section == currentSection) ? " active" : null);
|
return "nav-item" + ((section == currentSection) ? " active" : null);
|
||||||
|
@ -29,8 +29,11 @@ namespace MultiShop.Client.Pages
|
|||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
private Task<AuthenticationState> AuthenticationStateTask { get; set; }
|
private Task<AuthenticationState> AuthenticationStateTask { get; set; }
|
||||||
|
|
||||||
[CascadingParameter(Name = "Shops")]
|
[CascadingParameter(Name = "RuntimeDependencyManager")]
|
||||||
public IReadOnlyDictionary<string, IShop> Shops { get; set; }
|
public RuntimeDependencyManager RuntimeDependencyManager { get; set; }
|
||||||
|
|
||||||
|
private IReadOnlyDictionary<string, IShop> Shops { get; set; }
|
||||||
|
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Query { get; set; }
|
public string Query { get; set; }
|
||||||
@ -51,6 +54,7 @@ namespace MultiShop.Client.Pages
|
|||||||
{
|
{
|
||||||
base.OnInitialized();
|
base.OnInitialized();
|
||||||
LayoutStateChangeNotifier.Notify += UpdateState;
|
LayoutStateChangeNotifier.Notify += UpdateState;
|
||||||
|
Shops = RuntimeDependencyManager.Get<IReadOnlyDictionary<string, IShop>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
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)
|
@if (loadingDisplay == null)
|
||||||
{
|
{
|
||||||
<CascadingValue Value="@applicationProfile" Name="ApplicationProfile">
|
<CascadingValue Value="@manager" Name="RuntimeDependencyManager">
|
||||||
<CascadingValue Value="@shops" Name="Shops">
|
@Content
|
||||||
@Content
|
|
||||||
</CascadingValue>
|
|
||||||
</CascadingValue>
|
</CascadingValue>
|
||||||
} else {
|
} else {
|
||||||
@LoadingContent(loadingDisplay)
|
@LoadingContent(loadingDisplay)
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Json;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.AspNetCore.Components.Authorization;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using MultiShop.Client.Module;
|
|
||||||
using MultiShop.Shared.Models;
|
using MultiShop.Shared.Models;
|
||||||
using MultiShop.Shop.Framework;
|
using MultiShop.Shop.Framework;
|
||||||
|
|
||||||
namespace MultiShop.Client.Shared
|
namespace MultiShop.Client.Shared
|
||||||
{
|
{
|
||||||
public partial class CascadingDependencies : IDisposable
|
public partial class CascadingDependencies : IAsyncDisposable
|
||||||
{
|
{
|
||||||
[Inject]
|
[Inject]
|
||||||
private ILogger<CascadingDependencies> Logger { get; set; }
|
private ILogger<CascadingDependencies> Logger { get; set; }
|
||||||
@ -29,65 +27,36 @@ namespace MultiShop.Client.Shared
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment Content { get; set; }
|
public RenderFragment Content { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public ICollection<RuntimeDependencyManager.Dependency> Dependencies { get; set; }
|
||||||
|
|
||||||
|
private RuntimeDependencyManager manager;
|
||||||
private bool disposedValue;
|
private bool disposedValue;
|
||||||
|
|
||||||
private string loadingDisplay;
|
private string loadingDisplay;
|
||||||
|
|
||||||
private IReadOnlyDictionary<string, IShop> shops;
|
|
||||||
|
|
||||||
private ApplicationProfile applicationProfile;
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
loadingDisplay = "";
|
loadingDisplay = "stuff";
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
await DownloadShops(HttpClientFactory.CreateClient("Public-MultiShop.ServerAPI"));
|
manager = new RuntimeDependencyManager(HttpClientFactory.CreateClient("Public-MultiShop.ServerAPI"), HttpClientFactory.CreateClient("MultiShop.ServerAPI"), AuthenticationStateTask, Logger);
|
||||||
await DownloadApplicationProfile(HttpClientFactory.CreateClient("MultiShop.ServerAPI"));
|
foreach (RuntimeDependencyManager.Dependency dep in Dependencies)
|
||||||
|
{
|
||||||
|
loadingDisplay = dep.DisplayName;
|
||||||
|
await manager.SetupDependency(dep);
|
||||||
|
}
|
||||||
loadingDisplay = null;
|
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";
|
if (!disposedValue) {
|
||||||
AuthenticationState authState = await AuthenticationStateTask;
|
await manager.DisposeAsync();
|
||||||
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 (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
|
@implements IDisposable
|
||||||
@inject LayoutStateChangeNotifier LayoutStateChangeNotifier
|
@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="">
|
<a class="navbar-brand" href="">
|
||||||
@BrandContent
|
@BrandContent
|
||||||
</a>
|
</a>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<div class=@NavMenuCssClass>
|
<div class=@NavMenuCssClass>
|
||||||
<ul class="navbar-nav mr-auto">
|
<ul class="navbar-nav mr-auto">
|
||||||
@foreach (string item in Items)
|
@foreach (string item in Places)
|
||||||
{
|
{
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<NavLink class="nav-link" href=@item Match=@(string.IsNullOrEmpty(item) ? NavLinkMatch.All : NavLinkMatch.Prefix)>
|
<NavLink class="nav-link" href=@item Match=@(string.IsNullOrEmpty(item) ? NavLinkMatch.All : NavLinkMatch.Prefix)>
|
||||||
@ -28,15 +28,15 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[CascadingParameter(Name = "ApplicationProfile")]
|
[CascadingParameter(Name = "RuntimeDependencyManager")]
|
||||||
private ApplicationProfile ApplicationProfile { get; set; }
|
private RuntimeDependencyManager RuntimeDependencyManager { get; set; }
|
||||||
|
|
||||||
private bool collapseNavMenu = true;
|
private bool collapseNavMenu = true;
|
||||||
|
|
||||||
private string NavMenuCssClass => (collapseNavMenu ? "collapse " : " ") + "navbar-collapse";
|
private string NavMenuCssClass => (collapseNavMenu ? "collapse " : " ") + "navbar-collapse";
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public IList<string> Items { get; set; }
|
public IList<string> Places { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment BrandContent { get; set; }
|
public RenderFragment BrandContent { get; set; }
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
@inject SignOutSessionStateManager SignOutManager
|
@inject SignOutSessionStateManager SignOutManager
|
||||||
@inject IConfiguration Configuration
|
@inject IConfiguration Configuration
|
||||||
|
|
||||||
<HorizontalNavMenuTemplate Items=@PlaceOrder>
|
<HorizontalNavMenuTemplate Places=@PlaceOrder>
|
||||||
<BrandContent>
|
<BrandContent>
|
||||||
<img src="images/100x100.png" width="30" height="30" class="d-inline-block align-top">
|
<img src="images/100x100.png" width="30" height="30" class="d-inline-block align-top">
|
||||||
MultiShop
|
MultiShop
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
@inject LayoutStateChangeNotifier LayoutStateChangeNotifier
|
@inject LayoutStateChangeNotifier LayoutStateChangeNotifier
|
||||||
|
|
||||||
<div class=@ApplicationProfile.GetPageCssClass("page")>
|
<div class=@(dependencyManager.Get<ApplicationProfile>("").GetPageCssClass("page"))>
|
||||||
<HorizontalSiteNav />
|
<HorizontalSiteNav />
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@Body
|
@Body
|
||||||
@ -13,14 +13,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[CascadingParameter(Name = "ApplicationProfile")]
|
[CascadingParameter(Name = "RuntimeDependencyManager")]
|
||||||
private ApplicationProfile ApplicationProfile { get; set; }
|
private RuntimeDependencyManager dependencyManager { get; set; }
|
||||||
|
|
||||||
private bool disposed;
|
private bool disposed;
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
base.OnInitialized();
|
base.OnInitialized();
|
||||||
LayoutStateChangeNotifier.Notify += UpdateState;
|
LayoutStateChangeNotifier.Notify += UpdateState;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateState()
|
private async Task UpdateState()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user