Upgraded mooncore versions. Cleaned up code, especially startup code. Changed versions

This commit is contained in:
2025-10-05 16:07:27 +00:00
parent d2ef59d171
commit 9ab69ffef5
43 changed files with 429 additions and 632 deletions

View File

@@ -0,0 +1,3 @@
namespace Moonlight.Client;
public interface IAssemblyMarker;

View File

@@ -7,14 +7,14 @@ namespace Moonlight.Client.Implementations;
public class CoreStartup : IPluginStartup
{
public Task BuildApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHostBuilder builder)
public void AddPlugin(WebAssemblyHostBuilder builder)
{
builder.Services.AddSingleton<ISidebarItemProvider, DefaultSidebarItemProvider>();
builder.Services.AddSingleton<IOverviewElementProvider, DefaultOverviewElementProvider>();
return Task.CompletedTask;
}
public Task ConfigureApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHost app)
=> Task.CompletedTask;
public void ConfigurePlugin(WebAssemblyHost app)
{
}
}

View File

@@ -12,7 +12,7 @@
<PropertyGroup>
<PackageTags>frontend</PackageTags>
<PackageId>Moonlight.Client</PackageId>
<Version>2.1.11</Version>
<Version>2.1.12</Version>
<Authors>Moonlight Panel</Authors>
<Description>A build of the client for moonlight development</Description>
<PackageProjectUrl>https://github.com/Moonlight-Panel/Moonlight</PackageProjectUrl>
@@ -25,8 +25,8 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.9" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.8" />
<PackageReference Include="MoonCore" Version="2.0.1" />
<PackageReference Include="MoonCore.Blazor.FlyonUi" Version="1.2.5" />
<PackageReference Include="MoonCore" Version="2.0.4" />
<PackageReference Include="MoonCore.Blazor.FlyonUi" Version="1.3.1" />
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -4,6 +4,6 @@ namespace Moonlight.Client.Plugins;
public interface IPluginStartup
{
public Task BuildApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHostBuilder builder);
public Task ConfigureApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHost app);
public void AddPlugin(WebAssemblyHostBuilder builder);
public void ConfigurePlugin(WebAssemblyHost app);
}

View File

@@ -1,9 +1,7 @@
using MoonCore.Attributes;
using MoonCore.Helpers;
using MoonCore.Models;
using Moonlight.Shared.Http.Requests.Admin.Sys.Theme;
using Moonlight.Shared.Http.Responses.Admin;
using Moonlight.Shared.Misc;
namespace Moonlight.Client.Services;

View File

