mirror of
https://github.com/aliasvault/aliasvault.git
synced 2025-12-23 22:28:22 -05:00
Add ApexChart service and integrate dark mode (#641)
This commit is contained in:
19
.gitattributes
vendored
19
.gitattributes
vendored
@@ -1,12 +1,17 @@
|
||||
# Set default behavior to automatically normalize line endings
|
||||
* text=auto
|
||||
|
||||
# Shell scripts should always use LF (Unix-style) line endings
|
||||
# Common files should always use LF (Unix-style) line endings
|
||||
*.sh text eol=lf
|
||||
|
||||
# Batch scripts should always use CRLF (Windows-style) line endings
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
*.cs text eol=lf
|
||||
*.razor text eol=lf
|
||||
*.css text eol=lf
|
||||
*.html text eol=lf
|
||||
*.js text eol=lf
|
||||
*.json text eol=lf
|
||||
*.xml text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.yaml text eol=lf
|
||||
|
||||
# Docker files should use LF
|
||||
Dockerfile text eol=lf
|
||||
@@ -17,6 +22,10 @@ docker-compose*.yml text eol=lf
|
||||
*.config text eol=lf
|
||||
.env* text eol=lf
|
||||
|
||||
# Batch scripts should always use CRLF (Windows-style) line endings
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
|
||||
# Documentation should be normalized
|
||||
*.md text
|
||||
*.txt text
|
||||
@@ -45,6 +45,8 @@
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
await RefreshData();
|
||||
|
||||
@@ -12,9 +12,9 @@ using AliasVault.Admin.Services;
|
||||
using AliasVault.Auth;
|
||||
using AliasVault.RazorComponents.Models;
|
||||
using AliasVault.RazorComponents.Services;
|
||||
using ApexCharts;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
/// <summary>
|
||||
@@ -73,6 +73,12 @@ public abstract class MainBase : OwningComponentBase
|
||||
[Inject]
|
||||
protected ConfirmModalService ConfirmModalService { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ApexChartService.
|
||||
/// </summary>
|
||||
[Inject]
|
||||
protected IApexChartService ApexChartService { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the injected JSRuntime instance.
|
||||
/// </summary>
|
||||
@@ -96,6 +102,18 @@ public abstract class MainBase : OwningComponentBase
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Home", Url = NavigationService.BaseUri });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
// Update default ApexCharts chart color based on the dark mode setting.
|
||||
await SetDefaultApexChartOptionsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the username from the authentication state asynchronously.
|
||||
/// </summary>
|
||||
@@ -104,4 +122,50 @@ public abstract class MainBase : OwningComponentBase
|
||||
{
|
||||
return UserService.User().UserName ?? "[Unknown]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default ApexCharts chart color based on the dark mode setting.
|
||||
/// </summary>
|
||||
private async Task SetDefaultApexChartOptionsAsync()
|
||||
{
|
||||
var darkMode = await JsInvokeService.RetryInvokeWithResultAsync<bool>("isDarkMode", TimeSpan.Zero, 5);
|
||||
var options = new ApexChartBaseOptions
|
||||
{
|
||||
Chart = new Chart
|
||||
{
|
||||
ForeColor = darkMode ? "#bbb" : "#555",
|
||||
},
|
||||
Fill = new Fill
|
||||
{
|
||||
Colors = darkMode ?
|
||||
[
|
||||
"#FFB84D", // Bright gold
|
||||
"#8B6CB9", // Darker Purple
|
||||
"#68A890", // Darker Sea Green
|
||||
"#CD5C5C", // Darker Coral
|
||||
"#4F94CD", // Darker Sky Blue
|
||||
"#BA55D3", // Darker Plum
|
||||
"#CDC673", // Darker Khaki
|
||||
"#6B8E23", // Darker Sage Green
|
||||
"#CD853F", // Darker Burlywood
|
||||
"#7B68EE", // Darker Slate Blue
|
||||
]
|
||||
:
|
||||
[
|
||||
"#FFB366", // Light Orange
|
||||
"#B19CD9", // Light Purple
|
||||
"#98D8C1", // Light Sea Green
|
||||
"#F08080", // Light Coral
|
||||
"#87CEEB", // Sky Blue
|
||||
"#DDA0DD", // Plum
|
||||
"#F0E68C", // Khaki
|
||||
"#9CB071", // Sage Green
|
||||
"#DEB887", // Burlywood
|
||||
"#A7A1E8", // Light Slate Blue
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
await ApexChartService.SetGlobalOptionsAsync(options, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ using AliasVault.Logging;
|
||||
using AliasVault.RazorComponents.Services;
|
||||
using AliasVault.Shared.Models.Configuration;
|
||||
using AliasVault.Shared.Server.Services;
|
||||
using ApexCharts;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
@@ -61,6 +62,7 @@ builder.Services.AddScoped<AuthLoggingService>();
|
||||
builder.Services.AddScoped<ConfirmModalService>();
|
||||
builder.Services.AddScoped<ServerSettingsService>();
|
||||
builder.Services.AddSingleton(new VersionedContentService(Directory.GetCurrentDirectory() + "/wwwroot"));
|
||||
builder.Services.AddApexCharts();
|
||||
|
||||
builder.Services.AddAuthentication(options =>
|
||||
{
|
||||
|
||||
@@ -50,4 +50,44 @@ public class JsInvokeService(IJSRuntime js)
|
||||
|
||||
// Optionally log that the JS function could not be called after maxAttempts
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoke a JavaScript function with retry and exponential backoff that returns a value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of value to return from the JavaScript function.</typeparam>
|
||||
/// <param name="functionName">The JS function name to call.</param>
|
||||
/// <param name="initialDelay">Initial delay before calling the function.</param>
|
||||
/// <param name="maxAttempts">Maximum attempts before giving up.</param>
|
||||
/// <param name="args">Arguments to pass on to the javascript function.</param>
|
||||
/// <returns>The value returned from the JavaScript function.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the JS function could not be called after all attempts.</exception>
|
||||
public async Task<TValue> RetryInvokeWithResultAsync<TValue>(string functionName, TimeSpan initialDelay, int maxAttempts, params object[] args)
|
||||
{
|
||||
TimeSpan delay = initialDelay;
|
||||
|
||||
for (int attempt = 1; attempt <= maxAttempts; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isDefined = await js.InvokeAsync<bool>("isFunctionDefined", functionName);
|
||||
if (isDefined)
|
||||
{
|
||||
return await js.InvokeAsync<TValue>(functionName, args);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Optionally log the exception
|
||||
}
|
||||
|
||||
// Wait for the delay before the next attempt
|
||||
await Task.Delay(delay);
|
||||
|
||||
// Exponential backoff: double the delay for the next attempt
|
||||
delay = TimeSpan.FromTicks(delay.Ticks * 2);
|
||||
}
|
||||
|
||||
// All attempts failed, throw an exception
|
||||
throw new InvalidOperationException($"Failed to invoke JavaScript function '{functionName}' after {maxAttempts} attempts.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1969,10 +1969,6 @@ video {
|
||||
background-color: rgb(113 63 18 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.dark\:bg-yellow-900\/30:is(.dark *) {
|
||||
background-color: rgb(113 63 18 / 0.3);
|
||||
}
|
||||
|
||||
.dark\:bg-opacity-80:is(.dark *) {
|
||||
--tw-bg-opacity: 0.8;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ window.initTopMenu = function() {
|
||||
initDarkModeSwitcher();
|
||||
};
|
||||
|
||||
window.isDarkMode = function() {
|
||||
return document.documentElement.classList.contains('dark');
|
||||
};
|
||||
|
||||
window.registerClickOutsideHandler = (dotNetHelper) => {
|
||||
document.addEventListener('click', (event) => {
|
||||
const menu = document.getElementById('userMenuDropdown');
|
||||
|
||||
Reference in New Issue
Block a user