From 43e8870dbc2d76986c68d8004f09b388bbb00a2c Mon Sep 17 00:00:00 2001 From: Creation's Date: Wed, 28 May 2025 09:25:51 -0400 Subject: [PATCH] feat(timezones): add a database option (#272) * add a database option for timezones * forgot to remove wrong comments lol --- .../timezones/TimezoneModal.tsx | 50 +++++-- src/equicordplugins/timezones/database.tsx | 125 +++++++++++++++++ src/equicordplugins/timezones/index.tsx | 132 ++++++++++++++---- 3 files changed, 266 insertions(+), 41 deletions(-) create mode 100644 src/equicordplugins/timezones/database.tsx diff --git a/src/equicordplugins/timezones/TimezoneModal.tsx b/src/equicordplugins/timezones/TimezoneModal.tsx index 1f01922d..a1038eb3 100644 --- a/src/equicordplugins/timezones/TimezoneModal.tsx +++ b/src/equicordplugins/timezones/TimezoneModal.tsx @@ -8,9 +8,10 @@ import * as DataStore from "@api/DataStore"; import { classNameFactory } from "@api/Styles"; import { Margins } from "@utils/margins"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal"; -import { Button, Forms, SearchableSelect, useMemo, useState } from "@webpack/common"; +import { Button, Forms, SearchableSelect, useEffect, useMemo, useState } from "@webpack/common"; -import { DATASTORE_KEY, timezones } from "."; +import { DATASTORE_KEY, settings, timezones } from "."; +import { getTimezone, setTimezone, setUserDatabaseTimezone } from "./database"; export async function setUserTimezone(userId: string, timezone: string | null) { timezones[userId] = timezone; @@ -19,9 +20,24 @@ export async function setUserTimezone(userId: string, timezone: string | null) { const cl = classNameFactory("vc-timezone-"); -export function SetTimezoneModal({ userId, modalProps }: { userId: string, modalProps: ModalProps; }) { +export function SetTimezoneModal({ userId, modalProps, database }: { userId: string, modalProps: ModalProps; database?: boolean; }) { const [currentValue, setCurrentValue] = useState(timezones[userId] ?? null); + useEffect(() => { + if (!database) return; + + const localTimezone = timezones[userId]; + const shouldUseDatabase = + settings.store.useDatabase && + (settings.store.preferDatabaseOverLocal || localTimezone == null); + + if (shouldUseDatabase) { + getTimezone(userId).then(setCurrentValue); + } else { + setCurrentValue(localTimezone); + } + }, [userId, settings.store.useDatabase, settings.store.preferDatabaseOverLocal, database]); + const options = useMemo(() => { return Intl.supportedValuesOf("timeZone").map(timezone => { const offset = new Intl.DateTimeFormat(undefined, { timeZone: timezone, timeZoneName: "short" }) @@ -59,20 +75,28 @@ export function SetTimezoneModal({ userId, modalProps }: { userId: string, modal - + {!database && ( + + )} + ) + }, + + resetDatabaseTimezone: { + description: "Reset your timezone on the database", + type: OptionType.COMPONENT, + component: () => ( + + ) } }); @@ -66,23 +112,33 @@ interface Props { } const TimestampComponent = ErrorBoundary.wrap(({ userId, timestamp, type }: Props) => { const [currentTime, setCurrentTime] = useState(timestamp || Date.now()); - const timezone = timezones[userId]; + const [timezone, setTimezone] = useState(null); useEffect(() => { - let timer: NodeJS.Timeout; + const localTimezone = timezones[userId]; + const shouldUseDatabase = + settings.store.useDatabase && + (settings.store.preferDatabaseOverLocal || localTimezone == null); - if (type === "profile") { - setCurrentTime(Date.now()); - - const now = new Date(); - const delay = (60 - now.getSeconds()) * 1000 + 1000 - now.getMilliseconds(); - - timer = setTimeout(() => { - setCurrentTime(Date.now()); - }, delay); + if (shouldUseDatabase) { + getTimezone(userId).then(setTimezone); + } else { + setTimezone(localTimezone); } + }, [userId, settings.store.useDatabase, settings.store.preferDatabaseOverLocal]); - return () => timer && clearTimeout(timer); + useEffect(() => { + if (type !== "profile") return; + + setCurrentTime(Date.now()); + + const now = new Date(); + const delay = (60 - now.getSeconds()) * 1000 + 1000 - now.getMilliseconds(); + const timer = setTimeout(() => { + setCurrentTime(Date.now()); + }, delay); + + return () => clearTimeout(timer); }, [type, currentTime]); if (!timezone) return null; @@ -94,8 +150,9 @@ const TimestampComponent = ErrorBoundary.wrap(({ userId, timestamp, type }: Prop month: "long", day: "numeric", hour: "numeric", - minute: "numeric", + minute: "numeric" }); + return ( - {toolTipProps => { - return ( - - { - type === "message" ? `(${shortTime})` : shortTime - } - - ); - }} + {toolTipProps => ( + + {type === "message" ? `(${shortTime})` : shortTime} + + )} ); }, { noop: true }); + const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: { user: User; }) => { if (user?.id == null) return; @@ -136,11 +190,33 @@ const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: { children.push(, setTimezoneItem); + if (settings.store.useDatabase) { + const refreshTimezoneItem = ( + { + showToast("Refreshing timezone...", Toasts.Type.CLOCK); + + try { + const timezone = await getTimezone(user.id); + setUserDatabaseTimezone(user.id, timezone); + timezones[user.id] = timezone; + showToast("Timezone refreshed successfully!", Toasts.Type.SUCCESS); + } catch (error) { + console.error("Failed to refresh timezone:", error); + showToast("Failed to refresh timezone.", Toasts.Type.FAILURE); + } + }} + /> + ); + children.push(refreshTimezoneItem); + } }; export default definePlugin({ name: "Timezones", - authors: [Devs.Aria], + authors: [Devs.Aria, EquicordDevs.creations], description: "Shows the local time of users in profiles and message headers", contextMenus: { "user-context": userContextMenuPatch