From 5e242167feb0fdee9d1eb9f16bd910bd9ebab504 Mon Sep 17 00:00:00 2001 From: Supertiger Date: Wed, 22 Jan 2025 16:18:56 +0000 Subject: [PATCH] Add server transfer ownership modal --- src/chat-api/services/ServerService.ts | 8 ++ .../member-context-menu/MemberContextMenu.tsx | 109 ++++++++++++++++++ .../member-context-menu/styles.module.scss | 15 +++ src/components/ui/modal/Modal.tsx | 2 + src/locales/list/en-gb.json | 1 + 5 files changed, 135 insertions(+) diff --git a/src/chat-api/services/ServerService.ts b/src/chat-api/services/ServerService.ts index 2fb1d145..cf561d72 100644 --- a/src/chat-api/services/ServerService.ts +++ b/src/chat-api/services/ServerService.ts @@ -21,6 +21,14 @@ export async function getInvites(serverId: string): Promise { useToken: true, }); } +export async function transferOwnership(serverId: string, password: string, newOwnerUserId: string): Promise { + return request({ + method: "POST", + url: env.SERVER_URL + "/api" + ServiceEndpoints.server(serverId) + "/transfer-ownership", + body: { password, newOwnerUserId }, + useToken: true, + }); +} export async function updateServer( serverId: string, diff --git a/src/components/member-context-menu/MemberContextMenu.tsx b/src/components/member-context-menu/MemberContextMenu.tsx index 23fcaff3..e4f278fc 100644 --- a/src/components/member-context-menu/MemberContextMenu.tsx +++ b/src/components/member-context-menu/MemberContextMenu.tsx @@ -11,6 +11,7 @@ import Checkbox from "@/components/ui/Checkbox"; import { BanServerMember, kickServerMember, + transferOwnership, updateServerMember, updateServerMemberProfile, } from "@/chat-api/services/ServerService"; @@ -30,6 +31,9 @@ import Input from "../ui/input/Input"; import { Notice } from "../ui/Notice/Notice"; import Text from "../ui/Text"; import Avatar from "../ui/Avatar"; +import { Modal } from "../ui/modal"; +import { User } from "@/chat-api/store/useUsers"; +import { Server } from "@/chat-api/store/useServers"; type Props = Omit & { serverId?: string; userId: string; @@ -79,6 +83,12 @@ export default function MemberContextMenu(props: Props) { icon: "edit", onClick: onNicknameClick, }; + const transferOwnership = { + label: t("userContextMenu.transferOwnership"), + icon: "next_week", + onClick: onTransferOwnershipClick, + alert: true, + }; const isCurrentUserCreator = server()?.isCurrentUserCreator(); const clickedOnMyself = props.userId === account.user()?.id; @@ -110,6 +120,8 @@ export default function MemberContextMenu(props: Props) { separator, ...(member() ? [kick] : []), ban, + separator, + transferOwnership, ]; } @@ -152,6 +164,11 @@ export default function MemberContextMenu(props: Props) { )); }; + const onTransferOwnershipClick = () => { + createPortal?.((close) => ( + + )); + }; return ( <> @@ -356,6 +373,98 @@ function ServerNicknameModal(props: { ); } +function TransferOwnershipModal(props: { + member: ServerMember; + close: () => void; +}) { + const [requestSent, setRequestSent] = createSignal(false); + const [error, setError] = createSignal(null); + const [password, setPassword] = createSignal(props.member.nickname || ""); + + const store = useStore(); + + const server = () => store.servers.get(props.member.serverId!); + + const onUpdate = async () => { + if (requestSent()) return; + setRequestSent(true); + await transferOwnership( + props.member.serverId!, + password(), + props.member.userId + ) + .then(() => { + props.close(); + }) + .catch((err) => setError(err.message)) + .finally(() => { + setRequestSent(false); + }); + }; + + return ( + + + + + + + +
Server:
+ +
New Owner:
+ + + + {error()} + +
+ + + + +
+ ); +} + +function TransferOwnershipOwnerBox(props: { user?: User; server?: Server }) { + return ( +
+ + + {props.server?.name || props.user.username} + + :{props.user?.tag} + + +
+ ); +} + function BanModal(props: { user: RawUser; serverId: string; diff --git a/src/components/member-context-menu/styles.module.scss b/src/components/member-context-menu/styles.module.scss index 97a8e7a3..de121ecc 100644 --- a/src/components/member-context-menu/styles.module.scss +++ b/src/components/member-context-menu/styles.module.scss @@ -103,3 +103,18 @@ margin-left: 4px; } } + + +.transferOwnershipOwnerBox { + display: flex; + align-items: center; + gap: 12px; + background-color: rgb(22, 22, 22); + border-radius: 6px; + padding: 6px; + margin-bottom: 8px; + margin-top: 4px; + .tag { + opacity: 0.6; + } +} \ No newline at end of file diff --git a/src/components/ui/modal/Modal.tsx b/src/components/ui/modal/Modal.tsx index 944cd1c8..ef1a0376 100644 --- a/src/components/ui/modal/Modal.tsx +++ b/src/components/ui/modal/Modal.tsx @@ -25,6 +25,7 @@ interface RootProps { @default true */ closeOnEscape?: boolean; + doNotCloseOnBackgroundClick?: boolean; } const Root = (props: RootProps) => { const { isMobileWidth } = useWindowProperties(); @@ -33,6 +34,7 @@ const Root = (props: RootProps) => { let textSelected = false; const onBackgroundClick = (event: MouseEvent) => { + if (props.doNotCloseOnBackgroundClick) return; if (event.target !== event.currentTarget) return; const xDistance = Math.abs(startClick.x - event.clientX); diff --git a/src/locales/list/en-gb.json b/src/locales/list/en-gb.json index 03407980..18ff3632 100644 --- a/src/locales/list/en-gb.json +++ b/src/locales/list/en-gb.json @@ -354,6 +354,7 @@ "kick": "Kick", "ban": "Ban", "changeNickname": "Change Nickname", + "transferOwnership": "Transfer Ownership", "callVolume": "Call Volume" }, "kickServerMemberModal": {