PreviewMessage: Add attachments (& misc changes) (#1715)
This commit is contained in:
parent
cf7c4d63b6
commit
a73d09a2f0
6
src/plugins/favEmojiFirst/README.md
Normal file
6
src/plugins/favEmojiFirst/README.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# FavoriteEmojiFirst
|
||||||
|
|
||||||
|
Puts your favorite emoji first in the emoji autocomplete.
|
||||||
|
|
||||||
|
![FavEmojis](https://i.imgur.com/mEFCoZG.png)
|
||||||
|
![Example](https://i.imgur.com/wY3Tc43.png)
|
5
src/plugins/favGifSearch/README.md
Normal file
5
src/plugins/favGifSearch/README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# FavoriteGifSearch
|
||||||
|
|
||||||
|
Adds a search bar to favorite gifs.
|
||||||
|
|
||||||
|
![Screenshot](https://i.imgur.com/Bcgb7PD.png)
|
|
@ -87,7 +87,7 @@ export const settings = definePluginSettings({
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "FavoriteGifSearch",
|
name: "FavoriteGifSearch",
|
||||||
authors: [Devs.Aria],
|
authors: [Devs.Aria],
|
||||||
description: "Adds a search bar for favorite gifs",
|
description: "Adds a search bar to favorite gifs.",
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
6
src/plugins/imageZoom/README.md
Normal file
6
src/plugins/imageZoom/README.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# ImageZoom
|
||||||
|
|
||||||
|
Lets you zoom in to images and gifs. Use scroll wheel to zoom in and shift + scroll wheel to increase lens radius / size
|
||||||
|
|
||||||
|
![Example](https://i.imgur.com/VJdo4aq.png)
|
||||||
|
![ContextMenu](https://i.imgur.com/0oaRM2s.png)
|
|
@ -99,6 +99,15 @@ const imageContextMenuPatch: NavContextMenuPatchCallback = children => () => {
|
||||||
ContextMenu.close();
|
ContextMenu.close();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Menu.MenuCheckboxItem
|
||||||
|
id="vc-nearest-neighbour"
|
||||||
|
label="Nearset Neighbour"
|
||||||
|
checked={settings.store.nearestNeighbour}
|
||||||
|
action={() => {
|
||||||
|
settings.store.nearestNeighbour = !settings.store.nearestNeighbour;
|
||||||
|
ContextMenu.close();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Menu.MenuControlItem
|
<Menu.MenuControlItem
|
||||||
id="vc-zoom"
|
id="vc-zoom"
|
||||||
label="Zoom"
|
label="Zoom"
|
||||||
|
|
5
src/plugins/previewMessage/README.md
Normal file
5
src/plugins/previewMessage/README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# PreviewMessage
|
||||||
|
|
||||||
|
Lets you preview your message before sending it.
|
||||||
|
|
||||||
|
![Example](https://i.imgur.com/etqbkzu.png)
|
|
@ -16,37 +16,96 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { sendBotMessage } from "@api/Commands";
|
import { generateId, sendBotMessage } from "@api/Commands";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { Button, ButtonLooks, ButtonWrapperClasses, DraftStore, DraftType, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
import { Button, ButtonLooks, ButtonWrapperClasses, DraftStore, DraftType, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
||||||
|
import { MessageAttachment } from "discord-types/general";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
type: {
|
type: {
|
||||||
analyticsName: string;
|
analyticsName: string;
|
||||||
|
isEmpty: boolean;
|
||||||
|
attachments: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UploadStore = findByPropsLazy("getUploads");
|
||||||
|
|
||||||
const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage);
|
const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage);
|
||||||
|
|
||||||
|
|
||||||
|
const getImageBox = (url: string): Promise<{ width: number, height: number; } | null> =>
|
||||||
|
new Promise(res => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () =>
|
||||||
|
res({ width: img.width, height: img.height });
|
||||||
|
|
||||||
|
img.onerror = () =>
|
||||||
|
res(null);
|
||||||
|
|
||||||
|
img.src = url;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const getAttachments = async (channelId: string) =>
|
||||||
|
await Promise.all(
|
||||||
|
UploadStore.getUploads(channelId, DraftType.ChannelMessage)
|
||||||
|
.map(async (upload: any) => {
|
||||||
|
const { isImage, filename, spoiler, item: { file } } = upload;
|
||||||
|
const url = URL.createObjectURL(file);
|
||||||
|
const attachment: MessageAttachment = {
|
||||||
|
id: generateId(),
|
||||||
|
filename: spoiler ? "SPOILER_" + filename : filename,
|
||||||
|
// weird eh? if i give it the normal content type the preview doenst work
|
||||||
|
content_type: undefined,
|
||||||
|
size: await upload.getSize(),
|
||||||
|
spoiler,
|
||||||
|
// discord adds query params to the url, so we need to add a hash to prevent that
|
||||||
|
url: url + "#",
|
||||||
|
proxy_url: url + "#",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isImage) {
|
||||||
|
const box = await getImageBox(url);
|
||||||
|
if (!box) return attachment;
|
||||||
|
|
||||||
|
attachment.width = box.width;
|
||||||
|
attachment.height = box.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attachment;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
export function PreviewButton(chatBoxProps: Props) {
|
export function PreviewButton(chatBoxProps: Props) {
|
||||||
|
const { isEmpty, attachments } = chatBoxProps.type;
|
||||||
|
|
||||||
const channelId = SelectedChannelStore.getChannelId();
|
const channelId = SelectedChannelStore.getChannelId();
|
||||||
const draft = useStateFromStores([DraftStore], () => getDraft(channelId));
|
const draft = useStateFromStores([DraftStore], () => getDraft(channelId));
|
||||||
|
|
||||||
if (chatBoxProps.type.analyticsName !== "normal") return null;
|
if (chatBoxProps.type.analyticsName !== "normal") return null;
|
||||||
if (!draft) return null;
|
|
||||||
|
const hasAttachments = attachments && UploadStore.getUploads(channelId, DraftType.ChannelMessage).length > 0;
|
||||||
|
const hasContent = !isEmpty && draft?.length > 0;
|
||||||
|
|
||||||
|
if (!hasContent && !hasAttachments) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip text="Preview Message">
|
<Tooltip text="Preview Message">
|
||||||
{tooltipProps => (
|
{tooltipProps => (
|
||||||
<Button
|
<Button
|
||||||
{...tooltipProps}
|
{...tooltipProps}
|
||||||
onClick={() =>
|
onClick={async () =>
|
||||||
sendBotMessage(
|
sendBotMessage(
|
||||||
channelId,
|
channelId,
|
||||||
{
|
{
|
||||||
content: getDraft(channelId),
|
content: getDraft(channelId),
|
||||||
author: UserStore.getCurrentUser()
|
author: UserStore.getCurrentUser(),
|
||||||
|
attachments: hasAttachments ? await getAttachments(channelId) : undefined,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
size=""
|
size=""
|
||||||
|
@ -66,7 +125,7 @@ export function PreviewButton(chatBoxProps: Props) {
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "PreviewMessage",
|
name: "PreviewMessage",
|
||||||
description: "Lets you preview your message before sending it",
|
description: "Lets you preview your message before sending it.",
|
||||||
authors: [Devs.Aria],
|
authors: [Devs.Aria],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
5
src/plugins/searchReply/README.md
Normal file
5
src/plugins/searchReply/README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# SearchReply
|
||||||
|
|
||||||
|
Adds a reply button to search results.
|
||||||
|
|
||||||
|
![Screenshot](https://i.imgur.com/SjIEHpw.png)
|
9
src/webpack/common/types/menu.d.ts
vendored
9
src/webpack/common/types/menu.d.ts
vendored
|
@ -65,8 +65,13 @@ export interface Menu {
|
||||||
id: string;
|
id: string;
|
||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
}>;
|
}>;
|
||||||
// TODO: Type me
|
MenuSliderControl: RC<{
|
||||||
MenuSliderControl: RC<any>;
|
minValue: number,
|
||||||
|
maxValue: number,
|
||||||
|
value: number,
|
||||||
|
onChange(value: number): void,
|
||||||
|
renderValue?(value: number): string,
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ContextMenuApi {
|
export interface ContextMenuApi {
|
||||||
|
|
Loading…
Reference in a new issue