@@ -262,6 +262,69 @@ mainNav.innerHTML = mainNavHTML;
262262
263263 await window . fetchAllCooldowns ( ) ;
264264 await window . fetchRoster ( ) ;
265+
266+ // ──────────────────────────────────────────────────────────────────
267+ // GLOBALER ROSTER-LISTENER (persistent über alle Seiten)
268+ //
269+ // Wird einmal im DOMContentLoaded registriert und NIE gecancelt.
270+ // Sorgt dafür, dass Roster-Änderungen (Spieler hinzufügen/entfernen,
271+ // Klasse/Spec/Rolle ändern) auf JEDER Seite ankommen — nicht nur
272+ // wenn der User gerade auf der Comp-Seite ist.
273+ //
274+ // Auf der Comp-Seite: lokale UI (displayRoster, Buffs, Soulstones).
275+ // Auf Boss-Seiten: CD-Assignment-Dropdowns neu befüllen.
276+ // Auf allen Seiten: window.rosterData immer aktuell halten.
277+ // ──────────────────────────────────────────────────────────────────
278+ window . globalRosterListener = onSnapshot ( rosterDocRef , ( docSnap ) => {
279+ const currentRosterData = docSnap . exists ( ) ? docSnap . data ( ) . roster || [ ] : [ ] ;
280+ window . rosterData = currentRosterData ;
281+
282+ // 1. Comp-Seite-Updates (alle Funktionen prüfen DOM-Container selbst)
283+ if ( window . displayRoster ) window . displayRoster ( currentRosterData ) ;
284+ if ( window . initBuffAssignments ) window . initBuffAssignments ( currentRosterData ) ;
285+ if ( window . initSoulstoneAssignments ) window . initSoulstoneAssignments ( currentRosterData ) ;
286+ if ( window . updateRaidBuffsDisplay ) window . updateRaidBuffsDisplay ( currentRosterData ) ;
287+
288+ // 2. JSON-Input auf der Comp-Seite befüllen (falls leer)
289+ const jsonInput = document . getElementById ( 'json-input' ) ;
290+ if ( jsonInput && jsonInput . value === '' && docSnap . data ( ) ?. rawJson ) {
291+ jsonInput . value = docSnap . data ( ) . rawJson ;
292+ }
293+
294+ // 3. Boss-Seite: CD-Dropdowns refreshen
295+ // Aktuelle Auswahl pro Select merken, neu befüllen, Auswahl wiederherstellen.
296+ const bossId = window . currentBossIdForPatches ;
297+ if ( bossId && window . RosterPatches && window . populateDropdownOptions ) {
298+ const fullRoster = window . RosterPatches . buildEffectiveRoster ( currentRosterData , bossId ) ;
299+ document . querySelectorAll ( '.assignment-select' ) . forEach ( select => {
300+ // Buff-/Soulstone-Selects auf der Comp-Seite überspringen
301+ // (haben eigene Handler oben)
302+ if ( select . classList . contains ( 'buff-player-select' ) ||
303+ select . classList . contains ( 'soulstone-target-select' ) ||
304+ select . classList . contains ( 'buff-class-select' ) ) {
305+ return ;
306+ }
307+ const previousValue = select . value ;
308+ window . populateDropdownOptions ( select , fullRoster , bossId ) ;
309+ // Auswahl wiederherstellen — falls Spieler noch im Roster
310+ const stillExists = Array . from ( select . options ) . some ( o => o . value === previousValue ) ;
311+ if ( stillExists ) {
312+ select . value = previousValue ;
313+ const opt = select . options [ select . selectedIndex ] ;
314+ if ( opt ?. dataset ?. color ) select . style . color = opt . dataset . color ;
315+ select . style . fontStyle = ( opt ?. dataset ?. bench === '1' ) ? 'italic' : 'normal' ;
316+ }
317+ } ) ;
318+ }
319+
320+ // 4. Auto-Planner ggf. neu fütterm: window.effectiveRoster für aktuellen Boss neu bauen
321+ if ( bossId && window . RosterPatches ) {
322+ window . effectiveRoster = window . RosterPatches . buildEffectiveRoster (
323+ currentRosterData , bossId , { excludeBench : true }
324+ ) ;
325+ }
326+ } ) ;
327+
265328 try {
266329 const rosterSnap = await getDoc ( rosterDocRef ) ;
267330 window . rosterData = rosterSnap . exists ( ) ? rosterSnap . data ( ) . roster || [ ] : [ ] ;
@@ -542,24 +605,17 @@ window.applyThemeForRaid = function(raidId) {
542605 } ) ;
543606 }
544607
545- window . currentRosterUnsubscribe = onSnapshot ( rosterDocRef , ( docSnap ) => {
546- const jsonInput = document . getElementById ( 'json-input' ) ;
547- const currentRosterData = docSnap . exists ( ) ? docSnap . data ( ) . roster || [ ] : [ ] ;
548- window . rosterData = currentRosterData ;
549- // Reihenfolge der Aufrufe:
550- window . displayRoster ( currentRosterData ) ;
551- window . initBuffAssignments ( currentRosterData ) ;
552- window . initSoulstoneAssignments ( currentRosterData ) ;
553-
554- if ( window . updateRaidBuffsDisplay ) {
555- window . updateRaidBuffsDisplay ( currentRosterData ) ;
556- }
557-
558- if ( jsonInput && jsonInput . value === '' && docSnap . data ( ) ?. rawJson ) {
559- jsonInput . value = docSnap . data ( ) . rawJson ;
608+ // Roster-Updates kommen jetzt zentral aus dem globalen Listener (siehe DOMContentLoaded oben).
609+ // Wir müssen aber einmal beim Betreten der Comp-Seite manuell die UI mit dem aktuellen
610+ // window.rosterData füttern, weil der globale Listener nur bei Daten-Änderungen feuert.
611+ if ( window . rosterData ) {
612+ window . displayRoster ( window . rosterData ) ;
613+ window . initBuffAssignments ( window . rosterData ) ;
614+ window . initSoulstoneAssignments ( window . rosterData ) ;
615+ if ( window . updateRaidBuffsDisplay ) {
616+ window . updateRaidBuffsDisplay ( window . rosterData ) ;
617+ }
560618 }
561-
562- } ) ;
563619 // Master-View initialisieren
564620 if ( window . isManager ) {
565621 window . initMasterView ( ) ;
0 commit comments