feat: select multiple messages and forward

This commit is contained in:
zerosoul
2022-04-26 11:29:03 +08:00
parent f5ce856061
commit fbedaa8825
24 changed files with 702 additions and 329 deletions
+28 -2
View File
@@ -4,6 +4,7 @@ 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";
@@ -15,6 +16,7 @@ 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";
import PinMessageModal from "./PinMessageModal";
const StyledCmds = styled.ul`
z-index: 9999;
position: absolute;
@@ -50,12 +52,14 @@ const StyledCmds = styled.ul`
}
`;
export default function Commands({
context = "user",
contextId = 0,
mid = 0,
from_uid = 0,
toggleEditMessage,
}) {
const dispatch = useDispatch();
const [pinModalVisible, setPinModalVisible] = useState(false);
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
const [forwardModalVisible, setForwardModalVisible] = useState(false);
const [tippyVisible, setTippyVisible] = useState(false);
@@ -77,9 +81,17 @@ export default function Commands({
hideAll();
setDeleteModalVisible((prev) => !prev);
};
const togglePinModal = () => {
hideAll();
setPinModalVisible((prev) => !prev);
};
const handleTippyVisible = (visible = true) => {
setTippyVisible(visible);
};
const handleSelect = (mid) => {
dispatch(updateSelectMessages({ context, id: contextId, data: mid }));
hideAll();
};
return (
<StyledCmds
ref={cmdsRef}
@@ -127,13 +139,20 @@ export default function Commands({
content={
<StyledMenu className="menu">
{/* <li className="item">Edit Message</li> */}
{/* <li className="item underline">Pin Message</li> */}
{context == "channel" && (
<li className="item underline" onClick={togglePinModal}>
Pin Message
</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
@@ -153,7 +172,14 @@ export default function Commands({
<DeleteMessageConfirm closeModal={toggleDeleteModal} mid={mid} />
)}
{forwardModalVisible && (
<ForwardModal mid={mid} closeModal={toggleForwardModal} />
<ForwardModal mids={[mid]} closeModal={toggleForwardModal} />
)}
{pinModalVisible && (
<PinMessageModal
mid={mid}
gid={contextId}
closeModal={togglePinModal}
/>
)}
</StyledCmds>
);
@@ -0,0 +1,60 @@
// import React from "react";
import { useEffect } from "react";
import styled from "styled-components";
const StyledPinModal = styled(StyledModal)`
min-width: 406px;
.title,
.desc {
text-align: left;
}
.preview {
border: 1px solid #f2f4f7;
max-height: 256px;
overflow: auto;
background: none;
overflow-x: hidden;
}
`;
// import { useDispatch } from "react-redux";
// import BASE_URL from "../../app/config";
import { usePinMessageMutation } from "../../../app/services/message";
import StyledModal from "../styled/Modal";
import Button from "../styled/Button";
import Modal from "../Modal";
import PreviewMessage from "./PreviewMessage";
export default function PinMessageModal({ closeModal, mid = 0, gid = 0 }) {
// const dispatch = useDispatch();
const [pinMessage, { isLoading, isSuccess }] = usePinMessageMutation();
const handlePin = () => {
pinMessage({ mid, gid });
};
useEffect(() => {
if (isSuccess) {
closeModal();
}
}, [isSuccess]);
if (!mid) return null;
return (
<Modal>
<StyledPinModal
// className="animate__animated animate__fadeInDown animate__faster"
buttons={
<>
<Button onClick={closeModal} className="cancel">
Cancel
</Button>
<Button disabled={isLoading} onClick={handlePin} className="main">
{isLoading ? "Pining" : `Pin It`}
</Button>
</>
}
title="Pin It"
description="You sure you want to pin this message to #gerenal?"
>
<PreviewMessage mid={mid} />
</StyledPinModal>
</Modal>
);
}
+1
View File
@@ -38,6 +38,7 @@ const Styled = styled.div`
}
}
.content {
overflow: hidden;
font-weight: 500;
font-size: 14px;
line-height: 20px;
+10 -3
View File
@@ -17,6 +17,7 @@ import Tooltip from "../Tooltip";
dayjs.extend(isToday);
dayjs.extend(isYesterday);
function Message({
readOnly = false,
contextId = 0,
mid = "",
context = "user",
@@ -77,8 +78,13 @@ function Message({
console.log("render message");
// return null;
return (
<StyledWrapper data-msg-mid={mid} ref={inviewRef} className={`message`}>
<StyledWrapper
data-msg-mid={mid}
ref={inviewRef}
className={`message ${readOnly ? "readonly" : ""}`}
>
<Tippy
disabled={readOnly}
duration={0}
interactive
placement="left"
@@ -94,7 +100,7 @@ function Message({
<span className="name">{currUser.name}</span>
<Tooltip
delay={200}
disabled={!timePrefix}
disabled={!timePrefix || readOnly}
placement="top"
tip={dayjsTime.format("YYYY-MM-DD h:mm:ss A")}
>
@@ -130,8 +136,9 @@ function Message({
{reactions && <Reaction mid={mid} reactions={reactions} />}
</div>
</div>
{!edit && (
{!edit && !readOnly && (
<Commands
context={context}
contextId={contextId}
mid={mid}
from_uid={fromUid}
+75 -19
View File
@@ -1,12 +1,16 @@
import React, { useState, useEffect } from "react";
import Linkit from "react-linkify";
import styled from "styled-components";
import dayjs from "dayjs";
import StyledMsg from "./styled";
import { ContentTypes } from "../../../app/config";
import Mention from "./Mention";
import useNormalizeMessage from "../../hook/useNormalizeMessage";
import MrakdownRender from "../MrakdownRender";
import FileMessage from "../FileMessage";
import URLPreview from "./URLPreview";
import Avatar from "../Avatar";
import IconForward from "../../../assets/icons/forward.svg";
import reactStringReplace from "react-string-replace";
const renderContent = ({
context,
@@ -111,6 +115,29 @@ const renderContent = ({
}
return ctn;
};
const StyledForward = styled.div`
display: flex;
flex-direction: column;
border-radius: var(--br);
background-color: #f4f4f5;
padding: 8px;
> .tip {
display: flex;
align-items: center;
gap: 4px;
.icon {
width: 16px;
height: 16px;
path {
fill: #98a2b3;
}
}
font-weight: 400;
font-size: 12px;
line-height: 18px;
color: #98a2b3;
}
`;
const ForwardedMessage = ({ context, to, from_uid, id }) => {
const { normalizeMessage, messages } = useNormalizeMessage();
const [forwards, setForwards] = useState(null);
@@ -123,25 +150,54 @@ const ForwardedMessage = ({ context, to, from_uid, id }) => {
useEffect(() => {
if (messages) {
setForwards(
messages.map((msg) => {
const {
download,
content,
content_type,
properties,
thumbnail,
} = msg;
return renderContent({
download,
context,
to,
from_uid,
content,
content_type,
properties,
thumbnail,
});
})
<StyledForward>
<h4 className="tip">
<IconForward className="icon" />
Forwarded
</h4>
<div className="list">
{messages.map((msg, idx) => {
const {
user = {},
created_at,
download,
content,
content_type,
properties,
thumbnail,
} = msg;
return (
<StyledMsg key={idx}>
{user && (
<div className="avatar">
<Avatar url={user.avatar} name={user.name} />
</div>
)}
<div className="details">
<div className="up">
<span className="name">{user?.name}</span>
<i className="time">
{dayjs(created_at).format("YYYY-MM-DD h:mm:ss A")}
</i>
</div>
<div className="down">
{renderContent({
download,
context,
to,
from_uid,
content,
content_type,
properties,
thumbnail,
})}
</div>
</div>
</StyledMsg>
);
})}
</div>
</StyledForward>
);
}
}, [messages, context, to, from_uid]);
+5
View File
@@ -17,11 +17,16 @@ const StyledMsg = styled.div`
}
&:hover,
&.preview {
content-visibility: inherit;
contain-intrinsic-size: inherit;
background: #f5f6f7;
.cmds {
visibility: visible;
}
}
&.readonly:hover {
background: none;
}
.avatar {
flex-shrink: 0;
cursor: pointer;