MoreStickers: PromptToUpload Setting
This commit is contained in:
parent
6ce8a8654b
commit
e6396f8553
8 changed files with 84 additions and 109 deletions
|
|
@ -55,8 +55,8 @@ const extensionToMime: Record<string, string> = {
|
|||
"mkv": "video/x-matroska"
|
||||
};
|
||||
|
||||
export function getMimeFromExtension(ext: string): string {
|
||||
return extensionToMime[ext.toLowerCase()] || "application/octet-stream";
|
||||
export function getMimeFromExtension(ext?: string): string {
|
||||
return extensionToMime[ext?.toLowerCase() ?? ""] || "application/octet-stream";
|
||||
}
|
||||
|
||||
export async function getExtensionFromBytes(blob: Blob): Promise<string | undefined> {
|
||||
|
|
@ -113,7 +113,7 @@ export function isSupported(url: string): boolean {
|
|||
return ext ? supportedExtensions.includes(ext) : false;
|
||||
}
|
||||
|
||||
export function getMediaUrl(props: { src?: string; href?: string; itemSrc?: string; itemHref?: string; target?: any }): string | null {
|
||||
export function getMediaUrl(props: { src?: string; href?: string; itemSrc?: string; itemHref?: string; target?: any; }): string | null {
|
||||
const url = props.src || props.href || props.itemSrc || props.itemHref;
|
||||
if (url && isSupported(url)) return url;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,12 @@ import { getStickerPack, getStickerPackMetas } from "./stickers";
|
|||
import { StickerPack, StickerPackMeta } from "./types";
|
||||
import { cl, FFmpegStateContext, loadFFmpeg } from "./utils";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
export const settings = definePluginSettings({
|
||||
promptToUpload: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Inserts the sticker into your chatbar instead of sending immediately",
|
||||
default: false
|
||||
},
|
||||
packs: {
|
||||
type: OptionType.COMPONENT,
|
||||
description: "Packs",
|
||||
|
|
@ -72,13 +77,6 @@ export default definePlugin({
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: '==="remove_text"',
|
||||
replacement: {
|
||||
match: /,\i\.insertText=\i=>{[\w ;]*?1===\i\.length&&.+?==="remove_text"/,
|
||||
replace: ",$self.textEditor=arguments[0]$&"
|
||||
}
|
||||
}
|
||||
],
|
||||
stickerButton({
|
||||
innerClassName,
|
||||
|
|
|
|||
|
|
@ -4,17 +4,23 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { getMimeFromExtension } from "@equicordplugins/fileUpload/utils/getMediaUrl";
|
||||
import { FFmpeg } from "@ffmpeg/ffmpeg";
|
||||
import { fetchFile } from "@ffmpeg/util";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { ChannelStore, UploadHandler } from "@webpack/common";
|
||||
import { insertTextIntoChatInputBox, MessageOptions } from "@utils/discord";
|
||||
import { CloudUploadPlatform } from "@vencord/discord-types/enums";
|
||||
import { ChannelStore, CloudUploader, Constants, DraftStore, FluxDispatcher, MessageActions, PendingReplyStore, RestAPI, showToast, SnowflakeUtils, Toasts, UploadHandler } from "@webpack/common";
|
||||
|
||||
import moreStickers from ".";
|
||||
import { settings } from ".";
|
||||
import { FFmpegState, Sticker } from "./types";
|
||||
|
||||
const PendingReplyStore = findByPropsLazy("getPendingReply");
|
||||
const MessageUtils = findByPropsLazy("sendMessage");
|
||||
const DraftStore = findByPropsLazy("getDraft", "getState");
|
||||
type SendStickerOptions = {
|
||||
channelId: string;
|
||||
sticker: Sticker;
|
||||
ctrlKey: boolean;
|
||||
shiftKey: boolean;
|
||||
ffmpegState?: FFmpegState;
|
||||
};
|
||||
|
||||
export const ffmpeg = new FFmpeg();
|
||||
|
||||
|
|
@ -97,88 +103,64 @@ async function toGIF(url: string, ffmpeg: FFmpeg): Promise<File> {
|
|||
});
|
||||
}
|
||||
|
||||
export async function sendSticker({
|
||||
channelId,
|
||||
sticker,
|
||||
sendAsLink,
|
||||
ctrlKey,
|
||||
shiftKey,
|
||||
ffmpegState
|
||||
}: { channelId: string; sticker: Sticker; sendAsLink?: boolean; ctrlKey: boolean; shiftKey: boolean; ffmpegState?: FFmpegState; }) {
|
||||
export async function sendSticker({ channelId, sticker, ctrlKey, shiftKey, ffmpegState }: SendStickerOptions) {
|
||||
const reply = PendingReplyStore.getPendingReply(channelId);
|
||||
let content = DraftStore.getDraft(channelId, 0);
|
||||
let options: Partial<MessageOptions> = {};
|
||||
let file: File;
|
||||
|
||||
let messageContent = "";
|
||||
const { textEditor } = moreStickers;
|
||||
if (DraftStore) {
|
||||
messageContent = DraftStore.getDraft(channelId, 0);
|
||||
if (reply) {
|
||||
FluxDispatcher.dispatch({ type: "DELETE_PENDING_REPLY", channelId });
|
||||
options = MessageActions.getSendMessageOptionsForReply(reply);
|
||||
}
|
||||
|
||||
let messageOptions = {};
|
||||
if (PendingReplyStore) {
|
||||
const pendingReply = PendingReplyStore.getPendingReply(channelId);
|
||||
if (pendingReply) {
|
||||
messageOptions = MessageUtils.getSendMessageOptionsForReply(pendingReply);
|
||||
}
|
||||
if (shiftKey) {
|
||||
if (!content.endsWith(" ") && !content.endsWith("\n")) content = " ";
|
||||
content += sticker.image;
|
||||
|
||||
return ctrlKey
|
||||
? insertTextIntoChatInputBox(content)
|
||||
: MessageActions._sendMessage(channelId, { content: sticker.image }, options);
|
||||
}
|
||||
|
||||
if ((ctrlKey || !sendAsLink) && !shiftKey) {
|
||||
let file: File | null = null;
|
||||
|
||||
if (sticker?.isAnimated) {
|
||||
if (!ffmpegState) {
|
||||
throw new Error("FFmpeg state is not provided");
|
||||
}
|
||||
if (!ffmpegState?.ffmpeg) {
|
||||
throw new Error("FFmpeg is not provided");
|
||||
}
|
||||
if (!ffmpegState?.isLoaded) {
|
||||
throw new Error("FFmpeg is not loaded");
|
||||
}
|
||||
|
||||
file = await toGIF(sticker.image, ffmpegState.ffmpeg);
|
||||
}
|
||||
else {
|
||||
const url = new URL(sticker.image);
|
||||
url.searchParams.set("t", Date.now().toString()); // To prevent caching, in order to avoid CORS bug in Chrome
|
||||
const response = await fetch(sticker.image);
|
||||
const orgImageUrl = URL.createObjectURL(await response.blob());
|
||||
const processedImage = await resizeImage(orgImageUrl);
|
||||
|
||||
const filename = sticker.filename ?? (new URL(sticker.image)).pathname.split("/").pop();
|
||||
let mimeType = "image/png";
|
||||
switch (filename?.split(".").pop()?.toLowerCase()) {
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
mimeType = "image/jpeg";
|
||||
break;
|
||||
case "gif":
|
||||
mimeType = "image/gif";
|
||||
break;
|
||||
case "webp":
|
||||
mimeType = "image/webp";
|
||||
break;
|
||||
case "svg":
|
||||
mimeType = "image/svg+xml";
|
||||
break;
|
||||
}
|
||||
file = new File([processedImage], filename!, { type: mimeType });
|
||||
}
|
||||
|
||||
UploadHandler.promptToUpload([file], ChannelStore.getChannel(channelId), 0);
|
||||
return;
|
||||
} else if (shiftKey) {
|
||||
if (!messageContent.endsWith(" ") || !messageContent.endsWith("\n")) messageContent += " ";
|
||||
messageContent += sticker.image;
|
||||
|
||||
if (ctrlKey && textEditor && textEditor.insertText && typeof textEditor.insertText === "function") {
|
||||
textEditor.insertText(messageContent);
|
||||
} else {
|
||||
MessageUtils._sendMessage(channelId, {
|
||||
content: sticker.image
|
||||
}, messageOptions || {});
|
||||
}
|
||||
if (sticker?.isAnimated) {
|
||||
if (!ffmpegState?.ffmpeg || !ffmpegState.isLoaded) throw new Error("FFmpeg not ready");
|
||||
file = await toGIF(sticker.image, ffmpegState.ffmpeg);
|
||||
} else {
|
||||
MessageUtils._sendMessage(channelId, {
|
||||
content: `${messageContent} ${sticker.image}`.trim()
|
||||
}, messageOptions || {});
|
||||
const res = await fetch(sticker.image);
|
||||
const blobUrl = URL.createObjectURL(await res.blob());
|
||||
const processed = await resizeImage(blobUrl);
|
||||
const filename = sticker.filename ?? new URL(sticker.image).pathname.split("/").pop()!;
|
||||
const mimeType = getMimeFromExtension(filename.split(".").pop());
|
||||
|
||||
file = new File([processed], filename, { type: mimeType });
|
||||
}
|
||||
|
||||
if (settings.store.promptToUpload || content) return UploadHandler.promptToUpload([file], ChannelStore.getChannel(channelId), 0);
|
||||
|
||||
const upload = new CloudUploader({ file, platform: CloudUploadPlatform.WEB }, channelId);
|
||||
|
||||
upload.on("complete", () => {
|
||||
RestAPI.post({
|
||||
url: Constants.Endpoints.MESSAGES(channelId),
|
||||
body: {
|
||||
flags: 0,
|
||||
channel_id: channelId,
|
||||
content: "",
|
||||
nonce: SnowflakeUtils.fromTimestamp(Date.now()),
|
||||
sticker_ids: [],
|
||||
type: 0,
|
||||
attachments: [{
|
||||
id: "0",
|
||||
filename: upload.filename,
|
||||
uploaded_filename: upload.uploadedFilename,
|
||||
}],
|
||||
message_reference: reply ? options?.messageReference : null,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
upload.on("error", () => showToast("Failed to upload sticker", Toasts.Type.FAILURE));
|
||||
|
||||
upload.upload();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,18 +6,14 @@
|
|||
|
||||
import * as DataStore from "@api/DataStore";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { CloudUpload, Message } from "@vencord/discord-types";
|
||||
import { Message } from "@vencord/discord-types";
|
||||
import { CloudUploadPlatform } from "@vencord/discord-types/enums";
|
||||
import { findLazy } from "@webpack";
|
||||
import { ChannelStore, Constants, FluxDispatcher, GuildStore, IconUtils, MessageActions, MessageStore, RestAPI, showToast, SnowflakeUtils, Toasts, UserStore } from "@webpack/common";
|
||||
import { ChannelStore, CloudUploader, Constants, FluxDispatcher, GuildStore, IconUtils, MessageActions, MessageStore, RestAPI, showToast, SnowflakeUtils, Toasts, UserStore } from "@webpack/common";
|
||||
|
||||
import { settings } from ".";
|
||||
import { PhantomMessageData, ScheduledAttachment, ScheduledMessage, ScheduledReaction } from "./types";
|
||||
|
||||
const logger = new Logger("ScheduledMessages");
|
||||
|
||||
const CloudUploader = findLazy(m => m.prototype?.trackUploadFinished) as typeof CloudUpload;
|
||||
|
||||
const STORAGE_KEY = "ScheduledMessages_queue";
|
||||
|
||||
let scheduledMessages: ScheduledMessage[] = [];
|
||||
|
|
@ -297,7 +293,7 @@ async function uploadAttachment(channelId: string, att: ScheduledAttachment): Pr
|
|||
const file = new File([bytes], att.filename, { type: att.type });
|
||||
const upload = new CloudUploader({ file, platform: CloudUploadPlatform.WEB }, channelId);
|
||||
|
||||
upload.on("complete", () => resolve({ id: "", filename: upload.filename, uploaded_filename: upload.uploadedFilename }));
|
||||
upload.on("complete", () => resolve({ id: "0", filename: upload.filename, uploaded_filename: upload.uploadedFilename }));
|
||||
upload.on("error", () => resolve(null));
|
||||
upload.upload();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,10 +33,8 @@ import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModa
|
|||
import { useAwaiter } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { chooseFile } from "@utils/web";
|
||||
import { CloudUpload } from "@vencord/discord-types";
|
||||
import { CloudUploadPlatform } from "@vencord/discord-types/enums";
|
||||
import { findLazy, findStoreLazy } from "@webpack";
|
||||
import { Button, Constants, FluxDispatcher, Forms, lodash, Menu, MessageActions, PermissionsBits, PermissionStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, useEffect, useState } from "@webpack/common";
|
||||
import { Button, CloudUploader, Constants, FluxDispatcher, Forms, lodash, Menu, MessageActions, PendingReplyStore, PermissionsBits, PermissionStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { VoiceRecorderDesktop } from "./components/DesktopRecorder";
|
||||
import { VoicePreview } from "./components/VoicePreview";
|
||||
|
|
@ -56,9 +54,6 @@ const EMPTY_META: AudioMetadata = {
|
|||
duration: DEFAULT_DURATION,
|
||||
};
|
||||
|
||||
const CloudUploadConstructor = findLazy(m => m.prototype?.trackUploadFinished) as typeof CloudUpload;
|
||||
const PendingReplyStore = findStoreLazy("PendingReplyStore");
|
||||
|
||||
export const cl = classNameFactory("vc-vmsg-");
|
||||
|
||||
export type VoiceRecorder = React.ComponentType<{
|
||||
|
|
@ -108,7 +103,7 @@ function sendAudio(blob: Blob, meta: AudioMetadata) {
|
|||
const reply = PendingReplyStore.getPendingReply(channelId);
|
||||
if (reply) FluxDispatcher.dispatch({ type: "DELETE_PENDING_REPLY", channelId });
|
||||
|
||||
const upload = new CloudUploadConstructor({
|
||||
const upload = new CloudUploader({
|
||||
file: new File([blob], "voice-message.ogg", { type: "audio/ogg; codecs=opus" }),
|
||||
isThumbnail: false,
|
||||
platform: CloudUploadPlatform.WEB,
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ export async function copyWithToast(text: string, toastMessage = "Copied to clip
|
|||
});
|
||||
}
|
||||
|
||||
interface MessageOptions {
|
||||
export interface MessageOptions {
|
||||
messageReference: Message["messageReference"];
|
||||
allowedMentions: {
|
||||
parse: string[];
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ export let PopoutWindowStore: t.PopoutWindowStore;
|
|||
export let ApplicationCommandIndexStore: t.ApplicationCommandIndexStore;
|
||||
export let EditMessageStore: t.EditMessageStore;
|
||||
export let QuestStore: t.QuestStore;
|
||||
export let PendingReplyStore: GenericStore;
|
||||
|
||||
/**
|
||||
* @see jsdoc of {@link t.useStateFromStores}
|
||||
|
|
@ -135,6 +136,7 @@ waitForStore("SoundboardStore", m => SoundboardStore = m);
|
|||
waitForStore("PopoutWindowStore", m => PopoutWindowStore = m);
|
||||
waitForStore("ApplicationCommandIndexStore", m => ApplicationCommandIndexStore = m);
|
||||
waitForStore("EditMessageStore", m => EditMessageStore = m);
|
||||
waitForStore("PendingReplyStore", m => PendingReplyStore = m);
|
||||
waitForStore("QuestStore", m => QuestStore = m);
|
||||
waitForStore("ThemeStore", m => {
|
||||
ThemeStore = m;
|
||||
|
|
|
|||
|
|
@ -226,3 +226,5 @@ export const DateUtils: t.DateUtils = mapMangledModuleLazy("millisecondsInUnit:"
|
|||
export const MessageTypeSets: t.MessageTypeSets = findByPropsLazy("REPLYABLE", "FORWARDABLE");
|
||||
|
||||
export const fetchApplicationsRPC = findByCodeLazy('"Invalid Origin"', ".application");
|
||||
|
||||
export const CloudUploader = findLazy(m => m.prototype?.trackUploadFinished) as typeof t.CloudUpload;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue