Skip to content

fix(messages): remove duplicate channel header causing stacked rendering - #2167

Closed
hognek wants to merge 1 commit into
jaylfc:devfrom
hognek:fix/2124-duplicate-header
Closed

fix(messages): remove duplicate channel header causing stacked rendering#2167
hognek wants to merge 1 commit into
jaylfc:devfrom
hognek:fix/2124-duplicate-header

Conversation

@hognek

@hognek hognek commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Problem

The conversation header block in Messages rendered twice on desktop — @ icon, agent emoji, channel name, guide link (❓), pin badge, search button, member count, and description all appeared stacked.

Closes #2124

Root Cause

Both MessagesApp.tsx and MessageList.tsx contained an identical channel header:

  • MessagesApp.tsx (formerly line 1642): Rendered inside messageAreaUI
  • MessageList.tsx (line 214): Rendered at the top of the message list component

Since messageAreaUI rendered its own header AND then passed control to <MessageList> (which ALSO rendered its own header), the result was two identical headers stacked on top of each other.

Fix

Removed the duplicate channel header from MessagesApp.tsx and cleaned up the five imports that became dead code:

  • MessagesSquare, Search from lucide-react
  • PinBadge from ./chat/PinBadge
  • PinnedMessagesPopover from ./chat/PinnedMessagesPopover (type import preserved)
  • resolveAgentEmoji from @/lib/agent-emoji

The MessageList component already handles all channel header rendering correctly, including the same event handlers for settings, pinned messages, threads, and search toggles. The MessageList tests confirm all 49 tests pass, including 4 channel-header-specific tests.

Verification

  • TypeScript compilation: npx tsc --noEmit — zero errors
  • Desktop build: npm run build — succeeds
  • MessagesApp tests: MessagesApp.agentSections.test.ts / MessagesApp.stallWatch.test.ts — 22/22 pass
  • MessageList tests: MessageList.test.tsx — 49/49 pass (including channel header, pin badge, toggle buttons)

The channel header block in MessagesApp.tsx (type icon, agent emoji,
channel name, settings, guide link, pin/search, member count,
description) was a near-exact copy of the header already rendered
by MessageList.tsx at line 214. Since messageAreaUI renders both
its own header and <MessageList>, the result was two stacked headers.

Remove the duplicate header from MessagesApp.tsx and clean up the
five imports that became unused (MessagesSquare, Search, PinBadge,
PinnedMessagesPopover, resolveAgentEmoji). The PinnedMessage type
import is preserved.

Closes jaylfc#2124
@hognek
hognek marked this pull request as ready for review July 27, 2026 12:48
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@hognek, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 5 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a0033191-5d4e-48cf-9535-6f0003f1e484

📥 Commits

Reviewing files that changed from the base of the PR and between 45d17f5 and 0e7cd87.

📒 Files selected for processing (1)
  • desktop/src/apps/MessagesApp.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gitar-bot

gitar-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

Important

You are using the Gitar free plan. Upgrade to unlock code review, CI analysis, auto-apply, custom automations, and more.

Gitar

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Fix Messages desktop stacked channel header by removing duplicate render

🐞 Bug fix 🕐 10-20 Minutes

Grey Divider

AI Description

• Remove the redundant channel header from MessagesApp to prevent double-rendering on desktop
• Rely on MessageList as the single owner of channel header UI and interactions
• Clean up unused imports left behind after header removal
Diagram

graph TD
  msgApp["MessagesApp"] --> msgList["MessageList"] --> header("Channel header") --> actions("Header actions")
  msgApp --> state("Channel + UI state") --> header
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Extract a shared ChannelHeader component
  • ➕ Avoids future duplication if multiple parents need the header
  • ➕ Improves testability of header behavior in isolation
  • ➖ More refactor surface area than needed for this bug fix
  • ➖ Requires additional prop/API design work now
2. Keep header in MessagesApp and add a prop to MessageList to disable its header
  • ➕ Keeps layout control centralized in the app container
  • ➕ Allows reuse of MessageList in contexts without a header
  • ➖ Creates a more complex MessageList API surface
  • ➖ Easy to misconfigure and reintroduce header duplication

Recommendation: Current approach (make MessageList the single owner of the channel header) is the best low-risk fix: it removes duplication at the source and preserves existing header behavior/tests. Consider extracting a shared ChannelHeader later only if another screen genuinely needs the same header UI.

