fix(desktop): show unavailable activity state

Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-08-23 20:54:29 -07:00
committed by Rizz
parent bd49723afc
commit aa781c6d12
5 changed files with 102 additions and 14 deletions

View File

@@ -1,5 +1,5 @@
import * as React from "react";
import { Hash, LogIn } from "lucide-react";
import { Hash, LoaderCircle, LogIn } from "lucide-react";
import { AnimatePresence } from "motion/react";
import { useAppNavigation } from "@/app/navigation/useAppNavigation";
import { useMediaUpload } from "@/features/messages/lib/useMediaUpload";
@@ -87,6 +87,7 @@ export const ChannelPane = React.memo(function ChannelPane({
isMessageUnreadById,
isJoining = false,
isSinglePanelView = false,
isAgentSessionLoading = false,
isSending,
isTimelineLoading,
entranceMessageId = null,
@@ -914,6 +915,30 @@ export const ChannelPane = React.memo(function ChannelPane({
);
return wrapAux(panel, "agent-session-thread-panel");
})()
) : isSinglePanelView && openAgentSessionPubkey ? (
<div
className="flex min-h-0 flex-1 items-center justify-center p-8 text-center"
data-testid="agent-session-unavailable"
>
<div className="max-w-sm space-y-2">
{isAgentSessionLoading ? (
<LoaderCircle
aria-hidden="true"
className="mx-auto h-5 w-5 animate-spin text-muted-foreground"
/>
) : null}
<h1 className="text-lg font-semibold">
{isAgentSessionLoading
? "Loading agent activity…"
: "Agent activity unavailable"}
</h1>
<p className="text-sm text-muted-foreground">
{isAgentSessionLoading
? "Buzz is finding this agent."
: "This agent is no longer available in this community. You can close this window and open another activity feed from Buzz."}
</p>
</div>
</div>
) : profilePanelPubkey ? (
(() => {
const panel = (

View File

@@ -54,6 +54,7 @@ export type ChannelPaneProps = {
isHuddleTranscript?: boolean;
isJoining?: boolean;
isSinglePanelView?: boolean;
isAgentSessionLoading?: boolean;
isSending: boolean;
isTimelineLoading: boolean;
/** Newly-created message that should receive the one-shot conversation arrival motion. */

View File

@@ -306,6 +306,10 @@ export function ChannelScreen({
welcomeGuideAgent,
});
const relayAgentsQuery = useRelayAgentsQuery();
const agentsLoading =
channelMembersQuery.isLoading ||
managedAgentsQuery.isLoading ||
relayAgentsQuery.isLoading;
const relayAgents = relayAgentsQuery.data ?? [];
const knownAgentPubkeys = React.useMemo(
() =>
@@ -568,16 +572,14 @@ export function ChannelScreen({
} = useChannelAgentSessions({
activeChannel,
activeChannelId,
agentsLoaded:
!channelMembersQuery.isLoading &&
!managedAgentsQuery.isLoading &&
!relayAgentsQuery.isLoading,
agentsLoaded: !agentsLoading,
channelMembers,
handleOpenThread,
managedAgents: agentSessionCandidates,
openAgentSessionPubkey,
openThreadHeadId: effectiveOpenThreadHeadId,
profilePanelPubkey,
preserveUnresolvedSession: isDedicatedActivityWindow,
setChannelManagementOpen,
setExpandedThreadReplyIds,
setOpenAgentSessionChannelId,
@@ -890,6 +892,7 @@ export function ChannelScreen({
isMessageUnreadById={isMessageUnread}
isFollowingThread={isNotifiedForEffectiveThread}
isSending={sendMessageMutation.isPending}
isAgentSessionLoading={agentsLoading}
isSinglePanelView={isSinglePanelView}
isTimelineLoading={isTimelineLoading}
messages={timelineMessages}

View File

@@ -0,0 +1,31 @@
import assert from "node:assert/strict";
import test from "node:test";
import { shouldClearUnresolvedAgentSession } from "./useChannelAgentSessions.ts";
const unresolvedSession = {
agentSessionAgents: [],
agentsLoaded: true,
openAgentSessionPubkey: "agent-pubkey",
profilePanelPubkey: null,
};
test("dedicated activity windows preserve unresolved sessions for an empty state", () => {
assert.equal(
shouldClearUnresolvedAgentSession({
...unresolvedSession,
preserveUnresolvedSession: true,
}),
false,
);
});
test("the in-app panel still clears stale unresolved sessions", () => {
assert.equal(
shouldClearUnresolvedAgentSession({
...unresolvedSession,
preserveUnresolvedSession: false,
}),
true,
);
});

View File

@@ -39,6 +39,7 @@ type UseChannelAgentSessionsOptions = {
openAgentSessionPubkey: string | null;
openThreadHeadId: string | null;
profilePanelPubkey?: string | null;
preserveUnresolvedSession?: boolean;
setChannelManagementOpen: (open: boolean) => void;
setExpandedThreadReplyIds: (value: Set<string>) => void;
setOpenAgentSessionChannelId: PanelValueSetter;
@@ -163,6 +164,33 @@ export function getChannelAgentSessionAgents({
});
}
export function shouldClearUnresolvedAgentSession({
agentSessionAgents,
agentsLoaded,
openAgentSessionPubkey,
preserveUnresolvedSession,
profilePanelPubkey,
}: {
agentSessionAgents: ChannelAgentSessionAgent[];
agentsLoaded: boolean;
openAgentSessionPubkey: string | null;
preserveUnresolvedSession: boolean;
profilePanelPubkey: string | null;
}): boolean {
return Boolean(
openAgentSessionPubkey &&
!preserveUnresolvedSession &&
agentsLoaded &&
normalizePubkey(profilePanelPubkey ?? "") !==
normalizePubkey(openAgentSessionPubkey) &&
!agentSessionAgents.some(
(agent) =>
normalizePubkey(agent.pubkey) ===
normalizePubkey(openAgentSessionPubkey),
),
);
}
export function useChannelAgentSessions({
activeChannel,
activeChannelId,
@@ -173,6 +201,7 @@ export function useChannelAgentSessions({
openAgentSessionPubkey,
openThreadHeadId,
profilePanelPubkey = null,
preserveUnresolvedSession = false,
setChannelManagementOpen,
setExpandedThreadReplyIds,
setOpenAgentSessionChannelId,
@@ -298,15 +327,13 @@ export function useChannelAgentSessions({
// agent queries have settled. Once loaded, a channel that legitimately has
// zero agents will still auto-close a stale param.
if (
openAgentSessionPubkey &&
agentsLoaded &&
normalizePubkey(profilePanelPubkey ?? "") !==
normalizePubkey(openAgentSessionPubkey) &&
!agentSessionAgents.some(
(agent) =>
normalizePubkey(agent.pubkey) ===
normalizePubkey(openAgentSessionPubkey),
)
shouldClearUnresolvedAgentSession({
agentSessionAgents,
agentsLoaded,
openAgentSessionPubkey,
preserveUnresolvedSession,
profilePanelPubkey,
})
) {
returnTarget.clear();
setOpenAgentSessionPubkey(null, { replace: true });
@@ -315,6 +342,7 @@ export function useChannelAgentSessions({
agentSessionAgents,
agentsLoaded,
openAgentSessionPubkey,
preserveUnresolvedSession,
profilePanelPubkey,
returnTarget,
setOpenAgentSessionPubkey,