Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions desktop/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export default defineConfig({
"**/reminder-click-repro.spec.ts",
"**/virtualization.spec.ts",
"**/scroll-history.spec.ts",
"**/history-transactions.spec.ts",
"**/channel-dense-second-reach.spec.ts",
"**/channel-window-mock-paging.spec.ts",
"**/channel-head-restart.spec.ts",
Expand Down
34 changes: 14 additions & 20 deletions desktop/src/features/channels/ui/ChannelPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ import {
MessageTimeline,
type MessageTimelineHandle,
} from "@/features/messages/ui/MessageTimeline";
import { buildDirectMessageIntro } from "@/features/channels/lib/dmParticipantDisplay";
import {
getDmHuddleMemberPubkeys,
hasOtherDmParticipant,
} from "@/features/channels/lib/dmHuddleMembers";
import { useChannelPaneDmParticipants } from "./useChannelPaneDmParticipants";
import { buildVideoReviewPresentationByMessageId } from "@/features/messages/lib/videoReviewContext";
import { useComposerHeightPadding } from "@/features/messages/ui/useComposerHeightPadding";
import { UserProfilePanel } from "@/features/profile/ui/UserProfilePanel";
Expand Down Expand Up @@ -88,6 +84,7 @@ export const ChannelPane = React.memo(function ChannelPane({
currentPubkey,
editTarget = null,
fetchOlder,
historyRevision,
header,
idleAuxiliaryPanel = null,
idleAuxiliaryHeaderActions,
Expand Down Expand Up @@ -226,12 +223,17 @@ export const ChannelPane = React.memo(function ChannelPane({
void goChannel(activeChannelId, { replace: true });
}
}, [activeChannelId, goChannel, onAutoSendComplete]);
const huddleMemberPubkeys = React.useMemo(
() => getDmHuddleMemberPubkeys(activeChannel, agentPubkeys, currentPubkey),
[activeChannel, agentPubkeys, currentPubkey],
);
const huddleMemberPubkeysPending =
agentPubkeysPending && hasOtherDmParticipant(activeChannel, currentPubkey);
const {
directMessageIntro,
huddleMemberPubkeys,
huddleMemberPubkeysPending,
} = useChannelPaneDmParticipants({
activeChannel,
agentPubkeys,
agentPubkeysPending,
currentPubkey,
profiles,
});
const isActiveWelcomeChannel =
activeChannel !== null && isWelcomeExperience(activeChannel);
useComposerHeightPadding(
Expand Down Expand Up @@ -347,15 +349,6 @@ export const ChannelPane = React.memo(function ChannelPane({
);
const hasThreadComposerBotActivity =
threadComposerBotTypingPubkeys.length > 0;
const directMessageIntro = React.useMemo(
() =>
buildDirectMessageIntro({
channel: activeChannel,
currentPubkey,
profiles,
}),
[activeChannel, currentPubkey, profiles],
);
const handleWelcomeAddAgent = React.useCallback(() => {
onAddAgent?.({
beforeSend: () =>
Expand Down Expand Up @@ -630,6 +623,7 @@ export const ChannelPane = React.memo(function ChannelPane({
scrollContainerRef={timelineScrollRef}
currentPubkey={currentPubkey}
fetchOlder={fetchOlder}
historyRevision={historyRevision}
followThreadById={followThreadById}
hasComposerOverlay={hasMainComposerOverlay}
hasOlderMessages={hasOlderMessages}
Expand Down
4 changes: 3 additions & 1 deletion desktop/src/features/channels/ui/ChannelPane.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ export type ChannelPaneProps = {
channelManagementOpen?: boolean;
currentPubkey?: string;
editTarget?: MessageComposerEditTarget | null;
fetchOlder?: () => Promise<void>;
fetchOlder?: () => Promise<number | undefined>;
/** Authoritative history publication paired with the message snapshot. */
historyRevision?: number;
header?: React.ReactNode;
/**
* Idle-state body for the right auxiliary pane (project extras, etc.).
Expand Down
6 changes: 3 additions & 3 deletions desktop/src/features/channels/ui/ChannelScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ export function ChannelScreen({
threadScrollTargetId,
);
useChannelSubscription(activeChannel);
const { fetchOlder, hasOlderMessages, historyExhausted, isFetchingOlder } =
const { fetchOlder, isFetchingOlder } =
useFetchOlderMessages(activeChannel);
const latestActiveMessage = React.useMemo(() => {
const messages = messagesQuery.data;
Expand Down Expand Up @@ -255,8 +255,7 @@ export function ChannelScreen({
const editMessageMutation = useEditMessageMutation(activeChannel);
const joinChannelMutation = useJoinChannelMutation(activeChannelId);
const {
resolvedMessages,
threadSummaries,
resolvedMessages, threadSummaries, historyRevision, historyExhausted, hasOlderMessages,
threadRepliesError: huddleThreadRepliesError,
onRetryThreadReplies: onRetryHuddleThreadReplies,
} = useHuddleChannelMessages({
Expand Down Expand Up @@ -851,6 +850,7 @@ export function ChannelScreen({
currentPubkey={currentPubkey}
canResetThreadPanelWidth={canResetThreadPanelWidth}
fetchOlder={fetchOlder}
historyRevision={historyRevision}
header={channelHeader}
{...{ idleAuxiliaryHeaderActions, idleAuxiliaryOverridesThread, idleAuxiliaryPanel, idleAuxiliaryTitle, hasOlderMessages, historyExhausted }}
{...{ onAddFiles }}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as React from "react";
import { buildDirectMessageIntro } from "@/features/channels/lib/dmParticipantDisplay";
import {
getDmHuddleMemberPubkeys,
hasOtherDmParticipant,
} from "@/features/channels/lib/dmHuddleMembers";
import type { ChannelPaneProps } from "./ChannelPane.types";

/** Derive the shared DM intro and huddle participants without changing identity. */
export function useChannelPaneDmParticipants({
activeChannel,
agentPubkeys,
agentPubkeysPending,
currentPubkey,
profiles,
}: Pick<
ChannelPaneProps,
| "activeChannel"
| "agentPubkeys"
| "agentPubkeysPending"
| "currentPubkey"
| "profiles"
>) {
const huddleMemberPubkeys = React.useMemo(
() => getDmHuddleMemberPubkeys(activeChannel, agentPubkeys, currentPubkey),
[activeChannel, agentPubkeys, currentPubkey],
);
const huddleMemberPubkeysPending =
agentPubkeysPending && hasOtherDmParticipant(activeChannel, currentPubkey);
const directMessageIntro = React.useMemo(
() =>
buildDirectMessageIntro({
channel: activeChannel,
currentPubkey,
profiles,
}),
[activeChannel, currentPubkey, profiles],
);
return {
directMessageIntro,
huddleMemberPubkeys,
huddleMemberPubkeysPending,
};
}
26 changes: 22 additions & 4 deletions desktop/src/features/channels/ui/useHuddleChannelMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import { huddleWindowChannelId } from "@/features/huddle/lib/huddleWindow";
import { mergeMessages } from "@/features/messages/hooks";
import {
channelWindowThreadSummaries,
channelWindowHasMore,
channelWindowHistoryExhausted,
type ChannelWindowStore,
} from "@/features/messages/lib/channelWindowStore";
import { reconcileChannelWindowMessages } from "@/features/messages/lib/channelWindowReconciliation";
import { useThreadRepliesForRoots } from "@/features/messages/useThreadReplies";
import type { Channel, RelayEvent } from "@/shared/api/types";

Expand All @@ -32,11 +35,21 @@ export function useHuddleChannelMessages({
targetMessageEvents,
windowStore,
}: HuddleChannelMessagesOptions) {
// Query observers may notify separately. Project from the SAME store that
// supplies the receipt and structural metadata; never pair a new revision
// with an older channelMessages cache notification.
const windowMessages = React.useMemo(
() =>
windowStore
? reconcileChannelWindowMessages(windowStore, messages)
: messages,
[messages, windowStore],
);
const resolvedChannelMessages = React.useMemo(() => {
const extraEvents = targetMessageEvents;
if (!activeChannel || extraEvents.length === 0) return messages;
return extraEvents.reduce(mergeMessages, messages);
}, [activeChannel, messages, targetMessageEvents]);
if (!activeChannel || targetMessageEvents.length === 0)
return windowMessages;
return targetMessageEvents.reduce(mergeMessages, windowMessages);
}, [activeChannel, windowMessages, targetMessageEvents]);

const threadSummaries = React.useMemo(
() => (windowStore ? channelWindowThreadSummaries(windowStore) : new Map()),
Expand Down Expand Up @@ -69,6 +82,11 @@ export function useHuddleChannelMessages({
return {
resolvedMessages,
threadSummaries,
historyRevision: windowStore?.revision ?? 0,
hasOlderMessages: windowStore ? channelWindowHasMore(windowStore) : false,
historyExhausted: windowStore
? channelWindowHistoryExhausted(windowStore)
: false,
// A summarized reply subtree failing must not leave the transcript reading
// as complete: surface the aggregate failure so the consumer can show a
// non-destructive retry alert alongside the rows that did load.
Expand Down
2 changes: 2 additions & 0 deletions desktop/src/features/communities/useCommunityInit.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { resetChannelWindowRefreshIntents } from "@/features/messages/lib/channelWindowRefreshIntent";
import { useEffect, useRef, useState } from "react";
import { isTauri } from "@tauri-apps/api/core";
import { isMacPlatform } from "@/shared/lib/platform";
Expand Down Expand Up @@ -64,6 +65,7 @@ async function resetCommunityState({
relayClient.disconnect();
await resetNavigationDeepLinkDrain();
resetRateLimitGate();
resetChannelWindowRefreshIntents();
clearAllDrafts();
resetAgentObserverStore();
resetActiveAgentTurnsStore();
Expand Down
Loading
Loading