feat: forward message UX
This commit is contained in:
@@ -12,7 +12,9 @@ import EmojiPicker from "./EmojiPicker";
|
||||
import replyIcon from "../../../assets/icons/reply.svg?url";
|
||||
import reactIcon from "../../../assets/icons/reaction.svg?url";
|
||||
import editIcon from "../../../assets/icons/edit.svg?url";
|
||||
import bookmarkIcon from "../../../assets/icons/bookmark.svg?url";
|
||||
import moreIcon from "../../../assets/icons/more.svg?url";
|
||||
import ForwardModal from "../ForwardModal";
|
||||
const StyledCmds = styled.ul`
|
||||
z-index: 9999;
|
||||
position: absolute;
|
||||
@@ -60,6 +62,7 @@ export default function Commands({
|
||||
}) {
|
||||
const dispatch = useDispatch();
|
||||
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
|
||||
const [forwardModalVisible, setForwardModalVisible] = useState(false);
|
||||
const [tippyVisible, setTippyVisible] = useState(false);
|
||||
const currUid = useSelector((store) => store.authData.uid);
|
||||
const cmdsRef = useRef(null);
|
||||
@@ -71,7 +74,10 @@ export default function Commands({
|
||||
hideAll();
|
||||
}
|
||||
};
|
||||
|
||||
const toggleForwardModal = () => {
|
||||
hideAll();
|
||||
setForwardModalVisible((prev) => !prev);
|
||||
};
|
||||
const toggleDeleteModal = () => {
|
||||
hideAll();
|
||||
setDeleteModalVisible((prev) => !prev);
|
||||
@@ -111,6 +117,11 @@ export default function Commands({
|
||||
</Tooltip>
|
||||
</li>
|
||||
)}
|
||||
<li className="cmd">
|
||||
<Tooltip placement="top" tip="Add to Favorites">
|
||||
<img src={bookmarkIcon} className="toggler" alt="icon bookmark" />
|
||||
</Tooltip>
|
||||
</li>
|
||||
<Tippy
|
||||
onShow={handleTippyVisible.bind(null, true)}
|
||||
onHide={handleTippyVisible.bind(null, false)}
|
||||
@@ -121,6 +132,9 @@ export default function Commands({
|
||||
<StyledMenu className="menu">
|
||||
{/* <li className="item">Edit Message</li> */}
|
||||
<li className="item underline">Pin Message</li>
|
||||
<li className="item" onClick={toggleForwardModal}>
|
||||
Forward
|
||||
</li>
|
||||
<li className="item" onClick={handleReply.bind(null, true)}>
|
||||
Reply
|
||||
</li>
|
||||
@@ -142,6 +156,9 @@ export default function Commands({
|
||||
{deleteModalVisible && (
|
||||
<DeleteMessageConfirm closeModal={toggleDeleteModal} mid={mid} />
|
||||
)}
|
||||
{forwardModalVisible && (
|
||||
<ForwardModal mid={mid} closeModal={toggleForwardModal} />
|
||||
)}
|
||||
</StyledCmds>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,23 +4,26 @@ import Tippy from "@tippyjs/react";
|
||||
import Profile from "../Profile";
|
||||
import styled from "styled-components";
|
||||
const Styled = styled.span`
|
||||
cursor: pointer;
|
||||
padding: 0 2px;
|
||||
color: #1fe1f9;
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
export default function Mention({ uid }) {
|
||||
export default function Mention({ uid, popover = true }) {
|
||||
const contactsData = useSelector((store) => store.contacts.byId);
|
||||
const user = contactsData[uid];
|
||||
if (!user) return null;
|
||||
return (
|
||||
<Tippy
|
||||
disabled={!popover}
|
||||
duration={0}
|
||||
interactive
|
||||
placement="top"
|
||||
trigger="click"
|
||||
content={<Profile uid={uid} type="card" />}
|
||||
>
|
||||
<Styled>{`@${user.name}`}</Styled>
|
||||
<Styled className={popover ? "clickable" : ""}>{`@${user.name}`}</Styled>
|
||||
</Tippy>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
import reactStringReplace from "react-string-replace";
|
||||
import MrakdownRender from "../MrakdownRender";
|
||||
|
||||
import Mention from "./Mention";
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
import { getFileIcon, isImage } from "../../utils";
|
||||
|
||||
import Avatar from "../Avatar";
|
||||
const Styled = styled.div`
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 8px;
|
||||
@@ -15,6 +16,9 @@ const Styled = styled.div`
|
||||
border-radius: var(--br);
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -89,7 +93,19 @@ const renderContent = (data) => {
|
||||
let res = null;
|
||||
switch (content_type) {
|
||||
case ContentTypes.text:
|
||||
res = <span className="txt"> {content}</span>;
|
||||
res = (
|
||||
<span className="txt">
|
||||
{reactStringReplace(
|
||||
content,
|
||||
/(\s{1}\@[0-9]+\s{1})/g,
|
||||
(match, idx) => {
|
||||
console.log("match", match);
|
||||
const uid = match.trim().slice(1);
|
||||
return <Mention key={idx} uid={uid} popover={false} />;
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
break;
|
||||
case ContentTypes.markdown:
|
||||
res = (
|
||||
@@ -120,7 +136,7 @@ const renderContent = (data) => {
|
||||
}
|
||||
return res;
|
||||
};
|
||||
export default function Reply({ mid }) {
|
||||
export default function Reply({ mid, interactive = true }) {
|
||||
const { data, users } = useSelector((store) => {
|
||||
return { data: store.message[mid], users: store.contacts.byId };
|
||||
});
|
||||
@@ -140,7 +156,11 @@ export default function Reply({ mid }) {
|
||||
const currUser = users[data.from_uid];
|
||||
if (!currUser) return null;
|
||||
return (
|
||||
<Styled data-mid={mid} className="reply" onClick={handleClick}>
|
||||
<Styled
|
||||
data-mid={mid}
|
||||
className={`reply ${interactive ? "clickable" : ""}`}
|
||||
onClick={interactive ? handleClick : null}
|
||||
>
|
||||
<div className="user">
|
||||
<Avatar className="avatar" url={currUser.avatar} name={currUser.name} />
|
||||
<span className="name">{currUser.name}</span>
|
||||
|
||||
Reference in New Issue
Block a user