Fix MessageLoggerEnhanced React child crash from persisted render state (#854)

Co-authored-by: justjxke <Jake0300@protonmai.com>
This commit is contained in:
Jake 2026-03-01 21:46:36 +00:00 • committed by GitHub
parent b7e49fb6d1
commit 5610ba99d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 24 additions and 1 deletions

View file

@ -9,6 +9,7 @@ import { DBSchema, IDBPDatabase, openDB } from "idb";
import { LoggedMessageJSON } from "./types"; import { LoggedMessageJSON } from "./types";
import { getMessageStatus } from "./utils"; import { getMessageStatus } from "./utils";
import { stripTransientRenderState } from "./utils/cleanUp";
import { DB_NAME, DB_VERSION } from "./utils/constants"; import { DB_NAME, DB_VERSION } from "./utils/constants";
import { getAttachmentBlobUrl } from "./utils/saveImage"; import { getAttachmentBlobUrl } from "./utils/saveImage";
@ -61,6 +62,7 @@ async function cacheRecords(records: DBMessageRecord[]) {
async function cacheRecord(record?: DBMessageRecord | null) { async function cacheRecord(record?: DBMessageRecord | null) {
if (!record) return record; if (!record) return record;
stripTransientRenderState(record.message);
cachedMessages.set(record.message_id, record.message); cachedMessages.set(record.message_id, record.message);
return record; return record;
} }
@ -207,6 +209,8 @@ export async function getMessagesByChannelAndAfterTimestampIDB(channel_id: strin
} }
export async function addMessageIDB(message: LoggedMessageJSON, status: DBMessageStatus) { export async function addMessageIDB(message: LoggedMessageJSON, status: DBMessageStatus) {
stripTransientRenderState(message);
await db.put("messages", { await db.put("messages", {
channel_id: message.channel_id, channel_id: message.channel_id,
message_id: message.id, message_id: message.id,
@ -218,6 +222,8 @@ export async function addMessageIDB(message: LoggedMessageJSON, status: DBMessag
} }
export async function addMessagesBulkIDB(messages: LoggedMessageJSON[], status?: DBMessageStatus) { export async function addMessagesBulkIDB(messages: LoggedMessageJSON[], status?: DBMessageStatus) {
messages.forEach(stripTransientRenderState);
const tx = db.transaction("messages", "readwrite"); const tx = db.transaction("messages", "readwrite");
const { store } = tx; const { store } = tx;

View file

@ -189,11 +189,18 @@ function messageCreateHandler(payload: MessageCreatePayload) {
async function processMessageFetch(response: FetchMessagesResponse) { async function processMessageFetch(response: FetchMessagesResponse) {
try { try {
if (!response.ok || response.body.length === 0) { if (!response.ok) {
Flogger.error("Failed to fetch messages", response); Flogger.error("Failed to fetch messages", response);
return; return;
} }
if (!Array.isArray(response.body)) {
Flogger.error("Failed to fetch messages: response body is not an array", response);
return;
}
if (response.body.length === 0) return;
const firstMessage = response.body[response.body.length - 1]; const firstMessage = response.body[response.body.length - 1];
const messages = await idb.getMessagesByChannelAndAfterTimestampIDB(firstMessage.channel_id, firstMessage.timestamp); const messages = await idb.getMessagesByChannelAndAfterTimestampIDB(firstMessage.channel_id, firstMessage.timestamp);

View file

@ -22,8 +22,18 @@ import { MessageStore } from "@webpack/common";
import { LoggedMessageJSON, RefrencedMessage } from "../types"; import { LoggedMessageJSON, RefrencedMessage } from "../types";
import { getGuildIdByChannel, isGhostPinged } from "./index"; import { getGuildIdByChannel, isGhostPinged } from "./index";
export function stripTransientRenderState(message: any) {
// These runtime-only fields can contain React elements and must never be persisted.
delete message.customRenderedContent;
delete message.__messageloggerDiff;
delete message.__messageloggerDiffKey;
delete message.__messageloggerAggregated;
delete message.__messageloggerLastAppliedKey;
}
export function cleanupMessage(message: any, removeDetails: boolean = true): LoggedMessageJSON { export function cleanupMessage(message: any, removeDetails: boolean = true): LoggedMessageJSON {
const ret: LoggedMessageJSON = typeof message.toJS === "function" ? JSON.parse(JSON.stringify(message.toJS())) : { ...message }; const ret: LoggedMessageJSON = typeof message.toJS === "function" ? JSON.parse(JSON.stringify(message.toJS())) : { ...message };
stripTransientRenderState(ret);
if (removeDetails) { if (removeDetails) {
ret.author.phone = undefined; ret.author.phone = undefined;
ret.author.email = undefined; ret.author.email = undefined;