feat(desktop): pinned-messages pill in conv header

This commit is contained in:
byGalax
2026-05-16 17:45:43 +02:00
parent 7e85ffe548
commit ddeedeb71d
2 changed files with 35 additions and 0 deletions
@@ -14,6 +14,7 @@ import {
UsersIcon,
VideoIcon,
} from './icons';
import { PinnedMessagesPill } from './PinnedMessagesPill';
const PRESENCE_DOT: Record<PresenceState, string> = {
online: 'bg-emerald-500',
@@ -30,6 +31,8 @@ interface Props {
onMediaClick?: () => void;
onProfileClick?: (ev: React.MouseEvent) => void;
onSearchClick?: () => void;
pinnedCount?: number;
onOpenPinned?: () => void;
}
export function ConversationHeader({
@@ -39,6 +42,8 @@ export function ConversationHeader({
onMediaClick,
onProfileClick,
onSearchClick,
pinnedCount = 0,
onOpenPinned,
}: Props) {
if (!conversation) {
return <header className="h-[65px] border-b border-line px-6 py-3" aria-busy="true" />;
@@ -49,10 +54,12 @@ export function ConversationHeader({
<HeaderBar
conversation={conversation}
peerPresence={peerPresence}
pinnedCount={pinnedCount}
{...(onInfoClick ? { onInfoClick } : {})}
{...(onMediaClick ? { onMediaClick } : {})}
{...(onProfileClick ? { onProfileClick } : {})}
{...(onSearchClick ? { onSearchClick } : {})}
{...(onOpenPinned ? { onOpenPinned } : {})}
/>
{/* Voice-channel rail rendered separately in ConversationPage so it
can sit between the message surface and the in-call dock. */}
@@ -67,6 +74,8 @@ interface HeaderBarProps {
onMediaClick?: () => void;
onProfileClick?: (ev: React.MouseEvent) => void;
onSearchClick?: () => void;
pinnedCount?: number;
onOpenPinned?: () => void;
}
function HeaderBar({
@@ -76,6 +85,8 @@ function HeaderBar({
onMediaClick,
onProfileClick,
onSearchClick,
pinnedCount = 0,
onOpenPinned,
}: HeaderBarProps) {
const { t } = useTranslation(['app']);
@@ -140,6 +151,7 @@ function HeaderBar({
<div className="flex min-w-0 items-center gap-2">
{!isDm && <UsersIcon className="h-4 w-4 shrink-0 text-fg-muted" />}
<p className="truncate font-display text-base font-semibold text-fg">{title}</p>
<PinnedMessagesPill count={pinnedCount} onClick={() => onOpenPinned?.()} />
</div>
<p className="truncate text-xs text-fg-muted">
{isDm ? (
@@ -0,0 +1,23 @@
import { PinIcon } from './icons';
interface Props {
count: number;
onClick: () => void;
}
// Compact chip rendered in the conv header that opens the pinned panel.
// Renders nothing when count is 0 so a fresh conv shows no clutter.
export function PinnedMessagesPill({ count, onClick }: Props) {
if (count === 0) return null;
return (
<button
type="button"
onClick={onClick}
className="inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-full border border-line bg-surface-3 px-2.5 text-xs font-medium text-fg-muted transition hover:bg-surface-2 hover:text-fg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/40"
title="Angepinnte Nachrichten anzeigen"
>
<PinIcon className="h-3 w-3 text-accent" />
<span>{count} angepinnt</span>
</button>
);
}