Commit f790520
authored
### 🎯 Goal
Fixes three Tier‑1 issues from the bug‑bash triage. Each is an
independent fix kept as its own self‑contained commit (with tests):
- **Closes #2474** — a `message.new` for a not‑yet‑watched channel
inserts an uninitialized channel into the list; rendering it called
`channel.muteStatus()`, which throws `"hasn't been initialized yet"` and
**crashed the whole app**.
- **Closes #2441** — `ChannelList` issued a `queryChannel`
(`channel.watch()`) for **every** `notification.message_new`, even when
the result was discarded, exhausting the rate limit during bulk channel
activity.
- **Closes #2393** — once the shared client is disconnected,
`channel.lastRead()` (called during render in
`useCreateChannelStateContext`) throws `"You can't use a channel after
client.disconnect() was called"`, **crashing the render** with no error
boundary to catch it.
> **#2599 — fixed via the `stream-chat` upstream fix + this PR's
dependency bump.** An earlier `stopWatching`-on-removal attempt here was
implemented and then reverted: it did **not** make removal durable.
`channel.stopWatching()` does not evict the channel from
`client.activeChannels`, so a reconnect (`recoverState` re-watches every
active channel) or a queued/in-flight `message.new` re-added the removed
channel — the exact reported symptom. The real fix evicts
`activeChannels[cid]` on `notification.removed_from_channel` (mirroring
the existing `channel.deleted` handling); it shipped in
**`stream-chat@9.49.0`** (GetStream/stream-chat-js#1788). This PR bumps
the `stream-chat` requirement to `^9.49.0` and adds a regression test —
with the bump in place, `ChannelList` removal is durable and the test
passes.
### 🛠 Implementation details
**#2474 — `fix(ChannelListItem): guard muteStatus against an
uninitialized/disconnected channel`**
- `useIsChannelMuted` reads the mute status through a helper that calls
`channel.muteStatus()` only when the channel is initialized **and not
disconnected**, falling back to a not‑muted status otherwise.
`muteStatus()` throws on an uninitialized channel (`#2474`) and also
after the client disconnects — via `channel.getClient()` — even though
the channel stays initialized (the #2393 failure class on the
channel‑list path).
- The mute‑update subscription now depends on `[channel, client]` and
cleans up via the `unsubscribe` handle returned by `client.on()`. It
previously depended on `[muted]`, so it re‑subscribed on its own output
and could capture a stale channel.
**#2441 — `fix(ChannelList): do not query unfiltered channels on
notification.message_new`**
- Moved the `allowNewMessagesFromUnfilteredChannels` guard **before**
the `getChannel()` call in `handleNotificationMessageNew`, so the flag
actually suppresses the per‑event `watch()` instead of querying first
and throwing the result away. Behaviour with the flag enabled is
unchanged.
**#2393 — `fix(Channel): guard channel methods against a disconnected
client`**
- Guarded `channel.lastRead()` in `useCreateChannelStateContext` with
`!channel.disconnected` so a disconnect while the channel is mounted
does not crash the render.
- `loadMore` now early‑returns when `channel.disconnected`, mirroring
the existing `markRead` guard, to avoid a doomed pagination query.
- Note: the shared‑client race cannot be fully eliminated (the existing
`try/catch` backstops on the async paths remain); this removes the
uncaught **render‑time** crash, which is the part with no safety net.
**#2599 — `fix(deps): require stream-chat ^9.49.0 to evict removed
channels`**
- Bumped `stream-chat` to `^9.49.0` in `peerDependencies` and
`devDependencies`. No `ChannelList` runtime change is required: the
existing `handleNotificationRemovedFromChannel` removes the channel from
the list, and `stream-chat@9.49.0` now evicts it from
`client.activeChannels`, so `recoverState()` won't re‑watch it on
reconnect. The two compose into durable removal.
**Tests** — added test‑first (each test was confirmed to fail before the
fix):
- `useIsChannelMuted` does not throw on an uninitialized channel, nor on
an initialized‑but‑disconnected channel.
- `notification.message_new` does not call `watch()` when
`allowNewMessagesFromUnfilteredChannels={false}`.
- rendering does not crash when the client disconnects while the channel
is mounted.
- `loadMore` does not query when the client is disconnected.
- **#2599:** `notification.removed_from_channel` evicts the channel from
`client.activeChannels` (so reconnect cannot re‑watch it). Verified
red→green — red on `stream-chat@9.47.0`, green after the `^9.49.0` bump.
Verification: `yarn test` (full suite — 2589 passing, incl. ChannelList,
ChannelListItem, Channel, MessageList, Thread), `yarn types`, and ESLint
(`--max-warnings 0`) all clean. #2474, #2441 and #2393 were additionally
confirmed in a real browser against the example app; the #2599
regression test passes against the bumped `stream-chat@9.49.0`.
### 🎨 UI Changes
None — these are crash / correctness / rate‑limit fixes in `ChannelList`
and `Channel` event handling, with no visual changes.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed message-new handling when unfiltered channels aren’t allowed so
unlisted/unqueried channels aren’t watched or added to the list.
* Made mute status lookups safe when a channel isn’t initialized or is
disconnected.
* Prevented pagination and last-read handling from running while the
client/channel is disconnected to avoid crashes.
* **Tests**
* Added coverage for message-new filtering with unfiltered channels
disabled.
* Expanded mute hook tests for uninitialized and disconnected cases.
* Added disconnected-client scenarios for rendering and pagination.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent a768e30 commit f790520
11 files changed
Lines changed: 259 additions & 23 deletions
File tree
- examples
- tutorial
- vite
- src/components
- ChannelListItem/hooks
- __tests__
- ChannelList
- __tests__
- hooks
- Channel
- __tests__
- hooks
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
111 | 111 | | |
112 | 112 | | |
113 | 113 | | |
114 | | - | |
| 114 | + | |
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
| |||
181 | 181 | | |
182 | 182 | | |
183 | 183 | | |
184 | | - | |
| 184 | + | |
185 | 185 | | |
186 | 186 | | |
187 | 187 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
602 | 602 | | |
603 | 603 | | |
604 | 604 | | |
| 605 | + | |
605 | 606 | | |
606 | 607 | | |
607 | 608 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
779 | 780 | | |
780 | 781 | | |
781 | 782 | | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
782 | 824 | | |
783 | 825 | | |
784 | 826 | | |
| |||
Lines changed: 5 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | | - | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
48 | 52 | | |
49 | 53 | | |
50 | 54 | | |
| |||
Lines changed: 91 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1254 | 1254 | | |
1255 | 1255 | | |
1256 | 1256 | | |
| 1257 | + | |
| 1258 | + | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
| 1272 | + | |
| 1273 | + | |
| 1274 | + | |
| 1275 | + | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
| 1300 | + | |
| 1301 | + | |
| 1302 | + | |
| 1303 | + | |
| 1304 | + | |
| 1305 | + | |
| 1306 | + | |
1257 | 1307 | | |
1258 | 1308 | | |
1259 | 1309 | | |
| |||
1419 | 1469 | | |
1420 | 1470 | | |
1421 | 1471 | | |
| 1472 | + | |
| 1473 | + | |
| 1474 | + | |
| 1475 | + | |
| 1476 | + | |
| 1477 | + | |
| 1478 | + | |
| 1479 | + | |
| 1480 | + | |
| 1481 | + | |
| 1482 | + | |
| 1483 | + | |
| 1484 | + | |
| 1485 | + | |
| 1486 | + | |
| 1487 | + | |
| 1488 | + | |
| 1489 | + | |
| 1490 | + | |
| 1491 | + | |
| 1492 | + | |
| 1493 | + | |
| 1494 | + | |
| 1495 | + | |
| 1496 | + | |
| 1497 | + | |
| 1498 | + | |
| 1499 | + | |
| 1500 | + | |
| 1501 | + | |
| 1502 | + | |
| 1503 | + | |
| 1504 | + | |
| 1505 | + | |
| 1506 | + | |
| 1507 | + | |
| 1508 | + | |
| 1509 | + | |
| 1510 | + | |
| 1511 | + | |
| 1512 | + | |
1422 | 1513 | | |
1423 | 1514 | | |
1424 | 1515 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
163 | 163 | | |
164 | 164 | | |
165 | 165 | | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
166 | 175 | | |
167 | 176 | | |
168 | 177 | | |
| |||
174 | 183 | | |
175 | 184 | | |
176 | 185 | | |
177 | | - | |
178 | | - | |
179 | | - | |
180 | | - | |
181 | 186 | | |
182 | 187 | | |
183 | 188 | | |
| |||
Lines changed: 78 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
Lines changed: 21 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
7 | 22 | | |
8 | 23 | | |
9 | 24 | | |
10 | | - | |
| 25 | + | |
11 | 26 | | |
12 | 27 | | |
13 | | - | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
14 | 31 | | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
| 32 | + | |
| 33 | + | |
19 | 34 | | |
20 | 35 | | |
21 | 36 | | |
0 commit comments