fix(config): preserve persistent-tab message history across settings save

UpdateFrom replaced persistent tabs with Tab.Clone()s. Clone deliberately
omits the NonSerialized Messages list to avoid shared mutable state on
disk-load — but on a settings save (Plugin.Config.UpdateFrom(Mutable))
that path means every persistent tab loses its in-session chat history
the moment the user clicks Save.

Capture the live MessageList plus LastSendUnread counter by Identifier
before the replace and restore them onto the cloned tabs. Tab.Clone()
already preserves Identifier so the lookup matches one-to-one for
unchanged tabs. New tabs added in settings get a fresh empty list,
deleted tabs lose their history (both intended).

Reported by Flo in-game 2026-05-05 — chat got wiped on every settings
save during v1.2.0 testing in Limsa.
This commit is contained in:
2026-05-05 23:48:23 +02:00
parent a74e3da030
commit 59e86cd8dd
+26 -3
View File
@@ -302,10 +302,33 @@ public class Configuration : IPluginConfiguration
// never present in a disk-loaded copy. Keep the live temp tabs of // never present in a disk-loaded copy. Keep the live temp tabs of
// *this* configuration alive across an UpdateFrom so a settings // *this* configuration alive across an UpdateFrom so a settings
// save (or sidebar-mode toggle) does not silently destroy the // save (or sidebar-mode toggle) does not silently destroy the
// user's open tell conversations. Persistent tabs from `other` // user's open tell conversations.
// still get the regular clone-replace treatment. //
// For persistent tabs we go through Tab.Clone() which intentionally
// does NOT copy the NonSerialized Messages list (avoids shared
// mutable state on disk-load). On a settings save that means the
// chat history for every persistent tab would be wiped — bug
// reported by Flo 2026-05-05. We work around it by capturing the
// live MessageList (and LastSendUnread counter) by Identifier
// before the replace, then restoring it onto the freshly cloned
// tabs whose Identifier survives Tab.Clone(). New tabs added in
// settings get a fresh empty MessageList; deleted tabs lose their
// history (intended).
var liveTempTabs = Tabs.Where(t => t.IsTempTab).ToList(); var liveTempTabs = Tabs.Where(t => t.IsTempTab).ToList();
Tabs = other.Tabs.Where(t => !t.IsTempTab).Select(t => t.Clone()).ToList(); var livePersistentSession = Tabs
.Where(t => !t.IsTempTab)
.ToDictionary(t => t.Identifier, t => (t.Messages, t.LastSendUnread));
Tabs = other.Tabs.Where(t => !t.IsTempTab).Select(t =>
{
var clone = t.Clone();
if (livePersistentSession.TryGetValue(clone.Identifier, out var live))
{
clone.Messages = live.Messages;
clone.LastSendUnread = live.LastSendUnread;
}
return clone;
}).ToList();
Tabs.AddRange(liveTempTabs); Tabs.AddRange(liveTempTabs);
OverrideStyle = other.OverrideStyle; OverrideStyle = other.OverrideStyle;