feat: message context menu
This commit is contained in:
@@ -8,6 +8,7 @@ export default function ContextMenu({ items = [], hideMenu }) {
|
||||
if (!item) return null;
|
||||
const {
|
||||
title,
|
||||
icon = null,
|
||||
handler = (evt) => {
|
||||
evt.preventDefault();
|
||||
hideMenu();
|
||||
@@ -26,6 +27,7 @@ export default function ContextMenu({ items = [], hideMenu }) {
|
||||
hideMenu();
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
{title}
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -22,7 +22,9 @@ export default function DeleteMessageConfirmModal({ closeModal, mids = [] }) {
|
||||
// className="animate__animated animate__fadeInDown animate__faster"
|
||||
buttons={
|
||||
<>
|
||||
<Button onClick={closeModal.bind(null, false)}>Cancel</Button>
|
||||
<Button className="cancel" onClick={closeModal.bind(null, false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={isDeleting}
|
||||
onClick={handleDelete}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useState, useRef } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { hideAll } from "tippy.js";
|
||||
// import toast from "react-hot-toast";
|
||||
import { updateSelectMessages } from "../../../app/slices/ui";
|
||||
import { addReplyingMessage } from "../../../app/slices/message";
|
||||
import StyledMenu from "../styled/Menu";
|
||||
import Tooltip from "../../component/Tooltip";
|
||||
import DeleteMessageConfirm from "../DeleteMessageConfirm";
|
||||
|
||||
import useFavMessage from "../../hook/useFavMessage";
|
||||
import ReactionPicker from "./ReactionPicker";
|
||||
import replyIcon from "../../../assets/icons/reply.svg?url";
|
||||
@@ -16,13 +15,9 @@ import reactIcon from "../../../assets/icons/reaction.svg?url";
|
||||
import editIcon from "../../../assets/icons/edit.svg?url";
|
||||
import IconBookmark from "../../../assets/icons/bookmark.add.svg";
|
||||
import moreIcon from "../../../assets/icons/more.svg?url";
|
||||
import ForwardModal from "../ForwardModal";
|
||||
import PinMessageModal from "./PinMessageModal";
|
||||
import usePinMessage from "../../hook/usePinMessage";
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
import toast from "react-hot-toast";
|
||||
import useMessageOperation from "./useMessageOperation";
|
||||
const StyledCmds = styled.ul`
|
||||
/* z-index: 9999; */
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 0;
|
||||
@@ -62,26 +57,30 @@ const StyledCmds = styled.ul`
|
||||
}
|
||||
`;
|
||||
export default function Commands({
|
||||
content_type = ContentTypes.text,
|
||||
context = "user",
|
||||
contextId = 0,
|
||||
mid = 0,
|
||||
from_uid = 0,
|
||||
toggleEditMessage,
|
||||
}) {
|
||||
const {
|
||||
canDelete,
|
||||
canReply,
|
||||
canEdit,
|
||||
canPin,
|
||||
unPin,
|
||||
pinned,
|
||||
toggleDeleteModal,
|
||||
toggleForwardModal,
|
||||
togglePinModal,
|
||||
PinModal,
|
||||
DeleteModal,
|
||||
ForwardModal,
|
||||
} = useMessageOperation({ mid, context, contextId });
|
||||
const { addFavorite, isFavorited } = useFavMessage({
|
||||
cid: context == "channel" ? contextId : null,
|
||||
});
|
||||
const [mids, setMids] = useState([]);
|
||||
const dispatch = useDispatch();
|
||||
const [pinModalVisible, setPinModalVisible] = useState(false);
|
||||
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
|
||||
const [forwardModalVisible, setForwardModalVisible] = useState(false);
|
||||
const [tippyVisible, setTippyVisible] = useState(false);
|
||||
const { canPin, pins, unpinMessage, isUnpinSuccess } = usePinMessage(
|
||||
context == "channel" ? contextId : undefined
|
||||
);
|
||||
const currUid = useSelector((store) => store.authData.uid);
|
||||
const cmdsRef = useRef(null);
|
||||
const handleReply = (fromMenu) => {
|
||||
if (contextId) {
|
||||
@@ -91,19 +90,7 @@ export default function Commands({
|
||||
hideAll();
|
||||
}
|
||||
};
|
||||
const toggleForwardModal = () => {
|
||||
hideAll();
|
||||
console.log("midss", mids);
|
||||
setForwardModalVisible((prev) => !prev);
|
||||
};
|
||||
const toggleDeleteModal = () => {
|
||||
hideAll();
|
||||
setDeleteModalVisible((prev) => !prev);
|
||||
};
|
||||
const togglePinModal = () => {
|
||||
hideAll();
|
||||
setPinModalVisible((prev) => !prev);
|
||||
};
|
||||
|
||||
const handleTippyVisible = (visible = true) => {
|
||||
setTippyVisible(visible);
|
||||
};
|
||||
@@ -113,7 +100,7 @@ export default function Commands({
|
||||
};
|
||||
const handleUnpin = () => {
|
||||
hideAll();
|
||||
unpinMessage(mid);
|
||||
unPin(mid);
|
||||
};
|
||||
const handleAddFav = async () => {
|
||||
hideAll();
|
||||
@@ -129,128 +116,92 @@ export default function Commands({
|
||||
toast.error("Added Favorites Failed!");
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (isUnpinSuccess) {
|
||||
toast.success("Unpin Message Successfully!");
|
||||
}
|
||||
}, [isUnpinSuccess]);
|
||||
|
||||
useEffect(() => {
|
||||
if (content_type == ContentTypes.archive) {
|
||||
// forward message
|
||||
const forwardEle = document.querySelector(
|
||||
`[data-msg-mid='${mid}'] .down [data-forwarded-mids]`
|
||||
);
|
||||
if (forwardEle) {
|
||||
const mids = forwardEle.dataset.forwardedMids.split(",");
|
||||
setMids(mids);
|
||||
}
|
||||
} else {
|
||||
setMids([mid]);
|
||||
}
|
||||
}, [mid, content_type]);
|
||||
|
||||
const enablePin = context == "channel" && canPin;
|
||||
const enableEdit =
|
||||
currUid == from_uid &&
|
||||
[ContentTypes.text, ContentTypes.markdown].includes(content_type);
|
||||
const enableReply = currUid != from_uid;
|
||||
const pinned = enablePin ? pins.findIndex((p) => p.mid == mid) > -1 : false;
|
||||
return (
|
||||
<StyledCmds
|
||||
ref={cmdsRef}
|
||||
className={`cmds ${tippyVisible ? "visible" : ""}`}
|
||||
>
|
||||
<Tippy
|
||||
duration={0}
|
||||
delay={[0, 0]}
|
||||
onShow={handleTippyVisible.bind(null, true)}
|
||||
onHide={handleTippyVisible.bind(null, false)}
|
||||
interactive
|
||||
placement="left-start"
|
||||
trigger="click"
|
||||
content={<ReactionPicker mid={mid} hidePicker={hideAll} />}
|
||||
<>
|
||||
<StyledCmds
|
||||
ref={cmdsRef}
|
||||
className={`cmds ${tippyVisible ? "visible" : ""}`}
|
||||
>
|
||||
<li className="cmd">
|
||||
<Tooltip placement="top" tip="Add Reaction">
|
||||
<img src={reactIcon} className="toggler" alt="icon emoji" />
|
||||
<Tippy
|
||||
duration={0}
|
||||
delay={[0, 0]}
|
||||
onShow={handleTippyVisible.bind(null, true)}
|
||||
onHide={handleTippyVisible.bind(null, false)}
|
||||
interactive
|
||||
placement="left-start"
|
||||
trigger="click"
|
||||
content={<ReactionPicker mid={mid} hidePicker={hideAll} />}
|
||||
>
|
||||
<li className="cmd">
|
||||
<Tooltip placement="top" tip="Add Reaction">
|
||||
<img src={reactIcon} className="toggler" alt="icon emoji" />
|
||||
</Tooltip>
|
||||
</li>
|
||||
</Tippy>
|
||||
{canEdit && (
|
||||
<li className="cmd" onClick={toggleEditMessage}>
|
||||
<Tooltip placement="top" tip="Edit">
|
||||
<img src={editIcon} alt="icon edit" />
|
||||
</Tooltip>
|
||||
</li>
|
||||
)}
|
||||
{canReply && (
|
||||
<li className="cmd" onClick={handleReply}>
|
||||
<Tooltip placement="top" tip="Reply">
|
||||
<img src={replyIcon} alt="icon reply" />
|
||||
</Tooltip>
|
||||
</li>
|
||||
)}
|
||||
<li className="cmd fav" onClick={handleAddFav}>
|
||||
<Tooltip placement="top" tip="Add to Favorites">
|
||||
<IconBookmark />
|
||||
</Tooltip>
|
||||
</li>
|
||||
</Tippy>
|
||||
{enableEdit && (
|
||||
<li className="cmd" onClick={toggleEditMessage}>
|
||||
<Tooltip placement="top" tip="Edit">
|
||||
<img src={editIcon} alt="icon edit" />
|
||||
</Tooltip>
|
||||
</li>
|
||||
)}
|
||||
{enableReply && (
|
||||
<li className="cmd" onClick={handleReply}>
|
||||
<Tooltip placement="top" tip="Reply">
|
||||
<img src={replyIcon} alt="icon reply" />
|
||||
</Tooltip>
|
||||
</li>
|
||||
)}
|
||||
<li className="cmd fav" onClick={handleAddFav}>
|
||||
<Tooltip placement="top" tip="Add to Favorites">
|
||||
<IconBookmark />
|
||||
</Tooltip>
|
||||
</li>
|
||||
<Tippy
|
||||
onShow={handleTippyVisible.bind(null, true)}
|
||||
onHide={handleTippyVisible.bind(null, false)}
|
||||
interactive
|
||||
popperOptions={{ strategy: "fixed" }}
|
||||
placement="left-start"
|
||||
trigger="click"
|
||||
content={
|
||||
<StyledMenu className="menu">
|
||||
{/* <li className="item">Edit Message</li> */}
|
||||
{enablePin && (
|
||||
<li
|
||||
className="item"
|
||||
onClick={pinned ? handleUnpin : togglePinModal}
|
||||
>
|
||||
{pinned ? `Unpin Message` : `Pin Message`}
|
||||
<Tippy
|
||||
onShow={handleTippyVisible.bind(null, true)}
|
||||
onHide={handleTippyVisible.bind(null, false)}
|
||||
interactive
|
||||
popperOptions={{ strategy: "fixed" }}
|
||||
placement="left-start"
|
||||
trigger="click"
|
||||
content={
|
||||
<StyledMenu className="menu">
|
||||
{canPin && (
|
||||
<li
|
||||
className="item"
|
||||
onClick={pinned ? handleUnpin : togglePinModal}
|
||||
>
|
||||
{pinned ? `Unpin Message` : `Pin Message`}
|
||||
</li>
|
||||
)}
|
||||
<li className="item" onClick={toggleForwardModal}>
|
||||
Forward
|
||||
</li>
|
||||
)}
|
||||
<li className="item" onClick={toggleForwardModal}>
|
||||
Forward
|
||||
</li>
|
||||
<li className="item" onClick={handleReply.bind(null, true)}>
|
||||
Reply
|
||||
</li>
|
||||
<li className="item" onClick={handleSelect.bind(null, mid)}>
|
||||
Select
|
||||
</li>
|
||||
{currUid == from_uid && (
|
||||
<li className="item danger" onClick={toggleDeleteModal}>
|
||||
Delete Message
|
||||
<li className="item" onClick={handleReply.bind(null, true)}>
|
||||
Reply
|
||||
</li>
|
||||
)}
|
||||
</StyledMenu>
|
||||
}
|
||||
>
|
||||
<li className="cmd">
|
||||
<Tooltip placement="top" tip="More">
|
||||
<img src={moreIcon} alt="icon more" />
|
||||
</Tooltip>
|
||||
</li>
|
||||
</Tippy>
|
||||
|
||||
{deleteModalVisible && (
|
||||
<DeleteMessageConfirm closeModal={toggleDeleteModal} mids={mid} />
|
||||
)}
|
||||
{forwardModalVisible && (
|
||||
<ForwardModal mids={mids} closeModal={toggleForwardModal} />
|
||||
)}
|
||||
{pinModalVisible && (
|
||||
<PinMessageModal
|
||||
mid={mid}
|
||||
gid={contextId}
|
||||
closeModal={togglePinModal}
|
||||
/>
|
||||
)}
|
||||
</StyledCmds>
|
||||
<li className="item" onClick={handleSelect.bind(null, mid)}>
|
||||
Select
|
||||
</li>
|
||||
{canDelete && (
|
||||
<li className="item danger" onClick={toggleDeleteModal}>
|
||||
Delete Message
|
||||
</li>
|
||||
)}
|
||||
</StyledMenu>
|
||||
}
|
||||
>
|
||||
<li className="cmd">
|
||||
<Tooltip placement="top" tip="More">
|
||||
<img src={moreIcon} alt="icon more" />
|
||||
</Tooltip>
|
||||
</li>
|
||||
</Tippy>
|
||||
</StyledCmds>
|
||||
{PinModal}
|
||||
{ForwardModal}
|
||||
{DeleteModal}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// import { useState, useEffect } from "react";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
import IconDelete from "../../../assets/icons/delete.svg";
|
||||
import IconEdit from "../../../assets/icons/edit.svg";
|
||||
import IconReply from "../../../assets/icons/reply.svg";
|
||||
import IconForward from "../../../assets/icons/forward.svg";
|
||||
import IconPin from "../../../assets/icons/pin.svg";
|
||||
import IconCopy from "../../../assets/icons/copy.svg";
|
||||
import IconSelect from "../../../assets/icons/select.svg";
|
||||
import { updateSelectMessages } from "../../../app/slices/ui";
|
||||
import { addReplyingMessage } from "../../../app/slices/message";
|
||||
|
||||
import useMessageOperation from "./useMessageOperation";
|
||||
|
||||
export default function MessageContextMenu({
|
||||
context,
|
||||
contextId,
|
||||
mid,
|
||||
offset = {},
|
||||
visible,
|
||||
hide,
|
||||
editMessage,
|
||||
children,
|
||||
}) {
|
||||
const {
|
||||
copyContent,
|
||||
isMarkdown,
|
||||
canEdit,
|
||||
canPin,
|
||||
canCopy,
|
||||
canReply,
|
||||
pinned,
|
||||
unPin,
|
||||
toggleDeleteModal,
|
||||
toggleForwardModal,
|
||||
togglePinModal,
|
||||
PinModal,
|
||||
ForwardModal,
|
||||
DeleteModal,
|
||||
} = useMessageOperation({ mid, contextId, context });
|
||||
const dispatch = useDispatch();
|
||||
const handleSelect = () => {
|
||||
dispatch(updateSelectMessages({ context, id: contextId, data: mid }));
|
||||
// hideAll();
|
||||
};
|
||||
const handleReply = () => {
|
||||
// console.log("dddd", contextId, mid);
|
||||
if (contextId) {
|
||||
dispatch(addReplyingMessage({ id: contextId, mid }));
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{ForwardModal}
|
||||
{PinModal}
|
||||
{DeleteModal}
|
||||
<Tippy
|
||||
duration={0}
|
||||
delay={[0, 0]}
|
||||
content={
|
||||
<ContextMenu
|
||||
hideMenu={hide}
|
||||
items={[
|
||||
canEdit && {
|
||||
title: "Edit Message",
|
||||
icon: <IconEdit className="icon" />,
|
||||
handler: editMessage,
|
||||
},
|
||||
canReply && {
|
||||
title: "Reply",
|
||||
icon: <IconReply className="icon" />,
|
||||
handler: handleReply,
|
||||
},
|
||||
canCopy && {
|
||||
title: isMarkdown ? "Copy Markdown" : "Copy Text",
|
||||
icon: <IconCopy className="icon" />,
|
||||
handler: copyContent,
|
||||
},
|
||||
canPin && {
|
||||
title: pinned ? "Unpin" : "Pin",
|
||||
icon: <IconPin className="icon" />,
|
||||
handler: pinned ? unPin.bind(null, mid) : togglePinModal,
|
||||
},
|
||||
{
|
||||
title: "Forward",
|
||||
icon: <IconForward className="icon" />,
|
||||
handler: toggleForwardModal,
|
||||
},
|
||||
{
|
||||
title: "Select",
|
||||
icon: <IconSelect className="icon" />,
|
||||
handler: handleSelect,
|
||||
},
|
||||
{
|
||||
title: "Delete",
|
||||
danger: true,
|
||||
icon: <IconDelete className="icon" />,
|
||||
handler: toggleDeleteModal,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}
|
||||
interactive
|
||||
placement="right-start"
|
||||
popperOptions={{ strategy: "fixed" }}
|
||||
offset={[offset.y, offset.x]}
|
||||
visible={visible}
|
||||
onClickOutside={hide}
|
||||
key={mid}
|
||||
>
|
||||
{children}
|
||||
</Tippy>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -14,6 +14,9 @@ import Commands from "./Commands";
|
||||
import EditMessage from "./EditMessage";
|
||||
import renderContent from "./renderContent";
|
||||
import Tooltip from "../Tooltip";
|
||||
import ContextMenu from "./ContextMenu";
|
||||
|
||||
import useContextMenu from "../../hook/useContextMenu";
|
||||
dayjs.extend(isToday);
|
||||
dayjs.extend(isYesterday);
|
||||
function Message({
|
||||
@@ -24,6 +27,12 @@ function Message({
|
||||
updateReadIndex,
|
||||
read = true,
|
||||
}) {
|
||||
const {
|
||||
visible: contextMenuVisible,
|
||||
offset,
|
||||
handleContextMenuEvent,
|
||||
hideContextMenu,
|
||||
} = useContextMenu();
|
||||
const inviewRef = useInView();
|
||||
const [edit, setEdit] = useState(false);
|
||||
const avatarRef = useRef(null);
|
||||
@@ -78,71 +87,82 @@ function Message({
|
||||
console.log("render message");
|
||||
// return null;
|
||||
return (
|
||||
<StyledWrapper
|
||||
data-msg-mid={mid}
|
||||
ref={inviewRef}
|
||||
className={`message ${readOnly ? "readonly" : ""}`}
|
||||
<ContextMenu
|
||||
editMessage={toggleEditMessage}
|
||||
context={context}
|
||||
contextId={contextId}
|
||||
mid={mid}
|
||||
visible={contextMenuVisible}
|
||||
hide={hideContextMenu}
|
||||
offset={offset}
|
||||
>
|
||||
<Tippy
|
||||
disabled={readOnly}
|
||||
duration={0}
|
||||
interactive
|
||||
placement="left"
|
||||
trigger="click"
|
||||
content={<Profile uid={fromUid} type="card" />}
|
||||
<StyledWrapper
|
||||
onContextMenu={handleContextMenuEvent}
|
||||
data-msg-mid={mid}
|
||||
ref={inviewRef}
|
||||
className={`message ${readOnly ? "readonly" : ""}`}
|
||||
>
|
||||
<div className="avatar" data-uid={fromUid} ref={avatarRef}>
|
||||
<Avatar url={currUser.avatar} name={currUser.name} />
|
||||
<Tippy
|
||||
disabled={readOnly}
|
||||
duration={0}
|
||||
interactive
|
||||
placement="left"
|
||||
trigger="click"
|
||||
content={<Profile uid={fromUid} type="card" />}
|
||||
>
|
||||
<div className="avatar" data-uid={fromUid} ref={avatarRef}>
|
||||
<Avatar url={currUser.avatar} name={currUser.name} />
|
||||
</div>
|
||||
</Tippy>
|
||||
<div className="details">
|
||||
<div className="up">
|
||||
<span className="name">{currUser.name}</span>
|
||||
<Tooltip
|
||||
delay={200}
|
||||
disabled={!timePrefix || readOnly}
|
||||
placement="top"
|
||||
tip={dayjsTime.format("YYYY-MM-DD h:mm:ss A")}
|
||||
>
|
||||
<i className="time">
|
||||
{timePrefix
|
||||
? `${timePrefix} ${dayjsTime.format("h:mm A")}`
|
||||
: dayjsTime.format("YYYY-MM-DD h:mm:ss A")}
|
||||
</i>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className={`down ${sending ? "sending" : ""}`}>
|
||||
{reply_mid && <Reply mid={reply_mid} />}
|
||||
{edit ? (
|
||||
<EditMessage mid={mid} cancelEdit={toggleEditMessage} />
|
||||
) : (
|
||||
renderContent({
|
||||
context,
|
||||
to: contextId,
|
||||
from_uid: fromUid,
|
||||
created_at: time,
|
||||
content_type,
|
||||
properties,
|
||||
content,
|
||||
thumbnail,
|
||||
download,
|
||||
edited,
|
||||
})
|
||||
)}
|
||||
{reactions && <Reaction mid={mid} reactions={reactions} />}
|
||||
</div>
|
||||
</div>
|
||||
</Tippy>
|
||||
<div className="details">
|
||||
<div className="up">
|
||||
<span className="name">{currUser.name}</span>
|
||||
<Tooltip
|
||||
delay={200}
|
||||
disabled={!timePrefix || readOnly}
|
||||
placement="top"
|
||||
tip={dayjsTime.format("YYYY-MM-DD h:mm:ss A")}
|
||||
>
|
||||
<i className="time">
|
||||
{timePrefix
|
||||
? `${timePrefix} ${dayjsTime.format("h:mm A")}`
|
||||
: dayjsTime.format("YYYY-MM-DD h:mm:ss A")}
|
||||
</i>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className={`down ${sending ? "sending" : ""}`}>
|
||||
{reply_mid && <Reply mid={reply_mid} />}
|
||||
{edit ? (
|
||||
<EditMessage mid={mid} cancelEdit={toggleEditMessage} />
|
||||
) : (
|
||||
renderContent({
|
||||
context,
|
||||
to: contextId,
|
||||
from_uid: fromUid,
|
||||
created_at: time,
|
||||
content_type,
|
||||
properties,
|
||||
content,
|
||||
thumbnail,
|
||||
download,
|
||||
edited,
|
||||
})
|
||||
)}
|
||||
{reactions && <Reaction mid={mid} reactions={reactions} />}
|
||||
</div>
|
||||
</div>
|
||||
{!edit && !readOnly && (
|
||||
<Commands
|
||||
content_type={content_type}
|
||||
context={context}
|
||||
contextId={contextId}
|
||||
mid={mid}
|
||||
from_uid={fromUid}
|
||||
toggleEditMessage={toggleEditMessage}
|
||||
/>
|
||||
)}
|
||||
</StyledWrapper>
|
||||
{!edit && !readOnly && (
|
||||
<Commands
|
||||
content_type={content_type}
|
||||
context={context}
|
||||
contextId={contextId}
|
||||
mid={mid}
|
||||
from_uid={fromUid}
|
||||
toggleEditMessage={toggleEditMessage}
|
||||
/>
|
||||
)}
|
||||
</StyledWrapper>
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
export default React.memo(Message, (prevs, nexts) => {
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import DeleteMessageConfirm from "../DeleteMessageConfirm";
|
||||
import ForwardModal from "../ForwardModal";
|
||||
import PinMessageModal from "./PinMessageModal";
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
import { useSelector } from "react-redux";
|
||||
import useCopy from "../../hook/useCopy";
|
||||
import usePinMessage from "../../hook/usePinMessage";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function useMessageOperation({ mid, context, contextId }) {
|
||||
const [copied, copy] = useCopy();
|
||||
const { content_type, currUid, from_uid, content } = useSelector((store) => {
|
||||
return {
|
||||
content: store.message[mid]?.content,
|
||||
from_uid: store.message[mid]?.from_uid,
|
||||
content_type: store.message[mid]?.content_type,
|
||||
currUid: store.authData.uid,
|
||||
};
|
||||
});
|
||||
const { canPin, pins, unpinMessage, isUnpinSuccess } = usePinMessage(
|
||||
context == "channel" ? contextId : undefined
|
||||
);
|
||||
const [mids, setMids] = useState([]);
|
||||
const [pinModalVisible, setPinModalVisible] = useState(false);
|
||||
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
|
||||
const [forwardModalVisible, setForwardModalVisible] = useState(false);
|
||||
|
||||
const toggleForwardModal = () => {
|
||||
// hideAll();
|
||||
setForwardModalVisible((prev) => !prev);
|
||||
};
|
||||
const toggleDeleteModal = () => {
|
||||
// hideAll();
|
||||
setDeleteModalVisible((prev) => !prev);
|
||||
};
|
||||
const togglePinModal = () => {
|
||||
// hideAll();
|
||||
setPinModalVisible((prev) => !prev);
|
||||
};
|
||||
const copyContent = () => {
|
||||
copy(content);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (content_type == ContentTypes.archive) {
|
||||
// forward message
|
||||
const forwardEle = document.querySelector(
|
||||
`[data-msg-mid='${mid}'] .down [data-forwarded-mids]`
|
||||
);
|
||||
if (forwardEle) {
|
||||
const mids = forwardEle.dataset.forwardedMids.split(",");
|
||||
setMids(mids);
|
||||
}
|
||||
} else {
|
||||
setMids([mid]);
|
||||
}
|
||||
}, [mid, content_type]);
|
||||
useEffect(() => {
|
||||
if (isUnpinSuccess) {
|
||||
toast.success("Unpin Message Successfully!");
|
||||
}
|
||||
}, [isUnpinSuccess]);
|
||||
useEffect(() => {
|
||||
if (copied) {
|
||||
toast.success("Copied");
|
||||
}
|
||||
}, [copied]);
|
||||
const enablePin = context == "channel" && canPin;
|
||||
const enableReply = currUid != from_uid;
|
||||
|
||||
const enableEdit =
|
||||
currUid == from_uid &&
|
||||
[ContentTypes.text, ContentTypes.markdown].includes(content_type);
|
||||
const canDelete = currUid == from_uid;
|
||||
const canCopy = [ContentTypes.text, ContentTypes.markdown].includes(
|
||||
content_type
|
||||
);
|
||||
return {
|
||||
copyContent,
|
||||
canCopy,
|
||||
isMarkdown: content_type == ContentTypes.markdown,
|
||||
canDelete,
|
||||
canPin: context == "channel" && canPin,
|
||||
pinned: enablePin ? pins.findIndex((p) => p.mid == mid) > -1 : false,
|
||||
unPin: unpinMessage,
|
||||
canReply: enableReply,
|
||||
canEdit: enableEdit,
|
||||
toggleDeleteModal,
|
||||
toggleForwardModal,
|
||||
togglePinModal,
|
||||
DeleteModal: deleteModalVisible ? (
|
||||
<DeleteMessageConfirm closeModal={toggleDeleteModal} mids={mid} />
|
||||
) : null,
|
||||
ForwardModal: forwardModalVisible ? (
|
||||
<ForwardModal mids={mids} closeModal={toggleForwardModal} />
|
||||
) : null,
|
||||
PinModal: pinModalVisible ? (
|
||||
<PinMessageModal mid={mid} gid={contextId} closeModal={togglePinModal} />
|
||||
) : null,
|
||||
};
|
||||
}
|
||||
@@ -8,20 +8,35 @@ const StyledMenu = styled.ul`
|
||||
box-shadow: 0px 20px 25px 20px rgba(31, 41, 55, 0.1),
|
||||
0px 10px 10px rgba(31, 41, 55, 0.04);
|
||||
border-radius: 12px;
|
||||
min-width: 200px;
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
padding: 6px 8px;
|
||||
padding: 6px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #616161;
|
||||
/* transition: color 0.2s ease; */
|
||||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
path {
|
||||
fill: #475467;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background-color: #22ccee;
|
||||
color: #fff;
|
||||
.icon {
|
||||
path {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.underline {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
|
||||
Reference in New Issue
Block a user