Files changed (1) +1 / -122

Bug fix (1) +1 / -122
MessagesApp.tsxRemove duplicate channel header and dead imports +1/-122

Remove duplicate channel header and dead imports

• Deletes the channel header block previously rendered in MessagesApp’s message area to prevent stacked header rendering on desktop. Removes now-unused imports (lucide icons, pin/search header helpers), while keeping the type import needed for pinned message state.

desktop/src/apps/MessagesApp.tsx

@kilo-code-bot

kilo-code-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (1 file)
  • desktop/src/apps/MessagesApp.tsx

Reviewed by step-3.7-flash · Input: 97.9K · Output: 11K · Cached: 1.3M

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 35 rules

Grey Divider


Remediation recommended

1. Silent pin jump failure 🐞 Bug ≡ Correctness
Description
After removing the MessagesApp header, the remaining pinned-message “Jump to” handler (in
MessageList) no longer reports an error when the pinned message is not in the DOM, so the action
becomes a silent no-op. This can happen because MessagesApp only fetches the last 50 messages while
pin listing is not limited, and MessagesApp already has a sendError banner that could surface the
failure.
Code

desktop/src/apps/MessagesApp.tsx[L1692-1700]

-                        const el = document.querySelector(`[data-message-id="${id}"]`) as HTMLElement | null;
-                        if (el) {
-                          el.scrollIntoView({ behavior: "smooth", block: "center" });
-                          el.classList.add("data-highlight");
-                          setTimeout(() => el.classList.remove("data-highlight"), 2000);
-                        } else {
-                          // Only ~50 messages load; a pin older than that is not in the DOM.
-                          setSendError("Message is older than the loaded history");
-                        }
Relevance

⭐⭐⭐ High

