fix(tabs): preserve runtime channel across Settings Save, deep-clone seeded CurrentChannel
Smoke-test round 4 surfaced a clean reproducer: a Party or Linkshell tab with channel /p, then Settings → Save, popped the input back to /tell <pinned-partner> on the next interaction. Two bugs combined: 1. Configuration.UpdateFrom captured only Messages+LastSendUnread from the live state during the persistent-tab merge. CurrentChannel was not preserved, so a Settings save overwrote the runtime channel state with the settings-time snapshot. If the user switched channel in-game between Settings-open and Settings-save, that switch was lost. Live CurrentChannel now joins Messages and LastSendUnread in the per-Identifier preservation tuple. 2. TabSwitched seeded a new tab's CurrentChannel from previousTab via reference copy (`newTab.CurrentChannel = previousTab.CurrentChannel`). That left both tabs sharing the same UsedChannel instance, so a later mutation on one bled into the other — exactly the path that carried a pinned tell-target onto Party. Switched to a deep clone (UsedChannel.Clone(), same Cherry-Pick-Patch-B pattern from v1.4.6) plus a Debug log so the next smoke can confirm at a glance which previous tab donated its channel state. Pre-existing ChatTwo upstream pattern; v1.4.7 just made it visible because pinned tabs are now the kind of long-lived tell-target that sticks around for the seed path to grab.
This commit is contained in:
@@ -293,11 +293,14 @@ public class Configuration : IPluginConfiguration
|
||||
// not destroy open tell conversations. Pinned TempTabs are persistent
|
||||
// and come through `other` like regular tabs; unpinned TempTabs are
|
||||
// session-only and held from the local state. For persistent tabs
|
||||
// (incl. pinned), capture live MessageList + LastSendUnread by
|
||||
// Identifier and restore them onto the freshly cloned tabs.
|
||||
// (incl. pinned), capture live runtime state by Identifier and restore
|
||||
// it onto the freshly cloned tabs — CurrentChannel is critical because
|
||||
// the user may have switched channel in-game between settings-open
|
||||
// and settings-save, and we'd otherwise overwrite that with the
|
||||
// settings-time snapshot.
|
||||
var liveUnpinnedTempTabs = Tabs.Where(TabLifecycleHelpers.IsInUnpinnedPool).ToList();
|
||||
var livePersistentSession = Tabs.Where(t => !TabLifecycleHelpers.IsInUnpinnedPool(t))
|
||||
.ToDictionary(t => t.Identifier, t => (t.Messages, t.LastSendUnread));
|
||||
.ToDictionary(t => t.Identifier, t => (t.Messages, t.LastSendUnread, t.CurrentChannel));
|
||||
|
||||
Tabs = other
|
||||
.Tabs.Where(t => !t.IsTempTab || t.IsPinned)
|
||||
@@ -308,6 +311,7 @@ public class Configuration : IPluginConfiguration
|
||||
{
|
||||
clone.Messages = live.Messages;
|
||||
clone.LastSendUnread = live.LastSendUnread;
|
||||
clone.CurrentChannel = live.CurrentChannel;
|
||||
}
|
||||
return clone;
|
||||
})
|
||||
|
||||
@@ -441,11 +441,24 @@ public sealed class ChatLogWindow : Window
|
||||
|
||||
private void TabSwitched(Tab newTab, Tab previousTab)
|
||||
{
|
||||
// Use the fixed channel if set by the user, or set it to the current tabs channel if this tab wasn't accessed before
|
||||
// Use the fixed channel if set by the user. Otherwise, if the new tab
|
||||
// has no channel state yet (fresh from JSON, never selected this
|
||||
// session), seed from the previous tab — but deep-clone so we don't
|
||||
// share TellTarget with the previous tab. Without the clone, a later
|
||||
// /tell on the new tab would mutate the pinned tab's TellTarget and
|
||||
// the Party/Linkshell channel would pop back to the pinned tell-mark.
|
||||
if (newTab.Channel is not null)
|
||||
{
|
||||
newTab.CurrentChannel.Channel = newTab.Channel.Value;
|
||||
}
|
||||
else if (newTab.CurrentChannel.Channel is InputChannel.Invalid)
|
||||
newTab.CurrentChannel = previousTab.CurrentChannel;
|
||||
{
|
||||
newTab.CurrentChannel = previousTab.CurrentChannel.Clone();
|
||||
Plugin.LogProxy.Debug(
|
||||
$"[Tab] '{newTab.Name}' seeded channel from '{previousTab.Name}' "
|
||||
+ $"(Channel={newTab.CurrentChannel.Channel}, TellTarget={newTab.CurrentChannel.TellTarget?.ToTargetString() ?? "null"})"
|
||||
);
|
||||
}
|
||||
|
||||
SetChannel(newTab.CurrentChannel.Channel);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user