diff --git a/README.md b/README.md index 3bfac801..f9f7103e 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - Meow by Samwich - MessageBurst by port - MessageColors by Hen +- MessageFetchTimer by GroupXyz - MessageLinkTooltip by Kyuuhachi - MessageLoggerEnhanced by Aria - MessageTranslate by Samwich diff --git a/src/equicordplugins/messageFetchTimer/index.tsx b/src/equicordplugins/messageFetchTimer/index.tsx new file mode 100644 index 00000000..40a6a7bf --- /dev/null +++ b/src/equicordplugins/messageFetchTimer/index.tsx @@ -0,0 +1,170 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons"; +import { definePluginSettings } from "@api/Settings"; +import { EquicordDevs } from "@utils/constants"; +import { getCurrentChannel } from "@utils/discord"; +import definePlugin, { OptionType } from "@utils/types"; +import { FluxDispatcher, React } from "@webpack/common"; + +interface FetchTiming { + channelId: string; + startTime: number; + endTime?: number; + duration?: number; + timestamp?: Date; +} + +let currentFetch: FetchTiming | null = null; +let currentChannelId: string | null = null; +const channelTimings: Map = new Map(); + +const settings = definePluginSettings({ + showIcon: { + type: OptionType.BOOLEAN, + description: "Show fetch time icon in message bar", + default: true, + }, + showMs: { + type: OptionType.BOOLEAN, + description: "Show milliseconds in timing", + default: true, + }, + iconColor: { + type: OptionType.STRING, + description: "Icon color (CSS color value)", + default: "#00d166", + } +}); + +const FetchTimeButton: ChatBarButtonFactory = ({ isMainChat }) => { + const { showMs, iconColor } = settings.use(["showMs", "iconColor"]); + + if (!isMainChat || !settings.store.showIcon || !currentChannelId) { + return null; + } + + const channelData = channelTimings.get(currentChannelId); + if (!channelData) { + return null; + } + + const { time, timestamp } = channelData; + const displayTime = showMs ? `${Math.round(time)}ms` : `${Math.round(time / 1000)}s`; + + if (!showMs && Math.round(time / 1000) === 0) { + return null; + } + + const timeAgo = formatTimeAgo(timestamp); + + return ( + { }} + > +
+ + + + + {displayTime} + +
+
+ ); +}; + +function formatTimeAgo(timestamp: Date): string { + const now = new Date(); + const diff = now.getTime() - timestamp.getTime(); + const seconds = Math.floor(diff / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); + + if (days > 0) { + return `${days} day${days > 1 ? "s" : ""} ago`; + } else if (hours > 0) { + return `${hours} hour${hours > 1 ? "s" : ""} ago`; + } else if (minutes > 0) { + return `${minutes} minute${minutes > 1 ? "s" : ""} ago`; + } else { + return "just now"; + } +} + +function handleChannelSelect(data: any) { + if (data.channelId && data.channelId !== currentChannelId) { + currentChannelId = data.channelId; + currentFetch = { + channelId: data.channelId, + startTime: performance.now() + }; + } +} + +function handleMessageLoad(data: any) { + if (!currentFetch || data.channelId !== currentFetch.channelId) return; + + const existing = channelTimings.get(currentFetch.channelId); + if (existing) return; + + const endTime = performance.now(); + const duration = endTime - currentFetch.startTime; + + channelTimings.set(currentFetch.channelId, { + time: duration, + timestamp: new Date() + }); + + currentFetch = null; +} + + +export default definePlugin({ + name: "MessageFetchTimer", + description: "Shows how long it took to fetch messages for the current channel", + authors: [EquicordDevs.GroupXyz], + settings, + + start() { + FluxDispatcher.subscribe("CHANNEL_SELECT", handleChannelSelect); + FluxDispatcher.subscribe("LOAD_MESSAGES_SUCCESS", handleMessageLoad); + FluxDispatcher.subscribe("MESSAGE_CREATE", handleMessageLoad); + + const currentChannel = getCurrentChannel(); + if (currentChannel) { + currentChannelId = currentChannel.id; + } + }, + + stop() { + FluxDispatcher.unsubscribe("CHANNEL_SELECT", handleChannelSelect); + FluxDispatcher.unsubscribe("LOAD_MESSAGES_SUCCESS", handleMessageLoad); + FluxDispatcher.unsubscribe("MESSAGE_CREATE", handleMessageLoad); + + currentFetch = null; + channelTimings.clear(); + currentChannelId = null; + }, + + renderChatBarButton: FetchTimeButton, +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index bf4dd6a9..27d163af 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1078,6 +1078,10 @@ export const EquicordDevs = Object.freeze({ name: "bbgaming25k", id: 851222385528274964n, }, + GroupXyz: { + name: "GroupXyz", + id: 950033410229944331n + }, } satisfies Record); // iife so #__PURE__ works correctly