diff --git a/src/plugins/translate/TranslationAccessory.tsx b/src/plugins/translate/TranslationAccessory.tsx index 00e35689a1..64d395feb5 100644 --- a/src/plugins/translate/TranslationAccessory.tsx +++ b/src/plugins/translate/TranslationAccessory.tsx @@ -23,9 +23,11 @@ import { TranslateIcon } from "./TranslateIcon"; import { cl, TranslationValue } from "./utils"; const TranslationSetters = new Map void>(); +export const translationCache = new Map(); export function handleTranslate(messageId: string, data: TranslationValue) { - TranslationSetters.get(messageId)!(data); + translationCache.set(messageId, data); + TranslationSetters.get(messageId)?.(data); } function Dismiss({ onDismiss }: { onDismiss: () => void; }) { @@ -48,6 +50,9 @@ export function TranslationAccessory({ message }: { message: Message; }) { TranslationSetters.set(message.id, setTranslation); + const cached = translationCache.get(message.id); + if (cached) setTranslation(cached); + return () => void TranslationSetters.delete(message.id); }, []); @@ -58,7 +63,10 @@ export function TranslationAccessory({ message }: { message: Message; }) { {Parser.parse(translation.text)}
- (translated from {translation.sourceLanguage} - setTranslation(undefined)} />) + (translated from {translation.sourceLanguage} - { + translationCache.delete(message.id); + setTranslation(undefined); + }} />) ); } diff --git a/src/plugins/translate/index.tsx b/src/plugins/translate/index.tsx index 68a3a6d29f..729dd948a6 100644 --- a/src/plugins/translate/index.tsx +++ b/src/plugins/translate/index.tsx @@ -22,11 +22,11 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/Co import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; import { Message } from "@vencord/discord-types"; -import { ChannelStore, Menu } from "@webpack/common"; +import { ChannelStore, Menu, MessageStore, SelectedChannelStore, UserStore } from "@webpack/common"; import { settings } from "./settings"; import { setShouldShowTranslateEnabledTooltip, TranslateChatBarIcon, TranslateIcon } from "./TranslateIcon"; -import { handleTranslate, TranslationAccessory } from "./TranslationAccessory"; +import { handleTranslate, TranslationAccessory, translationCache } from "./TranslationAccessory"; import { translate } from "./utils"; const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => { @@ -59,13 +59,61 @@ function getMessageContent(message: Message) { || message.embeds?.find(embed => embed.type === "auto_moderation_message")?.rawDescription || ""; } +const inFlightTranslations = new Map(); + +function autoTranslateMessage(message: Message) { + if ((message as any).vencordEmbeddedBy) return; + + const me = UserStore.getCurrentUser(); + if (me && message.author?.id === me.id) return; + + if (message.channel_id !== SelectedChannelStore.getChannelId()) return; + const content = getMessageContent(message); + if (!content) return; + + if (inFlightTranslations.get(message.id) === content) return; + + const cached = translationCache.get(message.id); + if (cached) { + handleTranslate(message.id, cached); + return; + } + + inFlightTranslations.set(message.id, content); + + translate("received", content) + .then(trans => { + if (inFlightTranslations.get(message.id) !== content) return; + + handleTranslate(message.id, trans); + }) + .finally(() => { + if (inFlightTranslations.get(message.id) === content) + inFlightTranslations.delete(message.id); + }); +} + +function queueRecentMessages(channelId: string) { + if (!channelId) return; + if (channelId !== SelectedChannelStore.getChannelId()) return; + + const limit = settings.store.autoTranslateReceivedLimit ?? 0; + if (limit <= 0) return; + + const messages = MessageStore.getMessages(channelId)?._array as Message[]; + for (const message of messages.slice(-limit)) { + autoTranslateMessage(message); + } +} + + let tooltipTimeout: any; export default definePlugin({ name: "Translate", description: "Translate messages with Google Translate, DeepL or Kagi.", tags: ["Chat", "Utility"], - authors: [Devs.Ven, Devs.AshtonMemer, Devs.koish1], + authors: [Devs.Ven, Devs.AshtonMemer, Devs.koish1, Devs.RumBugen], settings, contextMenus: { "message": messageCtxPatch @@ -99,6 +147,36 @@ export default definePlugin({ } }, + flux: { + MESSAGE_CREATE({ message, optimistic }: { message: Message; optimistic: boolean; }) { + if (optimistic) return; + if (!settings.store.autoTranslateReceived) return; + if (message.channel_id !== SelectedChannelStore.getChannelId()) return; + + autoTranslateMessage(message); + }, + MESSAGE_UPDATE({ message }: { message: Message; }) { + if (!message?.id) return; + if (message.channel_id !== SelectedChannelStore.getChannelId()) return; + const limit = settings.store.autoTranslateReceivedLimit ?? 0; + if (!settings.store.autoTranslateReceived && limit <= 0) return; + + translationCache.delete(message.id); + autoTranslateMessage(message); + }, + async CHANNEL_SELECT({ channelId }: { channelId?: string; }) { + if (!channelId) return; + const limit = settings.store.autoTranslateReceivedLimit ?? 0; + if (limit <= 0) return; + if (!MessageStore.isReady(channelId)) { + await new Promise(resolve => MessageStore.whenReady(channelId, resolve)); + } + if (channelId !== SelectedChannelStore.getChannelId()) return; + + queueRecentMessages(channelId); + } + }, + async onBeforeMessageSend(_, message) { if (!settings.store.autoTranslate) return; if (!message.content) return; diff --git a/src/plugins/translate/settings.tsx b/src/plugins/translate/settings.tsx index cbb39e696c..df37461139 100644 --- a/src/plugins/translate/settings.tsx +++ b/src/plugins/translate/settings.tsx @@ -74,6 +74,16 @@ export const settings = definePluginSettings({ description: "Automatically translate your messages before sending. You can also Shift+click or right-click the translate button to toggle this", default: false }, + autoTranslateReceived: { + type: OptionType.BOOLEAN, + description: "Automatically translate new received messages in the currently open channel", + default: false + }, + autoTranslateReceivedLimit: { + type: OptionType.NUMBER, + description: "How many recent messages to auto-translate when you open a channel (0 = off)", + default: 0 + }, showAutoTranslateTooltip: { type: OptionType.BOOLEAN, description: "Show a tooltip on the chat bar button when a message is auto-translated", diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 61550c00a0..f34c3cd2aa 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -638,6 +638,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "prism", id: 390884143749136386n, }, + RumBugen: { + name: "RumBugen", + id: 296659878985072642n, + }, } satisfies Record); // iife so #__PURE__ works correctly