mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-23 03:25:07 +03:00
Fix dialog click closing the context menu
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import type { Message } from "../../../core/types";
|
||||
import { EditMessageDialog } from "./EditMessageDialog";
|
||||
import { ReplyMessageDialog } from "./ReplyMessageDialog";
|
||||
@@ -9,9 +9,9 @@ interface MessageContextMenuProps {
|
||||
onEdit: (message: Message) => void;
|
||||
onReply: (message: Message) => void;
|
||||
onDelete: (message: Message) => void;
|
||||
onClose: () => void;
|
||||
position: { x: number; y: number };
|
||||
isClosing: boolean;
|
||||
isOpen: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
export interface ContextMenuState {
|
||||
@@ -26,15 +26,54 @@ export function MessageContextMenu({
|
||||
onEdit,
|
||||
onReply,
|
||||
onDelete,
|
||||
onClose,
|
||||
position,
|
||||
isClosing
|
||||
isOpen,
|
||||
onOpenChange
|
||||
}: MessageContextMenuProps) {
|
||||
console.log("MessageContextMenu rendered with position:", position, "message:", message.id);
|
||||
|
||||
// Internal state for dialogs
|
||||
// Internal state for dialogs and closing animation
|
||||
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
||||
const [replyDialogOpen, setReplyDialogOpen] = useState(false);
|
||||
const [isClosing, setIsClosing] = useState(false);
|
||||
|
||||
// Effect to handle clicks outside the context menu
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (isOpen && !isClosing && !editDialogOpen && !replyDialogOpen) {
|
||||
// Check if the click is on a context menu element
|
||||
const target = event.target as Element;
|
||||
if (!target.closest('.context-menu')) {
|
||||
handleClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape' && isOpen && !isClosing && !editDialogOpen && !replyDialogOpen) {
|
||||
handleClose();
|
||||
}
|
||||
};
|
||||
|
||||
const handleWindowBlur = () => {
|
||||
// Close context menu when browser window loses focus
|
||||
if (isOpen && !isClosing && !editDialogOpen && !replyDialogOpen) {
|
||||
handleClose();
|
||||
}
|
||||
};
|
||||
|
||||
// Add event listeners
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
window.addEventListener('blur', handleWindowBlur);
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
document.removeEventListener('keydown', handleKeyDown);
|
||||
window.removeEventListener('blur', handleWindowBlur);
|
||||
};
|
||||
}, [isOpen, isClosing, editDialogOpen, replyDialogOpen]);
|
||||
|
||||
const handleAction = (action: string) => {
|
||||
console.log("Context menu action triggered:", action);
|
||||
@@ -50,12 +89,21 @@ export function MessageContextMenu({
|
||||
case "delete":
|
||||
if (isAuthor) {
|
||||
onDelete(message);
|
||||
onClose();
|
||||
handleClose();
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setIsClosing(true);
|
||||
// Wait for animation to complete before calling onOpenChange
|
||||
setTimeout(() => {
|
||||
onOpenChange(false);
|
||||
setIsClosing(false);
|
||||
}, 200); // Match the animation duration from _animations.scss
|
||||
};
|
||||
|
||||
const handleEditSave = (messageId: number, newContent: string) => {
|
||||
// Create a temporary message object with the updated content
|
||||
const updatedMessage = { ...message, content: newContent };
|
||||
@@ -70,36 +118,45 @@ export function MessageContextMenu({
|
||||
setReplyDialogOpen(false);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const content = (
|
||||
<div
|
||||
className={`context-menu ${isClosing ? 'closing' : 'entering'}`}
|
||||
style={{
|
||||
position: "fixed",
|
||||
display: "block",
|
||||
top: position.y,
|
||||
left: position.x,
|
||||
zIndex: 1000
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="context-menu-item" onClick={() => handleAction("reply")}>
|
||||
<span className="material-symbols">reply</span>
|
||||
Reply
|
||||
</div>
|
||||
{isAuthor && (
|
||||
<>
|
||||
<div className="context-menu-item" onClick={() => handleAction("edit")}>
|
||||
<span className="material-symbols">edit</span>
|
||||
Edit
|
||||
</div>
|
||||
<div className="context-menu-item" onClick={() => handleAction("delete")}>
|
||||
<span className="material-symbols">delete</span>
|
||||
Delete
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
// Don't render if not open
|
||||
if (!isOpen && !editDialogOpen && !replyDialogOpen) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`context-menu ${isClosing ? 'closing' : 'entering'}`}
|
||||
style={{
|
||||
position: "fixed",
|
||||
display: "block",
|
||||
top: position.y,
|
||||
left: position.x,
|
||||
zIndex: 1000
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="context-menu-item" onClick={() => handleAction("reply")}>
|
||||
<span className="material-symbols">reply</span>
|
||||
Reply
|
||||
</div>
|
||||
{isAuthor && (
|
||||
<>
|
||||
<div className="context-menu-item" onClick={() => handleAction("edit")}>
|
||||
<span className="material-symbols">edit</span>
|
||||
Edit
|
||||
</div>
|
||||
<div className="context-menu-item" onClick={() => handleAction("delete")}>
|
||||
<span className="material-symbols">delete</span>
|
||||
Delete
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{isOpen ? content : null}
|
||||
|
||||
{/* Edit Dialog */}
|
||||
<EditMessageDialog
|
||||
|
||||
Reference in New Issue
Block a user