diff --git a/src/assets/icons/invite.from.channel.svg b/src/assets/icons/invite.from.channel.svg new file mode 100644 index 00000000..6b72de9e --- /dev/null +++ b/src/assets/icons/invite.from.channel.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/component/InviteLink.js b/src/common/component/InviteLink.js index 5d6c2468..bc4ea393 100644 --- a/src/common/component/InviteLink.js +++ b/src/common/component/InviteLink.js @@ -1,12 +1,7 @@ -import { useEffect } from "react"; +// import { useEffect } from "react"; import styled from "styled-components"; -import { useSelector } from "react-redux"; -import { - useLazyCreateInviteLinkQuery, - useGetSMTPConfigQuery, -} from "../../app/services/server"; +import useInviteLink from "../hook/useInviteLink"; import Button from "./styled/Button"; -import useCopy from "../hook/useCopy"; const StyledWrapper = styled.div` display: flex; flex-direction: column; @@ -51,51 +46,31 @@ const StyledWrapper = styled.div` `; export default function InviteLink() { const { - data: config, - isSuccess: configQuerySuccess, - } = useGetSMTPConfigQuery(); - const [copid, copy] = useCopy(); - const { - inviteLink: { link }, - loginUser, - } = useSelector((store) => { - return { - inviteLink: store.server.inviteLink, - loginUser: store.contacts.byId[store.authData.uid], - }; - }); - const [createLink, { isLoading }] = useLazyCreateInviteLinkQuery(); - useEffect(() => { - if (!link && loginUser && loginUser.is_admin) { - createLink(); - } - }, [link, loginUser]); + generating, + link, + linkCopied, + copyLink, + generateNewLink, + } = useInviteLink(); const handleNewLink = () => { - createLink(); + generateNewLink(); }; - if (!loginUser || !loginUser.is_admin || !configQuerySuccess) return null; - let finalLink = null; - if (link) { - const tmpURL = new URL(link); - tmpURL.searchParams.set("code", config.enabled); - finalLink = tmpURL.href; - } return ( Share this link to invite people to this server.
- - {finalLink} + + {link} -
Invite link expires in 7 days. -
); diff --git a/src/common/component/InviteModal.js b/src/common/component/InviteModal.js new file mode 100644 index 00000000..b28fd9d7 --- /dev/null +++ b/src/common/component/InviteModal.js @@ -0,0 +1,134 @@ +// import React from 'react' +import styled from "styled-components"; +import useInviteLink from "../hook/useInviteLink"; +import CloseIcon from "../../assets/icons/close.svg"; +const Styled = styled.div` + display: flex; + flex-direction: column; + background: #fff; + box-shadow: 0px 25px 50px rgba(31, 41, 55, 0.25); + border-radius: var(--br); + padding: 0; + min-width: 408px; + > .title { + display: flex; + align-items: center; + justify-content: space-between; + font-style: normal; + font-weight: 600; + font-size: 18px; + line-height: 28px; + color: #374151; + padding: 16px; + .close { + cursor: pointer; + } + } + + .body { + padding: 16px; + box-shadow: 0px -1px 2px rgba(0, 0, 0, 0.06); + .input { + position: relative; + button { + position: absolute; + right: 4px; + top: 50%; + transform: translateY(-50%); + } + input { + padding-right: 80px; + } + } + > .invite { + display: flex; + flex-direction: column; + gap: 16px; + margin-bottom: 16px; + /* position: relative; */ + } + > .link { + display: flex; + flex-direction: column; + gap: 8px; + margin-bottom: 12px; + /* position: relative; */ + } + label { + color: #6b7280; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 20px; + } + > .tip { + color: #344054; + font-weight: 400; + font-size: 12px; + line-height: 18px; + button { + background: none; + border: none; + color: #22ccee; + } + } + } +`; +import Modal from "./Modal"; +import Button from "./styled/Button"; +import Input from "./styled/Input"; +export default function InviteModal({ title, closeModal }) { + const { + enableSMTP, + linkCopied, + link, + copyLink, + generateNewLink, + generating, + } = useInviteLink(); + return ( + + +

+ Invite friends to #{title}{" "} + +