@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using MoonCore.Blazor.FlyonUi.Exceptions;
using MoonCore.Permissions;
@@ -7,22 +8,20 @@ using Moonlight.Client.Services;
namespace Moonlight.Client.Startup;
public partial class Startup
public static partial class Startup
{
private Task RegisterAuthenticationAsync()
private static void AddAuth(this WebAssemblyHostBuilder builder)
{
WebAssemblyHostBuilder.Services.AddAuthorizationCore();
WebAssemblyHostBuilder.Services.AddCascadingAuthenticationState();
builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();
WebAssemblyHostBuilder.Services.AddScoped<AuthenticationStateProvider, RemoteAuthStateProvider>();
WebAssemblyHostBuilder.Services.AddScoped<IGlobalErrorFilter, UnauthenticatedErrorFilter>();
builder.Services.AddScoped<AuthenticationStateProvider, RemoteAuthStateProvider>();
builder.Services.AddScoped<IGlobalErrorFilter, UnauthenticatedErrorFilter>();
WebAssemblyHostBuilder.Services.AddAuthorizationPermissions(options =>
builder.Services.AddAuthorizationPermissions(options =>
{
options.ClaimName = "Permissions";
options.Prefix = "permissions:";
});
return Task.CompletedTask;
}
}

View File

@@ -1,39 +1,42 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using MoonCore.Blazor.FlyonUi;
using MoonCore.Blazor.FlyonUi.Exceptions;
using MoonCore.Extensions;
using MoonCore.Helpers;
using Moonlight.Client.Implementations;
using Moonlight.Client.Services;
using Moonlight.Client.UI;
namespace Moonlight.Client.Startup;
public partial class Startup
public static partial class Startup
{
private Task RegisterBaseAsync()
private static void AddBase(this WebAssemblyHostBuilder builder)
{
WebAssemblyHostBuilder.RootComponents.Add<App>("#app");
WebAssemblyHostBuilder.RootComponents.Add<HeadOutlet>("head::after");
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
WebAssemblyHostBuilder.Services.AddScoped(_ =>
builder.Services.AddScoped(_ =>
new HttpClient
{
BaseAddress = new Uri(Configuration.ApiUrl)
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
}
);
WebAssemblyHostBuilder.Services.AddScoped(sp =>
builder.Services.AddScoped(sp =>
{
var httpClient = sp.GetRequiredService<HttpClient>();
return new HttpApiClient(httpClient);
});
WebAssemblyHostBuilder.Services.AddFileManagerOperations();
WebAssemblyHostBuilder.Services.AddFlyonUiServices();
builder.Services.AddFileManagerOperations();
builder.Services.AddFlyonUiServices();
WebAssemblyHostBuilder.Services.AddScoped<ThemeService>();
builder.Services.AddScoped<ThemeService>();
WebAssemblyHostBuilder.Services.AutoAddServices<Startup>();
return Task.CompletedTask;
builder.Services.AutoAddServices<IAssemblyMarker>();
builder.Services.AddScoped<IGlobalErrorFilter, LogErrorFilter>();
}
}

View File

@@ -1,30 +1,13 @@
using Microsoft.Extensions.DependencyInjection;
using MoonCore.Blazor.FlyonUi.Exceptions;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MoonCore.Logging;
using Moonlight.Client.Implementations;
namespace Moonlight.Client.Startup;
public partial class Startup
public static partial class Startup
{
private Task SetupLoggingAsync()
private static void AddLogging(this WebAssemblyHostBuilder builder)
{
var loggerFactory = new LoggerFactory();
loggerFactory.AddAnsiConsole();
Logger = loggerFactory.CreateLogger<Startup>();
return Task.CompletedTask;
}
private Task RegisterLoggingAsync()
{
WebAssemblyHostBuilder.Logging.ClearProviders();
WebAssemblyHostBuilder.Logging.AddAnsiConsole();
WebAssemblyHostBuilder.Services.AddScoped<IGlobalErrorFilter, LogErrorFilter>();
return Task.CompletedTask;
builder.Logging.ClearProviders();
builder.Logging.AddAnsiConsole();
}
}

View File

@@ -1,12 +1,8 @@
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using Moonlight.Shared.Misc;
namespace Moonlight.Client.Startup;
public partial class Startup
public static partial class Startup
{
private Task PrintVersionAsync()
private static void PrintVersion()
{
// Fancy start console output... yes very fancy :>
Console.Write("Running ");
@@ -23,30 +19,5 @@ public partial class Startup
}
Console.WriteLine();
return Task.CompletedTask;
}
private async Task LoadConfigurationAsync()
{
try
{
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(WebAssemblyHostBuilder.HostEnvironment.BaseAddress);
var jsonText = await httpClient.GetStringAsync("frontend.json");
Configuration = JsonSerializer.Deserialize<FrontendConfiguration>(jsonText, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
})!;
WebAssemblyHostBuilder.Services.AddSingleton(Configuration);
}
catch (Exception e)
{
Logger.LogCritical("Unable to load configuration. Unable to continue: {e}", e);
throw;
}
}
}

View File

@@ -1,5 +1,5 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using MoonCore.Logging;
using Moonlight.Client.Plugins;
using Moonlight.Client.Services;
@@ -7,72 +7,25 @@ namespace Moonlight.Client.Startup;
public partial class Startup
{
private IPluginStartup[] PluginStartups;
private IServiceProvider PluginLoadServiceProvider;
private Task InitializePluginsAsync()
private static void AddPlugins(this WebAssemblyHostBuilder builder, IPluginStartup[] startups)
{
// Define minimal service collection
var startupSc = new ServiceCollection();
// Create logging proxy
startupSc.AddLogging(builder =>
foreach (var startup in startups)
startup.AddPlugin(builder);
// Get all assemblies and combine them into the application assembly service
// TODO: Consider rewriting this as it may not be that performant to do string checking to find distinct items
builder.Services.AddSingleton(new ApplicationAssemblyService()
{
builder.ClearProviders();
builder.AddAnsiConsole();
});
PluginLoadServiceProvider = startupSc.BuildServiceProvider();
// Add application assembly service
var appAssemblyService = new ApplicationAssemblyService();
appAssemblyService.Assemblies.AddRange(
PluginStartups
Assemblies = startups
.Select(x => x.GetType().Assembly)
.Distinct()
);
WebAssemblyHostBuilder.Services.AddSingleton(appAssemblyService);
return Task.CompletedTask;
.DistinctBy(x => x.FullName)
.ToList()
});
}
private async Task HookPluginBuildAsync()
private static void ConfigurePlugins(this WebAssemblyHost app, IPluginStartup[] startups)
{
foreach (var pluginAppStartup in PluginStartups)
{
try
{
await pluginAppStartup.BuildApplicationAsync(PluginLoadServiceProvider, WebAssemblyHostBuilder);
}
catch (Exception e)
{
Logger.LogError(
"An error occured while processing 'BuildApp' for '{name}': {e}",
pluginAppStartup.GetType().FullName,
e
);
}
}
}
private async Task HookPluginConfigureAsync()
{
foreach (var pluginAppStartup in PluginStartups)
{
try
{
await pluginAppStartup.ConfigureApplicationAsync(PluginLoadServiceProvider, WebAssemblyHost);
}
catch (Exception e)
{
Logger.LogError(
"An error occured while processing 'ConfigureApp' for '{name}': {e}",
pluginAppStartup.GetType().FullName,
e
);
}
}
foreach (var startup in startups)
startup.ConfigurePlugin(app);
}
}

