Plugin

SendTimestamps

Send timestamps easily via chat box button & text shortcuts. Read the extended description!

Chat Commands
index.tsx
Download

Source

src/plugins/sendTimestamps/index.tsx
1/*
2 * FiveCord — a Discord client mod
3 * Copyright (c) 2025 FiveCord
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7import "./styles.css";
8
9import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons";
10import { definePluginSettings } from "@api/Settings";
11import { Devs } from "@utils/constants";
12import { classNameFactory } from "@utils/css";
13import { getTheme, insertTextIntoChatInputBox, Theme } from "@utils/discord";
14import { Margins } from "@utils/margins";
15import definePlugin, { IconComponent, OptionType } from "@utils/types";
16import { RenderModalProps } from "@vencord/discord-types";
17import { Forms, Modal,openModal, Parser, Select, useMemo, useState } from "@webpack/common";
18
19const settings = definePluginSettings({
20 replaceMessageContents: {
21 description: "Replace timestamps in message contents",
22 type: OptionType.BOOLEAN,
23 default: true,
24 },
25});
26
27function parseTime(time: string) {
28 const cleanTime = time.slice(1, -1).replace(/(\d)(AM|PM)$/i, "$1 $2");
29
30 let ms = new Date(`${new Date().toDateString()} ${cleanTime}`).getTime() / 1000;
31 if (isNaN(ms)) return time;
32
33 // add 24h if time is in the past
34 if (Date.now() / 1000 > ms) ms += 86400;
35
36 return `<t:${Math.round(ms)}:t>`;
37}
38
39const Formats = ["", "t", "T", "d", "D", "f", "F", "s", "S", "R"] as const;
40type Format = typeof Formats[number];
41
42const cl = classNameFactory("vc-st-");
43
44function PickerModal(props: RenderModalProps) {
45 const [value, setValue] = useState<string>();
46 const [format, setFormat] = useState<Format>("");
47 const time = Math.round((new Date(value!).getTime() || Date.now()) / 1000);
48
49 const formatTimestamp = (time: number, format: Format) => `<t:${time}${format && `:${format}`}>`;
50
51 const [formatted, rendered] = useMemo(() => {
52 const formatted = formatTimestamp(time, format);
53 return [formatted, Parser.parse(formatted)];
54 }, [time, format]);
55
56 return (
57 <Modal
58 {...props}
59 title="Timestamp Picker"
60 actions={[{
61 text: "Insert",
62 variant: "primary",
63 onClick() {
64 insertTextIntoChatInputBox(formatted + " ");
65 props.onClose();
66 }
67 }]}
68 >
69 <input
70 className={cl("date-picker")}
71 type="datetime-local"
72 value={value}
73 onChange={e => setValue(e.currentTarget.value)}
74 style={{
75 colorScheme: getTheme() === Theme.Light ? "light" : "dark",
76 }}
77 />
78
79 <Forms.FormTitle>Timestamp Format</Forms.FormTitle>
80 <div className={cl("format-select")}>
81 <Select
82 options={
83 Formats.map(m => ({
84 label: m,
85 value: m
86 }))
87 }
88 isSelected={v => v === format}
89 select={v => setFormat(v)}
90 serialize={v => v}
91 renderOptionLabel={o => (
92 <div className={cl("format-label")}>
93 {Parser.parse(formatTimestamp(time, o.value))}
94 </div>
95 )}
96 renderOptionValue={() => rendered}
97 />
98 </div>
99
100 <Forms.FormTitle className={Margins.bottom8}>Preview</Forms.FormTitle>
101 <Forms.FormText className={cl("preview-text")}>
102 {rendered} ({formatted})
103 </Forms.FormText>
104 </Modal>
105 );
106}
107
108const SendTimestampIcon: IconComponent = ({ height = 20, width = 20, className }) => {
109 return (
110 <svg
111 aria-hidden="true"
112 role="img"
113 width={width}
114 height={height}
115 className={className}
116 viewBox="0 0 24 24"
117 style={{ scale: "1.2" }}
118 >
119 <g fill="none" fillRule="evenodd">
120 <path fill="currentColor" d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7v-5z" />
121 <rect width="24" height="24" />
122 </g>
123 </svg>
124 );
125};
126
127const SendTimestampButton: ChatBarButtonFactory = ({ isAnyChat }) => {
128 if (!isAnyChat) return null;
129
130 return (
131 <ChatBarButton
132 tooltip="Insert Timestamp"
133 onClick={() => openModal(props => <PickerModal {...props} />)}
134 buttonProps={{ "aria-haspopup": "dialog" }}
135 >
136 <SendTimestampIcon />
137 </ChatBarButton>
138 );
139};
140
141export default definePlugin({
142 name: "SendTimestamps",
143 description: "Send timestamps easily via chat box button & text shortcuts. Read the extended description!",
144 tags: ["Chat", "Commands"],
145 authors: [Devs.Ven, Devs.Tyler, Devs.Grzesiek11],
146 settings,
147
148 chatBarButton: {
149 icon: SendTimestampIcon,
150 render: SendTimestampButton
151 },
152
153 onBeforeMessageSend(_, msg) {
154 if (settings.store.replaceMessageContents) {
155 msg.content = msg.content.replace(/`\d{1,2}:\d{2} ?(?:AM|PM)?`/gi, parseTime);
156 }
157 },
158
159 settingsAboutComponent() {
160 const samples = [
161 "12:00",
162 "3:51",
163 "17:59",
164 "24:00",
165 "12:00 AM",
166 "0:13PM"
167 ].map(s => `\`${s}\``);
168
169 return (
170 <>
171 <Forms.FormText>
172 To quickly send time only timestamps, include timestamps formatted as `HH:MM` (including the backticks!) in your message
173 </Forms.FormText>
174 <Forms.FormText>
175 See below for examples.
176 If you need anything more specific, use the Date button in the chat bar!
177 </Forms.FormText>
178 <Forms.FormText>
179 Examples:
180 <ul>
181 {samples.map(s => (
182 <li key={s}>
183 <code>{s}</code> {"->"} {Parser.parse(parseTime(s))}
184 </li>
185 ))}
186 </ul>
187 </Forms.FormText>
188 </>
189 );
190 },
191});
192