refactor: send and message display
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.11904 0C3.91105 0 0.5 3.41105 0.5 7.61905C0.5 11.827 3.91105 15.2381 8.11904 15.2381C12.3271 15.2381 15.7381 11.827 15.7381 7.61905C15.7381 3.41105 12.3263 0 8.11904 0ZM11.7053 10.128L10.628 11.2053L8.11904 8.69714L5.61009 11.2053L4.53277 10.128L7.04095 7.61905L4.532 5.1101L5.60934 4.03353L8.11829 6.54248L10.6272 4.03353L11.7045 5.1101L9.19638 7.61905L11.7053 10.128Z" fill="#6C737F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 503 B |
@@ -0,0 +1,12 @@
|
||||
<svg width="15" height="20" viewBox="0 0 15 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_7682_4291)">
|
||||
<path d="M15 6.10417V18.6667C15 19.1333 15 19.3667 14.9083 19.5458C14.8292 19.7 14.7 19.8292 14.5458 19.9083C14.3667 20 14.1333 20 13.6667 20H1.33333C0.866666 20 0.633332 20 0.454166 19.9083C0.299999 19.8292 0.170833 19.7 0.0916666 19.5458C-1.17968e-07 19.3667 -8.10971e-08 19.1333 1.52874e-10 18.6667L3.0314e-06 1.33333C3.11265e-06 0.866666 3.15408e-06 0.633333 0.0916699 0.454166C0.170837 0.3 0.300003 0.170833 0.45417 0.0916664C0.633337 -1.24176e-07 0.86667 -8.10971e-08 1.33334 1.5288e-10L8.89584 1.32307e-06C9.30417 1.3939e-06 9.50834 1.43113e-06 9.7 0.0458348C9.80417 0.0708348 9.90417 0.104168 10 0.150001V3.66667C10 4.13333 10 4.36667 10.0917 4.54583C10.1708 4.7 10.3 4.82917 10.4542 4.90833C10.6333 5 10.8667 5 11.3333 5H14.85C14.8958 5.09583 14.9292 5.19583 14.9542 5.3C15 5.49167 15 5.69583 15 6.10417Z" fill="#A5F3FC"/>
|
||||
<path d="M14.2208 4.22074C14.5083 4.50824 14.65 4.64991 14.7542 4.82074C14.7917 4.87908 14.8208 4.93741 14.85 4.99991H11.3333C10.8667 4.99991 10.6333 4.99991 10.4542 4.90824C10.3 4.82907 10.1708 4.69991 10.0917 4.54574C10 4.36657 10 4.13324 10 3.66658V0.149902C10.0625 0.179069 10.1208 0.208236 10.1792 0.245735C10.35 0.349902 10.4917 0.491569 10.7792 0.779069L14.2208 4.22074Z" fill="#06B6D4"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.6666 9.99992C6.6666 10.9204 5.92041 11.6666 4.99993 11.6666C4.07945 11.6666 3.33326 10.9204 3.33326 9.99992C3.33326 9.07944 4.07945 8.33325 4.99993 8.33325C5.92041 8.33325 6.6666 9.07944 6.6666 9.99992ZM7.00992 14.9011L9.16658 11.6661L11.6666 16.6661H3.33325L5.83325 13.3328L7.00992 14.9011Z" fill="#0891B2"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_7682_4291">
|
||||
<rect width="15" height="20" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -66,10 +66,13 @@ export default function Commands({
|
||||
const currUid = useSelector((store) => store.authData.uid);
|
||||
const menuRef = useRef(null);
|
||||
|
||||
const handleReply = () => {
|
||||
const handleReply = (fromMenu = false) => {
|
||||
if (contextId) {
|
||||
dispatch(addReplyingMessage({ id: contextId, mid }));
|
||||
}
|
||||
if (fromMenu) {
|
||||
toggleMenu();
|
||||
}
|
||||
// toast.success("cooming soon");
|
||||
};
|
||||
|
||||
@@ -93,7 +96,7 @@ export default function Commands({
|
||||
<img src={editIcon} alt="icon edit" />
|
||||
</li>
|
||||
) : (
|
||||
<li className="cmd" onClick={handleReply}>
|
||||
<li className="cmd" onClick={handleReply.bind(null, false)}>
|
||||
<img src={replyIcon} alt="icon reply" />
|
||||
</li>
|
||||
)}
|
||||
@@ -104,7 +107,9 @@ export default function Commands({
|
||||
<StyledMenu className="menu" ref={menuRef}>
|
||||
{/* <li className="item">Edit Message</li> */}
|
||||
<li className="item underline">Pin Message</li>
|
||||
<li className="item">Reply</li>
|
||||
<li className="item" onClick={handleReply.bind(null, true)}>
|
||||
Reply
|
||||
</li>
|
||||
{currUid == from_uid && (
|
||||
<li className="item danger" onClick={toggleDeleteModal}>
|
||||
Delete Message
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function PreviewMessage({ mid = 0 }) {
|
||||
return { msg: store.message[mid], contactsData: store.contacts.byId };
|
||||
});
|
||||
if (!msg) return null;
|
||||
const { from_uid, created_at, content_type, content } = msg;
|
||||
const { from_uid, created_at, content_type, content, thumbnail } = msg;
|
||||
const { name, avatar } = contactsData[from_uid];
|
||||
return (
|
||||
<StyledWrapper className={`preview`}>
|
||||
@@ -23,7 +23,9 @@ export default function PreviewMessage({ mid = 0 }) {
|
||||
{dayjs(created_at).format("YYYY-MM-DD h:mm:ss A")}
|
||||
</i>
|
||||
</div>
|
||||
<div className={`down`}>{renderContent(content_type, content)}</div>
|
||||
<div className={`down`}>
|
||||
{renderContent({ content_type, content, thumbnail })}
|
||||
</div>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useReactMessageMutation } from "../../../app/services/message";
|
||||
// import { Emojis } from "../../../app/config";
|
||||
import addEmojiIcon from "../../../assets/icons/add.emoji.svg?url";
|
||||
const StyledWrapper = styled.span`
|
||||
z-index: 99;
|
||||
/* z-index: 99; */
|
||||
position: relative;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
@@ -1,15 +1,79 @@
|
||||
import React from "react";
|
||||
// import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
import Avatar from "../Avatar";
|
||||
const Styled = styled.div`
|
||||
color: #aaa;
|
||||
font-size: 12px;
|
||||
margin-bottom: -10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background: #e5e7eb;
|
||||
border-radius: var(--br);
|
||||
gap: 8px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom: 4px;
|
||||
.user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
.avatar {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.name {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #06b6d4;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #616161;
|
||||
.pic {
|
||||
display: inherit;
|
||||
max-width: 34px;
|
||||
}
|
||||
}
|
||||
/* padding-left: 10px; */
|
||||
`;
|
||||
const renderContent = (data) => {
|
||||
const { content_type, content, thumbnail } = data;
|
||||
let res = null;
|
||||
switch (content_type) {
|
||||
case ContentTypes.text:
|
||||
res = content;
|
||||
break;
|
||||
case ContentTypes.image:
|
||||
case ContentTypes.imageJPG:
|
||||
res = <img className="pic" src={thumbnail} />;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
};
|
||||
export default function Reply({ mid }) {
|
||||
const data = useSelector((store) => store.message[mid]);
|
||||
const { data, users } = useSelector((store) => {
|
||||
return { data: store.message[mid], users: store.contacts.byId };
|
||||
});
|
||||
if (!data) return null;
|
||||
return <Styled className="reply">{data.content}</Styled>;
|
||||
const currUser = users[data.from_uid];
|
||||
if (!currUser) return null;
|
||||
return (
|
||||
<Styled className="reply">
|
||||
<div className="user">
|
||||
<Avatar className="avatar" url={currUser.avatar} name={currUser.name} />
|
||||
<span className="name">{currUser.name}</span>
|
||||
</div>
|
||||
<div className="content">{renderContent(data)}</div>
|
||||
</Styled>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,8 +65,10 @@ function Message({ contextId = 0, mid = "", read = true, context = "user" }) {
|
||||
created_at: time,
|
||||
sending,
|
||||
content,
|
||||
thumbnail,
|
||||
content_type = "text/plain",
|
||||
edited,
|
||||
properties,
|
||||
} = message;
|
||||
const reactions = reactionMessageData[mid];
|
||||
const currUser = contactsData[fromUid] || {};
|
||||
@@ -89,12 +91,12 @@ function Message({ contextId = 0, mid = "", read = true, context = "user" }) {
|
||||
</div>
|
||||
</Tippy>
|
||||
<div className="details">
|
||||
{reply_mid && <Reply mid={reply_mid} />}
|
||||
<div className="up">
|
||||
<span className="name">{currUser.name}</span>
|
||||
<i className="time">{dayjs(time).format("YYYY-MM-DD h:mm:ss A")}</i>
|
||||
</div>
|
||||
<div className={`down ${sending ? "sending" : ""}`}>
|
||||
{reply_mid && <Reply mid={reply_mid} />}
|
||||
{edit ? (
|
||||
<EditMessage
|
||||
content={content}
|
||||
@@ -102,7 +104,13 @@ function Message({ contextId = 0, mid = "", read = true, context = "user" }) {
|
||||
cancelEdit={toggleEditMessage}
|
||||
/>
|
||||
) : (
|
||||
renderContent(content_type, content, edited)
|
||||
renderContent({
|
||||
content_type,
|
||||
properties,
|
||||
content,
|
||||
thumbnail,
|
||||
edited,
|
||||
})
|
||||
)}
|
||||
{reactions && <Reaction mid={mid} reactions={reactions} />}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import Linkify from "react-linkify";
|
||||
import dayjs from "dayjs";
|
||||
import BASE_URL from "../../../app/config";
|
||||
import MrakdownRender from "../MrakdownRender";
|
||||
const renderContent = (type, content, edited = false) => {
|
||||
import { getDefaultSize } from "../../utils";
|
||||
const renderContent = ({
|
||||
properties,
|
||||
content_type,
|
||||
content,
|
||||
thumbnail,
|
||||
edited = false,
|
||||
}) => {
|
||||
let ctn = null;
|
||||
switch (type) {
|
||||
switch (content_type) {
|
||||
case "text/plain":
|
||||
ctn = (
|
||||
<>
|
||||
@@ -40,16 +46,17 @@ const renderContent = (type, content, edited = false) => {
|
||||
break;
|
||||
case "image/png":
|
||||
case "image/jpeg":
|
||||
ctn = (
|
||||
<img
|
||||
className="img preview"
|
||||
src={
|
||||
content.startsWith("blob")
|
||||
? content
|
||||
: `${BASE_URL}/resource/image?id=${encodeURIComponent(content)}`
|
||||
}
|
||||
/>
|
||||
);
|
||||
{
|
||||
const { width, height } = getDefaultSize(properties);
|
||||
ctn = (
|
||||
<img
|
||||
className="img preview"
|
||||
style={{ width: `${width}px`, height: `${height}px` }}
|
||||
data-origin={content}
|
||||
src={thumbnail}
|
||||
/>
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,8 +1,73 @@
|
||||
// import React from 'react'
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { MdClose } from "react-icons/md";
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
import closeIcon from "../../../assets/icons/close.circle.svg?url";
|
||||
import pictureIcon from "../../../assets/icons/picture.svg?url";
|
||||
import { removeReplyingMessage } from "../../../app/slices/message";
|
||||
import styled from "styled-components";
|
||||
const Styled = styled.div`
|
||||
z-index: 999;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 16px;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
background-color: #f3f4f6;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform: translateY(-100%);
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
.prefix {
|
||||
color: #667085;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
em {
|
||||
font-weight: bold;
|
||||
color: #363f53;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
font-weight: 500;
|
||||
color: #616161;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 30px;
|
||||
}
|
||||
.close {
|
||||
background: none;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 16px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
`;
|
||||
const renderContent = (data) => {
|
||||
const { content_type, content } = data;
|
||||
let res = null;
|
||||
switch (content_type) {
|
||||
case ContentTypes.text:
|
||||
res = content;
|
||||
break;
|
||||
case ContentTypes.image:
|
||||
case ContentTypes.imageJPG:
|
||||
res = <img className="pic" src={pictureIcon} />;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
};
|
||||
export default function Replying({ id, mid }) {
|
||||
const { msg, contactsData } = useSelector((store) => {
|
||||
return { contactsData: store.contacts.byId, msg: store.message[mid] };
|
||||
@@ -15,14 +80,14 @@ export default function Replying({ id, mid }) {
|
||||
const { from_uid } = msg;
|
||||
const user = contactsData[from_uid];
|
||||
return (
|
||||
<div className="reply">
|
||||
<span className="txt">
|
||||
Replying to
|
||||
<em>{user?.name}</em>
|
||||
<Styled className="reply">
|
||||
<span className="prefix">
|
||||
Replying to <em>{user?.name}</em>
|
||||
</span>
|
||||
<span className="content">{renderContent(msg)}</span>
|
||||
<button className="close" onClick={removeReply}>
|
||||
<MdClose size={20} color="#78787C" />
|
||||
<img src={closeIcon} alt="close icon" />
|
||||
</button>
|
||||
</div>
|
||||
</Styled>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useState, useEffect } from "react";
|
||||
// import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
// import toast from "react-hot-toast";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
import { useSendChannelMsgMutation } from "../../../../app/services/channel";
|
||||
import { useSendMsgMutation } from "../../../../app/services/contact";
|
||||
import Modal from "../../Modal";
|
||||
import { formatBytes } from "../../../utils";
|
||||
import Button from "../../styled/Button";
|
||||
|
||||
import StyledWrapper from "./styled";
|
||||
@@ -22,27 +23,40 @@ export default function UploadModal({
|
||||
{ isLoading: channelSending },
|
||||
] = useSendChannelMsgMutation();
|
||||
const [sendUserMsg, { isLoading: userSending }] = useSendMsgMutation();
|
||||
const [blobs, setBlobs] = useState([]);
|
||||
useEffect(() => {
|
||||
files.forEach((file) => {
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onloadend = (e) => {
|
||||
const { name, size, type } = file;
|
||||
const obj = { name, size, type };
|
||||
let dataUrl = e.target.result;
|
||||
console.log({ dataUrl }, e.target);
|
||||
// let blob = new Blob([arrayBuffer]);
|
||||
obj.data = dataUrl;
|
||||
setBlobs((prevs) => {
|
||||
return [...prevs, obj];
|
||||
});
|
||||
};
|
||||
fileReader.readAsDataURL(file);
|
||||
});
|
||||
}, [files]);
|
||||
// const [properties, setProperties] = useState([]);
|
||||
// useEffect(() => {
|
||||
// files.forEach((file, idx) => {
|
||||
// const { name, size, type } = file;
|
||||
// setProperties((prevs) => {
|
||||
// prevs[idx] = { name, size, type };
|
||||
// return prevs;
|
||||
// });
|
||||
// var fileReader = new FileReader();
|
||||
// fileReader.onloadend = (e) => {
|
||||
// let dataUrl = e.target.result;
|
||||
// let tmp = new Image();
|
||||
// tmp.src = dataUrl;
|
||||
// tmp.onload = function () {
|
||||
// console.log("image load", this.width, this.height);
|
||||
// setProperties((prevs) => {
|
||||
// prevs[idx].width = this.width;
|
||||
// prevs[idx].height = this.height;
|
||||
// return prevs;
|
||||
// });
|
||||
// };
|
||||
// };
|
||||
// fileReader.readAsDataURL(file);
|
||||
// });
|
||||
// }, [files]);
|
||||
const handleUpload = () => {
|
||||
const uploadFn = type == "user" ? sendUserMsg : sendChannelMsg;
|
||||
uploadFn({ id: sendTo, content: files[0], type: "image", from_uid });
|
||||
uploadFn({
|
||||
id: sendTo,
|
||||
content: files[0],
|
||||
// properties: properties[0],
|
||||
type: "image",
|
||||
from_uid,
|
||||
});
|
||||
closeModal();
|
||||
};
|
||||
if (!sendTo) return null;
|
||||
@@ -68,22 +82,21 @@ export default function UploadModal({
|
||||
className="animate__animated animate__fadeInDown animate__faster"
|
||||
>
|
||||
<ul className="list">
|
||||
{blobs.map((b, idx) => {
|
||||
console.log({ b });
|
||||
{files.map((f, idx) => {
|
||||
console.log({ f });
|
||||
return (
|
||||
<li key={idx} className="item">
|
||||
<img
|
||||
src={b.data}
|
||||
// src={URL.createObjectURL(b.data)}
|
||||
src={URL.createObjectURL(f)}
|
||||
alt="thumb"
|
||||
className="thumb"
|
||||
/>
|
||||
<div className="right">
|
||||
<div className="name">
|
||||
<span className="input">{b.name}</span>
|
||||
<i className="tip">(click title to change name)</i>
|
||||
<span className="input">{f.name}</span>
|
||||
{/* <i className="tip">(click title to change name)</i> */}
|
||||
</div>
|
||||
<i className="size">{`${Math.floor(b.size / 1000)}KB`}</i>
|
||||
<i className="size">{formatBytes(f.size)}</i>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -113,7 +113,7 @@ export default function Send({
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledSend className={`send ${replying_mid ? "reply" : ""}`}>
|
||||
<StyledSend className={`send ${replying_mid ? "reply" : ""} ${type}`}>
|
||||
{replying_mid && <Replying mid={replying_mid} id={id} />}
|
||||
|
||||
<div className="input">
|
||||
|
||||
@@ -2,7 +2,7 @@ import styled from "styled-components";
|
||||
|
||||
const StyledSend = styled.div`
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
bottom: -70px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: #e5e7eb;
|
||||
@@ -15,6 +15,9 @@ const StyledSend = styled.div`
|
||||
gap: 5px;
|
||||
padding: 4px 18px;
|
||||
/* margin: 0 16px; */
|
||||
&.user {
|
||||
bottom: 10px;
|
||||
}
|
||||
&.reply {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
@@ -49,30 +52,6 @@ const StyledSend = styled.div`
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.reply {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
background-color: #f2f2f5;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform: translateY(-100%);
|
||||
width: 100%;
|
||||
padding: 6px 18px;
|
||||
.txt {
|
||||
color: #aaa;
|
||||
font-size: 12px;
|
||||
em {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledSend;
|
||||
|
||||
@@ -16,6 +16,24 @@ export const getNonNullValues = (obj, whiteList = ["log_id"]) => {
|
||||
});
|
||||
return tmp;
|
||||
};
|
||||
export function getDefaultSize(size = null, width = 240) {
|
||||
if (!size) return { width: 0, height: 0 };
|
||||
const { width: oWidth, height: oHeight } = size;
|
||||
const dWidth = oWidth >= width ? width : oWidth;
|
||||
const dHeight = (oHeight / oWidth) * dWidth;
|
||||
return { width: dWidth, height: dHeight };
|
||||
}
|
||||
export function formatBytes(bytes, decimals = 2) {
|
||||
if (bytes === 0) return "0 Bytes";
|
||||
|
||||
const k = 1000;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
||||
}
|
||||
export const getInitials = (name) => {
|
||||
const arr = name.split(" ").filter((n) => !!n);
|
||||
return arr
|
||||
|
||||
@@ -127,7 +127,6 @@ export default function ChannelChat({ cid = "", dropFiles = [] }) {
|
||||
</div>
|
||||
|
||||
<Send dragFiles={dragFiles} id={cid} type="channel" name={name} />
|
||||
<div className="placeholder"></div>
|
||||
</StyledChannelChat>
|
||||
{/* {unreads != 0 && (
|
||||
<StyledNotification>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
// import React from 'react'
|
||||
import { NavLink, useNavigate } from "react-router-dom";
|
||||
import { useDrop } from "react-dnd";
|
||||
import { NativeTypes } from "react-dnd-html5-backend";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import useContextMenu from "../../common/hook/useContextMenu";
|
||||
import ContextMenu from "../../common/component/ContextMenu";
|
||||
import { toggleChannelSetting } from "../../app/slices/ui";
|
||||
import ChannelIcon from "../../common/component/ChannelIcon";
|
||||
import { getUnreadCount } from "./utils";
|
||||
import { toggleChannelSetting } from "../../../app/slices/ui";
|
||||
import ChannelIcon from "../../../common/component/ChannelIcon";
|
||||
import { getUnreadCount } from "../utils";
|
||||
|
||||
const NavItem = ({ id, setFiles, contextMenuEventHandler }) => {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
@@ -44,7 +42,10 @@ const NavItem = ({ id, setFiles, contextMenuEventHandler }) => {
|
||||
const unreads = getUnreadCount(mids, messageData);
|
||||
return (
|
||||
<NavLink
|
||||
onContextMenu={contextMenuEventHandler}
|
||||
data-cid={id}
|
||||
onContextMenu={(evt) => {
|
||||
contextMenuEventHandler(evt, id);
|
||||
}}
|
||||
ref={drop}
|
||||
key={id}
|
||||
className={`link ${isActive ? "drop_over" : ""}`}
|
||||
@@ -65,58 +66,5 @@ const NavItem = ({ id, setFiles, contextMenuEventHandler }) => {
|
||||
</NavLink>
|
||||
);
|
||||
};
|
||||
export default function ChannelList({ setDropFiles }) {
|
||||
const channelIds = useSelector((store) => store.channels.ids);
|
||||
const {
|
||||
visible: contextMenuVisible,
|
||||
posX,
|
||||
posY,
|
||||
hideContextMenu,
|
||||
handleContextMenuEvent,
|
||||
} = useContextMenu();
|
||||
return (
|
||||
<>
|
||||
{channelIds.map((cid) => {
|
||||
return (
|
||||
<NavItem
|
||||
contextMenuEventHandler={handleContextMenuEvent}
|
||||
key={cid}
|
||||
id={cid}
|
||||
setFiles={setDropFiles}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{contextMenuVisible ? (
|
||||
<ContextMenu
|
||||
hideMenu={hideContextMenu}
|
||||
posX={posX}
|
||||
posY={posY}
|
||||
items={[
|
||||
{
|
||||
title: "Mark As Read",
|
||||
underline: true,
|
||||
},
|
||||
{
|
||||
title: "Mute",
|
||||
},
|
||||
{
|
||||
title: "Notification Settings",
|
||||
underline: true,
|
||||
},
|
||||
{
|
||||
title: "Edit Channel",
|
||||
underline: true,
|
||||
},
|
||||
{
|
||||
title: "Invite People",
|
||||
},
|
||||
{
|
||||
title: "Delete Channel",
|
||||
danger: true,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavItem;
|
||||
@@ -0,0 +1,77 @@
|
||||
import { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import useContextMenu from "../../../common/hook/useContextMenu";
|
||||
import ContextMenu from "../../../common/component/ContextMenu";
|
||||
import DeleteConfirmModal from "../../../common/component/ChannelSetting/DeleteConfirmModal";
|
||||
import NavItem from "./NavItem";
|
||||
|
||||
export default function ChannelList({ setDropFiles }) {
|
||||
const [removeConfirmVisible, setRemoveConfirmVisible] = useState(false);
|
||||
const [currId, setCurrId] = useState(null);
|
||||
const { channelIds, channelData } = useSelector((store) => {
|
||||
return { channelIds: store.channels.ids, channelData: store.channels.byId };
|
||||
});
|
||||
const {
|
||||
visible: contextMenuVisible,
|
||||
posX,
|
||||
posY,
|
||||
hideContextMenu,
|
||||
handleContextMenuEvent,
|
||||
} = useContextMenu();
|
||||
const handleContextMenuClick = (evt, id) => {
|
||||
console.log("wtf", evt, id);
|
||||
setCurrId(id);
|
||||
handleContextMenuEvent(evt);
|
||||
};
|
||||
const toggleRemoveConfirm = () => {
|
||||
setRemoveConfirmVisible((prev) => !prev);
|
||||
};
|
||||
const { is_public } = channelData[currId] || {};
|
||||
return (
|
||||
<>
|
||||
{channelIds.map((cid) => {
|
||||
return (
|
||||
<NavItem
|
||||
contextMenuEventHandler={handleContextMenuClick}
|
||||
key={cid}
|
||||
id={cid}
|
||||
setFiles={setDropFiles}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{contextMenuVisible ? (
|
||||
<ContextMenu
|
||||
hideMenu={hideContextMenu}
|
||||
posX={posX}
|
||||
posY={posY}
|
||||
items={[
|
||||
{
|
||||
title: "Mark As Read",
|
||||
underline: true,
|
||||
},
|
||||
{
|
||||
title: "Mute",
|
||||
},
|
||||
{
|
||||
title: "Notification Settings",
|
||||
underline: true,
|
||||
},
|
||||
is_public
|
||||
? null
|
||||
: {
|
||||
title: "Invite People",
|
||||
},
|
||||
{
|
||||
title: "Delete Channel",
|
||||
danger: true,
|
||||
handler: toggleRemoveConfirm,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : null}
|
||||
{removeConfirmVisible && (
|
||||
<DeleteConfirmModal id={currId} closeModal={toggleRemoveConfirm} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -75,7 +75,6 @@ export default function DMChat({ uid = "", dropFiles = [] }) {
|
||||
})}
|
||||
</div>
|
||||
</StyledDMChat>
|
||||
<div className="placeholder"></div>
|
||||
<Send
|
||||
dragFiles={dragFiles}
|
||||
type="user"
|
||||
|
||||
@@ -56,8 +56,4 @@ export const StyledDMChat = styled.article`
|
||||
overflow-y: scroll;
|
||||
overflow-x: visible;
|
||||
}
|
||||
.placeholder {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -96,13 +96,14 @@ export default function Layout({
|
||||
useEffect(() => {
|
||||
if (messagesContainer) {
|
||||
const container = messagesContainer.current;
|
||||
// 点击查看大图
|
||||
container.addEventListener(
|
||||
"click",
|
||||
(evt) => {
|
||||
console.log(evt);
|
||||
const { target } = evt;
|
||||
if (target.nodeType == 1 && target.classList.contains("preview")) {
|
||||
setPreviewImage(target.src);
|
||||
setPreviewImage(target.dataset.origin || target.src);
|
||||
}
|
||||
},
|
||||
true
|
||||
|
||||
Reference in New Issue
Block a user