chore: update prettier setting

This commit is contained in:
zerosoul
2022-06-12 15:30:14 +08:00
parent 14b4678d9e
commit 516794d352
209 changed files with 2435 additions and 4588 deletions
+5 -16
View File
@@ -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} />}
</>
);
}
+2 -2
View File
@@ -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 {
+2 -4
View File
@@ -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}
+6 -13
View File
@@ -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} />}
</>
);
}
+4 -12
View File
@@ -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>
+1 -5
View File
@@ -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 />}
+6 -18
View File
@@ -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>
+9 -16
View File
@@ -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
}
]}
/>
}
+4 -12
View File
@@ -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 />}
+1 -5
View File
@@ -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">
+2 -4
View File
@@ -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();
+1 -5
View File
@@ -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>
</>
+1 -3
View File
@@ -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>
);
}
+7 -11
View File
@@ -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}
+7 -23
View File
@@ -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>
)}
+8 -26
View File
@@ -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>
+1 -5
View File
@@ -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();
+3 -16
View File
@@ -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>
+4 -15
View File
@@ -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>
+4 -12
View File
@@ -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>
</>
+3 -14
View File
@@ -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} />;
}
+2 -11
View File
@@ -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>
+11 -33
View File
@@ -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>
)}
+1 -1
View File
@@ -11,6 +11,6 @@ export default function usePrompt() {
return {
setCanneled: setPrompt,
prompted: !!localStorage.getItem(KEY_PWA_INSTALLED),
resetPrompt,
resetPrompt
};
}
+1 -1
View File
@@ -16,7 +16,7 @@ function MarkdownEditor({
height = "50vh",
placeholder,
sendMarkdown,
setEditorInstance,
setEditorInstance
}) {
const editorRef = useRef(undefined);
const { uploadFile } = useUploadFile();
+9 -17
View File
@@ -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
}
]}
/>
}
+10 -10
View File
@@ -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
}
]}
/>
}
+2 -3
View File
@@ -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>
+3 -12
View File
@@ -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>
+4 -9
View File
@@ -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
+1 -5
View File
@@ -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 {
+19 -25
View File
@@ -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} />}
+9 -24
View File
@@ -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
};
}
+15 -15
View File
@@ -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 -39
View File
@@ -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;
+4 -4
View File
@@ -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]
};
};
+4 -8
View File
@@ -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
+2 -8
View File
@@ -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) => {
+4 -5
View File
@@ -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} />
+1 -2
View File
@@ -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;
+1 -1
View File
@@ -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 = "" }) {
+1 -2
View File
@@ -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;
+1 -6
View File
@@ -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>
+2 -7
View File
@@ -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>
);
+6 -14
View File
@@ -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 = (
+1 -1
View File
@@ -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
+11 -18
View File
@@ -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}`}>
+1 -1
View File
@@ -8,6 +8,6 @@ export default function useFiles(initialFiles = []) {
return {
files,
setFiles,
resetFiles,
resetFiles
};
}
+2 -7
View File
@@ -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>
);
+1 -1
View File
@@ -4,7 +4,7 @@ export default function TippyDefault() {
tippy.setDefaultProps({
duration: 0,
delay: [0, 0],
plugins: [followCursor],
plugins: [followCursor]
});
return null;
}
+1 -2
View File
@@ -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;
+1 -3
View File
@@ -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);
+6 -21
View File
@@ -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>
</>
+3 -11
View File
@@ -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;
+6 -6
View File
@@ -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;
+4 -21
View File
@@ -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} />
+1 -2
View File
@@ -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 {
+75 -75
View File
@@ -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>
);
}
+1 -3
View File
@@ -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}
+11 -26
View File
@@ -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
};
}
+4 -10
View File
@@ -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
};
}
+1 -1
View File
@@ -49,6 +49,6 @@ export default function useContextMenu(placement = "right-start") {
offset,
visible,
hideContextMenu,
handleContextMenuEvent,
handleContextMenuEvent
};
}
+1 -3
View File
@@ -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);
+3 -3
View File
@@ -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
};
}
+1 -1
View File
@@ -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
};
});
+3 -6
View File
@@ -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
};
}
+1 -1
View File
@@ -23,6 +23,6 @@ export default function useFilteredChannels() {
return {
input,
channels: filteredChannels,
updateInput,
updateInput
};
}
+1 -1
View File
@@ -23,6 +23,6 @@ export default function useFilteredUsers() {
return {
input,
contacts: filteredUsers,
updateInput,
updateInput
};
}
+7 -17
View File
@@ -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
};
}
+4 -6
View File
@@ -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
};
}
+4 -7
View File
@@ -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
};
}
+8 -17
View File
@@ -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
};
}
+5 -13
View File
@@ -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
};
}
+14 -22
View File
@@ -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
};
}
+3 -5
View File
@@ -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
};
}
+9 -15
View File
@@ -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 });
+1 -1
View File
@@ -12,6 +12,6 @@ export default function usePWABadge() {
}, []);
return {
isSupported: badge.isSupported(),
isSupported: badge.isSupported()
};
}
+5 -10
View File
@@ -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
};
}
+1 -2
View File
@@ -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));
+13 -31
View File
@@ -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
};
}
+13 -24
View File
@@ -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
})
);
}
+20 -38
View File
@@ -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
};
}
+15 -28
View File
@@ -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
View File
@@ -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
};
}
);