diff --git a/package.json b/package.json index 3f6fda66..5cf1afb3 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "@hcaptcha/react-hcaptcha": "^0.3.6", "@insertish/vite-plugin-babel-macros": "^1.0.5", "@preact/preset-vite": "^2.0.0", - "@revoltchat/ui": "1.0.61", + "@revoltchat/ui": "1.0.63", "@rollup/plugin-replace": "^2.4.2", "@styled-icons/boxicons-logos": "^10.38.0", "@styled-icons/boxicons-regular": "^10.38.0", diff --git a/src/components/common/messaging/bars/MessageOverlayBar.tsx b/src/components/common/messaging/bars/MessageOverlayBar.tsx index bc05e834..9104593f 100644 --- a/src/components/common/messaging/bars/MessageOverlayBar.tsx +++ b/src/components/common/messaging/bars/MessageOverlayBar.tsx @@ -25,6 +25,7 @@ import { } from "../../../../context/intermediate/Intermediate"; import { useClient } from "../../../../controllers/client/ClientController"; +import { modalController } from "../../../../controllers/modals/ModalController"; import Tooltip from "../../../common/Tooltip"; interface Props { @@ -136,11 +137,10 @@ export const MessageOverlayBar = observer(({ message, queued }: Props) => { onClick={(e) => e.shiftKey ? message.delete() - : openScreen({ - id: "special_prompt", + : modalController.push({ type: "delete_message", target: message, - } as unknown as Screen) + }) }> diff --git a/src/components/navigation/items/ButtonItem.tsx b/src/components/navigation/items/ButtonItem.tsx index adcd1442..903d3775 100644 --- a/src/components/navigation/items/ButtonItem.tsx +++ b/src/components/navigation/items/ButtonItem.tsx @@ -15,6 +15,7 @@ import { stopPropagation } from "../../../lib/stopPropagation"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; +import { modalController } from "../../../controllers/modals/ModalController"; import ChannelIcon from "../../common/ChannelIcon"; import Tooltip from "../../common/Tooltip"; import UserIcon from "../../common/user/UserIcon"; @@ -111,8 +112,7 @@ export const UserButton = observer((props: UserProps) => { className={styles.icon} onClick={(e) => stopPropagation(e) && - openScreen({ - id: "special_prompt", + modalController.push({ type: "close_dm", target: channel, }) @@ -195,8 +195,7 @@ export const ChannelButton = observer((props: ChannelProps) => { - openScreen({ - id: "special_prompt", + modalController.push({ type: "leave_group", target: channel, }) diff --git a/src/context/intermediate/modals/Prompt.tsx b/src/context/intermediate/modals/Prompt.tsx index fc7478fb..a6add7aa 100644 --- a/src/context/intermediate/modals/Prompt.tsx +++ b/src/context/intermediate/modals/Prompt.tsx @@ -58,19 +58,25 @@ export function PromptModal({ type SpecialProps = { onClose: () => void } & ( | { type: "leave_group"; target: Channel } | { type: "close_dm"; target: Channel } - | { type: "leave_server"; target: Server } - | { type: "delete_server"; target: Server } | { type: "delete_channel"; target: Channel } - | { type: "delete_bot"; target: string; name: string; cb?: () => void } - | { type: "delete_message"; target: MessageI } | { type: "create_invite"; target: Channel; } + + | { type: "leave_server"; target: Server } + | { type: "delete_server"; target: Server } + + | { type: "delete_bot"; target: string; name: string; cb?: () => void } + + | { type: "delete_message"; target: MessageI } + | { type: "kick_member"; target: Server; user: User } | { type: "ban_member"; target: Server; user: User } + | { type: "unfriend_user"; target: User } | { type: "block_user"; target: User } + | { type: "create_channel"; target: Server; @@ -80,6 +86,7 @@ type SpecialProps = { onClose: () => void } & ( }, ) => void; } + | { type: "create_category"; target: Server } ); diff --git a/src/controllers/modals/ModalController.tsx b/src/controllers/modals/ModalController.tsx index 340e1021..9e67b2d2 100644 --- a/src/controllers/modals/ModalController.tsx +++ b/src/controllers/modals/ModalController.tsx @@ -20,10 +20,12 @@ import AddFriend from "./components/AddFriend"; import Changelog from "./components/Changelog"; import ChannelInfo from "./components/ChannelInfo"; import Clipboard from "./components/Clipboard"; +import Confirmation from "./components/Confirmation"; import CreateGroup from "./components/CreateGroup"; import CreateRole from "./components/CreateRole"; import CreateServer from "./components/CreateServer"; import CustomStatus from "./components/CustomStatus"; +import DeleteMessage from "./components/DeleteMessage"; import Error from "./components/Error"; import ImageViewer from "./components/ImageViewer"; import LinkWarning from "./components/LinkWarning"; @@ -231,11 +233,20 @@ export const modalController = new ModalControllerExtended({ changelog: Changelog, channel_info: ChannelInfo, clipboard: Clipboard, + leave_group: Confirmation, + close_dm: Confirmation, + leave_server: Confirmation, + delete_server: Confirmation, + delete_channel: Confirmation, + delete_bot: Confirmation, + block_user: Confirmation, + unfriend_user: Confirmation, create_group: CreateGroup, create_role: CreateRole, create_server: CreateServer, create_bot: CreateBotModal, custom_status: CustomStatus, + delete_message: DeleteMessage, error: Error, image_viewer: ImageViewer, link_warning: LinkWarning, diff --git a/src/controllers/modals/components/Confirmation.tsx b/src/controllers/modals/components/Confirmation.tsx new file mode 100644 index 00000000..aba1c294 --- /dev/null +++ b/src/controllers/modals/components/Confirmation.tsx @@ -0,0 +1,101 @@ +import { useHistory } from "react-router-dom"; + +import { Text } from "preact-i18n"; + +import { ModalForm } from "@revoltchat/ui"; + +import { TextReact } from "../../../lib/i18n"; + +import { clientController } from "../../client/ClientController"; +import { ModalProps } from "../types"; + +/** + * Confirmation modal + */ +export default function Confirmation( + props: ModalProps< + | "leave_group" + | "close_dm" + | "leave_server" + | "delete_server" + | "delete_channel" + | "delete_bot" + | "block_user" + | "unfriend_user" + >, +) { + const history = useHistory(); + + const EVENTS = { + close_dm: ["confirm_close_dm", "close"], + delete_server: ["confirm_delete", "delete"], + delete_channel: ["confirm_delete", "delete"], + delete_bot: ["confirm_delete", "delete"], + leave_group: ["confirm_leave", "leave"], + leave_server: ["confirm_leave", "leave"], + unfriend_user: ["unfriend_user", "remove"], + block_user: ["block_user", "block"], + }; + + const event = EVENTS[props.type]; + let name; + switch (props.type) { + case "unfriend_user": + case "block_user": + name = props.target.username; + break; + case "close_dm": + name = props.target.recipient?.username; + break; + case "delete_bot": + name = props.name; + break; + default: + name = props.target.name; + } + + return ( + + } + description={ + {name} }} + /> + } + data={{}} + schema={{}} + callback={async () => { + switch (props.type) { + case "unfriend_user": + await props.target.removeFriend(); + break; + case "block_user": + await props.target.blockUser(); + break; + case "leave_group": + case "close_dm": + case "delete_channel": + case "leave_server": + case "delete_server": + if (props.type != "delete_channel") history.push("/"); + + props.target.delete(); + break; + case "delete_bot": + clientController + .getAvailableClient() + .bots.delete(props.target); + props.cb?.(); + break; + } + }} + /> + ); +} diff --git a/src/controllers/modals/components/DeleteMessage.tsx b/src/controllers/modals/components/DeleteMessage.tsx new file mode 100644 index 00000000..6dbf95c4 --- /dev/null +++ b/src/controllers/modals/components/DeleteMessage.tsx @@ -0,0 +1,35 @@ +import { Text } from "preact-i18n"; + +import { ModalForm } from "@revoltchat/ui"; + +import Message from "../../../components/common/messaging/Message"; +import { ModalProps } from "../types"; + +/** + * Delete message modal + */ +export default function DeleteMessage({ + target, + ...props +}: ModalProps<"delete_message">) { + return ( + } + description={ + + } + schema={{ + message: "custom", + }} + data={{ + message: { + element: , + }, + }} + callback={() => target.delete()} + /> + ); +} diff --git a/src/controllers/modals/types.ts b/src/controllers/modals/types.ts index 5ffc5fef..37b5e513 100644 --- a/src/controllers/modals/types.ts +++ b/src/controllers/modals/types.ts @@ -1,4 +1,4 @@ -import { API, Client, User, Member, Channel, Server } from "revolt.js"; +import { API, Client, User, Member, Channel, Server, Message } from "revolt.js"; export type Modal = { key?: string; @@ -117,6 +117,69 @@ export type Modal = { server: Server; callback: (id: string) => void; } + | { + type: "leave_group"; + target: Channel; + } + | { + type: "close_dm"; + target: Channel; + } + | { + type: "delete_channel"; + target: Channel; + } + | { + type: "create_invite"; + target: Channel; + } + | { + type: "leave_server"; + target: Server; + } + | { + type: "delete_server"; + target: Server; + } + | { + type: "delete_bot"; + target: string; + name: string; + cb?: () => void; + } + | { + type: "delete_message"; + target: Message; + } + | { + type: "kick_member"; + member: Member; + } + | { + type: "ban_member"; + member: Member; + } + | { + type: "unfriend_user"; + target: User; + } + | { + type: "block_user"; + target: User; + } + | { + type: "create_channel"; + target: Server; + cb?: ( + channel: Channel & { + channel_type: "TextChannel" | "VoiceChannel"; + }, + ) => void; + } + | { + type: "create_category"; + target: Server; + } ); export type ModalProps = Modal & { type: T } & { diff --git a/src/lib/ContextMenus.tsx b/src/lib/ContextMenus.tsx index 67fc3018..1fea137d 100644 --- a/src/lib/ContextMenus.tsx +++ b/src/lib/ContextMenus.tsx @@ -71,8 +71,8 @@ type Action = | { action: "open_link"; link: string } | { action: "copy_link"; link: string } | { action: "remove_member"; channel: Channel; user: User } - | { action: "kick_member"; target: Server; user: User } - | { action: "ban_member"; target: Server; user: User } + | { action: "kick_member"; target: Member } + | { action: "ban_member"; target: Member } | { action: "view_profile"; user: User } | { action: "message_user"; user: User } | { action: "block_user"; user: User } @@ -336,8 +336,7 @@ export default function ContextMenus() { break; case "block_user": - openScreen({ - id: "special_prompt", + modalController.push({ type: "block_user", target: data.user, }); @@ -346,8 +345,7 @@ export default function ContextMenus() { await data.user.unblockUser(); break; case "remove_friend": - openScreen({ - id: "special_prompt", + modalController.push({ type: "unfriend_user", target: data.user, }); @@ -374,26 +372,34 @@ export default function ContextMenus() { break; case "clear_status": - { - await client.users.edit({ remove: ["StatusText"] }); - } + await client.users.edit({ remove: ["StatusText"] }); + break; + + case "delete_message": + modalController.push({ + type: "delete_message", + target: data.target, + }); break; case "leave_group": case "close_dm": - case "leave_server": case "delete_channel": - case "delete_server": - case "delete_message": - case "create_channel": - case "create_category": case "create_invite": - // Typescript flattens the case types into a single type and type structure and specifity is lost - openScreen({ - id: "special_prompt", + modalController.push({ type: data.action, target: data.target, - } as unknown as Screen); + }); + break; + + case "leave_server": + case "delete_server": + case "create_channel": + case "create_category": + modalController.push({ + type: data.action, + target: data.target, + }); break; case "edit_identity": @@ -405,11 +411,9 @@ export default function ContextMenus() { case "ban_member": case "kick_member": - openScreen({ - id: "special_prompt", + modalController.push({ type: data.action, - target: data.target, - user: data.user, + member: data.target, }); break; @@ -669,8 +673,10 @@ export default function ContextMenus() { generateAction( { action: "kick_member", - target: server, - user: user!, + target: client.members.getKey({ + server: server._id, + user: user!._id, + })!, }, undefined, // this is needed because generateAction uses positional, not named parameters undefined, @@ -682,8 +688,10 @@ export default function ContextMenus() { generateAction( { action: "ban_member", - target: server, - user: user!, + target: client.members.getKey({ + server: server._id, + user: user!._id, + })!, }, undefined, undefined, diff --git a/src/pages/channels/messaging/MessageEditor.tsx b/src/pages/channels/messaging/MessageEditor.tsx index 8e64361a..cf5ed489 100644 --- a/src/pages/channels/messaging/MessageEditor.tsx +++ b/src/pages/channels/messaging/MessageEditor.tsx @@ -14,6 +14,7 @@ import { import AutoComplete, { useAutoComplete, } from "../../../components/common/AutoComplete"; +import { modalController } from "../../../controllers/modals/ModalController"; const EditorBase = styled.div` display: flex; @@ -50,14 +51,12 @@ interface Props { export default function MessageEditor({ message, finish }: Props) { const [content, setContent] = useState(message.content ?? ""); const { focusTaken } = useContext(IntermediateContext); - const { openScreen } = useIntermediate(); async function save() { finish(); if (content.length === 0) { - openScreen({ - id: "special_prompt", + modalController.push({ type: "delete_message", target: message, }); diff --git a/src/pages/channels/voice/VoiceHeader.tsx b/src/pages/channels/voice/VoiceHeader.tsx index 242e1041..89ef577c 100644 --- a/src/pages/channels/voice/VoiceHeader.tsx +++ b/src/pages/channels/voice/VoiceHeader.tsx @@ -21,6 +21,7 @@ import { useIntermediate } from "../../../context/intermediate/Intermediate"; import Tooltip from "../../../components/common/Tooltip"; import UserIcon from "../../../components/common/user/UserIcon"; import { useClient } from "../../../controllers/client/ClientController"; +import { modalController } from "../../../controllers/modals/ModalController"; interface Props { id: string; @@ -100,26 +101,27 @@ export default observer(({ id }: Props) => {
{users && users.length !== 0 ? users.map((user, index) => { - const id = keys![index]; + const user_id = keys![index]; return ( -
+
- openScreen({ - id: "profile", - user_id: id, + modalController.push({ + type: "user_profile", + user_id, }) } /> diff --git a/src/pages/friends/Friend.tsx b/src/pages/friends/Friend.tsx index f93d3531..7e3c1ef1 100644 --- a/src/pages/friends/Friend.tsx +++ b/src/pages/friends/Friend.tsx @@ -102,8 +102,7 @@ export const Friend = observer(({ user }: Props) => { stopPropagation( ev, user.relationship === "Friend" - ? openScreen({ - id: "special_prompt", + ? modalController.push({ type: "unfriend_user", target: user, }) diff --git a/src/pages/settings/ServerSettings.tsx b/src/pages/settings/ServerSettings.tsx index babd2755..72f2925f 100644 --- a/src/pages/settings/ServerSettings.tsx +++ b/src/pages/settings/ServerSettings.tsx @@ -20,6 +20,7 @@ import { useIntermediate } from "../../context/intermediate/Intermediate"; import ButtonItem from "../../components/navigation/items/ButtonItem"; import { useClient } from "../../controllers/client/ClientController"; import RequiresOnline from "../../controllers/client/jsx/RequiresOnline"; +import { modalController } from "../../controllers/modals/ModalController"; import { GenericSettings } from "./GenericSettings"; import { Bans } from "./server/Bans"; import { Categories } from "./server/Categories"; @@ -132,8 +133,7 @@ export default observer(() => { - openScreen({ - id: "special_prompt", + modalController.push({ type: "delete_server", target: server, }) diff --git a/src/pages/settings/panes/MyBots.tsx b/src/pages/settings/panes/MyBots.tsx index 4f3e26fd..158ca952 100644 --- a/src/pages/settings/panes/MyBots.tsx +++ b/src/pages/settings/panes/MyBots.tsx @@ -218,8 +218,8 @@ function BotCard({ bot, onDelete, onUpdate }: Props) { target={user} size={42} onClick={() => - openScreen({ - id: "profile", + modalController.push({ + type: "user_profile", user_id: user._id, }) } @@ -460,8 +460,7 @@ function BotCard({ bot, onDelete, onUpdate }: Props) { palette="error" onClick={async () => { setSaving(true); - openScreen({ - id: "special_prompt", + modalController.push({ type: "delete_bot", target: bot._id, name: user.username, @@ -508,16 +507,14 @@ export const MyBots = observer(() => { // eslint-disable-next-line }, []); - const { openScreen } = useIntermediate(); - return (
} onClick={() => - openScreen({ - id: "create_bot", + modalController.push({ + type: "create_bot", onCreate: (bot) => setBots([...(bots ?? []), bot]), }) } diff --git a/src/pages/settings/server/Categories.tsx b/src/pages/settings/server/Categories.tsx index 9af11841..cf3bb5c0 100644 --- a/src/pages/settings/server/Categories.tsx +++ b/src/pages/settings/server/Categories.tsx @@ -17,6 +17,7 @@ import { noop } from "../../../lib/js"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; import ChannelIcon from "../../../components/common/ChannelIcon"; +import { modalController } from "../../../controllers/modals/ModalController"; const KanbanEntry = styled.div` padding: 2px 4px; @@ -449,8 +450,7 @@ function ListElement({ - openScreen({ - id: "special_prompt", + modalController.push({ type: "create_channel", target: server, cb: addChannel, diff --git a/yarn.lock b/yarn.lock index b674b8fd..621d8370 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2231,9 +2231,9 @@ __metadata: languageName: node linkType: hard -"@revoltchat/ui@npm:1.0.61": - version: 1.0.61 - resolution: "@revoltchat/ui@npm:1.0.61" +"@revoltchat/ui@npm:1.0.63": + version: 1.0.63 + resolution: "@revoltchat/ui@npm:1.0.63" dependencies: "@styled-icons/boxicons-logos": ^10.38.0 "@styled-icons/boxicons-regular": ^10.38.0 @@ -2247,7 +2247,7 @@ __metadata: react-virtuoso: ^2.12.0 peerDependencies: revolt.js: "*" - checksum: 678584f84cb3d43307507058eb434507cb1d201455f9da6618eb1731c6439240ddc16601dc636b5790a83393884770bf4e8fee75d235871f65ba5abcd8f685ac + checksum: 4e785798d31b503bb7777da7661656cbbeb17734257aa4578357e6acf2d053abeb40e406f1e04c7b1338822260d5614ffb8262cb64ae95ce3b1c7edda0c2125b languageName: node linkType: hard @@ -3554,7 +3554,7 @@ __metadata: "@hcaptcha/react-hcaptcha": ^0.3.6 "@insertish/vite-plugin-babel-macros": ^1.0.5 "@preact/preset-vite": ^2.0.0 - "@revoltchat/ui": 1.0.61 + "@revoltchat/ui": 1.0.63 "@rollup/plugin-replace": ^2.4.2 "@styled-icons/boxicons-logos": ^10.38.0 "@styled-icons/boxicons-regular": ^10.38.0