Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions _Refactor_/02_Group_Organizer/01_Goals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Refactor Goals: 02_Group_Organizer

## 1. Primary Goal: Decouple UI from Core Logic

The main objective is to refactor the Group Organizer to operate as a **Feature Module**. This means its internal components, specifically the UI and the core state management, must be fully decoupled from each other.

- **From:** The UI (`ui/organizer/rosterBoard.lua`) directly calls functions in the core state module (`core/organizer/state.lua`) to pull data. This creates a tight, two-way dependency.
- **To:** The UI will become a passive listener. The core logic will become an independent announcer of state. They will communicate exclusively through `AceEvent-3.0`, establishing a clean, one-way data flow.

## 2. Desired Outcome

- **Core Logic as the "Single Source of Truth":** The `core/organizer/state.lua` module will be solely responsible for managing the roster data. It will not have any knowledge of the UI or any other module that might consume its data.
- **UI as a "Dumb" Renderer:** The `ui/organizer/rosterBoard.lua` module will be responsible only for rendering the data it is given. It will not contain any logic for fetching or managing state. It will simply listen for state change announcements and redraw itself accordingly.
- **Improved Modularity:** By breaking the direct link, both the core and UI components become more independent and easier to maintain or replace in the future.
- **Adherence to Architecture:** This refactor will align the Group Organizer with the established architectural pattern for "Feature Modules" in the addon.
74 changes: 74 additions & 0 deletions _Refactor_/02_Group_Organizer/02_Implementation_Plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Refactor Implementation Plan: 02_Group_Organizer

## 1. Core Logic (`core/organizer/state.lua`) Modifications

The `state.lua` module will be modified to become the sole announcer of the roster's state.

1. **Identify State-Changing Functions:** A thorough review of the file will be conducted to identify all public and private functions that result in a modification of the main roster table. This includes functions that add, remove, or update players.

2. **Create a Centralized Broadcast Function:** A new private function, `BroadcastRosterUpdate()`, will be created within the module.
```lua
local function BroadcastRosterUpdate()
-- The roster table is assumed to be a local variable 'roster'
NextKey:SendMessage("ORGANIZER_ROSTER_UPDATED", roster)
end
```

3. **Integrate the Broadcast:** At the end of every identified state-changing function, a call to `BroadcastRosterUpdate()` will be added. This ensures that every change, no matter how small, triggers a state announcement.

4. **Remove UI Dependencies:** Any code that suggests a dependency on the UI (e.g., checking if the UI is visible before processing data) will be removed. The core logic should be completely independent of any consumer.

## 2. UI (`ui/organizer/rosterBoard.lua`) Modifications

The `rosterBoard.lua` module will be refactored into a passive listener and renderer.

1. **Remove Direct Calls:** All direct function calls to the `core/organizer/state.lua` module (or any of its equivalents for fetching data) will be located and removed.

2. **Register Event Listener:** In the module's initialization function (e.g., `OnInitialize`), a listener for the new `ORGANIZER_ROSTER_UPDATED` event will be registered.
```lua
function RosterBoard:OnInitialize()
-- ... other initialization
NextKey:RegisterMessage("ORGANIZER_ROSTER_UPDATED", self.OnRosterUpdated, self)
end
```

3. **Implement the Event Handler:** A new method, `OnRosterUpdated`, will be created. This method will accept the roster data from the event payload.
```lua
function RosterBoard:OnRosterUpdated(eventName, roster)
-- Store the new roster data locally
self.rosterData = roster
-- Trigger a full redraw of the UI
self:Redraw()
end
```

4. **Adapt the Redraw Logic:** The existing `Redraw()` function (or its equivalent) will be modified to source its data exclusively from the locally stored `self.rosterData`. It will no longer fetch data on its own. It will be responsible for iterating over the data and rendering the player cards, sorting headers, and any other relevant UI elements.

## 3. Data Flow Diagram

The resulting data flow will be a simple, one-way push from the core to the UI.