View File

@@ -1,48 +1,22 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Moonlight.Client.Plugins;
using Moonlight.Shared.Misc;
namespace Moonlight.Client.Startup;
public partial class Startup
public static partial class Startup
{
public ILogger<Startup> Logger { get; private set; }
// WebAssemblyHost
public WebAssemblyHostBuilder WebAssemblyHostBuilder { get; private set; }
public WebAssemblyHost WebAssemblyHost { get; private set; }
// Configuration
public FrontendConfiguration Configuration { get; private set; }
public Task InitializeAsync(IPluginStartup[]? plugins = null)
public static void AddMoonlight(this WebAssemblyHostBuilder builder, IPluginStartup[] startups)
{
PluginStartups = plugins ?? [];
return Task.CompletedTask;
PrintVersion();
builder.AddLogging();
builder.AddBase();
builder.AddAuth();
builder.AddPlugins(startups);
}
public async Task AddMoonlightAsync(WebAssemblyHostBuilder builder)
public static void ConfigureMoonlight(this WebAssemblyHost app, IPluginStartup[] startups)
{
WebAssemblyHostBuilder = builder;
await PrintVersionAsync();
await SetupLoggingAsync();
await LoadConfigurationAsync();
await InitializePluginsAsync();
await RegisterLoggingAsync();
await RegisterBaseAsync();
await RegisterAuthenticationAsync();
await HookPluginBuildAsync();
}
public async Task AddMoonlightAsync(WebAssemblyHost assemblyHost)
{
WebAssemblyHost = assemblyHost;
await HookPluginConfigureAsync();
app.ConfigurePlugins(startups);
}
}

View File

