refactor: single-source display lifecycle + scenario test no React

Move activeDisplay lifecycle to shared (one path, not duplicated in App):
- activateDisplay({campaignId, encounterId})
- clearDisplay()
- toggleHidePlayerHp(current)
All 6 App sites wired (start/end/2×delete-encounter/delete-campaign/hp-toggle).
Existing {patch,log} return shape preserved. No interface changes.

Combat.scenario.test.js rewritten to call shared directly, no React:
- Same actions as old UI version (roster chars, monsters, addAll, hp-toggle×2,
  start, 100 rounds of damage/heal/conditions/toggle/edit/deathsave/pause/
  resume/add/remove, end). Same funcs, same order, same data.
- Models two firestore docs (encounter + activeDisplay) via shared patches.
- Run time: 72s -> 4ms.

Both suites green: shared 91/91, App 77/77.
This commit is contained in:
david raistrick
2026-07-03 18:26:02 -04:00
parent d83d1383c0
commit e650cd2710
3 changed files with 205 additions and 253 deletions
+7 -21
View File
@@ -59,6 +59,7 @@ const {
deathSave: combatDeathSave,
toggleCondition: combatToggleCondition,
reorderParticipants,
activateDisplay, clearDisplay, toggleHidePlayerHp: combatToggleHidePlayerHp,
} = shared;
const ROLL_DISPLAY_DURATION = 5000;
@@ -1495,7 +1496,7 @@ function InitiativeControls({ campaignId, encounter, encounterPath }) {
const handleToggleHidePlayerHp = async () => {
if (!db) return;
try {
await storage.updateDoc(getPath.activeDisplay(), { hidePlayerHp: !hidePlayerHp });
await storage.updateDoc(getPath.activeDisplay(), combatToggleHidePlayerHp(hidePlayerHp).patch);
} catch (err) {
console.error("Error toggling hidePlayerHp:", err);
}
@@ -1519,10 +1520,7 @@ function InitiativeControls({ campaignId, encounter, encounterPath }) {
try {
const { patch, log } = startEncounter(encounter);
await storage.updateDoc(encounterPath, patch);
await storage.updateDoc(getPath.activeDisplay(), {
activeCampaignId: campaignId,
activeEncounterId: encounter.id
});
await storage.updateDoc(getPath.activeDisplay(), activateDisplay({ campaignId, encounterId: encounter.id }).patch);
logAction(log.message, { encounterName: encounter.name }, {
encounterPath,
updates: log.undo,
@@ -1575,10 +1573,7 @@ function InitiativeControls({ campaignId, encounter, encounterPath }) {
try {
const { patch, log } = endEncounter(encounter);
await storage.updateDoc(encounterPath, patch);
await storage.updateDoc(getPath.activeDisplay(), {
activeCampaignId: null,
activeEncounterId: null
});
await storage.updateDoc(getPath.activeDisplay(), clearDisplay().patch);
logAction(log.message, { encounterName: encounter.name }, {
encounterPath,
updates: log.undo,
@@ -1774,10 +1769,7 @@ function EncounterManager({ campaignId, initialActiveEncounterId, campaignCharac
}
if (activeDisplayInfo && activeDisplayInfo.activeEncounterId === encounterId) {
await storage.updateDoc(getPath.activeDisplay(), {
activeCampaignId: null,
activeEncounterId: null
});
await storage.updateDoc(getPath.activeDisplay(), clearDisplay().patch);
}
} catch (err) {
console.error("Error deleting encounter:", err);
@@ -1796,10 +1788,7 @@ function EncounterManager({ campaignId, initialActiveEncounterId, campaignCharac
const currentActiveEncounter = activeDisplayInfo?.activeEncounterId;
if (currentActiveCampaign === campaignId && currentActiveEncounter === encounterId) {
await storage.updateDoc(getPath.activeDisplay(), {
activeCampaignId: null,
activeEncounterId: null,
});
await storage.updateDoc(getPath.activeDisplay(), clearDisplay().patch);
} else {
await storage.updateDoc(getPath.activeDisplay(), {
activeCampaignId: campaignId,
@@ -2042,10 +2031,7 @@ function AdminView({ userId }) {
const activeDisplay = await storage.getDoc(getPath.activeDisplay());
if (activeDisplay && activeDisplay.activeCampaignId === campaignId) {
await storage.updateDoc(getPath.activeDisplay(), {
activeCampaignId: null,
activeEncounterId: null
});
await storage.updateDoc(getPath.activeDisplay(), clearDisplay().patch);
}
} catch (err) {
console.error("Error deleting campaign:", err);