mirror of
https://github.com/block/buzz.git
synced 2026-10-31 22:41:12 +00:00
fix(desktop): scope relaxed spacing to threads
Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
This commit is contained in:
@@ -24,7 +24,7 @@ export const THREAD_FOCUS_SLIVER_WIDTH_PX = 72;
|
||||
* with auto horizontal margins so the reading measure stays comfortable no
|
||||
* matter how wide the window gets.
|
||||
*/
|
||||
export const THREAD_FOCUS_COLUMN_MAX_WIDTH_PX = 880;
|
||||
export const THREAD_FOCUS_COLUMN_MAX_WIDTH_PX = 820;
|
||||
|
||||
/**
|
||||
* Horizontal distance the focus drawer travels on enter/exit.
|
||||
|
||||
@@ -6,15 +6,13 @@
|
||||
/** Inline gutter around thread message rows. */
|
||||
export const THREAD_PANEL_MESSAGE_GUTTER_CLASS = "px-4";
|
||||
|
||||
/** Restores the original message gutter inside the expanded focus overlay. */
|
||||
export const THREAD_FOCUS_MESSAGE_GUTTER_CLASS = "px-2";
|
||||
|
||||
/** Inline gutter around the thread composer and its activity row. */
|
||||
export const THREAD_PANEL_COMPOSER_GUTTER_CLASS = "px-5";
|
||||
|
||||
/**
|
||||
* Centers the reading column when a `columnMaxWidthPx` is supplied (focus-mode
|
||||
* drawer). `px-10` is the original inline gutter between the column and drawer
|
||||
* edges; the max-width itself is applied inline since it is caller-provided.
|
||||
* drawer). The responsive gutter keeps compact focus drawers usable while
|
||||
* preserving generous whitespace at desktop widths. The max-width itself is
|
||||
* applied inline since it is a caller-provided value.
|
||||
*/
|
||||
export const THREAD_PANEL_COLUMN_CLASS = "mx-auto w-full px-10";
|
||||
export const THREAD_PANEL_COLUMN_CLASS = "mx-auto w-full px-6 sm:px-10";
|
||||
|
||||
@@ -12,7 +12,10 @@ import { useKnownAgentPubkeys } from "@/features/agents/useKnownAgentPubkeys";
|
||||
import { HuddleAttachment } from "@/features/huddle/components/HuddleAttachment";
|
||||
import { MessageReactions } from "@/features/messages/ui/MessageReactions";
|
||||
import { useReactionHandler } from "@/features/messages/ui/useReactionHandler";
|
||||
import type { UserProfileLookup } from "@/features/profile/lib/identity";
|
||||
import {
|
||||
formatOwnerLabel,
|
||||
type UserProfileLookup,
|
||||
} from "@/features/profile/lib/identity";
|
||||
import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover";
|
||||
import { useRemindLater } from "@/features/reminders/ui/RemindMeLaterProvider";
|
||||
import {
|
||||
@@ -81,6 +84,7 @@ export const MessageRow = React.memo(
|
||||
isUnread,
|
||||
layoutVariant = "default",
|
||||
message,
|
||||
currentPubkey,
|
||||
onCollapseDepthGuide,
|
||||
onCollapseDepthGuideHoverChange,
|
||||
onCollapseDescendants,
|
||||
@@ -120,6 +124,7 @@ export const MessageRow = React.memo(
|
||||
isUnread?: boolean;
|
||||
layoutVariant?: "default" | "thread-reply";
|
||||
message: TimelineMessage;
|
||||
currentPubkey?: string;
|
||||
onCollapseDepthGuide?: (message: TimelineMessage) => void;
|
||||
onCollapseDepthGuideHoverChange?: (
|
||||
message: TimelineMessage,
|
||||
@@ -209,11 +214,11 @@ export const MessageRow = React.memo(
|
||||
},
|
||||
[knownAgentPubkeys, profiles],
|
||||
);
|
||||
const profilePopoverRole =
|
||||
const displayedAuthorIsAgent =
|
||||
message.role === "bot" ||
|
||||
(message.pubkey && isKnownAgentPubkey(message.pubkey))
|
||||
? "bot"
|
||||
: message.role;
|
||||
message.isAgent === true ||
|
||||
Boolean(message.pubkey && isKnownAgentPubkey(message.pubkey));
|
||||
const profilePopoverRole = displayedAuthorIsAgent ? "bot" : message.role;
|
||||
const agentMentionPubkeysByName = React.useMemo(() => {
|
||||
if (!mentionPubkeysByName) {
|
||||
return undefined;
|
||||
@@ -246,7 +251,8 @@ export const MessageRow = React.memo(
|
||||
message.body,
|
||||
message.tags,
|
||||
);
|
||||
const bodyOffsetClass = "mt-1";
|
||||
const bodyOffsetClass =
|
||||
layoutVariant === "thread-reply" || emojiOnly ? "mt-1" : "-mt-0.5";
|
||||
|
||||
const { nonDmChannelNames: channelNames } = useChannelNavigation();
|
||||
const openVideoReviewAt = useOpenVideoReviewAt();
|
||||
@@ -367,7 +373,9 @@ export const MessageRow = React.memo(
|
||||
<Markdown
|
||||
channelNames={channelNames}
|
||||
className={cn(
|
||||
"max-w-full leading-relaxed [&>p+p]:mt-2 [&>ol]:space-y-1.5 [&>ul]:space-y-1.5",
|
||||
"max-w-full text-sm",
|
||||
layoutVariant === "thread-reply" &&
|
||||
"leading-relaxed [&>p+p]:mt-2 [&>ol]:space-y-1.5 [&>ul]:space-y-1.5",
|
||||
emojiOnly &&
|
||||
"text-4xl leading-tight [&_p]:leading-tight [&_img[data-custom-emoji]]:h-[1.45em] [&_img[data-custom-emoji]]:align-middle [&_button:has(img[data-custom-emoji])]:align-middle",
|
||||
)}
|
||||
@@ -503,10 +511,18 @@ export const MessageRow = React.memo(
|
||||
) : (
|
||||
<MessageAuthorText as="h3">{message.author}</MessageAuthorText>
|
||||
);
|
||||
const agentOwnerNode = message.isAgent ? (
|
||||
const agentOwnerPubkey =
|
||||
message.ownerPubkey ??
|
||||
(message.pubkey
|
||||
? profiles?.[normalizePubkey(message.pubkey)]?.ownerPubkey
|
||||
: null);
|
||||
const agentOwnerLabel =
|
||||
message.ownerLabel ??
|
||||
formatOwnerLabel(agentOwnerPubkey, currentPubkey, profiles);
|
||||
const agentOwnerNode = displayedAuthorIsAgent ? (
|
||||
<MessageAgentOwner
|
||||
ownerLabel={message.ownerLabel}
|
||||
ownerPubkey={message.ownerPubkey}
|
||||
ownerLabel={agentOwnerLabel}
|
||||
ownerPubkey={agentOwnerPubkey}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
@@ -823,7 +839,7 @@ export const MessageRow = React.memo(
|
||||
className={cn(
|
||||
"group/message relative z-10 rounded-2xl transition-colors",
|
||||
playEntrance && "motion-enter-conversation",
|
||||
isThreadReplyLayout ? "py-2" : "py-1.5",
|
||||
isThreadReplyLayout ? "py-2" : "py-1",
|
||||
hoverBackground
|
||||
? "mx-1 px-2 hover:bg-muted/50 focus-within:bg-muted/50"
|
||||
: isThreadReplyLayout
|
||||
@@ -872,6 +888,7 @@ export const MessageRow = React.memo(
|
||||
prev.message.isAgent === next.message.isAgent &&
|
||||
prev.message.ownerPubkey === next.message.ownerPubkey &&
|
||||
prev.message.ownerLabel === next.message.ownerLabel &&
|
||||
prev.currentPubkey === next.currentPubkey &&
|
||||
prev.message.avatarUrl === next.message.avatarUrl &&
|
||||
prev.message.accent === next.message.accent &&
|
||||
prev.message.time === next.message.time &&
|
||||
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
AuxiliaryPanelTitle,
|
||||
} from "@/shared/layout/AuxiliaryPanel";
|
||||
import {
|
||||
THREAD_FOCUS_MESSAGE_GUTTER_CLASS,
|
||||
THREAD_PANEL_COLUMN_CLASS,
|
||||
THREAD_PANEL_COMPOSER_GUTTER_CLASS,
|
||||
THREAD_PANEL_MESSAGE_GUTTER_CLASS,
|
||||
@@ -256,9 +255,6 @@ export function MessageThreadPanel({
|
||||
!isHuddleTranscript && (isOverlay || isSinglePanelView || isFocusMode),
|
||||
);
|
||||
const hasConstrainedColumn = columnMaxWidthPx != null;
|
||||
const messageGutterClass = hasConstrainedColumn
|
||||
? THREAD_FOCUS_MESSAGE_GUTTER_CLASS
|
||||
: THREAD_PANEL_MESSAGE_GUTTER_CLASS;
|
||||
// Whether the composer dock trades its quiet-state spacer for the
|
||||
// conditional activity accessory (agent working and/or someone typing).
|
||||
const hasComposerBottomActivity =
|
||||
@@ -573,18 +569,19 @@ export function MessageThreadPanel({
|
||||
}
|
||||
>
|
||||
{isHuddleTranscript ? (
|
||||
<div className={cn(messageGutterClass, "pb-2 pt-4")}>
|
||||
<div className={cn(THREAD_PANEL_MESSAGE_GUTTER_CLASS, "pb-2 pt-4")}>
|
||||
<HuddleTranscriptIntro />
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={cn(messageGutterClass, "pb-2 pt-1")}
|
||||
className={cn(THREAD_PANEL_MESSAGE_GUTTER_CLASS, "pb-2 pt-1")}
|
||||
data-testid="message-thread-head"
|
||||
>
|
||||
<div className="rounded-2xl">
|
||||
<MessageRow
|
||||
actionBarPlacement="inside"
|
||||
channelId={channelId}
|
||||
currentPubkey={currentPubkey}
|
||||
huddleMemberPubkeys={huddleMemberPubkeys}
|
||||
huddleMemberPubkeysPending={huddleMemberPubkeysPending}
|
||||
isFollowingThread={isFollowingThread}
|
||||
@@ -635,7 +632,7 @@ export function MessageThreadPanel({
|
||||
|
||||
{showThreadHeadDivider ? (
|
||||
<div
|
||||
className={cn(messageGutterClass, "pb-4 pt-3")}
|
||||
className={cn(THREAD_PANEL_MESSAGE_GUTTER_CLASS, "pb-4 pt-3")}
|
||||
data-testid="message-thread-head-divider"
|
||||
>
|
||||
<Separator className="bg-border/60" />
|
||||
@@ -643,7 +640,7 @@ export function MessageThreadPanel({
|
||||
) : null}
|
||||
|
||||
<div
|
||||
className={cn(messageGutterClass, "pb-4 pt-0")}
|
||||
className={cn(THREAD_PANEL_MESSAGE_GUTTER_CLASS, "pb-4 pt-0")}
|
||||
data-testid="message-thread-replies"
|
||||
>
|
||||
{threadRepliesPending && !isHuddleTranscript ? (
|
||||
@@ -717,6 +714,7 @@ export function MessageThreadPanel({
|
||||
{showUnreadDivider ? <UnreadDivider /> : null}
|
||||
<MessageRow
|
||||
channelId={channelId}
|
||||
currentPubkey={currentPubkey}
|
||||
collapseDepthGuideActions={collapseDepthGuideActions}
|
||||
collapseDescendantsLabel="Collapse replies"
|
||||
connectDescendants={
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { ThreadPanelLayoutProps } from "@/features/channels/lib/threadPanelLayout";
|
||||
import {
|
||||
THREAD_FOCUS_MESSAGE_GUTTER_CLASS,
|
||||
THREAD_PANEL_COLUMN_CLASS,
|
||||
THREAD_PANEL_COMPOSER_GUTTER_CLASS,
|
||||
THREAD_PANEL_MESSAGE_GUTTER_CLASS,
|
||||
@@ -107,9 +106,6 @@ export function MessageThreadPanelSkeleton({
|
||||
}: MessageThreadPanelSkeletonProps) {
|
||||
const isOverlay = useIsThreadPanelOverlay();
|
||||
const hasConstrainedColumn = columnMaxWidthPx != null;
|
||||
const messageGutterClass = hasConstrainedColumn
|
||||
? THREAD_FOCUS_MESSAGE_GUTTER_CLASS
|
||||
: THREAD_PANEL_MESSAGE_GUTTER_CLASS;
|
||||
useEscapeKey(onClose, isOverlay || isSinglePanelView || isFocusMode);
|
||||
|
||||
const threadHeaderContent = (
|
||||
@@ -135,12 +131,17 @@ export function MessageThreadPanelSkeleton({
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={cn(messageGutterClass, "pb-1 pt-0")}
|
||||
className={cn(THREAD_PANEL_MESSAGE_GUTTER_CLASS, "pb-1 pt-0")}
|
||||
data-testid="message-thread-head-loading"
|
||||
>
|
||||
<ThreadMessageSkeleton isHead />
|
||||
</div>
|
||||
<div className={cn("space-y-2.5 pb-3 pt-1", messageGutterClass)}>
|
||||
<div
|
||||
className={cn(
|
||||
"space-y-2.5 pb-3 pt-1",
|
||||
THREAD_PANEL_MESSAGE_GUTTER_CLASS,
|
||||
)}
|
||||
>
|
||||
<ThreadMessageSkeleton />
|
||||
<ThreadMessageSkeleton />
|
||||
<div className="ml-[58px] flex items-center gap-1.5 pt-0.5">
|
||||
|
||||
@@ -143,6 +143,7 @@ export function MessageRowItem({
|
||||
>
|
||||
<MessageRow
|
||||
channelId={channelId}
|
||||
currentPubkey={currentPubkey}
|
||||
highlighted={false}
|
||||
hoverBackground={false}
|
||||
huddleMemberPubkeys={huddleMemberPubkeys}
|
||||
@@ -202,6 +203,7 @@ export function MessageRowItem({
|
||||
>
|
||||
<MessageRow
|
||||
channelId={channelId}
|
||||
currentPubkey={currentPubkey}
|
||||
highlighted={message.id === highlightedMessageId || isSearchActive}
|
||||
huddleMemberPubkeys={huddleMemberPubkeys}
|
||||
huddleMemberPubkeysPending={huddleMemberPubkeysPending}
|
||||
|
||||
Reference in New Issue
Block a user