@@ -14,7 +14,6 @@
!rounded-xs
!text-sm
!w-2.5
*:[grid-area:1/1]
*:first:rounded-tl-lg
*:last:rounded-tr-lg
-left-4
@@ -43,18 +42,11 @@ alert-soft
align-bottom
align-middle
animate-bounce
animate-ping
animate-spin
aria-[current='page']:text-bg-soft-primary
avatar
avatar-placeholder
badge
badge-error
badge-info
badge-outline
badge-primary
badge-soft
badge-success
basis-full
bg-background
bg-background/60
bg-base-100
@@ -62,6 +54,7 @@ bg-base-150
bg-base-200
bg-base-200!
bg-base-200/50
bg-base-250
bg-base-300
bg-base-300/45
bg-base-300/50
@@ -157,7 +150,15 @@ disabled
divide-base-150/60
divide-y
divider
drop-shadow
drawer
drawer-body
drawer-bottom
drawer-end
drawer-footer
drawer-header
drawer-start
drawer-title
drawer-top
dropdown
dropdown-active
dropdown-disabled
@@ -217,14 +218,13 @@ gap-y-2.5
gap-y-3
grid
grid-cols-1
grid-cols-4
grid-flow-col
grow
grow-0
h-10
h-2
h-3
h-32
h-36
h-64
h-8
h-auto
@@ -270,7 +270,6 @@ justify-center
justify-end
justify-start
label-text
leading-3
leading-3.5
leading-none
left-0
@@ -296,7 +295,6 @@ link-animated
link-hover
list-disc
list-inside
list-none
loading
loading-sm
loading-spinner
@@ -310,9 +308,10 @@ max-lg:flex-col
max-lg:hidden
max-md:flex-wrap
max-md:justify-center
max-md:w-full
max-sm:hidden
max-w-64
max-w-7xl
max-w-8
max-w-80
max-w-full
max-w-lg
@@ -323,10 +322,16 @@ mb-1
mb-1.5
mb-2
mb-2.5
mb-25
mb-3
mb-4
mb-5
md:flex
md:gap-2
md:hidden!
md:items-center
md:min-w-md
md:navbar-end
md:table-cell
md:text-3xl
me-1
@@ -340,6 +345,7 @@ menu-disabled
menu-dropdown
menu-dropdown-show
menu-horizontal
menu-sm
menu-title
min-h-0
min-h-svh
@@ -376,9 +382,12 @@ mt-5
mt-8
mx-1
mx-auto
my-20
my-3
my-5
my-auto
navbar
navbar-start
object-cover
opacity-0
opacity-100
@@ -392,6 +401,9 @@ overflow-x-auto
overflow-y-auto
overlay-open:duration-50
overlay-open:opacity-100
overlay-open:translate-x-0
overlay-open:translate-y-0
p-0
p-0.5
p-1
p-1.5
@@ -403,6 +415,7 @@ p-5
p-6
p-8
pb-1
pe-1
pin-input
placeholder-base-content/60
pointer-events-auto
@@ -427,7 +440,6 @@ py-2
py-2.5
py-6
radial-progress
radio
range
relative
resize
@@ -455,6 +467,8 @@ selected:select-active
shadow-base-300/20
shadow-lg
shadow-md
shadow-none
shadow-sm
shadow-xs
shrink-0
size-10
@@ -462,8 +476,7 @@ size-12
size-4
size-5
size-8
skeleton
skeleton-animated
size-8.5
sm:auto-cols-max
sm:flex
sm:items-center
@@ -492,7 +505,6 @@ sr-only
stack
static
status
status-error
sticky
success-message
switch
@@ -548,26 +560,32 @@ tooltip
tooltip-content
top-0
top-1/2
top-3
top-full
transform
transition
transition-all
transition-opacity
transition-transform
translate-x-0
translate-x-full
truncate
underline
uppercase
validate
w-0
w-0.5
w-12
w-13
w-20
w-4
w-56
w-6
w-64
w-fit
w-full
whitespace-nowrap
z-10
z-1
z-40
z-50
z-50
z-69
z-70

View File

