diff --git a/src/common/component/Contact.js b/src/common/component/Contact.js deleted file mode 100644 index 8474e34f..00000000 --- a/src/common/component/Contact.js +++ /dev/null @@ -1,106 +0,0 @@ -// import React from "react"; -import styled from "styled-components"; -import { useSelector } from "react-redux"; -import { useNavigate } from "react-router-dom"; -import Tippy from "@tippyjs/react"; -import IconOwner from "../../assets/icons/owner.svg"; -import Avatar from "./Avatar"; -import Profile from "./Profile"; - -const StyledWrapper = styled.div` - display: flex; - align-items: center; - justify-content: flex-start; - gap: 8px; - padding: 8px; - border-radius: 8px; - user-select: none; - &.compact { - padding: 0; - } - &.interactive { - &:hover, - &.active { - background: rgba(116, 127, 141, 0.1); - } - } - .avatar { - cursor: pointer; - width: ${({ size }) => `${size}px`}; - height: ${({ size }) => `${size}px`}; - position: relative; - img { - border-radius: 50%; - width: 100%; - height: 100%; - } - .status { - position: absolute; - bottom: -2px; - right: -4px; - width: 10px; - height: 10px; - box-sizing: content-box; - border-radius: 50%; - border: 2px solid #fff; - &.online { - background-color: #22c55e; - } - &.offline { - background-color: #a1a1aa; - } - &.alert { - background-color: #dc2626; - } - } - } - .name { - /* user-select: text; */ - font-weight: 600; - font-size: 14px; - line-height: 20px; - color: #52525b; - } -`; -export default function Contact({ - cid = null, - owner = false, - dm = false, - interactive = true, - uid = "", - popover = false, - compact = false, - avatarSize = 32, -}) { - const navigate = useNavigate(); - const curr = useSelector((store) => store.contacts.byId[uid]); - const handleDoubleClick = () => { - navigate(`/chat/dm/${uid}`); - }; - if (!curr) return null; - return ( - } - > - -
- -
-
- {!compact && {curr?.name}} - {owner && } -
-
- ); -} diff --git a/src/common/component/Contact/ContextMenu.js b/src/common/component/Contact/ContextMenu.js new file mode 100644 index 00000000..a68ee9f2 --- /dev/null +++ b/src/common/component/Contact/ContextMenu.js @@ -0,0 +1,67 @@ +// import { useState } from "react"; +import Tippy from "@tippyjs/react"; +// import { useDispatch } from "react-redux"; +import useContactOperation from "../../hook/useContactOperation"; +import ContextMenu from "../ContextMenu"; + +export default function ContactContextMenu({ + enable = false, + uid, + cid, + visible, + hide, + children, +}) { + const { + canCall, + call, + copyEmail, + canCopyEmail, + startChat, + canRemoveFromChannel, + removeFromChannel, + } = useContactOperation({ + uid, + cid, + }); + return ( + <> + + } + > + {children} + + + ); +} diff --git a/src/common/component/Contact/index.js b/src/common/component/Contact/index.js new file mode 100644 index 00000000..fdc26378 --- /dev/null +++ b/src/common/component/Contact/index.js @@ -0,0 +1,69 @@ +// import React from "react"; +import { useSelector } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import Tippy from "@tippyjs/react"; +import IconOwner from "../../../assets/icons/owner.svg"; +import Avatar from "../Avatar"; +import Profile from "../Profile"; +import ContextMenu from "./ContextMenu"; +import StyledWrapper from "./styled"; +import useContextMenu from "../../hook/useContextMenu"; +export default function Contact({ + cid = null, + owner = false, + dm = false, + interactive = true, + uid = "", + popover = false, + compact = false, + avatarSize = 32, + enableContextMenu = false, +}) { + const navigate = useNavigate(); + const { + visible: contextMenuVisible, + handleContextMenuEvent, + hideContextMenu, + } = useContextMenu(); + const curr = useSelector((store) => store.contacts.byId[uid]); + const handleDoubleClick = () => { + navigate(`/chat/dm/${uid}`); + }; + if (!curr) return null; + return ( + + } + > + +
+ +
+
+ {!compact && {curr?.name}} + {owner && } +
+
+
+ ); +} diff --git a/src/common/component/Contact/styled.js b/src/common/component/Contact/styled.js new file mode 100644 index 00000000..f4913b14 --- /dev/null +++ b/src/common/component/Contact/styled.js @@ -0,0 +1,58 @@ +import styled from "styled-components"; + +const StyledWrapper = styled.div` + display: flex; + align-items: center; + justify-content: flex-start; + gap: 8px; + padding: 8px; + border-radius: 8px; + user-select: none; + &.compact { + padding: 0; + } + &.interactive { + &:hover, + &.active { + background: rgba(116, 127, 141, 0.1); + } + } + .avatar { + cursor: pointer; + width: ${({ size }) => `${size}px`}; + height: ${({ size }) => `${size}px`}; + position: relative; + img { + border-radius: 50%; + width: 100%; + height: 100%; + } + .status { + position: absolute; + bottom: -2px; + right: -4px; + width: 10px; + height: 10px; + box-sizing: content-box; + border-radius: 50%; + border: 2px solid #fff; + &.online { + background-color: #22c55e; + } + &.offline { + background-color: #a1a1aa; + } + &.alert { + background-color: #dc2626; + } + } + } + .name { + /* user-select: text; */ + font-weight: 600; + font-size: 14px; + line-height: 20px; + color: #52525b; + } +`; +export default StyledWrapper; diff --git a/src/common/component/Profile/index.js b/src/common/component/Profile/index.js index 09c9fd72..65c53d05 100644 --- a/src/common/component/Profile/index.js +++ b/src/common/component/Profile/index.js @@ -1,46 +1,34 @@ -import { useEffect } from "react"; +// import { useEffect } from "react"; import { useSelector } from "react-redux"; -import { NavLink, useNavigate } from "react-router-dom"; -import toast from "react-hot-toast"; +import { NavLink } from "react-router-dom"; +// import toast from "react-hot-toast"; import Tippy from "@tippyjs/react"; -import { hideAll } from "tippy.js"; +// import { hideAll } from "tippy.js"; import IconMessage from "../../../assets/icons/message.svg"; import IconCall from "../../../assets/icons/call.svg"; import IconMore from "../../../assets/icons/more.svg"; import Avatar from "../Avatar"; import StyledWrapper from "./styled"; import StyledMenu from "../styled/Menu"; -import useCopy from "../../hook/useCopy"; -import { useRemoveMembersMutation } from "../../../app/services/channel"; -import { useLazyDeleteContactQuery } from "../../../app/services/contact"; +import useContactOperation from "../../hook/useContactOperation"; export default function Profile({ uid = null, type = "embed", cid = null }) { - const navigateTo = useNavigate(); - const [ - removeUser, - { isSuccess: removeUserSuccess }, - ] = useLazyDeleteContactQuery(); - const [ + const { + canCall, + call, + canCopyEmail, + copyEmail, removeFromChannel, - { isSuccess: removeSuccess }, - ] = useRemoveMembersMutation(); - const { copy } = useCopy(); - const { data, channel, loginUid, isAdmin } = useSelector((store) => { + canRemoveFromChannel, + canRemove, + removeUser, + } = useContactOperation({ uid, cid }); + + const { data } = useSelector((store) => { return { data: store.contacts.byId[uid], - channel: store.channels.byId[cid], - loginUid: store.authData.uid, - isAdmin: store.contacts.byId[store.authData.uid]?.is_admin, }; }); - useEffect(() => { - if (removeSuccess || removeUserSuccess) { - toast.success("Remove Successfully"); - if (removeUserSuccess) { - navigateTo(`/contacts`); - } - } - }, [removeSuccess, removeUserSuccess]); if (!data) return null; // console.log("profile", data); @@ -50,27 +38,10 @@ export default function Profile({ uid = null, type = "embed", cid = null }) { avatar, // introduction = "This guy has nothing to introduce", } = data; - const handleClick = () => { - toast.success("cooming soon..."); - }; - const handlCopyEmail = () => { - copy(email); - hideAll(); - }; - const handleRemove = ({ from = "channel", uid }) => { - const remove = from == "channel" ? removeFromChannel : removeUser; - remove(from == "channel" ? { id: +cid, members: [+uid] } : uid); - hideAll(); - }; - const canCall = type == "card" && loginUid != uid; - const canRemoveFromServer = type == "embed" && isAdmin; - const canRemoveFromChannel = - cid && - !channel?.is_public && - (isAdmin || channel?.owner == loginUid) && - channel?.owner != uid; + const enableCall = type == "card" && canCall; + const canRemoveFromServer = type == "embed" && canRemove; const hasMore = - canCall || email || canRemoveFromChannel || canRemoveFromServer; + enableCall || email || canRemoveFromChannel || canRemoveFromServer; return ( @@ -86,7 +57,7 @@ export default function Profile({ uid = null, type = "embed", cid = null }) { {/* */} {type == "embed" && ( -
  • +
  • Call
  • @@ -101,30 +72,24 @@ export default function Profile({ uid = null, type = "embed", cid = null }) { hideOnClick={true} content={ - {canCall && ( -
  • + {enableCall && ( +
  • {/* */} Call
  • )} - {email && ( -
  • + {canCopyEmail && ( +
  • Copy Email
  • )} {canRemoveFromChannel && ( -
  • +
  • Remove from Channel
  • )} {canRemoveFromServer && ( -
  • +
  • Remove from Server
  • )} diff --git a/src/common/hook/useContactOperation.js b/src/common/hook/useContactOperation.js new file mode 100644 index 00000000..da1c69c6 --- /dev/null +++ b/src/common/hook/useContactOperation.js @@ -0,0 +1,75 @@ +import { useEffect } from "react"; +import toast from "react-hot-toast"; +// import { ContentTypes } from "../../../app/config"; +import { useSelector } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { hideAll } from "tippy.js"; +import { useRemoveMembersMutation } from "../../app/services/channel"; +import { useLazyDeleteContactQuery } from "../../app/services/contact"; +import useCopy from "./useCopy"; + +export default function useContactOperation({ uid, cid }) { + const [ + removeUser, + { isSuccess: removeUserSuccess }, + ] = useLazyDeleteContactQuery(); + const [ + removeFromChannel, + { isSuccess: removeSuccess }, + ] = useRemoveMembersMutation(); + const navigateTo = useNavigate(); + const { copy } = useCopy(); + const { user, channel, loginUid, isAdmin } = useSelector((store) => { + return { + user: store.contacts.byId[uid], + channel: store.channels.byId[cid], + loginUid: store.authData.uid, + isAdmin: store.contacts.byId[store.authData.uid]?.is_admin, + }; + }); + useEffect(() => { + if (removeSuccess || removeUserSuccess) { + toast.success("Remove Successfully"); + if (removeUserSuccess) { + navigateTo(`/contacts`); + } + } + }, [removeSuccess, removeUserSuccess]); + const handleRemoveFromChannel = () => { + removeFromChannel({ id: +cid, members: [+uid] }); + hideAll(); + }; + const handleRemove = () => { + removeUser(uid); + hideAll(); + }; + const copyEmail = () => { + copy(user?.email); + hideAll(); + }; + const startChat = () => { + navigateTo(`/chat/dm/${uid}`); + }; + const call = () => { + toast.success("Cooming Soon..."); + hideAll(); + }; + const canRemoveFromChannel = + cid && + !channel?.is_public && + (isAdmin || channel?.owner == loginUid) && + channel?.owner != uid; + const canCall = loginUid != uid; + const canRemove = isAdmin && loginUid != uid; + return { + canRemove, + removeUser: handleRemove, + startChat, + removeFromChannel: handleRemoveFromChannel, + canRemoveFromChannel, + canCopyEmail: !!user?.email, + copyEmail, + canCall, + call, + }; +}