From 1fd506da7310c3d47ac0aed6d6c0137f5405d3cd Mon Sep 17 00:00:00 2001 From: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Date: Sat, 29 Jun 2024 13:09:19 -0700 Subject: [PATCH] separate components/remove api backend setting/wip oauth --- src/plugins/reviewDB/auth.tsx | 5 +- src/plugins/timezones/api.ts | 26 ++-- .../timezones/components/LocalTimestamp.tsx | 95 +++++++++++++++ .../SetTimezoneOverrideModal.tsx} | 111 +++--------------- .../components/TimezoneDBAuthModal.tsx | 67 +++++++++++ src/plugins/timezones/index.tsx | 22 +--- src/plugins/timezones/settings.tsx | 34 +++--- 7 files changed, 210 insertions(+), 150 deletions(-) create mode 100644 src/plugins/timezones/components/LocalTimestamp.tsx rename src/plugins/timezones/{components.tsx => components/SetTimezoneOverrideModal.tsx} (58%) create mode 100644 src/plugins/timezones/components/TimezoneDBAuthModal.tsx diff --git a/src/plugins/reviewDB/auth.tsx b/src/plugins/reviewDB/auth.tsx index 4cd81f2ea..8d9789dd7 100644 --- a/src/plugins/reviewDB/auth.tsx +++ b/src/plugins/reviewDB/auth.tsx @@ -7,15 +7,12 @@ import { DataStore } from "@api/index"; import { Logger } from "@utils/Logger"; import { openModal } from "@utils/modal"; -import { findByPropsLazy } from "@webpack"; -import { showToast, Toasts, UserStore } from "@webpack/common"; +import { OAuth2AuthorizeModal, showToast, Toasts, UserStore } from "@webpack/common"; import { ReviewDBAuth } from "./entities"; const DATA_STORE_KEY = "rdb-auth"; -const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal"); - export let Auth: ReviewDBAuth = {}; export async function initAuth() { diff --git a/src/plugins/timezones/api.ts b/src/plugins/timezones/api.ts index 246ffb50d..dc29dea54 100644 --- a/src/plugins/timezones/api.ts +++ b/src/plugins/timezones/api.ts @@ -6,33 +6,33 @@ import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent"; import { Logger } from "@utils/Logger"; +import { UserStore } from "@webpack/common"; import settings from "./settings"; -export const DEFAULT_API = "https://timezonedb.catvibers.me/api"; +export const API_URL: string = "https://timezonedb.catvibers.me/api"; export type Snowflake = string; type ApiError = { error: string; }; type UserFetchResponse = ApiError | { timezoneId: string } type BulkFetchResponse = ApiError | Record; -export async function verifyApi(url: string): Promise { - if (url === DEFAULT_API) return true; - - const res = await fetch(url, { +export async function verifyLogin(token: string): Promise { + const res = await fetch(API_URL, { method: "GET", headers: { "User-Agent": VENCORD_USER_AGENT, + "Authorization": token, }, }); - return "logged_in" in await res.json(); + const json: { logged_in?: boolean } = await res.json(); + return !!json.logged_in; } export async function fetchTimezonesBulk(ids: Snowflake[]): Promise | undefined> { try { - const { apiUrl } = settings.store; - const req = await fetch(`${apiUrl}/user/bulk`, { + const req = await fetch(`${API_URL}/user/bulk`, { method: "POST", headers: { "Content-Type": "application/json", @@ -58,8 +58,7 @@ export async function fetchTimezonesBulk(ids: Snowflake[]): Promise { try { - const { apiUrl } = settings.store; - const req = await fetch(`${apiUrl}/user/${userId}`, { + const req = await fetch(`${API_URL}/user/${userId}`, { method: "GET", headers: { "User-Agent": VENCORD_USER_AGENT, @@ -80,3 +79,10 @@ export async function fetchTimezone(userId: Snowflake): Promise + + ; +} + +function LocalTimestampInner(props: LocalTimestampProps): JSX.Element | null { + const [timezone, setTimezone] = useState(); + const [timestamp, setTimestamp] = useState(props.timestamp ?? Date.now()); + + useEffect(() => { + if (!timezone) { + getUserTimezone(props.userId, props.type === "profile").then(setTimezone); + return; + } + + let timer: NodeJS.Timeout; + + if (props.type === "profile") { + setTimestamp(Date.now()); + + const now = new Date(); + const delay = (60 - now.getSeconds()) * 1000 + 1000 - now.getMilliseconds(); + + timer = setTimeout(() => setTimestamp(Date.now()), delay); + } + + return () => timer && clearTimeout(timer); + }, [timezone, timestamp]); + + if (!timezone) return null; + + const longTime = formatTimestamp(timezone, timestamp, true); + const shortTime = formatTimestamp(timezone, timestamp, false); + + if (props.type === "message" && !shortTime) + return null; + + const shortTimeFormatted = props.type === "message" + ? `• ${shortTime}` + : shortTime ?? "Error"; + const classes = props.type === "message" + ? `vc-timezones-message-display ${messageClasses.timestamp}` + : "vc-timezones-profile-display"; + + return + {toolTipProps => <> + { + toolTipProps.onClick(); + openTimezoneOverrideModal(props.userId); + }}> + {shortTimeFormatted} + + } + ; +} diff --git a/src/plugins/timezones/components.tsx b/src/plugins/timezones/components/SetTimezoneOverrideModal.tsx similarity index 58% rename from src/plugins/timezones/components.tsx rename to src/plugins/timezones/components/SetTimezoneOverrideModal.tsx index 91c4e3afd..1e8c6d2e1 100644 --- a/src/plugins/timezones/components.tsx +++ b/src/plugins/timezones/components/SetTimezoneOverrideModal.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import "./styles.css"; +import "../styles.css"; import { ErrorBoundary, Link } from "@components/index"; import { Margins } from "@utils/margins"; @@ -18,102 +18,27 @@ import { ModalRoot, openModal, } from "@utils/modal"; -import { findByPropsLazy } from "@webpack"; -import { Button, Forms, React, SearchableSelect, Text, Tooltip, useEffect, useState } from "@webpack/common"; +import { Button, Forms, React, SearchableSelect, Text, useEffect, useState } from "@webpack/common"; import { SelectOption } from "@webpack/types"; -import { Snowflake } from "./api"; -import { getUserTimezone } from "./cache"; -import settings, { TimezoneOverrides } from "./settings"; -import { formatTimestamp, getTimezonesLazy } from "./utils"; +import settings, { TimezoneOverrides } from "../settings"; +import { getTimezonesLazy } from "../utils"; +import { openTimezoneDBAuthModal } from "./TimezoneDBAuthModal"; -// Based on Syncxv's vc-timezones user plugin // - -const messageClasses = findByPropsLazy("timestamp", "compact", "contentOnly"); - -interface LocalTimestampProps { - userId: Snowflake; - timestamp?: Date; - type: "message" | "profile"; +export function openTimezoneOverrideModal(userId: string) { + openModal(modalProps => <> + + + + ); } -export function LocalTimestamp(props: LocalTimestampProps): JSX.Element { - return - - ; -} - -function LocalTimestampInner(props: LocalTimestampProps): JSX.Element | null { - const [timezone, setTimezone] = useState(); - const [timestamp, setTimestamp] = useState(props.timestamp ?? Date.now()); - - useEffect(() => { - if (!timezone) { - getUserTimezone(props.userId, props.type === "profile").then(setTimezone); - return; - } - - let timer: NodeJS.Timeout; - - if (props.type === "profile") { - setTimestamp(Date.now()); - - const now = new Date(); - const delay = (60 - now.getSeconds()) * 1000 + 1000 - now.getMilliseconds(); - - timer = setTimeout(() => setTimestamp(Date.now()), delay); - } - - return () => timer && clearTimeout(timer); - }, [timezone, timestamp]); - - if (!timezone) return null; - - const longTime = formatTimestamp(timezone, timestamp, true); - const shortTime = formatTimestamp(timezone, timestamp, false); - - if (props.type === "message" && !shortTime) - return null; - - const shortTimeFormatted = props.type === "message" - ? `• ${shortTime}` - : shortTime ?? "Error"; - const classes = props.type === "message" - ? `vc-timezones-message-display ${messageClasses.timestamp}` - : "vc-timezones-profile-display"; - - return <> - - {toolTipProps => <> - { - toolTipProps.onClick(); - openTimezoneOverrideModal(props.userId); - }}> - {shortTimeFormatted} - - } - - ; -} - -interface TimezoneOverrideModalProps { +interface SetTimezoneOverrideModalProps { userId: string, modalProps: ModalProps, } -function SetTimezoneOverrideModal(props: TimezoneOverrideModalProps) { +function SetTimezoneOverrideModal(props: SetTimezoneOverrideModalProps) { const [availableTimezones, setAvailableTimezones] = useState(); const [timezone, setTimezone] = useState(); @@ -174,7 +99,7 @@ function SetTimezoneOverrideModal(props: TimezoneOverrideModalProps) {

To set your own Timezone for other users to see, - click _}>here to + click here to authorize the public TimezoneDB API. @@ -207,11 +132,3 @@ function SetTimezoneOverrideModal(props: TimezoneOverrideModalProps) { ; } - -export function openTimezoneOverrideModal(userId: string) { - openModal(modalProps => <> - - - - ); -} diff --git a/src/plugins/timezones/components/TimezoneDBAuthModal.tsx b/src/plugins/timezones/components/TimezoneDBAuthModal.tsx new file mode 100644 index 000000000..70b9b3cd8 --- /dev/null +++ b/src/plugins/timezones/components/TimezoneDBAuthModal.tsx @@ -0,0 +1,67 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import ErrorBoundary from "@components/ErrorBoundary"; +import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent"; +import { Logger } from "@utils/Logger"; +import { ModalProps, openModal } from "@utils/modal"; +import { OAuth2AuthorizeModal, showToast, Toasts, UserStore } from "@webpack/common"; + +import { API_URL, getCurrentToken, verifyLogin } from "../api"; +import settings from "../settings"; + +const REDIRECT_URI: string = `${API_URL}/auth`; +const CLIENT_ID: string = "922650528821940224"; +const SCOPES: string[] = ["identify"]; + +export async function openTimezoneDBAuthModal() { + const token = getCurrentToken(); + if (token && await verifyLogin(token)) return; // TODO: open set current user modal + + openModal(modalProps => <> + + + + ); +} + +function TimezoneDBAuthModal(modalProps: ModalProps): JSX.Element { + return { + try { + const res = await fetch(response.location, { + redirect: "manual", + headers: { + "Content-Type": VENCORD_USER_AGENT, + }, + }); + + const { token } = await res.json() as { token: string }; + + if (!await verifyLogin(token)) { + throw "Returned token was invalid!"; + } + + settings.store.tokens = { + [UserStore.getCurrentUser().id]: token, + ...settings.store.tokens, + }; + + showToast("Successfully connected to TimezoneDB!", Toasts.Type.SUCCESS); + } catch (e) { + showToast("Failed to authorize TimezoneDB!", Toasts.Type.FAILURE); + new Logger("Timezones").error("Failed to authorize TimezoneDB", e); + } + }} + />; +} diff --git a/src/plugins/timezones/index.tsx b/src/plugins/timezones/index.tsx index 43e641b4e..d1dd13636 100644 --- a/src/plugins/timezones/index.tsx +++ b/src/plugins/timezones/index.tsx @@ -7,14 +7,12 @@ import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { CogWheel } from "@components/Icons"; import { Devs } from "@utils/constants"; -import { Logger } from "@utils/Logger"; import definePlugin from "@utils/types"; import { Menu, UserStore } from "@webpack/common"; import { Message, User } from "discord-types/general"; -import { Promisable } from "type-fest"; -import { verifyApi } from "./api"; -import { LocalTimestamp, openTimezoneOverrideModal } from "./components"; +import { LocalTimestamp } from "./components/LocalTimestamp"; +import { openTimezoneOverrideModal } from "./components/SetTimezoneOverrideModal"; import settings, { SettingsComponent } from "./settings"; const contextMenuPatch: NavContextMenuPatchCallback = (children, { user }: { user: User }) => { @@ -57,7 +55,7 @@ export default definePlugin({ }, })), { - find: '"Message Username"', + find: "\"Message Username\"", replacement: { match: /(?<=isVisibleOnlyOnHover.+?)id:.{1,11},timestamp.{1,50}}\),/, replace: "$&,$self.renderMessageTimezone(arguments[0]),", @@ -70,20 +68,6 @@ export default definePlugin({ "user-profile-overflow-menu": contextMenuPatch, }, - beforeSave(options: Record): Promisable { - // Check that API url is valid - const { apiUrl } = options; - if (!apiUrl) return "Invalid API url!"; - - return verifyApi(apiUrl).then(success => { - if (success) return true; - return "Failed to verify API!"; - }).catch(err => { - new Logger("Timezones").info("Failed to verify API url", err); - return "Failed to verify API!"; - }); - }, - renderProfileTimezone: (props?: { user?: User; }) => { if (!settings.store.displayInProfile || !props?.user?.id) return null; diff --git a/src/plugins/timezones/settings.tsx b/src/plugins/timezones/settings.tsx index 8c9537c8e..b8e9b3950 100644 --- a/src/plugins/timezones/settings.tsx +++ b/src/plugins/timezones/settings.tsx @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -import { DataStore } from "@api/index"; import { definePluginSettings } from "@api/Settings"; import { Link } from "@components/Link"; import { Margins } from "@utils/margins"; @@ -24,25 +23,24 @@ import { classes } from "@utils/misc"; import { IPluginOptionComponentProps, OptionType } from "@utils/types"; import { Text } from "@webpack/common"; -import { DEFAULT_API, Snowflake } from "./api"; -import { TimezoneCache } from "./cache"; +import { Snowflake } from "./api"; +/** A mapping of each user id to the override, being either a timezone or "disabled" */ export type TimezoneOverrides = Record; +/* A mapping of each authorized user id to the JWT token returned by the API. */ +export type TimezoneDBTokens = Record; + const settings = definePluginSettings({ enableApi: { type: OptionType.BOOLEAN, description: "Fetch user timezones from TimezoneDB when a local override does not exist", default: true, }, - apiUrl: { - type: OptionType.STRING, - description: "The TimezoneDB API instance", - default: DEFAULT_API, - placeholder: DEFAULT_API, - onChange(_: string) { - DataStore.clear(TimezoneCache).catch(_ => _); - }, + tokens: { + type: OptionType.COMPONENT, + description: "Authorization with TimezoneDB", + component: props => , }, displayInChat: { type: OptionType.BOOLEAN, @@ -57,21 +55,13 @@ const settings = definePluginSettings({ timezoneOverrides: { type: OptionType.COMPONENT, description: "Local overrides for users' timezones", - component: props => <> - - , + component: props => , }, }); export default settings; export function SettingsComponent(): JSX.Element { - // const { apiUrl } = settings.use(["apiUrl"]); - // const url = `${apiUrl}/../?client_mod=${encodeURIComponent(VENCORD_USER_AGENT)}`; - return <> This plugin supports setting your own timezone publicly for others to @@ -84,3 +74,7 @@ export function SettingsComponent(): JSX.Element { function TimezoneOverridesSetting(props: IPluginOptionComponentProps): JSX.Element { return <>; } + +function AuthorizeTimezoneDBSetting(props: IPluginOptionComponentProps): JSX.Element { + return <>; +}