mirror of
https://github.com/block/buzz.git
synced 2026-10-28 13:01:10 +00: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:
@@ -28,6 +28,7 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/shared/ui/dialog";
|
} from "@/shared/ui/dialog";
|
||||||
|
import { useProfilePanel } from "@/shared/context/ProfilePanelContext";
|
||||||
import { useFeedbackToasts } from "@/shared/hooks/useToastEffect";
|
import { useFeedbackToasts } from "@/shared/hooks/useToastEffect";
|
||||||
import { normalizePubkey } from "@/shared/lib/pubkey";
|
import { normalizePubkey } from "@/shared/lib/pubkey";
|
||||||
import {
|
import {
|
||||||
@@ -161,6 +162,21 @@ export function MembersSidebar({
|
|||||||
|
|
||||||
useFeedbackToasts(actionNoticeMessage, actionErrorMessage);
|
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] =
|
const [editRespondToAgent, setEditRespondToAgent] =
|
||||||
React.useState<ManagedAgent | null>(null);
|
React.useState<ManagedAgent | null>(null);
|
||||||
|
|
||||||
@@ -192,6 +208,7 @@ export function MembersSidebar({
|
|||||||
onManagedAgentAction={(agent) => {
|
onManagedAgentAction={(agent) => {
|
||||||
void handleAgentLifecycleAction(agent);
|
void handleAgentLifecycleAction(agent);
|
||||||
}}
|
}}
|
||||||
|
onOpenProfile={handleOpenProfile}
|
||||||
onRemoveMember={handleRemoveMember}
|
onRemoveMember={handleRemoveMember}
|
||||||
onViewActivity={
|
onViewActivity={
|
||||||
onViewActivity
|
onViewActivity
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ type MembersSidebarMemberCardProps = {
|
|||||||
onChangeRole: (member: ChannelMember, role: string) => void;
|
onChangeRole: (member: ChannelMember, role: string) => void;
|
||||||
onEditRespondTo?: (agent: ManagedAgent) => void;
|
onEditRespondTo?: (agent: ManagedAgent) => void;
|
||||||
onManagedAgentAction: (agent: ManagedAgent) => void;
|
onManagedAgentAction: (agent: ManagedAgent) => void;
|
||||||
|
onOpenProfile?: (pubkey: string) => void;
|
||||||
onRemoveMember: (member: ChannelMember) => void;
|
onRemoveMember: (member: ChannelMember) => void;
|
||||||
onViewActivity?: (pubkey: string) => void;
|
onViewActivity?: (pubkey: string) => void;
|
||||||
presenceStatus?: PresenceStatus | null;
|
presenceStatus?: PresenceStatus | null;
|
||||||
@@ -97,6 +98,7 @@ export function MembersSidebarMemberCard({
|
|||||||
onChangeRole,
|
onChangeRole,
|
||||||
onEditRespondTo,
|
onEditRespondTo,
|
||||||
onManagedAgentAction,
|
onManagedAgentAction,
|
||||||
|
onOpenProfile,
|
||||||
onRemoveMember,
|
onRemoveMember,
|
||||||
onViewActivity,
|
onViewActivity,
|
||||||
presenceStatus,
|
presenceStatus,
|
||||||
@@ -112,60 +114,76 @@ export function MembersSidebarMemberCard({
|
|||||||
? Boolean(managedAgent) || canRemoveMember || canViewActivity
|
? Boolean(managedAgent) || canRemoveMember || canViewActivity
|
||||||
: canRemoveMember || canChangeRole;
|
: 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 (
|
return (
|
||||||
<div
|
<div
|
||||||
className="group flex items-center justify-between gap-3 rounded-lg px-3 py-2 transition-colors hover:bg-muted/40"
|
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}`}
|
data-testid={`sidebar-member-${member.pubkey}`}
|
||||||
>
|
>
|
||||||
<div className="flex min-w-0 items-center gap-3">
|
{onOpenProfile ? (
|
||||||
<div className="relative shrink-0">
|
<button
|
||||||
<ProfileAvatar
|
aria-label={`Open profile for ${memberLabel}`}
|
||||||
avatarUrl={profileAvatarUrl ?? null}
|
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"
|
||||||
className="h-9 w-9 text-[11px] shadow-none"
|
data-testid={`sidebar-member-open-profile-${member.pubkey}`}
|
||||||
iconClassName="h-4 w-4"
|
onClick={() => onOpenProfile(member.pubkey)}
|
||||||
label={memberAvatarLabel}
|
type="button"
|
||||||
/>
|
>
|
||||||
{presenceStatus ? (
|
{memberIdentity}
|
||||||
<span
|
</button>
|
||||||
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}`}
|
<div className="flex min-w-0 items-center gap-3">{memberIdentity}</div>
|
||||||
>
|
)}
|
||||||
<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>
|
|
||||||
{hasActions ? (
|
{hasActions ? (
|
||||||
<MemberActionsMenu
|
<MemberActionsMenu
|
||||||
canChangeRole={canChangeRole}
|
canChangeRole={canChangeRole}
|
||||||
|
|||||||
Reference in New Issue
Block a user