feat: add call entry into profile

This commit is contained in:
Tristan Yang
2023-05-19 15:35:34 +08:00
parent 50387e726a
commit 7f2e976286
4 changed files with 58 additions and 16 deletions
+1
View File
@@ -19,6 +19,7 @@
"user": "User", "user": "User",
"copy_email": "Copy Email", "copy_email": "Copy Email",
"send_msg": "Send Message", "send_msg": "Send Message",
"call": "Call",
"remove": "Remove from Server", "remove": "Remove from Server",
"remove_from_contact": "Remove From Contact", "remove_from_contact": "Remove From Contact",
"remove_account": "Remove Account", "remove_account": "Remove Account",
+1
View File
@@ -18,6 +18,7 @@
"user": "普通用户", "user": "普通用户",
"copy_email": "复制邮箱", "copy_email": "复制邮箱",
"send_msg": "发消息", "send_msg": "发消息",
"call": "呼叫",
"remove": "从服务器删除", "remove": "从服务器删除",
"remove_from_contact": "从联系人中移除", "remove_from_contact": "从联系人中移除",
"remove_account": "注销账号", "remove_account": "注销账号",
+29 -11
View File
@@ -1,13 +1,16 @@
import { FC, memo } from "react"; import { useGetAgoraStatusQuery } from "@/app/services/server";
import { NavLink } from "react-router-dom"; import { useAppSelector } from "@/app/store";
import Tippy from "@tippyjs/react"; import IconCall from "@/assets/icons/call.svg";
import IconMessage from "@/assets/icons/message.svg"; import IconMessage from "@/assets/icons/message.svg";
import IconMore from "@/assets/icons/more.svg"; import IconMore from "@/assets/icons/more.svg";
import Avatar from "../Avatar";
import useUserOperation from "@/hooks/useUserOperation"; import useUserOperation from "@/hooks/useUserOperation";
import { useAppSelector } from "@/app/store"; import Tippy from "@tippyjs/react";
import { useTranslation } from "react-i18next";
import clsx from "clsx"; import clsx from "clsx";
import { FC, memo } from "react";
import { useTranslation } from "react-i18next";
import { NavLink } from "react-router-dom";
import Avatar from "../Avatar";
interface Props { interface Props {
uid: number; uid: number;
@@ -16,11 +19,13 @@ interface Props {
} }
const Profile: FC<Props> = ({ uid, type = "embed", cid }) => { const Profile: FC<Props> = ({ uid, type = "embed", cid }) => {
const { data: agoraEnabled } = useGetAgoraStatusQuery();
const { t } = useTranslation("member"); const { t } = useTranslation("member");
const { t: ct } = useTranslation(); const { t: ct } = useTranslation();
const { const {
canCopyEmail, canCopyEmail,
copyEmail, copyEmail,
startCall,
removeFromChannel, removeFromChannel,
canRemoveFromChannel, canRemoveFromChannel,
canRemove, canRemove,
@@ -32,7 +37,6 @@ const Profile: FC<Props> = ({ uid, type = "embed", cid }) => {
data: store.users.byId[uid] data: store.users.byId[uid]
}; };
}); });
if (!data) return null; if (!data) return null;
// console.log("profile", data); // console.log("profile", data);
const { const {
@@ -41,11 +45,14 @@ const Profile: FC<Props> = ({ uid, type = "embed", cid }) => {
avatar avatar
// introduction = "This guy has nothing to introduce", // introduction = "This guy has nothing to introduce",
} = data; } = data;
const isCard = type == 'card'; const isCard = type == "card";
const canRemoveFromServer = !isCard && canRemove; const canRemoveFromServer = !isCard && canRemove;
const hasMore = email || canRemoveFromChannel || canRemoveFromServer; const hasMore = email || canRemoveFromChannel || canRemoveFromServer;
const iconClass = `cursor-pointer flex flex-col items-center gap-1 rounded-lg w-32 text-primary-400 bg-gray-50 dark:bg-gray-800 text-sm pt-3.5 pb-3`; const iconClass = `cursor-pointer flex flex-col items-center gap-1 rounded-lg w-32 text-primary-400 bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 text-sm pt-3.5 pb-3`;
const containerClass = clsx(`flex-center flex-col gap-1 z-[99] mt-20 select-none`, isCard ? "p-4 w-[280px] bg-white dark:bg-gray-800 drop-shadow rounded-md" : "md:w-[432px]"); const containerClass = clsx(
`flex-center flex-col gap-1 z-[99] mt-20 select-none`,
isCard ? "p-4 w-[280px] bg-white dark:bg-gray-800 drop-shadow rounded-md" : "md:w-[432px]"
);
return ( return (
<div className={containerClass}> <div className={containerClass}>
<Avatar <Avatar
@@ -65,15 +72,26 @@ const Profile: FC<Props> = ({ uid, type = "embed", cid }) => {
<span>{t("send_msg")}</span> <span>{t("send_msg")}</span>
</li> </li>
</NavLink> </NavLink>
{agoraEnabled && type == "embed" && (
<li role="button" onClick={startCall} className={`${iconClass} icon chat`}>
<IconCall className="fill-primary-400" />
<span>{t("call")}</span>
</li>
)}
<Tippy <Tippy
disabled={!hasMore} disabled={!hasMore}
interactive interactive
popperOptions={{ strategy: "fixed" }} popperOptions={{ strategy: "fixed" }}
placement="bottom-start" placement="right"
trigger="click" trigger="click"
hideOnClick={true} hideOnClick={true}
content={ content={
<ul className="context-menu"> <ul className="context-menu">
{agoraEnabled && type == "card" && (
<li className="item" onClick={startCall}>
{t("call")}
</li>
)}
{canCopyEmail && ( {canCopyEmail && (
<li className="item" onClick={copyEmail.bind(undefined, email)}> <li className="item" onClick={copyEmail.bind(undefined, email)}>
{t("copy_email")} {t("copy_email")}
+26 -4
View File
@@ -3,18 +3,23 @@ import toast from "react-hot-toast";
// import { ContentTypes } from "@/app/config"; // import { ContentTypes } from "@/app/config";
import { useNavigate, useMatch } from "react-router-dom"; import { useNavigate, useMatch } from "react-router-dom";
import { hideAll } from "tippy.js"; import { hideAll } from "tippy.js";
import { useTranslation } from "react-i18next";
import { useRemoveMembersMutation } from "@/app/services/channel"; import { useRemoveMembersMutation } from "@/app/services/channel";
import { useLazyDeleteUserQuery, useUpdateContactStatusMutation } from "@/app/services/user"; import { useLazyDeleteUserQuery, useUpdateContactStatusMutation } from "@/app/services/user";
// import useConfig from "./useConfig";
import useCopy from "./useCopy"; import useCopy from "./useCopy";
import { useAppSelector } from "@/app/store"; import { useAppSelector } from "@/app/store";
import { useTranslation } from "react-i18next"; import { useVoice } from "@/components/Voice";
// import { AgoraConfig } from "@/types/server"; import { updateCallInfo } from "@/app/slices/voice";
import { updateDMVisibleAside } from "@/app/slices/footprint";
import { useDispatch } from "react-redux";
interface IProps { interface IProps {
uid?: number; uid?: number;
cid?: number; cid?: number;
} }
const useUserOperation = ({ uid, cid }: IProps) => { const useUserOperation = ({ uid, cid }: IProps) => {
const { joinVoice, joined, joining = false, joinedAtThisContext } = useVoice({ id: uid ?? 0, context: "dm" });
const dispatch = useDispatch();
const { t: ct } = useTranslation(); const { t: ct } = useTranslation();
const [passedUid, setPassedUid] = useState<number | undefined>(undefined); const [passedUid, setPassedUid] = useState<number | undefined>(undefined);
const isUserDetailPath = useMatch(`/users/${uid}`); const isUserDetailPath = useMatch(`/users/${uid}`);
@@ -68,7 +73,23 @@ const useUserOperation = ({ uid, cid }: IProps) => {
copy(finalEmail || ""); copy(finalEmail || "");
hideAll(); hideAll();
}; };
const startCall = () => {
if (joining || joined) {
alert("You have joined another channel, please leave first!");
return;
}
joinVoice();
const data = {
id: uid ?? 0,
aside: "voice" as const
};
dispatch(updateDMVisibleAside(data));
// 实时显示calling box
if (!joinedAtThisContext) {
dispatch(updateCallInfo({ from: loginUser?.uid ?? 0, to: uid, calling: false }));
}
navigateTo(`/chat/dm/${uid}`);
};
const startChat = () => { const startChat = () => {
navigateTo(`/chat/dm/${uid}`); navigateTo(`/chat/dm/${uid}`);
}; };
@@ -114,6 +135,7 @@ const useUserOperation = ({ uid, cid }: IProps) => {
canRemoveFromChannel, canRemoveFromChannel,
canCopyEmail: !!user?.email, canCopyEmail: !!user?.email,
copyEmail, copyEmail,
startCall
}; };
}; };
export default useUserOperation; export default useUserOperation;