stoat-for-desktop/components/app/menus/MessageContextMenu.tsx

275 lines
8.4 KiB
TypeScript

import { For, Match, Show, Switch } from "solid-js";
import { Trans } from "@lingui-solid/solid/macro";
import { File, Message } from "stoat.js";
import { useClient, useUser } from "@revolt/client";
import { CustomEmoji, UnicodeEmoji } from "@revolt/markdown/emoji";
import { useModals } from "@revolt/modal";
import { useState } from "@revolt/state";
import MdBadge from "@material-design-icons/svg/outlined/badge.svg?component-solid";
import MdContentCopy from "@material-design-icons/svg/outlined/content_copy.svg?component-solid";
import MdDelete from "@material-design-icons/svg/outlined/delete.svg?component-solid";
import MdDeleteSweep from "@material-design-icons/svg/outlined/delete_sweep.svg?component-solid";
import MdDownload from "@material-design-icons/svg/outlined/download.svg?component-solid";
import MdEdit from "@material-design-icons/svg/outlined/edit.svg?component-solid";
import MdLink from "@material-design-icons/svg/outlined/link.svg?component-solid";
import MdMarkChatUnread from "@material-design-icons/svg/outlined/mark_chat_unread.svg?component-solid";
import MdOpenInNew from "@material-design-icons/svg/outlined/open_in_new.svg?component-solid";
import MdPin from "@material-design-icons/svg/outlined/pin_invoke.svg?component-solid";
import MdReply from "@material-design-icons/svg/outlined/reply.svg?component-solid";
import MdReport from "@material-design-icons/svg/outlined/report.svg?component-solid";
import MdShare from "@material-design-icons/svg/outlined/share.svg?component-solid";
import MdShield from "@material-design-icons/svg/outlined/shield.svg?component-solid";
import MdSentimentContent from "@material-symbols/svg-400/outlined/sentiment_content.svg?component-solid";
import {
ContextMenu,
ContextMenuButton,
ContextMenuDivider,
ContextMenuSubMenu,
} from "./ContextMenu";
/**
* Context menu for messages
*/
export function MessageContextMenu(props: { message?: Message; file?: File }) {
const user = useUser();
const state = useState();
const client = useClient();
const { openModal, showError } = useModals();
/**
* Reply to this message
*/
function reply() {
state.draft.addReply(props.message!, user()!.id);
}
/**
* Mark message as unread
*/
function markAsUnread() {
props.message!.ack(true, false, true);
}
/**
* Copy message contents to clipboard
*/
function copyText() {
navigator.clipboard.writeText(props.message!.content);
}
/**
* Report the message
*/
function report() {
openModal({
type: "report_content",
target: props.message!,
client: client(),
});
}
/**
* Delete the message
*/
function deleteMessage(ev: MouseEvent) {
if (ev.shiftKey) {
props.message!.delete();
} else {
openModal({
type: "delete_message",
message: props.message!,
});
}
}
/**
* Open message in Stoat Admin Panel
*/
function openAdminPanel() {
window.open(
`https://old-admin.stoatinternal.com/panel/inspect/message/${props.message!.id}`,
"_blank",
);
}
/**
* Copy message link to clipboard
*/
function copyLink() {
navigator.clipboard.writeText(
`${location.origin}${
props.message!.server ? `/server/${props.message!.server?.id}` : ""
}/channel/${props.message!.channelId}/${props.message!.id}`,
);
}
/**
* Copy message id to clipboard
*/
function copyId() {
navigator.clipboard.writeText(props.message!.id);
}
/**
* Opens the file preview in a new tab
*/
function OpenFile() {
window.open(props.file?.originalUrl, "_blank");
}
/**
* Copies the link to the original url of the file
*/
function CopyLink() {
navigator.clipboard.writeText(props.file?.originalUrl ?? "");
}
return (
<ContextMenu>
<Show when={props.file}>
<ContextMenuButton icon={MdOpenInNew} onClick={OpenFile}>
<Trans>Open file</Trans>
</ContextMenuButton>
<ContextMenuButton icon={MdLink} onClick={CopyLink}>
<Trans>Copy link</Trans>
</ContextMenuButton>
<a
target="_blank"
download={props.file?.filename}
href={props.file?.originalUrl}
>
<ContextMenuButton icon={MdDownload}>
<Trans>Save file</Trans>
</ContextMenuButton>
</a>
<ContextMenuDivider />
</Show>
<Show when={props.message}>
<Show when={props.message!.channel?.havePermission("SendMessage")}>
<ContextMenuButton icon={MdReply} onClick={reply}>
<Trans>Reply</Trans>
</ContextMenuButton>
</Show>
<ContextMenuButton icon={MdMarkChatUnread} onClick={markAsUnread}>
<Trans>Mark as unread</Trans>
</ContextMenuButton>
<ContextMenuButton icon={MdContentCopy} onClick={copyText}>
<Trans>Copy text</Trans>
</ContextMenuButton>
<ContextMenuDivider />
<Show
when={
props.message!.author?.self &&
props.message!.channel?.havePermission("SendMessage")
}
>
<ContextMenuButton
icon={MdEdit}
onClick={() => state.draft.setEditingMessage(props.message!)}
>
<Trans>Edit message</Trans>
</ContextMenuButton>
</Show>
<Show
when={
props.message!.channel?.type === "DirectMessage" ||
props.message!.channel?.havePermission("ManageMessages")
}
>
<ContextMenuButton
icon={MdPin}
onClick={() => {
if (props.message!.pinned) {
props.message!.unpin().catch(showError);
} else {
props.message!.pin().catch(showError);
}
}}
>
<Switch fallback={<Trans>Pin message</Trans>}>
<Match when={props.message!.pinned}>
<Trans>Unpin message</Trans>
</Match>
</Switch>
</ContextMenuButton>
</Show>
<Show
when={
props.message!.reactions.size &&
props.message!.channel?.havePermission("ManageMessages")
}
>
<ContextMenuSubMenu
icon={MdDeleteSweep}
onClick={() => props.message!.clearReactions()}
destructive
buttonContent={<Trans>Remove reaction</Trans>}
>
<For each={[...props.message!.reactions.keys()]}>
{(key) => (
<ContextMenuButton
onClick={() => props.message!.unreact(key, true)}
>
<Switch fallback={<UnicodeEmoji emoji={key} />}>
<Match when={key.length === 26}>
<CustomEmoji id={key} />
</Match>
</Switch>
</ContextMenuButton>
)}
</For>
</ContextMenuSubMenu>
</Show>
<Show when={props.message!.reactions.size}>
<ContextMenuButton
symbol={MdSentimentContent}
onClick={() => props.message!.clearReactions()}
destructive
>
<Trans>Remove all reactions</Trans>
</ContextMenuButton>
</Show>
<Show
when={
props.message!.author?.self ||
props.message!.channel?.havePermission("ManageMessages")
}
>
<ContextMenuButton
icon={MdDelete}
onClick={deleteMessage}
destructive
>
<Trans>Delete message</Trans>
</ContextMenuButton>
</Show>
<Show when={!props.message!.author?.self}>
<ContextMenuButton icon={MdReport} onClick={report} destructive>
<Trans>Report message</Trans>
</ContextMenuButton>
</Show>
<ContextMenuDivider />
<Show when={state.settings.getValue("advanced:admin_panel")}>
<ContextMenuButton icon={MdShield} onClick={openAdminPanel}>
<Trans>Admin Panel</Trans>
</ContextMenuButton>
</Show>
<ContextMenuButton icon={MdShare} onClick={copyLink}>
<Trans>Copy link</Trans>
</ContextMenuButton>
<Show when={state.settings.getValue("advanced:copy_id")}>
<ContextMenuButton icon={MdBadge} onClick={copyId}>
<Trans>Copy message ID</Trans>
</ContextMenuButton>
</Show>
</Show>
</ContextMenu>
);
}