Skip to content

Hold channel loading state until the feed is complete so it opens at the latest message#3583

Merged
trunk-io[bot] merged 6 commits into
mainfrom
posthog-code/channel-open-land-at-latest
Jul 20, 2026
Merged

Hold channel loading state until the feed is complete so it opens at the latest message#3583
trunk-io[bot] merged 6 commits into
mainfrom
posthog-code/channel-open-land-at-latest

Conversation

@adboio

@adboio adboio commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Problem

Opening a channel from the sidebar feels slow and doesn't start you at the most recent message. The feed scroller (ChatMessageScrollerProvider defaultScrollPosition="end") applies its initial end-scroll exactly once, as soon as it has any content — and by design it never re-scrolls when more rows arrive (no forced scroll). Today the scroller mounts around partial content: the synthesized "joined" opener renders the moment the backend channel resolves, while the task cards and system messages are still fetching (the loading gate also ignored useChannelFeedMessages' loading state). The one-shot end-scroll spends itself on that near-empty list, so the real cards land off-screen below.

Changes

  • WebsiteChannelHome: fold useChannelFeedMessages' isLoading (already returned, previously unused) into the feed loading gate.
  • ChannelFeedView: scroll to the latest message on channel switch — channel-to-channel navigation swaps content without a route remount, so the scroller's one-shot defaultScrollPosition only covers the channel it mounted with and a revisited channel kept the previous channel's scroll offset (often the top). Implemented as a ref on the scroller viewport plus a layout effect keyed on the channel id that sets scrollTop = scrollHeight before paint; no remount, no extra components.
  • ChannelFeedView: hold the loading spinner while the feed is still loading even if some entries already exist, so the scroller mounts once with the complete feed and its initial end-scroll lands at the latest message. Optimistic pending kickoffs still bail out of the spinner immediately, and warm reopens render instantly from cache with no repeat spinner.

No new scroll code — verified against the scroller engine source that defaultScrollPosition="end" handles the landing correctly once the mount isn't premature.

Why: raised in project-bluebird — opening a channel should render at the most recent message immediately instead of showing a half-loaded feed scrolled to the top.

How did you test this?

  • pnpm --filter @posthog/ui typecheck and pnpm --filter @posthog/ui test (200 files / 1755 tests pass), pnpm lint clean on changed files.
  • Scroll/paint timing is best eyeballed live — worth a quick check in the running app that a cold channel open lands at the bottom and a reopen shows no spinner.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Created with PostHog Code

…the latest message

The channel scroller's one-shot initial end-scroll fired against partial
content (the synthesized "joined" opener renders as soon as the backend
channel resolves, before the task cards arrive), and the engine never
re-scrolls for late rows unless autoScroll forces it. Fold the feed-messages
loading state into the gate and hold the spinner while any feed data is
still loading, so the scroller mounts once with complete data and lands at
the most recent message.

Generated-By: PostHog Code
Task-Id: d887d524-7d87-4d7a-b38c-b20289937ad1
@trunk-io

trunk-io Bot commented Jul 20, 2026

Copy link
Copy Markdown

😎 Merged successfully - details.

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit d318994.

adboio added 3 commits July 20, 2026 10:05
Channel-to-channel navigation swaps the feed content without a route
remount, so the scroller provider persisted and its one-shot
defaultScrollPosition never re-applied — a revisited channel kept the
previous channel's scroll offset (often the top). Keying the provider by
channelId fresh-mounts it per channel, re-firing the initial end-scroll.

Generated-By: PostHog Code
Task-Id: d887d524-7d87-4d7a-b38c-b20289937ad1
Keying the provider by channelId re-fired the initial end-scroll but
rebuilt the entire feed subtree (rows, observers, reply polling) on every
switch, making channel changes feel sluggish. Replace the key with a
headless LandAtLatestOnChannelChange component that imperatively calls
scrollToEnd in a layout effect when the channel id changes — same commit
as the swapped rows, before paint, no remount.

Generated-By: PostHog Code
Task-Id: d887d524-7d87-4d7a-b38c-b20289937ad1
Drop the LandAtLatestOnChannelChange headless component. The same behavior
is a ref on the scroller viewport plus one layout effect keyed on channelId
that sets scrollTop = scrollHeight — the engine composes external viewport
refs, and first mounts are still covered by defaultScrollPosition.

