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:
2026-03-02 22:28:47 +10:00
parent 87179f0195
commit 5f728900dc
4 changed files with 32 additions and 19 deletions
@@ -68,7 +68,8 @@
},
ActivateFieldAsync = fieldKey => { ActivateField(fieldKey); return Task.CompletedTask; },
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); }
};
}
@@ -171,6 +171,8 @@ else
private void HandleValueChanged(object? value)
{
_draftValue = value;
if (_hasPendingChange && ValuesEqual(_valueAtActivation, value))
_ = InvokeAsync(CommitIfChangedAsync);
}
private async Task OnBlur(FocusEventArgs _)
@@ -182,26 +184,30 @@ else
private async Task<bool> CommitIfChangedAsync()
{
var newValue = _draftValue ?? _effectiveValue;
if (!ValuesEqual(_valueAtActivation, newValue))
if (ValuesEqual(_valueAtActivation, newValue))
{
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;
if (_hasPendingChange && Ui?.RevertFieldAsync != null)
await Ui.RevertFieldAsync(Descriptor.SectionId, Descriptor.OptionName);
return true;
}
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;
return true;
}
@@ -11,6 +11,7 @@ public sealed class EffectiveConfigSaveService : IEffectiveConfigSaveService
{
private readonly IDnsmasqConfigSetService _configSetService;
private readonly IDnsmasqConfigService _configService;
private readonly IConfigSetCache _configSetCache;
private readonly IConfigValidationService _validationService;
private readonly IReloadService _reloadService;
private readonly ILogger<EffectiveConfigSaveService> _logger;
@@ -18,12 +19,14 @@ public sealed class EffectiveConfigSaveService : IEffectiveConfigSaveService
public EffectiveConfigSaveService(
IDnsmasqConfigSetService configSetService,
IDnsmasqConfigService configService,
IConfigSetCache configSetCache,
IConfigValidationService validationService,
IReloadService reloadService,
ILogger<EffectiveConfigSaveService> logger)
{
_configSetService = configSetService;
_configService = configService;
_configSetCache = configSetCache;
_validationService = validationService;
_reloadService = reloadService;
_logger = logger;
@@ -139,6 +142,7 @@ public sealed class EffectiveConfigSaveService : IEffectiveConfigSaveService
var managedPath = set.ManagedFilePath!;
File.Copy(backupPath, managedPath, overwrite: true);
_configSetCache.Invalidate();
_logger.LogInformation("Restored managed config from backup: {BackupPath}", backupPath);
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<Task> DeactivateFieldAsync { 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; }
}