Team repeatedly accepts surfacing failures vs silent no-ops (error/loading handling accepted in PRs
#307, #377, #260).

PR-#307
PR-#377
PR-#260

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
MessagesApp only fetches 50 messages, while pins are listed independently; therefore a pin can
reference a message not currently rendered, making document.querySelector return null. The current
MessageList onJumpTo handler only scrolls when the element exists and otherwise does nothing, and
although MessagesApp renders a sendError banner, MessageList has no path to set it for this case.

desktop/src/apps/MessagesApp.tsx[541-548]
desktop/src/lib/chat-messages-api.ts[18-23]
desktop/src/apps/chat/MessageList.tsx[258-276]
desktop/src/apps/MessagesApp.tsx[1785-1790]
desktop/src/apps/MessagesApp.tsx[1637-1653]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PinnedMessagesPopover` “Jump to” can target a pinned message that isn’t present in the current DOM (e.g., older than the 50-message history window). Before this PR, the (now-removed) MessagesApp header path handled this by setting `sendError`; now the surviving `MessageList` handler does nothing when `document.querySelector` returns null.

## Issue Context
- Messages are fetched with `?limit=50`, so older pinned messages may not be rendered.
- Pins are listed without an explicit limit.
- MessagesApp already renders a `sendError` banner, but MessageList has no way to trigger it today.

## Fix Focus Areas
- desktop/src/apps/chat/MessageList.tsx[258-279]
- desktop/src/apps/MessagesApp.tsx[1637-1708]
- desktop/src/apps/MessagesApp.tsx[1785-1790]

## Suggested fix
1. Add an optional callback prop to `MessageList` (e.g. `onPinnedJumpMissing?: (id: string) => void`).
2. In `MessageList`’s `onJumpTo`, look up the element first; if missing, call `onPinnedJumpMissing` and **do not** close the popover.
3. In `MessagesApp`, pass `onPinnedJumpMissing={() => setSendError("Message is older than the loaded history")}` (or a similar message) so users get feedback via the existing error banner.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines -1692 to -1700
const el = document.querySelector(`[data-message-id="${id}"]`) as HTMLElement | null;
if (el) {
el.scrollIntoView({ behavior: "smooth", block: "center" });
el.classList.add("data-highlight");
setTimeout(() => el.classList.remove("data-highlight"), 2000);
} else {
// Only ~50 messages load; a pin older than that is not in the DOM.
setSendError("Message is older than the loaded history");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Silent pin jump failure 🐞 Bug ≡ Correctness

After removing the MessagesApp header, the remaining pinned-message “Jump to” handler (in
MessageList) no longer reports an error when the pinned message is not in the DOM, so the action
becomes a silent no-op. This can happen because MessagesApp only fetches the last 50 messages while
pin listing is not limited, and MessagesApp already has a sendError banner that could surface the
failure.
Agent Prompt
## Issue description
`PinnedMessagesPopover` “Jump to” can target a pinned message that isn’t present in the current DOM (e.g., older than the 50-message history window). Before this PR, the (now-removed) MessagesApp header path handled this by setting `sendError`; now the surviving `MessageList` handler does nothing when `document.querySelector` returns null.

## Issue Context
- Messages are fetched with `?limit=50`, so older pinned messages may not be rendered.
- Pins are listed without an explicit limit.
- MessagesApp already renders a `sendError` banner, but MessageList has no way to trigger it today.

## Fix Focus Areas
- desktop/src/apps/chat/MessageList.tsx[258-279]
- desktop/src/apps/MessagesApp.tsx[1637-1708]
- desktop/src/apps/MessagesApp.tsx[1785-1790]

## Suggested fix
1. Add an optional callback prop to `MessageList` (e.g. `onPinnedJumpMissing?: (id: string) => void`).
2. In `MessageList`’s `onJumpTo`, look up the element first; if missing, call `onPinnedJumpMissing` and **do not** close the popover.
3. In `MessagesApp`, pass `onPinnedJumpMissing={() => setSendError("Message is older than the loaded history")}` (or a similar message) so users get feedback via the existing error banner.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

jaylfc added a commit that referenced this pull request Jul 27, 2026
…2169)

* fix(messages): remove duplicate channel header causing stacked rendering

The channel header block in MessagesApp.tsx (type icon, agent emoji,
channel name, settings, guide link, pin/search, member count,
description) was a near-exact copy of the header already rendered
by MessageList.tsx at line 214. Since messageAreaUI renders both
its own header and <MessageList>, the result was two stacked headers.

Remove the duplicate header from MessagesApp.tsx and clean up the
five imports that became unused (MessagesSquare, Search, PinBadge,
PinnedMessagesPopover, resolveAgentEmoji). The PinnedMessage type
import is preserved.

Closes #2124

* tsk-55xqwq  [OPEN]  Land #2167: rebase the duplicate-channel-header fi

---------

Co-authored-by: Hogne <227774406+hognek@users.noreply.github.com>
@jaylfc

jaylfc commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Superseded by #2169, which landed the same duplicate-channel-header fix rebased cleanly onto dev. Closing to avoid two PRs touching MessagesApp.tsx for the same bug.

@jaylfc jaylfc closed this Jul 27, 2026
hognek added a commit to hognek/tinyagentos that referenced this pull request Jul 29, 2026
…er fi (jaylfc#2169)

* fix(messages): remove duplicate channel header causing stacked rendering

The channel header block in MessagesApp.tsx (type icon, agent emoji,
channel name, settings, guide link, pin/search, member count,
description) was a near-exact copy of the header already rendered
by MessageList.tsx at line 214. Since messageAreaUI renders both
its own header and <MessageList>, the result was two stacked headers.

Remove the duplicate header from MessagesApp.tsx and clean up the
five imports that became unused (MessagesSquare, Search, PinBadge,
PinnedMessagesPopover, resolveAgentEmoji). The PinnedMessage type
import is preserved.

Closes jaylfc#2124

* tsk-55xqwq  [OPEN]  Land jaylfc#2167: rebase the duplicate-channel-header fi

---------

Co-authored-by: Hogne <227774406+hognek@users.noreply.github.com>
hognek added a commit to hognek/tinyagentos that referenced this pull request Jul 30, 2026
…er fi (jaylfc#2169)

* fix(messages): remove duplicate channel header causing stacked rendering

The channel header block in MessagesApp.tsx (type icon, agent emoji,
channel name, settings, guide link, pin/search, member count,
description) was a near-exact copy of the header already rendered
by MessageList.tsx at line 214. Since messageAreaUI renders both
its own header and <MessageList>, the result was two stacked headers.

Remove the duplicate header from MessagesApp.tsx and clean up the
five imports that became unused (MessagesSquare, Search, PinBadge,
PinnedMessagesPopover, resolveAgentEmoji). The PinnedMessage type
import is preserved.

Closes jaylfc#2124

* tsk-55xqwq  [OPEN]  Land jaylfc#2167: rebase the duplicate-channel-header fi

---------

Co-authored-by: Hogne <227774406+hognek@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants