diff --git a/package.json b/package.json index ac4859593..cfd4bd010 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.9.4", + "version": "1.9.5", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { diff --git a/src/plugins/messageLogger/HistoryModal.tsx b/src/plugins/messageLogger/HistoryModal.tsx new file mode 100644 index 000000000..a7715203d --- /dev/null +++ b/src/plugins/messageLogger/HistoryModal.tsx @@ -0,0 +1,91 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { classNameFactory } from "@api/Styles"; +import ErrorBoundary from "@components/ErrorBoundary"; +import { Margins } from "@utils/margins"; +import { classes } from "@utils/misc"; +import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; +import { findByPropsLazy } from "@webpack"; +import { TabBar, Text, Timestamp, TooltipContainer, useState } from "@webpack/common"; + +import { parseEditContent } from "."; + +const CodeContainerClasses = findByPropsLazy("markup", "codeContainer"); +const MiscClasses = findByPropsLazy("messageContent", "markupRtl"); + +const cl = classNameFactory("vc-ml-modal-"); + +export function openHistoryModal(message: any) { + openModal(props => + + + + ); +} + +export function HistoryModal({ modalProps, message }: { modalProps: ModalProps; message: any; }) { + const [currentTab, setCurrentTab] = useState(message.editHistory.length); + const timestamps = [message.firstEditTimestamp, ...message.editHistory.map(m => m.timestamp)]; + const contents = [...message.editHistory.map(m => m.content), message.content]; + + return ( + + + Message Edit History + + + + + + {message.firstEditTimestamp.getTime() !== message.timestamp.getTime() && ( + + + + + + )} + + {timestamps.map((timestamp, index) => ( + + + + ))} + + +
+ {parseEditContent(contents[currentTab], message)} +
+
+
+ ); +} diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index f88af01a0..9181306ad 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -24,21 +24,26 @@ import { Settings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; +import { proxyLazy } from "@utils/lazy"; import { Logger } from "@utils/Logger"; +import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByCodeLazy, findByPropsLazy } from "@webpack"; import { ChannelStore, FluxDispatcher, i18n, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common"; import { Message } from "discord-types/general"; import overlayStyle from "./deleteStyleOverlay.css?managed"; import textStyle from "./deleteStyleText.css?managed"; +import { openHistoryModal } from "./HistoryModal"; interface MLMessage extends Message { deleted?: boolean; editHistory?: { timestamp: Date; content: string; }[]; + firstEditTimestamp?: Date; } const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage"); +const getMessage = findByCodeLazy('replace(/^\\n+|\\n+$/g,"")'); function addDeleteStyle() { if (Settings.plugins.MessageLogger.deleteStyle === "text") { @@ -125,10 +130,22 @@ const patchChannelContextMenu: NavContextMenuPatchCallback = (children, { channe ); }; +export function parseEditContent(content: string, message: Message) { + return Parser.parse(content, true, { + channelId: message.channel_id, + messageId: message.id, + allowLinks: true, + allowHeading: true, + allowList: true, + allowEmojiLinks: true, + viewingChannelId: SelectedChannelStore.getChannelId(), + }); +} + export default definePlugin({ name: "MessageLogger", description: "Temporarily logs deleted and edited messages.", - authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux], + authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux, Devs.Kyuuhachi], dependencies: ["MessageUpdaterAPI"], contextMenus: { @@ -150,19 +167,11 @@ export default definePlugin({ (oldMsg, newMsg) => oldMsg?.editHistory === newMsg?.editHistory ); - return ( + return Settings.plugins.MessageLogger.inlineEdits && ( <> {message.editHistory?.map(edit => (
- {Parser.parse(edit.content, true, { - channelId, - messageId, - allowLinks: true, - allowHeading: true, - allowList: true, - allowEmojiLinks: true, - viewingChannelId: SelectedChannelStore.getChannelId(), - })} + {parseEditContent(edit.content, message)} openHistoryModal(message)} + aria-role="button" + > + {children} + + ); + }, + + Messages: proxyLazy(() => ({ + DELETED_MESSAGE_COUNT: getMessage("{count, plural, =0 {No deleted messages} one {{count} deleted message} other {{count} deleted messages}}") + })), + patches: [ { // MessageStore @@ -332,7 +368,8 @@ export default definePlugin({ match: /this\.customRenderedContent=(\i)\.customRenderedContent,/, replace: "this.customRenderedContent = $1.customRenderedContent," + "this.deleted = $1.deleted || false," + - "this.editHistory = $1.editHistory || []," + "this.editHistory = $1.editHistory || []," + + "this.firstEditTimestamp = $1.firstEditTimestamp || this.editedTimestamp || this.timestamp," } ] }, @@ -345,7 +382,7 @@ export default definePlugin({ // Pass through editHistory & deleted & original attachments to the "edited message" transformer match: /(?<=null!=\i\.edited_timestamp\)return )\i\(\i,\{reactions:(\i)\.reactions.{0,50}\}\)/, replace: - "Object.assign($&,{ deleted:$1.deleted, editHistory:$1.editHistory })" + "Object.assign($&,{ deleted:$1.deleted, editHistory:$1.editHistory, firstEditTimestamp:$1.firstEditTimestamp })" }, { @@ -364,7 +401,8 @@ export default definePlugin({ " return $2;" + "})())," + "deleted: arguments[1]?.deleted," + - "editHistory: arguments[1]?.editHistory" + "editHistory: arguments[1]?.editHistory," + + "firstEditTimestamp: new Date(arguments[1]?.firstEditTimestamp ?? $2.editedTimestamp ?? $2.timestamp)" }, { // Preserve deleted attribute on attachments @@ -412,6 +450,11 @@ export default definePlugin({ // Render editHistory in the deepest div for message content match: /(\)\("div",\{id:.+?children:\[)/, replace: "$1 (!!arguments[0].message.editHistory?.length && $self.renderEdits(arguments[0]))," + }, + { + // Make edit marker clickable + match: /"span",\{(?=className:\i\.edited,)/, + replace: "$self.EditMarker,{message:arguments[0].message," } ] }, @@ -441,6 +484,30 @@ export default definePlugin({ replace: "children:arguments[0].message.deleted?[]:$1" } ] + }, + { + // Message grouping + find: "NON_COLLAPSIBLE.has(", + replacement: { + match: /if\((\i)\.blocked\)return \i\.\i\.MESSAGE_GROUP_BLOCKED;/, + replace: '$&else if($1.deleted) return"MESSAGE_GROUP_DELETED";', + }, + predicate: () => Settings.plugins.MessageLogger.collapseDeleted + }, + { + // Message group rendering + find: "Messages.NEW_MESSAGES_ESTIMATED_WITH_DATE", + replacement: [ + { + match: /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\|\|/, + replace: '$&$1.type==="MESSAGE_GROUP_DELETED"||', + }, + { + match: /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\?.*?:/, + replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.Messages.DELETED_MESSAGE_COUNT:', + }, + ], + predicate: () => Settings.plugins.MessageLogger.collapseDeleted } ] }); diff --git a/src/plugins/messageLogger/messageLogger.css b/src/plugins/messageLogger/messageLogger.css index a112b1961..2759129d9 100644 --- a/src/plugins/messageLogger/messageLogger.css +++ b/src/plugins/messageLogger/messageLogger.css @@ -38,3 +38,17 @@ .theme-light .messagelogger-edited { opacity: 0.5; } + +.messagelogger-edit-marker { + cursor: pointer; +} + +.vc-ml-modal-timestamp { + cursor: unset; + height: unset; +} + +.vc-ml-modal-tab-bar { + flex-wrap: wrap; + gap: 16px; +} diff --git a/src/plugins/viewRaw/index.tsx b/src/plugins/viewRaw/index.tsx index 68b33eed0..56c285ecd 100644 --- a/src/plugins/viewRaw/index.tsx +++ b/src/plugins/viewRaw/index.tsx @@ -55,6 +55,7 @@ function cleanMessage(msg: Message) { const cloneAny = clone as any; delete cloneAny.editHistory; delete cloneAny.deleted; + delete cloneAny.firstEditTimestamp; cloneAny.attachments?.forEach(a => delete a.deleted); return clone;