4 Commits

Author SHA1 Message Date
Samuele Lorefice
4c85fee004 Settings WIP 2025-09-09 22:01:55 +02:00
Samuele Lorefice
3039fdfd39 General Cleanup (and async/awaiting rules enforcing) 2025-09-09 19:32:19 +02:00
Samuele Lorefice
f2db2e5204 Added User Page 2025-09-09 18:49:20 +02:00
Samuele Lorefice
021e186611 Small Refactor 2025-09-09 17:30:33 +02:00
19 changed files with 271 additions and 82 deletions

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EditMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@@ -21,6 +21,7 @@
<body>
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
<script src="lib/js/bootstrap.bundle.min.js"></script>
@* ReSharper disable once Html.PathError *@
<script src="_framework/blazor.web.js"></script>
</body>

View File

@@ -6,7 +6,7 @@
<div class="card mb-3">
<div class="card-body align-items-center">
<div class="d-flex flex-row flex-grow-1 align-items-center">
<i class="bi bi-folder2 ms-3 mx-1"></i><p class="mx-1 mb-0 @Display">@(Folder.BasePath ?? "Unknown Folder")</p>
<i class="bi bi-folder2 ms-3 mx-1"></i><p class="mx-1 mb-0 @Display">@Folder.BasePath</p>
<input class="form-control form-control-sm mx-1 @Edit" type="text" @bind="Folder.BasePath"/>
<button class="btn btn-sm btn-success @Edit" @onclick="OnConfirmEdit"><i class="bi bi-check"></i></button>
<div class="ms-auto form-switch">
@@ -19,7 +19,7 @@
</div>
@code {
bool editing = false;
bool editing;
string Display => editing ? "d-none" : "";
string Edit => editing ? "" : "d-none";

View File

@@ -16,7 +16,7 @@
</div>
<div class="card-body">
<div class="progress mb-2" role="progressbar">
<div class="@BGColor" style="width: @(Job.Progress*100)%">@(Job.Progress*100)%</div>
<div class="@BgColor" style="width: @(Job.Progress*100)%">@(Job.Progress*100)%</div>
</div>
<p class="card-text"><i class="me-2 bi bi-stopwatch"></i>
@switch (Job.Status) {
@@ -56,7 +56,7 @@
@code {
[Parameter] public JobStatusDto Job { get; set; }
[Parameter] public required JobStatusDto Job { get; set; }
string BorderColor => Job.Status switch {
EJobStatus.Queued => "border-secondary",
EJobStatus.Running => "border-primary",
@@ -73,7 +73,7 @@
EJobStatus.Failed => "text-bg-danger",
_ => "text-bg-dark",
};
string BGColor => Job.Status switch {
string BgColor => Job.Status switch {
EJobStatus.Queued => "progress-bar text-bg-secondary",
EJobStatus.Running => "progress-bar text-bg-primary",
EJobStatus.Waiting => "progress-bar text-bg-info",

View File

@@ -2,10 +2,10 @@
@using Butter.Types
@inherits LayoutComponentBase
@inject NavigationManager navigation
@inject UserService userService
@inject LoginService loginService
@inject ProtectedLocalStorage localStorage
@inject NavigationManager Navigation
@inject UserService UserService
@inject LoginService LoginService
@inject ProtectedLocalStorage LocalStorage
<div class="navbar navbar-expand-sm bg-dark-subtle">
<div class="container-xl">
@@ -47,19 +47,25 @@
<i class="bi bi-search-heart"></i>
</button>
</div>
<div class="mx-2">@loginService.LoggedUser?.Username</div>
<div class="mx-2">@LoginService.LoggedUser?.Username</div>
<div class="btn-group">
<button class="btn btn-lg btn-outline-light" type="button">
<button class="btn btn-outline-light" type="button">
<i class="bi bi-person-circle"></i>
</button>
<button class="btn btn-lg btn-outline-light dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown"></button>
<button class="btn btn-outline-light dropdown-toggle dropdown-toggle-split"
data-bs-toggle="dropdown"></button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#"><i class="bi bi-person-lines-fill"></i> Profile</a></li>
@if(Admin) {
<li><a class="dropdown-item" @onclick="OnSettingsClick"><i class="bi bi-gear-wide-connected"></i> Settings</a></li>
<li><a class="dropdown-item" @onclick="OnJobsClick"><i class="bi bi-kanban-fill"></i> Jobs</a></li>
<li><a class="dropdown-item" @onclick="OnProfileClick"><i class="bi bi-person-lines-fill"></i> Profile</a></li>
@if (Admin) {
<li><a class="dropdown-item" @onclick="OnSettingsClick"><i
class="bi bi-gear-wide-connected"></i> Settings</a></li>
<li><a class="dropdown-item" @onclick="OnSettings1Click"><i
class="bi bi-gear-wide-connected"></i> Settings 1</a></li>
<li><a class="dropdown-item" @onclick="OnJobsClick"><i class="bi bi-kanban-fill"></i>
Jobs</a></li>
}
<li><a class="dropdown-item" @onclick="OnLogout"><i class="bi bi-door-closed"></i> Logout</a></li>
<li><a class="dropdown-item" @onclick="OnLogout"><i class="bi bi-door-closed"></i>
Logout</a></li>
</ul>
</div>
}
@@ -69,15 +75,15 @@
</div>
@code{
bool LoggedIn => loginService.IsLoggedIn;
bool Admin => loginService.LoggedUser?.AccessLevel == EAccessLevel.Admin;
bool isConnected;
bool needsUpdate;
bool LoggedIn => LoginService.IsLoggedIn;
bool Admin => LoginService.LoggedUser?.AccessLevel == EAccessLevel.Admin;
bool isConnected;
bool needsUpdate;
protected override void OnInitialized() {
base.OnInitialized();
loginService.LoggedUserChanged += (sender, dto) => needsUpdate = true;
LoadStateAsync().ConfigureAwait(false);
protected async override Task OnInitializedAsync() {
LoginService.LoggedUserChanged += (_, _) => needsUpdate = true;
await base.OnInitializedAsync();
//await LoadStateAsync();
}
protected async override Task OnAfterRenderAsync(bool firstRender) {
@@ -90,29 +96,34 @@
}
private async Task LoadStateAsync() {
var auth = await localStorage.GetAsync<AuthInfo>("auth");
var auth = await LocalStorage.GetAsync<AuthInfo>("auth");
if (auth.Success == false) return;
loginService.AuthInfo = auth.Value;
loginService.LoggedUser = await userService.GetUserAsync();//.ConfigureAwait(false);
LoginService.AuthInfo = auth.Value;
LoginService.LoggedUser = await UserService.GetUserAsync(); //.ConfigureAwait(false);
}
async Task OnLoginClick() {
loginService.LoggedUser = await userService.GetUserAsync();
LoginService.LoggedUser = await UserService.GetUserAsync();
if (loginService.LoggedUser != null) needsUpdate = true;
else navigation.NavigateTo("/login");
if (LoginService.LoggedUser != null) needsUpdate = true;
else Navigation.NavigateTo("/login");
}
void OnRegisterClick() => navigation.NavigateTo("/register");
void OnRegisterClick() => Navigation.NavigateTo("/register");
void OnSettingsClick() => navigation.NavigateTo("/settings");
void OnJobsClick() => navigation.NavigateTo("/jobs");
void OnSettingsClick() => Navigation.NavigateTo("/settings");
void OnJobsClick() => Navigation.NavigateTo("/jobs");
async Task OnLogout() {
_ = loginService.Logout();
await localStorage.DeleteAsync("auth");
navigation.NavigateTo("/", true);
_ = LoginService.Logout();
await LocalStorage.DeleteAsync("auth");
Navigation.NavigateTo("/", true);
}
void OnSettings1Click() => Navigation.NavigateTo("/Settings1");
void OnProfileClick() => Navigation.NavigateTo($"/User/{LoginService.AuthInfo?.UserId}", true);
}

View File

@@ -1,12 +1,11 @@
@page "/"
@using Butter.Dtos;
@using MilkStream.Components.Layout
@using MilkStream.Services
@inject NavigationManager navigationManager
@inject LoginService loginService
@inject ProtectedSessionStorage sessionStorage
@inject ProtectedLocalStorage localStorage
@inject NavigationManager NavigationManager
@inject LoginService LoginService
@inject ProtectedSessionStorage SessionStorage
@inject ProtectedLocalStorage LocalStorage
<PageTitle>Home</PageTitle>
@@ -17,7 +16,7 @@
@if (mediaList.Count == 0) {
<div class="border-warning border-5 border-opacity-100 rounded-3 p-3 m-5 bg-warning-subtle text-center">
<h2 class="text-warning">No media available</h2>
@if (loginService.IsLoggedIn) {
@if (LoginService.IsLoggedIn) {
<p class="text-warning-emphasis">You are logged in, but there is no media available at the moment.</p>
} else {
<p class="text-warning-emphasis">Please <a href="/login" class="link-warning">log-in</a> to be able to browse any media.</p>
@@ -41,7 +40,7 @@
protected async override Task OnAfterRenderAsync(bool firstRender) {
if (firstRender) {
loginService.LoggedUserChanged += (sender, dto) => StateHasChanged();
LoginService.LoggedUserChanged += (_, _) => StateHasChanged();
//do nothing for now
await Task.Delay(1);
isLoading = false;

View File

@@ -20,13 +20,13 @@
@code {
PeriodicTimer timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
List<JobStatusDto> jobs = new();
readonly PeriodicTimer timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
List<JobStatusDto> jobs = [];
protected async override Task OnInitializedAsync() {
LoginService.LoggedUserChanged += async (_, _) => {
jobs = await JobsService.GetJobs();
RefreshJobs();
await RefreshJobs();
StateHasChanged();
};
if (LoginService.IsLoggedIn) {
@@ -43,12 +43,12 @@
}
}
void LaunchScan() => JobsService.StartJob(EJobType.FileSystemScan);
Task LaunchScan() => JobsService.StartJob(EJobType.FileSystemScan);
void LaunchThumb() => JobsService.StartJob(EJobType.ThumbnailGeneration);
Task LaunchThumb() => JobsService.StartJob(EJobType.ThumbnailGeneration);
void LaunchIntegrityCheck() => JobsService.StartJob(EJobType.IntegrityCheck);
Task LaunchIntegrityCheck() => JobsService.StartJob(EJobType.IntegrityCheck);
void LaunchMeta() => JobsService.StartJob(EJobType.MetadataExtraction);
Task LaunchMeta() => JobsService.StartJob(EJobType.MetadataExtraction);
}

View File

@@ -37,10 +37,10 @@
<div class="">DATETIME NOT IMPLEMENTED Value: @option.Value</div>
break;
case DisplayType.TimePicker:
<SettingRange Setting="option" minValue="Convert.ToInt32(option.Options?[0])" maxValue="Convert.ToInt32(option.Options?[1])" Step="Convert.ToInt32(option.Options?[2])"/>
<SettingRange Setting="option" MinValue="Convert.ToInt32(option.Options?[0])" MaxValue="Convert.ToInt32(option.Options?[1])" Step="Convert.ToInt32(option.Options?[2])"/>
break;
case DisplayType.Po2W:
<SettingRange Setting="option" IsPowerOfTwo="true" minValue="Convert.ToInt32(option.Options?[0])" maxValue="Convert.ToInt32(option.Options?[1])" Step="Convert.ToInt32(option.Options?[2])"/>
<SettingRange Setting="option" IsPowerOfTwo="true" MinValue="Convert.ToInt32(option.Options?[0])" MaxValue="Convert.ToInt32(option.Options?[1])" Step="Convert.ToInt32(option.Options?[2])"/>
break;
}
}

View File

@@ -0,0 +1,94 @@
@page "/Settings1"
@using Butter.Dtos.Folder
@using Butter.Dtos.Settings
@using Butter.Settings
@using Butter.Types
@using MilkStream.Components.SettingBoxes
@using MilkStream.Services
@using System.Linq
@inject NavigationManager NavigationManager
@inject LoginService LoginService
@inject SettingsService SettingsService
@inject FoldersService FoldersService
<h3>Settings</h3>
@if (areSettingsLoading || settings is {Count: 0}) {
<LoadSpinner/>
} else {
<div class="row row-cols-2">
<div class="col">
<!--Folder Scan Enabled-->
<SettingSwitch Setting="settings!.FirstOrDefault(s => s.Name == Butter.Settings.Settings.FolderScanEnabled.AsString())"/>
</div>
<div class="col">
<!--Folder Scan Interval-->
<SettingRange Setting="settings!.FirstOrDefault(s => s.Name == Butter.Settings.Settings.FolderScanInterval.AsString())" MinValue="5" MaxValue="360" Step="5"/>
</div>
<div class="col">
<!--File Upload Enabled-->
<SettingSwitch Setting="settings!.FirstOrDefault(s => s.Name == Butter.Settings.Settings.FileUploadEnabled.AsString())"/>
</div>
<div class="col">
<!--File Upload Max Size-->
<SettingRange Setting="settings!.FirstOrDefault(s => s.Name == Butter.Settings.Settings.FileUploadMaxSize.AsString())" MinValue="1024" MaxValue="1000000" Step="1000"/>
</div>
<div class="col">
<!--Thumbnail Path-->
<SettingString Setting="settings!.FirstOrDefault(s => s.Name == Butter.Settings.Settings.ThumbnailPath.AsString())"/>
</div>
<div class="col">
<!--Thumbnail Size-->
<SettingRange Setting="settings!.FirstOrDefault(s => s.Name == Butter.Settings.Settings.ThumbnailSize.AsString())" IsPowerOfTwo="true" MinValue="8" MaxValue="12" Step="1"/>
</div>
<div class="col">
<!--Previews Path-->
<SettingString Setting="settings!.FirstOrDefault(s => s.Name == Butter.Settings.Settings.PreviewPath.AsString())"/>
</div>
<div class="col">
<!--Previews Size-->
<SettingRange Setting="settings!.FirstOrDefault(s => s.Name == Butter.Settings.Settings.PreviewSize.AsString())" IsPowerOfTwo="true" MinValue="8" MaxValue="14" Step="1"/>
</div>
<div class="col">
<!--Max concurrent jobs -->
<SettingRange Setting="settings!.FirstOrDefault(s => s.Name == Butter.Settings.Settings.MaxConcurrentJobs.AsString())" MinValue="1" MaxValue="32" Step="1"/>
</div>
<div class="col">
<!-- User registration enabled -->
<SettingSwitch Setting="settings!.FirstOrDefault(s => s.Name == Butter.Settings.Settings.UserRegistrationEnabled.AsString())"/>
</div>
</div>
<div>
<button class="btn btn-outline-primary my-2" @onclick="SaveSettings"><i class="bi bi-floppy2 mx-1"></i>Save Changes</button>
</div>
}
<h3>Folders</h3>
@code {
bool areSettingsLoading = true;
List<SettingDto>? settings = new();
protected async override Task OnInitializedAsync() {
if (LoginService.IsLoggedIn) {
await LoadSettings();
} else {
LoginService.AuthInfoChanged += async (_, _) => {
await LoadSettings();
};
}
}
async Task LoadSettings() {
settings = await SettingsService.GetAllSettings();
if(settings != null) areSettingsLoading = false;
StateHasChanged();
}
void SaveSettings() {
if (settings != null) {
SettingsService.UpdateSettings(settings);
}
}
}

View File

@@ -0,0 +1,62 @@
@page "/User/{UserId:guid}"
@using Butter.Dtos.User
@using Butter.Types
@using MilkStream.Services
@inject LoginService LoginService
@inject UserService UserService
<h3>@(user?.Username ?? "Username")</h3>
<div class="row">
@if (isSelf || AdminAccess) {
<div class="col-2"><i class="bi bi-envelope-at"></i> EMail:</div>
<div class="col-2">@user?.Email</div>
}
</div>
<div class="row">
<div class="col-2"><i class="bi bi-person-fill-lock"></i> Role:</div>
<div class="col-2">@user?.AccessLevel.ToString()</div>
</div>
<div class="row">
<div class="col-2"><i class="bi bi-calendar-date"></i> Creation Date:</div>
<div class="col-2">@user?.CreatedAt</div>
</div>
<div class="row">
<div class="col-2"><i class="bi bi-door-open"></i> Last Login:</div>
<div class="col-2">@(user?.LastLogin.ToString() ?? "Never")</div>
</div>
<div class="row">
<div class="col-2"><i class="bi bi-ban"></i>Banned:</div>
<div class="col-2">@(user is {IsBanned: true} ? $"Yes ({user.BannedAt.ToString()})" : "No")</div>
</div>
<div class="row">
@if (AdminAccess) {
<div class="col-2"><i class="bi bi-trash3"></i>Deleted:</div>
<div class="col-2">@(user is not {DeletedAt: null} ? user!.DeletedAt.ToString() : "No")</div>
}
</div>
@code {
[Parameter]
public Guid UserId { get; set; }
bool isSelf;
bool AdminAccess => LoginService.LoggedUser?.AccessLevel == EAccessLevel.Admin;
UserInfoDto? user;
protected async override Task OnInitializedAsync() {
await base.OnInitializedAsync();
if (LoginService.IsLoggedIn) await LoadUser();
LoginService.AuthInfoChanged += async (_, _) => await LoadUser();
}
async Task LoadUser() {
if (UserId == LoginService.AuthInfo?.UserId) isSelf = true;
user = await UserService.GetUserAsync();
StateHasChanged();
}
}

View File

@@ -5,13 +5,13 @@
@code {
[Parameter]
public required SettingDto Setting { get; set; }
public required SettingDto? Setting { get; set; }
protected virtual SettingDto GetSetting() {
protected virtual SettingDto? GetSetting() {
return Setting;
}
protected override void OnInitialized() {
SettingsService.BeginSave += (sender, args) => SettingsService.UpdateSetting(GetSetting());
SettingsService.BeginSave += (_, _) => SettingsService.UpdateSetting(GetSetting());
}
}

View File

@@ -19,15 +19,16 @@
private float currentValue;
protected override void OnInitialized() {
SettingsService.BeginSave += (sender, args) => SettingsService.UpdateSetting(GetSetting());
if (float.TryParse(Setting.Value, out float value)) {
SettingsService.BeginSave += (_, _) => SettingsService.UpdateSetting(GetSetting());
if (float.TryParse(Setting?.Value, out float value)) {
currentValue = value;
} else {
currentValue = 0; // Default value if parsing fails
}
}
protected override SettingDto GetSetting() {
protected override SettingDto? GetSetting() {
if (Setting == null) return null;
Setting.Value = currentValue.ToString(CultureInfo.InvariantCulture);
return Setting;
}

View File

@@ -1,6 +1,5 @@
@using Butter.Dtos.Settings
@using MilkStream.Services
@using System.Globalization
@inherits SettingBox
@inject SettingsService SettingsService
@@ -10,30 +9,35 @@
<p class="card-text">@(Setting?.Description ?? "No description provided")</p>
<div class="d-flex">
<div class="mx-2">@(IsPowerOfTwo ? Math.Pow(2, currentValue) : currentValue)</div>
<input type="range" id="@Setting?.Name" class="form-range" @bind="@currentValue" min="@minValue" max="@maxValue"/>
<input type="range" id="@Setting?.Name" class="form-range" @bind="@currentValue" min="@MinValue" max="@MaxValue"/>
</div>
</div>
</div>
@code {
[Parameter]
public int minValue {get; set;} = 0;
public int MinValue {get; set;}
[Parameter]
public int maxValue {get; set;} = 100;
public int MaxValue {get; set;} = 100;
[Parameter]
public bool IsPowerOfTwo {get; set;} = false;
public bool IsPowerOfTwo {get; set;}
[Parameter]
public int Step {get; set;} = 1;
private int currentValue;
protected override void OnInitialized() {
SettingsService.BeginSave += (sender, args) => SettingsService.UpdateSetting(GetSetting());
if (int.TryParse(Setting.Value, out int value)) { currentValue = value; } else {
currentValue = 0; // Default value if parsing fails
SettingsService.BeginSave += (_, _) => SettingsService.UpdateSetting(GetSetting());
if (IsPowerOfTwo) {
var converted = int.TryParse(Setting?.Value, out int value);
currentValue = converted ? (int)Math.Log2(value) : 0; // Default value if parsing fails
} else {
currentValue = int.TryParse(Setting?.Value, out int value) ? value : 0; // Default value if parsing fails
}
}
protected override SettingDto GetSetting() {
protected override SettingDto? GetSetting() {
if (Setting == null) return null;
Setting.Value = IsPowerOfTwo ? ((int)Math.Pow(2, currentValue)).ToString() : currentValue.ToString();
return Setting;
}

View File

@@ -14,15 +14,16 @@
</div>
@code {
private string currentValue;
private string currentValue = string.Empty;
protected override void OnInitialized() {
base.OnInitialized();
SettingsService.BeginSave += (sender, args) => SettingsService.UpdateSetting(GetSetting());
SettingsService.BeginSave += (_, _) => SettingsService.UpdateSetting(GetSetting());
currentValue = Setting?.Value ?? string.Empty;
}
protected override SettingDto GetSetting() {
protected override SettingDto? GetSetting() {
if (Setting == null) return null;
Setting.Value = currentValue;
return Setting;
}

View File

@@ -1,5 +1,4 @@
@using Butter.Dtos.Settings
@using Butter.Settings
@using MilkStream.Services
@inherits SettingBox
@inject SettingsService SettingsService
@@ -18,13 +17,14 @@
private bool isChecked;
protected override void OnInitialized() {
SettingsService.BeginSave += (sender, args) => SettingsService.UpdateSetting(GetSetting());
if (bool.TryParse(Setting.Value, out var parsedValue)) {
SettingsService.BeginSave += (_, _) => SettingsService.UpdateSetting(GetSetting());
if (bool.TryParse(Setting?.Value, out var parsedValue)) {
isChecked = parsedValue;
}
}
protected override SettingDto GetSetting() {
protected override SettingDto? GetSetting() {
if (Setting == null) return null;
Setting.Value = isChecked.ToString();
return Setting;
}

View File

@@ -1,5 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/WebPathMapping/IgnoredPaths/=BLAZOR_002EWEB_002EJS/@EntryIndexedValue">blazor.web.js</s:String>
<s:String x:Key="/Default/CodeInspection/WebPathMapping/IgnoredPaths/=WWWROOT_005CMILKSTREAM_002ESTYLES_002ECSS/@EntryIndexedValue">wwwroot\MilkStream.styles.css</s:String>
<s:String x:Key="/Default/CodeInspection/WebPathMapping/MappedPaths/=WWWROOT_005C_005FFRAMEWORK/@EntryIndexedValue"></s:String>
<s:String x:Key="/Default/CodeInspection/WebPathMapping/PathsInCorrectCasing/=BLAZOR_002EWEB_002EJS/@EntryIndexedValue">blazor.web.js</s:String>
<s:String x:Key="/Default/CodeInspection/WebPathMapping/PathsInCorrectCasing/=WWWROOT_005CMILKSTREAM_002ESTYLES_002ECSS/@EntryIndexedValue">wwwroot\MilkStream.styles.css</s:String>
<s:String x:Key="/Default/CodeInspection/WebPathMapping/PathsInCorrectCasing/=WWWROOT_005C_005FFRAMEWORK/@EntryIndexedValue">wwwroot\_framework</s:String></wpf:ResourceDictionary>

View File

@@ -20,13 +20,15 @@ public sealed class SettingsService : AuthServiceBase {
public async Task<List<SettingDto>?> GetAllSettings() {
var response = await Client.GetAsync("api/settings");
if (response.IsSuccessStatusCode) return await response.Content.ReadFromJsonAsync<List<SettingDto>>();
return null;
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<List<SettingDto>>();
}
public void UpdateSetting(SettingDto setting) {
public void UpdateSetting(SettingDto? setting) {
if (setting == null) {
Logger.Log(LogLevel.Warning, "Setting is null, cannot update.");
return;
}
Logger.Log(LogLevel.Information, $"Updating setting {setting.Name} to {setting.Value}");
var response = Client.PostAsJsonAsync("api/settings", setting).Result;

View File

@@ -77,7 +77,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUtf8JsonWriterCache_002Ecs_002Fl_003AC_0021_003FUsers_003FREDCODE_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003F96d87fca98b4167c66ffae61c9ee88dc182d6e7dbc7eb8dbf41297e660eb_003FUtf8JsonWriterCache_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AValueTask_002Ecs_002Fl_003AC_0021_003FUsers_003FREDCODE_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003F9ec7c6b688aa4b89a1477dc1b679f62bf856b50196f4b8d19cd77f86df0abc_003FValueTask_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AWriteStackFrame_002Ecs_002Fl_003AC_0021_003FUsers_003FREDCODE_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fcfb06811acbbb25f4c1f3ae4eee74f65da5b2e9bd1bdc01ad979ac9b6745e9_003FWriteStackFrame_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/SweaWarningsMode/@EntryValue">DoNotShowAndRun</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/SweaWarningsMode/@EntryValue">ShowAndRun</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ACCESSOR_OWNER_BODY/@EntryValue">ExpressionBody</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ThisQualifier/INSTANCE_MEMBERS_QUALIFY_DECLARED_IN/@EntryValue">0</s:String>
<s:Boolean x:Key="/Default/Dpa/IsEnabledInDebug/@EntryValue">True</s:Boolean>