diff --git a/src/equicordplugins/timezones/TimezoneModal.tsx b/src/equicordplugins/timezones/TimezoneModal.tsx index 4b9f859c..bd90d378 100644 --- a/src/equicordplugins/timezones/TimezoneModal.tsx +++ b/src/equicordplugins/timezones/TimezoneModal.tsx @@ -24,19 +24,17 @@ export function SetTimezoneModal({ userId, modalProps, database }: { userId: str const [currentValue, setCurrentValue] = useState(timezones[userId] ?? null); useEffect(() => { - if (!database) return; - const localTimezone = timezones[userId]; const shouldUseDatabase = settings.store.useDatabase && (settings.store.preferDatabaseOverLocal || !localTimezone); - if (shouldUseDatabase) { - getTimezone(userId).then(e => setCurrentValue(e ?? localTimezone)); - } else { - setCurrentValue(localTimezone); - } - }, [userId, settings.store.useDatabase, settings.store.preferDatabaseOverLocal, database]); + const value = shouldUseDatabase + ? getTimezone(userId) ?? localTimezone + : localTimezone; + + setCurrentValue(value ?? Intl.DateTimeFormat().resolvedOptions().timeZone); + }, [userId, settings.store.useDatabase, settings.store.preferDatabaseOverLocal]); const options = useMemo(() => { return Intl.supportedValuesOf("timeZone").map(timezone => { diff --git a/src/equicordplugins/timezones/database.tsx b/src/equicordplugins/timezones/database.tsx index 643f760b..f382c917 100644 --- a/src/equicordplugins/timezones/database.tsx +++ b/src/equicordplugins/timezones/database.tsx @@ -4,55 +4,45 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { DataStore } from "@api/index"; -import { openModal } from "@utils/modal"; +import { openModal } from "@utils/index"; import { OAuth2AuthorizeModal, showToast, Toasts } from "@webpack/common"; -import { databaseTimezones } from "."; +const databaseTimezones: Record = {}; -export const DOMAIN = "https://timezone.creations.works"; -export const REDIRECT_URI = `${DOMAIN}/auth/discord/callback`; -export const CLIENT_ID = "1377021506810417173"; - -export const DATASTORE_KEY = "vencord-database-timezones"; - -const pendingRequests: Record> = {}; +const DOMAIN = "https://timezone.creations.works"; +const REDIRECT_URI = `${DOMAIN}/auth/discord/callback`; +const CLIENT_ID = "1377021506810417173"; export async function setUserDatabaseTimezone(userId: string, timezone: string | null) { - databaseTimezones[userId] = { - value: timezone, - expires: Date.now() + 60 * 60 * 1000 // 1 hour - }; - await DataStore.set(DATASTORE_KEY, databaseTimezones); + databaseTimezones[userId] = { value: timezone }; } -export async function getTimezone(userId: string, force?: boolean): Promise { - const now = Date.now(); +export function getTimezone(userId: string): string | null { + return databaseTimezones[userId]?.value ?? null; +} - const cached = databaseTimezones[userId]; - if (cached && now < cached.expires && !force) return cached.value; +export async function loadDatabaseTimezones(): Promise { + try { + const res = await fetch(`${DOMAIN}/list`, { + headers: { Accept: "application/json" } + }); - if (!pendingRequests[userId]) { - pendingRequests[userId] = (async () => { - const res = await fetch(`${DOMAIN}/get?id=${userId}`, { - headers: { Accept: "application/json" } - }); - - let value: string | null = null; - if (res.ok) { - const json = await res.json(); - if (json?.timezone && typeof json.timezone === "string") { - value = json.timezone; - } + if (res.ok) { + const json = await res.json(); + for (const id in json) { + databaseTimezones[id] = { + value: json[id]?.timezone ?? null + }; } - setUserDatabaseTimezone(userId, value); - delete pendingRequests[userId]; - return value; - })(); - } + return true; + } - return pendingRequests[userId]; + return false; + } catch (e) { + console.error("Failed to fetch timezones list:", e); + return false; + } } export async function setTimezone(timezone: string): Promise { @@ -92,6 +82,8 @@ export function authModal(callback?: () => void) { permissions={0n} cancelCompletesFlow={false} callback={async (res: any) => { + if (!res || !res.location) return; + try { const url = new URL(res.location); @@ -109,10 +101,10 @@ export function authModal(callback?: () => void) { showToast("Authorization successful!", Toasts.Type.SUCCESS); callback?.(); } catch (e) { + console.error("Error during authorization:", e); showToast("Unexpected error during authorization", Toasts.Type.FAILURE); } }} /> )); } - diff --git a/src/equicordplugins/timezones/index.tsx b/src/equicordplugins/timezones/index.tsx index 12d58f72..c16b432d 100644 --- a/src/equicordplugins/timezones/index.tsx +++ b/src/equicordplugins/timezones/index.tsx @@ -1,6 +1,6 @@ /* * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors + * Copyright (c) 2025 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ @@ -17,15 +17,9 @@ import { findByPropsLazy } from "@webpack"; import { Button, Menu, showToast, Toasts, Tooltip, useEffect, UserStore, useState } from "@webpack/common"; import { Message, User } from "discord-types/general"; -import { authModal, deleteTimezone, getTimezone, setUserDatabaseTimezone } from "./database"; +import { authModal, deleteTimezone, getTimezone, loadDatabaseTimezones, setUserDatabaseTimezone } from "./database"; import { SetTimezoneModal } from "./TimezoneModal"; -type CacheEntry = { - value: string | null; - expires: number; -}; - -export let databaseTimezones: Record = {}; export let timezones: Record = {}; export const DATASTORE_KEY = "vencord-timezones"; @@ -33,6 +27,12 @@ const classes = findByPropsLazy("timestamp", "compact", "contentOnly"); const locale = findByPropsLazy("getLocale"); export const settings = definePluginSettings({ + "Show Own Timezone": { + type: OptionType.BOOLEAN, + description: "Show your own timezone in profiles and message headers", + default: true + }, + "24h Time": { type: OptionType.BOOLEAN, description: "Show time in 24h format", @@ -70,8 +70,7 @@ export const settings = definePluginSettings({ @@ -94,6 +93,13 @@ export const settings = definePluginSettings({ Reset Database Timezones ) + }, + + askedTimezone: { + type: OptionType.BOOLEAN, + description: "Whether the user has been asked to set their timezone", + hidden: true, + default: false } }); @@ -112,6 +118,7 @@ interface Props { timestamp?: string; type: "message" | "profile"; } + const TimestampComponent = ErrorBoundary.wrap(({ userId, timestamp, type }: Props) => { const [currentTime, setCurrentTime] = useState(timestamp || Date.now()); const [timezone, setTimezone] = useState(null); @@ -123,7 +130,7 @@ const TimestampComponent = ErrorBoundary.wrap(({ userId, timestamp, type }: Prop (settings.store.preferDatabaseOverLocal || !localTimezone); if (shouldUseDatabase) { - getTimezone(userId).then(e => setTimezone(e ?? localTimezone)); + setTimezone(getTimezone(userId) ?? localTimezone); } else { setTimezone(localTimezone); } @@ -178,45 +185,18 @@ const TimestampComponent = ErrorBoundary.wrap(({ userId, timestamp, type }: Prop ); }, { noop: true }); - const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: { user: User; }) => { if (user?.id == null) return; const setTimezoneItem = ( openModal(modalProps => )} /> ); children.push(, setTimezoneItem); - - if (settings.store.useDatabase) { - const refreshTimezoneItem = ( - { - showToast("Refreshing timezone...", Toasts.Type.CLOCK); - - try { - const timezone = await getTimezone(user.id, true); - - if (timezone) { - showToast("Timezone refreshed successfully!", Toasts.Type.SUCCESS); - } else { - showToast("Timezone reset 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({ @@ -246,31 +226,75 @@ export default definePlugin({ } ], + toolboxActions: { + "Set Database Timezone": () => { + authModal(async () => { + openModal(modalProps => ); + }); + }, + "Refresh Database Timezones": async () => { + try { + const good = await loadDatabaseTimezones(); + + if (good) { + showToast("Timezones refreshed successfully!", Toasts.Type.SUCCESS); + } else { + showToast("Timezones Failed to refresh!", Toasts.Type.FAILURE); + } + } + catch (error) { + console.error("Failed to refresh timezone:", error); + showToast("Failed to refresh timezones.", Toasts.Type.FAILURE); + } + } + }, + async start() { - databaseTimezones = await DataStore.get>(DATASTORE_KEY) || {}; timezones = await DataStore.get>(DATASTORE_KEY) || {}; + + if (settings.store.useDatabase) { + await loadDatabaseTimezones(); + + if (!settings.store.askedTimezone) { + showToast( + "", + Toasts.Type.MESSAGE, + { + duration: 10000, + component: ( + + ), + position: Toasts.Position.BOTTOM + } + ); + settings.store.askedTimezone = true; + } + } }, settings, getTime, - renderProfileTimezone: (props?: { user?: User; }) => { if (!settings.store.showProfileTime || !props?.user?.id) return null; + if (props.user.id === UserStore.getCurrentUser().id && !settings.store["Show Own Timezone"]) return null; - return ; + return ; }, renderMessageTimezone: (props?: { message?: Message; }) => { if (!settings.store.showMessageHeaderTime || !props?.message) return null; + if (props.message.author.id === UserStore.getCurrentUser().id && !settings.store["Show Own Timezone"]) return null; - return ; + return ; } });