2021-07-29 15:11:21 +01:00
|
|
|
import { observer } from "mobx-react-lite";
|
2021-07-05 11:23:23 +01:00
|
|
|
import { useHistory } from "react-router-dom";
|
|
|
|
import { Channels, Servers, Users } from "revolt.js/dist/api/objects";
|
2021-06-23 13:52:16 +01:00
|
|
|
import { ulid } from "ulid";
|
2021-07-05 11:23:23 +01:00
|
|
|
|
|
|
|
import styles from "./Prompt.module.scss";
|
2021-06-19 18:46:05 +01:00
|
|
|
import { Text } from "preact-i18n";
|
2021-07-05 11:23:23 +01:00
|
|
|
import { useContext, useEffect, useState } from "preact/hooks";
|
|
|
|
|
|
|
|
import { TextReact } from "../../../lib/i18n";
|
|
|
|
|
2021-07-29 15:11:21 +01:00
|
|
|
import { User } from "../../../mobx";
|
|
|
|
import { useData } from "../../../mobx/State";
|
|
|
|
|
2021-07-05 11:23:23 +01:00
|
|
|
import Message from "../../../components/common/messaging/Message";
|
|
|
|
import UserIcon from "../../../components/common/user/UserIcon";
|
2021-06-19 18:46:05 +01:00
|
|
|
import InputBox from "../../../components/ui/InputBox";
|
2021-07-05 11:23:23 +01:00
|
|
|
import Modal, { Action } from "../../../components/ui/Modal";
|
2021-06-19 18:46:05 +01:00
|
|
|
import Overline from "../../../components/ui/Overline";
|
2021-07-05 11:23:23 +01:00
|
|
|
import Radio from "../../../components/ui/Radio";
|
|
|
|
|
|
|
|
import { Children } from "../../../types/Preact";
|
2021-06-20 20:30:42 +01:00
|
|
|
import { AppContext } from "../../revoltjs/RevoltClient";
|
|
|
|
import { mapMessage, takeError } from "../../revoltjs/util";
|
2021-07-05 11:23:23 +01:00
|
|
|
import { useIntermediate } from "../Intermediate";
|
2021-06-19 18:46:05 +01:00
|
|
|
|
|
|
|
interface Props {
|
2021-07-05 11:25:20 +01:00
|
|
|
onClose: () => void;
|
|
|
|
question: Children;
|
|
|
|
content?: Children;
|
|
|
|
disabled?: boolean;
|
|
|
|
actions: Action[];
|
|
|
|
error?: string;
|
2021-06-19 18:46:05 +01:00
|
|
|
}
|
|
|
|
|
2021-07-05 11:23:23 +01:00
|
|
|
export function PromptModal({
|
2021-07-05 11:25:20 +01:00
|
|
|
onClose,
|
|
|
|
question,
|
|
|
|
content,
|
|
|
|
actions,
|
|
|
|
disabled,
|
|
|
|
error,
|
2021-07-05 11:23:23 +01:00
|
|
|
}: Props) {
|
2021-07-05 11:25:20 +01:00
|
|
|
return (
|
|
|
|
<Modal
|
|
|
|
visible={true}
|
|
|
|
title={question}
|
|
|
|
actions={actions}
|
|
|
|
onClose={onClose}
|
|
|
|
disabled={disabled}>
|
|
|
|
{error && <Overline error={error} type="error" />}
|
|
|
|
{content}
|
|
|
|
</Modal>
|
|
|
|
);
|
2021-06-19 18:46:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type SpecialProps = { onClose: () => void } & (
|
2021-07-05 11:25:20 +01:00
|
|
|
| { type: "leave_group"; target: Channels.GroupChannel }
|
|
|
|
| { type: "close_dm"; target: Channels.DirectMessageChannel }
|
|
|
|
| { type: "leave_server"; target: Servers.Server }
|
|
|
|
| { type: "delete_server"; target: Servers.Server }
|
|
|
|
| { type: "delete_channel"; target: Channels.TextChannel }
|
|
|
|
| { type: "delete_message"; target: Channels.Message }
|
|
|
|
| {
|
|
|
|
type: "create_invite";
|
|
|
|
target: Channels.TextChannel | Channels.GroupChannel;
|
|
|
|
}
|
2021-07-29 15:11:21 +01:00
|
|
|
| { type: "kick_member"; target: Servers.Server; user: User }
|
|
|
|
| { type: "ban_member"; target: Servers.Server; user: User }
|
|
|
|
| { type: "unfriend_user"; target: User }
|
|
|
|
| { type: "block_user"; target: User }
|
2021-07-05 11:25:20 +01:00
|
|
|
| { type: "create_channel"; target: Servers.Server }
|
2021-07-05 11:23:23 +01:00
|
|
|
);
|
2021-06-19 18:46:05 +01:00
|
|
|
|
2021-07-29 15:11:21 +01:00
|
|
|
export const SpecialPromptModal = observer((props: SpecialProps) => {
|
2021-07-05 11:25:20 +01:00
|
|
|
const client = useContext(AppContext);
|
|
|
|
const [processing, setProcessing] = useState(false);
|
|
|
|
const [error, setError] = useState<undefined | string>(undefined);
|
2021-07-05 11:23:23 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
const { onClose } = props;
|
|
|
|
switch (props.type) {
|
|
|
|
case "leave_group":
|
|
|
|
case "close_dm":
|
|
|
|
case "leave_server":
|
|
|
|
case "delete_server":
|
|
|
|
case "delete_channel":
|
|
|
|
case "unfriend_user":
|
|
|
|
case "block_user": {
|
|
|
|
const EVENTS = {
|
|
|
|
close_dm: ["confirm_close_dm", "close"],
|
|
|
|
delete_server: ["confirm_delete", "delete"],
|
|
|
|
delete_channel: ["confirm_delete", "delete"],
|
|
|
|
leave_group: ["confirm_leave", "leave"],
|
|
|
|
leave_server: ["confirm_leave", "leave"],
|
|
|
|
unfriend_user: ["unfriend_user", "remove"],
|
|
|
|
block_user: ["block_user", "block"],
|
|
|
|
};
|
2021-07-05 11:23:23 +01:00
|
|
|
|
2021-07-10 15:57:29 +01:00
|
|
|
const event = EVENTS[props.type];
|
2021-07-05 11:25:20 +01:00
|
|
|
let name;
|
|
|
|
switch (props.type) {
|
|
|
|
case "unfriend_user":
|
|
|
|
case "block_user":
|
|
|
|
name = props.target.username;
|
|
|
|
break;
|
|
|
|
case "close_dm":
|
|
|
|
name = client.users.get(
|
|
|
|
client.channels.getRecipient(props.target._id),
|
|
|
|
)?.username;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
name = props.target.name;
|
|
|
|
}
|
2021-07-05 11:23:23 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
return (
|
|
|
|
<PromptModal
|
|
|
|
onClose={onClose}
|
|
|
|
question={
|
|
|
|
<Text
|
|
|
|
id={`app.special.modals.prompt.${event[0]}`}
|
|
|
|
fields={{ name }}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
actions={[
|
|
|
|
{
|
|
|
|
confirmation: true,
|
|
|
|
contrast: true,
|
|
|
|
error: true,
|
2021-07-06 11:34:36 +01:00
|
|
|
children: (
|
2021-07-05 11:25:20 +01:00
|
|
|
<Text
|
|
|
|
id={`app.special.modals.actions.${event[1]}`}
|
|
|
|
/>
|
|
|
|
),
|
|
|
|
onClick: async () => {
|
|
|
|
setProcessing(true);
|
2021-06-19 18:46:05 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
try {
|
|
|
|
switch (props.type) {
|
|
|
|
case "unfriend_user":
|
|
|
|
await client.users.removeFriend(
|
|
|
|
props.target._id,
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case "block_user":
|
|
|
|
await client.users.blockUser(
|
|
|
|
props.target._id,
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case "leave_group":
|
|
|
|
case "close_dm":
|
|
|
|
case "delete_channel":
|
|
|
|
await client.channels.delete(
|
|
|
|
props.target._id,
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case "leave_server":
|
|
|
|
case "delete_server":
|
|
|
|
await client.servers.delete(
|
|
|
|
props.target._id,
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
2021-06-19 18:46:05 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
onClose();
|
|
|
|
} catch (err) {
|
|
|
|
setError(takeError(err));
|
|
|
|
setProcessing(false);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2021-07-06 11:34:36 +01:00
|
|
|
children: (
|
2021-07-05 11:25:20 +01:00
|
|
|
<Text id="app.special.modals.actions.cancel" />
|
|
|
|
),
|
|
|
|
onClick: onClose,
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
content={
|
|
|
|
<TextReact
|
|
|
|
id={`app.special.modals.prompt.${event[0]}_long`}
|
|
|
|
fields={{ name: <b>{name}</b> }}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
disabled={processing}
|
|
|
|
error={error}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
case "delete_message": {
|
|
|
|
return (
|
|
|
|
<PromptModal
|
|
|
|
onClose={onClose}
|
|
|
|
question={<Text id={"app.context_menu.delete_message"} />}
|
|
|
|
actions={[
|
|
|
|
{
|
|
|
|
confirmation: true,
|
|
|
|
contrast: true,
|
|
|
|
error: true,
|
2021-07-06 11:34:36 +01:00
|
|
|
children: (
|
2021-07-05 11:25:20 +01:00
|
|
|
<Text id="app.special.modals.actions.delete" />
|
|
|
|
),
|
|
|
|
onClick: async () => {
|
|
|
|
setProcessing(true);
|
2021-06-19 18:46:05 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
try {
|
|
|
|
await client.channels.deleteMessage(
|
|
|
|
props.target.channel,
|
|
|
|
props.target._id,
|
|
|
|
);
|
2021-06-19 18:46:05 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
onClose();
|
|
|
|
} catch (err) {
|
|
|
|
setError(takeError(err));
|
|
|
|
setProcessing(false);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2021-07-06 11:34:36 +01:00
|
|
|
children: (
|
2021-07-05 11:25:20 +01:00
|
|
|
<Text id="app.special.modals.actions.cancel" />
|
|
|
|
),
|
|
|
|
onClick: onClose,
|
2021-07-06 11:34:36 +01:00
|
|
|
plain: true,
|
2021-07-05 11:25:20 +01:00
|
|
|
},
|
|
|
|
]}
|
|
|
|
content={
|
|
|
|
<>
|
|
|
|
<Text
|
|
|
|
id={`app.special.modals.prompt.confirm_delete_message_long`}
|
|
|
|
/>
|
|
|
|
<Message
|
|
|
|
message={mapMessage(props.target)}
|
|
|
|
head={true}
|
|
|
|
contrast
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
}
|
|
|
|
disabled={processing}
|
|
|
|
error={error}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
case "create_invite": {
|
|
|
|
const [code, setCode] = useState("abcdef");
|
|
|
|
const { writeClipboard } = useIntermediate();
|
2021-06-19 18:46:05 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
useEffect(() => {
|
|
|
|
setProcessing(true);
|
2021-06-20 17:31:53 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
client.channels
|
|
|
|
.createInvite(props.target._id)
|
|
|
|
.then((code) => setCode(code))
|
|
|
|
.catch((err) => setError(takeError(err)))
|
|
|
|
.finally(() => setProcessing(false));
|
|
|
|
}, []);
|
2021-06-20 17:31:53 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
return (
|
|
|
|
<PromptModal
|
|
|
|
onClose={onClose}
|
|
|
|
question={<Text id={`app.context_menu.create_invite`} />}
|
|
|
|
actions={[
|
|
|
|
{
|
2021-07-06 19:29:27 +01:00
|
|
|
children: (
|
|
|
|
<Text id="app.special.modals.actions.ok" />
|
|
|
|
),
|
2021-07-05 11:25:20 +01:00
|
|
|
confirmation: true,
|
|
|
|
onClick: onClose,
|
|
|
|
},
|
|
|
|
{
|
2021-07-06 11:34:36 +01:00
|
|
|
children: <Text id="app.context_menu.copy_link" />,
|
2021-07-05 11:25:20 +01:00
|
|
|
onClick: () =>
|
|
|
|
writeClipboard(
|
|
|
|
`${window.location.protocol}//${window.location.host}/invite/${code}`,
|
|
|
|
),
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
content={
|
|
|
|
processing ? (
|
|
|
|
<Text id="app.special.modals.prompt.create_invite_generate" />
|
|
|
|
) : (
|
|
|
|
<div className={styles.invite}>
|
|
|
|
<Text id="app.special.modals.prompt.create_invite_created" />
|
|
|
|
<code>{code}</code>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
disabled={processing}
|
|
|
|
error={error}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
case "kick_member": {
|
|
|
|
return (
|
|
|
|
<PromptModal
|
|
|
|
onClose={onClose}
|
|
|
|
question={<Text id={`app.context_menu.kick_member`} />}
|
|
|
|
actions={[
|
|
|
|
{
|
2021-07-06 19:29:27 +01:00
|
|
|
children: (
|
|
|
|
<Text id="app.special.modals.actions.kick" />
|
|
|
|
),
|
2021-07-05 11:25:20 +01:00
|
|
|
contrast: true,
|
|
|
|
error: true,
|
|
|
|
confirmation: true,
|
|
|
|
onClick: async () => {
|
|
|
|
setProcessing(true);
|
2021-06-19 18:46:05 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
try {
|
2021-07-25 17:52:57 +01:00
|
|
|
await client.members.kickMember(
|
2021-07-05 11:25:20 +01:00
|
|
|
props.target._id,
|
2021-07-29 15:11:21 +01:00
|
|
|
props.user._id,
|
2021-07-05 11:25:20 +01:00
|
|
|
);
|
|
|
|
onClose();
|
|
|
|
} catch (err) {
|
|
|
|
setError(takeError(err));
|
|
|
|
setProcessing(false);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2021-07-06 11:34:36 +01:00
|
|
|
children: (
|
2021-07-05 11:25:20 +01:00
|
|
|
<Text id="app.special.modals.actions.cancel" />
|
|
|
|
),
|
|
|
|
onClick: onClose,
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
content={
|
|
|
|
<div className={styles.column}>
|
2021-07-29 15:11:21 +01:00
|
|
|
<UserIcon target={props.user} size={64} />
|
2021-07-05 11:25:20 +01:00
|
|
|
<Text
|
|
|
|
id="app.special.modals.prompt.confirm_kick"
|
2021-07-29 15:11:21 +01:00
|
|
|
fields={{ name: props.user?.username }}
|
2021-07-05 11:25:20 +01:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
disabled={processing}
|
|
|
|
error={error}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
case "ban_member": {
|
|
|
|
const [reason, setReason] = useState<string | undefined>(undefined);
|
2021-06-19 18:46:05 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
return (
|
|
|
|
<PromptModal
|
|
|
|
onClose={onClose}
|
|
|
|
question={<Text id={`app.context_menu.ban_member`} />}
|
|
|
|
actions={[
|
|
|
|
{
|
2021-07-06 19:29:27 +01:00
|
|
|
children: (
|
|
|
|
<Text id="app.special.modals.actions.ban" />
|
|
|
|
),
|
2021-07-05 11:25:20 +01:00
|
|
|
contrast: true,
|
|
|
|
error: true,
|
|
|
|
confirmation: true,
|
|
|
|
onClick: async () => {
|
|
|
|
setProcessing(true);
|
2021-06-19 18:46:05 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
try {
|
|
|
|
await client.servers.banUser(
|
|
|
|
props.target._id,
|
2021-07-29 15:11:21 +01:00
|
|
|
props.user._id,
|
2021-07-05 11:25:20 +01:00
|
|
|
{ reason },
|
|
|
|
);
|
|
|
|
onClose();
|
|
|
|
} catch (err) {
|
|
|
|
setError(takeError(err));
|
|
|
|
setProcessing(false);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2021-07-06 11:34:36 +01:00
|
|
|
children: (
|
2021-07-05 11:25:20 +01:00
|
|
|
<Text id="app.special.modals.actions.cancel" />
|
|
|
|
),
|
|
|
|
onClick: onClose,
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
content={
|
|
|
|
<div className={styles.column}>
|
2021-07-29 15:11:21 +01:00
|
|
|
<UserIcon target={props.user} size={64} />
|
2021-07-05 11:25:20 +01:00
|
|
|
<Text
|
|
|
|
id="app.special.modals.prompt.confirm_ban"
|
2021-07-29 15:11:21 +01:00
|
|
|
fields={{ name: props.user?.username }}
|
2021-07-05 11:25:20 +01:00
|
|
|
/>
|
|
|
|
<Overline>
|
|
|
|
<Text id="app.special.modals.prompt.confirm_ban_reason" />
|
|
|
|
</Overline>
|
|
|
|
<InputBox
|
|
|
|
value={reason ?? ""}
|
|
|
|
onChange={(e) =>
|
|
|
|
setReason(e.currentTarget.value)
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
disabled={processing}
|
|
|
|
error={error}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
case "create_channel": {
|
|
|
|
const [name, setName] = useState("");
|
|
|
|
const [type, setType] = useState<"Text" | "Voice">("Text");
|
|
|
|
const history = useHistory();
|
2021-06-19 18:46:05 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
return (
|
|
|
|
<PromptModal
|
|
|
|
onClose={onClose}
|
|
|
|
question={<Text id="app.context_menu.create_channel" />}
|
|
|
|
actions={[
|
|
|
|
{
|
|
|
|
confirmation: true,
|
|
|
|
contrast: true,
|
2021-07-06 11:34:36 +01:00
|
|
|
children: (
|
2021-07-05 11:25:20 +01:00
|
|
|
<Text id="app.special.modals.actions.create" />
|
|
|
|
),
|
|
|
|
onClick: async () => {
|
|
|
|
setProcessing(true);
|
2021-06-23 13:52:16 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
try {
|
|
|
|
const channel =
|
|
|
|
await client.servers.createChannel(
|
|
|
|
props.target._id,
|
|
|
|
{
|
|
|
|
type,
|
|
|
|
name,
|
|
|
|
nonce: ulid(),
|
|
|
|
},
|
|
|
|
);
|
2021-06-23 13:52:16 +01:00
|
|
|
|
2021-07-05 11:25:20 +01:00
|
|
|
history.push(
|
|
|
|
`/server/${props.target._id}/channel/${channel._id}`,
|
|
|
|
);
|
|
|
|
onClose();
|
|
|
|
} catch (err) {
|
|
|
|
setError(takeError(err));
|
|
|
|
setProcessing(false);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2021-07-06 11:34:36 +01:00
|
|
|
children: (
|
2021-07-05 11:25:20 +01:00
|
|
|
<Text id="app.special.modals.actions.cancel" />
|
|
|
|
),
|
|
|
|
onClick: onClose,
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
content={
|
|
|
|
<>
|
|
|
|
<Overline block type="subtle">
|
|
|
|
<Text id="app.main.servers.channel_type" />
|
|
|
|
</Overline>
|
|
|
|
<Radio
|
|
|
|
checked={type === "Text"}
|
|
|
|
onSelect={() => setType("Text")}>
|
|
|
|
<Text id="app.main.servers.text_channel" />
|
|
|
|
</Radio>
|
|
|
|
<Radio
|
|
|
|
checked={type === "Voice"}
|
|
|
|
onSelect={() => setType("Voice")}>
|
|
|
|
<Text id="app.main.servers.voice_channel" />
|
|
|
|
</Radio>
|
|
|
|
<Overline block type="subtle">
|
|
|
|
<Text id="app.main.servers.channel_name" />
|
|
|
|
</Overline>
|
|
|
|
<InputBox
|
|
|
|
value={name}
|
|
|
|
onChange={(e) => setName(e.currentTarget.value)}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
}
|
|
|
|
disabled={processing}
|
|
|
|
error={error}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
2021-07-29 15:11:21 +01:00
|
|
|
});
|