```
+----------------------------+
| core/organizer/state.lua |
+----------------------------+
| |
| - Roster data is changed |
| - Calls BroadcastRoster() |
| |
+-------------+--------------+
|
| Fires Event: "ORGANIZER_ROSTER_UPDATED"
| Payload: (roster_table)
v
+-------------+--------------+
| ui/organizer/rosterBoard.lua|
+----------------------------+
| |
| - Listens for event |
| - Calls OnRosterUpdated() |
| - Stores new roster data |
| - Calls self:Redraw() |
| |
+----------------------------+
```
32 changes: 32 additions & 0 deletions _Refactor_/02_Group_Organizer/03_Event_Map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Refactor Event Map: 02_Group_Organizer

## 1. Module Type: Feature (Stateful, Decoupled Component)

The Group Organizer is classified as a **Feature Module**. Its purpose is to provide a user-facing feature that has its own internal state, UI, and core logic. It is an active component that needs to communicate changes to other parts of the system without being tightly coupled to them.

## 2. Communication Pattern: `AceEvent-3.0` (Publish/Subscribe)

Based on its role as a feature, the primary method of communication between the Group Organizer's core logic and its UI (and any other potential features) will be the **`AceEvent-3.0` Publish/Subscribe system**.

- **Publisher:** The core logic module (`core/organizer/state.lua`) will act as the publisher. It will announce changes to its state.
- **Subscriber:** The UI module (`ui/organizer/rosterBoard.lua`) will act as a subscriber. It will listen for state change announcements and react accordingly.

This enforces the one-way data flow that is central to the addon's refactored architecture.

## 3. Events Published by this Module

---

### `ORGANIZER_ROSTER_UPDATED`

- **Fired By:** `core/organizer/state.lua`
- **When:** Fired any time the group roster's data is modified (e.g., players added/removed, data updated, list re-sorted).
- **Payload:**
- `arg1` (table): The complete, authoritative table representing the new state of the group roster.
- **Purpose:** To inform any interested module that the roster has changed and provide the new data, enabling UI updates or other reactions without requiring the consumer to fetch the data itself.

---

## 4. Events Listened For by this Module

The core logic of the Group Organizer may listen for events from other systems in the future, but for the scope of this specific UI/core decoupling refactor, it does not need to listen for any new events. The UI will listen for the event defined above.
36 changes: 34 additions & 2 deletions core/organizer/state.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ OrganizerState.groups = {} -- {[groupIndex][slotIndex] = playerID} - Group
OrganizerState.keystones = {} -- {[groupIndex] = {keystone, playerID}} - Designated keystones
OrganizerState.activePoll = nil -- {id, startTime, responses, timeout} - Active poll state

-- MARK: Private Methods
--- Broadcasts the current roster state to all listeners.
local function BroadcastRosterUpdate()
Debug:Dev("organizer_state", "Broadcasting ORGANIZER_ROSTER_UPDATED")
local rosterData = {
players = OrganizerState.players,
bench = OrganizerState.bench,
optOut = OrganizerState.optOut,
groups = OrganizerState.groups,
keystones = OrganizerState.keystones,
}
NextKey222:SendMessage("ORGANIZER_ROSTER_UPDATED", rosterData)
end

-- MARK: Initialization
function OrganizerState:Initialize()
return NextKey222.SafeRun(function()
Expand Down Expand Up @@ -109,6 +123,7 @@ function OrganizerState:SetPlayer(playerID, playerData)

Debug:Dev("organizer_state", "SetPlayer:", playerID, "- stored with roles:", playerData.roles and table.concat(playerData.roles, ",") or "NONE",
"specPreferences:", playerData.specPreferences ~= nil, "specDetails:", playerData.specDetails ~= nil)
BroadcastRosterUpdate()
end, "OrganizerState:SetPlayer")
end

Expand All @@ -135,6 +150,7 @@ function OrganizerState:UpdatePlayer(playerID, updates)
self.players[playerID] = playerData

Debug:Dev("organizer_state", "UpdatePlayer:", playerID, "- updates applied")
BroadcastRosterUpdate()
end, "OrganizerState:UpdatePlayer")
end

