mirror of
https://github.com/block/buzz.git
synced 2026-11-04 08:21:48 +00:00
fix(desktop): refine inbox context header
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { useAppNavigation } from "@/app/navigation/useAppNavigation";
|
||||
import { useChannelsQuery } from "@/features/channels/hooks";
|
||||
import { useIdentityQuery } from "@/shared/api/hooks";
|
||||
import { HomeScreen } from "@/features/home/ui/HomeScreen";
|
||||
@@ -9,6 +10,7 @@ export const Route = createFileRoute("/")({
|
||||
});
|
||||
|
||||
function HomeRouteComponent() {
|
||||
const { goChannel } = useAppNavigation();
|
||||
const channelsQuery = useChannelsQuery();
|
||||
const identityQuery = useIdentityQuery();
|
||||
const channels = channelsQuery.data ?? [];
|
||||
@@ -18,6 +20,9 @@ function HomeRouteComponent() {
|
||||
<HomeScreen
|
||||
availableChannelIds={availableChannelIds}
|
||||
currentPubkey={identityQuery.data?.pubkey}
|
||||
onOpenContext={(channelId, messageId) => {
|
||||
void goChannel(channelId, { messageId });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ import { HomeView } from "@/features/home/ui/HomeView";
|
||||
type HomeScreenProps = {
|
||||
availableChannelIds: ReadonlySet<string>;
|
||||
currentPubkey?: string;
|
||||
onOpenContext: (channelId: string, messageId: string) => void;
|
||||
};
|
||||
|
||||
export function HomeScreen({
|
||||
availableChannelIds,
|
||||
currentPubkey,
|
||||
onOpenContext,
|
||||
}: HomeScreenProps) {
|
||||
const homeFeedQuery = useHomeFeedQuery();
|
||||
|
||||
@@ -33,6 +35,7 @@ export function HomeScreen({
|
||||
}
|
||||
feed={homeFeedQuery.data}
|
||||
isLoading={homeFeedQuery.isLoading}
|
||||
onOpenContext={onOpenContext}
|
||||
onRefresh={() => {
|
||||
void homeFeedQuery.refetch();
|
||||
}}
|
||||
|
||||
@@ -104,6 +104,7 @@ type HomeViewProps = {
|
||||
errorMessage?: string;
|
||||
currentPubkey?: string;
|
||||
availableChannelIds: ReadonlySet<string>;
|
||||
onOpenContext: (channelId: string, messageId: string) => void;
|
||||
onRefresh: () => void;
|
||||
};
|
||||
|
||||
@@ -113,6 +114,7 @@ export function HomeView({
|
||||
errorMessage,
|
||||
currentPubkey,
|
||||
availableChannelIds,
|
||||
onOpenContext,
|
||||
onRefresh,
|
||||
}: HomeViewProps) {
|
||||
const [filter, setFilter] = React.useState<InboxFilter>("all");
|
||||
@@ -355,6 +357,7 @@ export function HomeView({
|
||||
item={selectedItem}
|
||||
messages={contextMessages}
|
||||
replies={selectedItemReplies}
|
||||
contextChannelName={selectedChannel?.name ?? null}
|
||||
onDelete={() => {
|
||||
if (!selectedItem || !canDelete) {
|
||||
return;
|
||||
@@ -369,6 +372,7 @@ export function HomeView({
|
||||
setIsDeletingMessage(false);
|
||||
});
|
||||
}}
|
||||
onOpenContext={onOpenContext}
|
||||
onSendReply={async ({
|
||||
content,
|
||||
mediaTags,
|
||||
|
||||
@@ -44,7 +44,9 @@ type InboxDetailPaneProps = {
|
||||
item: InboxItem | null;
|
||||
messages?: InboxContextMessage[];
|
||||
replies?: InboxReply[];
|
||||
contextChannelName?: string | null;
|
||||
onDelete: () => void;
|
||||
onOpenContext?: (channelId: string, messageId: string) => void;
|
||||
onSendReply: (input: {
|
||||
content: string;
|
||||
mediaTags?: string[][];
|
||||
@@ -88,7 +90,9 @@ export function InboxDetailPane({
|
||||
item,
|
||||
messages = [],
|
||||
replies = [],
|
||||
contextChannelName = null,
|
||||
onDelete,
|
||||
onOpenContext,
|
||||
onSendReply,
|
||||
onToggleDone,
|
||||
onToggleReaction,
|
||||
@@ -179,6 +183,11 @@ export function InboxDetailPane({
|
||||
id: replyTarget.id,
|
||||
}
|
||||
: null;
|
||||
const channelContextName = contextChannelName ?? item.channelLabel;
|
||||
const contextLabel = channelContextName
|
||||
? `#${channelContextName}`
|
||||
: item.categoryLabel;
|
||||
const contextChannelId = item.item.channelId;
|
||||
|
||||
const handleSelectReplyTarget = (message: InboxDisplayMessage) => {
|
||||
setReplyTargetId((currentReplyTargetId) =>
|
||||
@@ -193,76 +202,60 @@ export function InboxDetailPane({
|
||||
data-testid="home-inbox-detail"
|
||||
ref={detailPaneRef}
|
||||
>
|
||||
{!canOpenChannel ? (
|
||||
<div className="px-6 pb-4 pt-14">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<UserAvatar
|
||||
avatarUrl={item.avatarUrl}
|
||||
className="h-8 w-8 rounded-xl"
|
||||
displayName={item.senderLabel}
|
||||
size="md"
|
||||
/>
|
||||
<div className="min-w-0">
|
||||
<div className="flex min-w-0 flex-wrap items-center gap-2">
|
||||
<p className="truncate text-base font-semibold">
|
||||
{item.senderLabel}
|
||||
</p>
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex items-center text-[10px] font-semibold uppercase tracking-[0.14em]",
|
||||
item.isActionRequired
|
||||
? "text-amber-600 dark:text-amber-300"
|
||||
: "text-primary",
|
||||
)}
|
||||
>
|
||||
{item.categoryLabel}
|
||||
</span>
|
||||
{item.channelLabel ? (
|
||||
<span className="inline-flex items-center text-[11px] font-medium text-muted-foreground">
|
||||
#{item.channelLabel}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="mt-1 flex flex-wrap items-center gap-2 text-sm text-muted-foreground">
|
||||
<span>{item.fullTimestampLabel}</span>
|
||||
<span>Inbox only</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-6 pb-1 pt-14">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="flex min-w-0 flex-wrap items-center gap-2">
|
||||
{canOpenChannel && contextChannelId && onOpenContext ? (
|
||||
<button
|
||||
className="truncate text-left text-sm font-semibold text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
onClick={() => onOpenContext(contextChannelId, item.id)}
|
||||
type="button"
|
||||
>
|
||||
{contextLabel}
|
||||
</button>
|
||||
) : (
|
||||
<p className="truncate text-sm font-semibold text-foreground">
|
||||
{contextLabel}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-4">
|
||||
<TooltipProvider delayDuration={200}>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-0.5">
|
||||
<HeaderIconAction
|
||||
label={isDone ? "Mark unread" : "Mark done"}
|
||||
onClick={onToggleDone}
|
||||
icon={
|
||||
isDone ? (
|
||||
<MailOpen className="h-4 w-4" />
|
||||
) : (
|
||||
<CheckCheck className="h-4 w-4" />
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{canDelete ? (
|
||||
<HeaderMoreMenu
|
||||
isDeletingMessage={isDeletingMessage}
|
||||
onDelete={onDelete}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
<div className="mt-1 flex min-w-0 flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
||||
<span>{item.fullTimestampLabel}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-4">
|
||||
<TooltipProvider delayDuration={200}>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-0.5">
|
||||
<HeaderIconAction
|
||||
label={isDone ? "Mark unread" : "Mark done"}
|
||||
onClick={onToggleDone}
|
||||
icon={
|
||||
isDone ? (
|
||||
<MailOpen className="h-4 w-4" />
|
||||
) : (
|
||||
<CheckCheck className="h-4 w-4" />
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{canDelete ? (
|
||||
<HeaderMoreMenu
|
||||
isDeletingMessage={isDeletingMessage}
|
||||
onDelete={onDelete}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="relative min-h-0 flex-1 overflow-hidden">
|
||||
<div className="absolute inset-0 overflow-y-auto overscroll-contain pb-32 pt-14">
|
||||
<div className="absolute inset-0 overflow-y-auto overscroll-contain pb-32 pt-1">
|
||||
<div>
|
||||
{isThreadContextLoading ? (
|
||||
<div className="px-6 pb-3 text-[11px] text-muted-foreground">
|
||||
|
||||
@@ -29,8 +29,8 @@ export function InboxListPane({
|
||||
selectedId,
|
||||
}: InboxListPaneProps) {
|
||||
return (
|
||||
<section className="flex min-h-0 min-w-0 flex-col overflow-hidden border-r border-border/70 bg-background/60">
|
||||
<div className="px-4 pb-3 pt-14">
|
||||
<section className="flex min-h-0 min-w-0 flex-col overflow-hidden bg-background/60">
|
||||
<div className="px-5 pb-3 pt-14">
|
||||
<div className="flex flex-nowrap gap-1">
|
||||
{FILTER_OPTIONS.map((option) => (
|
||||
<Button
|
||||
@@ -72,7 +72,7 @@ export function InboxListPane({
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
"flex w-full items-start gap-2.5 border-l px-4 py-2 text-left transition-colors",
|
||||
"flex w-full items-start gap-2.5 border-l px-5 py-2 text-left transition-colors",
|
||||
isSelected
|
||||
? "border-l-primary bg-muted/30"
|
||||
: "border-l-transparent hover:bg-muted/25 active:bg-muted/40",
|
||||
|
||||
Reference in New Issue
Block a user