mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Fix profile sidebar review feedback
This commit is contained in:
@@ -1,13 +1,17 @@
|
||||
import * as React from "react";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import {
|
||||
parseProfilePanelView,
|
||||
type ProfilePanelView,
|
||||
} from "@/features/profile/ui/UserProfilePanelUtils";
|
||||
import { ViewLoadingFallback } from "@/shared/ui/ViewLoadingFallback";
|
||||
|
||||
type ChannelRouteSearch = {
|
||||
agentSession?: string;
|
||||
messageId?: string;
|
||||
profile?: string;
|
||||
profileView?: "memories" | "channels";
|
||||
profileView?: ProfilePanelView;
|
||||
thread?: string;
|
||||
threadRootId?: string;
|
||||
};
|
||||
@@ -16,8 +20,8 @@ function nonEmptyString(value: unknown): string | undefined {
|
||||
return typeof value === "string" && value.length > 0 ? value : undefined;
|
||||
}
|
||||
|
||||
function profileViewValue(value: unknown): "memories" | "channels" | undefined {
|
||||
return value === "memories" || value === "channels" ? value : undefined;
|
||||
function profileViewValue(value: unknown): ProfilePanelView | undefined {
|
||||
return parseProfilePanelView(value) ?? undefined;
|
||||
}
|
||||
|
||||
function validateChannelSearch(
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import * as React from "react";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import {
|
||||
parseProfilePanelView,
|
||||
type ProfilePanelView,
|
||||
} from "@/features/profile/ui/UserProfilePanelUtils";
|
||||
import { usePreviewFeatureWarning } from "@/shared/features";
|
||||
import { ViewLoadingFallback } from "@/shared/ui/ViewLoadingFallback";
|
||||
|
||||
@@ -11,7 +15,7 @@ const PulseScreen = React.lazy(async () => {
|
||||
|
||||
type PulseRouteSearch = {
|
||||
profile?: string;
|
||||
profileView?: "memories" | "channels";
|
||||
profileView?: ProfilePanelView;
|
||||
};
|
||||
|
||||
function validatePulseSearch(
|
||||
@@ -22,10 +26,7 @@ function validatePulseSearch(
|
||||
typeof search.profile === "string" && search.profile.length > 0
|
||||
? search.profile
|
||||
: undefined,
|
||||
profileView:
|
||||
search.profileView === "memories" || search.profileView === "channels"
|
||||
? search.profileView
|
||||
: undefined,
|
||||
profileView: parseProfilePanelView(search.profileView) ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { beforeEach, describe, it } from "node:test";
|
||||
|
||||
import {
|
||||
isKnownAgentPubkey,
|
||||
registerKnownAgentPubkeys,
|
||||
resetAgentObserverStore,
|
||||
unregisterKnownAgentPubkeys,
|
||||
} from "./observerRelayStore.ts";
|
||||
|
||||
const AGENT_A =
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
const AGENT_B =
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
|
||||
const AGENT_C =
|
||||
"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";
|
||||
|
||||
describe("observerRelayStore known agent registrations", () => {
|
||||
beforeEach(() => {
|
||||
resetAgentObserverStore();
|
||||
});
|
||||
|
||||
it("unions known agents from multiple bridge registrations", () => {
|
||||
const agentsPage = Symbol("agents-page");
|
||||
const profilePanel = Symbol("profile-panel");
|
||||
|
||||
registerKnownAgentPubkeys(agentsPage, [AGENT_A, AGENT_B]);
|
||||
registerKnownAgentPubkeys(profilePanel, [AGENT_C]);
|
||||
|
||||
assert.equal(isKnownAgentPubkey(AGENT_A), true);
|
||||
assert.equal(isKnownAgentPubkey(AGENT_B), true);
|
||||
assert.equal(isKnownAgentPubkey(AGENT_C), true);
|
||||
|
||||
registerKnownAgentPubkeys(profilePanel, []);
|
||||
|
||||
assert.equal(isKnownAgentPubkey(AGENT_A), true);
|
||||
assert.equal(isKnownAgentPubkey(AGENT_B), true);
|
||||
assert.equal(isKnownAgentPubkey(AGENT_C), false);
|
||||
|
||||
unregisterKnownAgentPubkeys(agentsPage);
|
||||
|
||||
assert.equal(isKnownAgentPubkey(AGENT_A), false);
|
||||
assert.equal(isKnownAgentPubkey(AGENT_B), false);
|
||||
});
|
||||
});
|
||||
@@ -47,7 +47,7 @@ const snapshotByAgent = new Map<string, ObserverSnapshot>();
|
||||
// We key each subscriber's contribution in `knownAgentsBySubscription` and
|
||||
// recompute the union, so co-mounted callers no longer clobber each other.
|
||||
const knownAgentPubkeys = new Set<string>();
|
||||
const knownAgentsBySubscription = new Map<string, Set<string>>();
|
||||
const knownAgentsBySubscription = new Map<string | symbol, Set<string>>();
|
||||
|
||||
function recomputeKnownAgentPubkeys() {
|
||||
knownAgentPubkeys.clear();
|
||||
@@ -58,8 +58,8 @@ function recomputeKnownAgentPubkeys() {
|
||||
}
|
||||
}
|
||||
|
||||
function registerKnownAgents(
|
||||
subscriptionId: string,
|
||||
export function registerKnownAgentPubkeys(
|
||||
subscriptionId: string | symbol,
|
||||
pubkeys: readonly string[],
|
||||
) {
|
||||
knownAgentsBySubscription.set(
|
||||
@@ -69,12 +69,16 @@ function registerKnownAgents(
|
||||
recomputeKnownAgentPubkeys();
|
||||
}
|
||||
|
||||
function unregisterKnownAgents(subscriptionId: string) {
|
||||
export function unregisterKnownAgentPubkeys(subscriptionId: string | symbol) {
|
||||
if (knownAgentsBySubscription.delete(subscriptionId)) {
|
||||
recomputeKnownAgentPubkeys();
|
||||
}
|
||||
}
|
||||
|
||||
export function isKnownAgentPubkey(pubkey: string) {
|
||||
return knownAgentPubkeys.has(normalizePubkey(pubkey));
|
||||
}
|
||||
|
||||
let connectionState: ConnectionState = "idle";
|
||||
let errorMessage: string | null = null;
|
||||
let unsubscribeRelay: (() => Promise<void>) | null = null;
|
||||
@@ -176,7 +180,7 @@ async function handleRelayObserverEvent(
|
||||
|
||||
// Verify agent is known/trusted before decrypting.
|
||||
// Silently drop events from agents we are not managing.
|
||||
if (!knownAgentPubkeys.has(normalizePubkey(agentPubkey))) {
|
||||
if (!isKnownAgentPubkey(agentPubkey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -326,9 +330,9 @@ export function useManagedAgentObserverBridge(
|
||||
// own agent list. The store recomputes the union across all subscribers, so
|
||||
// a co-mounted caller no longer wipes out this caller's agents.
|
||||
React.useEffect(() => {
|
||||
registerKnownAgents(subscriptionId, agentPubkeys);
|
||||
registerKnownAgentPubkeys(subscriptionId, agentPubkeys);
|
||||
return () => {
|
||||
unregisterKnownAgents(subscriptionId);
|
||||
unregisterKnownAgentPubkeys(subscriptionId);
|
||||
};
|
||||
}, [subscriptionId, agentPubkeys]);
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import * as React from "react";
|
||||
|
||||
import type { ProfilePanelView } from "@/features/profile/ui/UserProfilePanel";
|
||||
import {
|
||||
profilePanelViewFromSearch,
|
||||
type ProfilePanelView,
|
||||
} from "@/features/profile/ui/UserProfilePanelUtils";
|
||||
import {
|
||||
type HistorySearchSetterOptions,
|
||||
useHistorySearchState,
|
||||
@@ -36,10 +39,6 @@ const CHANNEL_SEARCH_KEYS = [
|
||||
|
||||
const CHANNEL_MANAGEMENT_OPEN_VALUE = "1";
|
||||
|
||||
function asProfilePanelView(value: string | null): ProfilePanelView {
|
||||
return value === "memories" || value === "channels" ? value : "summary";
|
||||
}
|
||||
|
||||
export function useChannelPanelHistoryState() {
|
||||
const { applyPatch, values } = useHistorySearchState(CHANNEL_SEARCH_KEYS);
|
||||
|
||||
@@ -88,7 +87,7 @@ export function useChannelPanelHistoryState() {
|
||||
openAgentSessionPubkey: values.agentSession,
|
||||
openThreadHeadId: values.thread,
|
||||
profilePanelPubkey: values.profile,
|
||||
profilePanelView: asProfilePanelView(values.profileView),
|
||||
profilePanelView: profilePanelViewFromSearch(values.profileView),
|
||||
setChannelManagementOpen,
|
||||
setOpenAgentSessionPubkey,
|
||||
setOpenThreadHeadId,
|
||||
|
||||
@@ -735,7 +735,11 @@ export function UserProfilePanel({
|
||||
const headerActions = (
|
||||
<div className="ml-auto flex shrink-0 items-center gap-2">
|
||||
{view === "memories" && viewerIsOwner && effectivePubkey ? (
|
||||
<MemoryRefreshButton agentPubkey={effectivePubkey} variant="outline" />
|
||||
<MemoryRefreshButton
|
||||
agentPubkey={effectivePubkey}
|
||||
variant="outline"
|
||||
viewerIsOwner={viewerIsOwner}
|
||||
/>
|
||||
) : null}
|
||||
<Button
|
||||
aria-label="Close profile"
|
||||
|
||||
@@ -734,7 +734,7 @@ export function MemoryFocusedView({
|
||||
|
||||
return (
|
||||
<div className="pt-4">
|
||||
<MemorySection agentPubkey={agentPubkey} />
|
||||
<MemorySection agentPubkey={agentPubkey} viewerIsOwner={isOwner} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,6 @@ test("personaManagedAgentUpdate syncs edited persona identity to linked agent",
|
||||
assert.deepEqual(personaManagedAgentUpdate(agent(), persona()), {
|
||||
pubkey: "deadbeef".repeat(8),
|
||||
name: "Fizz Prime",
|
||||
avatarUrl: null,
|
||||
systemPrompt: "New prompt",
|
||||
model: "new-model",
|
||||
envVars: { NEW_KEY: "2" },
|
||||
@@ -121,7 +120,6 @@ test("personaManagedAgentUpdate maps changed persona runtime to linked agent com
|
||||
{
|
||||
pubkey: "deadbeef".repeat(8),
|
||||
name: "Fizz Prime",
|
||||
avatarUrl: null,
|
||||
systemPrompt: "New prompt",
|
||||
model: "new-model",
|
||||
envVars: { NEW_KEY: "2" },
|
||||
|
||||
@@ -3,9 +3,10 @@ import * as React from "react";
|
||||
import { useAppNavigation } from "@/app/navigation/useAppNavigation";
|
||||
import { useOpenDmMutation } from "@/features/channels/hooks";
|
||||
import {
|
||||
profilePanelViewFromSearch,
|
||||
type ProfilePanelView,
|
||||
UserProfilePanel,
|
||||
} from "@/features/profile/ui/UserProfilePanel";
|
||||
} from "@/features/profile/ui/UserProfilePanelUtils";
|
||||
import { UserProfilePanel } from "@/features/profile/ui/UserProfilePanel";
|
||||
import { PulseView } from "@/features/pulse/ui/PulseView";
|
||||
import { useIdentityQuery } from "@/shared/api/hooks";
|
||||
import { ProfilePanelProvider } from "@/shared/context/ProfilePanelContext";
|
||||
@@ -18,10 +19,7 @@ export function PulseScreen() {
|
||||
const identityQuery = useIdentityQuery();
|
||||
const { applyPatch, values } = useHistorySearchState(PULSE_PANEL_SEARCH_KEYS);
|
||||
const profilePanelPubkey = values.profile;
|
||||
const profilePanelView: ProfilePanelView =
|
||||
values.profileView === "memories" || values.profileView === "channels"
|
||||
? values.profileView
|
||||
: "summary";
|
||||
const profilePanelView = profilePanelViewFromSearch(values.profileView);
|
||||
const handleOpenProfilePanel = React.useCallback(
|
||||
(pubkey: string) => applyPatch({ profile: pubkey, profileView: null }),
|
||||
[applyPatch],
|
||||
|
||||
Reference in New Issue
Block a user