mirror of
https://github.com/lukevella/rallly.git
synced 2025-06-02 10:41:54 +02:00
297 lines
7.1 KiB
TypeScript
297 lines
7.1 KiB
TypeScript
import type { TimeFormat } from "@rallly/database";
|
|
import dayjs from "dayjs";
|
|
import advancedFormat from "dayjs/plugin/advancedFormat";
|
|
import duration from "dayjs/plugin/duration";
|
|
import isBetween from "dayjs/plugin/isBetween";
|
|
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
|
|
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
|
|
import localeData from "dayjs/plugin/localeData";
|
|
import localizedFormat from "dayjs/plugin/localizedFormat";
|
|
import minMax from "dayjs/plugin/minMax";
|
|
import relativeTime from "dayjs/plugin/relativeTime";
|
|
import timezone from "dayjs/plugin/timezone";
|
|
import updateLocale from "dayjs/plugin/updateLocale";
|
|
import utc from "dayjs/plugin/utc";
|
|
import { useParams } from "next/navigation";
|
|
import * as React from "react";
|
|
import { useAsync } from "react-use";
|
|
|
|
import { usePreferences } from "@/contexts/preferences";
|
|
import { getBrowserTimeZone, normalizeTimeZone } from "@/utils/date-time-utils";
|
|
|
|
import { useRequiredContext } from "../components/use-required-context";
|
|
|
|
const dayjsLocales: Record<
|
|
string,
|
|
{
|
|
weekStart: number;
|
|
timeFormat: TimeFormat;
|
|
import: () => Promise<ILocale>;
|
|
}
|
|
> = {
|
|
eu: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/eu"),
|
|
},
|
|
en: {
|
|
weekStart: 1,
|
|
timeFormat: "hours12",
|
|
import: () => import("dayjs/locale/en"),
|
|
},
|
|
"en-GB": {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/en-gb"),
|
|
},
|
|
es: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/es"),
|
|
},
|
|
ca: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/ca"),
|
|
},
|
|
da: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/da"),
|
|
},
|
|
de: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/de"),
|
|
},
|
|
fi: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/fi"),
|
|
},
|
|
fr: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/fr"),
|
|
},
|
|
hr: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/hr"),
|
|
},
|
|
it: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/it"),
|
|
},
|
|
sv: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/sv"),
|
|
},
|
|
sk: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/sk"),
|
|
},
|
|
cs: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/cs"),
|
|
},
|
|
pl: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/pl"),
|
|
},
|
|
pt: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/pt"),
|
|
},
|
|
"pt-BR": {
|
|
weekStart: 0,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/pt-br"),
|
|
},
|
|
ru: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/ru"),
|
|
},
|
|
ko: {
|
|
weekStart: 0,
|
|
timeFormat: "hours12",
|
|
import: () => import("dayjs/locale/ko"),
|
|
},
|
|
nl: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/nl"),
|
|
},
|
|
no: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/nb"), // Bokmål
|
|
},
|
|
hu: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/hu"),
|
|
},
|
|
zh: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/zh"),
|
|
},
|
|
"zh-Hant": {
|
|
weekStart: 0,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/zh-tw"),
|
|
},
|
|
vi: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/vi"),
|
|
},
|
|
tr: {
|
|
weekStart: 1,
|
|
timeFormat: "hours24",
|
|
import: () => import("dayjs/locale/tr"),
|
|
},
|
|
};
|
|
|
|
dayjs.extend(localizedFormat);
|
|
dayjs.extend(relativeTime);
|
|
dayjs.extend(localeData);
|
|
dayjs.extend(isSameOrBefore);
|
|
dayjs.extend(isBetween);
|
|
dayjs.extend(minMax);
|
|
dayjs.extend(utc);
|
|
dayjs.extend(timezone);
|
|
dayjs.extend(duration);
|
|
dayjs.extend(isSameOrAfter);
|
|
dayjs.extend(updateLocale);
|
|
dayjs.extend(advancedFormat);
|
|
|
|
const DayjsContext = React.createContext<{
|
|
adjustTimeZone: (
|
|
date: dayjs.ConfigType,
|
|
keepLocalTime: boolean,
|
|
) => dayjs.Dayjs;
|
|
dayjs: (date?: dayjs.ConfigType) => dayjs.Dayjs;
|
|
locale: {
|
|
weekStart: number;
|
|
timeFormat: TimeFormat;
|
|
};
|
|
timeZone: string;
|
|
timeFormat: TimeFormat;
|
|
weekStart: number;
|
|
} | null>(null);
|
|
|
|
DayjsContext.displayName = "DayjsContext";
|
|
|
|
export const useDayjs = () => {
|
|
return useRequiredContext(DayjsContext);
|
|
};
|
|
|
|
export const DayjsProvider: React.FunctionComponent<{
|
|
children?: React.ReactNode;
|
|
config?: {
|
|
locale?: string;
|
|
timeZone?: string;
|
|
localeOverrides?: {
|
|
weekStart?: number;
|
|
timeFormat?: TimeFormat;
|
|
};
|
|
};
|
|
}> = ({ config, children }) => {
|
|
const locale = useParams()?.locale as string;
|
|
const l = config?.locale ?? locale ?? "en";
|
|
const state = useAsync(async () => {
|
|
return await dayjsLocales[l].import();
|
|
}, [l]);
|
|
|
|
const preferredTimeZone = React.useMemo(
|
|
() =>
|
|
config?.timeZone
|
|
? normalizeTimeZone(config?.timeZone)
|
|
: getBrowserTimeZone(),
|
|
[config?.timeZone],
|
|
);
|
|
|
|
const adjustTimeZone = React.useCallback(
|
|
(date: dayjs.ConfigType, localTime = false) => {
|
|
if (!localTime) {
|
|
return dayjs(date).tz(preferredTimeZone);
|
|
}
|
|
return dayjs(date).utc();
|
|
},
|
|
[preferredTimeZone],
|
|
);
|
|
|
|
if (!state.value) {
|
|
// wait for locale to load before rendering
|
|
return null;
|
|
}
|
|
|
|
const dayjsLocale = state.value;
|
|
const localeConfig = dayjsLocales[l];
|
|
const localeTimeFormat = localeConfig.timeFormat;
|
|
|
|
if (config?.localeOverrides) {
|
|
const timeFormat =
|
|
config.localeOverrides.timeFormat ?? localeConfig.timeFormat;
|
|
const weekStart =
|
|
config.localeOverrides.weekStart ?? localeConfig.weekStart;
|
|
dayjs.locale("custom", {
|
|
...dayjsLocale,
|
|
weekStart,
|
|
formats:
|
|
localeTimeFormat === config.localeOverrides?.timeFormat
|
|
? dayjsLocale.formats
|
|
: {
|
|
...dayjsLocale.formats,
|
|
LT: timeFormat === "hours12" ? "h:mm A" : "HH:mm",
|
|
},
|
|
});
|
|
} else {
|
|
dayjs.locale(dayjsLocale);
|
|
}
|
|
|
|
return (
|
|
<DayjsContext.Provider
|
|
value={{
|
|
adjustTimeZone,
|
|
dayjs,
|
|
locale: localeConfig, // locale defaults
|
|
timeZone: preferredTimeZone,
|
|
timeFormat:
|
|
config?.localeOverrides?.timeFormat ?? localeConfig.timeFormat,
|
|
weekStart: config?.localeOverrides?.weekStart ?? localeConfig.weekStart,
|
|
}}
|
|
>
|
|
{children}
|
|
</DayjsContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const ConnectedDayjsProvider = ({
|
|
children,
|
|
}: React.PropsWithChildren) => {
|
|
const { preferences } = usePreferences();
|
|
return (
|
|
<DayjsProvider
|
|
config={{
|
|
locale: preferences.locale ?? undefined,
|
|
timeZone: preferences.timeZone ?? undefined,
|
|
localeOverrides: {
|
|
weekStart: preferences.weekStart ?? undefined,
|
|
timeFormat: preferences.timeFormat ?? undefined,
|
|
},
|
|
}}
|
|
>
|
|
{children}
|
|
</DayjsProvider>
|
|
);
|
|
};
|