mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-02-24 15:35:11 +00:00
overwrite modal
This commit is contained in:
parent
f7e07bb797
commit
18c0e1d57f
5 changed files with 136 additions and 10 deletions
|
@ -13,7 +13,7 @@ import { fetchTimezone, fetchTimezonesBulk, Snowflake } from "./api";
|
||||||
import settings, { TimezoneOverwrites } from "./settings";
|
import settings, { TimezoneOverwrites } from "./settings";
|
||||||
|
|
||||||
// TODO: cache invalidation
|
// TODO: cache invalidation
|
||||||
const TimezoneCache = createStore("UsersTimezoneCache", "TimezoneCache");
|
const TimezoneCache = createStore("TimezoneCache", "TimezoneCache");
|
||||||
|
|
||||||
// A list of callbacks that will trigger on a completed debounced bulk fetch
|
// A list of callbacks that will trigger on a completed debounced bulk fetch
|
||||||
type BulkFetchCallback = (timezone: string | null) => void;
|
type BulkFetchCallback = (timezone: string | null) => void;
|
||||||
|
@ -54,10 +54,10 @@ export async function getUserTimezone(
|
||||||
immediate: boolean = false,
|
immediate: boolean = false,
|
||||||
force: boolean = false,
|
force: boolean = false,
|
||||||
): Promise<string | null> {
|
): Promise<string | null> {
|
||||||
const overwrites = settings.store.timezoneOverwrites ?? {} as TimezoneOverwrites;
|
const overwrites: TimezoneOverwrites = settings.store.timezoneOverwrites ?? {};
|
||||||
const useApi = settings.store.enableApi;
|
|
||||||
const overwrite = overwrites[userId];
|
const overwrite = overwrites[userId];
|
||||||
if (overwrite || !useApi) return overwrite ?? null;
|
if (overwrite !== undefined) return overwrite;
|
||||||
|
if (!settings.store.enableApi) return null;
|
||||||
|
|
||||||
if (!force) {
|
if (!force) {
|
||||||
const cachedTimezone = await DataStore.get<string | null>(userId, TimezoneCache);
|
const cachedTimezone = await DataStore.get<string | null>(userId, TimezoneCache);
|
||||||
|
|
|
@ -6,13 +6,26 @@
|
||||||
|
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
|
||||||
import { ErrorBoundary } from "@components/index";
|
import { ErrorBoundary, Link } from "@components/index";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { React, Tooltip, useEffect, useState } from "@webpack/common";
|
import { Button, Forms, React, SearchableSelect, Text, Tooltip, useEffect, useState } from "@webpack/common";
|
||||||
|
|
||||||
import { Snowflake } from "./api";
|
import { Snowflake } from "./api";
|
||||||
import { getUserTimezone } from "./cache";
|
import { getUserTimezone } from "./cache";
|
||||||
import { formatTimestamp } from "./utils";
|
import { formatTimestamp, getTimezonesLazy } from "./utils";
|
||||||
|
import {
|
||||||
|
ModalCloseButton,
|
||||||
|
ModalContent,
|
||||||
|
ModalFooter,
|
||||||
|
ModalHeader,
|
||||||
|
ModalProps,
|
||||||
|
ModalRoot,
|
||||||
|
openModal,
|
||||||
|
} from "@utils/modal";
|
||||||
|
import { Margins } from "@utils/margins";
|
||||||
|
import { SelectOption } from "@webpack/types";
|
||||||
|
import settings, { TimezoneOverwrites } from "./settings";
|
||||||
|
import { classes } from "@utils/misc";
|
||||||
|
|
||||||
// Based on Syncxv's vc-timezones user plugin //
|
// Based on Syncxv's vc-timezones user plugin //
|
||||||
|
|
||||||
|
@ -82,10 +95,116 @@ function LocalTimestampInner(props: LocalTimestampProps): JSX.Element | null {
|
||||||
text={longTime}
|
text={longTime}
|
||||||
>
|
>
|
||||||
{toolTipProps => <>
|
{toolTipProps => <>
|
||||||
<span className={classes} {...toolTipProps}>
|
<span {...toolTipProps}
|
||||||
|
className={classes}
|
||||||
|
onClick={() => {
|
||||||
|
toolTipProps.onClick();
|
||||||
|
openModal(modalProps =>
|
||||||
|
<TimezoneOverrideModal
|
||||||
|
userId={props.userId}
|
||||||
|
modalProps={modalProps} />,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
{shortTimeFormatted}
|
{shortTimeFormatted}
|
||||||
</span>
|
</span>
|
||||||
</>}
|
</>}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TimezoneOverrideModalProps {
|
||||||
|
userId: string,
|
||||||
|
modalProps: ModalProps,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TimezoneOverrideModal(props: TimezoneOverrideModalProps) {
|
||||||
|
const [availableTimezones, setAvailableTimezones] = useState<SelectOption[]>();
|
||||||
|
const [timezone, setTimezone] = useState<string | "NONE" | undefined>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getTimezonesLazy().then(timezones => {
|
||||||
|
const options: SelectOption[] = timezones.map(tz => {
|
||||||
|
const offset = new Intl.DateTimeFormat(undefined, { timeZone: tz, timeZoneName: "shortOffset" })
|
||||||
|
.formatToParts(Date.now())
|
||||||
|
.find(part => part.type === "timeZoneName")!.value;
|
||||||
|
|
||||||
|
return { label: `${tz} (${offset})`, value: tz };
|
||||||
|
});
|
||||||
|
|
||||||
|
options.unshift({
|
||||||
|
label: "None (Ignore TimezoneDB)",
|
||||||
|
value: "NONE", // I would use null but SearchableSelect is bugged in that null values get converted into undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
options.unshift({
|
||||||
|
label: "Auto (Retrieved from TimezoneDB)",
|
||||||
|
value: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
setAvailableTimezones(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
const overwrites: TimezoneOverwrites = settings.store.timezoneOverwrites ?? {};
|
||||||
|
const overwrite = overwrites[props.userId];
|
||||||
|
setTimezone(overwrite === null ? "NONE" : overwrite);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function saveOverwrite() {
|
||||||
|
if (availableTimezones === undefined) return;
|
||||||
|
|
||||||
|
const overwrites: TimezoneOverwrites = settings.store.timezoneOverwrites ?? {};
|
||||||
|
if (timezone === undefined) {
|
||||||
|
delete overwrites[props.userId];
|
||||||
|
} else if (timezone === "NONE") {
|
||||||
|
overwrites[props.userId] = null;
|
||||||
|
} else {
|
||||||
|
overwrites[props.userId] = timezone;
|
||||||
|
}
|
||||||
|
settings.store.timezoneOverwrites = overwrites;
|
||||||
|
|
||||||
|
props.modalProps.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ModalRoot {...props.modalProps}>
|
||||||
|
<ModalHeader className="vc-timezone-modal-header">
|
||||||
|
<Forms.FormTitle tag="h2">
|
||||||
|
Set Timezone Override for User
|
||||||
|
</Forms.FormTitle>
|
||||||
|
<ModalCloseButton onClick={props.modalProps.onClose} />
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalContent className="vc-timezone-modal-content">
|
||||||
|
<Text variant="text-md/normal">
|
||||||
|
This override will only be visible locally.
|
||||||
|
<br />
|
||||||
|
To set your own Timezone for other users to see,
|
||||||
|
click <Link onClick={/* TODO */ _ => _}>here</Link> to
|
||||||
|
authorize TimezoneDB.
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<section className={classes(Margins.bottom16, Margins.top16)}>
|
||||||
|
<SearchableSelect
|
||||||
|
options={availableTimezones ?? []}
|
||||||
|
value={availableTimezones?.find(opt => opt.value === timezone)}
|
||||||
|
placeholder="Select a Timezone"
|
||||||
|
maxVisibleItems={7}
|
||||||
|
closeOnSelect={true}
|
||||||
|
onChange={setTimezone}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</ModalContent>
|
||||||
|
|
||||||
|
<ModalFooter className="vc-timezone-modal-footer">
|
||||||
|
<Button
|
||||||
|
color={Button.Colors.BRAND}
|
||||||
|
disabled={availableTimezones === undefined}
|
||||||
|
onClick={saveOverwrite}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
<Button color={Button.Colors.RED} onClick={props.modalProps.onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalRoot>;
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { Text } from "@webpack/common";
|
||||||
|
|
||||||
import { Snowflake } from "./api";
|
import { Snowflake } from "./api";
|
||||||
|
|
||||||
export type TimezoneOverwrites = Record<Snowflake, string>;
|
export type TimezoneOverwrites = Record<Snowflake, string | null>;
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
enableApi: {
|
enableApi: {
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
.timezone-message-item {
|
.timezone-message-item {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-timezone-modal-header {
|
.vc-timezone-modal-header {
|
||||||
|
|
|
@ -27,6 +27,7 @@ export function formatTimestamp(
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
minute: "numeric",
|
minute: "numeric",
|
||||||
|
timeZoneName: "shortOffset",
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatter = new Intl.DateTimeFormat(locale, {
|
const formatter = new Intl.DateTimeFormat(locale, {
|
||||||
|
@ -50,7 +51,12 @@ async function getTimezones(): Promise<string[]> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await fetch(TIMEZONE_LIST).then(res => res.json());
|
try {
|
||||||
|
return await fetch(TIMEZONE_LIST).then(res => res.json());
|
||||||
|
} catch (e) {
|
||||||
|
new Logger("Timezones").error("Failed to fetch external timezones list", e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTimezonesLazy = makeLazy(getTimezones, 2);
|
export const getTimezonesLazy = makeLazy(getTimezones, 2);
|
||||||
|
|
Loading…
Add table
Reference in a new issue