chore: update prettier setting
This commit is contained in:
@@ -42,9 +42,7 @@ const Styled = styled.ul`
|
||||
}
|
||||
`;
|
||||
export default function AddEntriesMenu() {
|
||||
const currentUser = useSelector(
|
||||
(store) => store.contacts.byId[store.authData.uid]
|
||||
);
|
||||
const currentUser = useSelector((store) => store.contacts.byId[store.authData.uid]);
|
||||
const [isPrivate, setIsPrivate] = useState(false);
|
||||
const [inviteModalVisible, setInviteModalVisible] = useState(false);
|
||||
|
||||
@@ -78,10 +76,7 @@ export default function AddEntriesMenu() {
|
||||
<>
|
||||
<Styled>
|
||||
{currentUser?.is_admin && (
|
||||
<li
|
||||
className="item"
|
||||
onClick={handleOpenChannelModal.bind(null, false)}
|
||||
>
|
||||
<li className="item" onClick={handleOpenChannelModal.bind(null, false)}>
|
||||
<ChannelIcon className="icon" />
|
||||
New Channel
|
||||
</li>
|
||||
@@ -99,15 +94,9 @@ export default function AddEntriesMenu() {
|
||||
Invite People
|
||||
</li>
|
||||
</Styled>
|
||||
{channelModalVisible && (
|
||||
<ChannelModal personal={isPrivate} closeModal={handleCloseModal} />
|
||||
)}
|
||||
{contactsModalVisible && (
|
||||
<ContactsModal closeModal={toggleContactsModalVisible} />
|
||||
)}
|
||||
{inviteModalVisible && (
|
||||
<InviteModal closeModal={toggleInviteModalVisible} />
|
||||
)}
|
||||
{channelModalVisible && <ChannelModal personal={isPrivate} closeModal={handleCloseModal} />}
|
||||
{contactsModalVisible && <ContactsModal closeModal={toggleContactsModalVisible} />}
|
||||
{inviteModalVisible && <InviteModal closeModal={toggleInviteModalVisible} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ const Avatar = ({ url = "", name = "unkonw name", type = "user", ...rest }) => {
|
||||
const tmp = getInitialsAvatar({
|
||||
initials: getInitials(name),
|
||||
background: type == "channel" ? "#EAECF0" : undefined,
|
||||
foreground: type == "channel" ? "#475467" : undefined,
|
||||
foreground: type == "channel" ? "#475467" : undefined
|
||||
});
|
||||
setSrc(tmp);
|
||||
};
|
||||
@@ -17,7 +17,7 @@ const Avatar = ({ url = "", name = "unkonw name", type = "user", ...rest }) => {
|
||||
const tmp = getInitialsAvatar({
|
||||
initials: getInitials(name),
|
||||
background: type == "channel" ? "#EAECF0" : undefined,
|
||||
foreground: type == "channel" ? "#475467" : undefined,
|
||||
foreground: type == "channel" ? "#475467" : undefined
|
||||
});
|
||||
setSrc(tmp);
|
||||
} else {
|
||||
|
||||
@@ -64,7 +64,7 @@ export default function AvatarUploader({
|
||||
name = "",
|
||||
type = "user",
|
||||
uploadImage,
|
||||
disabled = false,
|
||||
disabled = false
|
||||
}) {
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const handleUpload = async (evt) => {
|
||||
@@ -80,9 +80,7 @@ export default function AvatarUploader({
|
||||
<Avatar type={type} url={url} name={name} />
|
||||
{!disabled && (
|
||||
<>
|
||||
<div className="tip">
|
||||
{uploading ? `Uploading` : `Change Avatar`}
|
||||
</div>
|
||||
<div className="tip">{uploading ? `Uploading` : `Change Avatar`}</div>
|
||||
<input
|
||||
multiple={false}
|
||||
onChange={handleUpload}
|
||||
|
||||
@@ -80,19 +80,16 @@ export default function BlankPlaceholder({ type = "chat" }) {
|
||||
setInviteModalVisible((prev) => !prev);
|
||||
};
|
||||
const chatTip =
|
||||
type == "chat"
|
||||
? "Create a Channel to Start a Conversation"
|
||||
: "Send a Direct Message";
|
||||
const chatHanlder =
|
||||
type == "chat" ? toggleChannelModalVisible : toggleContactListVisible;
|
||||
type == "chat" ? "Create a Channel to Start a Conversation" : "Send a Direct Message";
|
||||
const chatHanlder = type == "chat" ? toggleChannelModalVisible : toggleContactListVisible;
|
||||
return (
|
||||
<>
|
||||
<Styled>
|
||||
<div className="head">
|
||||
<h2 className="title">Welcome to {server.name} server</h2>
|
||||
<p className="desc">
|
||||
Here are some steps to help you get started. For more, check out our
|
||||
Getting Started guide
|
||||
Here are some steps to help you get started. For more, check out our Getting Started
|
||||
guide
|
||||
</p>
|
||||
</div>
|
||||
<div className="boxes">
|
||||
@@ -117,12 +114,8 @@ export default function BlankPlaceholder({ type = "chat" }) {
|
||||
{createChannelVisible && (
|
||||
<ChannelModal personal={true} closeModal={toggleChannelModalVisible} />
|
||||
)}
|
||||
{contactListVisible && (
|
||||
<ContactsModal closeModal={toggleContactListVisible} />
|
||||
)}
|
||||
{inviteModalVisible && (
|
||||
<InviteModal closeModal={toggleInviteModalVisible} />
|
||||
)}
|
||||
{contactListVisible && <ContactsModal closeModal={toggleContactListVisible} />}
|
||||
{inviteModalVisible && <InviteModal closeModal={toggleInviteModalVisible} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,16 +47,11 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function Channel({
|
||||
interactive = true,
|
||||
id = "",
|
||||
compact = false,
|
||||
avatarSize = 32,
|
||||
}) {
|
||||
export default function Channel({ interactive = true, id = "", compact = false, avatarSize = 32 }) {
|
||||
const { channel, totalMemberCount } = useSelector((store) => {
|
||||
return {
|
||||
channel: store.channels.byId[id],
|
||||
totalMemberCount: store.contacts.ids.length,
|
||||
totalMemberCount: store.contacts.ids.length
|
||||
};
|
||||
});
|
||||
console.log("channel item", id, channel);
|
||||
@@ -65,17 +60,14 @@ export default function Channel({
|
||||
return (
|
||||
<StyledWrapper
|
||||
size={avatarSize}
|
||||
className={`${interactive ? "interactive" : ""} ${
|
||||
compact ? "compact" : ""
|
||||
}`}
|
||||
className={`${interactive ? "interactive" : ""} ${compact ? "compact" : ""}`}
|
||||
>
|
||||
<div className="avatar">
|
||||
<Avatar type="channel" url={avatar} name={"#"} alt="avatar" />
|
||||
</div>
|
||||
{!compact && (
|
||||
<div className="name">
|
||||
<span className="txt">{name}</span> (
|
||||
{is_public ? totalMemberCount : members.length})
|
||||
<span className="txt">{name}</span> ({is_public ? totalMemberCount : members.length})
|
||||
</div>
|
||||
)}
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -8,11 +8,7 @@ const Styled = styled.div`
|
||||
fill: #d0d5dd;
|
||||
}
|
||||
`;
|
||||
export default function ChannelIcon({
|
||||
personal = false,
|
||||
muted = false,
|
||||
className,
|
||||
}) {
|
||||
export default function ChannelIcon({ personal = false, muted = false, className }) {
|
||||
return (
|
||||
<Styled className={`${muted ? "muted" : ""} ${className}`}>
|
||||
{personal ? <LockHashIcon /> : <HashIcon />}
|
||||
|
||||
@@ -22,13 +22,11 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
||||
name: "",
|
||||
dsecription: "",
|
||||
members: [loginUid],
|
||||
is_public: !personal,
|
||||
is_public: !personal
|
||||
});
|
||||
const { contacts, input, updateInput } = useFilteredUsers();
|
||||
const [
|
||||
createChannel,
|
||||
{ isSuccess, isError, isLoading, data: newChannelId },
|
||||
] = useCreateChannelMutation();
|
||||
const [createChannel, { isSuccess, isError, isLoading, data: newChannelId }] =
|
||||
useCreateChannelMutation();
|
||||
|
||||
const handleToggle = () => {
|
||||
const { is_public } = data;
|
||||
@@ -72,9 +70,7 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
||||
const toggleCheckMember = ({ currentTarget }) => {
|
||||
const { members } = data;
|
||||
const { uid } = currentTarget.dataset;
|
||||
let tmp = members.includes(+uid)
|
||||
? members.filter((m) => m != uid)
|
||||
: [...members, +uid];
|
||||
let tmp = members.includes(+uid) ? members.filter((m) => m != uid) : [...members, +uid];
|
||||
console.log(uid, currentTarget);
|
||||
setData((prev) => {
|
||||
return { ...prev, members: tmp };
|
||||
@@ -135,11 +131,7 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
||||
<div className="name">
|
||||
<span className="label normal">Channel Name</span>
|
||||
<div className="input">
|
||||
<input
|
||||
onChange={handleNameInput}
|
||||
value={name}
|
||||
placeholder="new channel"
|
||||
/>
|
||||
<input onChange={handleNameInput} value={name} placeholder="new channel" />
|
||||
<ChannelIcon personal={!is_public} className="icon" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -155,11 +147,7 @@ export default function ChannelModal({ personal = false, closeModal }) {
|
||||
<Button onClick={closeModal} className="normal cancel">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
onClick={handleCreate}
|
||||
className="normal"
|
||||
>
|
||||
<Button disabled={isLoading} onClick={handleCreate} className="normal">
|
||||
Create
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -4,14 +4,7 @@ import Tippy from "@tippyjs/react";
|
||||
import useContactOperation from "../../hook/useContactOperation";
|
||||
import ContextMenu from "../ContextMenu";
|
||||
|
||||
export default function ContactContextMenu({
|
||||
enable = false,
|
||||
uid,
|
||||
cid,
|
||||
visible,
|
||||
hide,
|
||||
children,
|
||||
}) {
|
||||
export default function ContactContextMenu({ enable = false, uid, cid, visible, hide, children }) {
|
||||
const {
|
||||
canCall,
|
||||
call,
|
||||
@@ -21,10 +14,10 @@ export default function ContactContextMenu({
|
||||
canRemove,
|
||||
canRemoveFromChannel,
|
||||
removeFromChannel,
|
||||
removeUser,
|
||||
removeUser
|
||||
} = useContactOperation({
|
||||
uid,
|
||||
cid,
|
||||
cid
|
||||
});
|
||||
return (
|
||||
<>
|
||||
@@ -43,26 +36,26 @@ export default function ContactContextMenu({
|
||||
items={[
|
||||
{
|
||||
title: "Message",
|
||||
handler: startChat,
|
||||
handler: startChat
|
||||
},
|
||||
canCall && {
|
||||
title: "Call",
|
||||
handler: call,
|
||||
handler: call
|
||||
},
|
||||
canCopyEmail && {
|
||||
title: "Copy Email",
|
||||
handler: copyEmail,
|
||||
handler: copyEmail
|
||||
},
|
||||
canRemoveFromChannel && {
|
||||
danger: true,
|
||||
title: "Remove From Channel",
|
||||
handler: removeFromChannel,
|
||||
handler: removeFromChannel
|
||||
},
|
||||
canRemove && {
|
||||
danger: true,
|
||||
title: "Remove From Server",
|
||||
handler: removeUser,
|
||||
},
|
||||
handler: removeUser
|
||||
}
|
||||
]}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -17,14 +17,10 @@ export default function Contact({
|
||||
popover = false,
|
||||
compact = false,
|
||||
avatarSize = 32,
|
||||
enableContextMenu = false,
|
||||
enableContextMenu = false
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
visible: contextMenuVisible,
|
||||
handleContextMenuEvent,
|
||||
hideContextMenu,
|
||||
} = useContextMenu();
|
||||
const { visible: contextMenuVisible, handleContextMenuEvent, hideContextMenu } = useContextMenu();
|
||||
const curr = useSelector((store) => store.contacts.byId[uid]);
|
||||
const handleDoubleClick = () => {
|
||||
navigate(`/chat/dm/${uid}`);
|
||||
@@ -50,15 +46,11 @@ export default function Contact({
|
||||
onContextMenu={enableContextMenu ? handleContextMenuEvent : null}
|
||||
size={avatarSize}
|
||||
onDoubleClick={dm ? handleDoubleClick : null}
|
||||
className={`${interactive ? "interactive" : ""} ${
|
||||
compact ? "compact" : ""
|
||||
}`}
|
||||
className={`${interactive ? "interactive" : ""} ${compact ? "compact" : ""}`}
|
||||
>
|
||||
<div className="avatar">
|
||||
<Avatar url={curr.avatar} name={curr.name} alt="avatar" />
|
||||
<div
|
||||
className={`status ${curr.online ? "online" : "offline"}`}
|
||||
></div>
|
||||
<div className={`status ${curr.online ? "online" : "offline"}`}></div>
|
||||
</div>
|
||||
{!compact && <span className="name">{curr?.name}</span>}
|
||||
{owner && <IconOwner />}
|
||||
|
||||
@@ -64,11 +64,7 @@ export default function ContactsModal({ closeModal }) {
|
||||
<Modal>
|
||||
<StyledWrapper ref={wrapperRef}>
|
||||
<div className="search">
|
||||
<input
|
||||
value={input}
|
||||
onChange={handleSearch}
|
||||
placeholder="Type Username to search"
|
||||
/>
|
||||
<input value={input} onChange={handleSearch} placeholder="Type Username to search" />
|
||||
</div>
|
||||
{contacts && (
|
||||
<ul className="users">
|
||||
|
||||
@@ -16,13 +16,11 @@ export default function ContextMenu({ items = [], hideMenu = null }) {
|
||||
}
|
||||
},
|
||||
underline = false,
|
||||
danger = false,
|
||||
danger = false
|
||||
} = item;
|
||||
return (
|
||||
<li
|
||||
className={`item ${underline ? "underline" : ""} ${
|
||||
danger ? "danger" : ""
|
||||
}`}
|
||||
className={`item ${underline ? "underline" : ""} ${danger ? "danger" : ""}`}
|
||||
key={title}
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation();
|
||||
|
||||
@@ -25,11 +25,7 @@ export default function DeleteMessageConfirmModal({ closeModal, mids = [] }) {
|
||||
<Button className="cancel" onClick={closeModal.bind(null, false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={isDeleting}
|
||||
onClick={handleDelete}
|
||||
className="danger"
|
||||
>
|
||||
<Button disabled={isDeleting} onClick={handleDelete} className="danger">
|
||||
{isDeleting ? "Deleting" : `Delete`}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@@ -13,9 +13,7 @@ export default function FAQ() {
|
||||
<Styled>
|
||||
<div className="item">Client Version: {process.env.VERSION}</div>
|
||||
<div className="item">Server Version: {serverVersion}</div>
|
||||
<div className="item">
|
||||
Build Timestamp: {process.env.REACT_APP_BUILD_TIME}
|
||||
</div>
|
||||
<div className="item">Build Timestamp: {process.env.REACT_APP_BUILD_TIME}</div>
|
||||
</Styled>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
ImagePreview,
|
||||
PdfPreview,
|
||||
CodePreview,
|
||||
DocPreview,
|
||||
DocPreview
|
||||
} from "./preview";
|
||||
import { getFileIcon, formatBytes } from "../../utils";
|
||||
import IconDownload from "../../../assets/icons/download.svg";
|
||||
@@ -24,7 +24,7 @@ const renderPreview = (data) => {
|
||||
video: /^video/gi,
|
||||
code: /(json|javascript|java|rb|c|php|xml|css|html)$/gi,
|
||||
doc: /^text/gi,
|
||||
pdf: /\/pdf$/gi,
|
||||
pdf: /\/pdf$/gi
|
||||
};
|
||||
const _arr = name.split(".");
|
||||
const _type = file_type || _arr[_arr.length - 1];
|
||||
@@ -65,7 +65,7 @@ export default function FileBox({
|
||||
size,
|
||||
created_at,
|
||||
from_uid,
|
||||
content,
|
||||
content
|
||||
}) {
|
||||
const fromUser = useSelector((store) => store.contacts.byId[from_uid]);
|
||||
const icon = getFileIcon(file_type, name);
|
||||
@@ -75,9 +75,9 @@ export default function FileBox({
|
||||
const withPreview = preview && previewContent;
|
||||
return (
|
||||
<Styled
|
||||
className={`file_box ${flex ? "flex" : ""} ${
|
||||
withPreview ? "preview" : ""
|
||||
} ${file_type.startsWith("audio") ? "audio" : ""}`}
|
||||
className={`file_box ${flex ? "flex" : ""} ${withPreview ? "preview" : ""} ${
|
||||
file_type.startsWith("audio") ? "audio" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="basic">
|
||||
{icon}
|
||||
@@ -91,11 +91,7 @@ export default function FileBox({
|
||||
</i>
|
||||
</span>
|
||||
</div>
|
||||
<a
|
||||
className="download"
|
||||
download={name}
|
||||
href={`${content}&download=true`}
|
||||
>
|
||||
<a className="download" download={name} href={`${content}&download=true`}>
|
||||
<IconDownload />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -5,11 +5,4 @@ import PdfPreview from "./Pdf";
|
||||
import CodePreview from "./Code";
|
||||
import DocPreview from "./Doc";
|
||||
|
||||
export {
|
||||
VideoPreview,
|
||||
AudioPreview,
|
||||
ImagePreview,
|
||||
PdfPreview,
|
||||
CodePreview,
|
||||
DocPreview,
|
||||
};
|
||||
export { VideoPreview, AudioPreview, ImagePreview, PdfPreview, CodePreview, DocPreview };
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function ImageMessage({
|
||||
thumbnail,
|
||||
download,
|
||||
content,
|
||||
properties = {},
|
||||
properties = {}
|
||||
}) {
|
||||
const [url, setUrl] = useState(thumbnail);
|
||||
const { width = 0, height = 0 } = getDefaultSize(properties);
|
||||
@@ -64,7 +64,7 @@ export default function ImageMessage({
|
||||
strokeWidth={50}
|
||||
styles={buildStyles({
|
||||
storke: "#000",
|
||||
strokeLinecap: "butt",
|
||||
strokeLinecap: "butt"
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
@@ -74,7 +74,7 @@ export default function ImageMessage({
|
||||
className="img preview"
|
||||
style={{
|
||||
width: width ? `${width}px` : "",
|
||||
height: height ? `${height}px` : "",
|
||||
height: height ? `${height}px` : ""
|
||||
}}
|
||||
data-meta={JSON.stringify(properties)}
|
||||
data-origin={content}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function FileMessage({
|
||||
content = "",
|
||||
download = "",
|
||||
thumbnail = "",
|
||||
properties = { local_id: 0, name: "", size: 0, content_type: "" },
|
||||
properties = { local_id: 0, name: "", size: 0, content_type: "" }
|
||||
}) {
|
||||
const [imageSize, setImageSize] = useState(null);
|
||||
const [uploadingFile, setUploadingFile] = useState(false);
|
||||
@@ -31,19 +31,13 @@ export default function FileMessage({
|
||||
const {
|
||||
sendMessage,
|
||||
isSuccess: sendMessageSuccess,
|
||||
isSending,
|
||||
isSending
|
||||
} = useSendMessage({
|
||||
context,
|
||||
from: from_uid,
|
||||
to,
|
||||
to
|
||||
});
|
||||
const {
|
||||
stopUploading,
|
||||
data,
|
||||
uploadFile,
|
||||
progress,
|
||||
isSuccess: uploadSuccess,
|
||||
} = useUploadFile();
|
||||
const { stopUploading, data, uploadFile, progress, isSuccess: uploadSuccess } = useUploadFile();
|
||||
const fromUser = useSelector((store) => store.contacts.byId[from_uid]);
|
||||
const { size, name, content_type } = properties ?? {};
|
||||
useEffect(() => {
|
||||
@@ -75,20 +69,14 @@ export default function FileMessage({
|
||||
const propsV2 = imageSize ? { ...props, ...imageSize } : props;
|
||||
// 本地文件 并且上传成功
|
||||
if (uploadSuccess && isLocalFile(content)) {
|
||||
console.log(
|
||||
"send local file message",
|
||||
uploadSuccess,
|
||||
propsV2,
|
||||
data,
|
||||
content
|
||||
);
|
||||
console.log("send local file message", uploadSuccess, propsV2, data, content);
|
||||
// 把已经上传的东西当做消息发出去
|
||||
const { path } = data;
|
||||
sendMessage({
|
||||
ignoreLocal: true,
|
||||
type: "file",
|
||||
content: { path },
|
||||
properties: propsV2,
|
||||
properties: propsV2
|
||||
});
|
||||
}
|
||||
}, [uploadSuccess, data, properties, content]);
|
||||
@@ -151,11 +139,7 @@ export default function FileMessage({
|
||||
{sending ? (
|
||||
<IconClose className="cancel" onClick={handleCancel} />
|
||||
) : (
|
||||
<a
|
||||
className="download"
|
||||
download={name}
|
||||
href={`${content}&download=true`}
|
||||
>
|
||||
<a className="download" download={name} href={`${content}&download=true`}>
|
||||
<IconDownload />
|
||||
</a>
|
||||
)}
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
const {
|
||||
channels,
|
||||
// input: channelInput,
|
||||
updateInput: updateChannelInput,
|
||||
updateInput: updateChannelInput
|
||||
} = useFilteredChannels();
|
||||
const { contacts, input, updateInput } = useFilteredUsers();
|
||||
// const { conactsData, loginUid } = useSelector((store) => {
|
||||
@@ -34,8 +34,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
const toggleCheck = ({ currentTarget }) => {
|
||||
const { id, type = "user" } = currentTarget.dataset;
|
||||
const ids = type == "user" ? selectedMembers : selectedChannels;
|
||||
const updateState =
|
||||
type == "user" ? setSelectedMembers : setSelectedChannels;
|
||||
const updateState = type == "user" ? setSelectedMembers : setSelectedChannels;
|
||||
let tmp = ids.includes(+id) ? ids.filter((m) => m != id) : [...ids, +id];
|
||||
console.log(id, currentTarget);
|
||||
updateState(tmp);
|
||||
@@ -47,13 +46,13 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
await forwardMessage({
|
||||
mids: mids.map((mid) => +mid),
|
||||
users: selectedMembers,
|
||||
channels: selectedChannels,
|
||||
channels: selectedChannels
|
||||
});
|
||||
if (appendText.trim()) {
|
||||
await sendMessages({
|
||||
content: appendText,
|
||||
users: selectedMembers,
|
||||
channels: selectedChannels,
|
||||
channels: selectedChannels
|
||||
});
|
||||
}
|
||||
toast.success("Forward Message Successfully");
|
||||
@@ -99,12 +98,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
className="user channel"
|
||||
onClick={toggleCheck}
|
||||
>
|
||||
<StyledCheckbox
|
||||
readOnly
|
||||
checked={checked}
|
||||
name="cb"
|
||||
id="cb"
|
||||
/>
|
||||
<StyledCheckbox readOnly checked={checked} name="cb" id="cb" />
|
||||
<Channel id={gid} interactive={false} />
|
||||
</li>
|
||||
);
|
||||
@@ -122,12 +116,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
className="user"
|
||||
onClick={toggleCheck}
|
||||
>
|
||||
<StyledCheckbox
|
||||
readOnly
|
||||
checked={checked}
|
||||
name="cb"
|
||||
id="cb"
|
||||
/>
|
||||
<StyledCheckbox readOnly checked={checked} name="cb" id="cb" />
|
||||
<Contact uid={uid} interactive={false} />
|
||||
</li>
|
||||
);
|
||||
@@ -162,10 +151,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
interactive={false}
|
||||
// avatarSize={40}
|
||||
/>
|
||||
<CloseIcon
|
||||
className="remove"
|
||||
onClick={removeSelected.bind(null, uid, "user")}
|
||||
/>
|
||||
<CloseIcon className="remove" onClick={removeSelected.bind(null, uid, "user")} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
@@ -185,11 +171,7 @@ export default function ForwardModal({ mids, closeModal }) {
|
||||
<Button onClick={closeModal} className="normal cancel">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
className="normal"
|
||||
disabled={sendButtonDisabled}
|
||||
onClick={handleForward}
|
||||
>
|
||||
<Button className="normal" disabled={sendButtonDisabled} onClick={handleForward}>
|
||||
Send To {selectedCount == 0 ? null : `(${selectedCount})`}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -58,11 +58,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function ImagePreviewModal({
|
||||
download = true,
|
||||
data,
|
||||
closeModal,
|
||||
}) {
|
||||
export default function ImagePreviewModal({ download = true, data, closeModal }) {
|
||||
const [url, setUrl] = useState(data?.thumbnail);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const wrapperRef = useRef();
|
||||
|
||||
@@ -38,28 +38,15 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
`;
|
||||
export default function InviteLink() {
|
||||
const {
|
||||
generating,
|
||||
link,
|
||||
linkCopied,
|
||||
copyLink,
|
||||
generateNewLink,
|
||||
} = useInviteLink();
|
||||
const { generating, link, linkCopied, copyLink, generateNewLink } = useInviteLink();
|
||||
const handleNewLink = () => {
|
||||
generateNewLink();
|
||||
};
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<span className="tip">
|
||||
Share this link to invite people to this server.
|
||||
</span>
|
||||
<span className="tip">Share this link to invite people to this server.</span>
|
||||
<div className="link">
|
||||
<Input
|
||||
readOnly
|
||||
className={"large"}
|
||||
placeholder="Generating"
|
||||
value={link}
|
||||
/>
|
||||
<Input readOnly className={"large"} placeholder="Generating" value={link} />
|
||||
<Button onClick={copyLink} className="ghost small border_less">
|
||||
{linkCopied ? "Copied" : `Copy`}
|
||||
</Button>
|
||||
|
||||
@@ -97,15 +97,12 @@ const Styled = styled.div`
|
||||
}
|
||||
`;
|
||||
export default function AddMembers({ cid = null, closeModal }) {
|
||||
const [
|
||||
addMembers,
|
||||
{ isLoading: isAdding, isSuccess },
|
||||
] = useAddMembersMutation();
|
||||
const [addMembers, { isLoading: isAdding, isSuccess }] = useAddMembersMutation();
|
||||
const [selects, setSelects] = useState([]);
|
||||
const { channel, contactData } = useSelector((store) => {
|
||||
return {
|
||||
channel: store.channels.byId[cid],
|
||||
contactData: store.contacts.byId,
|
||||
contactData: store.contacts.byId
|
||||
};
|
||||
});
|
||||
useEffect(() => {
|
||||
@@ -145,11 +142,7 @@ export default function AddMembers({ cid = null, closeModal }) {
|
||||
return (
|
||||
<li className="select" key={uid}>
|
||||
{contactData[uid].name}
|
||||
<CloseIcon
|
||||
data-uid={uid}
|
||||
onClick={toggleCheckMember}
|
||||
className="close"
|
||||
/>
|
||||
<CloseIcon data-uid={uid} onClick={toggleCheckMember} className="close" />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
@@ -185,11 +178,7 @@ export default function AddMembers({ cid = null, closeModal }) {
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<Button
|
||||
disabled={selects.length == 0 || isAdding}
|
||||
className="btn"
|
||||
onClick={handleAddMembers}
|
||||
>
|
||||
<Button disabled={selects.length == 0 || isAdding} className="btn" onClick={handleAddMembers}>
|
||||
{isAdding ? `Adding` : "Add"} to #{channel.name}
|
||||
</Button>
|
||||
</Styled>
|
||||
|
||||
@@ -68,14 +68,8 @@ import Button from "../styled/Button";
|
||||
import Input from "../styled/Input";
|
||||
export default function InviteByEmail({ cid = null }) {
|
||||
const [email, setEmail] = useState("");
|
||||
const {
|
||||
enableSMTP,
|
||||
linkCopied,
|
||||
link,
|
||||
copyLink,
|
||||
generateNewLink,
|
||||
generating,
|
||||
} = useInviteLink(cid);
|
||||
const { enableSMTP, linkCopied, link, copyLink, generateNewLink, generating } =
|
||||
useInviteLink(cid);
|
||||
useEffect(() => {
|
||||
if (linkCopied) {
|
||||
toast.success("Invite Link Copied!");
|
||||
@@ -104,12 +98,7 @@ export default function InviteByEmail({ cid = null }) {
|
||||
<div className="link">
|
||||
<label htmlFor="">Or Send invite link to your friends</label>
|
||||
<div className="input">
|
||||
<Input
|
||||
readOnly
|
||||
className="invite"
|
||||
placeholder="Generating"
|
||||
value={link}
|
||||
/>
|
||||
<Input readOnly className="invite" placeholder="Generating" value={link} />
|
||||
<button className="copy" onClick={copyLink}>
|
||||
Copy
|
||||
</button>
|
||||
|
||||
@@ -28,20 +28,14 @@ const Styled = styled.div`
|
||||
`;
|
||||
import Modal from "../Modal";
|
||||
// type: server,channel
|
||||
export default function InviteModal({
|
||||
type = "server",
|
||||
cid = null,
|
||||
title = "",
|
||||
closeModal,
|
||||
}) {
|
||||
export default function InviteModal({ type = "server", cid = null, title = "", closeModal }) {
|
||||
const { channel, server } = useSelector((store) => {
|
||||
return {
|
||||
channel: store.channels.byId[cid],
|
||||
server: store.server,
|
||||
server: store.server
|
||||
};
|
||||
});
|
||||
const finalTitle =
|
||||
type == "server" ? server.name : `#${title || channel?.name}`;
|
||||
const finalTitle = type == "server" ? server.name : `#${title || channel?.name}`;
|
||||
return (
|
||||
<Modal>
|
||||
<Styled>
|
||||
@@ -49,9 +43,7 @@ export default function InviteModal({
|
||||
Add friends to {finalTitle}
|
||||
<CloseIcon className="close" onClick={closeModal} />
|
||||
</h2>
|
||||
{!channel?.is_public && (
|
||||
<AddMembers cid={cid} closeModal={closeModal} />
|
||||
)}
|
||||
{!channel?.is_public && <AddMembers cid={cid} closeModal={closeModal} />}
|
||||
<InviteByEmail cid={channel?.is_public ? null : cid} />
|
||||
</Styled>
|
||||
</Modal>
|
||||
|
||||
@@ -30,10 +30,7 @@ export default function LeaveConfirmModal({ id, closeModal, handleNextStep }) {
|
||||
}
|
||||
buttons={
|
||||
<>
|
||||
<Button
|
||||
onClick={closeModal.bind(null, undefined)}
|
||||
className="cancel"
|
||||
>
|
||||
<Button onClick={closeModal.bind(null, undefined)} className="cancel">
|
||||
Cancel
|
||||
</Button>
|
||||
{isOwner ? (
|
||||
|
||||
@@ -28,11 +28,7 @@ const UserList = styled.ul`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export default function TransferOwnerModal({
|
||||
id,
|
||||
closeModal,
|
||||
withLeave = true,
|
||||
}) {
|
||||
export default function TransferOwnerModal({ id, closeModal, withLeave = true }) {
|
||||
const {
|
||||
transferOwner,
|
||||
otherMembers,
|
||||
@@ -40,7 +36,7 @@ export default function TransferOwnerModal({
|
||||
leaveChannel,
|
||||
leaveSuccess,
|
||||
transferSuccess,
|
||||
transfering,
|
||||
transfering
|
||||
} = useLeaveChannel(id);
|
||||
|
||||
const [uid, setUid] = useState(null);
|
||||
@@ -75,17 +71,10 @@ export default function TransferOwnerModal({
|
||||
description={"This cannot be undone."}
|
||||
buttons={
|
||||
<>
|
||||
<Button
|
||||
onClick={closeModal.bind(null, undefined)}
|
||||
className="cancel"
|
||||
>
|
||||
<Button onClick={closeModal.bind(null, undefined)} className="cancel">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={!uid}
|
||||
onClick={handleTransferAndLeave}
|
||||
className="danger"
|
||||
>
|
||||
<Button disabled={!uid} onClick={handleTransferAndLeave} className="danger">
|
||||
{operating ? "Assigning" : `Assign and Leave`}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@@ -2,22 +2,11 @@ import { useState } from "react";
|
||||
// import styled from "styled-components";
|
||||
import TransferOwnerModal from "./TransferOwnerModal";
|
||||
import LeaveConfirmModal from "./LeaveConfirmModal";
|
||||
export default function LeaveChannel({
|
||||
id = null,
|
||||
isOwner = false,
|
||||
closeModal,
|
||||
}) {
|
||||
export default function LeaveChannel({ id = null, isOwner = false, closeModal }) {
|
||||
const [transferOwner, setTransferOwner] = useState(isOwner);
|
||||
const handleNextStep = () => {
|
||||
setTransferOwner(true);
|
||||
};
|
||||
if (transferOwner)
|
||||
return <TransferOwnerModal id={id} closeModal={closeModal} />;
|
||||
return (
|
||||
<LeaveConfirmModal
|
||||
id={id}
|
||||
closeModal={closeModal}
|
||||
handleNextStep={handleNextStep}
|
||||
/>
|
||||
);
|
||||
if (transferOwner) return <TransferOwnerModal id={id} closeModal={closeModal} />;
|
||||
return <LeaveConfirmModal id={id} closeModal={closeModal} handleNextStep={handleNextStep} />;
|
||||
}
|
||||
|
||||
@@ -45,17 +45,8 @@ export default function Loading({ reload = false, fullscreen = false }) {
|
||||
};
|
||||
return (
|
||||
<StyledWrapper className={fullscreen ? "fullscreen" : ""}>
|
||||
<Ring
|
||||
className="loading"
|
||||
size={40}
|
||||
lineWeight={5}
|
||||
speed={2}
|
||||
color="black"
|
||||
/>
|
||||
<Button
|
||||
className={`reload danger ${reload ? "visible" : ""}`}
|
||||
onClick={handleReload}
|
||||
>
|
||||
<Ring className="loading" size={40} lineWeight={5} speed={2} color="black" />
|
||||
<Button className={`reload danger ${reload ? "visible" : ""}`} onClick={handleReload}>
|
||||
Reload
|
||||
</Button>
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -119,20 +119,12 @@ export default function ManageMembers({ cid = null }) {
|
||||
return {
|
||||
contacts: store.contacts,
|
||||
channels: store.channels,
|
||||
loginUser: store.contacts.byId[store.authData.uid],
|
||||
loginUser: store.contacts.byId[store.authData.uid]
|
||||
};
|
||||
});
|
||||
const {
|
||||
copyEmail,
|
||||
removeFromChannel,
|
||||
removeUser,
|
||||
canRemove,
|
||||
canRemoveFromChannel,
|
||||
} = useContactOperation({ cid });
|
||||
const [
|
||||
updateContact,
|
||||
{ isSuccess: updateSuccess },
|
||||
] = useUpdateContactMutation();
|
||||
const { copyEmail, removeFromChannel, removeUser, canRemove, canRemoveFromChannel } =
|
||||
useContactOperation({ cid });
|
||||
const [updateContact, { isSuccess: updateSuccess }] = useUpdateContactMutation();
|
||||
|
||||
useEffect(() => {
|
||||
if (updateSuccess) {
|
||||
@@ -146,11 +138,7 @@ export default function ManageMembers({ cid = null }) {
|
||||
updateContact({ id: uid, is_admin: isAdmin });
|
||||
};
|
||||
const channel = channels.byId[cid] ?? null;
|
||||
const uids = channel
|
||||
? channel.is_public
|
||||
? contacts.ids
|
||||
: channel.members
|
||||
: contacts.ids;
|
||||
const uids = channel ? (channel.is_public ? contacts.ids : channel.members) : contacts.ids;
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
@@ -158,8 +146,7 @@ export default function ManageMembers({ cid = null }) {
|
||||
<div className="intro">
|
||||
<h4 className="title">Manage Members</h4>
|
||||
<p className="desc">
|
||||
Disabling your account means you can recover it at any time after
|
||||
taking this action.
|
||||
Disabling your account means you can recover it at any time after taking this action.
|
||||
</p>
|
||||
</div>
|
||||
<ul className="members">
|
||||
@@ -194,7 +181,7 @@ export default function ManageMembers({ cid = null }) {
|
||||
onClick={handleToggleRole.bind(null, {
|
||||
ignore: is_admin,
|
||||
uid,
|
||||
isAdmin: true,
|
||||
isAdmin: true
|
||||
})}
|
||||
>
|
||||
Admin
|
||||
@@ -205,7 +192,7 @@ export default function ManageMembers({ cid = null }) {
|
||||
onClick={handleToggleRole.bind(null, {
|
||||
ignore: !is_admin,
|
||||
uid,
|
||||
isAdmin: false,
|
||||
isAdmin: false
|
||||
})}
|
||||
>
|
||||
User
|
||||
@@ -226,10 +213,7 @@ export default function ManageMembers({ cid = null }) {
|
||||
content={
|
||||
<StyledMenu className="menu">
|
||||
{email && (
|
||||
<li
|
||||
className="item"
|
||||
onClick={copyEmail.bind(null, email)}
|
||||
>
|
||||
<li className="item" onClick={copyEmail.bind(null, email)}>
|
||||
Copy Email
|
||||
</li>
|
||||
)}
|
||||
@@ -237,18 +221,12 @@ export default function ManageMembers({ cid = null }) {
|
||||
{/* <li className="item underline">Change Nickname</li> */}
|
||||
{/* <li className="item danger">Ban</li> */}
|
||||
{canRemoveFromChannel && (
|
||||
<li
|
||||
className="item danger"
|
||||
onClick={removeFromChannel.bind(null, uid)}
|
||||
>
|
||||
<li className="item danger" onClick={removeFromChannel.bind(null, uid)}>
|
||||
Remove From Channel
|
||||
</li>
|
||||
)}
|
||||
{canRemove && !cid && (
|
||||
<li
|
||||
className="item danger"
|
||||
onClick={removeUser.bind(null, uid)}
|
||||
>
|
||||
<li className="item danger" onClick={removeUser.bind(null, uid)}>
|
||||
Remove From Server
|
||||
</li>
|
||||
)}
|
||||
|
||||
@@ -11,6 +11,6 @@ export default function usePrompt() {
|
||||
return {
|
||||
setCanneled: setPrompt,
|
||||
prompted: !!localStorage.getItem(KEY_PWA_INSTALLED),
|
||||
resetPrompt,
|
||||
resetPrompt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ function MarkdownEditor({
|
||||
height = "50vh",
|
||||
placeholder,
|
||||
sendMarkdown,
|
||||
setEditorInstance,
|
||||
setEditorInstance
|
||||
}) {
|
||||
const editorRef = useRef(undefined);
|
||||
const { uploadFile } = useUploadFile();
|
||||
|
||||
@@ -62,12 +62,7 @@ const StyledCmds = styled.ul`
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
`;
|
||||
export default function Commands({
|
||||
context = "user",
|
||||
contextId = 0,
|
||||
mid = 0,
|
||||
toggleEditMessage,
|
||||
}) {
|
||||
export default function Commands({ context = "user", contextId = 0, mid = 0, toggleEditMessage }) {
|
||||
const {
|
||||
canDelete,
|
||||
canReply,
|
||||
@@ -80,11 +75,11 @@ export default function Commands({
|
||||
togglePinModal,
|
||||
PinModal,
|
||||
DeleteModal,
|
||||
ForwardModal,
|
||||
ForwardModal
|
||||
} = useMessageOperation({ mid, context, contextId });
|
||||
const { setReplying } = useSendMessage({ context, to: contextId });
|
||||
const { addFavorite, isFavorited } = useFavMessage({
|
||||
cid: context == "channel" ? contextId : null,
|
||||
cid: context == "channel" ? contextId : null
|
||||
});
|
||||
const dispatch = useDispatch();
|
||||
const [tippyVisible, setTippyVisible] = useState(false);
|
||||
@@ -126,10 +121,7 @@ export default function Commands({
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledCmds
|
||||
ref={cmdsRef}
|
||||
className={`cmds ${tippyVisible ? "visible" : ""}`}
|
||||
>
|
||||
<StyledCmds ref={cmdsRef} className={`cmds ${tippyVisible ? "visible" : ""}`}>
|
||||
<Tippy
|
||||
onShow={handleTippyVisible.bind(null, true)}
|
||||
onHide={handleTippyVisible.bind(null, false)}
|
||||
@@ -175,25 +167,25 @@ export default function Commands({
|
||||
canPin && {
|
||||
title: pinned ? `Unpin Message` : `Pin Message`,
|
||||
icon: <IconPin className="icon" />,
|
||||
handler: pinned ? handleUnpin : togglePinModal,
|
||||
handler: pinned ? handleUnpin : togglePinModal
|
||||
},
|
||||
{
|
||||
title: "Forward",
|
||||
icon: <IconForward className="icon" />,
|
||||
handler: toggleForwardModal,
|
||||
handler: toggleForwardModal
|
||||
},
|
||||
{
|
||||
title: "Select",
|
||||
icon: <IconSelect className="icon" />,
|
||||
handler: handleSelect.bind(null, mid),
|
||||
handler: handleSelect.bind(null, mid)
|
||||
},
|
||||
|
||||
canDelete && {
|
||||
title: " Delete",
|
||||
danger: true,
|
||||
icon: <IconDelete className="icon" />,
|
||||
handler: toggleDeleteModal,
|
||||
},
|
||||
handler: toggleDeleteModal
|
||||
}
|
||||
]}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function MessageContextMenu({
|
||||
visible,
|
||||
hide,
|
||||
editMessage,
|
||||
children,
|
||||
children
|
||||
}) {
|
||||
const {
|
||||
copyContent,
|
||||
@@ -37,7 +37,7 @@ export default function MessageContextMenu({
|
||||
togglePinModal,
|
||||
PinModal,
|
||||
ForwardModal,
|
||||
DeleteModal,
|
||||
DeleteModal
|
||||
} = useMessageOperation({ mid, contextId, context });
|
||||
const dispatch = useDispatch();
|
||||
const { setReplying } = useSendMessage({ context, to: contextId });
|
||||
@@ -71,39 +71,39 @@ export default function MessageContextMenu({
|
||||
canEdit && {
|
||||
title: "Edit Message",
|
||||
icon: <IconEdit className="icon" />,
|
||||
handler: editMessage,
|
||||
handler: editMessage
|
||||
},
|
||||
canReply && {
|
||||
title: "Reply",
|
||||
icon: <IconReply className="icon" />,
|
||||
handler: handleReply,
|
||||
handler: handleReply
|
||||
},
|
||||
canCopy && {
|
||||
title: "Copy",
|
||||
icon: <IconCopy className="icon" />,
|
||||
handler: copyContent,
|
||||
handler: copyContent
|
||||
},
|
||||
canPin && {
|
||||
title: pinned ? "Unpin" : "Pin",
|
||||
icon: <IconPin className="icon" />,
|
||||
handler: pinned ? unPin.bind(null, mid) : togglePinModal,
|
||||
handler: pinned ? unPin.bind(null, mid) : togglePinModal
|
||||
},
|
||||
{
|
||||
title: "Forward",
|
||||
icon: <IconForward className="icon" />,
|
||||
handler: toggleForwardModal,
|
||||
handler: toggleForwardModal
|
||||
},
|
||||
{
|
||||
title: "Select",
|
||||
icon: <IconSelect className="icon" />,
|
||||
handler: handleSelect,
|
||||
handler: handleSelect
|
||||
},
|
||||
canDelete && {
|
||||
title: "Delete",
|
||||
danger: true,
|
||||
icon: <IconDelete className="icon" />,
|
||||
handler: toggleDeleteModal,
|
||||
},
|
||||
handler: toggleDeleteModal
|
||||
}
|
||||
]}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export default function EditMessage({ mid, cancelEdit }) {
|
||||
edit({
|
||||
mid,
|
||||
content: currMsg,
|
||||
type: msg.content_type == ContentTypes.markdown ? "markdown" : "text",
|
||||
type: msg.content_type == ContentTypes.markdown ? "markdown" : "text"
|
||||
});
|
||||
};
|
||||
if (!msg) return null;
|
||||
@@ -121,8 +121,7 @@ export default function EditMessage({ mid, cancelEdit }) {
|
||||
esc to <button onClick={cancelEdit}>cancel</button>
|
||||
</span>
|
||||
<span className="opt">
|
||||
enter to{" "}
|
||||
<button onClick={handleSave}>{isEditing ? "saving" : `save`}</button>
|
||||
enter to <button onClick={handleSave}>{isEditing ? "saving" : `save`}</button>
|
||||
</span>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -23,20 +23,10 @@ const FavoritedMessage = ({ id }) => {
|
||||
const favorite_mids = messages.map(({ from_mid }) => from_mid) || [];
|
||||
|
||||
setMsgs(
|
||||
<StyledFav
|
||||
data-favorite-mids={favorite_mids.join(",")}
|
||||
className="favorite"
|
||||
>
|
||||
<StyledFav data-favorite-mids={favorite_mids.join(",")} className="favorite">
|
||||
<div className="list">
|
||||
{messages.map((msg, idx) => {
|
||||
const {
|
||||
user = {},
|
||||
download,
|
||||
content,
|
||||
content_type,
|
||||
properties,
|
||||
thumbnail,
|
||||
} = msg;
|
||||
const { user = {}, download, content, content_type, properties, thumbnail } = msg;
|
||||
return (
|
||||
<StyledMsg className="archive" key={idx}>
|
||||
{user && (
|
||||
@@ -54,7 +44,7 @@ const FavoritedMessage = ({ id }) => {
|
||||
content,
|
||||
content_type,
|
||||
properties,
|
||||
thumbnail,
|
||||
thumbnail
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,7 @@ const ForwardedMessage = ({ context, to, from_uid, id }) => {
|
||||
content,
|
||||
content_type,
|
||||
properties,
|
||||
thumbnail,
|
||||
thumbnail
|
||||
} = msg;
|
||||
return (
|
||||
<StyledMsg className="archive" key={idx}>
|
||||
@@ -78,7 +78,7 @@ const ForwardedMessage = ({ context, to, from_uid, id }) => {
|
||||
content,
|
||||
content_type,
|
||||
properties,
|
||||
thumbnail,
|
||||
thumbnail
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,14 +9,7 @@ export default function PreviewMessage({ mid = 0 }) {
|
||||
return { msg: store.message[mid], contactsData: store.contacts.byId };
|
||||
});
|
||||
if (!msg) return null;
|
||||
const {
|
||||
from_uid,
|
||||
created_at,
|
||||
content_type,
|
||||
content,
|
||||
thumbnail,
|
||||
properties,
|
||||
} = msg;
|
||||
const { from_uid, created_at, content_type, content, thumbnail, properties } = msg;
|
||||
const { name, avatar } = contactsData[from_uid];
|
||||
return (
|
||||
<StyledWrapper className={`preview`}>
|
||||
@@ -26,9 +19,7 @@ export default function PreviewMessage({ mid = 0 }) {
|
||||
<div className="details">
|
||||
<div className="up">
|
||||
<span className="name">{name}</span>
|
||||
<i className="time">
|
||||
{dayjs(created_at).format("YYYY-MM-DD h:mm:ss A")}
|
||||
</i>
|
||||
<i className="time">{dayjs(created_at).format("YYYY-MM-DD h:mm:ss A")}</i>
|
||||
</div>
|
||||
<div className={`down`}>
|
||||
{renderContent({
|
||||
@@ -36,7 +27,7 @@ export default function PreviewMessage({ mid = 0 }) {
|
||||
content,
|
||||
thumbnail,
|
||||
from_uid,
|
||||
properties,
|
||||
properties
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -70,8 +70,7 @@ const StyledDetails = styled.div`
|
||||
position: relative;
|
||||
background: #ffffff;
|
||||
border-radius: var(--br);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08),
|
||||
0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
@@ -132,7 +131,7 @@ export default function Reaction({ mid, reactions = null }) {
|
||||
const [reactWithEmoji] = useReactMessageMutation();
|
||||
const { currUid } = useSelector((store) => {
|
||||
return {
|
||||
currUid: store.authData.uid,
|
||||
currUid: store.authData.uid
|
||||
};
|
||||
});
|
||||
const handleReact = (emoji) => {
|
||||
@@ -156,18 +155,14 @@ export default function Reaction({ mid, reactions = null }) {
|
||||
// visible={true}
|
||||
interactive
|
||||
placement="top"
|
||||
content={
|
||||
<ReactionDetails uids={uids} emoji={reaction} index={idx} />
|
||||
}
|
||||
content={<ReactionDetails uids={uids} emoji={reaction} index={idx} />}
|
||||
>
|
||||
<i className="emoji">
|
||||
<ReactionItem native={reaction} />
|
||||
</i>
|
||||
</Tippy>
|
||||
|
||||
{uids.length > 1 ? (
|
||||
<em className="count">{`${uids.length}`} </em>
|
||||
) : null}
|
||||
{uids.length > 1 ? <em className="count">{`${uids.length}`} </em> : null}
|
||||
</span>
|
||||
) : null;
|
||||
})}
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function ReactionPicker({ mid, hidePicker }) {
|
||||
const { reactionData, currUid } = useSelector((store) => {
|
||||
return {
|
||||
reactionData: store.reactionMessage[mid] || {},
|
||||
currUid: store.authData.uid,
|
||||
currUid: store.authData.uid
|
||||
};
|
||||
});
|
||||
// useOutsideClick(wrapperRef, hidePicker);
|
||||
@@ -55,8 +55,7 @@ export default function ReactionPicker({ mid, hidePicker }) {
|
||||
<ul className={`emojis ${isLoading ? "reacting" : ""}`}>
|
||||
{Emojis.map((emoji) => {
|
||||
let reacted =
|
||||
reactionData[emoji] &&
|
||||
reactionData[emoji].findIndex((id) => id == currUid) > -1;
|
||||
reactionData[emoji] && reactionData[emoji].findIndex((id) => id == currUid) > -1;
|
||||
|
||||
return (
|
||||
<li
|
||||
|
||||
@@ -66,11 +66,7 @@ const Styled = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: "";
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0) 63.54%,
|
||||
#e5e7eb 93.09%
|
||||
);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 63.54%, #e5e7eb 93.09%);
|
||||
}
|
||||
}
|
||||
.pic {
|
||||
|
||||
@@ -26,26 +26,24 @@ function Message({
|
||||
mid = "",
|
||||
context = "user",
|
||||
updateReadIndex,
|
||||
read = true,
|
||||
read = true
|
||||
}) {
|
||||
const {
|
||||
visible: contextMenuVisible,
|
||||
handleContextMenuEvent,
|
||||
hideContextMenu,
|
||||
} = useContextMenu();
|
||||
const { visible: contextMenuVisible, handleContextMenuEvent, hideContextMenu } = useContextMenu();
|
||||
const inviewRef = useInView();
|
||||
const [edit, setEdit] = useState(false);
|
||||
const avatarRef = useRef(null);
|
||||
const { getPinInfo } = usePinMessage(context == "channel" ? contextId : null);
|
||||
const { message = {}, reactionMessageData, contactsData } = useSelector(
|
||||
(store) => {
|
||||
return {
|
||||
reactionMessageData: store.reactionMessage,
|
||||
message: store.message[mid] || {},
|
||||
contactsData: store.contacts.byId,
|
||||
};
|
||||
}
|
||||
);
|
||||
const {
|
||||
message = {},
|
||||
reactionMessageData,
|
||||
contactsData
|
||||
} = useSelector((store) => {
|
||||
return {
|
||||
reactionMessageData: store.reactionMessage,
|
||||
message: store.message[mid] || {},
|
||||
contactsData: store.contacts.byId
|
||||
};
|
||||
});
|
||||
|
||||
const toggleEditMessage = () => {
|
||||
setEdit((prev) => !prev);
|
||||
@@ -61,7 +59,7 @@ function Message({
|
||||
download,
|
||||
content_type = "text/plain",
|
||||
edited,
|
||||
properties,
|
||||
properties
|
||||
} = message;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -79,11 +77,7 @@ function Message({
|
||||
// if (!message) return null;
|
||||
let timePrefix = null;
|
||||
const dayjsTime = dayjs(time);
|
||||
timePrefix = dayjsTime.isToday()
|
||||
? "Today"
|
||||
: dayjsTime.isYesterday()
|
||||
? "Yesterday"
|
||||
: null;
|
||||
timePrefix = dayjsTime.isToday() ? "Today" : dayjsTime.isYesterday() ? "Yesterday" : null;
|
||||
|
||||
const pinInfo = getPinInfo(mid);
|
||||
// return null;
|
||||
@@ -94,9 +88,9 @@ function Message({
|
||||
onContextMenu={handleContextMenuEvent}
|
||||
data-msg-mid={mid}
|
||||
ref={inviewRef}
|
||||
className={`message ${readOnly ? "readonly" : ""} ${
|
||||
pinInfo ? "pinned" : ""
|
||||
} ${contextMenuVisible ? "contextVisible" : ""} `}
|
||||
className={`message ${readOnly ? "readonly" : ""} ${pinInfo ? "pinned" : ""} ${
|
||||
contextMenuVisible ? "contextVisible" : ""
|
||||
} `}
|
||||
>
|
||||
<Tippy
|
||||
key={_key}
|
||||
@@ -153,7 +147,7 @@ function Message({
|
||||
content,
|
||||
thumbnail,
|
||||
download,
|
||||
edited,
|
||||
edited
|
||||
})
|
||||
)}
|
||||
{reactions && <Reaction mid={mid} reactions={reactions} />}
|
||||
|
||||
@@ -20,7 +20,7 @@ const renderContent = ({
|
||||
content,
|
||||
download,
|
||||
thumbnail,
|
||||
edited = false,
|
||||
edited = false
|
||||
}) => {
|
||||
let ctn = null;
|
||||
switch (content_type) {
|
||||
@@ -30,38 +30,23 @@ const renderContent = ({
|
||||
<Linkit
|
||||
componentDecorator={(decoratedHref, decoratedText, key) => (
|
||||
<React.Fragment key={key}>
|
||||
<a
|
||||
className="link"
|
||||
target="_blank"
|
||||
href={decoratedHref}
|
||||
key={key}
|
||||
rel="noreferrer"
|
||||
>
|
||||
<a className="link" target="_blank" href={decoratedHref} key={key} rel="noreferrer">
|
||||
{decoratedText}
|
||||
</a>
|
||||
{!decoratedHref.startsWith("mailto") && (
|
||||
<URLPreview url={decoratedHref} />
|
||||
)}
|
||||
{!decoratedHref.startsWith("mailto") && <URLPreview url={decoratedHref} />}
|
||||
</React.Fragment>
|
||||
)}
|
||||
>
|
||||
{reactStringReplace(
|
||||
content,
|
||||
/(\s{1}@[0-9]+\s{1})/g,
|
||||
(match, idx) => {
|
||||
console.log("match", match);
|
||||
const uid = match.trim().slice(1);
|
||||
return <Mention key={idx} uid={uid} cid={to} />;
|
||||
}
|
||||
)}
|
||||
{reactStringReplace(content, /(\s{1}@[0-9]+\s{1})/g, (match, idx) => {
|
||||
console.log("match", match);
|
||||
const uid = match.trim().slice(1);
|
||||
return <Mention key={idx} uid={uid} cid={to} />;
|
||||
})}
|
||||
{/* {content.replace(/\s{1}\@[1-9]+\s{1}/g,)} */}
|
||||
{/* {new RegExp(/\s{1}\@[1-9]+\s{1}/g).exec(content)} */}
|
||||
</Linkit>
|
||||
{edited && (
|
||||
<span
|
||||
className="edited"
|
||||
title={dayjs(edited).format("YYYY-MM-DD h:mm:ss A")}
|
||||
>
|
||||
<span className="edited" title={dayjs(edited).format("YYYY-MM-DD h:mm:ss A")}>
|
||||
(edited)
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -10,17 +10,15 @@ import usePinMessage from "../../hook/usePinMessage";
|
||||
|
||||
export default function useMessageOperation({ mid, context, contextId }) {
|
||||
const { copy } = useCopy();
|
||||
const { content_type, properties, currUid, from_uid, content } = useSelector(
|
||||
(store) => {
|
||||
return {
|
||||
content: store.message[mid]?.content,
|
||||
from_uid: store.message[mid]?.from_uid,
|
||||
content_type: store.message[mid]?.content_type,
|
||||
properties: store.message[mid]?.properties,
|
||||
currUid: store.authData.uid,
|
||||
};
|
||||
}
|
||||
);
|
||||
const { content_type, properties, currUid, from_uid, content } = useSelector((store) => {
|
||||
return {
|
||||
content: store.message[mid]?.content,
|
||||
from_uid: store.message[mid]?.from_uid,
|
||||
content_type: store.message[mid]?.content_type,
|
||||
properties: store.message[mid]?.properties,
|
||||
currUid: store.authData.uid
|
||||
};
|
||||
});
|
||||
const { canPin, pins, unpinMessage, isUnpinSuccess } = usePinMessage(
|
||||
context == "channel" ? contextId : undefined
|
||||
);
|
||||
@@ -71,16 +69,11 @@ export default function useMessageOperation({ mid, context, contextId }) {
|
||||
properties?.content_type &&
|
||||
properties?.content_type.startsWith("image");
|
||||
const enableEdit =
|
||||
currUid == from_uid &&
|
||||
[ContentTypes.text, ContentTypes.markdown].includes(content_type);
|
||||
currUid == from_uid && [ContentTypes.text, ContentTypes.markdown].includes(content_type);
|
||||
const canDelete = currUid == from_uid;
|
||||
const canCopy =
|
||||
[ContentTypes.text, ContentTypes.markdown].includes(content_type) ||
|
||||
isImage;
|
||||
const canCopy = [ContentTypes.text, ContentTypes.markdown].includes(content_type) || isImage;
|
||||
return {
|
||||
copyContent: isImage
|
||||
? copyContent.bind(null, true)
|
||||
: copyContent.bind(null, false),
|
||||
copyContent: isImage ? copyContent.bind(null, true) : copyContent.bind(null, false),
|
||||
canCopy,
|
||||
isImage,
|
||||
isMarkdown: content_type == ContentTypes.markdown,
|
||||
@@ -101,6 +94,6 @@ export default function useMessageOperation({ mid, context, contextId }) {
|
||||
) : null,
|
||||
PinModal: pinModalVisible ? (
|
||||
<PinMessageModal mid={mid} gid={contextId} closeModal={togglePinModal} />
|
||||
) : null,
|
||||
) : null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ export const CONFIG = {
|
||||
editableProps: {
|
||||
spellCheck: false,
|
||||
autoFocus: true,
|
||||
placeholder: "Type…",
|
||||
placeholder: "Type…"
|
||||
},
|
||||
trailingBlock: { type: ELEMENT_PARAGRAPH },
|
||||
softBreak: {
|
||||
@@ -13,11 +13,11 @@ export const CONFIG = {
|
||||
{
|
||||
hotkey: "shift+enter",
|
||||
query: {
|
||||
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
exitBreak: {
|
||||
options: {
|
||||
@@ -25,9 +25,9 @@ export const CONFIG = {
|
||||
{
|
||||
hotkey: "mod+enter",
|
||||
query: {
|
||||
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH],
|
||||
},
|
||||
},
|
||||
allow: [ELEMENT_IMAGE, ELEMENT_PARAGRAPH]
|
||||
}
|
||||
}
|
||||
// {
|
||||
// hotkey: "mod+shift+enter",
|
||||
// before: true,
|
||||
@@ -40,14 +40,14 @@ export const CONFIG = {
|
||||
// allow: KEYS_HEADING,
|
||||
// },
|
||||
// },
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
selectOnBackspace: {
|
||||
options: {
|
||||
query: {
|
||||
allow: [ELEMENT_IMAGE],
|
||||
},
|
||||
},
|
||||
},
|
||||
allow: [ELEMENT_IMAGE]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
// usePlateEditorRef,
|
||||
// ELEMENT_IMAGE,
|
||||
// useComboboxControls,
|
||||
MentionCombobox,
|
||||
MentionCombobox
|
||||
} from "@udecode/plate";
|
||||
import { createComboboxPlugin } from "@udecode/plate-combobox";
|
||||
import { ReactEditor } from "slate-react";
|
||||
@@ -44,7 +44,7 @@ const Plugins = ({
|
||||
id = "",
|
||||
placeholder = "Write some markdown...",
|
||||
sendMessages,
|
||||
members = [],
|
||||
members = []
|
||||
}) => {
|
||||
// const { getMenuProps, getItemProps } = useComboboxControls();
|
||||
const [context, to] = id.split("_");
|
||||
@@ -57,7 +57,7 @@ const Plugins = ({
|
||||
const initialProps = {
|
||||
...CONFIG.editableProps,
|
||||
className: "box",
|
||||
placeholder,
|
||||
placeholder
|
||||
};
|
||||
const plateEditor = getPlateEditorRef(`${TEXT_EDITOR_PREFIX}_${id}`);
|
||||
useEffect(() => {
|
||||
@@ -91,13 +91,7 @@ const Plugins = ({
|
||||
(evt) => {
|
||||
// 是否在at操作
|
||||
const mentionInputs = findMentionInput(plateEditor);
|
||||
if (
|
||||
mentionInputs ||
|
||||
evt.shiftKey ||
|
||||
evt.ctrlKey ||
|
||||
evt.altKey ||
|
||||
evt.isComposing
|
||||
) {
|
||||
if (mentionInputs || evt.shiftKey || evt.ctrlKey || evt.altKey || evt.isComposing) {
|
||||
return true;
|
||||
}
|
||||
evt.preventDefault();
|
||||
@@ -107,14 +101,14 @@ const Plugins = ({
|
||||
Transforms.delete(plateEditor, {
|
||||
at: {
|
||||
anchor: Editor.start(plateEditor, []),
|
||||
focus: Editor.end(plateEditor, []),
|
||||
},
|
||||
focus: Editor.end(plateEditor, [])
|
||||
}
|
||||
});
|
||||
},
|
||||
{
|
||||
// eventTypes: ["keydown"],
|
||||
target: editableRef,
|
||||
when: !cmdKey,
|
||||
when: !cmdKey
|
||||
}
|
||||
);
|
||||
useKey(
|
||||
@@ -125,7 +119,7 @@ const Plugins = ({
|
||||
},
|
||||
{
|
||||
eventTypes: ["keydown", "keyup"],
|
||||
target: editableRef,
|
||||
target: editableRef
|
||||
}
|
||||
);
|
||||
const pluginArr = [
|
||||
@@ -133,7 +127,7 @@ const Plugins = ({
|
||||
createNodeIdPlugin(),
|
||||
createSoftBreakPlugin(CONFIG.softBreak),
|
||||
createTrailingBlockPlugin(CONFIG.trailingBlock),
|
||||
createExitBreakPlugin(CONFIG.exitBreak),
|
||||
createExitBreakPlugin(CONFIG.exitBreak)
|
||||
];
|
||||
const plugins = createPlugins(
|
||||
enableMentions
|
||||
@@ -145,17 +139,17 @@ const Plugins = ({
|
||||
console.log("mention", item);
|
||||
const {
|
||||
text,
|
||||
data: { uid },
|
||||
data: { uid }
|
||||
} = item;
|
||||
return { value: `@${text}`, uid };
|
||||
},
|
||||
insertSpaceAfterMention: true,
|
||||
},
|
||||
}),
|
||||
insertSpaceAfterMention: true
|
||||
}
|
||||
})
|
||||
])
|
||||
: pluginArr,
|
||||
{
|
||||
components,
|
||||
components
|
||||
}
|
||||
);
|
||||
|
||||
@@ -179,20 +173,16 @@ const Plugins = ({
|
||||
const { value, mentions } = getMixedText(v.children);
|
||||
const prev = tmps[tmps.length - 1];
|
||||
if (!prev) {
|
||||
tmps.push([
|
||||
{ type: "text", content: value, properties: { mentions } },
|
||||
]);
|
||||
tmps.push([{ type: "text", content: value, properties: { mentions } }]);
|
||||
} else {
|
||||
if (Array.isArray(prev)) {
|
||||
tmps[tmps.length - 1].push({
|
||||
type: "text",
|
||||
content: value,
|
||||
properties: { mentions },
|
||||
properties: { mentions }
|
||||
});
|
||||
} else {
|
||||
tmps.push([
|
||||
{ type: "text", content: value, properties: { mentions } },
|
||||
]);
|
||||
tmps.push([{ type: "text", content: value, properties: { mentions } }]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,8 +192,8 @@ const Plugins = ({
|
||||
type: "text",
|
||||
content: tmp.map((t) => t.content).join("\n"),
|
||||
properties: {
|
||||
mentions: tmp.map((t) => t.properties?.mentions || []).flat(),
|
||||
},
|
||||
mentions: tmp.map((t) => t.properties?.mentions || []).flat()
|
||||
}
|
||||
}
|
||||
: tmp;
|
||||
});
|
||||
@@ -229,13 +219,7 @@ const Plugins = ({
|
||||
// component={StyledCombobox}
|
||||
onRenderItem={({ item }) => {
|
||||
console.log("wtf", item);
|
||||
return (
|
||||
<Contact
|
||||
key={item.data.uid}
|
||||
uid={item.data.uid}
|
||||
interactive={false}
|
||||
/>
|
||||
);
|
||||
return <Contact key={item.data.uid} uid={item.data.uid} interactive={false} />;
|
||||
}}
|
||||
items={members.map((id) => {
|
||||
const data = contactData[id];
|
||||
@@ -246,8 +230,8 @@ const Plugins = ({
|
||||
text: name,
|
||||
data: {
|
||||
uid,
|
||||
...rest,
|
||||
},
|
||||
...rest
|
||||
}
|
||||
};
|
||||
})}
|
||||
/>
|
||||
@@ -273,7 +257,7 @@ export const useMixedEditor = (key) => {
|
||||
};
|
||||
return {
|
||||
focus,
|
||||
insertText,
|
||||
insertText
|
||||
};
|
||||
};
|
||||
export default Plugins;
|
||||
|
||||
@@ -2,12 +2,12 @@ import {
|
||||
createBasicElementsPlugin,
|
||||
createImagePlugin,
|
||||
createParagraphPlugin,
|
||||
createSelectOnBackspacePlugin,
|
||||
createSelectOnBackspacePlugin
|
||||
} from "@udecode/plate";
|
||||
import { CONFIG } from "./config";
|
||||
|
||||
const basicElements = [
|
||||
createParagraphPlugin(), // paragraph element
|
||||
createParagraphPlugin() // paragraph element
|
||||
];
|
||||
|
||||
export const PLUGINS = {
|
||||
@@ -16,6 +16,6 @@ export const PLUGINS = {
|
||||
image: [
|
||||
createBasicElementsPlugin(),
|
||||
createImagePlugin(),
|
||||
createSelectOnBackspacePlugin(CONFIG.selectOnBackspace),
|
||||
],
|
||||
createSelectOnBackspacePlugin(CONFIG.selectOnBackspace)
|
||||
]
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const nodeTypes = {
|
||||
paragraph: "p",
|
||||
image: "img",
|
||||
image: "img"
|
||||
};
|
||||
|
||||
export default nodeTypes;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import {
|
||||
ELEMENT_PARAGRAPH,
|
||||
ELEMENT_PARAGRAPH
|
||||
// TElement,
|
||||
} from "@udecode/plate";
|
||||
// import { Text } from 'slate'
|
||||
|
||||
export const createElement = (
|
||||
text = "",
|
||||
{ type = ELEMENT_PARAGRAPH, mark } = {}
|
||||
) => {
|
||||
export const createElement = (text = "", { type = ELEMENT_PARAGRAPH, mark } = {}) => {
|
||||
const leaf = { text };
|
||||
if (mark) {
|
||||
leaf[mark] = true;
|
||||
@@ -15,7 +12,7 @@ export const createElement = (
|
||||
|
||||
return {
|
||||
type,
|
||||
children: [leaf],
|
||||
children: [leaf]
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ const Styled = styled.div`
|
||||
}
|
||||
`;
|
||||
export default function MrakdownRender({ content }) {
|
||||
const mdContainer = useRef(null);
|
||||
const mdContainer = useRef(undefined);
|
||||
const [previewImage, setPreviewImage] = useState(null);
|
||||
useEffect(() => {
|
||||
if (mdContainer) {
|
||||
const container = mdContainer.current;
|
||||
const container = mdContainer?.current;
|
||||
if (container) {
|
||||
// 点击查看大图
|
||||
container.addEventListener(
|
||||
"click",
|
||||
@@ -56,11 +56,7 @@ export default function MrakdownRender({ content }) {
|
||||
return (
|
||||
<>
|
||||
{previewImage && (
|
||||
<ImagePreviewModal
|
||||
download={false}
|
||||
data={previewImage}
|
||||
closeModal={closePreviewModal}
|
||||
/>
|
||||
<ImagePreviewModal download={false} data={previewImage} closeModal={closePreviewModal} />
|
||||
)}
|
||||
<Styled ref={mdContainer}>
|
||||
<Viewer
|
||||
|
||||
@@ -21,15 +21,9 @@ const Notification = () => {
|
||||
navigateTo(newPath);
|
||||
};
|
||||
// https only
|
||||
navigator.serviceWorker?.addEventListener(
|
||||
"message",
|
||||
handleServiceworkerMessage
|
||||
);
|
||||
navigator.serviceWorker?.addEventListener("message", handleServiceworkerMessage);
|
||||
return () => {
|
||||
navigator.serviceWorker?.removeEventListener(
|
||||
"message",
|
||||
handleServiceworkerMessage
|
||||
);
|
||||
navigator.serviceWorker?.removeEventListener("message", handleServiceworkerMessage);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const useDeviceToken = (vapidKey) => {
|
||||
const messaging = getMessaging(initializeApp(firebaseConfig));
|
||||
|
||||
getToken(messaging, {
|
||||
vapidKey,
|
||||
vapidKey
|
||||
})
|
||||
.then((currentToken) => {
|
||||
if (currentToken) {
|
||||
@@ -19,9 +19,7 @@ const useDeviceToken = (vapidKey) => {
|
||||
// Perform any other neccessary action with the token
|
||||
} else {
|
||||
// Show permission request UI
|
||||
console.log(
|
||||
"No registration token available. Request permission to generate one."
|
||||
);
|
||||
console.log("No registration token available. Request permission to generate one.");
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
@@ -21,12 +21,12 @@ export default function Profile({ uid = null, type = "embed", cid = null }) {
|
||||
removeFromChannel,
|
||||
canRemoveFromChannel,
|
||||
canRemove,
|
||||
removeUser,
|
||||
removeUser
|
||||
} = useContactOperation({ uid, cid });
|
||||
|
||||
const { data } = useSelector((store) => {
|
||||
return {
|
||||
data: store.contacts.byId[uid],
|
||||
data: store.contacts.byId[uid]
|
||||
};
|
||||
});
|
||||
|
||||
@@ -35,13 +35,12 @@ export default function Profile({ uid = null, type = "embed", cid = null }) {
|
||||
const {
|
||||
name,
|
||||
email,
|
||||
avatar,
|
||||
avatar
|
||||
// introduction = "This guy has nothing to introduce",
|
||||
} = data;
|
||||
const enableCall = type == "card" && canCall;
|
||||
const canRemoveFromServer = type == "embed" && canRemove;
|
||||
const hasMore =
|
||||
enableCall || email || canRemoveFromChannel || canRemoveFromServer;
|
||||
const hasMore = enableCall || email || canRemoveFromChannel || canRemoveFromServer;
|
||||
return (
|
||||
<StyledWrapper className={type}>
|
||||
<Avatar className="avatar" url={avatar} name={name} />
|
||||
|
||||
@@ -86,8 +86,7 @@ const StyledWrapper = styled.div`
|
||||
padding: 16px;
|
||||
width: 280px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 20px 25px 20px rgba(31, 41, 55, 0.1),
|
||||
0px 10px 10px rgba(31, 41, 55, 0.04);
|
||||
box-shadow: 0px 20px 25px 20px rgba(31, 41, 55, 0.1), 0px 10px 10px rgba(31, 41, 55, 0.04);
|
||||
border-radius: 6px;
|
||||
.icons {
|
||||
padding-bottom: 2px;
|
||||
|
||||
@@ -15,7 +15,7 @@ const Emojis = {
|
||||
"🚀": <EmojiRocket className="emoji" />,
|
||||
"❤️": <EmojiHeart className="emoji" />,
|
||||
"🙁": <EmojiUnhappy className="emoji" />,
|
||||
"🎉": <EmojiCelebrate className="emoji" />,
|
||||
"🎉": <EmojiCelebrate className="emoji" />
|
||||
};
|
||||
|
||||
export default function ReactionItem({ native = "" }) {
|
||||
|
||||
@@ -13,8 +13,7 @@ const StyledWrapper = styled.div`
|
||||
background: #fff;
|
||||
/* gap: 20px; */
|
||||
border: 1px solid #e5e7eb;
|
||||
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1),
|
||||
0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
border-radius: 25px;
|
||||
.txt {
|
||||
padding: 8px;
|
||||
|
||||
@@ -44,12 +44,7 @@ export default function Search() {
|
||||
<input placeholder="Search..." className="input" />
|
||||
</div>
|
||||
<Tooltip tip="More" placement="bottom">
|
||||
<Tippy
|
||||
interactive
|
||||
placement="bottom-end"
|
||||
trigger="click"
|
||||
content={<AddEntriesMenu />}
|
||||
>
|
||||
<Tippy interactive placement="bottom-end" trigger="click" content={<AddEntriesMenu />}>
|
||||
<img src={addIcon} alt="add icon" className="add" />
|
||||
</Tippy>
|
||||
</Tooltip>
|
||||
|
||||
@@ -45,8 +45,7 @@ export default function EmojiPicker({ selectEmoji }) {
|
||||
const clickEle = evt.target;
|
||||
const ignore =
|
||||
(clickEle.nodeName == "svg" && clickEle.dataset.emoji == "toggler") ||
|
||||
(clickEle.nodeName == "path" &&
|
||||
clickEle.parentElement.dataset.emoji == "toggler");
|
||||
(clickEle.nodeName == "path" && clickEle.parentElement.dataset.emoji == "toggler");
|
||||
// console.log("outside click", clickEle, clickEle.parentElement, ignore);
|
||||
if (ignore) return;
|
||||
// if(clickEle)
|
||||
@@ -60,11 +59,7 @@ export default function EmojiPicker({ selectEmoji }) {
|
||||
<div ref={ref} className={`picker ${visible ? "visible" : ""}`}>
|
||||
<Picker onSelect={handleSelect} />
|
||||
</div>
|
||||
<SmileIcon
|
||||
data-emoji="toggler"
|
||||
className="emoji"
|
||||
onClick={togglePickerVisible}
|
||||
/>
|
||||
<SmileIcon data-emoji="toggler" className="emoji" onClick={togglePickerVisible} />
|
||||
</Styled>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -57,11 +57,7 @@ const Styled = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: "";
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0) 63.54%,
|
||||
#f3f4f6 93.09%
|
||||
);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 63.54%, #f3f4f6 93.09%);
|
||||
}
|
||||
}
|
||||
.icon {
|
||||
@@ -87,15 +83,11 @@ const renderContent = (data) => {
|
||||
let res = null;
|
||||
switch (content_type) {
|
||||
case ContentTypes.text:
|
||||
res = reactStringReplace(
|
||||
content,
|
||||
/(\s{1}@[0-9]+\s{1})/g,
|
||||
(match, idx) => {
|
||||
console.log("match", match);
|
||||
const uid = match.trim().slice(1);
|
||||
return <Mention popover={false} key={idx} uid={uid} />;
|
||||
}
|
||||
);
|
||||
res = reactStringReplace(content, /(\s{1}@[0-9]+\s{1})/g, (match, idx) => {
|
||||
console.log("match", match);
|
||||
const uid = match.trim().slice(1);
|
||||
return <Mention popover={false} key={idx} uid={uid} />;
|
||||
});
|
||||
break;
|
||||
case ContentTypes.markdown:
|
||||
res = (
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function Toolbar({
|
||||
toggleMode,
|
||||
mode,
|
||||
to,
|
||||
context,
|
||||
context
|
||||
}) {
|
||||
const { addStageFile } = useUploadFile({ context, id: to });
|
||||
const fileInputRef = useRef(null);
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function UploadFileList({ context = "", id = null }) {
|
||||
const [editInfo, setEditInfo] = useState(null);
|
||||
const { stageFiles, updateStageFile, removeStageFile } = useUploadFile({
|
||||
context,
|
||||
id,
|
||||
id
|
||||
});
|
||||
const toggleModalVisible = (info) => {
|
||||
setEditInfo((prev) => (prev ? null : info));
|
||||
@@ -50,19 +50,12 @@ export default function UploadFileList({ context = "", id = null }) {
|
||||
return (
|
||||
<li className="file" key={url}>
|
||||
<div className="preview">
|
||||
{type.startsWith("image") ? (
|
||||
<img src={url} alt="image" />
|
||||
) : (
|
||||
getFileIcon(type, name)
|
||||
)}
|
||||
{type.startsWith("image") ? <img src={url} alt="image" /> : getFileIcon(type, name)}
|
||||
</div>
|
||||
<h4 className="name">{name}</h4>
|
||||
<span className="size">{formatBytes(size)}</span>
|
||||
<ul className="opts">
|
||||
<li
|
||||
className="opt edit"
|
||||
onClick={handleOpenEditModal.bind(null, idx)}
|
||||
>
|
||||
<li className="opt edit" onClick={handleOpenEditModal.bind(null, idx)}>
|
||||
<EditIcon />
|
||||
</li>
|
||||
<li
|
||||
|
||||
@@ -18,12 +18,12 @@ import useUploadFile from "../../hook/useUploadFile";
|
||||
|
||||
const Modes = {
|
||||
text: "text",
|
||||
markdown: "markdown",
|
||||
markdown: "markdown"
|
||||
};
|
||||
function Send({
|
||||
// 发给谁,或者是channel,或者是user
|
||||
context = "channel",
|
||||
id = "",
|
||||
id = ""
|
||||
}) {
|
||||
const { resetStageFiles } = useUploadFile({ context, id });
|
||||
const { getDraft, getUpdateDraft } = useDraft({ context, id });
|
||||
@@ -40,7 +40,7 @@ function Send({
|
||||
uploadFiles,
|
||||
channelsData,
|
||||
contactsData,
|
||||
uids,
|
||||
uids
|
||||
} = useSelector((store) => {
|
||||
return {
|
||||
channelsData: store.channels.byId,
|
||||
@@ -49,7 +49,7 @@ function Send({
|
||||
mode: store.ui.inputMode,
|
||||
from_uid: store.authData.uid,
|
||||
replying_mid: store.message.replying[`${context}_${id}`],
|
||||
uploadFiles: store.ui.uploadFiles[`${context}_${id}`],
|
||||
uploadFiles: store.ui.uploadFiles[`${context}_${id}`]
|
||||
};
|
||||
});
|
||||
const { sendMessage } = useSendMessage({ context, from: from_uid, to: id });
|
||||
@@ -83,7 +83,7 @@ function Send({
|
||||
type: content_type,
|
||||
content,
|
||||
from_uid,
|
||||
properties,
|
||||
properties
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -101,10 +101,10 @@ function Send({
|
||||
content_type: type,
|
||||
name,
|
||||
size,
|
||||
local_id: ts,
|
||||
local_id: ts
|
||||
},
|
||||
from_uid,
|
||||
sending: true,
|
||||
sending: true
|
||||
};
|
||||
addLocalFileMesage(tmpMsg);
|
||||
});
|
||||
@@ -118,7 +118,7 @@ function Send({
|
||||
type: "markdown",
|
||||
content,
|
||||
from_uid,
|
||||
properties: { local_id: +new Date() },
|
||||
properties: { local_id: +new Date() }
|
||||
});
|
||||
};
|
||||
const toggleMode = () => {
|
||||
@@ -127,24 +127,17 @@ function Send({
|
||||
const toggleMarkdownFullscreen = () => {
|
||||
setMarkdownFullscreen((prev) => !prev);
|
||||
};
|
||||
const name =
|
||||
context == "channel" ? channelsData[id]?.name : contactsData[id]?.name;
|
||||
const name = context == "channel" ? channelsData[id]?.name : contactsData[id]?.name;
|
||||
const placeholder = `Send to ${ChatPrefixs[context]}${name} `;
|
||||
const members =
|
||||
context == "channel"
|
||||
? channelsData[id]?.is_public
|
||||
? uids
|
||||
: channelsData[id]?.members
|
||||
: [];
|
||||
context == "channel" ? (channelsData[id]?.is_public ? uids : channelsData[id]?.members) : [];
|
||||
return (
|
||||
<StyledSend
|
||||
className={`send ${mode} ${markdownFullscreen ? "fullscreen" : ""} ${
|
||||
replying_mid ? "reply" : ""
|
||||
} ${context}`}
|
||||
>
|
||||
{replying_mid && (
|
||||
<Replying context={context} mid={replying_mid} id={id} />
|
||||
)}
|
||||
{replying_mid && <Replying context={context} mid={replying_mid} id={id} />}
|
||||
{mode == Modes.text && <UploadFileList context={context} id={id} />}
|
||||
|
||||
<div className={`send_box ${mode}`}>
|
||||
|
||||
@@ -8,6 +8,6 @@ export default function useFiles(initialFiles = []) {
|
||||
return {
|
||||
files,
|
||||
setFiles,
|
||||
resetFiles,
|
||||
resetFiles
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export default function Server() {
|
||||
const { server, userCount } = useSelector((store) => {
|
||||
return {
|
||||
userCount: store.contacts.ids.length,
|
||||
server: store.server,
|
||||
server: store.server
|
||||
};
|
||||
});
|
||||
// console.log("server info", server);
|
||||
@@ -78,12 +78,7 @@ export default function Server() {
|
||||
</div>
|
||||
</NavLink>
|
||||
<Tooltip tip="More" placement="bottom">
|
||||
<Tippy
|
||||
interactive
|
||||
placement="bottom-end"
|
||||
trigger="click"
|
||||
content={<AddEntriesMenu />}
|
||||
>
|
||||
<Tippy interactive placement="bottom-end" trigger="click" content={<AddEntriesMenu />}>
|
||||
<img src={addIcon} alt="add icon" className="add" />
|
||||
</Tippy>
|
||||
</Tooltip>
|
||||
|
||||
@@ -87,7 +87,7 @@ export default function StyledSettingContainer({
|
||||
navs = [],
|
||||
dangers = [],
|
||||
nav,
|
||||
children,
|
||||
children
|
||||
}) {
|
||||
const { pathname } = useLocation();
|
||||
return (
|
||||
@@ -101,10 +101,7 @@ export default function StyledSettingContainer({
|
||||
<ul key={title} data-title={title} className="items">
|
||||
{items.map(({ name, title }) => {
|
||||
return (
|
||||
<li
|
||||
key={name}
|
||||
className={`item ${name == nav?.name ? "curr" : ""}`}
|
||||
>
|
||||
<li key={name} className={`item ${name == nav?.name ? "curr" : ""}`}>
|
||||
<NavLink to={`${pathname}?nav=${name}`}>{title}</NavLink>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ export default function TippyDefault() {
|
||||
tippy.setDefaultProps({
|
||||
duration: 0,
|
||||
delay: [0, 0],
|
||||
plugins: [followCursor],
|
||||
plugins: [followCursor]
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ const StyledTip = styled.div`
|
||||
line-height: 18px;
|
||||
color: #1d2939;
|
||||
border-radius: var(--br);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08),
|
||||
0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
&::after {
|
||||
background-color: inherit;
|
||||
position: absolute;
|
||||
|
||||
@@ -7,9 +7,7 @@ export default function FileItem({ file = null }) {
|
||||
if (file) {
|
||||
const { type, name } = file;
|
||||
if (isTreatAsImage(file)) {
|
||||
setIcon(
|
||||
<img src={URL.createObjectURL(file)} alt="thumb" className="thumb" />
|
||||
);
|
||||
setIcon(<img src={URL.createObjectURL(file)} alt="thumb" className="thumb" />);
|
||||
return;
|
||||
}
|
||||
console.log("file type", type, name);
|
||||
|
||||
@@ -8,29 +8,18 @@ import Button from "../styled/Button";
|
||||
import StyledWrapper from "./styled";
|
||||
import useSendMessage from "../../hook/useSendMessage";
|
||||
|
||||
export default function UploadModal({
|
||||
context = "user",
|
||||
sendTo = 0,
|
||||
files = [],
|
||||
closeModal,
|
||||
}) {
|
||||
export default function UploadModal({ context = "user", sendTo = 0, files = [], closeModal }) {
|
||||
const from_uid = useSelector((store) => store.authData.uid);
|
||||
const {
|
||||
sendMessage,
|
||||
isSuccess: sendMessageSuccess,
|
||||
isSending,
|
||||
isSending
|
||||
} = useSendMessage({
|
||||
context,
|
||||
from: from_uid,
|
||||
to: sendTo,
|
||||
to: sendTo
|
||||
});
|
||||
const {
|
||||
data,
|
||||
uploadFile,
|
||||
progress,
|
||||
isUploading,
|
||||
isSuccess: uploadSuccess,
|
||||
} = useUploadFile();
|
||||
const { data, uploadFile, progress, isUploading, isSuccess: uploadSuccess } = useUploadFile();
|
||||
const handleUpload = () => {
|
||||
const file = files[0];
|
||||
uploadFile(file);
|
||||
@@ -42,7 +31,7 @@ export default function UploadModal({
|
||||
sendMessage({
|
||||
type: "file",
|
||||
content: { path },
|
||||
properties: rest,
|
||||
properties: rest
|
||||
});
|
||||
}
|
||||
}, [uploadSuccess, data]);
|
||||
@@ -65,11 +54,7 @@ export default function UploadModal({
|
||||
<Button className="cancel" onClick={closeModal}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
className="upload"
|
||||
disabled={sending}
|
||||
onClick={handleUpload}
|
||||
>
|
||||
<Button className="upload" disabled={sending} onClick={handleUpload}>
|
||||
{sending ? `Uploading (${progress}%)` : `Upload`}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@@ -11,8 +11,7 @@ const Styled = styled.div`
|
||||
padding: 24px;
|
||||
background: #06aed4;
|
||||
border-radius: var(--br);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08),
|
||||
0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
color: #ffffff;
|
||||
min-width: 362px;
|
||||
.title {
|
||||
@@ -57,16 +56,9 @@ const OverrideTippyArrowColor = createGlobalStyle`
|
||||
}
|
||||
`;
|
||||
import steps from "./steps";
|
||||
export default function UserGuide({
|
||||
step = 1,
|
||||
placement = "right-start",
|
||||
delay = null,
|
||||
children,
|
||||
}) {
|
||||
export default function UserGuide({ step = 1, placement = "right-start", delay = null, children }) {
|
||||
const dispatch = useDispatch();
|
||||
const { visible, step: reduxStep } = useSelector(
|
||||
(store) => store.ui.userGuide
|
||||
);
|
||||
const { visible, step: reduxStep } = useSelector((store) => store.ui.userGuide);
|
||||
const currStep = steps[step - 1];
|
||||
const isLastStep = steps.length == step;
|
||||
// if (!visible) return children;
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
const steps = [
|
||||
{
|
||||
title: "Step 1",
|
||||
description: "hello world 1",
|
||||
description: "hello world 1"
|
||||
},
|
||||
{
|
||||
title: "Step 2",
|
||||
description: "hello world 1",
|
||||
description: "hello world 1"
|
||||
},
|
||||
{
|
||||
title: "Step 3",
|
||||
description: "hello world 1",
|
||||
description: "hello world 1"
|
||||
},
|
||||
{
|
||||
title: "Step 4",
|
||||
description: "hello world 1",
|
||||
description: "hello world 1"
|
||||
},
|
||||
{
|
||||
title: "Step 5",
|
||||
description: "hello world 1",
|
||||
},
|
||||
description: "hello world 1"
|
||||
}
|
||||
];
|
||||
export default steps;
|
||||
|
||||
@@ -65,39 +65,22 @@ const StyledInput = styled.input`
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Input({
|
||||
type = "text",
|
||||
prefix = "",
|
||||
className,
|
||||
...rest
|
||||
}) {
|
||||
export default function Input({ type = "text", prefix = "", className, ...rest }) {
|
||||
const [inputType, setInputType] = useState(type);
|
||||
const togglePasswordVisible = () => {
|
||||
setInputType((prev) => (prev == "password" ? "text" : "password"));
|
||||
};
|
||||
return type == "password" ? (
|
||||
<StyledWrapper className={className}>
|
||||
<StyledInput
|
||||
type={inputType}
|
||||
className={`inner ${className}`}
|
||||
{...rest}
|
||||
/>
|
||||
<StyledInput type={inputType} className={`inner ${className}`} {...rest} />
|
||||
<div className="view" onClick={togglePasswordVisible}>
|
||||
{inputType == "password" ? (
|
||||
<HiEyeOff color="#78787c" />
|
||||
) : (
|
||||
<HiEye color="#78787c" />
|
||||
)}
|
||||
{inputType == "password" ? <HiEyeOff color="#78787c" /> : <HiEye color="#78787c" />}
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
) : prefix ? (
|
||||
<StyledWrapper className={className}>
|
||||
<span className="prefix">{prefix}</span>
|
||||
<StyledInput
|
||||
className={`inner ${className}`}
|
||||
type={inputType}
|
||||
{...rest}
|
||||
/>
|
||||
<StyledInput className={`inner ${className}`} type={inputType} {...rest} />
|
||||
</StyledWrapper>
|
||||
) : (
|
||||
<StyledInput type={inputType} className={className} {...rest} />
|
||||
|
||||
@@ -5,8 +5,7 @@ const StyledMenu = styled.ul`
|
||||
gap: 2px;
|
||||
padding: 4px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 20px 25px 20px rgba(31, 41, 55, 0.1),
|
||||
0px 10px 10px rgba(31, 41, 55, 0.04);
|
||||
box-shadow: 0px 20px 25px 20px rgba(31, 41, 55, 0.1), 0px 10px 10px rgba(31, 41, 55, 0.04);
|
||||
border-radius: 12px;
|
||||
min-width: 200px;
|
||||
.item {
|
||||
|
||||
@@ -3,89 +3,89 @@ import styled from "styled-components";
|
||||
import { nanoid } from "@reduxjs/toolkit";
|
||||
|
||||
const StyledForm = styled.form`
|
||||
> .option {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
> input[type="radio"] {
|
||||
display: none;
|
||||
|
||||
& + .box {
|
||||
width: 512px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #d0d5dd;
|
||||
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 8px;
|
||||
transition: all ease-in-out 250ms;
|
||||
|
||||
& > label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #667085;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: all ease-in-out 250ms;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
box-shadow: inset 0 0 0 4px #ffffff;
|
||||
border: 1px solid #d0d5dd;
|
||||
margin: 14px 8px 14px 14px;
|
||||
transition: all ease-in-out 500ms;
|
||||
}
|
||||
> .option {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&:checked + .box {
|
||||
background: #22ccee;
|
||||
border: 1px solid #d0d5dd;
|
||||
> input[type="radio"] {
|
||||
display: none;
|
||||
|
||||
& > label {
|
||||
color: #ffffff;
|
||||
& + .box {
|
||||
width: 512px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #d0d5dd;
|
||||
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 8px;
|
||||
transition: all ease-in-out 250ms;
|
||||
|
||||
&:before {
|
||||
background: #ffffff;
|
||||
box-shadow: inset 0 0 0 4px #22ccee;
|
||||
border: 1px solid #ffffff;
|
||||
}
|
||||
& > label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #667085;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: all ease-in-out 250ms;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
box-shadow: inset 0 0 0 4px #ffffff;
|
||||
border: 1px solid #d0d5dd;
|
||||
margin: 14px 8px 14px 14px;
|
||||
transition: all ease-in-out 500ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:checked + .box {
|
||||
background: #22ccee;
|
||||
border: 1px solid #d0d5dd;
|
||||
|
||||
& > label {
|
||||
color: #ffffff;
|
||||
|
||||
&:before {
|
||||
background: #ffffff;
|
||||
box-shadow: inset 0 0 0 4px #22ccee;
|
||||
border: 1px solid #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default function Radio({ options, value = undefined, onChange = undefined }) {
|
||||
const [innerValue, setInnerValue] = useState(0);
|
||||
const id = useRef(nanoid());
|
||||
const [innerValue, setInnerValue] = useState(0);
|
||||
const id = useRef(nanoid());
|
||||
|
||||
return (
|
||||
<StyledForm>
|
||||
{options.map((item, index) => (
|
||||
<div className="option" key={index}>
|
||||
<input
|
||||
type="radio"
|
||||
checked={(value !== undefined ? value : innerValue) === index}
|
||||
onChange={() => {
|
||||
value === undefined && setInnerValue(index);
|
||||
onChange !== null && onChange(index);
|
||||
}}
|
||||
id={`${id.current}-${index}`}
|
||||
/>
|
||||
<div className="box">
|
||||
<label htmlFor={`${id.current}-${index}`}>{item}</label>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</StyledForm>
|
||||
);
|
||||
return (
|
||||
<StyledForm>
|
||||
{options.map((item, index) => (
|
||||
<div className="option" key={index}>
|
||||
<input
|
||||
type="radio"
|
||||
checked={(value !== undefined ? value : innerValue) === index}
|
||||
onChange={() => {
|
||||
value === undefined && setInnerValue(index);
|
||||
onChange !== null && onChange(index);
|
||||
}}
|
||||
id={`${id.current}-${index}`}
|
||||
/>
|
||||
<div className="box">
|
||||
<label htmlFor={`${id.current}-${index}`}>{item}</label>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</StyledForm>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@ export default function Select({ options = [], updateSelect = null }) {
|
||||
{options.map(({ title, value, selected, underline }) => {
|
||||
return (
|
||||
<li
|
||||
onClick={
|
||||
selected ? null : handleSelect.bind(null, { title, value })
|
||||
}
|
||||
onClick={selected ? null : handleSelect.bind(null, { title, value })}
|
||||
className={`item sb ${underline ? "underline" : ""}`}
|
||||
data-disabled={selected}
|
||||
key={value}
|
||||
|
||||
Reference in New Issue
Block a user