mirror of
https://github.com/alexhopeoconnor/dnsmasq-webui.git
synced 2026-10-03 22:12:00 +10:00
Effective config: auto-revert flag when toggled to original; fix stale cache after restore
- Flag options: when user toggles value back to original, clear pending change (same as revert button); trigger revert immediately on change so UI updates without blur - UiContext: add RevertFieldAsync so section can revert a field and refresh; section wires it to Session.RevertChange + StateHasChanged - RestoreAsync: call config cache Invalidate() after File.Copy so next read sees restored file (fixes validate-fail → restore showing pre-restore data)
This commit is contained in:
+2
-1
@@ -68,7 +68,8 @@
|
|||||||
},
|
},
|
||||||
ActivateFieldAsync = fieldKey => { ActivateField(fieldKey); return Task.CompletedTask; },
|
ActivateFieldAsync = fieldKey => { ActivateField(fieldKey); return Task.CompletedTask; },
|
||||||
DeactivateFieldAsync = () => { Session.DeactivateField(); return InvokeAsync(StateHasChanged); },
|
DeactivateFieldAsync = () => { Session.DeactivateField(); return InvokeAsync(StateHasChanged); },
|
||||||
CommitFieldAsync = args => { HandleFieldEditCommitted(args); return Task.CompletedTask; }
|
CommitFieldAsync = args => { HandleFieldEditCommitted(args); return Task.CompletedTask; },
|
||||||
|
RevertFieldAsync = (sectionId, optionName) => { Session.RevertChange(sectionId, optionName); return InvokeAsync(StateHasChanged); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-18
@@ -171,6 +171,8 @@ else
|
|||||||
private void HandleValueChanged(object? value)
|
private void HandleValueChanged(object? value)
|
||||||
{
|
{
|
||||||
_draftValue = value;
|
_draftValue = value;
|
||||||
|
if (_hasPendingChange && ValuesEqual(_valueAtActivation, value))
|
||||||
|
_ = InvokeAsync(CommitIfChangedAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnBlur(FocusEventArgs _)
|
private async Task OnBlur(FocusEventArgs _)
|
||||||
@@ -182,26 +184,30 @@ else
|
|||||||
private async Task<bool> CommitIfChangedAsync()
|
private async Task<bool> CommitIfChangedAsync()
|
||||||
{
|
{
|
||||||
var newValue = _draftValue ?? _effectiveValue;
|
var newValue = _draftValue ?? _effectiveValue;
|
||||||
if (!ValuesEqual(_valueAtActivation, newValue))
|
if (ValuesEqual(_valueAtActivation, newValue))
|
||||||
{
|
{
|
||||||
var validationError = ValidateDraftValue(newValue);
|
_draftValue = null;
|
||||||
if (validationError != null)
|
if (_hasPendingChange && Ui?.RevertFieldAsync != null)
|
||||||
{
|
await Ui.RevertFieldAsync(Descriptor.SectionId, Descriptor.OptionName);
|
||||||
NotificationService.ShowError(validationError);
|
return true;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var source = Descriptor.GetSource();
|
|
||||||
object? valueToCommit = newValue;
|
|
||||||
if (newValue is IReadOnlyList<string> list)
|
|
||||||
{
|
|
||||||
var deduped = list.Distinct(StringComparer.Ordinal).ToList();
|
|
||||||
valueToCommit = deduped;
|
|
||||||
}
|
|
||||||
var args = new EffectiveConfigEditCommittedArgs(Descriptor.SectionId, Descriptor.OptionName, _valueAtActivation, valueToCommit, source?.FilePath);
|
|
||||||
if (Ui != null)
|
|
||||||
await Ui.CommitFieldAsync(args);
|
|
||||||
}
|
}
|
||||||
|
var validationError = ValidateDraftValue(newValue);
|
||||||
|
if (validationError != null)
|
||||||
|
{
|
||||||
|
NotificationService.ShowError(validationError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var source = Descriptor.GetSource();
|
||||||
|
object? valueToCommit = newValue;
|
||||||
|
if (newValue is IReadOnlyList<string> list)
|
||||||
|
{
|
||||||
|
var deduped = list.Distinct(StringComparer.Ordinal).ToList();
|
||||||
|
valueToCommit = deduped;
|
||||||
|
}
|
||||||
|
var args = new EffectiveConfigEditCommittedArgs(Descriptor.SectionId, Descriptor.OptionName, _valueAtActivation, valueToCommit, source?.FilePath);
|
||||||
|
if (Ui != null)
|
||||||
|
await Ui.CommitFieldAsync(args);
|
||||||
_draftValue = null;
|
_draftValue = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public sealed class EffectiveConfigSaveService : IEffectiveConfigSaveService
|
|||||||
{
|
{
|
||||||
private readonly IDnsmasqConfigSetService _configSetService;
|
private readonly IDnsmasqConfigSetService _configSetService;
|
||||||
private readonly IDnsmasqConfigService _configService;
|
private readonly IDnsmasqConfigService _configService;
|
||||||
|
private readonly IConfigSetCache _configSetCache;
|
||||||
private readonly IConfigValidationService _validationService;
|
private readonly IConfigValidationService _validationService;
|
||||||
private readonly IReloadService _reloadService;
|
private readonly IReloadService _reloadService;
|
||||||
private readonly ILogger<EffectiveConfigSaveService> _logger;
|
private readonly ILogger<EffectiveConfigSaveService> _logger;
|
||||||
@@ -18,12 +19,14 @@ public sealed class EffectiveConfigSaveService : IEffectiveConfigSaveService
|
|||||||
public EffectiveConfigSaveService(
|
public EffectiveConfigSaveService(
|
||||||
IDnsmasqConfigSetService configSetService,
|
IDnsmasqConfigSetService configSetService,
|
||||||
IDnsmasqConfigService configService,
|
IDnsmasqConfigService configService,
|
||||||
|
IConfigSetCache configSetCache,
|
||||||
IConfigValidationService validationService,
|
IConfigValidationService validationService,
|
||||||
IReloadService reloadService,
|
IReloadService reloadService,
|
||||||
ILogger<EffectiveConfigSaveService> logger)
|
ILogger<EffectiveConfigSaveService> logger)
|
||||||
{
|
{
|
||||||
_configSetService = configSetService;
|
_configSetService = configSetService;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
|
_configSetCache = configSetCache;
|
||||||
_validationService = validationService;
|
_validationService = validationService;
|
||||||
_reloadService = reloadService;
|
_reloadService = reloadService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -139,6 +142,7 @@ public sealed class EffectiveConfigSaveService : IEffectiveConfigSaveService
|
|||||||
|
|
||||||
var managedPath = set.ManagedFilePath!;
|
var managedPath = set.ManagedFilePath!;
|
||||||
File.Copy(backupPath, managedPath, overwrite: true);
|
File.Copy(backupPath, managedPath, overwrite: true);
|
||||||
|
_configSetCache.Invalidate();
|
||||||
_logger.LogInformation("Restored managed config from backup: {BackupPath}", backupPath);
|
_logger.LogInformation("Restored managed config from backup: {BackupPath}", backupPath);
|
||||||
|
|
||||||
var restartResult = await _reloadService.ReloadAsync(ct);
|
var restartResult = await _reloadService.ReloadAsync(ct);
|
||||||
|
|||||||
@@ -12,4 +12,6 @@ public sealed class EffectiveConfigUiContext
|
|||||||
public required Func<string, Task> ActivateFieldAsync { get; init; }
|
public required Func<string, Task> ActivateFieldAsync { get; init; }
|
||||||
public required Func<Task> DeactivateFieldAsync { get; init; }
|
public required Func<Task> DeactivateFieldAsync { get; init; }
|
||||||
public required Func<EffectiveConfigEditCommittedArgs, Task> CommitFieldAsync { get; init; }
|
public required Func<EffectiveConfigEditCommittedArgs, Task> CommitFieldAsync { get; init; }
|
||||||
|
/// <summary>Revert a pending change for the given field and refresh the section (e.g. when user toggles a flag back to its original value).</summary>
|
||||||
|
public Func<string, string, Task>? RevertFieldAsync { get; init; }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user