diff --git a/plugins/better-timestamps/src/index.ts b/plugins/better-timestamps/src/index.ts index c92302e..53f601b 100644 --- a/plugins/better-timestamps/src/index.ts +++ b/plugins/better-timestamps/src/index.ts @@ -2,6 +2,7 @@ import { logger } from "@vendetta"; import { storage } from "@vendetta/plugin"; import { applyPatches } from "@shared/lib/patcher"; import patchRowManager from "./patches/rowManager"; +import patchTimestamp from "./patches/timestamp"; import Settings from "./ui/Settings"; let unpatchAll: () => void = () => {}; @@ -13,7 +14,8 @@ export default { storage.separateMessages ??= false; unpatchAll = applyPatches("BetterTimestamps", logger, { - "message & day timestamps": patchRowManager + "message & day timestamps": patchRowManager, + "message timestamp component": patchTimestamp }); }, onUnload: () => unpatchAll(), diff --git a/plugins/better-timestamps/src/patches/timestamp.ts b/plugins/better-timestamps/src/patches/timestamp.ts new file mode 100644 index 0000000..9da16e0 --- /dev/null +++ b/plugins/better-timestamps/src/patches/timestamp.ts @@ -0,0 +1,71 @@ +import { moment } from "@vendetta/metro/common"; +import { before } from "@vendetta/patcher"; +import { storage } from "@vendetta/plugin"; +import { waitFor } from "@shared/lib/waitFor"; +import { rawFind } from "@shared/lib/rawFind"; +import renderTimestamp from "../lib/renderTimestamp"; + +function wrapTimestamp(original: any): any { + const getFormatted = (..._args: any[]) => renderTimestamp(original); + if (typeof Proxy === "undefined") return getFormatted(); + return new Proxy(original, { + get(target, prop) { + if (prop === "format" || prop === "calendar" || prop === "fromNow" || prop === "toISOString" || prop === "toString") { + return getFormatted; + } + const value = target[prop]; + return typeof value === "function" ? value.bind(target) : value; + } + }); +} + +function parseTimestamp(value: any): any { + if (value && typeof value.format === "function") return value; + return moment(value); +} + +function findTimestampModule(): any { + return rawFind((m) => { + if (typeof m !== "object" || m == null) return false; + return Object.values(m).some((v: any) => { + if (typeof v === "string") return v.includes("MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL"); + if (typeof v === "function") { + const s = v.toString(); + return s.includes("MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL") || s.includes("MESSAGE_CREATED_TIMESTAMP_A11Y_LABEL"); + } + return false; + }); + }); +} + +export default function patchTimestamp(): () => void { + const patches: (() => void)[] = []; + + const handle = waitFor( + () => { + const module = findTimestampModule(); + return module?.default ? module : undefined; + }, + (module) => { + patches.push(before("default", module, ([props]: any[]) => { + try { + if (props?.timestamp != null) { + const parsed = parseTimestamp(props.timestamp); + props.timestamp = wrapTimestamp(parsed); + } + if (props?.editedTimestamp != null) { + const parsed = parseTimestamp(props.editedTimestamp); + props.editedTimestamp = wrapTimestamp(parsed); + } + } catch { + // Leave timestamps untouched. + } + })); + } + ); + + return () => { + handle.cancel(); + patches.forEach((p) => p()); + }; +}