refactor: remark
This commit is contained in:
@@ -66,6 +66,8 @@
|
||||
"file_expired": "File Expired",
|
||||
"only_owner_can_send": "only Channel owner can send message!",
|
||||
|
||||
"remark": "remark",
|
||||
"remark_placeholder": "remark this user"
|
||||
"remark": "add friend nickname",
|
||||
"remark_clear": "reset friend nickname",
|
||||
"remark_intro": "Find a friend faster with a personal nickname. It will only be visible to you in your direct messages.",
|
||||
"remark_placeholder": "Set Nickname"
|
||||
}
|
||||
|
||||
@@ -66,5 +66,7 @@
|
||||
"only_owner_can_send": "只有频道创建者才能发消息!",
|
||||
|
||||
"remark": "备注",
|
||||
"remark_clear": "重置备注",
|
||||
"remark_intro": "给联系人备注,方便日后查找,备注仅您可见",
|
||||
"remark_placeholder": "备注该用户"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import Modal from "./Modal";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import StyledButton from "./styled/Button";
|
||||
import Input from "./styled/Input";
|
||||
import { useUpdateRemarkMutation } from "../app/services/user";
|
||||
import { useAppSelector } from "../app/store";
|
||||
import { ChangeEvent, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
type Props = {
|
||||
uid: number;
|
||||
visible: boolean;
|
||||
updateVisible: (_visible: boolean) => void;
|
||||
};
|
||||
|
||||
const NicknameModal = ({ visible, updateVisible, uid }: Props) => {
|
||||
const remark = useAppSelector((store) => store.footprint.remarkMap[uid] || "");
|
||||
const [updateRemark, { isLoading }] = useUpdateRemarkMutation();
|
||||
const { t } = useTranslation("chat");
|
||||
const { t: ct } = useTranslation("common", { keyPrefix: "action" });
|
||||
const [input, setInput] = useState(remark);
|
||||
const handleChange = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
setInput(evt.target.value);
|
||||
};
|
||||
const handleOK = async () => {
|
||||
const { error } = await updateRemark({ contact_uid: uid, remark: input });
|
||||
if (!error) {
|
||||
updateVisible(false);
|
||||
} else {
|
||||
toast.error("failed");
|
||||
}
|
||||
};
|
||||
if (!visible) return null;
|
||||
return (
|
||||
<Modal>
|
||||
<div className="px-3 py-4 flex flex-col bg-white dark:bg-gray-900 rounded-lg max-h-[85vh] overflow-y-scroll md:min-w-[408px] relative">
|
||||
<h2 className="z-50 py-2 bg-white dark:bg-gray-900 flex items-center justify-between text-lg text-gray-700 dark:text-gray-50 sticky top-0">
|
||||
{t("remark")}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500">{t("remark_intro")}</p>
|
||||
<div className="py-2 flex flex-col gap-2 items-start my-4">
|
||||
{/* <label className="dark:text-white text-sm pb-2" htmlFor="nickname">
|
||||
{t("remark")}
|
||||
</label> */}
|
||||
<Input
|
||||
placeholder={t("remark_placeholder")}
|
||||
name="nickname"
|
||||
value={input}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<button onClick={setInput.bind(null, "")} className="text-primary-500 text-sm">
|
||||
{t("remark_clear")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 justify-end w-full">
|
||||
<StyledButton disabled={isLoading} onClick={handleOK} className="small">
|
||||
{ct("yes")}
|
||||
</StyledButton>
|
||||
<StyledButton onClick={updateVisible.bind(null, false)} className="small ghost">
|
||||
{ct("cancel")}
|
||||
</StyledButton>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default NicknameModal;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC, memo } from "react";
|
||||
import { FC, memo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import Tippy from "@tippyjs/react";
|
||||
@@ -14,6 +14,7 @@ import Avatar from "../Avatar";
|
||||
import ContextMenu, { Item } from "../ContextMenu";
|
||||
import { shallowEqual } from "react-redux";
|
||||
import Remark from "./remark";
|
||||
import NicknameModal from "../NicknameModal";
|
||||
|
||||
interface Props {
|
||||
uid: number;
|
||||
@@ -22,8 +23,10 @@ interface Props {
|
||||
}
|
||||
|
||||
const Profile: FC<Props> = ({ uid, type = "embed", cid }) => {
|
||||
const [remarkVisible, setRemarkVisible] = useState(false);
|
||||
const { data: agoraEnabled } = useGetAgoraStatusQuery();
|
||||
const { t } = useTranslation("member");
|
||||
const { t: chatTrans } = useTranslation("chat");
|
||||
const { t: ct } = useTranslation();
|
||||
const {
|
||||
canDM,
|
||||
@@ -57,97 +60,107 @@ const Profile: FC<Props> = ({ uid, type = "embed", cid }) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={containerClass}>
|
||||
<Avatar
|
||||
width={80}
|
||||
height={80}
|
||||
className="rounded-full w-20 h-20 object-cover"
|
||||
src={avatar}
|
||||
name={name}
|
||||
/>
|
||||
<Remark uid={uid} />
|
||||
<h2 className="text-lg select-text font-bold text-gray-900 dark:text-white">
|
||||
{name} {canDM && <span className="font-normal text-gray-500">#{uid}</span>}
|
||||
</h2>
|
||||
{canCopyEmail && (
|
||||
<span className="text-sm text-gray-400 dark:text-gray-200 select-text">{email}</span>
|
||||
)}
|
||||
{/* <p className="intro">{introduction}</p> */}
|
||||
{canDM && (
|
||||
<ul
|
||||
className={clsx("mt-6 flex flex-col md:flex-row items-center gap-2", isCard && "pb-0.5")}
|
||||
>
|
||||
<NavLink to={`/chat/dm/${uid}`}>
|
||||
<li className={`${iconClass} icon chat`}>
|
||||
<IconMessage />
|
||||
<span>{t("send_msg")}</span>
|
||||
</li>
|
||||
</NavLink>
|
||||
{agoraEnabled && type == "embed" && (
|
||||
<li role="button" onClick={startCall} className={`${iconClass} icon chat`}>
|
||||
<IconCall className="fill-primary-400" />
|
||||
<span>{t("call")}</span>
|
||||
</li>
|
||||
)}
|
||||
<Tippy
|
||||
disabled={!hasMore}
|
||||
interactive
|
||||
popperOptions={{ strategy: "fixed" }}
|
||||
placement="right"
|
||||
trigger="click"
|
||||
hideOnClick={true}
|
||||
content={
|
||||
<ContextMenu
|
||||
items={
|
||||
[
|
||||
agoraEnabled &&
|
||||
type == "card" && {
|
||||
title: t("call"),
|
||||
handler: startCall,
|
||||
},
|
||||
canCopyEmail && {
|
||||
title: t("copy_email"),
|
||||
handler: copyEmail,
|
||||
},
|
||||
canUpdateRole && {
|
||||
title: t("roles"),
|
||||
handler: updateRole,
|
||||
subs: [
|
||||
{
|
||||
title: t("set_normal"),
|
||||
checked: !isAdmin,
|
||||
handler: updateRole,
|
||||
},
|
||||
{
|
||||
title: t("set_admin"),
|
||||
checked: isAdmin,
|
||||
handler: updateRole,
|
||||
},
|
||||
],
|
||||
},
|
||||
canRemoveFromChannel && {
|
||||
title: t("remove_from_channel"),
|
||||
danger: true,
|
||||
handler: removeFromChannel,
|
||||
},
|
||||
canRemoveFromServer && {
|
||||
title: t("remove"),
|
||||
handler: removeUser,
|
||||
danger: true,
|
||||
},
|
||||
].filter(Boolean) as Item[]
|
||||
}
|
||||
/>
|
||||
}
|
||||
<>
|
||||
<NicknameModal uid={uid} visible={remarkVisible} updateVisible={setRemarkVisible} />
|
||||
<div className={containerClass}>
|
||||
<Avatar
|
||||
width={80}
|
||||
height={80}
|
||||
className="rounded-full w-20 h-20 object-cover"
|
||||
src={avatar}
|
||||
name={name}
|
||||
/>
|
||||
<Remark uid={uid} />
|
||||
<h2 className="text-lg select-text font-bold text-gray-900 dark:text-white">
|
||||
{name} {canDM && <span className="font-normal text-gray-500">#{uid}</span>}
|
||||
</h2>
|
||||
{canCopyEmail && (
|
||||
<span className="text-sm text-gray-400 dark:text-gray-200 select-text">{email}</span>
|
||||
)}
|
||||
{/* <p className="intro">{introduction}</p> */}
|
||||
{canDM && (
|
||||
<ul
|
||||
className={clsx(
|
||||
"mt-6 flex flex-col md:flex-row items-center gap-2",
|
||||
isCard && "pb-0.5"
|
||||
)}
|
||||
>
|
||||
<li className={`${iconClass} icon ${hasMore ? "" : "text-gray-500"}`}>
|
||||
<IconMore className={hasMore ? "fill-primary-500" : ""} />
|
||||
<span>{ct("more")}</span>
|
||||
</li>
|
||||
</Tippy>
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
<NavLink to={`/chat/dm/${uid}`}>
|
||||
<li className={`${iconClass} icon chat`}>
|
||||
<IconMessage />
|
||||
<span>{t("send_msg")}</span>
|
||||
</li>
|
||||
</NavLink>
|
||||
{agoraEnabled && type == "embed" && (
|
||||
<li role="button" onClick={startCall} className={`${iconClass} icon chat`}>
|
||||
<IconCall className="fill-primary-400" />
|
||||
<span>{t("call")}</span>
|
||||
</li>
|
||||
)}
|
||||
<Tippy
|
||||
disabled={!hasMore}
|
||||
interactive
|
||||
popperOptions={{ strategy: "fixed" }}
|
||||
placement="right"
|
||||
trigger="click"
|
||||
hideOnClick={true}
|
||||
content={
|
||||
<ContextMenu
|
||||
items={
|
||||
[
|
||||
{
|
||||
title: chatTrans("remark"),
|
||||
handler: setRemarkVisible.bind(null, true),
|
||||
},
|
||||
agoraEnabled &&
|
||||
type == "card" && {
|
||||
title: t("call"),
|
||||
handler: startCall,
|
||||
},
|
||||
canCopyEmail && {
|
||||
title: t("copy_email"),
|
||||
handler: copyEmail,
|
||||
},
|
||||
canUpdateRole && {
|
||||
title: t("roles"),
|
||||
handler: updateRole,
|
||||
subs: [
|
||||
{
|
||||
title: t("set_normal"),
|
||||
checked: !isAdmin,
|
||||
handler: updateRole,
|
||||
},
|
||||
{
|
||||
title: t("set_admin"),
|
||||
checked: isAdmin,
|
||||
handler: updateRole,
|
||||
},
|
||||
],
|
||||
},
|
||||
canRemoveFromChannel && {
|
||||
title: t("remove_from_channel"),
|
||||
danger: true,
|
||||
handler: removeFromChannel,
|
||||
},
|
||||
canRemoveFromServer && {
|
||||
title: t("remove"),
|
||||
handler: removeUser,
|
||||
danger: true,
|
||||
},
|
||||
].filter(Boolean) as Item[]
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<li className={`${iconClass} icon ${hasMore ? "" : "text-gray-500"}`}>
|
||||
<IconMore className={hasMore ? "fill-primary-500" : ""} />
|
||||
<span>{ct("more")}</span>
|
||||
</li>
|
||||
</Tippy>
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import React, { ChangeEvent, useState } from "react";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
import Input from "../styled/Input";
|
||||
import IconEdit from "@/assets/icons/edit.svg";
|
||||
import StyledButton from "../styled/Button";
|
||||
import { useUpdateRemarkMutation } from "../../app/services/user";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ServerVersionChecker from "../ServerVersionChecker";
|
||||
|
||||
type Props = {
|
||||
@@ -12,50 +6,13 @@ type Props = {
|
||||
};
|
||||
|
||||
const Remark = ({ uid }: Props) => {
|
||||
const { t } = useTranslation("chat");
|
||||
const { t: ct } = useTranslation("common", { keyPrefix: "action" });
|
||||
const [updateRemark, { isLoading }] = useUpdateRemarkMutation();
|
||||
const [editMode, setEditMode] = useState(false);
|
||||
const remark = useAppSelector((store) => store.footprint.remarkMap[uid] || "");
|
||||
const [input, setInput] = useState(remark);
|
||||
const handleChange = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
setInput(evt.target.value);
|
||||
};
|
||||
const handleOK = async () => {
|
||||
const { error } = await updateRemark({ contact_uid: uid, remark: input });
|
||||
if (!error) {
|
||||
setEditMode(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<ServerVersionChecker empty version="0.5.0">
|
||||
<div className="flex items-center gap-1 text-white py-2">
|
||||
{editMode ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
<Input
|
||||
onChange={handleChange}
|
||||
value={input}
|
||||
placeholder={t("remark_placeholder")}
|
||||
className="small"
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<StyledButton disabled={isLoading} onClick={handleOK} className="small">
|
||||
{ct("yes")}
|
||||
</StyledButton>
|
||||
<StyledButton onClick={setEditMode.bind(null, false)} className="small ghost">
|
||||
{ct("cancel")}
|
||||
</StyledButton>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-1">
|
||||
<span>{remark}</span>
|
||||
<button className="flex items-center gap-1" onClick={setEditMode.bind(null, true)}>
|
||||
{!remark && t("remark")}
|
||||
<IconEdit />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-1">
|
||||
<span>{remark}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ServerVersionChecker>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { FC, ReactElement } from "react";
|
||||
import { FC, ReactElement, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Tippy from "@tippyjs/react";
|
||||
|
||||
import useUserOperation from "@/hooks/useUserOperation";
|
||||
import ContextMenu, { Item } from "../ContextMenu";
|
||||
import NicknameModal from "../NicknameModal";
|
||||
|
||||
interface Props {
|
||||
enable?: boolean;
|
||||
@@ -15,6 +16,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const UserContextMenu: FC<Props> = ({ enable = false, uid, cid, visible, hide, children }) => {
|
||||
const [remarkVisible, setRemarkVisible] = useState(false);
|
||||
const { t } = useTranslation("member");
|
||||
const { t: chatTran } = useTranslation("chat");
|
||||
const {
|
||||
@@ -31,77 +33,84 @@ const UserContextMenu: FC<Props> = ({ enable = false, uid, cid, visible, hide, c
|
||||
removeUser,
|
||||
isAdmin,
|
||||
canUpdateRole,
|
||||
updateRole
|
||||
updateRole,
|
||||
} = useUserOperation({
|
||||
uid,
|
||||
cid
|
||||
cid,
|
||||
});
|
||||
return (
|
||||
<Tippy
|
||||
disabled={!enable}
|
||||
visible={visible}
|
||||
followCursor={"initial"}
|
||||
interactive
|
||||
placement="right-start"
|
||||
popperOptions={{ strategy: "fixed" }}
|
||||
onClickOutside={hide}
|
||||
key={uid}
|
||||
content={
|
||||
<ContextMenu
|
||||
hideMenu={hide}
|
||||
items={
|
||||
[
|
||||
{
|
||||
title: t("send_msg"),
|
||||
handler: startChat
|
||||
},
|
||||
canCopyEmail && {
|
||||
title: t("copy_email"),
|
||||
handler: copyEmail
|
||||
},
|
||||
canUpdateRole && {
|
||||
title: t("roles"),
|
||||
handler: updateRole,
|
||||
subs: [
|
||||
{
|
||||
title: t("set_normal"),
|
||||
checked: !isAdmin,
|
||||
handler: updateRole
|
||||
},
|
||||
{
|
||||
title: t("set_admin"),
|
||||
checked: isAdmin,
|
||||
handler: updateRole
|
||||
}
|
||||
]
|
||||
},
|
||||
canRemoveFromChannel && {
|
||||
danger: true,
|
||||
title: t("remove_from_channel"),
|
||||
handler: removeFromChannel
|
||||
},
|
||||
canRemoveFromContact && {
|
||||
danger: true,
|
||||
title: t("remove_from_contact"),
|
||||
handler: removeFromContact
|
||||
},
|
||||
canBlock && {
|
||||
danger: true,
|
||||
title: chatTran("block"),
|
||||
handler: blockThisContact
|
||||
},
|
||||
canRemove && {
|
||||
danger: true,
|
||||
title: t("remove"),
|
||||
handler: removeUser
|
||||
}
|
||||
].filter(Boolean) as Item[]
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Tippy>
|
||||
<>
|
||||
<NicknameModal uid={uid} visible={remarkVisible} updateVisible={setRemarkVisible} />
|
||||
<Tippy
|
||||
disabled={!enable}
|
||||
visible={visible}
|
||||
followCursor={"initial"}
|
||||
interactive
|
||||
placement="right-start"
|
||||
popperOptions={{ strategy: "fixed" }}
|
||||
onClickOutside={hide}
|
||||
key={uid}
|
||||
content={
|
||||
<ContextMenu
|
||||
hideMenu={hide}
|
||||
items={
|
||||
[
|
||||
{
|
||||
title: t("send_msg"),
|
||||
handler: startChat,
|
||||
},
|
||||
{
|
||||
title: chatTran("remark"),
|
||||
handler: setRemarkVisible.bind(null, true),
|
||||
},
|
||||
canCopyEmail && {
|
||||
title: t("copy_email"),
|
||||
handler: copyEmail,
|
||||
},
|
||||
canUpdateRole && {
|
||||
title: t("roles"),
|
||||
handler: updateRole,
|
||||
subs: [
|
||||
{
|
||||
title: t("set_normal"),
|
||||
checked: !isAdmin,
|
||||
handler: updateRole,
|
||||
},
|
||||
{
|
||||
title: t("set_admin"),
|
||||
checked: isAdmin,
|
||||
handler: updateRole,
|
||||
},
|
||||
],
|
||||
},
|
||||
canRemoveFromChannel && {
|
||||
danger: true,
|
||||
title: t("remove_from_channel"),
|
||||
handler: removeFromChannel,
|
||||
},
|
||||
canRemoveFromContact && {
|
||||
danger: true,
|
||||
title: t("remove_from_contact"),
|
||||
handler: removeFromContact,
|
||||
},
|
||||
canBlock && {
|
||||
danger: true,
|
||||
title: chatTran("block"),
|
||||
handler: blockThisContact,
|
||||
},
|
||||
canRemove && {
|
||||
danger: true,
|
||||
title: t("remove"),
|
||||
handler: removeUser,
|
||||
},
|
||||
].filter(Boolean) as Item[]
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Tippy>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC, ReactElement } from "react";
|
||||
import { FC, ReactElement, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { shallowEqual, useDispatch } from "react-redux";
|
||||
import { useLocation, useMatch, useNavigate } from "react-router-dom";
|
||||
@@ -8,13 +8,15 @@ import { useReadMessageMutation } from "@/app/services/message";
|
||||
import {
|
||||
usePinChatMutation,
|
||||
useUnpinChatMutation,
|
||||
useUpdateMuteSettingMutation
|
||||
useUpdateMuteSettingMutation,
|
||||
} from "@/app/services/user";
|
||||
import { removeUserSession } from "@/app/slices/message.user";
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import { ChatContext } from "@/types/common";
|
||||
import ContextMenu, { Item } from "@/components/ContextMenu";
|
||||
import useUserOperation from "@/hooks/useUserOperation";
|
||||
import Modal from "../../../components/Modal";
|
||||
import NicknameModal from "../../../components/NicknameModal";
|
||||
|
||||
type Props = {
|
||||
context: ChatContext;
|
||||
@@ -36,12 +38,14 @@ const SessionContextMenu: FC<Props> = ({
|
||||
hide,
|
||||
deleteChannel,
|
||||
setInviteChannelId,
|
||||
children
|
||||
children,
|
||||
}) => {
|
||||
const [remarkVisible, setRemarkVisible] = useState(false);
|
||||
const { t: tChat } = useTranslation("chat");
|
||||
const { t } = useTranslation();
|
||||
const { canCopyEmail, copyEmail, canDeleteChannel, canInviteChannel } = useUserOperation({
|
||||
uid: context == "dm" ? id : undefined,
|
||||
cid: context == "channel" ? id : undefined
|
||||
cid: context == "channel" ? id : undefined,
|
||||
});
|
||||
const [muteChannel] = useUpdateMuteSettingMutation();
|
||||
const [pinChat] = usePinChatMutation();
|
||||
@@ -97,67 +101,74 @@ const SessionContextMenu: FC<Props> = ({
|
||||
? [
|
||||
{
|
||||
title: pinTxt,
|
||||
handler: handlePinChat
|
||||
handler: handlePinChat,
|
||||
},
|
||||
{
|
||||
title: t("action.mark_read"),
|
||||
handler: handleReadAll
|
||||
handler: handleReadAll,
|
||||
},
|
||||
{
|
||||
title: tChat("remark"),
|
||||
handler: setRemarkVisible.bind(null, true),
|
||||
},
|
||||
{
|
||||
title: t("setting"),
|
||||
handler: handleDMSetting
|
||||
handler: handleDMSetting,
|
||||
},
|
||||
canCopyEmail && {
|
||||
title: t("action.copy_email"),
|
||||
handler: copyEmail
|
||||
handler: copyEmail,
|
||||
},
|
||||
{
|
||||
title: t("action.hide_session"),
|
||||
danger: true,
|
||||
handler: handleRemoveSession
|
||||
}
|
||||
handler: handleRemoveSession,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
title: pinTxt,
|
||||
handler: handlePinChat
|
||||
handler: handlePinChat,
|
||||
},
|
||||
{
|
||||
title: t("setting"),
|
||||
underline: true,
|
||||
handler: handleChannelSetting
|
||||
handler: handleChannelSetting,
|
||||
},
|
||||
{
|
||||
title: t("action.mark_read"),
|
||||
// underline: true
|
||||
handler: handleReadAll
|
||||
handler: handleReadAll,
|
||||
},
|
||||
{
|
||||
title: channelMuted ? t("action.unmute") : t("action.mute"),
|
||||
handler: handleChannelMute
|
||||
handler: handleChannelMute,
|
||||
},
|
||||
canInviteChannel && {
|
||||
title: t("action.invite_people"),
|
||||
handler: setInviteChannelId.bind(null, id)
|
||||
handler: setInviteChannelId.bind(null, id),
|
||||
},
|
||||
canDeleteChannel && {
|
||||
title: t("action.delete_channel"),
|
||||
danger: true,
|
||||
handler: deleteChannel.bind(null, id)
|
||||
}
|
||||
handler: deleteChannel.bind(null, id),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<Tippy
|
||||
interactive
|
||||
placement="right-start"
|
||||
popperOptions={{ strategy: "fixed" }}
|
||||
followCursor={"initial"}
|
||||
visible={visible}
|
||||
onClickOutside={hide}
|
||||
content={<ContextMenu hideMenu={hide} items={items.filter(Boolean) as Item[]} />}
|
||||
>
|
||||
{children}
|
||||
</Tippy>
|
||||
<>
|
||||
<NicknameModal uid={id} visible={remarkVisible} updateVisible={setRemarkVisible} />
|
||||
<Tippy
|
||||
interactive
|
||||
placement="right-start"
|
||||
popperOptions={{ strategy: "fixed" }}
|
||||
followCursor={"initial"}
|
||||
visible={visible}
|
||||
onClickOutside={hide}
|
||||
content={<ContextMenu hideMenu={hide} items={items.filter(Boolean) as Item[]} />}
|
||||
>
|
||||
{children}
|
||||
</Tippy>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default SessionContextMenu;
|
||||
|
||||
Reference in New Issue
Block a user