feat: add call entry into profile
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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": "注销账号",
|
||||||
|
|||||||
@@ -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")}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user