refactor(desktop): host the markdown-doc provider in GuardedChannelPane

ChannelScreen is over the file-size ratchet on main, and wrapping its
140-line channel-pane JSX in MarkdownDocViewerProvider re-indented the
block past the line limit. GuardedChannelPane — the wrapper the channel
branch already mounts and the forum branch never does — is the natural
host: same provider scoping (forum FileCards keep download behavior),
no indent shift, and ChannelScreen shrinks back under its allowance.

Signed-off-by: Riley Crane <rileycrane@squareup.com>
Co-authored-by: Fizz <d4ec90109b43092b0ec94cf136850915a3732a29ab3089a1dec43d2ed43dd6eb@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Riley Crane
2026-08-25 13:43:31 -07:00
parent e505e54610
commit 7aae92ba52
2 changed files with 172 additions and 165 deletions

View File

@@ -55,7 +55,6 @@ import { useHuddleThreadIsolation } from "@/features/channels/ui/useHuddleThread
import { AgentSessionProvider } from "@/shared/context/AgentSessionContext";
import { ProfilePanelProvider } from "@/shared/context/ProfilePanelContext";
import { useChannelPaneOpeners } from "@/features/channels/ui/useChannelPaneOpeners";
import { MarkdownDocViewerProvider } from "@/shared/ui/markdown/markdownDocViewerContext";
import { useMainInsetRef } from "@/shared/layout/MainInsetContext";
import { channelContentTopPaddingMeasurement } from "@/shared/layout/chromeLayout";
import { useMeasuredCssVariable } from "@/shared/layout/useMeasuredCssVariable";
@@ -833,166 +832,156 @@ export function ChannelScreen({
searchTarget,
)
) : (
// Forum has no doc-panel host; its FileCards keep downloading.
<MarkdownDocViewerProvider value={handleOpenMarkdownDoc}>
<React.Suspense
fallback={
<ChannelScreenLoadingFallback
isHuddleTranscript={isHuddleTranscript}
/>
}
>
{searchForwarding.renderSearchAwareChannel(
<GuardedChannelPane
activeChannel={activeChannel}
activityAgents={channelAgentSessionAgents}
agentPubkeys={agentPubkeys}
agentPubkeysPending={agentPubkeysPending}
agentSessionAgents={agentSessionAgents}
autoSendDraftKey={autoSendDraftKey}
onAutoSendComplete={clearAutoSend}
botTypingEntries={botTypingEntries}
channelManagementOpen={channelManagementOpen}
currentPubkey={currentPubkey}
canResetThreadPanelWidth={canResetThreadPanelWidth}
fetchOlder={fetchOlder}
header={channelHeader}
hasOlderMessages={hasOlderMessages}
historyExhausted={historyExhausted}
onAddAgent={handleOpenAddBot}
onBrowseChannels={openBrowseChannels}
onCreateChannel={openCreateChannel}
onOpenMembers={handleOpenMembersSidebar}
isFetchingOlder={isFetchingOlder}
isHuddleTranscript={isHuddleTranscript}
entranceMessageId={welcomeEntranceMessageId}
onEntranceMessageComplete={handleWelcomeEntranceComplete}
welcomeKickoffStage={welcomeKickoffStage}
welcomeKickoffSettingUp={welcomeKickoffSettingUp}
editTarget={
editTargetMessage
? buildMessageComposerEditTarget(
editTargetMessage,
messageProfiles,
(pubkey) =>
knownAgentPubkeys.has(pubkey) ||
!!messageProfiles?.[pubkey]?.isAgent,
)
: null
}
followThreadById={followThread}
unfollowThreadById={unfollowThread}
isFollowingThreadById={isFollowingThread}
isMessageUnreadById={isMessageUnread}
isFollowingThread={isNotifiedForEffectiveThread}
isSending={sendMessageMutation.isPending}
isSinglePanelView={isSinglePanelView}
isTimelineLoading={isTimelineLoading}
messages={timelineMessages}
threadSummaries={threadSummaries}
huddleThreadRepliesError={huddleThreadRepliesError}
onRetryHuddleThreadReplies={onRetryHuddleThreadReplies}
onCancelEdit={handleCancelEdit}
onCancelThreadReply={handleCancelThreadReply}
onChannelManagementDeleted={
handleChannelManagementDeleted
}
onFollowThread={
effectiveOpenThreadHeadId != null &&
!isNotifiedForEffectiveThread
? () => followThread(effectiveOpenThreadHeadId)
: undefined
}
onUnfollowThread={
effectiveOpenThreadHeadId != null &&
isNotifiedForEffectiveThread
? () => unfollowThread(effectiveOpenThreadHeadId)
: undefined
}
onCloseAgentSession={handleCloseAgentSession}
onBackFromAgentSession={
hasAgentSessionReturnTarget
? handleBackFromAgentSession
: undefined
}
onCloseChannelManagement={handleCloseChannelManagement}
onCloseThread={handleCloseThread}
onDelete={
activeChannel?.archivedAt ? undefined : handleDelete
}
onEdit={
activeChannel?.archivedAt ? undefined : handleEdit
}
onEditSave={
activeChannel?.archivedAt ? undefined : handleEditSave
}
onMarkUnread={handleMessageMarkUnread}
onMarkRead={handleMessageMarkRead}
onExpandThreadReplies={handleExpandThreadReplies}
onOpenAgentSession={handleOpenAgentSession}
onOpenDm={handleOpenDm}
onOpenProfilePanel={handleOpenProfilePanel}
onResetThreadPanelWidth={handleThreadPanelWidthReset}
onCloseProfilePanel={handleCloseProfilePanel}
onOpenThread={handleOpenThreadAndCloseAgentSession}
onSelectThreadReplyTarget={handleSelectThreadReplyTarget}
onSendMessage={handleSendMessage}
onSendToChannel={handleSendToChannel}
onSendVideoReviewComment={effectiveSendVideoReviewComment}
onSendThreadReply={handleSendThreadReply}
onThreadScrollTargetResolved={() =>
setThreadScrollTargetId(null)
}
onThreadPanelResizeStart={handleThreadPanelResizeStart}
onTargetReached={() =>
clearMessageRouteTarget({ replace: true })
}
onToggleReaction={effectiveToggleReaction}
openAgentSessionChannelId={openAgentSessionChannelId}
openAgentSessionPubkey={openAgentSessionPubkey}
openThreadHeadId={effectiveOpenThreadHeadId}
shouldShowThreadSkeleton={shouldShowThreadSkeleton}
onProfilePanelViewChange={setProfilePanelView}
onProfilePanelTabChange={setProfilePanelTab}
markdownDocName={markdownDocName}
markdownDocUrl={markdownDocUrl}
onCloseMarkdownDoc={closeMarkdownDoc}
profilePanelPubkey={profilePanelPubkey}
profilePanelTab={profilePanelTab}
profilePanelView={profilePanelView}
personaLookup={personaLookup}
profiles={messageProfiles}
ownerProfiles={messageOwnerProfiles}
firstUnreadMessageId={firstUnreadMessageId}
unreadCount={unreadCount}
targetMessageId={mainTimelineTargetMessageId}
threadAllMessages={displayedThreadAllMessages}
threadHeadMessage={displayedThreadHeadMessage}
threadMessages={displayedThreadMessages}
threadMessagesPending={threadRepliesQuery.isPending}
threadMessagesError={threadRepliesQuery.isError}
onRetryThreadReplies={() => {
void threadRepliesQuery.refetch();
}}
threadPanelWidthPx={threadPanelWidthPx}
threadTypingPubkeys={threadTypingPubkeys}
threadReplyTargetMessage={
displayedThreadReplyTargetMessage
}
threadScrollTargetId={threadScrollTargetId}
threadUnreadCounts={threadUnreadCounts}
threadReplyUnreadCounts={threadReplyUnreadCounts}
threadFirstUnreadReplyId={
displayedThreadFirstUnreadReplyId
}
isJoining={joinChannelMutation.isPending}
onJoinChannel={joinChannelMutation.mutateAsync}
typingPubkeys={humanTypingPubkeys}
/>,
searchTarget,
)}
</React.Suspense>
</MarkdownDocViewerProvider>
<React.Suspense
fallback={
<ChannelScreenLoadingFallback
isHuddleTranscript={isHuddleTranscript}
/>
}
>
{searchForwarding.renderSearchAwareChannel(
<GuardedChannelPane
onOpenMarkdownDoc={handleOpenMarkdownDoc}
activeChannel={activeChannel}
activityAgents={channelAgentSessionAgents}
agentPubkeys={agentPubkeys}
agentPubkeysPending={agentPubkeysPending}
agentSessionAgents={agentSessionAgents}
autoSendDraftKey={autoSendDraftKey}
onAutoSendComplete={clearAutoSend}
botTypingEntries={botTypingEntries}
channelManagementOpen={channelManagementOpen}
currentPubkey={currentPubkey}
canResetThreadPanelWidth={canResetThreadPanelWidth}
fetchOlder={fetchOlder}
header={channelHeader}
hasOlderMessages={hasOlderMessages}
historyExhausted={historyExhausted}
onAddAgent={handleOpenAddBot}
onBrowseChannels={openBrowseChannels}
onCreateChannel={openCreateChannel}
onOpenMembers={handleOpenMembersSidebar}
isFetchingOlder={isFetchingOlder}
isHuddleTranscript={isHuddleTranscript}
entranceMessageId={welcomeEntranceMessageId}
onEntranceMessageComplete={handleWelcomeEntranceComplete}
welcomeKickoffStage={welcomeKickoffStage}
welcomeKickoffSettingUp={welcomeKickoffSettingUp}
editTarget={
editTargetMessage
? buildMessageComposerEditTarget(
editTargetMessage,
messageProfiles,
(pubkey) =>
knownAgentPubkeys.has(pubkey) ||
!!messageProfiles?.[pubkey]?.isAgent,
)
: null
}
followThreadById={followThread}
unfollowThreadById={unfollowThread}
isFollowingThreadById={isFollowingThread}
isMessageUnreadById={isMessageUnread}
isFollowingThread={isNotifiedForEffectiveThread}
isSending={sendMessageMutation.isPending}
isSinglePanelView={isSinglePanelView}
isTimelineLoading={isTimelineLoading}
messages={timelineMessages}
threadSummaries={threadSummaries}
huddleThreadRepliesError={huddleThreadRepliesError}
onRetryHuddleThreadReplies={onRetryHuddleThreadReplies}
onCancelEdit={handleCancelEdit}
onCancelThreadReply={handleCancelThreadReply}
onChannelManagementDeleted={handleChannelManagementDeleted}
onFollowThread={
effectiveOpenThreadHeadId != null &&
!isNotifiedForEffectiveThread
? () => followThread(effectiveOpenThreadHeadId)
: undefined
}
onUnfollowThread={
effectiveOpenThreadHeadId != null &&
isNotifiedForEffectiveThread
? () => unfollowThread(effectiveOpenThreadHeadId)
: undefined
}
onCloseAgentSession={handleCloseAgentSession}
onBackFromAgentSession={
hasAgentSessionReturnTarget
? handleBackFromAgentSession
: undefined
}
onCloseChannelManagement={handleCloseChannelManagement}
onCloseThread={handleCloseThread}
onDelete={
activeChannel?.archivedAt ? undefined : handleDelete
}
onEdit={activeChannel?.archivedAt ? undefined : handleEdit}
onEditSave={
activeChannel?.archivedAt ? undefined : handleEditSave
}
onMarkUnread={handleMessageMarkUnread}
onMarkRead={handleMessageMarkRead}
onExpandThreadReplies={handleExpandThreadReplies}
onOpenAgentSession={handleOpenAgentSession}
onOpenDm={handleOpenDm}
onOpenProfilePanel={handleOpenProfilePanel}
onResetThreadPanelWidth={handleThreadPanelWidthReset}
onCloseProfilePanel={handleCloseProfilePanel}
onOpenThread={handleOpenThreadAndCloseAgentSession}
onSelectThreadReplyTarget={handleSelectThreadReplyTarget}
onSendMessage={handleSendMessage}
onSendToChannel={handleSendToChannel}
onSendVideoReviewComment={effectiveSendVideoReviewComment}
onSendThreadReply={handleSendThreadReply}
onThreadScrollTargetResolved={() =>
setThreadScrollTargetId(null)
}
onThreadPanelResizeStart={handleThreadPanelResizeStart}
onTargetReached={() =>
clearMessageRouteTarget({ replace: true })
}
onToggleReaction={effectiveToggleReaction}
openAgentSessionChannelId={openAgentSessionChannelId}
openAgentSessionPubkey={openAgentSessionPubkey}
openThreadHeadId={effectiveOpenThreadHeadId}
shouldShowThreadSkeleton={shouldShowThreadSkeleton}
onProfilePanelViewChange={setProfilePanelView}
onProfilePanelTabChange={setProfilePanelTab}
markdownDocName={markdownDocName}
markdownDocUrl={markdownDocUrl}
onCloseMarkdownDoc={closeMarkdownDoc}
profilePanelPubkey={profilePanelPubkey}
profilePanelTab={profilePanelTab}
profilePanelView={profilePanelView}
personaLookup={personaLookup}
profiles={messageProfiles}
ownerProfiles={messageOwnerProfiles}
firstUnreadMessageId={firstUnreadMessageId}
unreadCount={unreadCount}
targetMessageId={mainTimelineTargetMessageId}
threadAllMessages={displayedThreadAllMessages}
threadHeadMessage={displayedThreadHeadMessage}
threadMessages={displayedThreadMessages}
threadMessagesPending={threadRepliesQuery.isPending}
threadMessagesError={threadRepliesQuery.isError}
onRetryThreadReplies={() => {
void threadRepliesQuery.refetch();
}}
threadPanelWidthPx={threadPanelWidthPx}
threadTypingPubkeys={threadTypingPubkeys}
threadReplyTargetMessage={displayedThreadReplyTargetMessage}
threadScrollTargetId={threadScrollTargetId}
threadUnreadCounts={threadUnreadCounts}
threadReplyUnreadCounts={threadReplyUnreadCounts}
threadFirstUnreadReplyId={displayedThreadFirstUnreadReplyId}
isJoining={joinChannelMutation.isPending}
onJoinChannel={joinChannelMutation.mutateAsync}
typingPubkeys={humanTypingPubkeys}
/>,
searchTarget,
)}
</React.Suspense>
)
) : (
<ChannelScreenEmptyState />

View File

@@ -1,9 +1,27 @@
import type * as React from "react";
import {
type MarkdownDocTarget,
MarkdownDocViewerProvider,
} from "@/shared/ui/markdown/markdownDocViewerContext";
import { ChannelPane } from "./ChannelScreenLazyViews";
export function GuardedChannelPane(
props: React.ComponentProps<typeof ChannelPane>,
) {
return <ChannelPane {...props} />;
/**
* Hosts the markdown-doc viewer context for the channel pane — the surface
* that renders the doc auxiliary panel. The forum branch never mounts this
* wrapper, so forum FileCards keep plain download behavior instead of a
* dead open-in-viewer click.
*/
export function GuardedChannelPane({
onOpenMarkdownDoc,
...props
}: React.ComponentProps<typeof ChannelPane> & {
onOpenMarkdownDoc: (doc: MarkdownDocTarget) => void;
}) {
return (
<MarkdownDocViewerProvider value={onOpenMarkdownDoc}>
<ChannelPane {...props} />
</MarkdownDocViewerProvider>
);
}