@@ -64,6 +64,7 @@ badge-outline
badge-primary
badge-soft
badge-success
basis-full
bg-background
bg-background/60
bg-base-100
@@ -71,6 +72,7 @@ bg-base-150
bg-base-200
bg-base-200!
bg-base-200/50
bg-base-250
bg-base-300
bg-base-300/45
bg-base-300/50
@@ -189,6 +191,15 @@ disabled
divide-base-150/60
divide-y
divider
drawer
drawer-body
drawer-bottom
drawer-end
drawer-footer
drawer-header
drawer-start
drawer-title
drawer-top
drop-shadow
dropdown
dropdown-active
@@ -275,6 +286,7 @@ h-14
h-2
h-3
h-32
h-36
h-6
h-64
h-8
@@ -372,7 +384,9 @@ max-lg:flex-col
max-lg:hidden
max-md:flex-wrap
max-md:justify-center
max-md:w-full
max-sm:hidden
max-w-64
max-w-7xl
max-w-8
max-w-80
@@ -386,14 +400,20 @@ mb-1
mb-1.5
mb-2
mb-2.5
mb-25
mb-3
mb-4
mb-5
mb-8
md:col-span-1
md:col-span-6
md:flex
md:gap-2
md:grid-cols-2
md:hidden!
md:items-center
md:min-w-md
md:navbar-end
md:table-cell
md:text-3xl
me-1
@@ -408,6 +428,7 @@ menu-dropdown
menu-dropdown-show
menu-focus
menu-horizontal
menu-sm
menu-title
min-h-0
min-h-full
@@ -452,10 +473,13 @@ mt-8
mx-1
mx-auto
my-2.5
my-20
my-3
my-5
my-8
my-auto
navbar
navbar-start
object-cover
opacity-0
opacity-100
@@ -470,6 +494,9 @@ overflow-x-hidden
overflow-y-auto
overlay-open:duration-50
overlay-open:opacity-100
overlay-open:translate-x-0
overlay-open:translate-y-0
p-0
p-0.5
p-1
p-1.5
@@ -481,6 +508,7 @@ p-5
p-6
p-8
pb-1
pe-1
pe-1.5
pin-input
pin-input-underline
@@ -548,6 +576,7 @@ shadow
shadow-base-300/20
shadow-lg
shadow-md
shadow-none
shadow-sm
shadow-xs
shrink-0
@@ -557,6 +586,7 @@ size-4
size-5
size-7
size-8
size-8.5
skeleton
skeleton-animated
sm:auto-cols-max
@@ -686,6 +716,7 @@ tooltip
tooltip-content
top-0
top-1/2
top-3
top-full
tracking-tight
tracking-wide
@@ -693,7 +724,9 @@ transform
transition
transition-all
transition-opacity
transition-transform
translate-x-0
translate-x-full
truncate
underline
uppercase
@@ -703,9 +736,11 @@ w-0
w-0.5
w-12
w-13
w-20
w-32
w-4
w-56
w-6
w-64
w-8
w-auto
@@ -717,4 +752,6 @@ xl:grid-cols-4
z-1
z-10
z-40
z-50
z-50
z-69
z-70

View File

@@ -1,4 +1,5 @@
@using Moonlight.Client.UI.Partials
@using MoonCore.Blazor.FlyonUi.Drawers
@using Moonlight.Client.UI.Partials
@using MoonCore.Blazor.FlyonUi.Files.Drop
@inherits LayoutComponentBase
@@ -20,6 +21,7 @@
<ToastLauncher/>
<ModalLauncher/>
<DrawerLauncher />
<DropHandler />

View File

