import { Text } from "preact-i18n";
import { useContext } from "preact/hooks";
import { useHistory } from "react-router-dom";
import { Attachment, Channels, Message, Servers, Users } from "revolt.js/dist/api/objects";
import {
ContextMenu,
ContextMenuWithData,
MenuItem
} from "preact-context-menu";
import { ChannelPermission, ServerPermission, UserPermission } from "revolt.js/dist/api/permissions";
import { QueuedMessage } from "../redux/reducers/queue";
import { WithDispatcher } from "../redux/reducers";
import { useIntermediate } from "../context/intermediate/Intermediate";
import { AppContext, ClientStatus, StatusContext } from "../context/revoltjs/RevoltClient";
import { takeError } from "../context/revoltjs/util";
import { useChannel, useChannelPermission, useForceUpdate, useServer, useServerPermission, useUser, useUserPermission } from "../context/revoltjs/hooks";
import { Children } from "../types/Preact";
import LineDivider from "../components/ui/LineDivider";
import { connectState } from "../redux/connector";
import { internalEmit } from "./eventEmitter";
interface ContextMenuData {
user?: string;
server?: string;
server_list?: string;
channel?: string;
message?: Message;
unread?: boolean;
queued?: QueuedMessage;
contextualChannel?: string;
}
type Action =
| { action: "copy_id"; id: string }
| { action: "copy_selection" }
| { action: "copy_text"; content: string }
| { action: "mark_as_read"; channel: Channels.Channel }
| { action: "retry_message"; message: QueuedMessage }
| { action: "cancel_message"; message: QueuedMessage }
| { action: "mention"; user: string }
| { action: "quote_message"; content: string }
| { action: "edit_message"; id: string }
| { action: "delete_message"; target: Channels.Message }
| { action: "open_file"; attachment: Attachment }
| { action: "save_file"; attachment: Attachment }
| { action: "copy_file_link"; attachment: Attachment }
| { action: "open_link"; link: string }
| { action: "copy_link"; link: string }
| { action: "remove_member"; channel: string; user: string }
| { action: "kick_member"; target: Servers.Server; user: string }
| { action: "ban_member"; target: Servers.Server; user: string }
| { action: "view_profile"; user: string }
| { action: "message_user"; user: string }
| { action: "block_user"; user: string }
| { action: "unblock_user"; user: string }
| { action: "add_friend"; user: string }
| { action: "remove_friend"; user: string }
| { action: "cancel_friend"; user: string }
| { action: "set_presence"; presence: Users.Presence }
| { action: "set_status" }
| { action: "clear_status" }
| { action: "create_channel"; server: string }
| { action: "create_invite"; target: Channels.GroupChannel | Channels.TextChannel }
| { action: "leave_group"; target: Channels.GroupChannel }
| { action: "delete_channel"; target: Channels.TextChannel }
| { action: "close_dm"; target: Channels.DirectMessageChannel }
| { action: "leave_server"; target: Servers.Server }
| { action: "delete_server"; target: Servers.Server }
| { action: "open_channel_settings", id: string }
| { action: "open_server_settings", id: string }
| { action: "open_server_channel_settings", server: string, id: string };
function ContextMenus(props: WithDispatcher) {
const { openScreen, writeClipboard } = useIntermediate();
const client = useContext(AppContext);
const userId = client.user!._id;
const status = useContext(StatusContext);
const isOnline = status === ClientStatus.ONLINE;
const history = useHistory();
function contextClick(data?: Action) {
if (typeof data === "undefined") return;
(async () => {
switch (data.action) {
case "copy_id":
writeClipboard(data.id);
break;
case "copy_selection":
writeClipboard(document.getSelection()?.toString() ?? '');
break;
case "mark_as_read":
if (data.channel.channel_type === 'SavedMessages') return;
props.dispatcher({
type: "UNREADS_MARK_READ",
channel: data.channel._id,
message: data.channel.channel_type === 'TextChannel' ? data.channel.last_message : data.channel.last_message._id,
request: true
});
break;
case "retry_message":
{
const nonce = data.message.id;
const fail = (error: any) =>
props.dispatcher({
type: "QUEUE_FAIL",
nonce,
error
});
client.channels
.sendMessage(
data.message.channel,
{
content: data.message.data.content as string,
nonce
}
)
.catch(fail);
props.dispatcher({
type: "QUEUE_START",
nonce
});
}
break;
case "cancel_message":
{
props.dispatcher({
type: "QUEUE_REMOVE",
nonce: data.message.id
});
}
break;
case "mention":
{
internalEmit(
"MessageBox",
"append",
`<@${data.user}>`,
"mention"
);
}
break;
case "copy_text":
writeClipboard(data.content);
break;
case "quote_message":
{
internalEmit(
"MessageBox",
"append",
data.content,
"quote"
);
}
break;
case "edit_message":
{
internalEmit("MessageRenderer", "edit_message", data.id);
}
break;
case "open_file":
{
window
.open(
client.generateFileURL(data.attachment),
"_blank"
)
?.focus();
}
break;
case "save_file":
{
window.open(
// ! FIXME: do this from revolt.js
client.generateFileURL(data.attachment)?.replace('attachments', 'attachments/download'),
"_blank"
);
}
break;
case "copy_file_link":
{
const { filename } = data.attachment;
writeClipboard(
// ! FIXME: do from r.js
client.generateFileURL(data.attachment) + `/${encodeURI(filename)}`,
);
}
break;
case "open_link":
{
window.open(data.link, "_blank")?.focus();
}
break;
case "copy_link":
{
writeClipboard(data.link);
}
break;
case "remove_member":
{
client.channels.removeMember(data.channel, data.user);
}
break;
case "view_profile":
openScreen({ id: 'profile', user_id: data.user });
break;
case "message_user":
{
const channel = await client.users.openDM(data.user);
if (channel) {
history.push(`/channel/${channel._id}`);
}
}
break;
case "add_friend":
{
let user = client.users.get(data.user);
if (user) {
await client.users.addFriend(user.username);
}
}
break;
case "block_user":
await client.users.blockUser(data.user);
break;
case "unblock_user":
await client.users.unblockUser(data.user);
break;
case "remove_friend":
case "cancel_friend":
await client.users.removeFriend(data.user);
break;
case "set_presence":
{
await client.users.editUser({
status: {
...client.user?.status,
presence: data.presence
}
});
}
break;
case "set_status": openScreen({ id: "special_input", type: "set_custom_status" }); break;
case "clear_status":
{
let { text, ...status } = client.user?.status ?? {};
await client.users.editUser({ status });
}
break;
case "leave_group":
case "close_dm":
case "leave_server":
case "delete_channel":
case "delete_server":
case "delete_message":
// @ts-expect-error
case "create_invite": openScreen({ id: "special_prompt", type: data.action, target: data.target }); break;
case "ban_member":
case "kick_member": openScreen({ id: "special_prompt", type: data.action, target: data.target, user: data.user }); break;
case "create_channel": openScreen({ id: "special_input", type: "create_channel", server: data.server }); break;
case "open_channel_settings": history.push(`/channel/${data.id}/settings`); break;
case "open_server_channel_settings": history.push(`/server/${data.server}/channel/${data.id}/settings`); break;
case "open_server_settings": history.push(`/server/${data.id}/settings`); break;
}
})().catch(err => {
openScreen({ id: "error", error: takeError(err) });
});
}
return (
<>
@{client.user?.username}
{client.user?.status?.text && (
)}
>
);
}
export default connectState(
ContextMenus,
() => {
return {};
},
true
);