Skip to content

Commit 68f637b

Browse files
LucHeartCopilot
andauthored
Feature/root main components (#24)
* support root components in blazor page * fix nuget stuffs * Update ModuleBase/DesktopModuleBase.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update ModuleBase/DesktopModuleBase.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * rework how module icons are defined --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 3fb2d29 commit 68f637b

File tree

11 files changed

+137
-37
lines changed

11 files changed

+137
-37
lines changed

Desktop/Desktop.csproj

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@
3737
</PropertyGroup>
3838

3939
<ItemGroup Condition="'$(Configuration)' == 'Debug-Windows' Or '$(Configuration)' == 'Release-Windows'">
40-
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.81" />
41-
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.81" />
42-
<PackageReference Update="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.81" />
40+
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.100" />
41+
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.100" />
42+
<PackageReference Update="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.100" />
4343
</ItemGroup>
4444

4545
<PropertyGroup Condition="'$(Configuration)' == 'Debug-Photino' Or '$(Configuration)' == 'Release-Photino'">
@@ -60,16 +60,17 @@
6060
</PropertyGroup>
6161

6262
<ItemGroup>
63-
<PackageReference Include="AspNetCore.SassCompiler" Version="1.89.2" />
63+
<PackageReference Update="Microsoft.AspNetCore.Components.Web" Version="9.0.8" />
64+
<PackageReference Include="AspNetCore.SassCompiler" Version="1.90.0" />
6465
<PackageReference Include="CommandLineParser" Version="2.9.1"/>
6566
<PackageReference Include="dnlib" Version="4.5.0" />
66-
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.7" />
67-
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
68-
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.7" />
69-
<PackageReference Include="MudBlazor" Version="8.9.0" />
67+
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.8" />
68+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
69+
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.8" />
70+
<PackageReference Include="MudBlazor" Version="8.11.0" />
7071
<PackageReference Include="Semver" Version="3.0.0" />
7172
<PackageReference Include="Serilog" Version="4.3.0" />
72-
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.7" />
73+
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.8" />
7374
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
7475
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.2" />
7576
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />

Desktop/ModuleFileProvider.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public void SetModuleManager(ModuleManager.ModuleManager moduleManager)
4848
{
4949
if (_moduleManager != null)
5050
{
51-
_modulesLoadedSubscription.Dispose();
51+
_modulesLoadedSubscription?.Dispose();
5252
}
5353

5454
_moduleManager = moduleManager;
@@ -306,13 +306,13 @@ private static void MakeValidEverettFolderIdentifier(StringBuilder builder, stri
306306
#endregion
307307

308308
private bool _disposed;
309-
private IDisposable _modulesLoadedSubscription;
309+
private IDisposable? _modulesLoadedSubscription;
310310

311311
public void Dispose()
312312
{
313313
if (_disposed) return;
314314
_disposed = true;
315315

316-
_modulesLoadedSubscription.Dispose();
316+
_modulesLoadedSubscription?.Dispose();
317317
}
318318
}

Desktop/ModuleManager/ModuleManager.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,13 @@ private void LoadModule(string moduleFolderPath, string moduleDll, string module
223223

224224
var module = (DesktopModuleBase?)ActivatorUtilities.CreateInstance(_serviceProvider, moduleAttribute.ModuleType);
225225
if (module is null) throw new Exception("Failed to instantiate module!");
226+
227+
#pragma warning disable CS0618
228+
if (module.IconPath is not null)
229+
{
230+
module.Icon = IconOneOf.FromPath(module.IconPath);
231+
}
232+
#pragma warning restore CS0618
226233

227234
var loadedModule = new LoadedModule
228235
{

Desktop/Ui/Pages/Dash/Components/ModuleManagerItem.razor

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@using System.Collections.Immutable
22
@using OpenShock.Desktop.Config
3+
@using OpenShock.Desktop.ModuleBase
34
@using OpenShock.Desktop.ModuleManager
45
@using OpenShock.Desktop.ModuleManager.Repository
56
@using OpenShock.Desktop.Utils
@@ -10,7 +11,15 @@
1011
@inject ISnackbar Snackbar
1112

1213
<MudPaper Class="d-flex module-manager-item-root rounded-lg gap-10" Outlined="true">
13-
<img src="@_moduleIcon" alt="@_moduleName" class="icon rounded-lg"/>
14+
@switch (_moduleIcon?.Index)
15+
{
16+
case 0:
17+
<img src="@_moduleIcon.AsT0" alt="Module Icon" class="icon rounded-lg"/>
18+
break;
19+
case 1:
20+
<MudIcon Icon="@_moduleIcon.AsT1"/>
21+
break;
22+
}
1423

1524
<div class="d-flex flex-column justify-space-evenly overflow-hidden">
1625
<MudText Typo="Typo.h4">@_moduleName</MudText>
@@ -112,7 +121,7 @@
112121
[Parameter] public required string ModuleId { get; init; }
113122

114123
private string _moduleName = null!;
115-
private string? _moduleIcon;
124+
private IconOneOf? _moduleIcon;
116125

117126
private SemVersion? LatestVersion => RepoModule?.Versions.Keys.Where(x => x.IsRelease).OrderByDescending(x => x, SemVersion.PrecedenceComparer).FirstOrDefault();
118127
private SemVersion? LatestPreReleaseVersion => RepoModule?.Versions.Keys.Where(x => !x.IsRelease).OrderByDescending(x => x, SemVersion.PrecedenceComparer).FirstOrDefault();
@@ -127,7 +136,13 @@
127136
}
128137

129138
_moduleName = RepoModule?.Name ?? (LoadedModule?.Name ?? "error");
130-
_moduleIcon = RepoModule?.IconUrl?.ToString() ?? LoadedModule?.Module.IconPath;
139+
if (RepoModule?.IconUrl is not null)
140+
{
141+
_moduleIcon = IconOneOf.FromPath(RepoModule.IconUrl.ToString());
142+
}
143+
{
144+
_moduleIcon = LoadedModule?.Module.Icon;
145+
}
131146
_availableVersions = GetAvailableVersions();
132147
}
133148

Desktop/Ui/Pages/Dash/Components/ModuleNavComponent.razor

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
@using OpenShock.Desktop.ModuleManager
22

3-
<ModuleNavGroup IconImage="@LoadedModule.Module.IconPath" Title="@LoadedModule.Name">
4-
@foreach (var subComponent in LoadedModule.Module.NavigationComponents)
5-
{
6-
<ModuleNavLink Icon="@subComponent.Icon" Href="@("/dash/module/" + LoadedModule.Id + "/" + subComponent.Name)">@(subComponent.Name)</ModuleNavLink>
7-
}
8-
</ModuleNavGroup>
3+
@if (LoadedModule.Module.RootComponent is not null)
4+
{
5+
<ModuleNavLink Icon="LoadedModule.Module.Icon" Href="@("/dash/module/" + LoadedModule.Id)">@LoadedModule.Name</ModuleNavLink>
6+
}
7+
else
8+
{
9+
<ModuleNavGroup Icon="@LoadedModule.Module.Icon" Title="@LoadedModule.Name">
10+
@foreach (var subComponent in LoadedModule.Module.NavigationComponents)
11+
{
12+
<ModuleNavLink Icon="@subComponent.Icon" Href="@("/dash/module/" + LoadedModule.Id + "/" + subComponent.Name)">@(subComponent.Name)</ModuleNavLink>
13+
}
14+
</ModuleNavGroup>
15+
}
916

1017

1118
@code {

Desktop/Ui/Pages/Dash/Components/ModuleNavGroup.razor

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,21 @@
1111
aria-controls="@_navigationContext.MenuId"
1212
aria-expanded="@_navigationContext.Expanded.ToString().ToLowerInvariant()"
1313
aria-label="Toggle Module Navigation">
14-
@if (!string.IsNullOrEmpty(IconImage))
14+
@switch (Icon?.Index)
1515
{
16-
<img src="@IconImage" alt="Module Icon" style="width: 24px; height: 24px">
16+
case 0:
17+
<img src="@Icon.AsT0" alt="Module Icon" style="width: 24px; height: 24px">
18+
break;
19+
case 1:
20+
<MudIcon Icon="@Icon.AsT1"/>
21+
break;
1722
}
1823
<div Class="mud-nav-link-text">
1924
@Title
2025
</div>
2126
@if (!HideExpandIcon)
2227
{
23-
<MudIcon Disabled="@Disabled" Icon="@ExpandIcon" Class="@ExpandIconClassname" />
28+
<MudIcon Disabled="@Disabled" Icon="@ExpandIcon" Class="@ExpandIconClassname"/>
2429
}
2530
</button>
2631
<MudCollapse aria-hidden="@((_navigationContext.Expanded is false).ToString().ToLowerInvariant())"

Desktop/Ui/Pages/Dash/Components/ModuleNavGroup.razor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using MudBlazor;
44
using MudBlazor.State;
55
using MudBlazor.Utilities;
6+
using OpenShock.Desktop.ModuleBase;
67

78
namespace OpenShock.Desktop.Ui.Pages.Dash.Components;
89

@@ -71,7 +72,7 @@ protected override void OnInitialized()
7172
/// </remarks>
7273
[Parameter]
7374
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
74-
public string? IconImage { get; set; }
75+
public IconOneOf? Icon { get; set; }
7576

7677
/// <summary>
7778
/// The CSS classes applied to this nav group title.
Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,49 @@
11
@using OpenShock.Desktop.ModuleManager
22

33
@inject ModuleManager ModuleManager
4+
@page "/dash/module/{module}"
45
@page "/dash/module/{module}/{component}"
56

6-
@if (LoadedModule == null)
7+
@if (_loadedModule == null)
78
{
89
<MudPaper Outlined="true" Class="mud-paper-padding">
910
<MudText Typo="Typo.h6">Module not found [@Module]</MudText>
1011
</MudPaper>
1112
}
12-
else if (ComponentType == null)
13+
else if (_loadedModule.Module.RootComponent is not null)
1314
{
14-
<MudPaper Outlined="true" Class="mud-paper-padding">
15-
<MudText Typo="Typo.h6">Sub Component not found [@Module] [@Component]</MudText>
16-
</MudPaper>
15+
<DynamicComponent Type="_loadedModule.Module.RootComponent"></DynamicComponent>
16+
}
17+
else if (_componentType is not null)
18+
{
19+
<DynamicComponent Type="_componentType"></DynamicComponent>
1720
}
1821
else
1922
{
20-
<DynamicComponent Type="ComponentType"></DynamicComponent>
23+
<MudPaper Outlined="true" Class="mud-paper-padding">
24+
<MudText Typo="Typo.h6">Sub Component not found [@Module] [@Component]</MudText>
25+
</MudPaper>
2126
}
2227

2328
@code {
2429
[Parameter] public string? Module { get; set; }
2530

2631
[Parameter] public string? Component { get; set; }
2732

28-
private LoadedModule? LoadedModule => string.IsNullOrWhiteSpace(Module) ? null : ModuleManager.Modules.GetValueOrDefault(Module);
33+
private LoadedModule? _loadedModule;
34+
35+
private Type? _componentType;
2936

30-
private Type? ComponentType => LoadedModule?.Module.NavigationComponents.FirstOrDefault(x => x.Name.Equals(Component, StringComparison.InvariantCultureIgnoreCase))
31-
?.ComponentType;
37+
public override async Task SetParametersAsync(ParameterView parameters)
38+
{
39+
await base.SetParametersAsync(parameters);
40+
SetupVariables();
41+
}
3242

43+
private void SetupVariables()
44+
{
45+
_loadedModule = string.IsNullOrWhiteSpace(Module) ? null : ModuleManager.Modules.GetValueOrDefault(Module);
46+
_componentType = _loadedModule?.Module.NavigationComponents.FirstOrDefault(x => x.Name.Equals(Component, StringComparison.InvariantCultureIgnoreCase))
47+
?.ComponentType;
48+
}
3349
}

ExampleModule/ExampleModule.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
</ItemGroup>
2222

2323
<ItemGroup>
24-
<PackageReference Include="MudBlazor" Version="8.10.0" />
24+
<PackageReference Include="MudBlazor" Version="8.11.0" />
2525
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
2626
</ItemGroup>
2727

ModuleBase/DesktopModuleBase.cs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,75 @@
44

55
namespace OpenShock.Desktop.ModuleBase;
66

7+
/// <summary>
8+
/// The main base class for all desktop modules.
9+
/// </summary>
710
public abstract class DesktopModuleBase
811
{
12+
/// <summary>
13+
/// Icon path for the module, used in navigation and other UI components.
14+
/// </summary>
15+
[Obsolete("Use Icon property instead. This property will be removed in a future version.")]
916
public virtual string? IconPath { get; } = null;
17+
18+
/// <summary>
19+
/// Main icon for the module, used in navigation and other UI components.
20+
/// </summary>
21+
public virtual IconOneOf? Icon { get; set; } = null;
22+
23+
/// <summary>
24+
/// Main interface for modules to interact with OpenShock Desktop.
25+
/// </summary>
1026
public IModuleInstanceManager ModuleInstanceManager { get; private set; } = null!;
11-
27+
28+
/// <summary>
29+
/// A collection of navigation components that this module provides.
30+
/// These components will be displayed in the main navigation menu of the application.
31+
/// </summary>
1232
public abstract IReadOnlyCollection<NavigationItem> NavigationComponents { get; }
1333

34+
/// <summary>
35+
/// Alternative to <see cref="NavigationComponents"/> for modules that have a single main component.
36+
/// </summary>
37+
public virtual Type? RootComponent { get; } = null;
38+
39+
/// <summary>
40+
/// This method is called by OpenShock Desktop to set the context for the module. Do not call this method by yourself.
41+
/// </summary>
42+
/// <param name="moduleInstanceManager"></param>
1443
public void SetContext(IModuleInstanceManager moduleInstanceManager)
1544
{
1645
ModuleInstanceManager = moduleInstanceManager;
1746
}
1847

48+
/// <summary>
49+
/// During startup, this method is called to allow the module to perform any necessary setup.
50+
/// </summary>
51+
/// <returns>A <see cref="Task"/> that represents the completion of any asynchronous setup operations required by the module.</returns>
1952
public virtual Task Setup()
2053
{
2154
return Task.CompletedTask;
2255
}
2356

57+
/// <summary>
58+
/// Called after <see cref="Setup"/> to allow the module to start any necessary services or perform additional initialization.
59+
/// </summary>
60+
/// <returns>A <see cref="Task"/> that represents the completion of any startup logic or initialization performed by the module.</returns>
2461
public virtual Task Start()
2562
{
2663
return Task.CompletedTask;
2764
}
2865

66+
/// <summary>
67+
/// Module service provider.
68+
/// You can use the <see cref="ModuleInjectAttribute"/> to resolve services that are registered for this module within blazor components.
69+
///
70+
/// <example>
71+
/// <code>
72+
/// [ModuleInject] private IModuleConfig<SomeModuleConfig> ModuleConfig { get; set; } = null!;
73+
/// </code>
74+
/// </example>
75+
///
76+
/// </summary>
2977
public IServiceProvider ModuleServiceProvider { get; protected set; } = new ServiceCollection().BuildServiceProvider();
3078
}

0 commit comments

Comments
 (0)