fix: guard player display from cross-encounter conflicts

- Start encounter only claims display if slot empty or already ours
  (prevents stealing display from another live encounter)
- End encounter only clears display if THIS encounter is the one showing
  (prevents killing display for a different live encounter)
- Use unwrapped activeDisplayData (not snapshot wrapper activeDisplayInfo)
- Tests: 4 display guard cases (claim empty, no-steal busy, clear own,
  no-clear other)
This commit is contained in:
david raistrick
2026-07-08 19:56:00 -04:00
parent c05a283cf0
commit 2c6dfdafc8
2 changed files with 108 additions and 2 deletions
+14 -2
View File
@@ -2165,7 +2165,13 @@ function InitiativeControls({ campaignId, encounter, encounterPath }) {
try {
await startEncounter(encounter, buildCtx(encounterPath));
await storage.setDoc(getPath.activeDisplay(), { activeCampaignId: campaignId, activeEncounterId: encounter.id }, { merge: true });
// Only claim player display if slot is empty or already showing this encounter.
// Don't steal display from another live encounter.
const displayEmpty = !activeDisplayData || !activeDisplayData.activeEncounterId;
const displayIsOurs = activeDisplayData && activeDisplayData.activeCampaignId === campaignId && activeDisplayData.activeEncounterId === encounter.id;
if (displayEmpty || displayIsOurs) {
await storage.setDoc(getPath.activeDisplay(), { activeCampaignId: campaignId, activeEncounterId: encounter.id }, { merge: true });
}
} catch (err) {
showToast(err.message || "Failed to start encounter. Please try again."); }
};
@@ -2207,7 +2213,13 @@ function InitiativeControls({ campaignId, encounter, encounterPath }) {
} catch {}
}
await endEncounter(encounter, ctx);
await storage.setDoc(getPath.activeDisplay(), { activeCampaignId: null, activeEncounterId: null }, { merge: true });
// Only clear display if THIS encounter is the one showing.
const displayIsThis = activeDisplayData &&
activeDisplayData.activeCampaignId === campaignId &&
activeDisplayData.activeEncounterId === encounter.id;
if (displayIsThis) {
await storage.setDoc(getPath.activeDisplay(), { activeCampaignId: null, activeEncounterId: null }, { merge: true });
}
} catch (err) {
showToast("Failed to end encounter. Please try again."); }