From 639791671244ce6e47d8e6139fdb459a3a2303d3 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Tue, 18 Aug 2026 14:43:34 +0200 Subject: [PATCH] fix(settings): show which channels get their history deleted Three of the four first-run profiles switch the retention sweep on and write a per-channel deletion policy. The window only ever showed the global default, so whatever the wizard decided about individual channels was invisible from the moment the wizard closed. The tab now lists every channel whose retention differs from the global value, tagged by where the number comes from, plus a button to drop the custom ones. It also finally uses the strings written for this screen, sweep description and default help included. Read-only, deliberately. Zero means "keep forever" as a global default and "delete this channel's entire history" as a per-channel value, because the default takes a separate SQL branch while a mapped channel gets cutoff = now. No shipped profile contains a zero and no UI could set one, so nothing is broken today -- but an editor cannot be offered until those two meanings agree. Noted at the branch in DeleteByRetentionPolicy. The default slider now goes down to 0, which is the value that means "never delete anything". Its own label has been promising "0 = never" while the slider started at 1. --- HellionChat/MessageStore.cs | 6 ++ .../Settings/Tabs/DataPrivacyTab.cs | 64 ++++++++++++++++++- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/HellionChat/MessageStore.cs b/HellionChat/MessageStore.cs index 1ec4372..c46f841 100644 --- a/HellionChat/MessageStore.cs +++ b/HellionChat/MessageStore.cs @@ -529,6 +529,12 @@ internal class MessageStore : IDisposable var index = 0; foreach (var (type, days) in chatTypeDaysMap) { + // Careful: 0 here is NOT the "keep forever" it means for + // defaultDays below. A per-channel 0 puts the cutoff at now + // and deletes the channel's entire history. No profile ships + // a 0 and no UI can set one, which is why this is a comment + // and not a guard -- but any editor added later has to + // reconcile the two meanings before it exposes the value. var cutoff = nowMs - days * 86400000L; var typeParam = $"$type{index}"; var cutoffParam = $"$cutoff{index}"; diff --git a/HellionChat/Ui/Components/Settings/Tabs/DataPrivacyTab.cs b/HellionChat/Ui/Components/Settings/Tabs/DataPrivacyTab.cs index c8713a7..d7803bb 100644 --- a/HellionChat/Ui/Components/Settings/Tabs/DataPrivacyTab.cs +++ b/HellionChat/Ui/Components/Settings/Tabs/DataPrivacyTab.cs @@ -1,5 +1,7 @@ using Dalamud.Bindings.ImGui; using HellionChat.Code; +using HellionChat.Resources; +using HellionChat.Util; namespace HellionChat.Ui.Components.Settings.Tabs; @@ -19,17 +21,24 @@ internal sealed class DataPrivacyTab if (ImGui.CollapsingHeader("Logging", ImGuiTreeNodeFlags.DefaultOpen)) { _w.Toggle( - "Enable retention sweep", + HellionStrings.Retention_Enabled_Name, () => Plugin.Config.RetentionEnabled, v => Plugin.Config.RetentionEnabled = v ); + ImGuiUtil.HelpMarker(HellionStrings.Retention_Enabled_Description); + + // Down to 0, which DeleteByRetentionPolicy reads as "keep forever" + // for channels without an override. The slider started at 1, so the + // one value that means "never delete anything" was unreachable + // through the UI while the label promised it. _w.SliderInt( - "Default retention (days)", + HellionStrings.Retention_Default_Label, () => Plugin.Config.RetentionDefaultDays, v => Plugin.Config.RetentionDefaultDays = v, - 1, + 0, 365 ); + ImGuiUtil.HelpMarker(HellionStrings.Retention_Default_Help); // RetentionLastRunAt defaults to MinValue on a fresh install, which // would render as "0001-01-01 00:00" and look like a bug; the "Never" @@ -41,6 +50,8 @@ internal sealed class DataPrivacyTab ? "Never" : Plugin.Config.RetentionLastRunAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm"); ImGui.TextDisabled($"Last run: {lastRun}"); + + DrawRetentionOverrides(); } if (ImGui.CollapsingHeader("Privacy filter", ImGuiTreeNodeFlags.DefaultOpen)) @@ -66,6 +77,53 @@ internal sealed class DataPrivacyTab } } + // Read-only on purpose. Three of the four wizard profiles write a + // per-channel policy and switch the sweep on, and until now the window only + // showed the global default -- so whatever the wizard decided about your + // channels was invisible from here on. + // + // Editing them is a separate matter: 0 means "keep forever" as a global + // default but "delete everything" as a per-channel value, and no UI should + // offer that until the two agree. + private void DrawRetentionOverrides() + { + var overrides = Plugin.Config.RetentionPerChannelDays; + + ImGui.Spacing(); + ImGui.TextUnformatted(HellionStrings.Retention_Tree_Heading); + + var shown = 0; + foreach (var type in EnumValues.All) + { + var days = Plugin.Config.GetRetentionDays(type); + if (days == Plugin.Config.RetentionDefaultDays && !overrides.ContainsKey(type)) + continue; + + var tag = overrides.ContainsKey(type) + ? HellionStrings.Retention_Tag_Override + : HellionStrings.Retention_Tag_Spec; + ImGui.TextDisabled($" {type.Name()}: {days} d {tag}"); + shown++; + } + + if (shown == 0) + { + ImGui.TextDisabled($" {HellionStrings.Retention_Tag_Global}"); + return; + } + + if (overrides.Count > 0 && ImGui.Button(HellionStrings.Retention_Clear_Overrides)) + { + // Same lock the sweep takes when it snapshots the policy, so a clear + // cannot cut its enumeration short. + lock (_plugin.ConfigMapsLock) + { + overrides.Clear(); + } + _plugin.SaveConfig(); + } + } + private void DrawPrivacyPersistChannelsGrid() { // HashSet: iterate Enum.GetValues() for stable