Expand Down Expand Up @@ -181,7 +197,7 @@ function OrganizerState:UpdatePlayerFromPollResponse(playerID, response)
Debug:Dev("organizer_state", "UpdatePlayerFromPollResponse:", playerID,
"- specPreferences:", playerData.specPreferences ~= nil,
"specDetails:", playerData.specDetails ~= nil)

BroadcastRosterUpdate()
end, "OrganizerState:UpdatePlayerFromPollResponse")
end

Expand Down Expand Up @@ -214,6 +230,9 @@ function OrganizerState:RemovePlayer(playerID)
end

Debug:Dev("organizer_state", "RemovePlayer:", playerID, "- removed:", existed)
if existed then
BroadcastRosterUpdate()
end
return existed

end, "OrganizerState:RemovePlayer")
Expand Down Expand Up @@ -311,6 +330,7 @@ function OrganizerState:MoveToBench(playerID)
self.bench[playerID] = true

Debug:Dev("organizer_state", "MoveToBench:", playerID)
BroadcastRosterUpdate()
return true
end, "OrganizerState:MoveToBench")
end
Expand Down Expand Up @@ -340,6 +360,7 @@ function OrganizerState:MoveToOptOut(playerID)
self.optOut[playerID] = true

Debug:Dev("organizer_state", "MoveToOptOut:", playerID)
BroadcastRosterUpdate()
return true
end, "OrganizerState:MoveToOptOut")
end
Expand Down Expand Up @@ -398,6 +419,7 @@ function OrganizerState:MoveToSlot(playerID, groupIndex, slotIndex)
end
Debug:User("AFTER SAVE - Total slots in memory:", totalSlots)

BroadcastRosterUpdate()
return true
end, "OrganizerState:MoveToSlot")
end
Expand Down Expand Up @@ -478,6 +500,7 @@ function OrganizerState:AssignToGroup(playerID, groupIndex, slotIndex)
self.groups[groupIndex][slotIndex] = playerID

Debug:Dev("organizer_state", "AssignToGroup:", playerID, "to group", groupIndex, "slot", slotIndex)
BroadcastRosterUpdate()
return true
end, "OrganizerState:AssignToGroup")
end
Expand Down Expand Up @@ -505,6 +528,9 @@ function OrganizerState:UnassignFromGroup(playerID)
end

Debug:Dev("organizer_state", "UnassignFromGroup:", playerID, "- was in group:", wasInGroup)
if wasInGroup then
BroadcastRosterUpdate()
end
return wasInGroup
end, "OrganizerState:UnassignFromGroup")
end
Expand Down Expand Up @@ -587,6 +613,7 @@ function OrganizerState:DesignateKeystone(groupIndex, playerID, keystone)
}

Debug:Dev("organizer_state", "DesignateKeystone: group", groupIndex, "keystone from", playerID)
BroadcastRosterUpdate()
return true
end, "OrganizerState:DesignateKeystone")
end
Expand All @@ -605,6 +632,9 @@ function OrganizerState:ClearKeystone(groupIndex)
self.keystones[groupIndex] = nil

Debug:Dev("organizer_state", "ClearKeystone: group", groupIndex, "- had keystone:", hadKeystone)
if hadKeystone then
BroadcastRosterUpdate()
end
return hadKeystone
end, "OrganizerState:ClearKeystone")
end
Expand Down Expand Up @@ -938,6 +968,7 @@ function OrganizerState:LoadFromPersistence()

Debug:Dev("organizer_state", "LoadFromPersistence: Restored", restoredCount, "players from SavedVariables")

BroadcastRosterUpdate()
return true

end, "OrganizerState:LoadFromPersistence")
Expand Down Expand Up @@ -970,6 +1001,7 @@ function OrganizerState:ClearPersistedData()
Debug:User("Poll data cleared successfully")
Debug:Dev("organizer_state", "ClearPersistedData: All state cleared")

BroadcastRosterUpdate()
return true

end, "OrganizerState:ClearPersistedData")
Expand Down Expand Up @@ -1020,4 +1052,4 @@ function OrganizerState:PrintState()
print("=========================")

end, "OrganizerState:PrintState")
end
end
Loading