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,
+ };
+}