Generated-By: PostHog Code
Task-Id: d887d524-7d87-4d7a-b38c-b20289937ad1
@adboio
adboio marked this pull request as ready for review July 20, 2026 14:28
@adboio adboio added the Stamphog This will request an autostamp by stamphog on small changes label Jul 20, 2026
github-actions[bot]
github-actions Bot previously approved these changes Jul 20, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low-risk UI fix: holds the channel loading state until the full feed is available so the one-shot scroll-to-end lands correctly, plus a useLayoutEffect to handle channel switches without remount. Changes are confined to view layer, touch no business logic, API contracts, or data models.

@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
packages/ui/src/features/canvas/components/ChannelFeedView.tsx:714
**Pending Row Mounts Incomplete Feed**

If a kickoff is submitted while the initial tasks or system messages are still loading, `pending.length > 0` mounts the scroller around partial content and consumes its one-shot end scroll. The remaining rows then arrive without changing `channelId`, so neither the default scroll nor the layout effect runs again and the channel can open above its latest message.

Reviews (1): Last reviewed commit: "Simplify channel-switch scroll to an inl..." | Re-trigger Greptile

// mounting the scroller around partial content spends its one-shot initial
// end-scroll on a near-empty list, and it never re-scrolls when the real
// cards arrive. Pending kickoffs bail out so an optimistic post shows now.
if (isLoading && pending.length === 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Pending Row Mounts Incomplete Feed

If a kickoff is submitted while the initial tasks or system messages are still loading, pending.length > 0 mounts the scroller around partial content and consumes its one-shot end scroll. The remaining rows then arrive without changing channelId, so neither the default scroll nor the layout effect runs again and the channel can open above its latest message.

Rule Used: In cases where there are multiple states to handle... (source)

Learned From
PostHog/posthog#32610

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/features/canvas/components/ChannelFeedView.tsx
Line: 714

Comment:
**Pending Row Mounts Incomplete Feed**

If a kickoff is submitted while the initial tasks or system messages are still loading, `pending.length > 0` mounts the scroller around partial content and consumes its one-shot end scroll. The remaining rows then arrive without changing `channelId`, so neither the default scroll nor the layout effect runs again and the channel can open above its latest message.

**Rule Used:** In cases where there are multiple states to handle... ([source](https://app.greptile.com/posthog-org-19734/-/custom-context?memory=b99946fe-4cce-4bff-bdc0-8480d200548a))

**Learned From**
[PostHog/posthog#32610](https://github.com/PostHog/posthog/pull/32610)

How can I resolve this? If you propose a fix, please make it concise.

CI's biome ci errored on the scroll effect's [channelId] dependency (not
referenced in the body — it's the re-run trigger). Suppress with the repo's
biome-ignore idiom and cut the added comments down to the essentials.

Generated-By: PostHog Code
Task-Id: d887d524-7d87-4d7a-b38c-b20289937ad1
@github-actions
github-actions Bot dismissed their stale review July 20, 2026 14:32

New commits pushed (delta classified non_trivial_delta) — stamphog approval dismissed; re-review running automatically.

github-actions[bot]
github-actions Bot previously approved these changes Jul 20, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small, view-layer-only scroll fix. No business logic, API contracts, data models, or infrastructure involved. The bot-flagged edge case about pending rows during loading is a scroll-position UX concern, not a showstopper.

…ckoff

Greptile P1: submitting a kickoff mid-load mounts the scroller around
partial content via the pending escape hatch, consuming the one-shot end
scroll; the remaining rows then arrive with no re-scroll. Extend the
scroll effect to also fire when isLoading settles, so the feed lands at
the latest message in that path too.

Generated-By: PostHog Code
Task-Id: d887d524-7d87-4d7a-b38c-b20289937ad1
@github-actions
github-actions Bot dismissed their stale review July 20, 2026 14:50

New commits pushed (delta classified non_trivial_delta) — stamphog approval dismissed; re-review running automatically.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pure view-layer scroll fix confined to two UI components. The greptile bot comment about pending rows is from an older commit and describes a narrow edge-case scroll-position UX quirk — not a correctness, data, or crash risk. The useLayoutEffect dependency on isLoading means the scroll-to-end fires when loading completes, which covers the primary scenario.

@trunk-io
trunk-io Bot merged commit ef9c7b2 into main Jul 20, 2026
32 checks passed
@trunk-io
trunk-io Bot deleted the posthog-code/channel-open-land-at-latest branch July 20, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Stamphog This will request an autostamp by stamphog on small changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant