feat: forward message and channel invite
This commit is contained in:
@@ -8,6 +8,7 @@ export const ContentTypes = {
|
||||
file: "rustchat/file",
|
||||
formData: "multipart/form-data",
|
||||
json: "application/json",
|
||||
archive: "rustchat/archive",
|
||||
};
|
||||
export const firebaseConfig = {
|
||||
apiKey: "AIzaSyDyJ6B1Ouenoha_gdGkBwIkBNStlwhlbO0",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { addUserMsg, removeUserMsg } from "../slices/message.user";
|
||||
import { addMessage, removeMessage } from "../slices/message";
|
||||
export const onMessageSendStarted = async (
|
||||
{
|
||||
ignoreLocal = false,
|
||||
id,
|
||||
content,
|
||||
type = "text",
|
||||
@@ -16,9 +17,11 @@ export const onMessageSendStarted = async (
|
||||
{ dispatch, queryFulfilled },
|
||||
from = "channel"
|
||||
) => {
|
||||
// 忽略archive类型的消息
|
||||
if (type == "archive") return;
|
||||
// id: who send to ,from_uid: who sent
|
||||
console.log("handlers data", content, type, properties);
|
||||
const isImage = properties.file_type?.startsWith("image");
|
||||
console.log("handlers data", content, type, properties, ignoreLocal, id);
|
||||
const isImage = properties.content_type?.startsWith("image");
|
||||
const ts = properties.local_id || new Date().getTime();
|
||||
// let imageData = null;
|
||||
// if (type == "image") {
|
||||
@@ -39,17 +42,17 @@ export const onMessageSendStarted = async (
|
||||
properties,
|
||||
from_uid,
|
||||
reply_mid,
|
||||
// 已读
|
||||
read: true,
|
||||
sending: true,
|
||||
};
|
||||
const addContextMessage = from == "channel" ? addChannelMsg : addUserMsg;
|
||||
const removeContextMessage =
|
||||
from == "channel" ? removeChannelMsg : removeUserMsg;
|
||||
batch(() => {
|
||||
dispatch(addMessage({ mid: ts, ...tmpMsg }));
|
||||
dispatch(addContextMessage({ id, mid: ts }));
|
||||
});
|
||||
if (!ignoreLocal) {
|
||||
batch(() => {
|
||||
dispatch(addMessage({ mid: ts, ...tmpMsg }));
|
||||
dispatch(addContextMessage({ id, mid: ts }));
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const { data: server_mid } = await queryFulfilled;
|
||||
|
||||
@@ -45,6 +45,13 @@ export const messageApi = createApi({
|
||||
body: meta,
|
||||
}),
|
||||
}),
|
||||
createArchive: builder.mutation({
|
||||
query: (mids = []) => ({
|
||||
url: `/resource/archive`,
|
||||
method: "POST",
|
||||
body: { mid_list: mids },
|
||||
}),
|
||||
}),
|
||||
uploadFile: builder.mutation({
|
||||
query: (formData) => ({
|
||||
// headers: {
|
||||
@@ -64,6 +71,11 @@ export const messageApi = createApi({
|
||||
url: `/resource/open_graphic_parse?url=${encodeURIComponent(url)}`,
|
||||
}),
|
||||
}),
|
||||
getArchiveMessage: builder.query({
|
||||
query: (file_path) => ({
|
||||
url: `/resource/archive?file_path=${encodeURIComponent(file_path)}`,
|
||||
}),
|
||||
}),
|
||||
replyMessage: builder.mutation({
|
||||
query: ({ reply_mid, content, type = "text" }) => ({
|
||||
headers: {
|
||||
@@ -102,6 +114,7 @@ export const messageApi = createApi({
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetArchiveMessageQuery,
|
||||
useLazyGetOGInfoQuery,
|
||||
usePrepareUploadFileMutation,
|
||||
useUploadFileMutation,
|
||||
@@ -110,4 +123,5 @@ export const {
|
||||
useReplyMessageMutation,
|
||||
useLazyDeleteMessageQuery,
|
||||
useReadMessageMutation,
|
||||
useCreateArchiveMutation,
|
||||
} = messageApi;
|
||||
|
||||
@@ -74,6 +74,9 @@ export const serverApi = createApi({
|
||||
getSMTPConfig: builder.query({
|
||||
query: () => ({ url: `admin/smtp/config` }),
|
||||
}),
|
||||
getSMTPStatus: builder.query({
|
||||
query: () => ({ url: `/admin/smtp/enabled` }),
|
||||
}),
|
||||
updateSMTPConfig: builder.mutation({
|
||||
query: (data) => ({
|
||||
url: `admin/smtp/config`,
|
||||
@@ -148,6 +151,7 @@ export const serverApi = createApi({
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetSMTPStatusQuery,
|
||||
useSendTestEmailMutation,
|
||||
useUpdateFirebaseConfigMutation,
|
||||
useGetFirebaseConfigQuery,
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// import React from 'react';
|
||||
import styled from "styled-components";
|
||||
import { useSelector } from "react-redux";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
import Avatar from "./Avatar";
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
user-select: none;
|
||||
&.compact {
|
||||
padding: 0;
|
||||
}
|
||||
&.interactive {
|
||||
&:hover,
|
||||
&.active {
|
||||
background: rgba(116, 127, 141, 0.1);
|
||||
}
|
||||
}
|
||||
.avatar {
|
||||
cursor: pointer;
|
||||
width: ${({ size }) => `${size}px`};
|
||||
height: ${({ size }) => `${size}px`};
|
||||
position: relative;
|
||||
img {
|
||||
border-radius: 50%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.name {
|
||||
/* user-select: text; */
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #52525b;
|
||||
}
|
||||
`;
|
||||
export default function Channel({
|
||||
interactive = true,
|
||||
id = "",
|
||||
compact = false,
|
||||
avatarSize = 32,
|
||||
}) {
|
||||
const { channel, totalMemberCount } = useSelector((store) => {
|
||||
return {
|
||||
channel: store.channels.byId[id],
|
||||
totalMemberCount: store.contacts.ids.length,
|
||||
};
|
||||
});
|
||||
console.log("channel item", id, channel);
|
||||
if (!channel) return null;
|
||||
const { name, members = [], is_public, avatar } = channel;
|
||||
return (
|
||||
<StyledWrapper
|
||||
size={avatarSize}
|
||||
className={`${interactive ? "interactive" : ""} ${
|
||||
compact ? "compact" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="avatar">
|
||||
<Avatar type="channel" url={avatar} name={"#"} alt="avatar" />
|
||||
</div>
|
||||
{!compact && (
|
||||
<span className="name">
|
||||
{name} ({is_public ? totalMemberCount : members.length})
|
||||
</span>
|
||||
)}
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
@@ -45,7 +45,9 @@ export default function ChannelInviteModal({
|
||||
Add friends to #{title || channel?.name}{" "}
|
||||
<CloseIcon className="close" onClick={closeModal} />
|
||||
</h2>
|
||||
{!channel?.is_public && <AddMembers cid={cid} />}
|
||||
{!channel?.is_public && (
|
||||
<AddMembers cid={cid} closeModal={closeModal} />
|
||||
)}
|
||||
<InviteByEmail cid={cid} />
|
||||
</Styled>
|
||||
</Modal>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { useSelector } from "react-redux";
|
||||
import Modal from "../Modal";
|
||||
import Button from "../styled/Button";
|
||||
import ChannelIcon from "../ChannelIcon";
|
||||
@@ -10,7 +10,6 @@ import StyledWrapper from "./styled";
|
||||
import StyledToggle from "../../component/styled/Toggle";
|
||||
import StyledCheckbox from "../../component/styled/Checkbox";
|
||||
import useFilteredUsers from "../../hook/useFilteredUsers";
|
||||
import { addChannel } from "../../../app/slices/channels";
|
||||
|
||||
import { useCreateChannelMutation } from "../../../app/services/channel";
|
||||
|
||||
@@ -19,7 +18,6 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
||||
return { conactsData: store.contacts.byId, loginUid: store.authData.uid };
|
||||
});
|
||||
const navigateTo = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const [data, setData] = useState({
|
||||
name: "",
|
||||
dsecription: "",
|
||||
@@ -29,7 +27,7 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
||||
const { contacts, input, updateInput } = useFilteredUsers();
|
||||
const [
|
||||
createChannel,
|
||||
{ isSuccess, isError, isLoading, data: newChannel },
|
||||
{ isSuccess, isError, isLoading, data: newChannelId },
|
||||
] = useCreateChannelMutation();
|
||||
|
||||
const handleToggle = () => {
|
||||
@@ -59,10 +57,9 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
||||
if (isSuccess) {
|
||||
toast.success("create new channel success");
|
||||
closeModal();
|
||||
dispatch(addChannel(newChannel));
|
||||
navigateTo(`/chat/channel/${newChannel.gid}`);
|
||||
navigateTo(`/chat/channel/${newChannelId}`);
|
||||
}
|
||||
}, [isSuccess, newChannel]);
|
||||
}, [isSuccess, newChannelId]);
|
||||
|
||||
const handleNameInput = (evt) => {
|
||||
setData((prev) => {
|
||||
|
||||
@@ -1,41 +1,79 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
// import { useSelector, useDispatch } from "react-redux";
|
||||
import { useState } from "react";
|
||||
// import toast from "react-hot-toast";
|
||||
import Modal from "../Modal";
|
||||
import Button from "../styled/Button";
|
||||
import Input from "../styled/Input";
|
||||
import Channel from "../Channel";
|
||||
import Contact from "../Contact";
|
||||
// import Channel from "../Channel";
|
||||
import Reply from "../Message/Reply";
|
||||
import StyledWrapper from "./styled";
|
||||
import useForwardMessage from "../../hook/useForwardMessage";
|
||||
import useSendMessage from "../../hook/useSendMessage";
|
||||
import useFilteredChannels from "../../hook/useFilteredChannels";
|
||||
import useFilteredUsers from "../../hook/useFilteredUsers";
|
||||
import CloseIcon from "../../../assets/icons/close.circle.svg";
|
||||
import StyledCheckbox from "../../component/styled/Checkbox";
|
||||
import useFilteredUsers from "../../hook/useFilteredUsers";
|
||||
// import { addChannel } from "../../../app/slices/channels";
|
||||
|
||||
// import { useCreateChannelMutation } from "../../../app/services/channel";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function ForwardModal({ mid, closeModal }) {
|
||||
const [members, setMembers] = useState([]);
|
||||
const [appendText, setAppendText] = useState("");
|
||||
const { sendMessages } = useSendMessage();
|
||||
const { forwardMessage, forwarding } = useForwardMessage();
|
||||
const [selectedMembers, setSelectedMembers] = useState([]);
|
||||
const [selectedChannels, setSelectedChannels] = useState([]);
|
||||
const {
|
||||
channels,
|
||||
// input: channelInput,
|
||||
updateInput: updateChannelInput,
|
||||
} = useFilteredChannels();
|
||||
const { contacts, input, updateInput } = useFilteredUsers();
|
||||
// const { conactsData, loginUid } = useSelector((store) => {
|
||||
// return { conactsData: store.contacts.byId, loginUid: store.authData.uid };
|
||||
// });
|
||||
const handleInputChange = (evt) => {
|
||||
updateInput(evt.target.value);
|
||||
const toggleCheck = ({ currentTarget }) => {
|
||||
const { id, type = "user" } = currentTarget.dataset;
|
||||
const ids = type == "user" ? selectedMembers : selectedChannels;
|
||||
const updateState =
|
||||
type == "user" ? setSelectedMembers : setSelectedChannels;
|
||||
let tmp = ids.includes(+id) ? ids.filter((m) => m != id) : [...ids, +id];
|
||||
console.log(id, currentTarget);
|
||||
updateState(tmp);
|
||||
};
|
||||
const toggleCheckMember = ({ currentTarget }) => {
|
||||
const { uid } = currentTarget.dataset;
|
||||
let tmp = members.includes(+uid)
|
||||
? members.filter((m) => m != uid)
|
||||
: [...members, +uid];
|
||||
console.log(uid, currentTarget);
|
||||
setMembers(tmp);
|
||||
const updateAppendText = (evt) => {
|
||||
setAppendText(evt.target.value);
|
||||
};
|
||||
const removeSelected = (uid) => {
|
||||
setMembers(members.filter((m) => m != uid));
|
||||
const handleForward = async () => {
|
||||
await forwardMessage({
|
||||
mids: [mid],
|
||||
users: selectedMembers,
|
||||
channels: selectedChannels,
|
||||
});
|
||||
if (appendText) {
|
||||
await sendMessages({
|
||||
content: appendText,
|
||||
users: selectedMembers,
|
||||
channels: selectedChannels,
|
||||
});
|
||||
}
|
||||
toast.success("Forward Message Successfully");
|
||||
closeModal();
|
||||
};
|
||||
const selectedCount = members.length ? `(${members.length})` : null;
|
||||
const removeSelected = (id, from = "user") => {
|
||||
if (from == "user") {
|
||||
setSelectedMembers(selectedMembers.filter((m) => m != id));
|
||||
} else {
|
||||
setSelectedChannels(selectedChannels.filter((cid) => cid != id));
|
||||
}
|
||||
};
|
||||
const handleSearchChange = (evt) => {
|
||||
const newVal = evt.target.value;
|
||||
updateChannelInput(newVal);
|
||||
updateInput(newVal);
|
||||
};
|
||||
let selectedCount = selectedMembers.length + selectedChannels.length;
|
||||
const sendButtonDisabled =
|
||||
(selectedChannels.length == 0 && selectedMembers.length == 0) || forwarding;
|
||||
return (
|
||||
<Modal>
|
||||
<StyledWrapper>
|
||||
@@ -43,22 +81,46 @@ export default function ForwardModal({ mid, closeModal }) {
|
||||
<div className="search">
|
||||
<input
|
||||
value={input}
|
||||
onChange={handleInputChange}
|
||||
onChange={handleSearchChange}
|
||||
placeholder="Search user or channel"
|
||||
/>
|
||||
</div>
|
||||
{contacts && (
|
||||
<ul className="users">
|
||||
{contacts.map((u) => {
|
||||
<ul className="users">
|
||||
{channels &&
|
||||
channels.map((c) => {
|
||||
const { gid } = c;
|
||||
const checked = selectedChannels.includes(gid);
|
||||
console.log({ checked });
|
||||
return (
|
||||
<li
|
||||
key={gid}
|
||||
data-type="channel"
|
||||
data-id={gid}
|
||||
className="user channel"
|
||||
onClick={toggleCheck}
|
||||
>
|
||||
<StyledCheckbox
|
||||
readOnly
|
||||
checked={checked}
|
||||
name="cb"
|
||||
id="cb"
|
||||
/>
|
||||
<Channel id={gid} interactive={false} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
{contacts &&
|
||||
contacts.map((u) => {
|
||||
const { uid } = u;
|
||||
const checked = members.includes(uid);
|
||||
const checked = selectedMembers.includes(uid);
|
||||
console.log({ checked });
|
||||
return (
|
||||
<li
|
||||
key={uid}
|
||||
data-uid={uid}
|
||||
data-id={uid}
|
||||
data-type="user"
|
||||
className="user"
|
||||
onClick={toggleCheckMember}
|
||||
onClick={toggleCheck}
|
||||
>
|
||||
<StyledCheckbox
|
||||
readOnly
|
||||
@@ -70,13 +132,28 @@ export default function ForwardModal({ mid, closeModal }) {
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
<div className={`right`}>
|
||||
<h3 className="title">Send To {selectedCount}</h3>
|
||||
<ul className="selected">
|
||||
{members.map((uid) => {
|
||||
{selectedChannels.map((cid) => {
|
||||
return (
|
||||
<li key={cid} className="item">
|
||||
<Channel
|
||||
key={cid}
|
||||
id={cid}
|
||||
interactive={false}
|
||||
avatarSize={40}
|
||||
/>
|
||||
<CloseIcon
|
||||
className="remove"
|
||||
onClick={removeSelected.bind(null, cid, "channel")}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
{selectedMembers.map((uid) => {
|
||||
return (
|
||||
<li key={uid} className="item">
|
||||
<Contact
|
||||
@@ -87,7 +164,7 @@ export default function ForwardModal({ mid, closeModal }) {
|
||||
/>
|
||||
<CloseIcon
|
||||
className="remove"
|
||||
onClick={removeSelected.bind(null, uid)}
|
||||
onClick={removeSelected.bind(null, uid, "user")}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
@@ -96,13 +173,22 @@ export default function ForwardModal({ mid, closeModal }) {
|
||||
<div className="reply">
|
||||
<Reply mid={mid} interactive={false} />
|
||||
</div>
|
||||
<Input className="input" placeholder="Leave a message"></Input>
|
||||
<Input
|
||||
className="input"
|
||||
placeholder="Leave a message"
|
||||
value={appendText}
|
||||
onChange={updateAppendText}
|
||||
></Input>
|
||||
<div className="btns">
|
||||
<Button onClick={closeModal} className="normal cancel">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button className="normal" disabled={members.length == 0}>
|
||||
Send To {selectedCount}
|
||||
<Button
|
||||
className="normal"
|
||||
disabled={sendButtonDisabled}
|
||||
onClick={handleForward}
|
||||
>
|
||||
Send To {selectedCount == 0 ? null : `(${selectedCount})`}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ const StyledWrapper = styled.div`
|
||||
transition: all 0.5s ease;
|
||||
overflow: hidden;
|
||||
.left {
|
||||
width: 260px;
|
||||
width: 276px;
|
||||
/* height: 100%; */
|
||||
box-shadow: inset -1px 0px 0px rgba(0, 0, 0, 0.1);
|
||||
overflow-y: scroll;
|
||||
|
||||
@@ -105,7 +105,7 @@ export default function URLPreview({ url = "" }) {
|
||||
getMetaData(url);
|
||||
}, [url]);
|
||||
|
||||
if (!url || !data) return null;
|
||||
if (!url || !data || !data.title) return null;
|
||||
const { favicon, title, description, ogImage } = data;
|
||||
return ogImage ? (
|
||||
<StyledDetails href={url} target="_blank">
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { useState } from "react";
|
||||
import { useSendChannelMsgMutation } from "../../app/services/channel";
|
||||
import { useSendMsgMutation } from "../../app/services/contact";
|
||||
import { useCreateArchiveMutation } from "../../app/services/message";
|
||||
export default function useForwardMessage() {
|
||||
const [forwarding, setForwarding] = useState(false);
|
||||
const [
|
||||
createArchive,
|
||||
{
|
||||
isError: createArchiveError,
|
||||
isLoading: creatingArchive,
|
||||
isSuccess: createArchiveSuccess,
|
||||
},
|
||||
] = useCreateArchiveMutation();
|
||||
const [
|
||||
sendChannelMsg,
|
||||
{
|
||||
isLoading: channelSending,
|
||||
isSuccess: channelSuccess,
|
||||
isError: channelError,
|
||||
},
|
||||
] = useSendChannelMsgMutation();
|
||||
const [
|
||||
sendUserMsg,
|
||||
{ isLoading: userSending, isSuccess: userSuccess, isError: userError },
|
||||
] = useSendMsgMutation();
|
||||
const forwardMessage = async ({ mids = [], users = [], channels = [] }) => {
|
||||
setForwarding(true);
|
||||
const { data: archive_id } = await createArchive(mids);
|
||||
if (users.length) {
|
||||
for await (const uid of users) {
|
||||
await sendUserMsg({
|
||||
type: "archive",
|
||||
id: uid,
|
||||
content: archive_id,
|
||||
// from_uid: from,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (channels.length) {
|
||||
for await (const cid of channels) {
|
||||
await sendChannelMsg({
|
||||
type: "archive",
|
||||
id: cid,
|
||||
content: archive_id,
|
||||
// from_uid: from,
|
||||
});
|
||||
}
|
||||
}
|
||||
setForwarding(false);
|
||||
};
|
||||
return {
|
||||
forwardMessage,
|
||||
forwarding,
|
||||
isError: channelError || userError || createArchiveError,
|
||||
isSending: userSending || channelSending || creatingArchive,
|
||||
isSuccess: channelSuccess || userSuccess || createArchiveSuccess,
|
||||
};
|
||||
}
|
||||
@@ -2,15 +2,15 @@ import { useState, useEffect } from "react";
|
||||
import useCopy from "./useCopy";
|
||||
import {
|
||||
useLazyCreateInviteLinkQuery as useCreateServerInviteLinkQuery,
|
||||
useGetSMTPConfigQuery,
|
||||
useGetSMTPStatusQuery,
|
||||
} from "../../app/services/server";
|
||||
import { useLazyCreateInviteLinkQuery as useCreateChannelInviteLinkQuery } from "../../app/services/channel";
|
||||
export default function useInviteLink(cid = null) {
|
||||
const [finalLink, setFinalLink] = useState("");
|
||||
const {
|
||||
data: config,
|
||||
isSuccess: configQuerySuccess,
|
||||
} = useGetSMTPConfigQuery();
|
||||
data: SMTPEnabled,
|
||||
isSuccess: smtpStatusFetchSuccess,
|
||||
} = useGetSMTPStatusQuery();
|
||||
const [
|
||||
generateChannelInviteLink,
|
||||
{ data: channelInviteLink, isLoading: generatingChannelLink },
|
||||
@@ -39,15 +39,15 @@ export default function useInviteLink(cid = null) {
|
||||
useEffect(() => {
|
||||
const _link = serverInviteLink || channelInviteLink;
|
||||
console.log("fetching", serverInviteLink, channelInviteLink);
|
||||
if (_link && config) {
|
||||
if (_link && smtpStatusFetchSuccess) {
|
||||
const tmpURL = new URL(_link);
|
||||
tmpURL.searchParams.set("code", config.enabled);
|
||||
tmpURL.searchParams.set("code", SMTPEnabled);
|
||||
setFinalLink(tmpURL.href);
|
||||
}
|
||||
}, [serverInviteLink, channelInviteLink, config]);
|
||||
}, [serverInviteLink, channelInviteLink, smtpStatusFetchSuccess]);
|
||||
return {
|
||||
enableSMTP: config?.enabled,
|
||||
generating: generatingChannelLink || generatingServerLink,
|
||||
enableSMTP: SMTPEnabled,
|
||||
generating: cid ? generatingChannelLink : generatingServerLink,
|
||||
generateNewLink: regenLink,
|
||||
link: finalLink,
|
||||
linkCopied,
|
||||
|
||||
@@ -82,8 +82,10 @@ const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => {
|
||||
}
|
||||
};
|
||||
const toggleInviteModalVisible = (evt) => {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
if (evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
}
|
||||
setInviteModalVisible((prev) => !prev);
|
||||
};
|
||||
const handleMute = () => {
|
||||
|
||||
Reference in New Issue
Block a user