@@ -1,11 +1,12 @@
@page "/admin/api"
@using MoonCore.Blazor.FlyonUi.Common
@using MoonCore.Helpers
@using MoonCore.Models
@using Moonlight.Shared.Http.Responses.Admin.ApiKeys
@using MoonCore.Blazor.FlyonUi.Grid
@using MoonCore.Blazor.FlyonUi.Grid.Columns
@using MoonCore.Blazor.FlyonUi.Grid.ToolbarItems
@using MoonCore.Common
@inject HttpApiClient ApiClient
@inject AlertService AlertService
@@ -51,9 +52,7 @@
<DataGrid @ref="Grid"
TGridItem="ApiKeyResponse"
ItemsProvider="ItemsProviderAsync"
EnableFiltering="true"
EnablePagination="true">
ItemSource="ItemSource">
<PropertyColumn Field="x => x.Id" Sortable="true" />
<PropertyColumn Field="x => x.Description" />
<TemplateColumn Sortable="true" Title="Expires At">
@@ -91,27 +90,25 @@
@code
{
private DataGrid<ApiKeyResponse> Grid;
private async Task<DataGridItemResult<ApiKeyResponse>> ItemsProviderAsync(DataGridItemRequest request)
{
var query = $"?startIndex={request.StartIndex}&count={request.Count}";
if (!string.IsNullOrEmpty(request.SortColumn))
private ItemSource<ApiKeyResponse> ItemSource => ItemSourceFactory.From(LoadItemsAsync);
private async Task<IEnumerable<ApiKeyResponse>> LoadItemsAsync(
int startIndex, int count, string? filter, SortOption? sortOption
)
{
var query = $"?startIndex={startIndex}&count={count}";
if (sortOption != null)
{
var dir = request.SortDirection == SortState.Descending ? "desc" : "asc";
query += $"&orderBy={request.SortColumn}&orderByDir={dir}";
var dir = sortOption.Direction == SortDirection.Descending ? "desc" : "asc";
query += $"&orderBy={sortOption.Column}&orderByDir={dir}";
}
if (!string.IsNullOrEmpty(request.Filter))
query += $"&filter={request.Filter}";
if (!string.IsNullOrEmpty(filter))
query += $"&filter={filter}";
var data = await ApiClient.GetJson<CountedData<ApiKeyResponse>>($"api/admin/apikeys{query}");
return new()
{
Items = data.Items,
TotalCount = data.TotalCount
};
return await ApiClient.GetJson<CountedData<ApiKeyResponse>>($"api/admin/apikeys{query}");
}
private async Task DeleteAsync(ApiKeyResponse apiKeyResponse)

View File

@@ -2,12 +2,13 @@
@using System.Text.Json
@using Microsoft.AspNetCore.Authorization
@using MoonCore.Blazor.FlyonUi.Common
@using MoonCore.Blazor.FlyonUi.Grid
@using MoonCore.Blazor.FlyonUi.Grid.Columns
@using MoonCore.Blazor.FlyonUi.Grid.ToolbarItems
@using MoonCore.Blazor.FlyonUi.Helpers
@using MoonCore.Common
@using MoonCore.Helpers
@using MoonCore.Models
@using Moonlight.Client.Models
@using Moonlight.Client.Services
@using Moonlight.Shared.Http.Requests.Admin.Sys.Theme
@@ -30,11 +31,9 @@
<div class="my-8">
<DataGrid TGridItem="ThemeResponse"
ItemsProvider="ItemsProviderAsync"
EnableFiltering="true"
EnablePagination="true">
<PropertyColumn Field="x => x.Id" Sortable="true" />
ItemSource="ItemSource">
<PropertyColumn Field="x => x.Id" Sortable="true"/>
<TemplateColumn Title="Name" Sortable="true">
<td>
<div class="flex items-center">
@@ -47,9 +46,9 @@
</div>
</td>
</TemplateColumn>
<PropertyColumn Field="x => x.Version" Sortable="true" />
<PropertyColumn Field="x => x.Author" />
<PropertyColumn Field="x => x.Version" Sortable="true"/>
<PropertyColumn Field="x => x.Author"/>
<TemplateColumn>
<td>
<div class="flex justify-end">
@@ -69,7 +68,8 @@
</a>
}
<a @onclick="() => ExportAsync(context)" @onclick:preventDefault href="#" class="flex items-center mr-2 sm:mr-3">
<a @onclick="() => ExportAsync(context)" @onclick:preventDefault href="#"
class="flex items-center mr-2 sm:mr-3">
<i class="text-success icon-download me-1"></i>
<span class="text-success">Export</span>
</a>
@@ -90,7 +90,7 @@
<i class="icon-file-up"></i>
Import
</label>
<InputFile OnChange="ImportAsync" id="import-theme" class="hidden" multiple />
<InputFile OnChange="ImportAsync" id="import-theme" class="hidden" multiple/>
<a href="/admin/system/customisation/themes/create" class="btn btn-primary">Create</a>
</TemplateToolbarItem>
</DataGrid>
@@ -104,32 +104,29 @@
@code
{
private DataGrid<ThemeResponse> Grid;
private async Task<DataGridItemResult<ThemeResponse>> ItemsProviderAsync(DataGridItemRequest request)
{
var query = $"?startIndex={request.StartIndex}&count={request.Count}";
private ItemSource<ThemeResponse> ItemSource => ItemSourceFactory.From(LoadItemsAsync);
if (!string.IsNullOrEmpty(request.SortColumn))
private async Task<IEnumerable<ThemeResponse>> LoadItemsAsync(
int startIndex, int count, string? filter, SortOption? sortOption
)
{
var query = $"?startIndex={startIndex}&count={count}";
if (sortOption != null)
{
var dir = request.SortDirection == SortState.Descending ? "desc" : "asc";
query += $"&orderBy={request.SortColumn}&orderByDir={dir}";
var dir = sortOption.Direction == SortDirection.Descending ? "desc" : "asc";
query += $"&orderBy={sortOption.Column}&orderByDir={dir}";
}
if (!string.IsNullOrEmpty(request.Filter))
query += $"&filter={request.Filter}";
var data = await ApiClient.GetJson<CountedData<ThemeResponse>>($"api/admin/system/customisation/themes{query}");
if (!string.IsNullOrEmpty(filter))
query += $"&filter={filter}";
return new()
{
Items = data.Items,
TotalCount = data.TotalCount
};
return await ApiClient.GetJson<CountedData<ThemeResponse>>($"api/admin/system/customisation/themes{query}");
}
private async Task ImportAsync(InputFileChangeEventArgs eventArgs)
{
if(eventArgs.FileCount < 1)
if (eventArgs.FileCount < 1)
return;
var files = eventArgs.GetMultipleFiles();
@@ -154,7 +151,7 @@
await using var stream = file.OpenReadStream(maxFileSize);
var themeTransfer = await JsonSerializer.DeserializeAsync<ThemeTransferModel>(stream);
stream.Close();
if (themeTransfer == null)
@@ -174,7 +171,7 @@
});
await ToastService.SuccessAsync("Successfully imported theme", theme.Name);
await Grid.RefreshAsync();
}
catch (Exception e)
@@ -200,7 +197,7 @@
{
WriteIndented = true
});
var fileName = $"{transfer.Name.Replace(" ", string.Empty).Trim()}.json";
await DownloadService.DownloadAsync(fileName, json);

