mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(desktop): keep message actions hover-only (#844)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -91,7 +91,6 @@ type ChannelPaneProps = {
|
||||
threadMessages: MainTimelineEntry[];
|
||||
threadPanelWidthPx: number;
|
||||
threadTypingPubkeys: string[];
|
||||
threadReplyTargetId: string | null;
|
||||
threadReplyTargetMessage: TimelineMessage | null;
|
||||
threadScrollTargetId: string | null;
|
||||
targetMessageId: string | null;
|
||||
@@ -160,7 +159,6 @@ export const ChannelPane = React.memo(function ChannelPane({
|
||||
threadPanelWidthPx,
|
||||
threadScrollTargetId,
|
||||
threadTypingPubkeys,
|
||||
threadReplyTargetId,
|
||||
threadReplyTargetMessage,
|
||||
typingPubkeys,
|
||||
}: ChannelPaneProps) {
|
||||
@@ -307,7 +305,6 @@ export const ChannelPane = React.memo(function ChannelPane({
|
||||
) : null}
|
||||
<MessageTimeline
|
||||
channelId={activeChannel?.id}
|
||||
activeReplyTargetId={openThreadHeadId}
|
||||
scrollContainerRef={timelineScrollRef}
|
||||
currentPubkey={currentPubkey}
|
||||
fetchOlder={fetchOlder}
|
||||
@@ -456,7 +453,6 @@ export const ChannelPane = React.memo(function ChannelPane({
|
||||
onToggleReaction={onToggleReaction}
|
||||
onUnfollowThread={onUnfollowThread}
|
||||
profiles={profiles}
|
||||
replyTargetId={threadReplyTargetId}
|
||||
replyTargetMessage={threadReplyTargetMessage}
|
||||
scrollTargetId={threadScrollTargetId}
|
||||
canResetWidth={canResetThreadPanelWidth}
|
||||
|
||||
@@ -551,7 +551,6 @@ export function ChannelScreen({
|
||||
threadMessages={threadMessages}
|
||||
threadPanelWidthPx={threadPanelWidthPx}
|
||||
threadTypingPubkeys={threadTypingPubkeys}
|
||||
threadReplyTargetId={threadReplyTargetId}
|
||||
threadReplyTargetMessage={threadReplyTargetMessage}
|
||||
threadScrollTargetId={threadScrollTargetId}
|
||||
isJoining={joinChannelMutation.isPending}
|
||||
|
||||
@@ -324,7 +324,6 @@ export function InboxDetailPane({
|
||||
<div className="mx-6 my-3 border-t border-border/60" />
|
||||
) : null}
|
||||
<InboxMessageRow
|
||||
activeReplyTargetId={replyTargetId}
|
||||
canReply={canReply}
|
||||
channelId={item.item.channelId}
|
||||
isFocusHighlightVisible={isFocusHighlightVisible}
|
||||
|
||||
@@ -27,7 +27,6 @@ function toTimelineMessage(message: InboxDisplayMessage): TimelineMessage {
|
||||
}
|
||||
|
||||
type InboxMessageRowProps = {
|
||||
activeReplyTargetId: string | null;
|
||||
canReply: boolean;
|
||||
/** Channel UUID for "Copy link" — passed straight through to MessageActionBar. */
|
||||
channelId?: string | null;
|
||||
@@ -42,7 +41,6 @@ type InboxMessageRowProps = {
|
||||
};
|
||||
|
||||
export function InboxMessageRow({
|
||||
activeReplyTargetId,
|
||||
canReply,
|
||||
channelId = null,
|
||||
isFocusHighlightVisible,
|
||||
@@ -89,7 +87,6 @@ export function InboxMessageRow({
|
||||
{canReply || canToggleReactions ? (
|
||||
<div className="absolute right-2 top-1 z-10">
|
||||
<MessageActionBar
|
||||
activeReplyTargetId={activeReplyTargetId}
|
||||
channelId={channelId}
|
||||
message={timelineMessage}
|
||||
onReactionSelect={
|
||||
|
||||
@@ -257,7 +257,6 @@ function MoreActionsMenu({
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function MessageActionBar({
|
||||
activeReplyTargetId = null,
|
||||
channelId,
|
||||
message,
|
||||
onDelete,
|
||||
@@ -272,7 +271,6 @@ export function MessageActionBar({
|
||||
reactionPending = false,
|
||||
isFollowingThread,
|
||||
}: {
|
||||
activeReplyTargetId?: string | null;
|
||||
/** Channel UUID — required for the "Copy link" action; when omitted the
|
||||
* action is hidden (callers like the home inbox that lack the context). */
|
||||
channelId?: string | null;
|
||||
@@ -306,7 +304,6 @@ export function MessageActionBar({
|
||||
return null;
|
||||
}
|
||||
|
||||
const isReplyingToMessage = activeReplyTargetId === message.id;
|
||||
const selectedReactionCount = reactions.filter(
|
||||
(reaction) => reaction.reactedByCurrentUser,
|
||||
).length;
|
||||
@@ -318,7 +315,7 @@ export function MessageActionBar({
|
||||
"opacity-100 sm:pointer-events-none sm:opacity-0",
|
||||
"sm:group-hover/message:pointer-events-auto sm:group-hover/message:opacity-100",
|
||||
"sm:group-focus-within/message:pointer-events-auto sm:group-focus-within/message:opacity-100",
|
||||
isReplyingToMessage || isReactionPickerOpen || isDropdownOpen
|
||||
isReactionPickerOpen || isDropdownOpen
|
||||
? "sm:pointer-events-auto sm:opacity-100"
|
||||
: "",
|
||||
)}
|
||||
@@ -390,7 +387,7 @@ export function MessageActionBar({
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
aria-label={isReplyingToMessage ? "Cancel reply" : "Reply"}
|
||||
aria-label="Reply"
|
||||
className="h-6 w-6 rounded-full p-0"
|
||||
data-testid={`reply-message-${message.id}`}
|
||||
onClick={() => {
|
||||
@@ -398,14 +395,12 @@ export function MessageActionBar({
|
||||
}}
|
||||
size="sm"
|
||||
type="button"
|
||||
variant={isReplyingToMessage ? "secondary" : "ghost"}
|
||||
variant="ghost"
|
||||
>
|
||||
<CornerUpLeft className="h-3 w-3" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{isReplyingToMessage ? "Cancel reply" : "Reply"}
|
||||
</TooltipContent>
|
||||
<TooltipContent>Reply</TooltipContent>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ const NESTED_REPLY_OFFSET_PX = 28;
|
||||
|
||||
export const MessageRow = React.memo(
|
||||
function MessageRow({
|
||||
activeReplyTargetId = null,
|
||||
channelId = null,
|
||||
highlighted = false,
|
||||
isFollowingThread,
|
||||
@@ -44,7 +43,6 @@ export const MessageRow = React.memo(
|
||||
profiles,
|
||||
searchQuery,
|
||||
}: {
|
||||
activeReplyTargetId?: string | null;
|
||||
channelId?: string | null;
|
||||
highlighted?: boolean;
|
||||
isFollowingThread?: boolean;
|
||||
@@ -212,7 +210,6 @@ export const MessageRow = React.memo(
|
||||
const actionBarNode = (
|
||||
<div className="absolute right-2 top-1 z-10">
|
||||
<MessageActionBar
|
||||
activeReplyTargetId={activeReplyTargetId}
|
||||
channelId={channelId}
|
||||
isFollowingThread={isFollowingThread}
|
||||
message={message}
|
||||
@@ -451,7 +448,6 @@ export const MessageRow = React.memo(
|
||||
prev.message.role === next.message.role &&
|
||||
prev.message.personaDisplayName === next.message.personaDisplayName &&
|
||||
prev.highlighted === next.highlighted &&
|
||||
prev.activeReplyTargetId === next.activeReplyTargetId &&
|
||||
prev.isFollowingThread === next.isFollowingThread &&
|
||||
prev.layoutVariant === next.layoutVariant &&
|
||||
prev.profiles === next.profiles &&
|
||||
|
||||
@@ -63,7 +63,6 @@ type MessageThreadPanelProps = {
|
||||
remove: boolean,
|
||||
) => Promise<void>;
|
||||
profiles?: UserProfileLookup;
|
||||
replyTargetId: string | null;
|
||||
replyTargetMessage: TimelineMessage | null;
|
||||
scrollTargetId: string | null;
|
||||
threadHead: TimelineMessage | null;
|
||||
@@ -116,7 +115,6 @@ export function MessageThreadPanel({
|
||||
onToggleReaction,
|
||||
onUnfollowThread,
|
||||
profiles,
|
||||
replyTargetId,
|
||||
replyTargetMessage,
|
||||
scrollTargetId,
|
||||
threadHead,
|
||||
@@ -275,7 +273,6 @@ export function MessageThreadPanel({
|
||||
<div className="px-3 pb-1 pt-0" data-testid="message-thread-head">
|
||||
<div className="rounded-2xl">
|
||||
<MessageRow
|
||||
activeReplyTargetId={replyTargetId}
|
||||
channelId={channelId}
|
||||
isFollowingThread={isFollowingThread}
|
||||
layoutVariant="thread-reply"
|
||||
@@ -316,7 +313,6 @@ export function MessageThreadPanel({
|
||||
key={entry.message.id}
|
||||
>
|
||||
<MessageRow
|
||||
activeReplyTargetId={replyTargetId}
|
||||
channelId={channelId}
|
||||
layoutVariant="thread-reply"
|
||||
message={entry.message}
|
||||
|
||||
@@ -18,7 +18,6 @@ type MessageTimelineProps = {
|
||||
isLoading?: boolean;
|
||||
emptyTitle?: string;
|
||||
emptyDescription?: string;
|
||||
activeReplyTargetId?: string | null;
|
||||
currentPubkey?: string;
|
||||
fetchOlder?: () => Promise<void>;
|
||||
hasOlderMessages?: boolean;
|
||||
@@ -58,7 +57,6 @@ export const MessageTimeline = React.memo(function MessageTimeline({
|
||||
isLoading = false,
|
||||
emptyTitle = "No messages yet",
|
||||
emptyDescription = "Send the first message to start the thread.",
|
||||
activeReplyTargetId = null,
|
||||
currentPubkey,
|
||||
fetchOlder,
|
||||
hasOlderMessages = true,
|
||||
@@ -192,7 +190,6 @@ export const MessageTimeline = React.memo(function MessageTimeline({
|
||||
|
||||
{!isLoading && messages.length > 0 ? (
|
||||
<TimelineMessageList
|
||||
activeReplyTargetId={activeReplyTargetId}
|
||||
channelId={channelId}
|
||||
currentPubkey={currentPubkey}
|
||||
followThreadById={followThreadById}
|
||||
|
||||
@@ -15,7 +15,6 @@ import { MessageThreadSummaryRow } from "./MessageThreadSummaryRow";
|
||||
import { SystemMessageRow } from "./SystemMessageRow";
|
||||
|
||||
type TimelineMessageListProps = {
|
||||
activeReplyTargetId?: string | null;
|
||||
channelId?: string | null;
|
||||
currentPubkey?: string;
|
||||
followThreadById?: (rootId: string) => void;
|
||||
@@ -45,7 +44,6 @@ type TimelineMessageListProps = {
|
||||
};
|
||||
|
||||
export const TimelineMessageList = React.memo(function TimelineMessageList({
|
||||
activeReplyTargetId = null,
|
||||
channelId,
|
||||
currentPubkey,
|
||||
followThreadById,
|
||||
@@ -116,7 +114,6 @@ export const TimelineMessageList = React.memo(function TimelineMessageList({
|
||||
)}
|
||||
>
|
||||
<MessageRow
|
||||
activeReplyTargetId={activeReplyTargetId}
|
||||
channelId={channelId}
|
||||
highlighted={false}
|
||||
isFollowingThread={
|
||||
@@ -165,7 +162,6 @@ export const TimelineMessageList = React.memo(function TimelineMessageList({
|
||||
currentDayGroup?.elements.push(
|
||||
<div key={message.id} className="flex flex-col gap-1">
|
||||
<MessageRow
|
||||
activeReplyTargetId={activeReplyTargetId}
|
||||
channelId={channelId}
|
||||
highlighted={message.id === highlightedMessageId || isSearchActive}
|
||||
message={message}
|
||||
|
||||
Reference in New Issue
Block a user