Skip to content

Commit 12bf669

Browse files
fix(app-shell,plugin-detail): the chatter chain gets a loading signal source (#3209) (#3270)
The discussion panel spent every feed fetch asserting "No comments yet" — a factual claim about the record — then contradicted itself when the rows landed. #3205 gave RecordActivityTimeline the render branch that prefers a loading row over the empty copy, and RecordChatterPanel already forwarded `loading` to it in both positions, but on the chatter chain nothing PRODUCED the signal, so the branch could never fire. `record:activity` computes its own flag and was fixed by #3205 on sight; chatter was not. Closes all four wiring points, because any one left open still ships the empty copy to some user: - RecordDetailView — the host that owns the fetch — derives `feedLoading` from its two reads (sys_comment + sys_activity); - <DiscussionContextProvider loading={feedLoading}> publishes it (the field was already declared on DiscussionContextValue and already read by `record:activity`); - the auto-appended <RecordChatterPanel loading={feedLoading}> — what authored pages that place no discussion slot get — takes it directly; - the `record:chatter` / `record:discussion` renderer forwards `loading={discussion?.loading}`, putting hand-placed blocks on the same chain as the synthesized one. The two reads are parallel promises, so the flag closes over BOTH: it clears on Promise.allSettled, and a REJECTED read counts as an answer (sys_activity 404s without the audit plugin, sys_comment 403s under `enable.feeds: false`) — a permanent spinner would be worse than the bug being fixed. The flag is keyed by `object:recordId` rather than a plain boolean so the first render of a record already reads as loading (no one-frame flash of the empty state) and navigating between records cannot show the previous record's settled answer. No consumer-side tolerance was added: the timeline still does not guess that "no items yet and just mounted" means loading. Same shape as #3165 / #3205 — divergence converges at the producer. Tests pin the rendered outcome (loading row vs. empty copy), not prop passing, on both delivery paths — the synthesized page's DiscussionContext hop and the authored page's auto-append — plus the one-settled-of-two and rejected-read cases. Each of the four wires was mutation-verified red. Claude-Session: https://claude.ai/code/session_01NVPjPzmmAJ2Ngtvgg5MSRa Co-authored-by: Claude <noreply@anthropic.com>
1 parent d2363e7 commit 12bf669

5 files changed

Lines changed: 593 additions & 5 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
"@object-ui/app-shell": patch
3+
"@object-ui/plugin-detail": patch
4+
---
5+
6+
The record discussion panel now says "loading" while it is loading, instead of
7+
"No comments yet" (objectui#3209).
8+
9+
FROM: opening any record page showed the discussion/chatter panel asserting
10+
`No comments yet` for the whole first leg of the page, then contradicting
11+
itself when the comments appeared. TO: the panel shows the loading row until
12+
the feed has actually answered, and only then commits to "this record has no
13+
comments".
14+
15+
objectui#3205 gave `RecordActivityTimeline` the render branch that prefers a
16+
loading row over the empty copy, and `RecordChatterPanel` already forwarded
17+
`loading` to it in both positions — but on the chatter chain **nothing
18+
produced the signal**, so that branch could never fire. `record:activity`
19+
computes its own flag and was visibly fixed by #3205; chatter was not. The
20+
four wiring points are one chain and are all closed here, because any one of
21+
them left open still ships the empty copy to some user:
22+
23+
- `RecordDetailView` — the host that OWNS the feed fetch — now derives a
24+
`feedLoading` flag from its two reads (`sys_comment` + `sys_activity`);
25+
- `<DiscussionContextProvider loading={feedLoading}>` publishes it (the field
26+
was already declared on `DiscussionContextValue`, and already read by
27+
`record:activity`);
28+
- the auto-appended `<RecordChatterPanel loading={feedLoading}>` — the panel
29+
authored pages get when they place no discussion slot — receives it
30+
directly;
31+
- the `record:chatter` / `record:discussion` renderer forwards
32+
`loading={discussion?.loading}`, so a hand-placed block is on the same
33+
chain as the synthesized one.
34+
35+
The two reads run in parallel, so the flag closes over **both**: it clears on
36+
`Promise.allSettled`, and a REJECTED read counts as an answer. A deployment
37+
without the audit plugin 404s `sys_activity` and an object with
38+
`enable.feeds: false` 403s `sys_comment`; neither may pin the panel in a
39+
permanent spinner, which would be a worse bug than the one being fixed. The
40+
flag is keyed by `object:recordId` rather than being a plain boolean, so the
41+
first render of a record already reads as loading (no one-frame flash of the
42+
empty state) and navigating between records cannot show the previous record's
43+
settled answer.
44+
45+
No tolerance was added at the consumer. The timeline still does not guess that
46+
"no items yet and just mounted" means loading — that guess is wrong the moment
47+
a record genuinely has no comments, and the signal belongs to whoever owns the
48+
fetch. Same shape as objectui#3165 / #3205: divergence converges at the
49+
producer.

0 commit comments

Comments
 (0)