View File

@@ -1,10 +1,11 @@
@page "/admin/users"
@using MoonCore.Blazor.FlyonUi.Common
@using MoonCore.Helpers
@using MoonCore.Models
@using Moonlight.Shared.Http.Responses.Admin.Users
@using MoonCore.Blazor.FlyonUi.Grid
@using MoonCore.Blazor.FlyonUi.Grid.Columns
@using MoonCore.Common
@inject HttpApiClient ApiClient
@inject AlertService AlertService
@@ -19,14 +20,11 @@
</div>
<DataGrid @ref="Grid"
TGridItem="UserResponse"
ItemsProvider="ItemsProviderAsync"
EnableFiltering="true"
EnablePagination="true">
<PropertyColumn Field="x => x.Id" Sortable="true" />
<PropertyColumn Field="x => x.Username" Sortable="true" />
<PropertyColumn Field="x => x.Email" Sortable="true" />
ItemSource="ItemSource">
<PropertyColumn Field="x => x.Id" Sortable="true"/>
<PropertyColumn Field="x => x.Username" Sortable="true"/>
<PropertyColumn Field="x => x.Email" Sortable="true"/>
<TemplateColumn>
<td>
<div class="flex justify-end">
@@ -45,29 +43,26 @@
@code
{
private DataGrid<UserResponse> Grid;
private async Task<DataGridItemResult<UserResponse>> ItemsProviderAsync(DataGridItemRequest request)
{
var query = $"?startIndex={request.StartIndex}&count={request.Count}";
private ItemSource<UserResponse> ItemSource => ItemSourceFactory.From(LoadItemsAsync);
if (!string.IsNullOrEmpty(request.SortColumn))
private async Task<IEnumerable<UserResponse>> LoadItemsAsync(
int startIndex, int count, string? filter, SortOption? sortOption
)
{
var query = $"?startIndex={startIndex}&count={count}";
if (sortOption != null)
{
var dir = request.SortDirection == SortState.Descending ? "desc" : "asc";
query += $"&orderBy={request.SortColumn}&orderByDir={dir}";
var dir = sortOption.Direction == SortDirection.Descending ? "desc" : "asc";
query += $"&orderBy={sortOption.Column}&orderByDir={dir}";
}
if (!string.IsNullOrEmpty(request.Filter))
query += $"&filter={request.Filter}";
if (!string.IsNullOrEmpty(filter))
query += $"&filter={filter}";
var data = await ApiClient.GetJson<CountedData<UserResponse>>($"api/admin/users{query}");
return new()
{
Items = data.Items,
TotalCount = data.TotalCount
};
return await ApiClient.GetJson<CountedData<UserResponse>>($"api/admin/users{query}");
}
private async Task DeleteAsync(UserResponse response)
{
await AlertService.ConfirmDangerAsync(