chore: update prettier setting
This commit is contained in:
@@ -42,9 +42,7 @@ const Styled = styled.ul`
|
||||
}
|
||||
`;
|
||||
export default function AddEntriesMenu() {
|
||||
const currentUser = useSelector(
|
||||
(store) => store.contacts.byId[store.authData.uid]
|
||||
);
|
||||
const currentUser = useSelector((store) => store.contacts.byId[store.authData.uid]);
|
||||
const [isPrivate, setIsPrivate] = useState(false);
|
||||
const [inviteModalVisible, setInviteModalVisible] = useState(false);
|
||||
|
||||
@@ -78,10 +76,7 @@ export default function AddEntriesMenu() {
|
||||
<>
|
||||
<Styled>
|
||||
{currentUser?.is_admin && (
|
||||
<li
|
||||
className="item"
|
||||
onClick={handleOpenChannelModal.bind(null, false)}
|
||||
>
|
||||
<li className="item" onClick={handleOpenChannelModal.bind(null, false)}>
|
||||
<ChannelIcon className="icon" />
|
||||
New Channel
|
||||
</li>
|
||||
@@ -99,15 +94,9 @@ export default function AddEntriesMenu() {
|
||||
Invite People
|
||||
</li>
|
||||
</Styled>
|
||||
{channelModalVisible && (
|
||||
<ChannelModal personal={isPrivate} closeModal={handleCloseModal} />
|
||||
)}
|
||||
{contactsModalVisible && (
|
||||
<ContactsModal closeModal={toggleContactsModalVisible} />
|
||||
)}
|
||||
{inviteModalVisible && (
|
||||
<InviteModal closeModal={toggleInviteModalVisible} />
|
||||
)}
|
||||
{channelModalVisible && <ChannelModal personal={isPrivate} closeModal={handleCloseModal} />}
|
||||
{contactsModalVisible && <ContactsModal closeModal={toggleContactsModalVisible} />}
|
||||
{inviteModalVisible && <InviteModal closeModal={toggleInviteModalVisible} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ const Avatar = ({ url = "", name = "unkonw name", type = "user", ...rest }) => {
|
||||
const tmp = getInitialsAvatar({
|
||||
initials: getInitials(name),
|
||||
background: type == "channel" ? "#EAECF0" : undefined,
|
||||
foreground: type == "channel" ? "#475467" : undefined,
|
||||
foreground: type == "channel" ? "#475467" : undefined
|
||||
});
|
||||
setSrc(tmp);
|
||||
};
|
||||
@@ -17,7 +17,7 @@ const Avatar = ({ url = "", name = "unkonw name", type = "user", ...rest }) => {
|
||||
const tmp = getInitialsAvatar({
|
||||
initials: getInitials(name),
|
||||
background: type == "channel" ? "#EAECF0" : undefined,
|
||||
foreground: type == "channel" ? "#475467" : undefined,
|
||||
foreground: type == "channel" ? "#475467" : undefined
|
||||
});
|
||||
setSrc(tmp);
|
||||
} else {
|
||||
|
||||
@@ -64,7 +64,7 @@ export default function AvatarUploader({
|
||||
name = "",
|
||||
type = "user",
|
||||
uploadImage,
|
||||
disabled = false,
|
||||
disabled = false
|
||||
}) {
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const handleUpload = async (evt) => {
|
||||
@@ -80,9 +80,7 @@ export default function AvatarUploader({
|
||||
<Avatar type={type} url={url} name={name} />
|
||||
{!disabled && (
|
||||
<>
|
||||
<div className="tip">
|
||||
{uploading ? `Uploading` : `Change Avatar`}
|
||||
</div>
|
||||
<div className="tip">{uploading ? `Uploading` : `Change Avatar`}</div>
|
||||
<input
|
||||
multiple={false}
|
||||
onChange={handleUpload}
|
||||
|
||||
@@ -80,19 +80,16 @@ export default function BlankPlaceholder({ type = "chat" }) {
|
||||
setInviteModalVisible((prev) => !prev);
|
||||
};
|
||||
const chatTip =
|
||||
type == "chat"
|
||||
? "Create a Channel to Start a Conversation"
|
||||
: "Send a Direct Message";
|
||||
const chatHanlder =
|
||||
type == "chat" ? toggleChannelModalVisible : toggleContactListVisible;
|
||||
type == "chat" ? "Create a Channel to Start a Conversation" : "Send a Direct Message";
|
||||
const chatHanlder = type == "chat" ? toggleChannelModalVisible : toggleContactListVisible;
|
||||
return (
|
||||
<>
|
||||
<Styled>
|
||||
<div className="head">
|
||||
<h2 className="title">Welcome to {server.name} server</h2>
|
||||
<p className="desc">
|
||||
Here are some steps to help you get started. For more, check out our
|
||||
Getting Started guide
|
||||
Here are some steps to help you get started. For more, check out our Getting Started
|
||||
guide
|
||||
</p>
|
||||
</div>
|
||||
<div className="boxes">
|
||||
@@ -117,12 +114,8 @@ export default function BlankPlaceholder({ type = "chat" }) {
|
||||
{createChannelVisible && (
|
||||
<ChannelModal personal={true} closeModal={toggleChannelModalVisible} />
|
||||
)}
|
||||
{contactListVisible && (
|
||||
<ContactsModal closeModal={toggleContactListVisible} />
|
||||
)}
|
||||
{inviteModalVisible && (
|
||||
<InviteModal closeModal={toggleInviteModalVisible} />
|
||||
)}
|
||||
{contactListVisible && <ContactsModal closeModal={toggleContactListVisible} />}
|
||||
{inviteModalVisible && <InviteModal closeModal={toggleInviteModalVisible} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,16 +47,11 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function Channel({
|
||||
interactive = true,
|
||||
id = "",
|
||||
compact = false,
|
||||
avatarSize = 32,
|
||||
}) {
|
||||
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,
|
||||
totalMemberCount: store.contacts.ids.length
|
||||
};
|
||||
});
|
||||
console.log("channel item", id, channel);
|
||||
@@ -65,17 +60,14 @@ export default function Channel({
|
||||
return (
|
||||
<StyledWrapper
|
||||
size={avatarSize}
|
||||
className={`${interactive ? "interactive" : ""} ${
|
||||
compact ? "compact" : ""
|
||||
}`}
|
||||
className={`${interactive ? "interactive" : ""} ${compact ? "compact" : ""}`}
|
||||
>
|
||||
<div className="avatar">
|
||||
<Avatar type="channel" url={avatar} name={"#"} alt="avatar" />
|
||||
</div>
|
||||
{!compact && (
|
||||
<div className="name">
|
||||
<span className="txt">{name}</span> (
|
||||
{is_public ? totalMemberCount : members.length})
|
||||
<span className="txt">{name}</span> ({is_public ? totalMemberCount : members.length})
|
||||
</div>
|
||||
)}
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -8,11 +8,7 @@ const Styled = styled.div`
|
||||
fill: #d0d5dd;
|
||||
}
|
||||
`;
|
||||
export default function ChannelIcon({
|
||||
personal = false,
|
||||
muted = false,
|
||||
className,
|
||||
}) {
|
||||
export default function ChannelIcon({ personal = false, muted = false, className }) {
|
||||
return (
|
||||
<Styled className={`${muted ? "muted" : ""} ${className}`}>
|
||||
{personal ? <LockHashIcon /> : <HashIcon />}
|
||||
|
||||
@@ -22,13 +22,11 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
||||
name: "",
|
||||
dsecription: "",
|
||||
members: [loginUid],
|
||||
is_public: !personal,
|
||||
is_public: !personal
|
||||
});
|
||||
const { contacts, input, updateInput } = useFilteredUsers();
|
||||
const [
|
||||
createChannel,
|
||||
{ isSuccess, isError, isLoading, data: newChannelId },
|
||||
] = useCreateChannelMutation();
|
||||
const [createChannel, { isSuccess, isError, isLoading, data: newChannelId }] =
|
||||
useCreateChannelMutation();
|
||||
|
||||
const handleToggle = () => {
|
||||
const { is_public } = data;
|
||||
@@ -72,9 +70,7 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
||||
const toggleCheckMember = ({ currentTarget }) => {
|
||||
const { members } = data;
|
||||
const { uid } = currentTarget.dataset;
|
||||
let tmp = members.includes(+uid)
|
||||
? members.filter((m) => m != uid)
|
||||
: [...members, +uid];
|
||||
let tmp = members.includes(+uid) ? members.filter((m) => m != uid) : [...members, +uid];
|
||||
console.log(uid, currentTarget);
|
||||
setData((prev) => {
|
||||
return { ...prev, members: tmp };
|
||||
@@ -135,11 +131,7 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
||||
<div className="name">
|
||||
<span className="label normal">Channel Name</span>
|
||||
<div className="input">
|
||||
<input
|
||||
onChange={handleNameInput}
|
||||
value={name}
|
||||
placeholder="new channel"
|
||||
/>
|
||||
<input onChange={handleNameInput} value={name} placeholder="new channel" />
|
||||
<ChannelIcon personal={!is_public} className="icon" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -155,11 +147,7 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
||||
<Button onClick={closeModal} className="normal cancel">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
onClick={handleCreate}
|
||||
className="normal"
|
||||
>
|
||||
<Button disabled={isLoading} onClick={handleCreate} className="normal">
|
||||
Create
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -4,14 +4,7 @@ import Tippy from "@tippyjs/react";
|
||||
import useContactOperation from "../../hook/useContactOperation";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
|
||||
export default function ContactContextMenu({
|
||||
enable = false,
|
||||
uid,
|
||||
cid,
|
||||
visible,
|
||||
hide,
|
||||
children,
|
||||
}) {
|
||||
export default function ContactContextMenu({ enable = false, uid, cid, visible, hide, children }) {
|
||||
const {
|
||||
canCall,
|
||||
call,
|
||||
@@ -21,10 +14,10 @@ export default function ContactContextMenu({
|
||||
canRemove,
|
||||
canRemoveFromChannel,
|
||||
removeFromChannel,
|
||||
removeUser,
|
||||
removeUser
|
||||
} = useContactOperation({
|
||||
uid,
|
||||
cid,
|
||||
cid
|
||||
});
|
||||
return (
|
||||
<>
|
||||
@@ -43,26 +36,26 @@ export default function ContactContextMenu({
|
||||
items={[
|
||||
{
|
||||
title: "Message",
|
||||
handler: startChat,
|
||||
handler: startChat
|
||||
},
|
||||
canCall && {
|
||||
title: "Call",
|
||||
handler: call,
|
||||
handler: call
|
||||
},
|
||||
canCopyEmail && {
|
||||
title: "Copy Email",
|
||||
handler: copyEmail,
|
||||
handler: copyEmail
|
||||
},
|
||||
canRemoveFromChannel && {
|
||||
danger: true,
|
||||
title: "Remove From Channel",
|
||||
handler: removeFromChannel,
|
||||
handler: removeFromChannel
|
||||
},
|
||||
canRemove && {
|
||||
danger: true,
|
||||
title: "Remove From Server",
|
||||
handler: removeUser,
|
||||
},
|
||||
handler: removeUser
|
||||
}
|
||||
]}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -17,14 +17,10 @@ export default function Contact({
|
||||
popover = false,
|
||||
compact = false,
|
||||
avatarSize = 32,
|
||||
enableContextMenu = false,
|
||||
enableContextMenu = false
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
visible: contextMenuVisible,
|
||||
handleContextMenuEvent,
|
||||
hideContextMenu,
|
||||
} = useContextMenu();
|
||||
const { visible: contextMenuVisible, handleContextMenuEvent, hideContextMenu } = useContextMenu();
|
||||
const curr = useSelector((store) => store.contacts.byId[uid]);
|
||||
const handleDoubleClick = () => {
|
||||
navigate(`/chat/dm/${uid}`);
|
||||
@@ -50,15 +46,11 @@ export default function Contact({
|
||||
onContextMenu={enableContextMenu ? handleContextMenuEvent : null}
|
||||
size={avatarSize}
|
||||
onDoubleClick={dm ? handleDoubleClick : null}
|
||||
className={`${interactive ? "interactive" : ""} ${
|
||||
compact ? "compact" : ""
|
||||
}`}
|
||||
className={`${interactive ? "interactive" : ""} ${compact ? "compact" : ""}`}
|
||||
>
|
||||
<div className="avatar">
|
||||
<Avatar url={curr.avatar} name={curr.name} alt="avatar" />
|
||||
<div
|
||||
className={`status ${curr.online ? "online" : "offline"}`}
|
||||
></div>
|
||||
<div className={`status ${curr.online ? "online" : "offline"}`}></div>
|
||||
</div>
|
||||
{!compact && <span className="name">{curr?.name}</span>}
|
||||
{owner && <IconOwner />}
|
||||
|
||||
@@ -64,11 +64,7 @@ export default function ContactsModal({ closeModal }) {
|
||||
<Modal>
|
||||
<StyledWrapper ref={wrapperRef}>
|
||||
<div className="search">
|
||||
<input
|
||||
value={input}
|
||||
onChange={handleSearch}
|
||||
placeholder="Type Username to search"
|
||||
/>
|
||||
<input value={input} onChange={handleSearch} placeholder="Type Username to search" />
|
||||
</div>
|
||||
{contacts && (
|
||||
<ul className="users">
|
||||
|
||||
@@ -16,13 +16,11 @@ export default function ContextMenu({ items = [], hideMenu = null }) {
|
||||
}
|
||||
},
|
||||
underline = false,
|
||||
danger = false,
|
||||
danger = false
|
||||
} = item;
|
||||
return (
|
||||
<li
|
||||
className={`item ${underline ? "underline" : ""} ${
|
||||
danger ? "danger" : ""
|
||||
}`}
|
||||
className={`item ${underline ? "underline" : ""} ${danger ? "danger" : ""}`}
|
||||
key={title}
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation();
|
||||
|
||||
@@ -25,11 +25,7 @@ export default function DeleteMessageConfirmModal({ closeModal, mids = [] }) {
|
||||
<Button className="cancel" onClick={closeModal.bind(null, false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={isDeleting}
|
||||
onClick={handleDelete}
|
||||
className="danger"
|
||||
>
|
||||
<Button disabled={isDeleting} onClick={handleDelete} className="danger">
|
||||
{isDeleting ? "Deleting" : `Delete`}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@@ -13,9 +13,7 @@ export default function FAQ() {
|
||||
<Styled>
|
||||
<div className="item">Client Version: {process.env.VERSION}</div>
|
||||
<div className="item">Server Version: {serverVersion}</div>
|
||||
<div className="item">
|
||||
Build Timestamp: {process.env.REACT_APP_BUILD_TIME}
|
||||
</div>
|
||||
<div className="item">Build Timestamp: {process.env.REACT_APP_BUILD_TIME}</div>
|
||||
</Styled>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
ImagePreview,
|
||||
PdfPreview,
|
||||
CodePreview,
|
||||
DocPreview,
|
||||
DocPreview
|
||||
} from "./preview";
|
||||
import { getFileIcon, formatBytes } from "../../utils";
|
||||
import IconDownload from "../../../assets/icons/download.svg";
|
||||
@@ -24,7 +24,7 @@ const renderPreview = (data) => {
|
||||
video: /^video/gi,
|
||||
code: /(json|javascript|java|rb|c|php|xml|css|html)$/gi,
|
||||
doc: /^text/gi,
|
||||
pdf: /\/pdf$/gi,
|
||||
pdf: /\/pdf$/gi
|
||||
};
|
||||
const _arr = name.split(".");
|
||||
const _type = file_type || _arr[_arr.length - 1];
|
||||
@@ -65,7 +65,7 @@ export default function FileBox({
|
||||
size,
|
||||
created_at,
|
||||
from_uid,
|
||||
content,
|
||||
content
|
||||
}) {
|
||||
const fromUser = useSelector((store) => store.contacts.byId[from_uid]);
|
||||
const icon = getFileIcon(file_type, name);
|
||||
@@ -75,9 +75,9 @@ export default function FileBox({
|
||||
const withPreview = preview && previewContent;
|
||||
return (
|
||||
<Styled
|
||||
className={`file_box ${flex ? "flex" : ""} ${
|
||||
withPreview ? "preview" : ""
|
||||
} ${file_type.startsWith("audio") ? "audio" : ""}`}
|
||||
className={`file_box ${flex ? "flex" : ""} ${withPreview ? "preview" : ""} ${
|
||||
file_type.startsWith("audio") ? "audio" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="basic">
|
||||
{icon}
|
||||
@@ -91,11 +91,7 @@ export default function FileBox({
|
||||
</i>
|
||||
</span>
|
||||
</div>
|
||||
<a
|
||||
className="download"
|
||||
download={name}
|
||||
href={`${content}&download=true`}
|
||||
>
|
||||
<a className="download" download={name} href={`${content}&download=true`}>
|
||||
<IconDownload />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -5,11 +5,4 @@ import PdfPreview from "./Pdf";
|
||||
import CodePreview from "./Code";
|
||||
import DocPreview from "./Doc";
|
||||
|
||||
export {
|
||||
VideoPreview,
|
||||
AudioPreview,
|
||||
ImagePreview,
|
||||
PdfPreview,
|
||||
CodePreview,
|
||||
DocPreview,
|
||||
};
|
||||
export { VideoPreview, AudioPreview, ImagePreview, PdfPreview, CodePreview, DocPreview };
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function ImageMessage({
|
||||
thumbnail,
|
||||
download,
|
||||
content,
|
||||
properties = {},
|
||||
properties = {}
|
||||
}) {
|
||||
const [url, setUrl] = useState(thumbnail);
|
||||
const { width = 0, height = 0 } = getDefaultSize(properties);
|
||||
@@ -64,7 +64,7 @@ export default function ImageMessage({
|
||||
strokeWidth={50}
|
||||
styles={buildStyles({
|
||||
storke: "#000",
|
||||
strokeLinecap: "butt",
|
||||
strokeLinecap: "butt"
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
@@ -74,7 +74,7 @@ export default function ImageMessage({
|
||||
className="img preview"
|
||||
style={{
|
||||
width: width ? `${width}px` : "",
|
||||
height: height ? `${height}px` : "",
|
||||
height: height ? `${height}px` : ""
|
||||
}}
|
||||
data-meta={JSON.stringify(properties)}
|
||||
data-origin={content}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function FileMessage({
|
||||
content = "",
|
||||
download = "",
|
||||
thumbnail = "",
|
||||
properties = { local_id: 0, name: "", size: 0, content_type: "" },
|
||||
properties = { local_id: 0, name: "", size: 0, content_type: "" }
|
||||
}) {
|
||||
const [imageSize, setImageSize] = useState(null);
|
||||
const [uploadingFile, setUploadingFile] = useState(false);
|
||||
@@ -31,19 +31,13 @@ export default function FileMessage({
|
||||
const {
|
||||
sendMessage,
|
||||
isSuccess: sendMessageSuccess,
|
||||
isSending,
|
||||
isSending
|
||||
} = useSendMessage({
|
||||
context,
|
||||
from: from_uid,
|
||||
to,
|
||||
to
|
||||
});
|
||||
const {
|
||||
stopUploading,
|
||||
data,
|
||||
uploadFile,
|
||||
progress,
|
||||
isSuccess: uploadSuccess,
|
||||
} = useUploadFile();
|
||||
const { stopUploading, data, uploadFile, progress, isSuccess: uploadSuccess } = useUploadFile();
|
||||
const fromUser = useSelector((store) => store.contacts.byId[from_uid]);
|
||||
const { size, name, content_type } = properties ?? {};
|
||||
useEffect(() => {
|
||||
@@ -75,20 +69,14 @@ export default function FileMessage({
|
||||
const propsV2 = imageSize ? { ...props, ...imageSize } : props;
|
||||
// 本地文件 并且上传成功
|
||||
if (uploadSuccess && isLocalFile(content)) {
|
||||
console.log(
|
||||
"send local file message",
|
||||
uploadSuccess,
|
||||
propsV2,
|
||||
data,
|
||||
content
|
||||
);
|
||||
console.log("send local file message", uploadSuccess, propsV2, data, content);
|
||||
// 把已经上传的东西当做消息发出去
|
||||
const { path } = data;
|
||||
sendMessage({
|
||||
ignoreLocal: true,
|
||||
type: "file",
|
||||
content: { path },
|
||||
properties: propsV2,
|
||||
properties: propsV2
|
||||
});
|
||||
}
|
||||
}, [uploadSuccess, data, properties, content]);
|
||||
@@ -151,11 +139,7 @@ export default function FileMessage({
|
||||
{sending ? (
|
||||
<IconClose className="cancel" onClick={handleCancel} />
|
||||
) : (
|
||||
<a
|
||||
className="download"
|
||||
download={name}
|
||||
href={`${content}&download=true`}
|
||||
>
|
||||
<a className="download" download={name} href={`${content}&download=true`}>
|
||||
<IconDownload />
|
||||
</a>
|
||||
)}
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
const {
|
||||
channels,
|
||||
// input: channelInput,
|
||||
updateInput: updateChannelInput,
|
||||
updateInput: updateChannelInput
|
||||
} = useFilteredChannels();
|
||||
const { contacts, input, updateInput } = useFilteredUsers();
|
||||
// const { conactsData, loginUid } = useSelector((store) => {
|
||||
@@ -34,8 +34,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
const toggleCheck = ({ currentTarget }) => {
|
||||
const { id, type = "user" } = currentTarget.dataset;
|
||||
const ids = type == "user" ? selectedMembers : selectedChannels;
|
||||
const updateState =
|
||||
type == "user" ? setSelectedMembers : setSelectedChannels;
|
||||
const updateState = type == "user" ? setSelectedMembers : setSelectedChannels;
|
||||
let tmp = ids.includes(+id) ? ids.filter((m) => m != id) : [...ids, +id];
|
||||
console.log(id, currentTarget);
|
||||
updateState(tmp);
|
||||
@@ -47,13 +46,13 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
await forwardMessage({
|
||||
mids: mids.map((mid) => +mid),
|
||||
users: selectedMembers,
|
||||
channels: selectedChannels,
|
||||
channels: selectedChannels
|
||||
});
|
||||
if (appendText.trim()) {
|
||||
await sendMessages({
|
||||
content: appendText,
|
||||
users: selectedMembers,
|
||||
channels: selectedChannels,
|
||||
channels: selectedChannels
|
||||
});
|
||||
}
|
||||
toast.success("Forward Message Successfully");
|
||||
@@ -99,12 +98,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
className="user channel"
|
||||
onClick={toggleCheck}
|
||||
>
|
||||
<StyledCheckbox
|
||||
readOnly
|
||||
checked={checked}
|
||||
name="cb"
|
||||
id="cb"
|
||||
/>
|
||||
<StyledCheckbox readOnly checked={checked} name="cb" id="cb" />
|
||||
<Channel id={gid} interactive={false} />
|
||||
</li>
|
||||
);
|
||||
@@ -122,12 +116,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
className="user"
|
||||
onClick={toggleCheck}
|
||||
>
|
||||
<StyledCheckbox
|
||||
readOnly
|
||||
checked={checked}
|
||||
name="cb"
|
||||
id="cb"
|
||||
/>
|
||||
<StyledCheckbox readOnly checked={checked} name="cb" id="cb" />
|
||||
<Contact uid={uid} interactive={false} />
|
||||
</li>
|
||||
);
|
||||
@@ -162,10 +151,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
interactive={false}
|
||||
// avatarSize={40}
|
||||
/>
|
||||
<CloseIcon
|
||||
className="remove"
|
||||
onClick={removeSelected.bind(null, uid, "user")}
|
||||
/>
|
||||
<CloseIcon className="remove" onClick={removeSelected.bind(null, uid, "user")} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
@@ -185,11 +171,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
<Button onClick={closeModal} className="normal cancel">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
className="normal"
|
||||
disabled={sendButtonDisabled}
|
||||
onClick={handleForward}
|
||||
>
|
||||
<Button className="normal" disabled={sendButtonDisabled} onClick={handleForward}>
|
||||
Send To {selectedCount == 0 ? null : `(${selectedCount})`}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -58,11 +58,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function ImagePreviewModal({
|
||||
download = true,
|
||||
data,
|
||||
closeModal,
|
||||
}) {
|
||||
export default function ImagePreviewModal({ download = true, data, closeModal }) {
|
||||
const [url, setUrl] = useState(data?.thumbnail);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const wrapperRef = useRef();
|
||||
|
||||
@@ -38,28 +38,15 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
`;
|
||||
export default function InviteLink() {
|
||||
const {
|
||||
generating,
|
||||
link,
|
||||
linkCopied,
|
||||
copyLink,
|
||||
generateNewLink,
|
||||
} = useInviteLink();
|
||||
const { generating, link, linkCopied, copyLink, generateNewLink } = useInviteLink();
|
||||
const handleNewLink = () => {
|
||||
generateNewLink();
|
||||
};
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<span className="tip">
|
||||
Share this link to invite people to this server.
|
||||
</span>
|
||||
<span className="tip">Share this link to invite people to this server.</span>
|
||||
<div className="link">
|
||||
<Input
|
||||
readOnly
|
||||
className={"large"}
|
||||
placeholder="Generating"
|
||||
value={link}
|
||||
/>
|
||||
<Input readOnly className={"large"} placeholder="Generating" value={link} />
|
||||
<Button onClick={copyLink} className="ghost small border_less">
|
||||
{linkCopied ? "Copied" : `Copy`}
|
||||
</Button>
|
||||
|
||||
@@ -97,15 +97,12 @@ const Styled = styled.div`
|
||||
}
|
||||
`;
|
||||
export default function AddMembers({ cid = null, closeModal }) {
|
||||
const [
|
||||
addMembers,
|
||||
{ isLoading: isAdding, isSuccess },
|
||||
] = useAddMembersMutation();
|
||||
const [addMembers, { isLoading: isAdding, isSuccess }] = useAddMembersMutation();
|
||||
const [selects, setSelects] = useState([]);
|
||||
const { channel, contactData } = useSelector((store) => {
|
||||
return {
|
||||
channel: store.channels.byId[cid],
|
||||
contactData: store.contacts.byId,
|
||||
contactData: store.contacts.byId
|
||||
};
|
||||
});
|
||||
useEffect(() => {
|
||||
@@ -145,11 +142,7 @@ export default function AddMembers({ cid = null, closeModal }) {
|
||||
return (
|
||||
<li className="select" key={uid}>
|
||||
{contactData[uid].name}
|
||||
<CloseIcon
|
||||
data-uid={uid}
|
||||
onClick={toggleCheckMember}
|
||||
className="close"
|
||||
/>
|
||||
<CloseIcon data-uid={uid} onClick={toggleCheckMember} className="close" />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
@@ -185,11 +178,7 @@ export default function AddMembers({ cid = null, closeModal }) {
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<Button
|
||||
disabled={selects.length == 0 || isAdding}
|
||||
className="btn"
|
||||
onClick={handleAddMembers}
|
||||
>
|
||||
<Button disabled={selects.length == 0 || isAdding} className="btn" onClick={handleAddMembers}>
|
||||
{isAdding ? `Adding` : "Add"} to #{channel.name}
|
||||
</Button>
|
||||
</Styled>
|
||||
|
||||
@@ -68,14 +68,8 @@ import Button from "../styled/Button";
|
||||
import Input from "../styled/Input";
|
||||
export default function InviteByEmail({ cid = null }) {
|
||||
const [email, setEmail] = useState("");
|
||||
const {
|
||||
enableSMTP,
|
||||
linkCopied,
|
||||
link,
|
||||
copyLink,
|
||||
generateNewLink,
|
||||
generating,
|
||||
} = useInviteLink(cid);
|
||||
const { enableSMTP, linkCopied, link, copyLink, generateNewLink, generating } =
|
||||
useInviteLink(cid);
|
||||
useEffect(() => {
|
||||
if (linkCopied) {
|
||||
toast.success("Invite Link Copied!");
|
||||
@@ -104,12 +98,7 @@ export default function InviteByEmail({ cid = null }) {
|
||||
<div className="link">
|
||||
<label htmlFor="">Or Send invite link to your friends</label>
|
||||
<div className="input">
|
||||
<Input
|
||||
readOnly
|
||||
className="invite"
|
||||
placeholder="Generating"
|
||||
value={link}
|
||||
/>
|
||||
<Input readOnly className="invite" placeholder="Generating" value={link} />
|
||||
<button className="copy" onClick={copyLink}>
|
||||
Copy
|
||||
</button>
|
||||
|
||||
@@ -28,20 +28,14 @@ const Styled = styled.div`
|
||||
`;
|
||||
import Modal from "../Modal";
|
||||
// type: server,channel
|
||||
export default function InviteModal({
|
||||
type = "server",
|
||||
cid = null,
|
||||
title = "",
|
||||
closeModal,
|
||||
}) {
|
||||
export default function InviteModal({ type = "server", cid = null, title = "", closeModal }) {
|
||||
const { channel, server } = useSelector((store) => {
|
||||
return {
|
||||
channel: store.channels.byId[cid],
|
||||
server: store.server,
|
||||
server: store.server
|
||||
};
|
||||
});
|
||||
const finalTitle =
|
||||
type == "server" ? server.name : `#${title || channel?.name}`;
|
||||
const finalTitle = type == "server" ? server.name : `#${title || channel?.name}`;
|
||||
return (
|
||||
<Modal>
|
||||
<Styled>
|
||||
@@ -49,9 +43,7 @@ export default function InviteModal({
|
||||
Add friends to {finalTitle}
|
||||
<CloseIcon className="close" onClick={closeModal} />
|
||||
</h2>
|
||||
{!channel?.is_public && (
|
||||
<AddMembers cid={cid} closeModal={closeModal} />
|
||||
)}
|
||||
{!channel?.is_public && <AddMembers cid={cid} closeModal={closeModal} />}
|
||||
<InviteByEmail cid={channel?.is_public ? null : cid} />
|
||||
</Styled>
|
||||
</Modal>
|
||||
|
||||
@@ -30,10 +30,7 @@ export default function LeaveConfirmModal({ id, closeModal, handleNextStep }) {
|
||||
}
|
||||
buttons={
|
||||
<>
|
||||
<Button
|
||||
onClick={closeModal.bind(null, undefined)}
|
||||
className="cancel"
|
||||
>
|
||||
<Button onClick={closeModal.bind(null, undefined)} className="cancel">
|
||||
Cancel
|
||||
</Button>
|
||||
{isOwner ? (
|
||||
|
||||
@@ -28,11 +28,7 @@ const UserList = styled.ul`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function TransferOwnerModal({
|
||||
id,
|
||||
closeModal,
|
||||
withLeave = true,
|
||||
}) {
|
||||
export default function TransferOwnerModal({ id, closeModal, withLeave = true }) {
|
||||
const {
|
||||
transferOwner,
|
||||
otherMembers,
|
||||
@@ -40,7 +36,7 @@ export default function TransferOwnerModal({
|
||||
leaveChannel,
|
||||
leaveSuccess,
|
||||
transferSuccess,
|
||||
transfering,
|
||||
transfering
|
||||
} = useLeaveChannel(id);
|
||||
|
||||
const [uid, setUid] = useState(null);
|
||||
@@ -75,17 +71,10 @@ export default function TransferOwnerModal({
|
||||
description={"This cannot be undone."}
|
||||
buttons={
|
||||
<>
|
||||
<Button
|
||||
onClick={closeModal.bind(null, undefined)}
|
||||
className="cancel"
|
||||
>
|
||||
<Button onClick={closeModal.bind(null, undefined)} className="cancel">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={!uid}
|
||||
onClick={handleTransferAndLeave}
|
||||
className="danger"
|
||||
>
|
||||
<Button disabled={!uid} onClick={handleTransferAndLeave} className="danger">
|
||||
{operating ? "Assigning" : `Assign and Leave`}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@@ -2,22 +2,11 @@ import { useState } from "react";
|
||||
// import styled from "styled-components";
|
||||
import TransferOwnerModal from "./TransferOwnerModal";
|
||||
import LeaveConfirmModal from "./LeaveConfirmModal";
|
||||
export default function LeaveChannel({
|
||||
id = null,
|
||||
isOwner = false,
|
||||
closeModal,
|
||||
}) {
|
||||
export default function LeaveChannel({ id = null, isOwner = false, closeModal }) {
|
||||
const [transferOwner, setTransferOwner] = useState(isOwner);
|
||||
const handleNextStep = () => {
|
||||
setTransferOwner(true);
|
||||
};
|
||||
if (transferOwner)
|
||||
return <TransferOwnerModal id={id} closeModal={closeModal} />;
|
||||
return (
|
||||
<LeaveConfirmModal
|
||||
id={id}
|
||||
closeModal={closeModal}
|
||||
handleNextStep={handleNextStep}
|
||||
/>
|
||||
);
|
||||
if (transferOwner) return <TransferOwnerModal id={id} closeModal={closeModal} />;
|
||||
return <LeaveConfirmModal id={id} closeModal={closeModal} handleNextStep={handleNextStep} />;
|
||||
}
|
||||
|
||||
@@ -45,17 +45,8 @@ export default function Loading({ reload = false, fullscreen = false }) {
|
||||
};
|
||||
return (
|
||||
<StyledWrapper className={fullscreen ? "fullscreen" : ""}>
|
||||
<Ring
|
||||
className="loading"
|
||||
size={40}
|
||||
lineWeight={5}
|
||||
speed={2}
|
||||
color="black"
|
||||
/>
|
||||
<Button
|
||||
className={`reload danger ${reload ? "visible" : ""}`}
|
||||
onClick={handleReload}
|
||||
>
|
||||
<Ring className="loading" size={40} lineWeight={5} speed={2} color="black" />
|
||||
<Button className={`reload danger ${reload ? "visible" : ""}`} onClick={handleReload}>
|
||||
Reload
|
||||
</Button>
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -119,20 +119,12 @@ export default function ManageMembers({ cid = null }) {
|
||||
return {
|
||||
contacts: store.contacts,
|
||||
channels: store.channels,
|
||||
loginUser: store.contacts.byId[store.authData.uid],
|
||||
loginUser: store.contacts.byId[store.authData.uid]
|
||||
};
|
||||
});
|
||||
const {
|
||||
copyEmail,
|
||||
removeFromChannel,
|
||||
removeUser,
|
||||
canRemove,
|
||||
canRemoveFromChannel,
|
||||
} = useContactOperation({ cid });
|
||||
const [
|
||||
updateContact,
|
||||
{ isSuccess: updateSuccess },
|
||||
] = useUpdateContactMutation();
|
||||
const { copyEmail, removeFromChannel, removeUser, canRemove, canRemoveFromChannel } =
|
||||
useContactOperation({ cid });
|
||||
const [updateContact, { isSuccess: updateSuccess }] = useUpdateContactMutation();
|
||||
|
||||
useEffect(() => {
|
||||
if (updateSuccess) {
|
||||
@@ -146,11 +138,7 @@ export default function ManageMembers({ cid = null }) {
|
||||
updateContact({ id: uid, is_admin: isAdmin });
|
||||
};
|
||||
const channel = channels.byId[cid] ?? null;
|
||||
const uids = channel
|
||||
? channel.is_public
|
||||
? contacts.ids
|
||||
: channel.members
|
||||
: contacts.ids;
|
||||
const uids = channel ? (channel.is_public ? contacts.ids : channel.members) : contacts.ids;
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
@@ -158,8 +146,7 @@ export default function ManageMembers({ cid = null }) {
|
||||
<div className="intro">
|
||||
<h4 className="title">Manage Members</h4>
|
||||
<p className="desc">
|
||||
Disabling your account means you can recover it at any time after
|
||||
taking this action.
|
||||
Disabling your account means you can recover it at any time after taking this action.
|
||||
</p>
|
||||
</div>
|
||||
<ul className="members">
|
||||
@@ -194,7 +181,7 @@ export default function ManageMembers({ cid = null }) {
|
||||
onClick={handleToggleRole.bind(null, {
|
||||
ignore: is_admin,
|
||||
uid,
|
||||
isAdmin: true,
|
||||
isAdmin: true
|
||||
})}
|
||||
>
|
||||
Admin
|
||||
@@ -205,7 +192,7 @@ export default function ManageMembers({ cid = null }) {
|
||||
onClick={handleToggleRole.bind(null, {
|
||||
ignore: !is_admin,
|
||||
uid,
|
||||
isAdmin: false,
|
||||
isAdmin: false
|
||||
})}
|
||||
>
|
||||
User
|
||||
@@ -226,10 +213,7 @@ export default function ManageMembers({ cid = null }) {
|
||||
content={
|
||||
<StyledMenu className="menu">
|
||||
{email && (
|
||||
<li
|
||||
className="item"
|
||||
onClick={copyEmail.bind(null, email)}
|
||||
>
|
||||
<li className="item" onClick={copyEmail.bind(null, email)}>
|
||||
Copy Email
|
||||
</li>
|
||||
)}
|
||||
@@ -237,18 +221,12 @@ export default function ManageMembers({ cid = null }) {
|
||||
{/* <li className="item underline">Change Nickname</li> */}
|
||||
{/* <li className="item danger">Ban</li> */}
|
||||
{canRemoveFromChannel && (
|
||||
<li
|
||||
className="item danger"
|
||||
onClick={removeFromChannel.bind(null, uid)}
|
||||
>
|
||||
<li className="item danger" onClick={removeFromChannel.bind(null, uid)}>
|
||||
Remove From Channel
|
||||
</li>
|
||||
)}
|
||||
{canRemove && !cid && (
|
||||
<li
|
||||
className="item danger"
|
||||
onClick={removeUser.bind(null, uid)}
|
||||
>
|
||||
<li className="item danger" onClick={removeUser.bind(null, uid)}>
|
||||
Remove From Server
|
||||
</li>
|
||||
)}
|
||||
|
||||
@@ -11,6 +11,6 @@ export default function usePrompt() {
|
||||
return {
|
||||
setCanneled: setPrompt,
|
||||
prompted: !!localStorage.getItem(KEY_PWA_INSTALLED),
|
||||
resetPrompt,
|
||||
resetPrompt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ function MarkdownEditor({
|
||||
height = "50vh",
|
||||
placeholder,
|
||||
sendMarkdown,
|
||||
setEditorInstance,
|
||||
setEditorInstance
|
||||
}) {
|
||||
const editorRef = useRef(undefined);
|
||||
const { uploadFile } = useUploadFile();
|
||||
|
||||
@@ -62,12 +62,7 @@ const StyledCmds = styled.ul`
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
`;
|
||||
export default function Commands({
|
||||
context = "user",
|
||||
contextId = 0,
|
||||
mid = 0,
|
||||
toggleEditMessage,
|
||||
}) {
|
||||
export default function Commands({ context = "user", contextId = 0, mid = 0, toggleEditMessage }) {
|
||||
const {
|
||||
canDelete,
|
||||
canReply,
|
||||
@@ -80,11 +75,11 @@ export default function Commands({
|
||||
togglePinModal,
|
||||
PinModal,
|
||||
DeleteModal,
|
||||
ForwardModal,
|
||||
ForwardModal
|
||||
} = useMessageOperation({ mid, context, contextId });
|
||||
const { setReplying } = useSendMessage({ context, to: contextId });
|
||||
const { addFavorite, isFavorited } = useFavMessage({
|
||||
cid: context == "channel" ? contextId : null,
|
||||
cid: context == "channel" ? contextId : null
|
||||
});
|
||||
const dispatch = useDispatch();
|
||||
const [tippyVisible, setTippyVisible] = useState(false);
|
||||
@@ -126,10 +121,7 @@ export default function Commands({
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledCmds
|
||||
ref={cmdsRef}
|
||||
className={`cmds ${tippyVisible ? "visible" : ""}`}
|
||||
>
|
||||
<StyledCmds ref={cmdsRef} className={`cmds ${tippyVisible ? "visible" : ""}`}>
|
||||
<Tippy
|
||||
onShow={handleTippyVisible.bind(null, true)}
|
||||
onHide={handleTippyVisible.bind(null, false)}
|
||||
@@ -175,25 +167,25 @@ export default function Commands({
|
||||
canPin && {
|
||||
title: pinned ? `Unpin Message` : `Pin Message`,
|
||||
icon: <IconPin className="icon" />,
|
||||
handler: pinned ? handleUnpin : togglePinModal,
|
||||
handler: pinned ? handleUnpin : togglePinModal
|
||||
},
|
||||
{
|
||||
title: "Forward",
|
||||
icon: <IconForward className="icon" />,
|
||||
handler: toggleForwardModal,
|
||||
handler: toggleForwardModal
|
||||
},
|
||||
{
|
||||
title: "Select",
|
||||
icon: <IconSelect className="icon" />,
|
||||
handler: handleSelect.bind(null, mid),
|
||||
handler: handleSelect.bind(null, mid)
|
||||
},
|
||||
|
||||
canDelete && {
|
||||
title: " Delete",
|
||||
danger: true,
|
||||
icon: <IconDelete className="icon" />,
|
||||
handler: toggleDeleteModal,
|
||||
},
|
||||
handler: toggleDeleteModal
|
||||
}
|
||||
]}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function MessageContextMenu({
|
||||
visible,
|
||||
hide,
|
||||
editMessage,
|
||||
children,
|
||||
children
|
||||
}) {
|
||||
const {
|
||||
copyContent,
|
||||
@@ -37,7 +37,7 @@ export default function MessageContextMenu({
|
||||
togglePinModal,
|
||||
PinModal,
|
||||
ForwardModal,
|
||||
DeleteModal,
|
||||
DeleteModal
|
||||
} = useMessageOperation({ mid, contextId, context });
|
||||
const dispatch = useDispatch();
|
||||
const { setReplying } = useSendMessage({ context, to: contextId });
|
||||
@@ -71,39 +71,39 @@ export default function MessageContextMenu({
|
||||
canEdit && {
|
||||
title: "Edit Message",
|
||||
icon: <IconEdit className="icon" />,
|
||||
handler: editMessage,
|
||||
handler: editMessage
|
||||
},
|
||||
canReply && {
|
||||
title: "Reply",
|
||||
icon: <IconReply className="icon" />,
|
||||
handler: handleReply,
|
||||
handler: handleReply
|
||||
},
|
||||
canCopy && {
|
||||
title: "Copy",
|
||||
icon: <IconCopy className="icon" />,
|
||||
handler: copyContent,
|
||||
handler: copyContent
|
||||
},
|
||||
canPin && {
|
||||
title: pinned ? "Unpin" : "Pin",
|
||||
icon: <IconPin className="icon" />,
|
||||
handler: pinned ? unPin.bind(null, mid) : togglePinModal,
|
||||
handler: pinned ? unPin.bind(null, mid) : togglePinModal
|
||||
},
|
||||
{
|
||||
title: "Forward",
|
||||
icon: <IconForward className="icon" />,
|
||||
handler: toggleForwardModal,
|
||||
handler: toggleForwardModal
|
||||
},
|
||||
{
|
||||
title: "Select",
|
||||
icon: <IconSelect className="icon" />,
|
||||
handler: handleSelect,
|
||||
handler: handleSelect
|
||||
},
|
||||
canDelete && {
|
||||
title: "Delete",
|
||||
danger: true,
|
||||
icon: <IconDelete className="icon" />,
|
||||
handler: toggleDeleteModal,
|
||||
},
|
||||
handler: toggleDeleteModal
|
||||
}
|
||||
]}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export default function EditMessage({ mid, cancelEdit }) {
|
||||
edit({
|
||||
mid,
|
||||
content: currMsg,
|
||||
type: msg.content_type == ContentTypes.markdown ? "markdown" : "text",
|
||||
type: msg.content_type == ContentTypes.markdown ? "markdown" : "text"
|
||||
});
|
||||
};
|
||||
if (!msg) return null;
|
||||
@@ -121,8 +121,7 @@ export default function EditMessage({ mid, cancelEdit }) {
|
||||
esc to <button onClick={cancelEdit}>cancel</button>
|
||||
</span>
|
||||
<span className="opt">
|
||||
enter to{" "}
|
||||
<button onClick={handleSave}>{isEditing ? "saving" : `save`}</button>
|
||||
enter to <button onClick={handleSave}>{isEditing ? "saving" : `save`}</button>
|
||||
</span>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -23,20 +23,10 @@ const FavoritedMessage = ({ id }) => {
|
||||
const favorite_mids = messages.map(({ from_mid }) => from_mid) || [];
|
||||
|
||||
setMsgs(
|
||||
<StyledFav
|
||||
data-favorite-mids={favorite_mids.join(",")}
|
||||
className="favorite"
|
||||
>
|
||||
<StyledFav data-favorite-mids={favorite_mids.join(",")} className="favorite">
|
||||
<div className="list">
|
||||
{messages.map((msg, idx) => {
|
||||
const {
|
||||
user = {},
|
||||
download,
|
||||
content,
|
||||
content_type,
|
||||
properties,
|
||||
thumbnail,
|
||||
} = msg;
|
||||
const { user = {}, download, content, content_type, properties, thumbnail } = msg;
|
||||
return (
|
||||
<StyledMsg className="archive" key={idx}>
|
||||
{user && (
|
||||
@@ -54,7 +44,7 @@ const FavoritedMessage = ({ id }) => {
|
||||
content,
|
||||
content_type,
|
||||
properties,
|
||||
thumbnail,
|
||||
thumbnail
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,7 @@ const ForwardedMessage = ({ context, to, from_uid, id }) => {
|
||||
content,
|
||||
content_type,
|
||||
properties,
|
||||
thumbnail,
|
||||
thumbnail
|
||||
} = msg;
|
||||
return (
|
||||
<StyledMsg className="archive" key={idx}>
|
||||
@@ -78,7 +78,7 @@ const ForwardedMessage = ({ context, to, from_uid, id }) => {
|
||||
content,
|
||||
content_type,
|
||||
properties,
|
||||
thumbnail,
|
||||
thumbnail
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,14 +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,
|
||||
thumbnail,
|
||||
properties,
|
||||
} = msg;
|
||||
const { from_uid, created_at, content_type, content, thumbnail, properties } = msg;
|
||||
const { name, avatar } = contactsData[from_uid];
|
||||
return (
|
||||
<StyledWrapper className={`preview`}>
|
||||
@@ -26,9 +19,7 @@ export default function PreviewMessage({ mid = 0 }) {
|
||||
<div className="details">
|
||||
<div className="up">
|
||||
<span className="name">{name}</span>
|
||||
<i className="time">
|
||||
{dayjs(created_at).format("YYYY-MM-DD h:mm:ss A")}
|
||||
</i>
|
||||
<i className="time">{dayjs(created_at).format("YYYY-MM-DD h:mm:ss A")}</i>
|
||||
</div>
|
||||
<div className={`down`}>
|
||||
{renderContent({
|
||||
@@ -36,7 +27,7 @@ export default function PreviewMessage({ mid = 0 }) {
|
||||
content,
|
||||
thumbnail,
|
||||
from_uid,
|
||||
properties,
|
||||
properties
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -70,8 +70,7 @@ const StyledDetails = styled.div`
|
||||
position: relative;
|
||||
background: #ffffff;
|
||||
border-radius: var(--br);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08),
|
||||
0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
@@ -132,7 +131,7 @@ export default function Reaction({ mid, reactions = null }) {
|
||||
const [reactWithEmoji] = useReactMessageMutation();
|
||||
const { currUid } = useSelector((store) => {
|
||||
return {
|
||||
currUid: store.authData.uid,
|
||||
currUid: store.authData.uid
|
||||
};
|
||||
});
|
||||
const handleReact = (emoji) => {
|
||||
@@ -156,18 +155,14 @@ export default function Reaction({ mid, reactions = null }) {
|
||||
// visible={true}
|
||||
interactive
|
||||
placement="top"
|
||||
content={
|
||||
<ReactionDetails uids={uids} emoji={reaction} index={idx} />
|
||||
}
|
||||
content={<ReactionDetails uids={uids} emoji={reaction} index={idx} />}
|
||||
>
|
||||
<i className="emoji">
|
||||
<ReactionItem native={reaction} />
|
||||
</i>
|
||||
</Tippy>
|
||||
|
||||
{uids.length > 1 ? (
|
||||
<em className="count">{`${uids.length}`} </em>
|
||||
) : null}
|
||||
{uids.length > 1 ? <em className="count">{`${uids.length}`} </em> : null}
|
||||
</span>
|
||||
) : null;
|
||||
})}
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function ReactionPicker({ mid, hidePicker }) {
|
||||
const { reactionData, currUid } = useSelector((store) => {
|
||||
return {
|
||||
reactionData: store.reactionMessage[mid] || {},
|
||||
currUid: store.authData.uid,
|
||||
currUid: store.authData.uid
|
||||
};
|
||||
});
|
||||
// useOutsideClick(wrapperRef, hidePicker);
|
||||
@@ -55,8 +55,7 @@ export default function ReactionPicker({ mid, hidePicker }) {
|
||||
<ul className={`emojis ${isLoading ? "reacting" : ""}`}>
|
||||
{Emojis.map((emoji) => {
|
||||
let reacted =
|
||||
reactionData[emoji] &&
|
||||
reactionData[emoji].findIndex((id) => id == currUid) > -1;
|
||||
reactionData[emoji] && reactionData[emoji].findIndex((id) => id == currUid) > -1;
|
||||
|
||||
return (
|
||||
<li
|
||||
|
||||
@@ -66,11 +66,7 @@ const Styled = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: "";
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0) 63.54%,
|
||||
#e5e7eb 93.09%
|
||||
);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 63.54%, #e5e7eb 93.09%);
|
||||
}
|
||||
}
|
||||
.pic {
|
||||
|
||||
@@ -26,26 +26,24 @@ function Message({
|
||||
mid = "",
|
||||
context = "user",
|
||||
updateReadIndex,
|
||||
read = true,
|
||||
read = true
|
||||
}) {
|
||||
const {
|
||||
visible: contextMenuVisible,
|
||||
handleContextMenuEvent,
|
||||
hideContextMenu,
|
||||
} = useContextMenu();
|
||||
const { visible: contextMenuVisible, handleContextMenuEvent, hideContextMenu } = useContextMenu();
|
||||
const inviewRef = useInView();
|
||||
const [edit, setEdit] = useState(false);
|
||||
const avatarRef = useRef(null);
|
||||
const { getPinInfo } = usePinMessage(context == "channel" ? contextId : null);
|
||||
const { message = {}, reactionMessageData, contactsData } = useSelector(
|
||||
(store) => {
|
||||
return {
|
||||
reactionMessageData: store.reactionMessage,
|
||||
message: store.message[mid] || {},
|
||||
contactsData: store.contacts.byId,
|
||||
};
|
||||
}
|
||||
);
|
||||
const {
|
||||
message = {},
|
||||
reactionMessageData,
|
||||
contactsData
|
||||
} = useSelector((store) => {
|
||||
return {
|
||||
reactionMessageData: store.reactionMessage,
|
||||
message: store.message[mid] || {},
|
||||
contactsData: store.contacts.byId
|
||||
};
|
||||
});
|
||||
|
||||
const toggleEditMessage = () => {
|
||||
setEdit((prev) => !prev);
|
||||
@@ -61,7 +59,7 @@ function Message({
|
||||
download,
|
||||
content_type = "text/plain",
|
||||
edited,
|
||||
properties,
|
||||
properties
|
||||
} = message;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -79,11 +77,7 @@ function Message({
|
||||
// if (!message) return null;
|
||||
let timePrefix = null;
|
||||
const dayjsTime = dayjs(time);
|
||||
timePrefix = dayjsTime.isToday()
|
||||
? "Today"
|
||||
: dayjsTime.isYesterday()
|
||||
? "Yesterday"
|
||||
: null;
|
||||
timePrefix = dayjsTime.isToday() ? "Today" : dayjsTime.isYesterday() ? "Yesterday" : null;
|
||||
|
||||
const pinInfo = getPinInfo(mid);
|
||||
// return null;
|
||||
@@ -94,9 +88,9 @@ function Message({
|
||||
onContextMenu={handleContextMenuEvent}
|
||||
data-msg-mid={mid}
|
||||
ref={inviewRef}
|
||||
className={`message ${readOnly ? "readonly" : ""} ${
|
||||
pinInfo ? "pinned" : ""
|
||||
} ${contextMenuVisible ? "contextVisible" : ""} `}
|
||||
className={`message ${readOnly ? "readonly" : ""} ${pinInfo ? "pinned" : ""} ${
|
||||
contextMenuVisible ? "contextVisible" : ""
|
||||
} `}
|
||||
>
|
||||
<Tippy
|
||||
key={_key}
|
||||
@@ -153,7 +147,7 @@ function Message({
|
||||
content,
|
||||
thumbnail,
|
||||
download,
|
||||
edited,
|
||||
edited
|
||||
})
|
||||
)}
|
||||
{reactions && <Reaction mid={mid} reactions={reactions} />}
|
||||
|
||||
@@ -20,7 +20,7 @@ const renderContent = ({
|
||||
content,
|
||||
download,
|
||||
thumbnail,
|
||||
edited = false,
|
||||
edited = false
|
||||
}) => {
|
||||
let ctn = null;
|
||||
switch (content_type) {
|
||||
@@ -30,38 +30,23 @@ const renderContent = ({
|
||||
<Linkit
|
||||
componentDecorator={(decoratedHref, decoratedText, key) => (
|
||||
<React.Fragment key={key}>
|
||||
<a
|
||||
className="link"
|
||||
target="_blank"
|
||||
href={decoratedHref}
|
||||
key={key}
|
||||
rel="noreferrer"
|
||||
>
|
||||
<a className="link" target="_blank" href={decoratedHref} key={key} rel="noreferrer">
|
||||
{decoratedText}
|
||||
</a>
|
||||
{!decoratedHref.startsWith("mailto") && (
|
||||
<URLPreview url={decoratedHref} />
|
||||
)}
|
||||
{!decoratedHref.startsWith("mailto") && <URLPreview url={decoratedHref} />}
|
||||
</React.Fragment>
|
||||
)}
|
||||
>
|
||||
{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} cid={to} />;
|
||||
}
|
||||
)}
|
||||
{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} cid={to} />;
|
||||
})}
|
||||
{/* {content.replace(/\s{1}\@[1-9]+\s{1}/g,)} */}
|
||||
{/* {new RegExp(/\s{1}\@[1-9]+\s{1}/g).exec(content)} */}
|
||||
</Linkit>
|
||||
{edited && (
|
||||
<span
|
||||
className="edited"
|
||||
title={dayjs(edited).format("YYYY-MM-DD h:mm:ss A")}
|
||||
>
|
||||
<span className="edited" title={dayjs(edited).format("YYYY-MM-DD h:mm:ss A")}>
|
||||
(edited)
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -10,17 +10,15 @@ import usePinMessage from "../../hook/usePinMessage";
|
||||
|
||||
export default function useMessageOperation({ mid, context, contextId }) {
|
||||
const { copy } = useCopy();
|
||||
const { content_type, properties, 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,
|
||||
properties: store.message[mid]?.properties,
|
||||
currUid: store.authData.uid,
|
||||
};
|
||||
}
|
||||
);
|
||||
const { content_type, properties, 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,
|
||||
properties: store.message[mid]?.properties,
|
||||
currUid: store.authData.uid
|
||||
};
|
||||
});
|
||||
const { canPin, pins, unpinMessage, isUnpinSuccess } = usePinMessage(
|
||||
context == "channel" ? contextId : undefined
|
||||
);
|
||||
@@ -71,16 +69,11 @@ export default function useMessageOperation({ mid, context, contextId }) {
|
||||
properties?.content_type &&
|
||||
properties?.content_type.startsWith("image");
|
||||
const enableEdit =
|
||||
currUid == from_uid &&
|
||||
[ContentTypes.text, ContentTypes.markdown].includes(content_type);
|
||||
currUid == from_uid && [ContentTypes.text, ContentTypes.markdown].includes(content_type);
|
||||
const canDelete = currUid == from_uid;
|
||||
const canCopy =
|
||||
[ContentTypes.text, ContentTypes.markdown].includes(content_type) ||
|
||||
isImage;
|
||||
const canCopy = [ContentTypes.text, ContentTypes.markdown].includes(content_type) || isImage;
|
||||
return {
|
||||
copyContent: isImage
|
||||
? copyContent.bind(null, true)
|
||||
: copyContent.bind(null, false),
|
||||
copyContent: isImage ? copyContent.bind(null, true) : copyContent.bind(null, false),
|
||||
canCopy,
|
||||
isImage,
|
||||
isMarkdown: content_type == ContentTypes.markdown,
|
||||
@@ -101,6 +94,6 @@ export default function useMessageOperation({ mid, context, contextId }) {
|
||||
) : null,
|
||||
PinModal: pinModalVisible ? (
|
||||
<PinMessageModal mid={mid} gid={contextId} closeModal={togglePinModal} />
|
||||
) : null,
|
||||
) : null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ export const CONFIG = {
|
||||
editableProps: {
|
||||
spellCheck: false,
|
||||
autoFocus: true,
|
||||
placeholder: "Type…",
|
||||
placeholder: "Type…"
|
||||
},
|
||||
trailingBlock: { type: ELEMENT_PARAGRAPH },
|
||||
softBreak: {
|
||||
@@ -13,11 +13,11 @@ export const CONFIG = {
|
||||
{
|
||||
hotkey: "shift+enter",
|
||||
query: {
|
||||
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
exitBreak: {
|
||||
options: {
|
||||
@@ -25,9 +25,9 @@ export const CONFIG = {
|
||||
{
|
||||
hotkey: "mod+enter",
|
||||
query: {
|
||||
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH],
|
||||
},
|
||||
},
|
||||
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH]
|
||||
}
|
||||
}
|
||||
// {
|
||||
// hotkey: "mod+shift+enter",
|
||||
// before: true,
|
||||
@@ -40,14 +40,14 @@ export const CONFIG = {
|
||||
// allow: KEYS_HEADING,
|
||||
// },
|
||||
// },
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
selectOnBackspace: {
|
||||
options: {
|
||||
query: {
|
||||
allow: [ELEMENT_IMAGE],
|
||||
},
|
||||
},
|
||||
},
|
||||
allow: [ELEMENT_IMAGE]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
// usePlateEditorRef,
|
||||
// ELEMENT_IMAGE,
|
||||
// useComboboxControls,
|
||||
MentionCombobox,
|
||||
MentionCombobox
|
||||
} from "@udecode/plate";
|
||||
import { createComboboxPlugin } from "@udecode/plate-combobox";
|
||||
import { ReactEditor } from "slate-react";
|
||||
@@ -44,7 +44,7 @@ const Plugins = ({
|
||||
id = "",
|
||||
placeholder = "Write some markdown...",
|
||||
sendMessages,
|
||||
members = [],
|
||||
members = []
|
||||
}) => {
|
||||
// const { getMenuProps, getItemProps } = useComboboxControls();
|
||||
const [context, to] = id.split("_");
|
||||
@@ -57,7 +57,7 @@ const Plugins = ({
|
||||
const initialProps = {
|
||||
...CONFIG.editableProps,
|
||||
className: "box",
|
||||
placeholder,
|
||||
placeholder
|
||||
};
|
||||
const plateEditor = getPlateEditorRef(`${TEXT_EDITOR_PREFIX}_${id}`);
|
||||
useEffect(() => {
|
||||
@@ -91,13 +91,7 @@ const Plugins = ({
|
||||
(evt) => {
|
||||
// 是否在at操作
|
||||
const mentionInputs = findMentionInput(plateEditor);
|
||||
if (
|
||||
mentionInputs ||
|
||||
evt.shiftKey ||
|
||||
evt.ctrlKey ||
|
||||
evt.altKey ||
|
||||
evt.isComposing
|
||||
) {
|
||||
if (mentionInputs || evt.shiftKey || evt.ctrlKey || evt.altKey || evt.isComposing) {
|
||||
return true;
|
||||
}
|
||||
evt.preventDefault();
|
||||
@@ -107,14 +101,14 @@ const Plugins = ({
|
||||
Transforms.delete(plateEditor, {
|
||||
at: {
|
||||
anchor: Editor.start(plateEditor, []),
|
||||
focus: Editor.end(plateEditor, []),
|
||||
},
|
||||
focus: Editor.end(plateEditor, [])
|
||||
}
|
||||
});
|
||||
},
|
||||
{
|
||||
// eventTypes: ["keydown"],
|
||||
target: editableRef,
|
||||
when: !cmdKey,
|
||||
when: !cmdKey
|
||||
}
|
||||
);
|
||||
useKey(
|
||||
@@ -125,7 +119,7 @@ const Plugins = ({
|
||||
},
|
||||
{
|
||||
eventTypes: ["keydown", "keyup"],
|
||||
target: editableRef,
|
||||
target: editableRef
|
||||
}
|
||||
);
|
||||
const pluginArr = [
|
||||
@@ -133,7 +127,7 @@ const Plugins = ({
|
||||
createNodeIdPlugin(),
|
||||
createSoftBreakPlugin(CONFIG.softBreak),
|
||||
createTrailingBlockPlugin(CONFIG.trailingBlock),
|
||||
createExitBreakPlugin(CONFIG.exitBreak),
|
||||
createExitBreakPlugin(CONFIG.exitBreak)
|
||||
];
|
||||
const plugins = createPlugins(
|
||||
enableMentions
|
||||
@@ -145,17 +139,17 @@ const Plugins = ({
|
||||
console.log("mention", item);
|
||||
const {
|
||||
text,
|
||||
data: { uid },
|
||||
data: { uid }
|
||||
} = item;
|
||||
return { value: `@${text}`, uid };
|
||||
},
|
||||
insertSpaceAfterMention: true,
|
||||
},
|
||||
}),
|
||||
insertSpaceAfterMention: true
|
||||
}
|
||||
})
|
||||
])
|
||||
: pluginArr,
|
||||
{
|
||||
components,
|
||||
components
|
||||
}
|
||||
);
|
||||
|
||||
@@ -179,20 +173,16 @@ const Plugins = ({
|
||||
const { value, mentions } = getMixedText(v.children);
|
||||
const prev = tmps[tmps.length - 1];
|
||||
if (!prev) {
|
||||
tmps.push([
|
||||
{ type: "text", content: value, properties: { mentions } },
|
||||
]);
|
||||
tmps.push([{ type: "text", content: value, properties: { mentions } }]);
|
||||
} else {
|
||||
if (Array.isArray(prev)) {
|
||||
tmps[tmps.length - 1].push({
|
||||
type: "text",
|
||||
content: value,
|
||||
properties: { mentions },
|
||||
properties: { mentions }
|
||||
});
|
||||
} else {
|
||||
tmps.push([
|
||||
{ type: "text", content: value, properties: { mentions } },
|
||||
]);
|
||||
tmps.push([{ type: "text", content: value, properties: { mentions } }]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,8 +192,8 @@ const Plugins = ({
|
||||
type: "text",
|
||||
content: tmp.map((t) => t.content).join("\n"),
|
||||
properties: {
|
||||
mentions: tmp.map((t) => t.properties?.mentions || []).flat(),
|
||||
},
|
||||
mentions: tmp.map((t) => t.properties?.mentions || []).flat()
|
||||
}
|
||||
}
|
||||
: tmp;
|
||||
});
|
||||
@@ -229,13 +219,7 @@ const Plugins = ({
|
||||
// component={StyledCombobox}
|
||||
onRenderItem={({ item }) => {
|
||||
console.log("wtf", item);
|
||||
return (
|
||||
<Contact
|
||||
key={item.data.uid}
|
||||
uid={item.data.uid}
|
||||
interactive={false}
|
||||
/>
|
||||
);
|
||||
return <Contact key={item.data.uid} uid={item.data.uid} interactive={false} />;
|
||||
}}
|
||||
items={members.map((id) => {
|
||||
const data = contactData[id];
|
||||
@@ -246,8 +230,8 @@ const Plugins = ({
|
||||
text: name,
|
||||
data: {
|
||||
uid,
|
||||
...rest,
|
||||
},
|
||||
...rest
|
||||
}
|
||||
};
|
||||
})}
|
||||
/>
|
||||
@@ -273,7 +257,7 @@ export const useMixedEditor = (key) => {
|
||||
};
|
||||
return {
|
||||
focus,
|
||||
insertText,
|
||||
insertText
|
||||
};
|
||||
};
|
||||
export default Plugins;
|
||||
|
||||
@@ -2,12 +2,12 @@ import {
|
||||
createBasicElementsPlugin,
|
||||
createImagePlugin,
|
||||
createParagraphPlugin,
|
||||
createSelectOnBackspacePlugin,
|
||||
createSelectOnBackspacePlugin
|
||||
} from "@udecode/plate";
|
||||
import { CONFIG } from "./config";
|
||||
|
||||
const basicElements = [
|
||||
createParagraphPlugin(), // paragraph element
|
||||
createParagraphPlugin() // paragraph element
|
||||
];
|
||||
|
||||
export const PLUGINS = {
|
||||
@@ -16,6 +16,6 @@ export const PLUGINS = {
|
||||
image: [
|
||||
createBasicElementsPlugin(),
|
||||
createImagePlugin(),
|
||||
createSelectOnBackspacePlugin(CONFIG.selectOnBackspace),
|
||||
],
|
||||
createSelectOnBackspacePlugin(CONFIG.selectOnBackspace)
|
||||
]
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const nodeTypes = {
|
||||
paragraph: "p",
|
||||
image: "img",
|
||||
image: "img"
|
||||
};
|
||||
|
||||
export default nodeTypes;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import {
|
||||
ELEMENT_PARAGRAPH,
|
||||
ELEMENT_PARAGRAPH
|
||||
// TElement,
|
||||
} from "@udecode/plate";
|
||||
// import { Text } from 'slate'
|
||||
|
||||
export const createElement = (
|
||||
text = "",
|
||||
{ type = ELEMENT_PARAGRAPH, mark } = {}
|
||||
) => {
|
||||
export const createElement = (text = "", { type = ELEMENT_PARAGRAPH, mark } = {}) => {
|
||||
const leaf = { text };
|
||||
if (mark) {
|
||||
leaf[mark] = true;
|
||||
@@ -15,7 +12,7 @@ export const createElement = (
|
||||
|
||||
return {
|
||||
type,
|
||||
children: [leaf],
|
||||
children: [leaf]
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ const Styled = styled.div`
|
||||
}
|
||||
`;
|
||||
export default function MrakdownRender({ content }) {
|
||||
const mdContainer = useRef(null);
|
||||
const mdContainer = useRef(undefined);
|
||||
const [previewImage, setPreviewImage] = useState(null);
|
||||
useEffect(() => {
|
||||
if (mdContainer) {
|
||||
const container = mdContainer.current;
|
||||
const container = mdContainer?.current;
|
||||
if (container) {
|
||||
// 点击查看大图
|
||||
container.addEventListener(
|
||||
"click",
|
||||
@@ -56,11 +56,7 @@ export default function MrakdownRender({ content }) {
|
||||
return (
|
||||
<>
|
||||
{previewImage && (
|
||||
<ImagePreviewModal
|
||||
download={false}
|
||||
data={previewImage}
|
||||
closeModal={closePreviewModal}
|
||||
/>
|
||||
<ImagePreviewModal download={false} data={previewImage} closeModal={closePreviewModal} />
|
||||
)}
|
||||
<Styled ref={mdContainer}>
|
||||
<Viewer
|
||||
|
||||
@@ -21,15 +21,9 @@ const Notification = () => {
|
||||
navigateTo(newPath);
|
||||
};
|
||||
// https only
|
||||
navigator.serviceWorker?.addEventListener(
|
||||
"message",
|
||||
handleServiceworkerMessage
|
||||
);
|
||||
navigator.serviceWorker?.addEventListener("message", handleServiceworkerMessage);
|
||||
return () => {
|
||||
navigator.serviceWorker?.removeEventListener(
|
||||
"message",
|
||||
handleServiceworkerMessage
|
||||
);
|
||||
navigator.serviceWorker?.removeEventListener("message", handleServiceworkerMessage);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const useDeviceToken = (vapidKey) => {
|
||||
const messaging = getMessaging(initializeApp(firebaseConfig));
|
||||
|
||||
getToken(messaging, {
|
||||
vapidKey,
|
||||
vapidKey
|
||||
})
|
||||
.then((currentToken) => {
|
||||
if (currentToken) {
|
||||
@@ -19,9 +19,7 @@ const useDeviceToken = (vapidKey) => {
|
||||
// Perform any other neccessary action with the token
|
||||
} else {
|
||||
// Show permission request UI
|
||||
console.log(
|
||||
"No registration token available. Request permission to generate one."
|
||||
);
|
||||
console.log("No registration token available. Request permission to generate one.");
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
@@ -21,12 +21,12 @@ export default function Profile({ uid = null, type = "embed", cid = null }) {
|
||||
removeFromChannel,
|
||||
canRemoveFromChannel,
|
||||
canRemove,
|
||||
removeUser,
|
||||
removeUser
|
||||
} = useContactOperation({ uid, cid });
|
||||
|
||||
const { data } = useSelector((store) => {
|
||||
return {
|
||||
data: store.contacts.byId[uid],
|
||||
data: store.contacts.byId[uid]
|
||||
};
|
||||
});
|
||||
|
||||
@@ -35,13 +35,12 @@ export default function Profile({ uid = null, type = "embed", cid = null }) {
|
||||
const {
|
||||
name,
|
||||
email,
|
||||
avatar,
|
||||
avatar
|
||||
// introduction = "This guy has nothing to introduce",
|
||||
} = data;
|
||||
const enableCall = type == "card" && canCall;
|
||||
const canRemoveFromServer = type == "embed" && canRemove;
|
||||
const hasMore =
|
||||
enableCall || email || canRemoveFromChannel || canRemoveFromServer;
|
||||
const hasMore = enableCall || email || canRemoveFromChannel || canRemoveFromServer;
|
||||
return (
|
||||
<StyledWrapper className={type}>
|
||||
<Avatar className="avatar" url={avatar} name={name} />
|
||||
|
||||
@@ -86,8 +86,7 @@ const StyledWrapper = styled.div`
|
||||
padding: 16px;
|
||||
width: 280px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 20px 25px 20px rgba(31, 41, 55, 0.1),
|
||||
0px 10px 10px rgba(31, 41, 55, 0.04);
|
||||
box-shadow: 0px 20px 25px 20px rgba(31, 41, 55, 0.1), 0px 10px 10px rgba(31, 41, 55, 0.04);
|
||||
border-radius: 6px;
|
||||
.icons {
|
||||
padding-bottom: 2px;
|
||||
|
||||
@@ -15,7 +15,7 @@ const Emojis = {
|
||||
"🚀": <EmojiRocket className="emoji" />,
|
||||
"❤️": <EmojiHeart className="emoji" />,
|
||||
"🙁": <EmojiUnhappy className="emoji" />,
|
||||
"🎉": <EmojiCelebrate className="emoji" />,
|
||||
"🎉": <EmojiCelebrate className="emoji" />
|
||||
};
|
||||
|
||||
export default function ReactionItem({ native = "" }) {
|
||||
|
||||
@@ -13,8 +13,7 @@ const StyledWrapper = styled.div`
|
||||
background: #fff;
|
||||
/* gap: 20px; */
|
||||
border: 1px solid #e5e7eb;
|
||||
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1),
|
||||
0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
border-radius: 25px;
|
||||
.txt {
|
||||
padding: 8px;
|
||||
|
||||
@@ -44,12 +44,7 @@ export default function Search() {
|
||||
<input placeholder="Search..." className="input" />
|
||||
</div>
|
||||
<Tooltip tip="More" placement="bottom">
|
||||
<Tippy
|
||||
interactive
|
||||
placement="bottom-end"
|
||||
trigger="click"
|
||||
content={<AddEntriesMenu />}
|
||||
>
|
||||
<Tippy interactive placement="bottom-end" trigger="click" content={<AddEntriesMenu />}>
|
||||
<img src={addIcon} alt="add icon" className="add" />
|
||||
</Tippy>
|
||||
</Tooltip>
|
||||
|
||||
@@ -45,8 +45,7 @@ export default function EmojiPicker({ selectEmoji }) {
|
||||
const clickEle = evt.target;
|
||||
const ignore =
|
||||
(clickEle.nodeName == "svg" && clickEle.dataset.emoji == "toggler") ||
|
||||
(clickEle.nodeName == "path" &&
|
||||
clickEle.parentElement.dataset.emoji == "toggler");
|
||||
(clickEle.nodeName == "path" && clickEle.parentElement.dataset.emoji == "toggler");
|
||||
// console.log("outside click", clickEle, clickEle.parentElement, ignore);
|
||||
if (ignore) return;
|
||||
// if(clickEle)
|
||||
@@ -60,11 +59,7 @@ export default function EmojiPicker({ selectEmoji }) {
|
||||
<div ref={ref} className={`picker ${visible ? "visible" : ""}`}>
|
||||
<Picker onSelect={handleSelect} />
|
||||
</div>
|
||||
<SmileIcon
|
||||
data-emoji="toggler"
|
||||
className="emoji"
|
||||
onClick={togglePickerVisible}
|
||||
/>
|
||||
<SmileIcon data-emoji="toggler" className="emoji" onClick={togglePickerVisible} />
|
||||
</Styled>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -57,11 +57,7 @@ const Styled = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: "";
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0) 63.54%,
|
||||
#f3f4f6 93.09%
|
||||
);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 63.54%, #f3f4f6 93.09%);
|
||||
}
|
||||
}
|
||||
.icon {
|
||||
@@ -87,15 +83,11 @@ const renderContent = (data) => {
|
||||
let res = null;
|
||||
switch (content_type) {
|
||||
case ContentTypes.text:
|
||||
res = reactStringReplace(
|
||||
content,
|
||||
/(\s{1}@[0-9]+\s{1})/g,
|
||||
(match, idx) => {
|
||||
console.log("match", match);
|
||||
const uid = match.trim().slice(1);
|
||||
return <Mention popover={false} key={idx} uid={uid} />;
|
||||
}
|
||||
);
|
||||
res = reactStringReplace(content, /(\s{1}@[0-9]+\s{1})/g, (match, idx) => {
|
||||
console.log("match", match);
|
||||
const uid = match.trim().slice(1);
|
||||
return <Mention popover={false} key={idx} uid={uid} />;
|
||||
});
|
||||
break;
|
||||
case ContentTypes.markdown:
|
||||
res = (
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function Toolbar({
|
||||
toggleMode,
|
||||
mode,
|
||||
to,
|
||||
context,
|
||||
context
|
||||
}) {
|
||||
const { addStageFile } = useUploadFile({ context, id: to });
|
||||
const fileInputRef = useRef(null);
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function UploadFileList({ context = "", id = null }) {
|
||||
const [editInfo, setEditInfo] = useState(null);
|
||||
const { stageFiles, updateStageFile, removeStageFile } = useUploadFile({
|
||||
context,
|
||||
id,
|
||||
id
|
||||
});
|
||||
const toggleModalVisible = (info) => {
|
||||
setEditInfo((prev) => (prev ? null : info));
|
||||
@@ -50,19 +50,12 @@ export default function UploadFileList({ context = "", id = null }) {
|
||||
return (
|
||||
<li className="file" key={url}>
|
||||
<div className="preview">
|
||||
{type.startsWith("image") ? (
|
||||
<img src={url} alt="image" />
|
||||
) : (
|
||||
getFileIcon(type, name)
|
||||
)}
|
||||
{type.startsWith("image") ? <img src={url} alt="image" /> : getFileIcon(type, name)}
|
||||
</div>
|
||||
<h4 className="name">{name}</h4>
|
||||
<span className="size">{formatBytes(size)}</span>
|
||||
<ul className="opts">
|
||||
<li
|
||||
className="opt edit"
|
||||
onClick={handleOpenEditModal.bind(null, idx)}
|
||||
>
|
||||
<li className="opt edit" onClick={handleOpenEditModal.bind(null, idx)}>
|
||||
<EditIcon />
|
||||
</li>
|
||||
<li
|
||||
|
||||
@@ -18,12 +18,12 @@ import useUploadFile from "../../hook/useUploadFile";
|
||||
|
||||
const Modes = {
|
||||
text: "text",
|
||||
markdown: "markdown",
|
||||
markdown: "markdown"
|
||||
};
|
||||
function Send({
|
||||
// 发给谁,或者是channel,或者是user
|
||||
context = "channel",
|
||||
id = "",
|
||||
id = ""
|
||||
}) {
|
||||
const { resetStageFiles } = useUploadFile({ context, id });
|
||||
const { getDraft, getUpdateDraft } = useDraft({ context, id });
|
||||
@@ -40,7 +40,7 @@ function Send({
|
||||
uploadFiles,
|
||||
channelsData,
|
||||
contactsData,
|
||||
uids,
|
||||
uids
|
||||
} = useSelector((store) => {
|
||||
return {
|
||||
channelsData: store.channels.byId,
|
||||
@@ -49,7 +49,7 @@ function Send({
|
||||
mode: store.ui.inputMode,
|
||||
from_uid: store.authData.uid,
|
||||
replying_mid: store.message.replying[`${context}_${id}`],
|
||||
uploadFiles: store.ui.uploadFiles[`${context}_${id}`],
|
||||
uploadFiles: store.ui.uploadFiles[`${context}_${id}`]
|
||||
};
|
||||
});
|
||||
const { sendMessage } = useSendMessage({ context, from: from_uid, to: id });
|
||||
@@ -83,7 +83,7 @@ function Send({
|
||||
type: content_type,
|
||||
content,
|
||||
from_uid,
|
||||
properties,
|
||||
properties
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -101,10 +101,10 @@ function Send({
|
||||
content_type: type,
|
||||
name,
|
||||
size,
|
||||
local_id: ts,
|
||||
local_id: ts
|
||||
},
|
||||
from_uid,
|
||||
sending: true,
|
||||
sending: true
|
||||
};
|
||||
addLocalFileMesage(tmpMsg);
|
||||
});
|
||||
@@ -118,7 +118,7 @@ function Send({
|
||||
type: "markdown",
|
||||
content,
|
||||
from_uid,
|
||||
properties: { local_id: +new Date() },
|
||||
properties: { local_id: +new Date() }
|
||||
});
|
||||
};
|
||||
const toggleMode = () => {
|
||||
@@ -127,24 +127,17 @@ function Send({
|
||||
const toggleMarkdownFullscreen = () => {
|
||||
setMarkdownFullscreen((prev) => !prev);
|
||||
};
|
||||
const name =
|
||||
context == "channel" ? channelsData[id]?.name : contactsData[id]?.name;
|
||||
const name = context == "channel" ? channelsData[id]?.name : contactsData[id]?.name;
|
||||
const placeholder = `Send to ${ChatPrefixs[context]}${name} `;
|
||||
const members =
|
||||
context == "channel"
|
||||
? channelsData[id]?.is_public
|
||||
? uids
|
||||
: channelsData[id]?.members
|
||||
: [];
|
||||
context == "channel" ? (channelsData[id]?.is_public ? uids : channelsData[id]?.members) : [];
|
||||
return (
|
||||
<StyledSend
|
||||
className={`send ${mode} ${markdownFullscreen ? "fullscreen" : ""} ${
|
||||
replying_mid ? "reply" : ""
|
||||
} ${context}`}
|
||||
>
|
||||
{replying_mid && (
|
||||
<Replying context={context} mid={replying_mid} id={id} />
|
||||
)}
|
||||
{replying_mid && <Replying context={context} mid={replying_mid} id={id} />}
|
||||
{mode == Modes.text && <UploadFileList context={context} id={id} />}
|
||||
|
||||
<div className={`send_box ${mode}`}>
|
||||
|
||||
@@ -8,6 +8,6 @@ export default function useFiles(initialFiles = []) {
|
||||
return {
|
||||
files,
|
||||
setFiles,
|
||||
resetFiles,
|
||||
resetFiles
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export default function Server() {
|
||||
const { server, userCount } = useSelector((store) => {
|
||||
return {
|
||||
userCount: store.contacts.ids.length,
|
||||
server: store.server,
|
||||
server: store.server
|
||||
};
|
||||
});
|
||||
// console.log("server info", server);
|
||||
@@ -78,12 +78,7 @@ export default function Server() {
|
||||
</div>
|
||||
</NavLink>
|
||||
<Tooltip tip="More" placement="bottom">
|
||||
<Tippy
|
||||
interactive
|
||||
placement="bottom-end"
|
||||
trigger="click"
|
||||
content={<AddEntriesMenu />}
|
||||
>
|
||||
<Tippy interactive placement="bottom-end" trigger="click" content={<AddEntriesMenu />}>
|
||||
<img src={addIcon} alt="add icon" className="add" />
|
||||
</Tippy>
|
||||
</Tooltip>
|
||||
|
||||
@@ -87,7 +87,7 @@ export default function StyledSettingContainer({
|
||||
navs = [],
|
||||
dangers = [],
|
||||
nav,
|
||||
children,
|
||||
children
|
||||
}) {
|
||||
const { pathname } = useLocation();
|
||||
return (
|
||||
@@ -101,10 +101,7 @@ export default function StyledSettingContainer({
|
||||
<ul key={title} data-title={title} className="items">
|
||||
{items.map(({ name, title }) => {
|
||||
return (
|
||||
<li
|
||||
key={name}
|
||||
className={`item ${name == nav?.name ? "curr" : ""}`}
|
||||
>
|
||||
<li key={name} className={`item ${name == nav?.name ? "curr" : ""}`}>
|
||||
<NavLink to={`${pathname}?nav=${name}`}>{title}</NavLink>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ export default function TippyDefault() {
|
||||
tippy.setDefaultProps({
|
||||
duration: 0,
|
||||
delay: [0, 0],
|
||||
plugins: [followCursor],
|
||||
plugins: [followCursor]
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ const StyledTip = styled.div`
|
||||
line-height: 18px;
|
||||
color: #1d2939;
|
||||
border-radius: var(--br);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08),
|
||||
0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
&::after {
|
||||
background-color: inherit;
|
||||
position: absolute;
|
||||
|
||||
@@ -7,9 +7,7 @@ export default function FileItem({ file = null }) {
|
||||
if (file) {
|
||||
const { type, name } = file;
|
||||
if (isTreatAsImage(file)) {
|
||||
setIcon(
|
||||
<img src={URL.createObjectURL(file)} alt="thumb" className="thumb" />
|
||||
);
|
||||
setIcon(<img src={URL.createObjectURL(file)} alt="thumb" className="thumb" />);
|
||||
return;
|
||||
}
|
||||
console.log("file type", type, name);
|
||||
|
||||
@@ -8,29 +8,18 @@ import Button from "../styled/Button";
|
||||
import StyledWrapper from "./styled";
|
||||
import useSendMessage from "../../hook/useSendMessage";
|
||||
|
||||
export default function UploadModal({
|
||||
context = "user",
|
||||
sendTo = 0,
|
||||
files = [],
|
||||
closeModal,
|
||||
}) {
|
||||
export default function UploadModal({ context = "user", sendTo = 0, files = [], closeModal }) {
|
||||
const from_uid = useSelector((store) => store.authData.uid);
|
||||
const {
|
||||
sendMessage,
|
||||
isSuccess: sendMessageSuccess,
|
||||
isSending,
|
||||
isSending
|
||||
} = useSendMessage({
|
||||
context,
|
||||
from: from_uid,
|
||||
to: sendTo,
|
||||
to: sendTo
|
||||
});
|
||||
const {
|
||||
data,
|
||||
uploadFile,
|
||||
progress,
|
||||
isUploading,
|
||||
isSuccess: uploadSuccess,
|
||||
} = useUploadFile();
|
||||
const { data, uploadFile, progress, isUploading, isSuccess: uploadSuccess } = useUploadFile();
|
||||
const handleUpload = () => {
|
||||
const file = files[0];
|
||||
uploadFile(file);
|
||||
@@ -42,7 +31,7 @@ export default function UploadModal({
|
||||
sendMessage({
|
||||
type: "file",
|
||||
content: { path },
|
||||
properties: rest,
|
||||
properties: rest
|
||||
});
|
||||
}
|
||||
}, [uploadSuccess, data]);
|
||||
@@ -65,11 +54,7 @@ export default function UploadModal({
|
||||
<Button className="cancel" onClick={closeModal}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
className="upload"
|
||||
disabled={sending}
|
||||
onClick={handleUpload}
|
||||
>
|
||||
<Button className="upload" disabled={sending} onClick={handleUpload}>
|
||||
{sending ? `Uploading (${progress}%)` : `Upload`}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@@ -11,8 +11,7 @@ const Styled = styled.div`
|
||||
padding: 24px;
|
||||
background: #06aed4;
|
||||
border-radius: var(--br);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08),
|
||||
0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
color: #ffffff;
|
||||
min-width: 362px;
|
||||
.title {
|
||||
@@ -57,16 +56,9 @@ const OverrideTippyArrowColor = createGlobalStyle`
|
||||
}
|
||||
`;
|
||||
import steps from "./steps";
|
||||
export default function UserGuide({
|
||||
step = 1,
|
||||
placement = "right-start",
|
||||
delay = null,
|
||||
children,
|
||||
}) {
|
||||
export default function UserGuide({ step = 1, placement = "right-start", delay = null, children }) {
|
||||
const dispatch = useDispatch();
|
||||
const { visible, step: reduxStep } = useSelector(
|
||||
(store) => store.ui.userGuide
|
||||
);
|
||||
const { visible, step: reduxStep } = useSelector((store) => store.ui.userGuide);
|
||||
const currStep = steps[step - 1];
|
||||
const isLastStep = steps.length == step;
|
||||
// if (!visible) return children;
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
const steps = [
|
||||
{
|
||||
title: "Step 1",
|
||||
description: "hello world 1",
|
||||
description: "hello world 1"
|
||||
},
|
||||
{
|
||||
title: "Step 2",
|
||||
description: "hello world 1",
|
||||
description: "hello world 1"
|
||||
},
|
||||
{
|
||||
title: "Step 3",
|
||||
description: "hello world 1",
|
||||
description: "hello world 1"
|
||||
},
|
||||
{
|
||||
title: "Step 4",
|
||||
description: "hello world 1",
|
||||
description: "hello world 1"
|
||||
},
|
||||
{
|
||||
title: "Step 5",
|
||||
description: "hello world 1",
|
||||
},
|
||||
description: "hello world 1"
|
||||
}
|
||||
];
|
||||
export default steps;
|
||||
|
||||
@@ -65,39 +65,22 @@ const StyledInput = styled.input`
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Input({
|
||||
type = "text",
|
||||
prefix = "",
|
||||
className,
|
||||
...rest
|
||||
}) {
|
||||
export default function Input({ type = "text", prefix = "", className, ...rest }) {
|
||||
const [inputType, setInputType] = useState(type);
|
||||
const togglePasswordVisible = () => {
|
||||
setInputType((prev) => (prev == "password" ? "text" : "password"));
|
||||
};
|
||||
return type == "password" ? (
|
||||
<StyledWrapper className={className}>
|
||||
<StyledInput
|
||||
type={inputType}
|
||||
className={`inner ${className}`}
|
||||
{...rest}
|
||||
/>
|
||||
<StyledInput type={inputType} className={`inner ${className}`} {...rest} />
|
||||
<div className="view" onClick={togglePasswordVisible}>
|
||||
{inputType == "password" ? (
|
||||
<HiEyeOff color="#78787c" />
|
||||
) : (
|
||||
<HiEye color="#78787c" />
|
||||
)}
|
||||
{inputType == "password" ? <HiEyeOff color="#78787c" /> : <HiEye color="#78787c" />}
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
) : prefix ? (
|
||||
<StyledWrapper className={className}>
|
||||
<span className="prefix">{prefix}</span>
|
||||
<StyledInput
|
||||
className={`inner ${className}`}
|
||||
type={inputType}
|
||||
{...rest}
|
||||
/>
|
||||
<StyledInput className={`inner ${className}`} type={inputType} {...rest} />
|
||||
</StyledWrapper>
|
||||
) : (
|
||||
<StyledInput type={inputType} className={className} {...rest} />
|
||||
|
||||
@@ -5,8 +5,7 @@ const StyledMenu = styled.ul`
|
||||
gap: 2px;
|
||||
padding: 4px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 20px 25px 20px rgba(31, 41, 55, 0.1),
|
||||
0px 10px 10px rgba(31, 41, 55, 0.04);
|
||||
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 {
|
||||
|
||||
@@ -3,89 +3,89 @@ import styled from "styled-components";
|
||||
import { nanoid } from "@reduxjs/toolkit";
|
||||
|
||||
const StyledForm = styled.form`
|
||||
> .option {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
> input[type="radio"] {
|
||||
display: none;
|
||||
|
||||
& + .box {
|
||||
width: 512px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #d0d5dd;
|
||||
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 8px;
|
||||
transition: all ease-in-out 250ms;
|
||||
|
||||
& > label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #667085;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: all ease-in-out 250ms;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
box-shadow: inset 0 0 0 4px #ffffff;
|
||||
border: 1px solid #d0d5dd;
|
||||
margin: 14px 8px 14px 14px;
|
||||
transition: all ease-in-out 500ms;
|
||||
}
|
||||
> .option {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&:checked + .box {
|
||||
background: #22ccee;
|
||||
border: 1px solid #d0d5dd;
|
||||
> input[type="radio"] {
|
||||
display: none;
|
||||
|
||||
& > label {
|
||||
color: #ffffff;
|
||||
& + .box {
|
||||
width: 512px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #d0d5dd;
|
||||
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 8px;
|
||||
transition: all ease-in-out 250ms;
|
||||
|
||||
&:before {
|
||||
background: #ffffff;
|
||||
box-shadow: inset 0 0 0 4px #22ccee;
|
||||
border: 1px solid #ffffff;
|
||||
}
|
||||
& > label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #667085;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: all ease-in-out 250ms;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
box-shadow: inset 0 0 0 4px #ffffff;
|
||||
border: 1px solid #d0d5dd;
|
||||
margin: 14px 8px 14px 14px;
|
||||
transition: all ease-in-out 500ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:checked + .box {
|
||||
background: #22ccee;
|
||||
border: 1px solid #d0d5dd;
|
||||
|
||||
& > label {
|
||||
color: #ffffff;
|
||||
|
||||
&:before {
|
||||
background: #ffffff;
|
||||
box-shadow: inset 0 0 0 4px #22ccee;
|
||||
border: 1px solid #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Radio({ options, value = undefined, onChange = undefined }) {
|
||||
const [innerValue, setInnerValue] = useState(0);
|
||||
const id = useRef(nanoid());
|
||||
const [innerValue, setInnerValue] = useState(0);
|
||||
const id = useRef(nanoid());
|
||||
|
||||
return (
|
||||
<StyledForm>
|
||||
{options.map((item, index) => (
|
||||
<div className="option" key={index}>
|
||||
<input
|
||||
type="radio"
|
||||
checked={(value !== undefined ? value : innerValue) === index}
|
||||
onChange={() => {
|
||||
value === undefined && setInnerValue(index);
|
||||
onChange !== null && onChange(index);
|
||||
}}
|
||||
id={`${id.current}-${index}`}
|
||||
/>
|
||||
<div className="box">
|
||||
<label htmlFor={`${id.current}-${index}`}>{item}</label>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</StyledForm>
|
||||
);
|
||||
return (
|
||||
<StyledForm>
|
||||
{options.map((item, index) => (
|
||||
<div className="option" key={index}>
|
||||
<input
|
||||
type="radio"
|
||||
checked={(value !== undefined ? value : innerValue) === index}
|
||||
onChange={() => {
|
||||
value === undefined && setInnerValue(index);
|
||||
onChange !== null && onChange(index);
|
||||
}}
|
||||
id={`${id.current}-${index}`}
|
||||
/>
|
||||
<div className="box">
|
||||
<label htmlFor={`${id.current}-${index}`}>{item}</label>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</StyledForm>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@ export default function Select({ options = [], updateSelect = null }) {
|
||||
{options.map(({ title, value, selected, underline }) => {
|
||||
return (
|
||||
<li
|
||||
onClick={
|
||||
selected ? null : handleSelect.bind(null, { title, value })
|
||||
}
|
||||
onClick={selected ? null : handleSelect.bind(null, { title, value })}
|
||||
className={`item sb ${underline ? "underline" : ""}`}
|
||||
data-disabled={selected}
|
||||
key={value}
|
||||
|
||||
@@ -9,58 +9,43 @@ import {
|
||||
useUpdateLoginConfigMutation,
|
||||
useUpdateSMTPConfigMutation,
|
||||
useUpdateAgoraConfigMutation,
|
||||
useUpdateFirebaseConfigMutation,
|
||||
useUpdateFirebaseConfigMutation
|
||||
} from "../../app/services/server";
|
||||
export default function useConfig(config = "smtp") {
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [values, setValues] = useState({});
|
||||
const { data: Login, refetch: refetchLogin } = useGetLoginConfigQuery();
|
||||
const [
|
||||
updateLoginConfig,
|
||||
{ isSuccess: LoginUpdated },
|
||||
] = useUpdateLoginConfigMutation();
|
||||
const [updateLoginConfig, { isSuccess: LoginUpdated }] = useUpdateLoginConfigMutation();
|
||||
const { data: SMTP, refetch: refetchSMTP } = useGetSMTPConfigQuery();
|
||||
const [
|
||||
updateSMTPConfig,
|
||||
{ isSuccess: SMTPUpdated },
|
||||
] = useUpdateSMTPConfigMutation();
|
||||
const [updateSMTPConfig, { isSuccess: SMTPUpdated }] = useUpdateSMTPConfigMutation();
|
||||
const { data: Agora, refetch: refetchAgora } = useGetAgoraConfigQuery();
|
||||
const [
|
||||
updateAgoraConfig,
|
||||
{ isSuccess: AgoraUpdated },
|
||||
] = useUpdateAgoraConfigMutation();
|
||||
const {
|
||||
data: Firebase,
|
||||
refetch: refetchFirebase,
|
||||
} = useGetFirebaseConfigQuery();
|
||||
const [
|
||||
updateFirebaseConfig,
|
||||
{ isSuccess: FirebaseUpdated },
|
||||
] = useUpdateFirebaseConfigMutation();
|
||||
const [updateAgoraConfig, { isSuccess: AgoraUpdated }] = useUpdateAgoraConfigMutation();
|
||||
const { data: Firebase, refetch: refetchFirebase } = useGetFirebaseConfigQuery();
|
||||
const [updateFirebaseConfig, { isSuccess: FirebaseUpdated }] = useUpdateFirebaseConfigMutation();
|
||||
|
||||
const datas = {
|
||||
login: Login,
|
||||
smtp: SMTP,
|
||||
agora: Agora,
|
||||
firebase: Firebase,
|
||||
firebase: Firebase
|
||||
};
|
||||
const updateFns = {
|
||||
login: updateLoginConfig,
|
||||
smtp: updateSMTPConfig,
|
||||
agora: updateAgoraConfig,
|
||||
firebase: updateFirebaseConfig,
|
||||
firebase: updateFirebaseConfig
|
||||
};
|
||||
const refetchs = {
|
||||
smtp: refetchSMTP,
|
||||
agora: refetchAgora,
|
||||
firebase: refetchFirebase,
|
||||
login: refetchLogin,
|
||||
login: refetchLogin
|
||||
};
|
||||
const updateds = {
|
||||
login: LoginUpdated,
|
||||
smtp: SMTPUpdated,
|
||||
agora: AgoraUpdated,
|
||||
firebase: FirebaseUpdated,
|
||||
firebase: FirebaseUpdated
|
||||
};
|
||||
const data = datas[config];
|
||||
const updateConfig = updateFns[config];
|
||||
@@ -102,6 +87,6 @@ export default function useConfig(config = "smtp") {
|
||||
updateConfig,
|
||||
values,
|
||||
setValues,
|
||||
toggleEnable,
|
||||
toggleEnable
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,14 +13,8 @@ export default function useContactOperation({ uid, cid }) {
|
||||
const [passedUid, setPassedUid] = useState(undefined);
|
||||
const { values: agoraConfig } = useConfig("agora");
|
||||
const isUserDetailPath = useMatch(`/contacts/${uid}`);
|
||||
const [
|
||||
removeUser,
|
||||
{ isSuccess: removeUserSuccess },
|
||||
] = useLazyDeleteContactQuery();
|
||||
const [
|
||||
removeInChannel,
|
||||
{ isSuccess: removeSuccess },
|
||||
] = useRemoveMembersMutation();
|
||||
const [removeUser, { isSuccess: removeUserSuccess }] = useLazyDeleteContactQuery();
|
||||
const [removeInChannel, { isSuccess: removeSuccess }] = useRemoveMembersMutation();
|
||||
const navigateTo = useNavigate();
|
||||
const { copy } = useCopy();
|
||||
const { user, channel, loginUid, isAdmin } = useSelector((store) => {
|
||||
@@ -28,7 +22,7 @@ export default function useContactOperation({ uid, cid }) {
|
||||
user: store.contacts.byId[uid],
|
||||
channel: store.channels.byId[cid],
|
||||
loginUid: store.authData.uid,
|
||||
isAdmin: store.contacts.byId[store.authData.uid]?.is_admin,
|
||||
isAdmin: store.contacts.byId[store.authData.uid]?.is_admin
|
||||
};
|
||||
});
|
||||
useEffect(() => {
|
||||
@@ -81,6 +75,6 @@ export default function useContactOperation({ uid, cid }) {
|
||||
canCopyEmail: !!user?.email,
|
||||
copyEmail,
|
||||
canCall,
|
||||
call,
|
||||
call
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,6 +49,6 @@ export default function useContextMenu(placement = "right-start") {
|
||||
offset,
|
||||
visible,
|
||||
hideContextMenu,
|
||||
handleContextMenuEvent,
|
||||
handleContextMenuEvent
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,9 +20,7 @@ const useCopy = (config) => {
|
||||
el.style.left = "-9999px";
|
||||
document.body.appendChild(el);
|
||||
const selected =
|
||||
document.getSelection().rangeCount > 0
|
||||
? document.getSelection().getRangeAt(0)
|
||||
: false;
|
||||
document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;
|
||||
el.select();
|
||||
const success = document.execCommand("copy");
|
||||
document.body.removeChild(el);
|
||||
|
||||
@@ -6,11 +6,11 @@ export default function useDeleteMessage() {
|
||||
const { loginUser, messageData } = useSelector((store) => {
|
||||
return {
|
||||
messageData: store.message,
|
||||
loginUser: store.contacts.byId[store.authData.uid],
|
||||
loginUser: store.contacts.byId[store.authData.uid]
|
||||
};
|
||||
});
|
||||
const [
|
||||
remove,
|
||||
remove
|
||||
// { isError, isLoading, isSuccess },
|
||||
] = useLazyDeleteMessageQuery();
|
||||
const deleteMessage = async (mids) => {
|
||||
@@ -40,6 +40,6 @@ export default function useDeleteMessage() {
|
||||
return {
|
||||
canDelete,
|
||||
isDeleting: deleting,
|
||||
deleteMessage,
|
||||
deleteMessage
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function useDraft({ context = "", id = "" }) {
|
||||
const { draftMarkdown, draftMixedText } = useSelector((store) => {
|
||||
return {
|
||||
draftMarkdown: store.ui.draftMarkdown,
|
||||
draftMixedText: store.ui.draftMixedText,
|
||||
draftMixedText: store.ui.draftMixedText
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import {
|
||||
useLazyRemoveFavoriteQuery,
|
||||
useFavoriteMessageMutation,
|
||||
} from "../../app/services/message";
|
||||
import { useLazyRemoveFavoriteQuery, useFavoriteMessageMutation } from "../../app/services/message";
|
||||
export default function useFavMessage({ cid = null, uid = null }) {
|
||||
const [removeFav] = useLazyRemoveFavoriteQuery();
|
||||
const [addFav] = useFavoriteMessageMutation();
|
||||
const [favorites, setFavorites] = useState([]);
|
||||
const { favs = [] } = useSelector((store) => {
|
||||
return {
|
||||
favs: store.favorites,
|
||||
favs: store.favorites
|
||||
};
|
||||
});
|
||||
const addFavorite = async (mid = []) => {
|
||||
@@ -56,6 +53,6 @@ export default function useFavMessage({ cid = null, uid = null }) {
|
||||
isFavorited,
|
||||
addFavorite,
|
||||
removeFavorite,
|
||||
favorites,
|
||||
favorites
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,6 +23,6 @@ export default function useFilteredChannels() {
|
||||
return {
|
||||
input,
|
||||
channels: filteredChannels,
|
||||
updateInput,
|
||||
updateInput
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,6 +23,6 @@ export default function useFilteredUsers() {
|
||||
return {
|
||||
input,
|
||||
contacts: filteredUsers,
|
||||
updateInput,
|
||||
updateInput
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,24 +6,14 @@ export default function useForwardMessage() {
|
||||
const [forwarding, setForwarding] = useState(false);
|
||||
const [
|
||||
createArchive,
|
||||
{
|
||||
isError: createArchiveError,
|
||||
isLoading: creatingArchive,
|
||||
isSuccess: createArchiveSuccess,
|
||||
},
|
||||
{ isError: createArchiveError, isLoading: creatingArchive, isSuccess: createArchiveSuccess }
|
||||
] = useCreateArchiveMutation();
|
||||
const [
|
||||
sendChannelMsg,
|
||||
{
|
||||
isLoading: channelSending,
|
||||
isSuccess: channelSuccess,
|
||||
isError: channelError,
|
||||
},
|
||||
{ isLoading: channelSending, isSuccess: channelSuccess, isError: channelError }
|
||||
] = useSendChannelMsgMutation();
|
||||
const [
|
||||
sendUserMsg,
|
||||
{ isLoading: userSending, isSuccess: userSuccess, isError: userError },
|
||||
] = useSendMsgMutation();
|
||||
const [sendUserMsg, { isLoading: userSending, isSuccess: userSuccess, isError: userError }] =
|
||||
useSendMsgMutation();
|
||||
const forwardMessage = async ({ mids = [], users = [], channels = [] }) => {
|
||||
setForwarding(true);
|
||||
const { data: archive_id } = await createArchive(mids);
|
||||
@@ -32,7 +22,7 @@ export default function useForwardMessage() {
|
||||
await sendUserMsg({
|
||||
type: "archive",
|
||||
id: uid,
|
||||
content: archive_id,
|
||||
content: archive_id
|
||||
// from_uid: from,
|
||||
});
|
||||
}
|
||||
@@ -42,7 +32,7 @@ export default function useForwardMessage() {
|
||||
await sendChannelMsg({
|
||||
type: "archive",
|
||||
id: cid,
|
||||
content: archive_id,
|
||||
content: archive_id
|
||||
// from_uid: from,
|
||||
});
|
||||
}
|
||||
@@ -54,6 +44,6 @@ export default function useForwardMessage() {
|
||||
forwarding,
|
||||
isError: channelError || userError || createArchiveError,
|
||||
isSending: userSending || channelSending || creatingArchive,
|
||||
isSuccess: channelSuccess || userSuccess || createArchiveSuccess,
|
||||
isSuccess: channelSuccess || userSuccess || createArchiveSuccess
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ import { useState, useEffect } from "react";
|
||||
// import toast from "react-hot-toast";
|
||||
import {
|
||||
useGetGithubAuthConfigQuery,
|
||||
useUpdateGithubAuthConfigMutation,
|
||||
useUpdateGithubAuthConfigMutation
|
||||
} from "../../app/services/server";
|
||||
export default function useGithubAuthConfig() {
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [config, setConfig] = useState({});
|
||||
const { data } = useGetGithubAuthConfigQuery(undefined, {
|
||||
refetchOnMountOrArgChange: true,
|
||||
refetchOnMountOrArgChange: true
|
||||
});
|
||||
const [updateAuthConfig, { isSuccess }] = useUpdateGithubAuthConfigMutation();
|
||||
useEffect(() => {
|
||||
@@ -18,9 +18,7 @@ export default function useGithubAuthConfig() {
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
setChanged(
|
||||
isSuccess ? false : JSON.stringify(data) !== JSON.stringify(config)
|
||||
);
|
||||
setChanged(isSuccess ? false : JSON.stringify(data) !== JSON.stringify(config));
|
||||
}, [data, config, isSuccess]);
|
||||
const updateGithubAuthConfig = (obj) => {
|
||||
setConfig((prev) => {
|
||||
@@ -35,6 +33,6 @@ export default function useGithubAuthConfig() {
|
||||
changed,
|
||||
updateGithubAuthConfig,
|
||||
updateGithubAuthConfigToServer,
|
||||
isSuccess,
|
||||
isSuccess
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,18 +2,15 @@ import { useState, useEffect } from "react";
|
||||
// import toast from "react-hot-toast";
|
||||
import {
|
||||
useGetGoogleAuthConfigQuery,
|
||||
useUpdateGoogleAuthConfigMutation,
|
||||
useUpdateGoogleAuthConfigMutation
|
||||
} from "../../app/services/server";
|
||||
export default function useGoogleAuthConfig() {
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [clientId, setClientId] = useState("");
|
||||
const { data } = useGetGoogleAuthConfigQuery(undefined, {
|
||||
refetchOnMountOrArgChange: true,
|
||||
refetchOnMountOrArgChange: true
|
||||
});
|
||||
const [
|
||||
updateGoogleAuthConfig,
|
||||
{ isSuccess },
|
||||
] = useUpdateGoogleAuthConfigMutation();
|
||||
const [updateGoogleAuthConfig, { isSuccess }] = useUpdateGoogleAuthConfigMutation();
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setClientId(data.client_id);
|
||||
@@ -36,6 +33,6 @@ export default function useGoogleAuthConfig() {
|
||||
updateClientId: setClientId,
|
||||
updateClientIdToServer,
|
||||
updateGoogleAuthConfig,
|
||||
isSuccess,
|
||||
isSuccess
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,23 +2,16 @@ import { useState, useEffect } from "react";
|
||||
import useCopy from "./useCopy";
|
||||
import {
|
||||
useLazyCreateInviteLinkQuery as useCreateServerInviteLinkQuery,
|
||||
useGetSMTPStatusQuery,
|
||||
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: SMTPEnabled,
|
||||
isSuccess: smtpStatusFetchSuccess,
|
||||
} = useGetSMTPStatusQuery();
|
||||
const [
|
||||
generateChannelInviteLink,
|
||||
{ data: channelInviteLink, isLoading: generatingChannelLink },
|
||||
] = useCreateChannelInviteLinkQuery();
|
||||
const [
|
||||
generateServerInviteLink,
|
||||
{ data: serverInviteLink, isLoading: generatingServerLink },
|
||||
] = useCreateServerInviteLinkQuery();
|
||||
const { data: SMTPEnabled, isSuccess: smtpStatusFetchSuccess } = useGetSMTPStatusQuery();
|
||||
const [generateChannelInviteLink, { data: channelInviteLink, isLoading: generatingChannelLink }] =
|
||||
useCreateChannelInviteLinkQuery();
|
||||
const [generateServerInviteLink, { data: serverInviteLink, isLoading: generatingServerLink }] =
|
||||
useCreateServerInviteLinkQuery();
|
||||
const { copied, copy } = useCopy({ enableToast: false });
|
||||
const copyLink = () => {
|
||||
copy(finalLink);
|
||||
@@ -46,11 +39,9 @@ export default function useInviteLink(cid = null) {
|
||||
return {
|
||||
enableSMTP: SMTPEnabled,
|
||||
generating: cid ? generatingChannelLink : generatingServerLink,
|
||||
generateNewLink: cid
|
||||
? generateChannelInviteLink.bind(null, cid)
|
||||
: genServerLink,
|
||||
generateNewLink: cid ? generateChannelInviteLink.bind(null, cid) : genServerLink,
|
||||
link: finalLink,
|
||||
linkCopied: copied,
|
||||
copyLink,
|
||||
copyLink
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
// import React from 'react'
|
||||
import { useSelector } from "react-redux";
|
||||
import {
|
||||
useUpdateChannelMutation,
|
||||
useLazyLeaveChannelQuery,
|
||||
} from "../../app/services/channel";
|
||||
import { useUpdateChannelMutation, useLazyLeaveChannelQuery } from "../../app/services/channel";
|
||||
export default function useLeaveChannel(cid = null) {
|
||||
const { channel, loginUid } = useSelector((store) => {
|
||||
return { channel: store.channels.byId[cid], loginUid: store.authData.uid };
|
||||
});
|
||||
const [
|
||||
update,
|
||||
{ isLoading: transfering, isSuccess: transferSuccess },
|
||||
] = useUpdateChannelMutation();
|
||||
const [
|
||||
leave,
|
||||
{ isLoading: leaving, isSuccess: leaveSuccess },
|
||||
] = useLazyLeaveChannelQuery();
|
||||
const [update, { isLoading: transfering, isSuccess: transferSuccess }] =
|
||||
useUpdateChannelMutation();
|
||||
const [leave, { isLoading: leaving, isSuccess: leaveSuccess }] = useLazyLeaveChannelQuery();
|
||||
const transferOwner = (uid = null) => {
|
||||
if (!uid) return;
|
||||
update({ id: cid, owner: uid });
|
||||
@@ -34,6 +26,6 @@ export default function useLeaveChannel(cid = null) {
|
||||
leaveSuccess,
|
||||
isOwner,
|
||||
transfering,
|
||||
transferSuccess,
|
||||
transferSuccess
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import { useLazyGetHistoryMessagesQuery } from "../../app/services/channel";
|
||||
import { useLazyGetHistoryMessagesQuery as useLazyGetDMHistoryMsg } from "../../app/services/contact";
|
||||
|
||||
const getFeedWithPagination = (config) => {
|
||||
const { pageNumber = 1, pageSize = 40, mids = [], isLast = false } =
|
||||
config || {};
|
||||
const { pageNumber = 1, pageSize = 40, mids = [], isLast = false } = config || {};
|
||||
const shadowMids = mids.slice(0);
|
||||
|
||||
if (shadowMids.length == 0)
|
||||
@@ -15,7 +14,7 @@ const getFeedWithPagination = (config) => {
|
||||
pageCount: 0,
|
||||
pageSize,
|
||||
pageNumber: 1,
|
||||
ids: [],
|
||||
ids: []
|
||||
};
|
||||
shadowMids.sort((a, b) => {
|
||||
return Number(a) - Number(b);
|
||||
@@ -34,7 +33,7 @@ const getFeedWithPagination = (config) => {
|
||||
pageCount,
|
||||
pageSize,
|
||||
pageNumber: computedPageNumber,
|
||||
ids,
|
||||
ids
|
||||
};
|
||||
};
|
||||
let curScrollPos = 0;
|
||||
@@ -48,16 +47,13 @@ export default function useMessageFeed({ context = "channel", id = null }) {
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const [appends, setAppends] = useState([]);
|
||||
const [items, setItems] = useState([]);
|
||||
const loadMoreMsgsFromServer =
|
||||
context == "channel" ? loadMoreChannelMsgs : loadMoreDmMsgs;
|
||||
const loadMoreMsgsFromServer = context == "channel" ? loadMoreChannelMsgs : loadMoreDmMsgs;
|
||||
const { mids, messageData, loginUid } = useSelector((store) => {
|
||||
return {
|
||||
loginUid: store.authData.uid,
|
||||
mids:
|
||||
context == "channel"
|
||||
? store.channelMessage[id] || []
|
||||
: store.userMessage.byId[id] || [],
|
||||
messageData: store.message,
|
||||
context == "channel" ? store.channelMessage[id] || [] : store.userMessage.byId[id] || [],
|
||||
messageData: store.message
|
||||
};
|
||||
});
|
||||
useEffect(() => {
|
||||
@@ -69,12 +65,9 @@ export default function useMessageFeed({ context = "channel", id = null }) {
|
||||
}, [context, id]);
|
||||
useEffect(() => {
|
||||
if (items.length) {
|
||||
containerRef.current = document.querySelector(
|
||||
`#RUSTCHAT_FEED_${context}_${id}`
|
||||
);
|
||||
containerRef.current = document.querySelector(`#RUSTCHAT_FEED_${context}_${id}`);
|
||||
if (containerRef.current) {
|
||||
const newScroll =
|
||||
containerRef.current.scrollHeight - containerRef.current.clientHeight;
|
||||
const newScroll = containerRef.current.scrollHeight - containerRef.current.clientHeight;
|
||||
containerRef.current.scrollTop = curScrollPos + (newScroll - oldScroll);
|
||||
}
|
||||
}
|
||||
@@ -88,7 +81,7 @@ export default function useMessageFeed({ context = "channel", id = null }) {
|
||||
// 初次
|
||||
const pageInfo = getFeedWithPagination({
|
||||
mids: serverMids,
|
||||
isLast: true,
|
||||
isLast: true
|
||||
});
|
||||
console.log("pull up 2", pageInfo);
|
||||
pageRef.current = pageInfo;
|
||||
@@ -111,8 +104,7 @@ export default function useMessageFeed({ context = "channel", id = null }) {
|
||||
if (container) {
|
||||
const msgFromSelf = loginUid == messageData[newestMsgId]?.from_uid;
|
||||
const scrollDistance =
|
||||
container.scrollHeight -
|
||||
(container.offsetHeight + container.scrollTop);
|
||||
container.scrollHeight - (container.offsetHeight + container.scrollTop);
|
||||
console.log("scrollDistance", msgFromSelf, scrollDistance);
|
||||
if (msgFromSelf) {
|
||||
container.scrollTop = container.scrollHeight;
|
||||
@@ -133,7 +125,7 @@ export default function useMessageFeed({ context = "channel", id = null }) {
|
||||
const [firstMid] = currPageInfo.ids;
|
||||
const { data: newList } = await loadMoreMsgsFromServer({
|
||||
mid: firstMid,
|
||||
id,
|
||||
id
|
||||
});
|
||||
if (newList.length == 0) {
|
||||
setHasMore(false);
|
||||
@@ -145,13 +137,13 @@ export default function useMessageFeed({ context = "channel", id = null }) {
|
||||
// 初始化
|
||||
pageInfo = getFeedWithPagination({
|
||||
mids,
|
||||
isLast: true,
|
||||
isLast: true
|
||||
});
|
||||
} else {
|
||||
const prevPageNumber = currPageInfo.pageNumber - 1;
|
||||
pageInfo = getFeedWithPagination({
|
||||
mids,
|
||||
pageNumber: prevPageNumber,
|
||||
pageNumber: prevPageNumber
|
||||
});
|
||||
}
|
||||
pageRef.current = pageInfo;
|
||||
@@ -177,6 +169,6 @@ export default function useMessageFeed({ context = "channel", id = null }) {
|
||||
hasMore,
|
||||
pullUp,
|
||||
pullDown,
|
||||
list: items,
|
||||
list: items
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,10 +4,8 @@ import { useLazyGetArchiveMessageQuery } from "../../app/services/message";
|
||||
export default function useNormalizeMessage() {
|
||||
const [filePath, setFilePath] = useState(null);
|
||||
const [normalizedMessages, setNormalizedMessages] = useState(null);
|
||||
const [
|
||||
getArchiveMessage,
|
||||
{ data, isError, isLoading, isSuccess },
|
||||
] = useLazyGetArchiveMessageQuery();
|
||||
const [getArchiveMessage, { data, isError, isLoading, isSuccess }] =
|
||||
useLazyGetArchiveMessageQuery();
|
||||
useEffect(() => {
|
||||
if (data && isSuccess) {
|
||||
const msgs = normalizeArchiveData(data, filePath);
|
||||
@@ -28,6 +26,6 @@ export default function useNormalizeMessage() {
|
||||
messages: normalizedMessages,
|
||||
isError,
|
||||
isLoading,
|
||||
isSuccess,
|
||||
isSuccess
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
const isSafariBrowser = () =>
|
||||
navigator.userAgent.indexOf("Safari") > -1 &&
|
||||
navigator.userAgent.indexOf("Chrome") <= -1;
|
||||
navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") <= -1;
|
||||
export default function useNotification() {
|
||||
// const navigate = useNavigate();
|
||||
// granted default denied /
|
||||
@@ -17,18 +16,13 @@ export default function useNotification() {
|
||||
};
|
||||
document.addEventListener("visibilitychange", visibleChangeHandler);
|
||||
if (!isSafariBrowser) {
|
||||
navigator.permissions
|
||||
.query({ name: "notifications" })
|
||||
.then(function (permissionStatus) {
|
||||
console.log(
|
||||
"notifications permission status is ",
|
||||
permissionStatus.state
|
||||
);
|
||||
permissionStatus.onchange = notifyPermissionChangeHandler.bind(
|
||||
null,
|
||||
permissionStatus.state
|
||||
);
|
||||
});
|
||||
navigator.permissions.query({ name: "notifications" }).then(function (permissionStatus) {
|
||||
console.log("notifications permission status is ", permissionStatus.state);
|
||||
permissionStatus.onchange = notifyPermissionChangeHandler.bind(
|
||||
null,
|
||||
permissionStatus.state
|
||||
);
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
document.removeEventListener("visibilitychange", visibleChangeHandler);
|
||||
@@ -48,7 +42,7 @@ export default function useNotification() {
|
||||
const {
|
||||
title = "New Message",
|
||||
body = "You have one new message",
|
||||
icon = "https://static.nicegoodthings.com/project/ext/webrowse.logo.png",
|
||||
icon = "https://static.nicegoodthings.com/project/ext/webrowse.logo.png"
|
||||
} = payload;
|
||||
new Notification(title, { body, icon });
|
||||
// const n = new Notification(title, { body, icon });
|
||||
|
||||
@@ -12,6 +12,6 @@ export default function usePWABadge() {
|
||||
}, []);
|
||||
|
||||
return {
|
||||
isSupported: badge.isSupported(),
|
||||
isSupported: badge.isSupported()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import {
|
||||
usePinMessageMutation,
|
||||
useUnpinMessageMutation,
|
||||
} from "../../app/services/message";
|
||||
import { usePinMessageMutation, useUnpinMessageMutation } from "../../app/services/message";
|
||||
export default function usePinMessage(cid = null) {
|
||||
const [pins, setPins] = useState([]);
|
||||
const { channel, loginUser } = useSelector((store) => {
|
||||
return {
|
||||
channel: store.channels.byId[cid],
|
||||
loginUser: store.contacts.byId[store.authData.uid],
|
||||
loginUser: store.contacts.byId[store.authData.uid]
|
||||
};
|
||||
});
|
||||
const [pin, { isError, isLoading, isSuccess }] = usePinMessageMutation();
|
||||
const [
|
||||
unpin,
|
||||
{ isError: isUnpinError, isLoading: isUnpining, isSuccess: isUnpinSuccess },
|
||||
] = useUnpinMessageMutation();
|
||||
const [unpin, { isError: isUnpinError, isLoading: isUnpining, isSuccess: isUnpinSuccess }] =
|
||||
useUnpinMessageMutation();
|
||||
const pinMessage = (mid) => {
|
||||
if (!mid || !cid) return;
|
||||
pin({ mid, gid: +cid });
|
||||
@@ -50,6 +45,6 @@ export default function usePinMessage(cid = null) {
|
||||
isSuccess,
|
||||
isUnpinError,
|
||||
isUnpining,
|
||||
isUnpinSuccess,
|
||||
isUnpinSuccess
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@ import { removeChannelMsg } from "../../app/slices/message.channel";
|
||||
import { removeUserMsg } from "../../app/slices/message.user";
|
||||
export default function useRemoveLocalMessage({ context = "user", id = 0 }) {
|
||||
const dispatch = useDispatch();
|
||||
const removeContextMessage =
|
||||
context == "channel" ? removeChannelMsg : removeUserMsg;
|
||||
const removeContextMessage = context == "channel" ? removeChannelMsg : removeUserMsg;
|
||||
const removeLocalMessage = (mid) => {
|
||||
dispatch(removeContextMessage({ id, mid }));
|
||||
dispatch(removeMessage(mid));
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
// import second from 'first'
|
||||
import {
|
||||
removeReplyingMessage,
|
||||
addReplyingMessage,
|
||||
} from "../../app/slices/message";
|
||||
import { removeReplyingMessage, addReplyingMessage } from "../../app/slices/message";
|
||||
import { useSendChannelMsgMutation } from "../../app/services/channel";
|
||||
import { useSendMsgMutation } from "../../app/services/contact";
|
||||
import { useReplyMessageMutation } from "../../app/services/message";
|
||||
@@ -11,38 +8,23 @@ import toast from "react-hot-toast";
|
||||
export default function useSendMessage(props) {
|
||||
const { context = "user", from = null, to = null } = props || {};
|
||||
const dispatch = useDispatch();
|
||||
const stageFiles = useSelector(
|
||||
(store) => store.ui.uploadFiles[`${context}_${to}`] || []
|
||||
);
|
||||
const [
|
||||
replyMessage,
|
||||
{ isError: replyErr, isLoading: replying, isSuccess: replySuccess },
|
||||
] = useReplyMessageMutation();
|
||||
const stageFiles = useSelector((store) => store.ui.uploadFiles[`${context}_${to}`] || []);
|
||||
const [replyMessage, { isError: replyErr, isLoading: replying, isSuccess: replySuccess }] =
|
||||
useReplyMessageMutation();
|
||||
const [
|
||||
sendChannelMsg,
|
||||
{
|
||||
isLoading: channelSending,
|
||||
isSuccess: channelSuccess,
|
||||
isError: channelError,
|
||||
},
|
||||
{ isLoading: channelSending, isSuccess: channelSuccess, isError: channelError }
|
||||
] = useSendChannelMsgMutation();
|
||||
const [
|
||||
sendUserMsg,
|
||||
{ isLoading: userSending, isSuccess: userSuccess, isError: userError },
|
||||
] = useSendMsgMutation();
|
||||
const [sendUserMsg, { isLoading: userSending, isSuccess: userSuccess, isError: userError }] =
|
||||
useSendMsgMutation();
|
||||
const sendFn = context == "user" ? sendUserMsg : sendChannelMsg;
|
||||
const sendMessages = async ({
|
||||
type = "text",
|
||||
content,
|
||||
users = [],
|
||||
channels = [],
|
||||
}) => {
|
||||
const sendMessages = async ({ type = "text", content, users = [], channels = [] }) => {
|
||||
if (users.length) {
|
||||
for await (const uid of users) {
|
||||
await sendUserMsg({
|
||||
type,
|
||||
id: uid,
|
||||
content,
|
||||
content
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -51,7 +33,7 @@ export default function useSendMessage(props) {
|
||||
await sendChannelMsg({
|
||||
type,
|
||||
id: cid,
|
||||
content,
|
||||
content
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -71,7 +53,7 @@ export default function useSendMessage(props) {
|
||||
type,
|
||||
content,
|
||||
context,
|
||||
from_uid: from,
|
||||
from_uid: from
|
||||
});
|
||||
} else {
|
||||
await sendFn({
|
||||
@@ -80,7 +62,7 @@ export default function useSendMessage(props) {
|
||||
properties: { ...properties },
|
||||
type,
|
||||
from_uid: from,
|
||||
...rest,
|
||||
...rest
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -101,6 +83,6 @@ export default function useSendMessage(props) {
|
||||
sendMessage,
|
||||
isError: channelError || userError || replyErr,
|
||||
isSending: userSending || channelSending || replying,
|
||||
isSuccess: channelSuccess || userSuccess || replySuccess,
|
||||
isSuccess: channelSuccess || userSuccess || replySuccess
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,18 +1,8 @@
|
||||
import { batch } from "react-redux";
|
||||
import {
|
||||
addChannelMsg,
|
||||
removeChannelMsg,
|
||||
} from "../../../app/slices/message.channel";
|
||||
import {
|
||||
addMessage,
|
||||
removeMessage,
|
||||
updateMessage,
|
||||
} from "../../../app/slices/message";
|
||||
import { addChannelMsg, removeChannelMsg } from "../../../app/slices/message.channel";
|
||||
import { addMessage, removeMessage, updateMessage } from "../../../app/slices/message";
|
||||
import { toggleReactionMessage } from "../../../app/slices/message.reaction";
|
||||
import {
|
||||
addFileMessage,
|
||||
removeFileMessage,
|
||||
} from "../../../app/slices/message.file";
|
||||
import { addFileMessage, removeFileMessage } from "../../../app/slices/message.file";
|
||||
import { addUserMsg, removeUserMsg } from "../../../app/slices/message.user";
|
||||
import { updateAfterMid } from "../../../app/slices/footprint";
|
||||
import { ContentTypes } from "../../../app/config";
|
||||
@@ -29,8 +19,8 @@ const handler = (data, dispatch, currState) => {
|
||||
type,
|
||||
properties,
|
||||
expires_in,
|
||||
detail: innerDetail,
|
||||
},
|
||||
detail: innerDetail
|
||||
}
|
||||
} = data;
|
||||
const common = {
|
||||
from_uid,
|
||||
@@ -38,7 +28,7 @@ const handler = (data, dispatch, currState) => {
|
||||
content,
|
||||
content_type,
|
||||
properties,
|
||||
expires_in,
|
||||
expires_in
|
||||
};
|
||||
switch (type) {
|
||||
case "normal":
|
||||
@@ -64,7 +54,7 @@ const handler = (data, dispatch, currState) => {
|
||||
mid,
|
||||
// 如果是自己发的消息,就是已读
|
||||
read,
|
||||
...common,
|
||||
...common
|
||||
})
|
||||
);
|
||||
// 未推送完 or 不是自己发的消息
|
||||
@@ -74,7 +64,7 @@ const handler = (data, dispatch, currState) => {
|
||||
appendMessage({
|
||||
id,
|
||||
mid,
|
||||
local_id: properties ? properties.local_id : null,
|
||||
local_id: properties ? properties.local_id : null
|
||||
})
|
||||
);
|
||||
// 加到file message 列表
|
||||
@@ -94,7 +84,7 @@ const handler = (data, dispatch, currState) => {
|
||||
reply_mid: detailMid,
|
||||
// 如果是自己发的消息,就是已读
|
||||
read,
|
||||
...common,
|
||||
...common
|
||||
})
|
||||
);
|
||||
// 未推送完 or 不是自己发的消息
|
||||
@@ -103,7 +93,7 @@ const handler = (data, dispatch, currState) => {
|
||||
appendMessage({
|
||||
id,
|
||||
mid,
|
||||
local_id: properties ? properties.local_id : null,
|
||||
local_id: properties ? properties.local_id : null
|
||||
})
|
||||
);
|
||||
// }
|
||||
@@ -112,8 +102,7 @@ const handler = (data, dispatch, currState) => {
|
||||
break;
|
||||
case "reaction":
|
||||
{
|
||||
const removeContextMessage =
|
||||
to == "user" ? removeUserMsg : removeChannelMsg;
|
||||
const removeContextMessage = to == "user" ? removeUserMsg : removeChannelMsg;
|
||||
const { type, action, content, content_type, properties } = innerDetail;
|
||||
switch (type) {
|
||||
case "like":
|
||||
@@ -124,7 +113,7 @@ const handler = (data, dispatch, currState) => {
|
||||
from_uid,
|
||||
mid: detailMid,
|
||||
rid: mid,
|
||||
action,
|
||||
action
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -150,7 +139,7 @@ const handler = (data, dispatch, currState) => {
|
||||
content,
|
||||
content_type,
|
||||
properties,
|
||||
edited: true,
|
||||
edited: true
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
fetchEventSource,
|
||||
EventStreamContentType,
|
||||
} from "@microsoft/fetch-event-source";
|
||||
import { fetchEventSource, EventStreamContentType } from "@microsoft/fetch-event-source";
|
||||
import toast from "react-hot-toast";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
@@ -14,18 +11,15 @@ import {
|
||||
addChannel,
|
||||
removeChannel,
|
||||
updateChannel,
|
||||
updatePinMessage,
|
||||
updatePinMessage
|
||||
} from "../../../app/slices/channels";
|
||||
import {
|
||||
updateUsersVersion,
|
||||
updateReadChannels,
|
||||
updateReadUsers,
|
||||
updateMute,
|
||||
updateMute
|
||||
} from "../../../app/slices/footprint";
|
||||
import {
|
||||
updateUsersByLogs,
|
||||
updateUsersStatus,
|
||||
} from "../../../app/slices/contacts";
|
||||
import { updateUsersByLogs, updateUsersStatus } from "../../../app/slices/contacts";
|
||||
import { resetAuthData } from "../../../app/slices/auth.data";
|
||||
import chatMessageHandler from "./chat.handler";
|
||||
import store from "../../../app/store";
|
||||
@@ -52,7 +46,7 @@ export default function useStreaming() {
|
||||
const {
|
||||
authData: { uid: loginUid },
|
||||
ui: { ready, online },
|
||||
footprint: { afterMid, usersVersion, readUsers, readChannels },
|
||||
footprint: { afterMid, usersVersion, readUsers, readChannels }
|
||||
} = useSelector((store) => store);
|
||||
const [renewToken] = useRenewMutation();
|
||||
const dispatch = useDispatch();
|
||||
@@ -64,7 +58,7 @@ export default function useStreaming() {
|
||||
if (initialized || initializing) return;
|
||||
// 如果token快要过期,先renew
|
||||
const {
|
||||
authData: { token, expireTime = +new Date(), refreshToken },
|
||||
authData: { token, expireTime = +new Date(), refreshToken }
|
||||
} = store.getState();
|
||||
let api_token = token;
|
||||
const tokenAlmostExpire = dayjs().isAfter(new Date(expireTime - 20 * 1000));
|
||||
@@ -72,10 +66,10 @@ export default function useStreaming() {
|
||||
if (tokenAlmostExpire) {
|
||||
const {
|
||||
data: { token: newToken },
|
||||
isError,
|
||||
isError
|
||||
} = await renewToken({
|
||||
token,
|
||||
refreshToken,
|
||||
refreshToken
|
||||
});
|
||||
if (isError) return;
|
||||
api_token = newToken;
|
||||
@@ -87,25 +81,18 @@ export default function useStreaming() {
|
||||
`${BASE_URL}/user/events?${getQueryString({
|
||||
"api-key": api_token,
|
||||
users_version: usersVersion,
|
||||
after_mid: afterMid,
|
||||
after_mid: afterMid
|
||||
})}`,
|
||||
{
|
||||
openWhenHidden: true,
|
||||
signal: controller.signal,
|
||||
async onopen(response) {
|
||||
initializing = false;
|
||||
if (
|
||||
response.ok &&
|
||||
response.headers.get("content-type") === EventStreamContentType
|
||||
) {
|
||||
if (response.ok && response.headers.get("content-type") === EventStreamContentType) {
|
||||
console.log("sse everything ok");
|
||||
initialized = true;
|
||||
return; // everything's good
|
||||
} else if (
|
||||
response.status >= 400 &&
|
||||
response.status < 500 &&
|
||||
response.status !== 429
|
||||
) {
|
||||
} else if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
||||
// 重新登录
|
||||
// client-side errors are usually non-retriable:
|
||||
console.log("sse debug: open fatal");
|
||||
@@ -168,9 +155,7 @@ export default function useStreaming() {
|
||||
{
|
||||
const arr = data[key];
|
||||
if (arr && arr.length) {
|
||||
const _key = key.endsWith("users")
|
||||
? "add_users"
|
||||
: "add_groups";
|
||||
const _key = key.endsWith("users") ? "add_users" : "add_groups";
|
||||
dispatch(updateMute({ [_key]: arr }));
|
||||
}
|
||||
}
|
||||
@@ -180,9 +165,7 @@ export default function useStreaming() {
|
||||
{
|
||||
const arr = data[key];
|
||||
if (arr && arr.length) {
|
||||
const _key = key.endsWith("users")
|
||||
? "remove_users"
|
||||
: "remove_groups";
|
||||
const _key = key.endsWith("users") ? "remove_users" : "remove_groups";
|
||||
dispatch(updateMute({ [_key]: arr }));
|
||||
}
|
||||
}
|
||||
@@ -198,8 +181,7 @@ export default function useStreaming() {
|
||||
case "users_state_changed":
|
||||
{
|
||||
let { type, ...rest } = data;
|
||||
const onlines =
|
||||
type == "users_state_changed" ? [rest] : rest.users;
|
||||
const onlines = type == "users_state_changed" ? [rest] : rest.users;
|
||||
dispatch(updateUsersStatus(onlines));
|
||||
}
|
||||
break;
|
||||
@@ -234,7 +216,7 @@ export default function useStreaming() {
|
||||
dispatch(
|
||||
updateChannel({
|
||||
id: gid,
|
||||
...rest,
|
||||
...rest
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -248,7 +230,7 @@ export default function useStreaming() {
|
||||
updateChannel({
|
||||
operation: "add_member",
|
||||
id: gid,
|
||||
members: uids,
|
||||
members: uids
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -263,7 +245,7 @@ export default function useStreaming() {
|
||||
updateChannel({
|
||||
operation: "remove_member",
|
||||
id: gid,
|
||||
members: uids,
|
||||
members: uids
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -285,7 +267,7 @@ export default function useStreaming() {
|
||||
ready,
|
||||
loginUid,
|
||||
readUsers,
|
||||
readChannels,
|
||||
readChannels
|
||||
});
|
||||
}
|
||||
break;
|
||||
@@ -321,7 +303,7 @@ export default function useStreaming() {
|
||||
// do nothing to automatically retry. You can also
|
||||
// return a specific retry interval here.
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
initializing = false;
|
||||
@@ -354,6 +336,6 @@ export default function useStreaming() {
|
||||
return {
|
||||
setStreamingReady,
|
||||
startStreaming,
|
||||
stopStreaming,
|
||||
stopStreaming
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,10 +3,7 @@ import { useDispatch, useSelector } from "react-redux";
|
||||
// import { ContentTypes } from "../../app/config";
|
||||
import { updateUploadFiles } from "../../app/slices/ui";
|
||||
import BASE_URL, { FILE_SLICE_SIZE } from "../../app/config";
|
||||
import {
|
||||
usePrepareUploadFileMutation,
|
||||
useUploadFileMutation,
|
||||
} from "../../app/services/message";
|
||||
import { usePrepareUploadFileMutation, useUploadFileMutation } from "../../app/services/message";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function useUploadFile(props = {}) {
|
||||
@@ -15,7 +12,7 @@ export default function useUploadFile(props = {}) {
|
||||
const { stageFiles, replying } = useSelector((store) => {
|
||||
return {
|
||||
stageFiles: store.ui.uploadFiles[`${context}_${id}`] || [],
|
||||
replying: store.message.replying[`${context}_${id}`],
|
||||
replying: store.message.replying[`${context}_${id}`]
|
||||
};
|
||||
});
|
||||
const [data, setData] = useState(null);
|
||||
@@ -23,13 +20,11 @@ export default function useUploadFile(props = {}) {
|
||||
const canneledRef = useRef(false);
|
||||
const sliceUploadedCountRef = useRef(0);
|
||||
const totalSliceCountRef = useRef(1);
|
||||
const [
|
||||
prepareUploadFile,
|
||||
{ isLoading: isPreparing, isSuccess: isPrepared },
|
||||
] = usePrepareUploadFileMutation();
|
||||
const [prepareUploadFile, { isLoading: isPreparing, isSuccess: isPrepared }] =
|
||||
usePrepareUploadFileMutation();
|
||||
const [
|
||||
uploadFileFn,
|
||||
{ isLoading: isUploading, isSuccess: isUploaded, isError: uploadFileError },
|
||||
{ isLoading: isUploading, isSuccess: isUploaded, isError: uploadFileError }
|
||||
] = useUploadFileMutation();
|
||||
|
||||
const uploadChunk = (data) => {
|
||||
@@ -46,12 +41,12 @@ export default function useUploadFile(props = {}) {
|
||||
const {
|
||||
name = `rustchat-${+new Date()}.${file.type.split("/")[1]}`,
|
||||
type: file_type,
|
||||
size: file_size,
|
||||
size: file_size
|
||||
} = file;
|
||||
// 拿file id
|
||||
const { data: file_id } = await prepareUploadFile({
|
||||
content_type: file_type,
|
||||
filename: name,
|
||||
filename: name
|
||||
});
|
||||
console.log("file id", file_id);
|
||||
|
||||
@@ -76,17 +71,13 @@ export default function useUploadFile(props = {}) {
|
||||
// 退出循环
|
||||
if (canneledRef.current) break;
|
||||
try {
|
||||
const chunk = file.slice(
|
||||
FILE_SLICE_SIZE * idx,
|
||||
FILE_SLICE_SIZE * (idx + 1),
|
||||
file_type
|
||||
);
|
||||
const chunk = file.slice(FILE_SLICE_SIZE * idx, FILE_SLICE_SIZE * (idx + 1), file_type);
|
||||
|
||||
uploadResult = await uploadChunk({
|
||||
file_id,
|
||||
chunk,
|
||||
// 如果是最后一个chunk,标记下
|
||||
is_last: idx == _arr.length - 1,
|
||||
is_last: idx == _arr.length - 1
|
||||
});
|
||||
sliceUploadedCountRef.current++;
|
||||
} catch (error) {
|
||||
@@ -98,7 +89,7 @@ export default function useUploadFile(props = {}) {
|
||||
}
|
||||
// setUploadingFile(false);
|
||||
const {
|
||||
data: { path, size, hash },
|
||||
data: { path, size, hash }
|
||||
} = uploadResult;
|
||||
const encodedPath = encodeURIComponent(path);
|
||||
const res = {
|
||||
@@ -111,7 +102,7 @@ export default function useUploadFile(props = {}) {
|
||||
thumbnail: file_type.startsWith("image")
|
||||
? `${BASE_URL}/resource/file?file_path=${encodedPath}&thumbnail=true`
|
||||
: "",
|
||||
download: `${BASE_URL}/resource/file?file_path=${encodedPath}&download=true`,
|
||||
download: `${BASE_URL}/resource/file?file_path=${encodedPath}&download=true`
|
||||
};
|
||||
setData(res);
|
||||
return res;
|
||||
@@ -120,9 +111,7 @@ export default function useUploadFile(props = {}) {
|
||||
canneledRef.current = true;
|
||||
};
|
||||
const removeStageFile = (idx) => {
|
||||
dispatch(
|
||||
updateUploadFiles({ context, id, operation: "remove", index: idx })
|
||||
);
|
||||
dispatch(updateUploadFiles({ context, id, operation: "remove", index: idx }));
|
||||
};
|
||||
const addStageFile = (fileData = {}) => {
|
||||
if (replying) {
|
||||
@@ -141,7 +130,7 @@ export default function useUploadFile(props = {}) {
|
||||
id,
|
||||
operation: "update",
|
||||
index: idx,
|
||||
...data,
|
||||
...data
|
||||
})
|
||||
);
|
||||
};
|
||||
@@ -149,9 +138,7 @@ export default function useUploadFile(props = {}) {
|
||||
stopUploading,
|
||||
data,
|
||||
isUploading: isPreparing || isUploading,
|
||||
progress: Number(
|
||||
(sliceUploadedCountRef.current / totalSliceCountRef.current) * 100
|
||||
).toFixed(2),
|
||||
progress: Number((sliceUploadedCountRef.current / totalSliceCountRef.current) * 100).toFixed(2),
|
||||
uploadFile,
|
||||
isError: uploadFileError,
|
||||
isSuccess: !!data,
|
||||
@@ -159,6 +146,6 @@ export default function useUploadFile(props = {}) {
|
||||
addStageFile,
|
||||
resetStageFiles,
|
||||
removeStageFile,
|
||||
updateStageFile,
|
||||
updateStageFile
|
||||
};
|
||||
}
|
||||
|
||||
+17
-38
@@ -98,7 +98,7 @@ export const getInitialsAvatar = ({
|
||||
foreground = "#fff",
|
||||
background = "#4c99e9",
|
||||
weight = 400,
|
||||
fontFamily = "'Lato', 'Lato-Regular', 'Helvetica Neue'",
|
||||
fontFamily = "'Lato', 'Lato-Regular', 'Helvetica Neue'"
|
||||
}) => {
|
||||
const canvas = document.createElement("canvas");
|
||||
const width = size;
|
||||
@@ -154,7 +154,7 @@ export const getFileIcon = (type, name = "") => {
|
||||
video: /^video/gi,
|
||||
code: /(json|javascript|java|rb|c|php|xml|css|html)$/gi,
|
||||
doc: /^text/gi,
|
||||
pdf: /\/pdf$/gi,
|
||||
pdf: /\/pdf$/gi
|
||||
};
|
||||
const _arr = name.split(".");
|
||||
const _type = type || _arr[_arr.length - 1];
|
||||
@@ -187,56 +187,35 @@ export const getFileIcon = (type, name = "") => {
|
||||
}
|
||||
return icon;
|
||||
};
|
||||
export const normalizeArchiveData = (
|
||||
data = null,
|
||||
filePath = null,
|
||||
uid = null
|
||||
) => {
|
||||
export const normalizeArchiveData = (data = null, filePath = null, uid = null) => {
|
||||
if (!data || !filePath) return [];
|
||||
const { messages, users } = data;
|
||||
const getUrls = (
|
||||
uid,
|
||||
{ content, content_type, file_id, thumbnail_id, filePath, avatar }
|
||||
) => {
|
||||
const getUrls = (uid, { content, content_type, file_id, thumbnail_id, filePath, avatar }) => {
|
||||
// uid存在,则favorite,否则archive
|
||||
const prefix = uid
|
||||
? `${BASE_URL}/favorite/attachment/${uid}/${filePath}/`
|
||||
: `${BASE_URL}/resource/archive/attachment?file_path=${filePath}&attachment_id=`;
|
||||
return {
|
||||
transformedContent:
|
||||
content_type == ContentTypes.file ? `${prefix}${file_id}` : content,
|
||||
thumbnail:
|
||||
content_type == ContentTypes.file ? `${prefix}${thumbnail_id}` : "",
|
||||
transformedContent: content_type == ContentTypes.file ? `${prefix}${file_id}` : content,
|
||||
thumbnail: content_type == ContentTypes.file ? `${prefix}${thumbnail_id}` : "",
|
||||
download:
|
||||
content_type == ContentTypes.file
|
||||
? `${prefix}${file_id}${uid ? "?" : "&"}download=true`
|
||||
: "",
|
||||
avatarUrl: avatar !== null ? `${prefix}${avatar}` : "",
|
||||
avatarUrl: avatar !== null ? `${prefix}${avatar}` : ""
|
||||
};
|
||||
};
|
||||
return messages.map(
|
||||
({
|
||||
source,
|
||||
mid,
|
||||
content,
|
||||
file_id,
|
||||
thumbnail_id,
|
||||
content_type,
|
||||
properties,
|
||||
from_user,
|
||||
}) => {
|
||||
({ source, mid, content, file_id, thumbnail_id, content_type, properties, from_user }) => {
|
||||
let user = { ...(users[from_user] || {}) };
|
||||
const { transformedContent, thumbnail, download, avatarUrl } = getUrls(
|
||||
uid,
|
||||
{
|
||||
content,
|
||||
content_type,
|
||||
filePath,
|
||||
file_id,
|
||||
thumbnail_id,
|
||||
avatar: user.avatar,
|
||||
}
|
||||
);
|
||||
const { transformedContent, thumbnail, download, avatarUrl } = getUrls(uid, {
|
||||
content,
|
||||
content_type,
|
||||
filePath,
|
||||
file_id,
|
||||
thumbnail_id,
|
||||
avatar: user.avatar
|
||||
});
|
||||
|
||||
user.avatar = avatarUrl;
|
||||
|
||||
@@ -249,7 +228,7 @@ export const normalizeArchiveData = (
|
||||
content_type,
|
||||
properties,
|
||||
download,
|
||||
thumbnail,
|
||||
thumbnail
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user