mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
feat(desktop): open profile panel from MembersSidebar rows (#962)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
4e4dc723e4
commit
dcb2639b35
@@ -28,6 +28,7 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/shared/ui/dialog";
|
||||
import { useProfilePanel } from "@/shared/context/ProfilePanelContext";
|
||||
import { useFeedbackToasts } from "@/shared/hooks/useToastEffect";
|
||||
import { normalizePubkey } from "@/shared/lib/pubkey";
|
||||
import {
|
||||
@@ -161,6 +162,21 @@ export function MembersSidebar({
|
||||
|
||||
useFeedbackToasts(actionNoticeMessage, actionErrorMessage);
|
||||
|
||||
const { openProfilePanel } = useProfilePanel();
|
||||
// UserProfilePanel only renders inside ChannelPane, which forums replace
|
||||
// with ForumView — opening there would close the sheet and show nothing.
|
||||
const isForumChannel = channel?.channelType === "forum";
|
||||
const handleOpenProfile = React.useMemo(
|
||||
() =>
|
||||
openProfilePanel && !isForumChannel
|
||||
? (pubkey: string) => {
|
||||
onOpenChange(false);
|
||||
openProfilePanel(pubkey);
|
||||
}
|
||||
: undefined,
|
||||
[isForumChannel, onOpenChange, openProfilePanel],
|
||||
);
|
||||
|
||||
const [editRespondToAgent, setEditRespondToAgent] =
|
||||
React.useState<ManagedAgent | null>(null);
|
||||
|
||||
@@ -192,6 +208,7 @@ export function MembersSidebar({
|
||||
onManagedAgentAction={(agent) => {
|
||||
void handleAgentLifecycleAction(agent);
|
||||
}}
|
||||
onOpenProfile={handleOpenProfile}
|
||||
onRemoveMember={handleRemoveMember}
|
||||
onViewActivity={
|
||||
onViewActivity
|
||||
|
||||
@@ -46,6 +46,7 @@ type MembersSidebarMemberCardProps = {
|
||||
onChangeRole: (member: ChannelMember, role: string) => void;
|
||||
onEditRespondTo?: (agent: ManagedAgent) => void;
|
||||
onManagedAgentAction: (agent: ManagedAgent) => void;
|
||||
onOpenProfile?: (pubkey: string) => void;
|
||||
onRemoveMember: (member: ChannelMember) => void;
|
||||
onViewActivity?: (pubkey: string) => void;
|
||||
presenceStatus?: PresenceStatus | null;
|
||||
@@ -97,6 +98,7 @@ export function MembersSidebarMemberCard({
|
||||
onChangeRole,
|
||||
onEditRespondTo,
|
||||
onManagedAgentAction,
|
||||
onOpenProfile,
|
||||
onRemoveMember,
|
||||
onViewActivity,
|
||||
presenceStatus,
|
||||
@@ -112,60 +114,76 @@ export function MembersSidebarMemberCard({
|
||||
? Boolean(managedAgent) || canRemoveMember || canViewActivity
|
||||
: canRemoveMember || canChangeRole;
|
||||
|
||||
const memberIdentity = (
|
||||
<>
|
||||
<div className="relative shrink-0">
|
||||
<ProfileAvatar
|
||||
avatarUrl={profileAvatarUrl ?? null}
|
||||
className="h-9 w-9 text-[11px] shadow-none"
|
||||
iconClassName="h-4 w-4"
|
||||
label={memberAvatarLabel}
|
||||
/>
|
||||
{presenceStatus ? (
|
||||
<span
|
||||
className="absolute -bottom-0.5 -right-0.5 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-background"
|
||||
data-testid={`sidebar-member-presence-${member.pubkey}`}
|
||||
>
|
||||
<PresenceDot className="h-2 w-2" status={presenceStatus} />
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<p className="truncate text-sm font-medium leading-5">
|
||||
{memberLabel}
|
||||
</p>
|
||||
<Badge className="shrink-0" variant="secondary">
|
||||
{roleLabel}
|
||||
</Badge>
|
||||
{managedAgent ? (
|
||||
<>
|
||||
<Badge
|
||||
className="shrink-0"
|
||||
data-testid={`sidebar-managed-agent-status-${member.pubkey}`}
|
||||
variant="secondary"
|
||||
>
|
||||
{formatManagedAgentStatus(managedAgent)}
|
||||
</Badge>
|
||||
<Badge
|
||||
className="shrink-0"
|
||||
data-testid={`sidebar-managed-agent-respond-to-${member.pubkey}`}
|
||||
variant="outline"
|
||||
>
|
||||
{formatRespondToLabel(managedAgent)}
|
||||
</Badge>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<p className="truncate font-mono text-[10px] text-muted-foreground/50">
|
||||
{truncatePubkey(member.pubkey)}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="group flex items-center justify-between gap-3 rounded-lg px-3 py-2 transition-colors hover:bg-muted/40"
|
||||
data-testid={`sidebar-member-${member.pubkey}`}
|
||||
>
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<div className="relative shrink-0">
|
||||
<ProfileAvatar
|
||||
avatarUrl={profileAvatarUrl ?? null}
|
||||
className="h-9 w-9 text-[11px] shadow-none"
|
||||
iconClassName="h-4 w-4"
|
||||
label={memberAvatarLabel}
|
||||
/>
|
||||
{presenceStatus ? (
|
||||
<span
|
||||
className="absolute -bottom-0.5 -right-0.5 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-background"
|
||||
data-testid={`sidebar-member-presence-${member.pubkey}`}
|
||||
>
|
||||
<PresenceDot className="h-2 w-2" status={presenceStatus} />
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<p className="truncate text-sm font-medium leading-5">
|
||||
{memberLabel}
|
||||
</p>
|
||||
<Badge className="shrink-0" variant="secondary">
|
||||
{roleLabel}
|
||||
</Badge>
|
||||
{managedAgent ? (
|
||||
<>
|
||||
<Badge
|
||||
className="shrink-0"
|
||||
data-testid={`sidebar-managed-agent-status-${member.pubkey}`}
|
||||
variant="secondary"
|
||||
>
|
||||
{formatManagedAgentStatus(managedAgent)}
|
||||
</Badge>
|
||||
<Badge
|
||||
className="shrink-0"
|
||||
data-testid={`sidebar-managed-agent-respond-to-${member.pubkey}`}
|
||||
variant="outline"
|
||||
>
|
||||
{formatRespondToLabel(managedAgent)}
|
||||
</Badge>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<p className="truncate font-mono text-[10px] text-muted-foreground/50">
|
||||
{truncatePubkey(member.pubkey)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{onOpenProfile ? (
|
||||
<button
|
||||
aria-label={`Open profile for ${memberLabel}`}
|
||||
className="flex min-w-0 flex-1 items-center gap-3 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background rounded-md"
|
||||
data-testid={`sidebar-member-open-profile-${member.pubkey}`}
|
||||
onClick={() => onOpenProfile(member.pubkey)}
|
||||
type="button"
|
||||
>
|
||||
{memberIdentity}
|
||||
</button>
|
||||
) : (
|
||||
<div className="flex min-w-0 items-center gap-3">{memberIdentity}</div>
|
||||
)}
|
||||
{hasActions ? (
|
||||
<MemberActionsMenu
|
||||
canChangeRole={canChangeRole}
|
||||
|
||||
Reference in New Issue
Block a user