feat: port DeleteMessage and Confirmation

This commit is contained in:
Paul Makles 2022-07-05 18:46:13 +01:00
parent 79c90c1b00
commit 160d71684f
16 changed files with 290 additions and 69 deletions

View file

@ -73,7 +73,7 @@
"@hcaptcha/react-hcaptcha": "^0.3.6", "@hcaptcha/react-hcaptcha": "^0.3.6",
"@insertish/vite-plugin-babel-macros": "^1.0.5", "@insertish/vite-plugin-babel-macros": "^1.0.5",
"@preact/preset-vite": "^2.0.0", "@preact/preset-vite": "^2.0.0",
"@revoltchat/ui": "1.0.61", "@revoltchat/ui": "1.0.63",
"@rollup/plugin-replace": "^2.4.2", "@rollup/plugin-replace": "^2.4.2",
"@styled-icons/boxicons-logos": "^10.38.0", "@styled-icons/boxicons-logos": "^10.38.0",
"@styled-icons/boxicons-regular": "^10.38.0", "@styled-icons/boxicons-regular": "^10.38.0",

View file

@ -25,6 +25,7 @@ import {
} from "../../../../context/intermediate/Intermediate"; } from "../../../../context/intermediate/Intermediate";
import { useClient } from "../../../../controllers/client/ClientController"; import { useClient } from "../../../../controllers/client/ClientController";
import { modalController } from "../../../../controllers/modals/ModalController";
import Tooltip from "../../../common/Tooltip"; import Tooltip from "../../../common/Tooltip";
interface Props { interface Props {
@ -136,11 +137,10 @@ export const MessageOverlayBar = observer(({ message, queued }: Props) => {
onClick={(e) => onClick={(e) =>
e.shiftKey e.shiftKey
? message.delete() ? message.delete()
: openScreen({ : modalController.push({
id: "special_prompt",
type: "delete_message", type: "delete_message",
target: message, target: message,
} as unknown as Screen) })
}> }>
<Trash size={18} color={"var(--error)"} /> <Trash size={18} color={"var(--error)"} />
</Entry> </Entry>

View file

@ -15,6 +15,7 @@ import { stopPropagation } from "../../../lib/stopPropagation";
import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { modalController } from "../../../controllers/modals/ModalController";
import ChannelIcon from "../../common/ChannelIcon"; import ChannelIcon from "../../common/ChannelIcon";
import Tooltip from "../../common/Tooltip"; import Tooltip from "../../common/Tooltip";
import UserIcon from "../../common/user/UserIcon"; import UserIcon from "../../common/user/UserIcon";
@ -111,8 +112,7 @@ export const UserButton = observer((props: UserProps) => {
className={styles.icon} className={styles.icon}
onClick={(e) => onClick={(e) =>
stopPropagation(e) && stopPropagation(e) &&
openScreen({ modalController.push({
id: "special_prompt",
type: "close_dm", type: "close_dm",
target: channel, target: channel,
}) })
@ -195,8 +195,7 @@ export const ChannelButton = observer((props: ChannelProps) => {
<IconButton <IconButton
className={styles.icon} className={styles.icon}
onClick={() => onClick={() =>
openScreen({ modalController.push({
id: "special_prompt",
type: "leave_group", type: "leave_group",
target: channel, target: channel,
}) })

View file

@ -58,19 +58,25 @@ export function PromptModal({
type SpecialProps = { onClose: () => void } & ( type SpecialProps = { onClose: () => void } & (
| { type: "leave_group"; target: Channel } | { type: "leave_group"; target: Channel }
| { type: "close_dm"; target: Channel } | { type: "close_dm"; target: Channel }
| { type: "leave_server"; target: Server }
| { type: "delete_server"; target: Server }
| { type: "delete_channel"; target: Channel } | { type: "delete_channel"; target: Channel }
| { type: "delete_bot"; target: string; name: string; cb?: () => void }
| { type: "delete_message"; target: MessageI }
| { | {
type: "create_invite"; type: "create_invite";
target: Channel; 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: "kick_member"; target: Server; user: User }
| { type: "ban_member"; target: Server; user: User } | { type: "ban_member"; target: Server; user: User }
| { type: "unfriend_user"; target: User } | { type: "unfriend_user"; target: User }
| { type: "block_user"; target: User } | { type: "block_user"; target: User }
| { | {
type: "create_channel"; type: "create_channel";
target: Server; target: Server;
@ -80,6 +86,7 @@ type SpecialProps = { onClose: () => void } & (
}, },
) => void; ) => void;
} }
| { type: "create_category"; target: Server } | { type: "create_category"; target: Server }
); );

View file

@ -20,10 +20,12 @@ import AddFriend from "./components/AddFriend";
import Changelog from "./components/Changelog"; import Changelog from "./components/Changelog";
import ChannelInfo from "./components/ChannelInfo"; import ChannelInfo from "./components/ChannelInfo";
import Clipboard from "./components/Clipboard"; import Clipboard from "./components/Clipboard";
import Confirmation from "./components/Confirmation";
import CreateGroup from "./components/CreateGroup"; import CreateGroup from "./components/CreateGroup";
import CreateRole from "./components/CreateRole"; import CreateRole from "./components/CreateRole";
import CreateServer from "./components/CreateServer"; import CreateServer from "./components/CreateServer";
import CustomStatus from "./components/CustomStatus"; import CustomStatus from "./components/CustomStatus";
import DeleteMessage from "./components/DeleteMessage";
import Error from "./components/Error"; import Error from "./components/Error";
import ImageViewer from "./components/ImageViewer"; import ImageViewer from "./components/ImageViewer";
import LinkWarning from "./components/LinkWarning"; import LinkWarning from "./components/LinkWarning";
@ -231,11 +233,20 @@ export const modalController = new ModalControllerExtended({
changelog: Changelog, changelog: Changelog,
channel_info: ChannelInfo, channel_info: ChannelInfo,
clipboard: Clipboard, 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_group: CreateGroup,
create_role: CreateRole, create_role: CreateRole,
create_server: CreateServer, create_server: CreateServer,
create_bot: CreateBotModal, create_bot: CreateBotModal,
custom_status: CustomStatus, custom_status: CustomStatus,
delete_message: DeleteMessage,
error: Error, error: Error,
image_viewer: ImageViewer, image_viewer: ImageViewer,
link_warning: LinkWarning, link_warning: LinkWarning,

View file

@ -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 (
<ModalForm
{...props}
title={
<Text
id={`app.special.modals.prompt.${event[0]}`}
fields={{ name }}
/>
}
description={
<TextReact
id={`app.special.modals.prompt.${event[0]}_long`}
fields={{ name: <b>{name}</b> }}
/>
}
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;
}
}}
/>
);
}

View file

@ -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 (
<ModalForm
{...props}
title={<Text id={"app.context_menu.delete_message"} />}
description={
<Text
id={`app.special.modals.prompt.confirm_delete_message_long`}
/>
}
schema={{
message: "custom",
}}
data={{
message: {
element: <Message message={target} head={true} contrast />,
},
}}
callback={() => target.delete()}
/>
);
}

View file

@ -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 = { export type Modal = {
key?: string; key?: string;
@ -117,6 +117,69 @@ export type Modal = {
server: Server; server: Server;
callback: (id: string) => void; 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<T extends Modal["type"]> = Modal & { type: T } & { export type ModalProps<T extends Modal["type"]> = Modal & { type: T } & {

View file

@ -71,8 +71,8 @@ type Action =
| { action: "open_link"; link: string } | { action: "open_link"; link: string }
| { action: "copy_link"; link: string } | { action: "copy_link"; link: string }
| { action: "remove_member"; channel: Channel; user: User } | { action: "remove_member"; channel: Channel; user: User }
| { action: "kick_member"; target: Server; user: User } | { action: "kick_member"; target: Member }
| { action: "ban_member"; target: Server; user: User } | { action: "ban_member"; target: Member }
| { action: "view_profile"; user: User } | { action: "view_profile"; user: User }
| { action: "message_user"; user: User } | { action: "message_user"; user: User }
| { action: "block_user"; user: User } | { action: "block_user"; user: User }
@ -336,8 +336,7 @@ export default function ContextMenus() {
break; break;
case "block_user": case "block_user":
openScreen({ modalController.push({
id: "special_prompt",
type: "block_user", type: "block_user",
target: data.user, target: data.user,
}); });
@ -346,8 +345,7 @@ export default function ContextMenus() {
await data.user.unblockUser(); await data.user.unblockUser();
break; break;
case "remove_friend": case "remove_friend":
openScreen({ modalController.push({
id: "special_prompt",
type: "unfriend_user", type: "unfriend_user",
target: data.user, target: data.user,
}); });
@ -374,26 +372,34 @@ export default function ContextMenus() {
break; break;
case "clear_status": 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; break;
case "leave_group": case "leave_group":
case "close_dm": case "close_dm":
case "leave_server":
case "delete_channel": case "delete_channel":
case "delete_server":
case "delete_message":
case "create_channel":
case "create_category":
case "create_invite": case "create_invite":
// Typescript flattens the case types into a single type and type structure and specifity is lost modalController.push({
openScreen({
id: "special_prompt",
type: data.action, type: data.action,
target: data.target, 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; break;
case "edit_identity": case "edit_identity":
@ -405,11 +411,9 @@ export default function ContextMenus() {
case "ban_member": case "ban_member":
case "kick_member": case "kick_member":
openScreen({ modalController.push({
id: "special_prompt",
type: data.action, type: data.action,
target: data.target, member: data.target,
user: data.user,
}); });
break; break;
@ -669,8 +673,10 @@ export default function ContextMenus() {
generateAction( generateAction(
{ {
action: "kick_member", action: "kick_member",
target: server, target: client.members.getKey({
user: user!, server: server._id,
user: user!._id,
})!,
}, },
undefined, // this is needed because generateAction uses positional, not named parameters undefined, // this is needed because generateAction uses positional, not named parameters
undefined, undefined,
@ -682,8 +688,10 @@ export default function ContextMenus() {
generateAction( generateAction(
{ {
action: "ban_member", action: "ban_member",
target: server, target: client.members.getKey({
user: user!, server: server._id,
user: user!._id,
})!,
}, },
undefined, undefined,
undefined, undefined,

View file

@ -14,6 +14,7 @@ import {
import AutoComplete, { import AutoComplete, {
useAutoComplete, useAutoComplete,
} from "../../../components/common/AutoComplete"; } from "../../../components/common/AutoComplete";
import { modalController } from "../../../controllers/modals/ModalController";
const EditorBase = styled.div` const EditorBase = styled.div`
display: flex; display: flex;
@ -50,14 +51,12 @@ interface Props {
export default function MessageEditor({ message, finish }: Props) { export default function MessageEditor({ message, finish }: Props) {
const [content, setContent] = useState(message.content ?? ""); const [content, setContent] = useState(message.content ?? "");
const { focusTaken } = useContext(IntermediateContext); const { focusTaken } = useContext(IntermediateContext);
const { openScreen } = useIntermediate();
async function save() { async function save() {
finish(); finish();
if (content.length === 0) { if (content.length === 0) {
openScreen({ modalController.push({
id: "special_prompt",
type: "delete_message", type: "delete_message",
target: message, target: message,
}); });

View file

@ -21,6 +21,7 @@ import { useIntermediate } from "../../../context/intermediate/Intermediate";
import Tooltip from "../../../components/common/Tooltip"; import Tooltip from "../../../components/common/Tooltip";
import UserIcon from "../../../components/common/user/UserIcon"; import UserIcon from "../../../components/common/user/UserIcon";
import { useClient } from "../../../controllers/client/ClientController"; import { useClient } from "../../../controllers/client/ClientController";
import { modalController } from "../../../controllers/modals/ModalController";
interface Props { interface Props {
id: string; id: string;
@ -100,26 +101,27 @@ export default observer(({ id }: Props) => {
<div className="participants"> <div className="participants">
{users && users.length !== 0 {users && users.length !== 0
? users.map((user, index) => { ? users.map((user, index) => {
const id = keys![index]; const user_id = keys![index];
return ( return (
<div key={id}> <div key={user_id}>
<UserIcon <UserIcon
size={80} size={80}
target={user} target={user}
status={false} status={false}
voice={ voice={
client.user?._id === id && client.user?._id === user_id &&
voiceState.isDeaf() voiceState.isDeaf()
? "deaf" ? "deaf"
: voiceState.participants!.get(id) : voiceState.participants!.get(
?.audio user_id,
)?.audio
? undefined ? undefined
: "muted" : "muted"
} }
onClick={() => onClick={() =>
openScreen({ modalController.push({
id: "profile", type: "user_profile",
user_id: id, user_id,
}) })
} }
/> />

View file

@ -102,8 +102,7 @@ export const Friend = observer(({ user }: Props) => {
stopPropagation( stopPropagation(
ev, ev,
user.relationship === "Friend" user.relationship === "Friend"
? openScreen({ ? modalController.push({
id: "special_prompt",
type: "unfriend_user", type: "unfriend_user",
target: user, target: user,
}) })

View file

@ -20,6 +20,7 @@ import { useIntermediate } from "../../context/intermediate/Intermediate";
import ButtonItem from "../../components/navigation/items/ButtonItem"; import ButtonItem from "../../components/navigation/items/ButtonItem";
import { useClient } from "../../controllers/client/ClientController"; import { useClient } from "../../controllers/client/ClientController";
import RequiresOnline from "../../controllers/client/jsx/RequiresOnline"; import RequiresOnline from "../../controllers/client/jsx/RequiresOnline";
import { modalController } from "../../controllers/modals/ModalController";
import { GenericSettings } from "./GenericSettings"; import { GenericSettings } from "./GenericSettings";
import { Bans } from "./server/Bans"; import { Bans } from "./server/Bans";
import { Categories } from "./server/Categories"; import { Categories } from "./server/Categories";
@ -132,8 +133,7 @@ export default observer(() => {
<LineDivider /> <LineDivider />
<ButtonItem <ButtonItem
onClick={() => onClick={() =>
openScreen({ modalController.push({
id: "special_prompt",
type: "delete_server", type: "delete_server",
target: server, target: server,
}) })

View file

@ -218,8 +218,8 @@ function BotCard({ bot, onDelete, onUpdate }: Props) {
target={user} target={user}
size={42} size={42}
onClick={() => onClick={() =>
openScreen({ modalController.push({
id: "profile", type: "user_profile",
user_id: user._id, user_id: user._id,
}) })
} }
@ -460,8 +460,7 @@ function BotCard({ bot, onDelete, onUpdate }: Props) {
palette="error" palette="error"
onClick={async () => { onClick={async () => {
setSaving(true); setSaving(true);
openScreen({ modalController.push({
id: "special_prompt",
type: "delete_bot", type: "delete_bot",
target: bot._id, target: bot._id,
name: user.username, name: user.username,
@ -508,16 +507,14 @@ export const MyBots = observer(() => {
// eslint-disable-next-line // eslint-disable-next-line
}, []); }, []);
const { openScreen } = useIntermediate();
return ( return (
<div className={styles.myBots}> <div className={styles.myBots}>
<CategoryButton <CategoryButton
account account
icon={<Plus size={24} />} icon={<Plus size={24} />}
onClick={() => onClick={() =>
openScreen({ modalController.push({
id: "create_bot", type: "create_bot",
onCreate: (bot) => setBots([...(bots ?? []), bot]), onCreate: (bot) => setBots([...(bots ?? []), bot]),
}) })
} }

View file

@ -17,6 +17,7 @@ import { noop } from "../../../lib/js";
import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { useIntermediate } from "../../../context/intermediate/Intermediate";
import ChannelIcon from "../../../components/common/ChannelIcon"; import ChannelIcon from "../../../components/common/ChannelIcon";
import { modalController } from "../../../controllers/modals/ModalController";
const KanbanEntry = styled.div` const KanbanEntry = styled.div`
padding: 2px 4px; padding: 2px 4px;
@ -449,8 +450,7 @@ function ListElement({
</Droppable> </Droppable>
<KanbanListHeader <KanbanListHeader
onClick={() => onClick={() =>
openScreen({ modalController.push({
id: "special_prompt",
type: "create_channel", type: "create_channel",
target: server, target: server,
cb: addChannel, cb: addChannel,

View file

@ -2231,9 +2231,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@revoltchat/ui@npm:1.0.61": "@revoltchat/ui@npm:1.0.63":
version: 1.0.61 version: 1.0.63
resolution: "@revoltchat/ui@npm:1.0.61" resolution: "@revoltchat/ui@npm:1.0.63"
dependencies: dependencies:
"@styled-icons/boxicons-logos": ^10.38.0 "@styled-icons/boxicons-logos": ^10.38.0
"@styled-icons/boxicons-regular": ^10.38.0 "@styled-icons/boxicons-regular": ^10.38.0
@ -2247,7 +2247,7 @@ __metadata:
react-virtuoso: ^2.12.0 react-virtuoso: ^2.12.0
peerDependencies: peerDependencies:
revolt.js: "*" revolt.js: "*"
checksum: 678584f84cb3d43307507058eb434507cb1d201455f9da6618eb1731c6439240ddc16601dc636b5790a83393884770bf4e8fee75d235871f65ba5abcd8f685ac checksum: 4e785798d31b503bb7777da7661656cbbeb17734257aa4578357e6acf2d053abeb40e406f1e04c7b1338822260d5614ffb8262cb64ae95ce3b1c7edda0c2125b
languageName: node languageName: node
linkType: hard linkType: hard
@ -3554,7 +3554,7 @@ __metadata:
"@hcaptcha/react-hcaptcha": ^0.3.6 "@hcaptcha/react-hcaptcha": ^0.3.6
"@insertish/vite-plugin-babel-macros": ^1.0.5 "@insertish/vite-plugin-babel-macros": ^1.0.5
"@preact/preset-vite": ^2.0.0 "@preact/preset-vite": ^2.0.0
"@revoltchat/ui": 1.0.61 "@revoltchat/ui": 1.0.63
"@rollup/plugin-replace": ^2.4.2 "@rollup/plugin-replace": ^2.4.2
"@styled-icons/boxicons-logos": ^10.38.0 "@styled-icons/boxicons-logos": ^10.38.0
"@styled-icons/boxicons-regular": ^10.38.0 "@styled-icons/boxicons-regular": ^10.38.0