+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ Invite link expires in 7 days.{" "} + +
+
+
+
+ ); +} diff --git a/src/common/hook/useInviteLink.js b/src/common/hook/useInviteLink.js new file mode 100644 index 00000000..1001ab8d --- /dev/null +++ b/src/common/hook/useInviteLink.js @@ -0,0 +1,35 @@ +import { useState, useEffect } from "react"; +import useCopy from "./useCopy"; +import { + useCreateInviteLinkQuery, + useGetSMTPConfigQuery, +} from "../../app/services/server"; +export default function useInviteLink() { + const [finalLink, setFinalLink] = useState(""); + const { + data: config, + + isSuccess: configQuerySuccess, + } = useGetSMTPConfigQuery(); + const { data: link, isLoading, refetch } = useCreateInviteLinkQuery(); + const [linkCopied, copy] = useCopy(); + const copyLink = () => { + copy(finalLink); + }; + useEffect(() => { + if (link && config) { + const tmpURL = new URL(link); + tmpURL.searchParams.set("code", config.enabled); + setFinalLink(tmpURL.href); + } + }, [link, config]); + + return { + enableSMTP: config?.enabled, + generating: isLoading, + generateNewLink: refetch, + link: finalLink, + linkCopied, + copyLink, + }; +} diff --git a/src/routes/chat/ChannelList/NavItem.js b/src/routes/chat/ChannelList/NavItem.js index 5cbd590c..ac28062c 100644 --- a/src/routes/chat/ChannelList/NavItem.js +++ b/src/routes/chat/ChannelList/NavItem.js @@ -1,3 +1,4 @@ +import { useState } from "react"; import { useNavigate } from "react-router-dom"; import { useDrop } from "react-dnd"; import { NativeTypes } from "react-dnd-html5-backend"; @@ -5,6 +6,7 @@ import { useSelector } from "react-redux"; import Tippy from "@tippyjs/react"; import useContextMenu from "../../../common/hook/useContextMenu"; import ContextMenu from "../../../common/component/ContextMenu"; +import InviteModal from "../../../common/component/InviteModal"; import Tooltip from "../../../common/component/Tooltip"; // import { useDebounce} from "rooks"; import { useReadMessageMutation } from "../../../app/services/message"; @@ -15,6 +17,7 @@ import ChannelIcon from "../../../common/component/ChannelIcon"; import { getUnreadCount } from "../utils"; const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => { + const [inviteModalVisible, setInviteModalVisible] = useState(false); const navigate = useNavigate(); const [muteChannel] = useUpdateMuteSettingMutation(); const [updateReadIndex] = useReadMessageMutation(); @@ -75,6 +78,11 @@ const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => { updateReadIndex(param); } }; + const toggleInviteModalVisible = (evt) => { + evt.preventDefault(); + evt.stopPropagation(); + setInviteModalVisible((prev) => !prev); + }; const handleMute = () => { const data = muted ? { remove_groups: [id] } @@ -82,77 +90,97 @@ const NavItem = ({ id, setFiles, toggleRemoveConfirm }) => { muteChannel(data); }; const { is_public, name } = channel; - const unreads = getUnreadCount({ mids, messageData, readIndex, loginUid }); - const isDot = muted || unreads > 99; + const { unreads, mentions = [] } = getUnreadCount({ + mids, + messageData, + readIndex, + loginUid, + }); + const isMentions = mentions.length !== 0; return ( - - } - > - + + } > -
- - {name} -
-
- - - - {unreads > 0 && ( - - {isDot ? null : unreads} - - )} -
-
-
+ +
+ + {name} +
+
+ + + + + + + {unreads > 0 && ( + + {isMentions ? mentions.length : unreads} + + )} +
+
+ + {inviteModalVisible && ( + + )} + ); }; diff --git a/src/routes/chat/ChannelList/styled.js b/src/routes/chat/ChannelList/styled.js index d4e86839..2aab7a9a 100644 --- a/src/routes/chat/ChannelList/styled.js +++ b/src/routes/chat/ChannelList/styled.js @@ -1,6 +1,7 @@ import styled from "styled-components"; import { NavLink } from "react-router-dom"; import settingIcon from "../../../assets/icons/setting.svg?url"; +import inviteIcon from "../../../assets/icons/invite.from.channel.svg?url"; const Styled = styled(NavLink)` position: relative; display: flex; @@ -21,7 +22,7 @@ const Styled = styled(NavLink)` font-weight: 600; font-size: 14px; line-height: 20px; - width: 160px; + width: 140px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -33,14 +34,20 @@ const Styled = styled(NavLink)` > .icons { display: flex; align-items: center; - gap: 4px; - > .setting { + gap: 8px; + > .icon { visibility: hidden; display: flex; width: 16px; height: 16px; - background-image: url(${settingIcon}); - background-size: 16px; + background-size: contain; + + &.setting { + background-image: url(${settingIcon}); + } + &.invite { + background-image: url(${inviteIcon}); + } } > .badge { color: #fff; @@ -54,6 +61,9 @@ const Styled = styled(NavLink)` font-weight: 900; font-size: 10px; line-height: 10px; + &.mention { + background: #f97066; + } &.dot { min-width: unset; width: 6px; @@ -70,11 +80,11 @@ const Styled = styled(NavLink)` background: #bfbfbf; } } - &:hover > .icons > { - .badge { + &:hover > .icons { + > .badge { display: none; } - .setting { + > .icon { visibility: visible; } }