1
0
mirror of https://github.com/spacebarchat/client.git synced 2024-11-25 11:42:30 +01:00

guild action menu

This commit is contained in:
Puyodead1 2023-12-19 11:29:51 -05:00
parent 52f7f63ba9
commit ba31016686
No known key found for this signature in database
GPG Key ID: BA5F91AAEF68E5CE
8 changed files with 141 additions and 25 deletions

View File

@ -44,18 +44,6 @@ export default styled.button<Props>`
return "45px";
}
}};
width: ${(props) => {
if (props.grow) return "auto";
switch (props.size) {
default:
case "small":
return "96px";
case "medium":
return "96px";
case "large":
return "130px";
}
}};
min-width: ${(props) => {
if (props.grow) return "auto";
switch (props.size) {

View File

@ -1,9 +1,11 @@
import { observer } from "mobx-react-lite";
import React from "react";
import React, { useEffect } from "react";
import styled from "styled-components";
import { useAppStore } from "../stores/AppStore";
import Icon, { IconProps } from "./Icon";
import { SectionHeader } from "./SectionHeader";
import Floating from "./floating/Floating";
import FloatingTrigger from "./floating/FloatingTrigger";
const Wrapper = styled(SectionHeader)`
background-color: var(--background-secondary);
@ -26,13 +28,17 @@ const HeaderText = styled.header`
function ChannelHeader() {
const app = useAppStore();
const [isOpen, setOpen] = React.useState(false);
const [icon, setIcon] = React.useState<IconProps["icon"]>("mdiChevronDown");
function openMenu(e: React.MouseEvent<HTMLDivElement>) {
e.stopPropagation();
const onOpenChange = (open: boolean) => {
setOpen(open);
};
setIcon("mdiClose");
}
useEffect(() => {
if (isOpen) setIcon("mdiChevronDown");
else setIcon("mdiClose");
}, [isOpen]);
if (app.activeGuildId === "@me") {
return (
@ -52,10 +58,14 @@ function ChannelHeader() {
if (!app.activeGuild) return null;
return (
<Wrapper onClick={openMenu}>
<HeaderText>{app.activeGuild.name}</HeaderText>
<Icon icon={icon} size="20px" color="var(--text)" />
</Wrapper>
<Floating type="guild" open={isOpen} onOpenChange={onOpenChange} props={{ guild: app.activeGuild! }}>
<FloatingTrigger>
<Wrapper>
<HeaderText>{app.activeGuild.name}</HeaderText>
<Icon icon={icon} size="20px" color="var(--text)" />
</Wrapper>
</FloatingTrigger>
</Floating>
);
}

View File

@ -2,9 +2,11 @@ import { FloatingArrow, FloatingPortal, Placement } from "@floating-ui/react";
import { motion } from "framer-motion";
import { FloatingContext } from "../../contexts/FloatingContext";
import useFloating from "../../hooks/useFloating";
import Guild from "../../stores/objects/Guild";
import GuildMember from "../../stores/objects/GuildMember";
import User from "../../stores/objects/User";
import Tooltip from "../Tooltip";
import GuildMenuPopout from "./GuildMenuPopout";
import UserProfilePopout from "./UserProfilePopout";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -13,6 +15,7 @@ type Components = Record<string, React.FC<any>>;
const components: Components = {
userPopout: UserProfilePopout,
tooltip: Tooltip,
guild: GuildMenuPopout,
};
export type FloatingOptions = {
@ -36,6 +39,12 @@ export type FloatingOptions = {
aria?: string;
};
}
| {
type: "guild";
props: {
guild: Guild;
};
}
);
export type FloatingProps<T extends FloatingOptions["type"]> = (FloatingOptions & {

View File

@ -0,0 +1,53 @@
import styled from "styled-components";
import useLogger from "../../hooks/useLogger";
import { modalController } from "../../controllers/modals";
import { useAppStore } from "../../stores/AppStore";
import { ContextMenu, ContextMenuButton, ContextMenuDivider } from "../contextMenus/ContextMenu";
const CustomContextMenu = styled(ContextMenu)`
width: 200px;
`;
function GuildMenuPopout() {
const { activeGuild } = useAppStore();
const logger = useLogger("GuildMenuPopout");
if (!activeGuild) {
logger.error("activeGuild is undefined");
return null;
}
function leaveGuild() {
modalController.push({
type: "leave_server",
target: activeGuild!,
});
}
return (
<CustomContextMenu>
<ContextMenuButton icon="mdiCog" disabled>
Server Settings
</ContextMenuButton>
<ContextMenuButton icon="mdiPlusCircle" disabled>
Create Channel
</ContextMenuButton>
<ContextMenuButton icon="mdiFolderPlus" disabled>
Create Channel
</ContextMenuButton>
<ContextMenuDivider />
<ContextMenuButton icon="mdiBell" disabled>
Notification Settings
</ContextMenuButton>
<ContextMenuButton icon="mdiShieldLock" disabled>
Privacy Settings
</ContextMenuButton>
<ContextMenuDivider />
<ContextMenuButton icon="mdiLocationExit" destructive onClick={leaveGuild}>
Leave Guild
</ContextMenuButton>
</CustomContextMenu>
);
}
export default GuildMenuPopout;

View File

@ -1,3 +1,53 @@
export function LeaveServerModal() {
return null;
import { Routes } from "@spacebarchat/spacebar-api-types/v9";
import { useState } from "react";
import { ModalProps, modalController } from "../../controllers/modals";
import { useAppStore } from "../../stores/AppStore";
import { Modal } from "./ModalComponents";
export function LeaveServerModal({ target, ...props }: ModalProps<"leave_server">) {
const app = useAppStore();
const [isDisabled, setDisabled] = useState(false);
async function leaveGuild() {
setDisabled(true);
await app.rest
.delete(Routes.guildMember(target.id, "@me"))
.then(() => {
modalController.pop("close");
})
.catch((e) => {
console.error(e);
})
.finally(() => setDisabled(false));
}
return (
<Modal
{...props}
title={`Leave '${target.name}'`}
description={
<span>
Are you sure you want to leave <b>{target.name}</b>? You won't be able to rejoin this guild unless
you are re-invited.
</span>
}
actions={[
{
onClick: leaveGuild,
children: <span>Leave Server</span>,
palette: "danger",
confirmation: true,
disabled: isDisabled,
size: "small",
},
{
onClick: () => modalController.pop("close"),
children: <span>Cancel</span>,
palette: "link",
disabled: isDisabled,
size: "small",
},
]}
/>
);
}

View File

@ -13,7 +13,7 @@ export type ModalAction = Omit<React.HTMLAttributes<HTMLButtonElement>, "as"> &
};
interface ModalProps {
children: React.ReactNode;
children?: React.ReactNode;
onClose?: (force: boolean) => void;
signal?: "close" | "confirm" | "cancel";
title?: string;

View File

@ -10,6 +10,7 @@ import {
ErrorModal,
JoinServerModal,
KickMemberModal,
LeaveServerModal,
} from "../../components/modals";
import { Modal } from "./types";
@ -144,7 +145,7 @@ export const modalController = new ModalControllerExtended({
// clipboard: Clipboard,
// leave_group: ConfirmLeave,
// close_dm: Confirmation,
// leave_server: ConfirmLeave,
leave_server: LeaveServerModal,
// delete_server: Confirmation,
// delete_channel: Confirmation,
// delete_bot: Confirmation,

View File

@ -1,6 +1,7 @@
// adapted from https://github.com/revoltchat/revite/blob/master/src/controllers/modals/types.ts
import Channel from "../../stores/objects/Channel";
import Guild from "../../stores/objects/Guild";
import GuildMember from "../../stores/objects/GuildMember";
import Message from "../../stores/objects/Message";
@ -37,6 +38,10 @@ export type Modal = {
type: "delete_message";
target: Message;
}
| {
type: "leave_server";
target: Guild;
}
);
export type ModalProps<T extends Modal["type"]> = Modal & { type: T } & {