You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Users reported that when clicking a notification to navigate to a room, sometimes the messages would fail to load, and the "load more" function would not trigger.
12
+
Investigation revealed a race condition in `ChatController`:
13
+
14
+
- The timeline loading logic (`_tryLoadTimeline`) was called in `initState` but used the `room` variable.
15
+
- The `room` variable was previously initialized in the `build` method of `ChatView`.
16
+
- When navigating via deep link or notification, the build method might not have run before `initState` tried to access `room`, leading to initialization with a null or stale room, or simply failing to load the correct timeline for the context event.
17
+
18
+
Additionally, when switching between rooms via notifications (e.g., being in Room A and clicking a notification for Room B), the `ChatController` did not cleanly detect the change and re-initialize the timeline, leading to mixed state.
19
+
20
+
## Decision
21
+
22
+
We decided to move the room initialization logic to the earliest possible point in the `ChatController` lifecycle and explicitly handle room updates.
23
+
24
+
### 1. Early Room Initialization
25
+
26
+
Moved `room` initialization from `ChatView.build` to `ChatController.initState`.
27
+
28
+
- A new method `_initRoom()` was created to initialize `matrix` client and `room`.
29
+
-`_initRoom()` is called at the beginning of `initState`.
30
+
31
+
### 2. Explicit Update Handling
32
+
33
+
Updated `didUpdateWidget` in `ChatController`:
34
+
35
+
- Checks if `widget.roomId` has changed compared to `oldWidget.roomId`.
36
+
- If changed, it performs a full room state reset and re-initialization:
37
+
- Unsubscribes existing room event listeners (canceling any active timers or streams).
38
+
- Clears and re-fetches pinned events.
39
+
- Resets cached presence and participants data.
40
+
- Calls `_initRoom()` to switch the internal matrix room reference.
41
+
- Re-subscribes to room listeners for the new room.
42
+
- Calls `_tryLoadTimeline()` to load the initial messages for the new room.
43
+
- This ensures all room-scoped resources are fully refreshed and correctly isolated on room switch.
44
+
45
+
### 3. Removal of Side-Effects from Build
46
+
47
+
Removed the assignment of `controller.room` inside `ChatView.build`. The build method now strictly reads from the controller and returns early if `room` is null (though `initState` protections make this unlikely to happen in a visible way).
48
+
49
+
## Consequences
50
+
51
+
### Positive
52
+
53
+
-**Reliability:** Notifications and deep links now reliably load the correct room and timeline.
54
+
-**Correctness:** Timeline loading (`_tryLoadTimeline`) now is guaranteed to have a valid `room` instance.
55
+
-**Maintainability:**`ChatView.build` is purer and has fewer side effects. State initialization is where it belongs, in `initState`.
56
+
57
+
### Negative
58
+
59
+
-**Boilerplate:** We have to manually handle `didUpdateWidget` to detect changes that `build` would have picked up automatically (though incorrectly for this use case).
0 commit comments