Plugin
SendTimestamps
Send timestamps easily via chat box button & text shortcuts. Read the extended description!
1
/*2
* FiveCord — a Discord client mod3
* Copyright (c) 2025 FiveCord4
* SPDX-License-Identifier: GPL-3.0-or-later5
*/6
7
import "./styles.css";8
9
import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons";10
import { definePluginSettings } from "@api/Settings";11
import { Devs } from "@utils/constants";12
import { classNameFactory } from "@utils/css";13
import { getTheme, insertTextIntoChatInputBox, Theme } from "@utils/discord";14
import { Margins } from "@utils/margins";15
import definePlugin, { IconComponent, OptionType } from "@utils/types";16
import { RenderModalProps } from "@vencord/discord-types";17
import { Forms, Modal,openModal, Parser, Select, useMemo, useState } from "@webpack/common";18
19
const settings = definePluginSettings({20
replaceMessageContents: {21
description: "Replace timestamps in message contents",22
type: OptionType.BOOLEAN,23
default: true,24
},25
});26
27
function 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 past34
if (Date.now() / 1000 > ms) ms += 86400;35
36
return `<t:${Math.round(ms)}:t>`;37
}38
39
const Formats = ["", "t", "T", "d", "D", "f", "F", "s", "S", "R"] as const;40
type Format = typeof Formats[number];41
42
const cl = classNameFactory("vc-st-");43
44
function 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
<Modal58
{...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
<input70
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
<Select82
options={83
Formats.map(m => ({84
label: m,85
value: m86
}))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
108
const SendTimestampIcon: IconComponent = ({ height = 20, width = 20, className }) => {109
return (110
<svg111
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
127
const SendTimestampButton: ChatBarButtonFactory = ({ isAnyChat }) => {128
if (!isAnyChat) return null;129
130
return (131
<ChatBarButton132
tooltip="Insert Timestamp"133
onClick={() => openModal(props => <PickerModal {...props} />)}134
buttonProps={{ "aria-haspopup": "dialog" }}135
>136
<SendTimestampIcon />137
</ChatBarButton>138
);139
};140
141
export 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: SendTimestampButton151
},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 message173
</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