2025-05-28 09:25:51 -04:00
|
|
|
/*
|
|
|
|
|
* Vencord, a Discord client mod
|
|
|
|
|
* Copyright (c) 2025 Vendicated and contributors
|
|
|
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
*/
|
|
|
|
|
|
2025-05-29 19:30:17 -04:00
|
|
|
import { openModal } from "@utils/index";
|
2025-05-28 09:25:51 -04:00
|
|
|
import { OAuth2AuthorizeModal, showToast, Toasts } from "@webpack/common";
|
|
|
|
|
|
2025-08-03 14:39:48 -04:00
|
|
|
import { settings } from ".";
|
|
|
|
|
|
2025-05-29 19:30:17 -04:00
|
|
|
const databaseTimezones: Record<string, { value: string | null; }> = {};
|
|
|
|
|
const CLIENT_ID = "1377021506810417173";
|
2025-05-28 09:25:51 -04:00
|
|
|
|
2025-08-03 14:39:48 -04:00
|
|
|
function getDomain(): string {
|
|
|
|
|
return settings.store.databaseUrl || "https://timezone.creations.works";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getRedirectUri(): string {
|
|
|
|
|
return `${getDomain()}/auth/discord/callback`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleApiError(error: any, defaultMessage: string): void {
|
|
|
|
|
console.error(defaultMessage, error);
|
|
|
|
|
const message = error?.message || defaultMessage;
|
|
|
|
|
showToast(message, Toasts.Type.FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function safeJsonParse(response: Response): Promise<any> {
|
|
|
|
|
try {
|
|
|
|
|
return await response.json();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.warn("Failed to parse JSON response:", e);
|
|
|
|
|
return { message: "Invalid response format" };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-28 09:25:51 -04:00
|
|
|
export async function setUserDatabaseTimezone(userId: string, timezone: string | null) {
|
2025-05-29 19:30:17 -04:00
|
|
|
databaseTimezones[userId] = { value: timezone };
|
2025-05-28 09:25:51 -04:00
|
|
|
}
|
|
|
|
|
|
2025-05-29 19:30:17 -04:00
|
|
|
export function getTimezone(userId: string): string | null {
|
|
|
|
|
return databaseTimezones[userId]?.value ?? null;
|
|
|
|
|
}
|
2025-05-28 09:25:51 -04:00
|
|
|
|
2025-05-29 19:30:17 -04:00
|
|
|
export async function loadDatabaseTimezones(): Promise<boolean> {
|
|
|
|
|
try {
|
2025-08-03 14:39:48 -04:00
|
|
|
const res = await fetch(`${getDomain()}/list`, {
|
2025-05-29 19:30:17 -04:00
|
|
|
headers: { Accept: "application/json" }
|
|
|
|
|
});
|
|
|
|
|
if (res.ok) {
|
|
|
|
|
const json = await res.json();
|
|
|
|
|
for (const id in json) {
|
|
|
|
|
databaseTimezones[id] = {
|
|
|
|
|
value: json[id]?.timezone ?? null
|
|
|
|
|
};
|
2025-05-28 09:25:51 -04:00
|
|
|
}
|
2025-05-29 19:30:17 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Failed to fetch timezones list:", e);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-05-28 09:25:51 -04:00
|
|
|
}
|
|
|
|
|
|
2025-06-08 15:31:55 -04:00
|
|
|
async function checkAuthentication(): Promise<boolean> {
|
|
|
|
|
try {
|
2025-08-03 14:39:48 -04:00
|
|
|
const res = await fetch(`${getDomain()}/me`, {
|
2025-06-08 15:31:55 -04:00
|
|
|
credentials: "include",
|
|
|
|
|
headers: { Accept: "application/json" }
|
|
|
|
|
});
|
|
|
|
|
return res.ok;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Failed to check authentication:", e);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-28 09:25:51 -04:00
|
|
|
export async function setTimezone(timezone: string): Promise<boolean> {
|
2025-06-08 15:31:55 -04:00
|
|
|
const isAuthenticated = await checkAuthentication();
|
|
|
|
|
|
|
|
|
|
if (!isAuthenticated) {
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
authModal(() => {
|
|
|
|
|
setTimezoneInternal(timezone).then(resolve);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return setTimezoneInternal(timezone);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function setTimezoneInternal(timezone: string): Promise<boolean> {
|
|
|
|
|
const formData = new URLSearchParams();
|
|
|
|
|
formData.append("timezone", timezone);
|
|
|
|
|
|
|
|
|
|
try {
|
2025-08-03 14:39:48 -04:00
|
|
|
const res = await fetch(`${getDomain()}/set`, {
|
2025-06-08 15:31:55 -04:00
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
|
|
|
Accept: "application/json"
|
|
|
|
|
},
|
|
|
|
|
credentials: "include",
|
|
|
|
|
body: formData
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!res.ok) {
|
2025-08-03 14:39:48 -04:00
|
|
|
const error = await safeJsonParse(res);
|
|
|
|
|
handleApiError(error, "Failed to set timezone");
|
2025-06-08 15:31:55 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
showToast("Timezone updated successfully!", Toasts.Type.SUCCESS);
|
|
|
|
|
return true;
|
|
|
|
|
} catch (e) {
|
2025-08-03 14:39:48 -04:00
|
|
|
handleApiError(e, "Failed to set timezone");
|
2025-06-08 15:31:55 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2025-05-28 09:25:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function deleteTimezone(): Promise<boolean> {
|
2025-06-08 15:31:55 -04:00
|
|
|
const isAuthenticated = await checkAuthentication();
|
|
|
|
|
|
|
|
|
|
if (!isAuthenticated) {
|
2025-08-03 14:39:48 -04:00
|
|
|
return new Promise(resolve => {
|
|
|
|
|
authModal(() => {
|
|
|
|
|
deleteTimezoneInternal().then(resolve);
|
|
|
|
|
});
|
|
|
|
|
});
|
2025-06-08 15:31:55 -04:00
|
|
|
}
|
|
|
|
|
|
2025-08-03 14:39:48 -04:00
|
|
|
return deleteTimezoneInternal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function deleteTimezoneInternal(): Promise<boolean> {
|
2025-06-08 15:31:55 -04:00
|
|
|
try {
|
2025-08-03 14:39:48 -04:00
|
|
|
const res = await fetch(`${getDomain()}/delete`, {
|
2025-06-08 15:31:55 -04:00
|
|
|
method: "DELETE",
|
|
|
|
|
headers: {
|
|
|
|
|
Accept: "application/json"
|
|
|
|
|
},
|
|
|
|
|
credentials: "include"
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!res.ok) {
|
2025-08-03 14:39:48 -04:00
|
|
|
const error = await safeJsonParse(res);
|
|
|
|
|
handleApiError(error, "Failed to delete timezone");
|
2025-06-08 15:31:55 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
showToast("Timezone deleted successfully!", Toasts.Type.SUCCESS);
|
|
|
|
|
return true;
|
|
|
|
|
} catch (e) {
|
2025-08-03 14:39:48 -04:00
|
|
|
handleApiError(e, "Failed to delete timezone");
|
2025-06-08 15:31:55 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2025-05-28 09:25:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function authModal(callback?: () => void) {
|
|
|
|
|
openModal(modalProps => (
|
|
|
|
|
<OAuth2AuthorizeModal
|
|
|
|
|
{...modalProps}
|
|
|
|
|
clientId={CLIENT_ID}
|
2025-08-03 14:39:48 -04:00
|
|
|
redirectUri={getRedirectUri()}
|
2025-05-28 09:25:51 -04:00
|
|
|
responseType="code"
|
|
|
|
|
scopes={["identify"]}
|
|
|
|
|
permissions={0n}
|
|
|
|
|
cancelCompletesFlow={false}
|
|
|
|
|
callback={async (res: any) => {
|
2025-05-29 19:30:17 -04:00
|
|
|
if (!res || !res.location) return;
|
2025-05-28 09:25:51 -04:00
|
|
|
try {
|
|
|
|
|
const url = new URL(res.location);
|
|
|
|
|
const r = await fetch(url, {
|
|
|
|
|
credentials: "include",
|
|
|
|
|
headers: { Accept: "application/json" }
|
|
|
|
|
});
|
2025-08-03 14:39:48 -04:00
|
|
|
const json = await safeJsonParse(r);
|
2025-05-28 09:25:51 -04:00
|
|
|
if (!r.ok) {
|
2025-08-03 14:39:48 -04:00
|
|
|
handleApiError(json, "Authorization failed");
|
2025-05-28 09:25:51 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
showToast("Authorization successful!", Toasts.Type.SUCCESS);
|
|
|
|
|
callback?.();
|
|
|
|
|
} catch (e) {
|
2025-08-03 14:39:48 -04:00
|
|
|
handleApiError(e, "Unexpected error during authorization");
|
2025-05-28 09:25:51 -04:00
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
));
|
|
|
|
|
}
|