fix(settings): conditional refilter on save — preserve chat history for cosmetic changes
Settings.Save() unconditionally ran ClearAllTabs + FilterAllTabsAsync after every save. The cycle reloads messages from the DB, which silently wipes any in-session message that wasn't persisted — Privacy-First configurations block most channels from the DB, so all unlogged channels (Allgemein/Say/Yell/Shout under default filters) showed up empty after every settings save. New HasFilterRelevantChanges helper compares Mutable to Plugin.Config across: - PrivacyFilterEnabled - PrivacyPersistChannels (HashSet<ChatType>) - PrivacyPersistUnknownChannels - FilterIncludePreviousSessions - per-persistent-tab: Identifier (reorder/swap), SelectedChannels, ExtraChatAll, ExtraChatChannels Refilter only runs if any of those changed. Cosmetic settings (theme, tab icons, layout, fonts, language) leave the chat log untouched. Combined with the prior UpdateFrom Identifier-mapping fix and the TempTab skip in ClearAllTabs/FilterAllTabs, both persistent and Auto-Tell tabs now fully survive a settings save. Reported by Flo from in-game testing 2026-05-05/06: 'der allgemein chat tab z.b immernoch gecleart wird' / 'alle vom plugin nicht geloggten channel sind dann leer'. Also updated yaml changelog, docs/CHANGELOG.md and .github/forge-posts/ v1.2.0.md to describe the actual fix shape rather than the partial UpdateFrom-only fix that preceded it.
This commit is contained in:
@@ -114,13 +114,15 @@ changelog: |-
|
||||
|
||||
Bug fixes from in-game testing:
|
||||
|
||||
- Settings save no longer wipes the in-session chat history of
|
||||
persistent tabs (Tab.Clone preserved Identifier but not
|
||||
Messages — Identifier-based mapping now restores the live
|
||||
MessageList onto cloned tabs).
|
||||
- Settings save no longer clears Auto-Tell tabs either
|
||||
(ClearAllTabs/FilterAllTabs now skip TempTabs since their
|
||||
messages have no DB persistence to refilter from).
|
||||
- Settings save no longer wipes chat history by default. The
|
||||
heavy ClearAllTabs + FilterAllTabsAsync refilter cycle now
|
||||
only runs when a filter-relevant setting actually changed
|
||||
(Privacy filter, persisted channels, per-tab channel
|
||||
selection). Cosmetic changes — theme, tab icons, layout
|
||||
flags — keep the in-session chat intact. Combined with an
|
||||
Identifier-based MessageList restore in Configuration.
|
||||
UpdateFrom and a TempTab skip in ClearAllTabs/FilterAllTabs,
|
||||
persistent tabs and Auto-Tell tabs both survive the save.
|
||||
- Sidebar buttons now align vertically with the first message
|
||||
row (top padding mirrors the chat header toolbar height).
|
||||
- Sidebar child window no longer paints the top padding with
|
||||
|
||||
@@ -210,14 +210,24 @@ public sealed class SettingsWindow : Dalamud.Interface.Windowing.Window
|
||||
var fontSizeChanged = Math.Abs(Mutable.SymbolsFontSizeV2 - Plugin.Config.SymbolsFontSizeV2) > 0.001
|
||||
|| Math.Abs(Mutable.FontSizeV2 - Plugin.Config.FontSizeV2) > 0.001;
|
||||
var italicStateChanged = Mutable.ItalicEnabled != Plugin.Config.ItalicEnabled;
|
||||
// v1.2.0 — Refilter only if a filter-relevant setting actually
|
||||
// changed. The Clear+Refilter cycle reloads messages from the DB,
|
||||
// which silently wipes any in-session message that wasn't
|
||||
// persisted (Privacy-First config blocks most channels from DB).
|
||||
// Cosmetic changes (theme, tab icons, layout flags) trigger no
|
||||
// refilter — chat history stays intact.
|
||||
var filtersChanged = HasFilterRelevantChanges();
|
||||
|
||||
Plugin.Config.UpdateFrom(Mutable, true);
|
||||
|
||||
// save after 60 frames have passed, which should hopefully not
|
||||
// commit any changes that cause a crash
|
||||
Plugin.DeferredSaveFrames = 60;
|
||||
Plugin.MessageManager.ClearAllTabs();
|
||||
Plugin.MessageManager.FilterAllTabsAsync();
|
||||
if (filtersChanged)
|
||||
{
|
||||
Plugin.MessageManager.ClearAllTabs();
|
||||
Plugin.MessageManager.FilterAllTabsAsync();
|
||||
}
|
||||
|
||||
if (fontChanged || fontSizeChanged || italicStateChanged)
|
||||
Plugin.FontManager.BuildFonts();
|
||||
@@ -233,4 +243,59 @@ public sealed class SettingsWindow : Dalamud.Interface.Windowing.Window
|
||||
|
||||
Initialise();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// v1.2.0 — Detects whether any setting that influences message
|
||||
/// filtering changed between Plugin.Config and the Mutable working
|
||||
/// copy. Used to gate the heavy ClearAllTabs+FilterAllTabsAsync cycle
|
||||
/// in Save: cosmetic changes (theme, tab icons, layout flags) do not
|
||||
/// touch the chat log, only filter-relevant changes do. Without this
|
||||
/// gate, every settings save wipes the chat history of any channel
|
||||
/// the Privacy filter blocks from being persisted to the DB —
|
||||
/// reported by Flo from in-game testing 2026-05-05/06.
|
||||
/// </summary>
|
||||
private bool HasFilterRelevantChanges()
|
||||
{
|
||||
// Top-level privacy controls.
|
||||
if (Mutable.PrivacyFilterEnabled != Plugin.Config.PrivacyFilterEnabled) return true;
|
||||
if (Mutable.PrivacyPersistUnknownChannels != Plugin.Config.PrivacyPersistUnknownChannels) return true;
|
||||
if (!Mutable.PrivacyPersistChannels.SetEquals(Plugin.Config.PrivacyPersistChannels)) return true;
|
||||
|
||||
// FilterIncludePreviousSessions changes the GetMostRecentMessages
|
||||
// window in MessageManager.FilterAllTabs and is therefore filter-
|
||||
// relevant even though it lives outside the Privacy block.
|
||||
if (Mutable.FilterIncludePreviousSessions != Plugin.Config.FilterIncludePreviousSessions) return true;
|
||||
|
||||
// Per-tab channel selection. Compare persistent tabs only —
|
||||
// TempTabs are session-only and never refiltered anyway.
|
||||
var origPersistent = Plugin.Config.Tabs.Where(t => !t.IsTempTab).ToList();
|
||||
var newPersistent = Mutable.Tabs.Where(t => !t.IsTempTab).ToList();
|
||||
|
||||
if (origPersistent.Count != newPersistent.Count) return true; // add or delete
|
||||
|
||||
for (var i = 0; i < origPersistent.Count; i++)
|
||||
{
|
||||
var orig = origPersistent[i];
|
||||
var neu = newPersistent[i];
|
||||
|
||||
// Identifier mismatch at the same index means reorder or
|
||||
// a slot got swapped — treat as filter-relevant so the new
|
||||
// channel-selection layout actually applies.
|
||||
if (orig.Identifier != neu.Identifier) return true;
|
||||
|
||||
if (orig.ExtraChatAll != neu.ExtraChatAll) return true;
|
||||
if (!orig.ExtraChatChannels.SetEquals(neu.ExtraChatChannels)) return true;
|
||||
|
||||
// SelectedChannels is a Dictionary<ChatType, (ChatSource, ChatSource)>
|
||||
// — value-tuple equality already does the right thing per-pair.
|
||||
if (orig.SelectedChannels.Count != neu.SelectedChannels.Count) return true;
|
||||
foreach (var pair in orig.SelectedChannels)
|
||||
{
|
||||
if (!neu.SelectedChannels.TryGetValue(pair.Key, out var nv)) return true;
|
||||
if (!pair.Value.Equals(nv)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user