This commit is contained in:
2025-10-06 14:20:15 +03:00
Unverified
parent 9da50f2430
commit 9540c88d5a
5 changed files with 7 additions and 107 deletions
@@ -131,7 +131,7 @@ export function ChatMessages({ messages = [], children, isDm = false, onReplySel
const dmEnvelopeId = message?.runtimeData?.dmEnvelope?.id;
if (dmEnvelopeId) {
await request<AddDmReactionRequest["data"], any>({
await request<AddDmReactionRequest["data"]>({
type: "addDmReaction",
credentials: { scheme: "Bearer", credentials: user.authToken },
data: {
@@ -142,7 +142,7 @@ export function ChatMessages({ messages = [], children, isDm = false, onReplySel
}
} else {
// For regular chat messages
await request<AddReactionRequest["data"], any>({
await request<AddReactionRequest["data"]>({
type: "addReaction",
credentials: { scheme: "Bearer", credentials: user.authToken },
data: {
@@ -149,7 +149,6 @@ export function MessageContextMenu({
setIsClosing(false);
setAnimationClass('entering'); // Reset for next opening
}, 200); // Match the animation duration from _animations.scss
// TODO no hardcoded delays
}
interface Action {
@@ -88,12 +88,9 @@ export function MessageReactions({ reactions, onReactionClick, messageId }: Mess
const hasUserReacted = reaction.users.some(u => u.id === user.currentUser?.id);
const isAnimating = animatingReactions.has(reaction.emoji);
// Create a unique key that includes messageId, emoji, count, and index to prevent duplicates
const uniqueKey = `${messageId || 'unknown'}-${reaction.emoji}-${reaction.count}-${index}`;
return (
<button
key={uniqueKey}
key={`${messageId || 'unknown'}-${reaction.emoji}-${reaction.count}-${index}`}
className={`reaction-button ${hasUserReacted ? "reacted" : ""} ${isAnimating ? "removing" : ""}`}
onClick={() => onReactionClick(reaction.emoji)}
title={reaction.users.map(u => u.username).join(", ")}
@@ -1,100 +0,0 @@
import { useState, useEffect } from "react";
import type { Size2D } from "../../../core/types";
interface ReactionBarProps {
isOpen: boolean;
onClose: () => void;
onEmojiSelect: (emoji: string) => void;
onExpandClick: () => void;
position: Size2D;
}
// Most common emojis for quick reactions
const QUICK_REACTIONS = ["👍", "❤️", "😂", "😮", "😢", "😡"];
export function ReactionBar({ isOpen, onClose, onEmojiSelect, onExpandClick, position }: ReactionBarProps) {
const [isClosing, setIsClosing] = useState(false);
const [calculatedPosition, setCalculatedPosition] = useState(position);
function handleEmojiClick(emoji: string) {
onEmojiSelect(emoji);
handleClose();
}
// Smart positioning logic to avoid screen edge clipping
useEffect(() => {
if (isOpen) {
const barWidth = 240; // Approximate width of reaction bar (6 emojis + expand button)
const barHeight = 48; // Approximate height
const padding = 10; // Padding from viewport edges
const viewportWidth = window.innerWidth;
let x = position.x;
let y = position.y - barHeight - 20; // 20px above the position
// Check if bar would overflow right edge
if (x + barWidth + padding > viewportWidth) {
x = viewportWidth - barWidth - padding;
}
// Check if bar would overflow left edge
if (x < padding) {
x = padding;
}
// Check if bar would overflow top edge
if (y < padding) {
y = position.y + 40; // Position below instead of above
}
setCalculatedPosition({ x, y });
}
}, [isOpen, position]);
function handleClose() {
setIsClosing(true);
setTimeout(() => {
onClose();
setIsClosing(false);
}, 150);
}
if (!isOpen) return null;
return (
<div
className={`reaction-bar ${isClosing ? "closing" : ""}`}
style={{
position: "fixed",
left: calculatedPosition.x,
top: calculatedPosition.y,
zIndex: 1001 // Higher than context menu to appear above it
}}
onClick={(e) => e.stopPropagation()}
>
<div className="reaction-bar-content">
{QUICK_REACTIONS.map((emoji, index) => (
<button
key={index}
className="reaction-emoji-button"
onClick={() => handleEmojiClick(emoji)}
title={emoji}
>
{emoji}
</button>
))}
<button
className="reaction-expand-button"
onClick={() => {
handleClose();
onExpandClick();
}}
title="More emojis"
>
<span className="material-symbols">add</span>
</button>
</div>
</div>
);
}