diff --git a/apps/web/src/app/[locale]/(auth)/auth/login/components/login-page.tsx b/apps/web/src/app/[locale]/(auth)/auth/login/components/login-page.tsx index ced99b943..d55052cf4 100644 --- a/apps/web/src/app/[locale]/(auth)/auth/login/components/login-page.tsx +++ b/apps/web/src/app/[locale]/(auth)/auth/login/components/login-page.tsx @@ -1,9 +1,7 @@ "use client"; -import { usePostHog } from "@rallly/posthog/client"; import { Button } from "@rallly/ui/button"; import { useMutation } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; -import { useSession } from "next-auth/react"; import React from "react"; import { Logo } from "@/components/logo"; @@ -16,8 +14,6 @@ import { trpc } from "@/trpc/client"; type PageProps = { magicLink: string; email: string }; export const LoginPage = ({ magicLink, email }: PageProps) => { - const session = useSession(); - const posthog = usePostHog(); const { t } = useTranslation(); const [error, setError] = React.useState(null); @@ -28,15 +24,6 @@ export const LoginPage = ({ magicLink, email }: PageProps) => { }, onSuccess: async (data) => { if (!data.url.includes("auth/error")) { - // if login was successful, update the session - const updatedSession = await session.update(); - if (updatedSession?.user) { - // identify the user in posthog - posthog?.identify(updatedSession.user.id, { - email: updatedSession.user.email, - name: updatedSession.user.name, - }); - } router.push(data.url); } else { setError( diff --git a/apps/web/src/app/[locale]/(auth)/register/verify/components/otp-form.tsx b/apps/web/src/app/[locale]/(auth)/register/verify/components/otp-form.tsx index a238e5777..eec3725a9 100644 --- a/apps/web/src/app/[locale]/(auth)/register/verify/components/otp-form.tsx +++ b/apps/web/src/app/[locale]/(auth)/register/verify/components/otp-form.tsx @@ -1,7 +1,6 @@ "use client"; import { zodResolver } from "@hookform/resolvers/zod"; -import { usePostHog } from "@rallly/posthog/client"; import { Button } from "@rallly/ui/button"; import { Form, @@ -37,12 +36,11 @@ export function OTPForm({ token }: { token: string }) { resolver: zodResolver(otpFormSchema), }); - const { timeZone } = useDayjs(); + const { timeZone, weekStart, timeFormat } = useDayjs(); const locale = i18n.language; const queryClient = trpc.useUtils(); - const posthog = usePostHog(); const authenticateRegistration = trpc.auth.authenticateRegistration.useMutation(); const searchParams = useSearchParams(); @@ -52,6 +50,8 @@ export function OTPForm({ token }: { token: string }) { token, timeZone, locale, + weekStart, + timeFormat, code: data.otp, }); @@ -64,11 +64,6 @@ export function OTPForm({ token }: { token: string }) { queryClient.invalidate(); - posthog?.identify(res.user.id, { - email: res.user.email, - name: res.user.name, - }); - signIn("registration-token", { token, redirectTo: searchParams?.get("redirectTo") ?? "/", diff --git a/apps/web/src/app/[locale]/(space)/settings/preferences/actions.ts b/apps/web/src/app/[locale]/(space)/settings/preferences/actions.ts new file mode 100644 index 000000000..c7e8934b5 --- /dev/null +++ b/apps/web/src/app/[locale]/(space)/settings/preferences/actions.ts @@ -0,0 +1,17 @@ +"use server"; + +import { prisma } from "@rallly/database"; + +import { getUserId } from "@/next-auth"; + +export async function updateLocale(locale: string) { + const userId = await getUserId(); + await prisma.user.update({ + where: { + id: userId, + }, + data: { + locale, + }, + }); +} diff --git a/apps/web/src/app/[locale]/(space)/settings/components/date-time-preferences.tsx b/apps/web/src/app/[locale]/(space)/settings/preferences/components/date-time-preferences.tsx similarity index 84% rename from apps/web/src/app/[locale]/(space)/settings/components/date-time-preferences.tsx rename to apps/web/src/app/[locale]/(space)/settings/preferences/components/date-time-preferences.tsx index 116558ad9..8f57269cf 100644 --- a/apps/web/src/app/[locale]/(space)/settings/components/date-time-preferences.tsx +++ b/apps/web/src/app/[locale]/(space)/settings/preferences/components/date-time-preferences.tsx @@ -27,8 +27,8 @@ const formSchema = z.object({ type FormData = z.infer; const DateTimePreferencesForm = () => { - const { timeFormat, weekStart, timeZone, locale } = useDayjs(); - const { preferences, updatePreferences } = usePreferences(); + const { timeFormat, weekStart, timeZone } = useDayjs(); + const { updatePreferences } = usePreferences(); const form = useForm({ resolver: zodResolver(formSchema), @@ -126,25 +126,6 @@ const DateTimePreferencesForm = () => { > - {preferences.timeFormat || preferences.weekStart ? ( - - ) : null} diff --git a/apps/web/src/app/[locale]/(space)/settings/components/language-preference.tsx b/apps/web/src/app/[locale]/(space)/settings/preferences/components/language-preference.tsx similarity index 92% rename from apps/web/src/app/[locale]/(space)/settings/components/language-preference.tsx rename to apps/web/src/app/[locale]/(space)/settings/preferences/components/language-preference.tsx index e124d52eb..5d9faeb7b 100644 --- a/apps/web/src/app/[locale]/(space)/settings/components/language-preference.tsx +++ b/apps/web/src/app/[locale]/(space)/settings/preferences/components/language-preference.tsx @@ -8,9 +8,10 @@ import { z } from "zod"; import { LanguageSelect } from "@/components/poll/language-selector"; import { Trans } from "@/components/trans"; -import { usePreferences } from "@/contexts/preferences"; import { useTranslation } from "@/i18n/client"; +import { updateLocale } from "../actions"; + const formSchema = z.object({ language: z.string(), }); @@ -25,13 +26,12 @@ export const LanguagePreference = () => { }, resolver: zodResolver(formSchema), }); - const { updatePreferences } = usePreferences(); return (
{ - await updatePreferences({ locale: data.language }); + await updateLocale(data.language); i18n.changeLanguage(data.language); form.reset({ language: data.language }); })} diff --git a/apps/web/src/app/[locale]/(space)/settings/preferences/preferences-page.tsx b/apps/web/src/app/[locale]/(space)/settings/preferences/preferences-page.tsx index 1d1f5cf6c..4ba2e21a4 100644 --- a/apps/web/src/app/[locale]/(space)/settings/preferences/preferences-page.tsx +++ b/apps/web/src/app/[locale]/(space)/settings/preferences/preferences-page.tsx @@ -2,12 +2,12 @@ import { Trans } from "@/components/trans"; -import { DateTimePreferences } from "../components/date-time-preferences"; -import { LanguagePreference } from "../components/language-preference"; import { SettingsContent, SettingsSection, } from "../components/settings-layout"; +import { DateTimePreferences } from "./components/date-time-preferences"; +import { LanguagePreference } from "./components/language-preference"; export function PreferencesPage() { return ( diff --git a/apps/web/src/app/[locale]/layout.tsx b/apps/web/src/app/[locale]/layout.tsx index 4982b8c97..cbaa6aafc 100644 --- a/apps/web/src/app/[locale]/layout.tsx +++ b/apps/web/src/app/[locale]/layout.tsx @@ -8,11 +8,11 @@ import { TooltipProvider } from "@rallly/ui/tooltip"; import { domAnimation, LazyMotion } from "motion/react"; import type { Viewport } from "next"; import { Inter } from "next/font/google"; -import { SessionProvider } from "next-auth/react"; import React from "react"; import { TimeZoneChangeDetector } from "@/app/[locale]/timezone-change-detector"; import { UserProvider } from "@/components/user-provider"; +import { PreferencesProvider } from "@/contexts/preferences"; import { getUser } from "@/data/get-user"; import { TimezoneProvider } from "@/features/timezone/client/context"; import { I18nProvider } from "@/i18n/client"; @@ -33,32 +33,26 @@ export const viewport: Viewport = { initialScale: 1, }; -async function loadLocale() { - let locale = getLocale(); - - const userId = await getUserId(); - - if (userId) { - const user = await getUser(); - if (user.locale) { - locale = user.locale; - } - } - - if (!supportedLngs.includes(locale)) { - return defaultLocale; - } - - return locale; -} - export default async function Root({ children, }: { children: React.ReactNode; }) { const session = await auth(); - const locale = await loadLocale(); + + let locale = getLocale(); + + const userId = await getUserId(); + + const user = userId ? await getUser() : null; + + if (user?.locale) { + locale = user.locale; + } + + if (!supportedLngs.includes(locale)) { + locale = defaultLocale; + } return ( @@ -67,23 +61,48 @@ export default async function Root({ - - - - - - + + + + + + {children} - - - - + + + + diff --git a/apps/web/src/app/components/user-language-switcher.tsx b/apps/web/src/app/components/user-language-switcher.tsx deleted file mode 100644 index 2aa3070eb..000000000 --- a/apps/web/src/app/components/user-language-switcher.tsx +++ /dev/null @@ -1,22 +0,0 @@ -"use client"; - -import { useRouter } from "next/navigation"; - -import { LanguageSelect } from "@/components/poll/language-selector"; -import { usePreferences } from "@/contexts/preferences"; -import { useTranslation } from "@/i18n/client"; - -export function UserLanguageSwitcher() { - const { i18n } = useTranslation(); - const { preferences, updatePreferences } = usePreferences(); - const router = useRouter(); - return ( - { - await updatePreferences({ locale: language }); - router.refresh(); - }} - /> - ); -} diff --git a/apps/web/src/components/create-poll.tsx b/apps/web/src/components/create-poll.tsx index 2665b5688..be1a6fdef 100644 --- a/apps/web/src/components/create-poll.tsx +++ b/apps/web/src/components/create-poll.tsx @@ -11,7 +11,6 @@ import { import { Form } from "@rallly/ui/form"; import { useToast } from "@rallly/ui/hooks/use-toast"; import { useRouter } from "next/navigation"; -import { signIn, useSession } from "next-auth/react"; import React from "react"; import { useForm } from "react-hook-form"; import useFormPersist from "react-hook-form-persist"; @@ -42,9 +41,8 @@ export interface CreatePollPageProps { export const CreatePoll: React.FunctionComponent = () => { const router = useRouter(); - const { user } = useUser(); + const { user, createGuestIfNeeded } = useUser(); const { toast } = useToast(); - const session = useSession(); const form = useForm({ defaultValues: { title: "", @@ -84,11 +82,7 @@ export const CreatePoll: React.FunctionComponent = () => { { const title = required(formData?.title); - if (session.status !== "authenticated") { - await signIn("guest", { - redirect: false, - }); - } + await createGuestIfNeeded(); await createPoll.mutateAsync( { title: title, diff --git a/apps/web/src/components/discussion/discussion.tsx b/apps/web/src/components/discussion/discussion.tsx index 098764902..abf49e40c 100644 --- a/apps/web/src/components/discussion/discussion.tsx +++ b/apps/web/src/components/discussion/discussion.tsx @@ -26,7 +26,6 @@ import { MoreHorizontalIcon, TrashIcon, } from "lucide-react"; -import { signIn, useSession } from "next-auth/react"; import * as React from "react"; import { Controller, useForm } from "react-hook-form"; @@ -57,8 +56,7 @@ function NewCommentForm({ }) { const { t } = useTranslation(); const poll = usePoll(); - const { user } = useUser(); - const session = useSession(); + const { user, createGuestIfNeeded } = useUser(); const { participants } = useParticipants(); const authorName = React.useMemo(() => { @@ -98,11 +96,7 @@ function NewCommentForm({ { - if (session.status !== "authenticated") { - await signIn("guest", { - redirect: false, - }); - } + await createGuestIfNeeded(); await addComment.mutateAsync({ authorName, content, pollId }); reset({ authorName, content: "" }); onSubmit?.(); diff --git a/apps/web/src/components/new-participant-modal.tsx b/apps/web/src/components/new-participant-modal.tsx index c15a234f8..0ee432513 100644 --- a/apps/web/src/components/new-participant-modal.tsx +++ b/apps/web/src/components/new-participant-modal.tsx @@ -7,7 +7,6 @@ import { FormMessage } from "@rallly/ui/form"; import { Input } from "@rallly/ui/input"; import * as Sentry from "@sentry/nextjs"; import { TRPCClientError } from "@trpc/client"; -import { signIn, useSession } from "next-auth/react"; import { useForm } from "react-hook-form"; import z from "zod"; @@ -91,8 +90,7 @@ export const NewParticipantForm = (props: NewParticipantModalProps) => { const isEmailRequired = poll.requireParticipantEmail; - const { user } = useUser(); - const session = useSession(); + const { user, createGuestIfNeeded } = useUser(); const isLoggedIn = !user.isGuest; const { register, setError, formState, handleSubmit } = useForm({ @@ -113,11 +111,7 @@ export const NewParticipantForm = (props: NewParticipantModalProps) => { { try { - if (session.status !== "authenticated") { - await signIn("guest", { - redirect: false, - }); - } + await createGuestIfNeeded(); const newParticipant = await addParticipant.mutateAsync({ name: data.name, votes: props.votes, diff --git a/apps/web/src/components/user-provider.tsx b/apps/web/src/components/user-provider.tsx index 8a95666bf..945ba9092 100644 --- a/apps/web/src/components/user-provider.tsx +++ b/apps/web/src/components/user-provider.tsx @@ -1,14 +1,11 @@ "use client"; import { usePostHog } from "@rallly/posthog/client"; import { useRouter } from "next/navigation"; -import type { Session } from "next-auth"; -import { signOut, useSession } from "next-auth/react"; +import { signIn, signOut } from "next-auth/react"; import React from "react"; import { useSubscription } from "@/contexts/plan"; -import { PreferencesProvider } from "@/contexts/preferences"; import { useTranslation } from "@/i18n/client"; -import { trpc } from "@/trpc/client"; import { isOwner } from "@/utils/permissions"; import { useRequiredContext } from "./use-required-context"; @@ -16,23 +13,20 @@ import { useRequiredContext } from "./use-required-context"; type UserData = { id?: string; name: string; - email?: string | null; + email?: string; isGuest: boolean; tier: "guest" | "hobby" | "pro"; - timeZone?: string | null; - timeFormat?: "hours12" | "hours24" | null; - weekStart?: number | null; - image?: string | null; - locale?: string | null; + image?: string; }; export const UserContext = React.createContext<{ user: UserData; - refresh: (data?: Record) => Promise; + refresh: () => void; ownsObject: (obj: { userId?: string | null; guestId?: string | null; }) => boolean; + createGuestIfNeeded: () => Promise; logout: () => Promise; } | null>(null); @@ -58,16 +52,37 @@ export const IfGuest = (props: { children?: React.ReactNode }) => { return <>{props.children}; }; -export const UserProvider = (props: { children?: React.ReactNode }) => { - const session = useSession(); - const user = session.data?.user; +type BaseUser = { + id: string; + tier: "guest" | "hobby" | "pro"; + image?: string; + name?: string; + email?: string; +}; + +type RegisteredUser = BaseUser & { + email: string; + name: string; + tier: "hobby" | "pro"; +}; + +type GuestUser = BaseUser & { + tier: "guest"; +}; + +export const UserProvider = ({ + children, + user, +}: { + children?: React.ReactNode; + user?: RegisteredUser | GuestUser; +}) => { const subscription = useSubscription(); - const updatePreferences = trpc.user.updatePreferences.useMutation(); - const { t, i18n } = useTranslation(); + const { t } = useTranslation(); const router = useRouter(); const posthog = usePostHog(); - const isGuest = !user?.email; + const isGuest = !user || user.tier === "guest"; const tier = isGuest ? "guest" : subscription?.active ? "pro" : "hobby"; React.useEffect(() => { @@ -76,9 +91,7 @@ export const UserProvider = (props: { children?: React.ReactNode }) => { email: user.email, name: user.name, tier, - timeZone: user.timeZone ?? null, - image: user.image ?? null, - locale: user.locale ?? i18n.language, + image: user.image, }); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -90,17 +103,20 @@ export const UserProvider = (props: { children?: React.ReactNode }) => { user: { id: user?.id, name: user?.name ?? t("guest"), - email: user?.email || null, + email: user?.email, isGuest, tier, - timeZone: user?.timeZone ?? null, - image: user?.image ?? null, - locale: user?.locale ?? i18n.language, + image: user?.image, }, - refresh: async (data) => { - router.refresh(); - return await session.update(data); + createGuestIfNeeded: async () => { + if (!user) { + await signIn("guest", { + redirect: false, + }); + router.refresh(); + } }, + refresh: router.refresh, logout: async () => { await signOut(); posthog?.capture("logout"); @@ -111,27 +127,7 @@ export const UserProvider = (props: { children?: React.ReactNode }) => { }, }} > - { - if (!isGuest) { - await updatePreferences.mutateAsync({ - locale: newPreferences.locale ?? undefined, - timeZone: newPreferences.timeZone ?? undefined, - timeFormat: newPreferences.timeFormat ?? undefined, - weekStart: newPreferences.weekStart ?? undefined, - }); - } - await session.update(newPreferences); - }} - > - {props.children} - + {children} ); }; diff --git a/apps/web/src/contexts/preferences.tsx b/apps/web/src/contexts/preferences.tsx index 01785ebe5..9d9236743 100644 --- a/apps/web/src/contexts/preferences.tsx +++ b/apps/web/src/contexts/preferences.tsx @@ -1,16 +1,22 @@ +"use client"; + import type { TimeFormat } from "@rallly/database"; import React from "react"; -import { useSetState } from "react-use"; +import { useLocalStorage } from "react-use"; +import { z } from "zod"; import { useRequiredContext } from "@/components/use-required-context"; +import { useUser } from "@/components/user-provider"; +import { trpc } from "@/trpc/client"; type Preferences = { - timeZone?: string | null; - locale?: string | null; - timeFormat?: TimeFormat | null; - weekStart?: number | null; + timeZone?: string; + timeFormat?: TimeFormat; + weekStart?: number; }; +const timeFormatSchema = z.enum(["hours12", "hours24"]); + type PreferencesContextValue = { preferences: Preferences; updatePreferences: (preferences: Partial) => Promise; @@ -23,21 +29,58 @@ const PreferencesContext = React.createContext( export const PreferencesProvider = ({ children, initialValue, - onUpdate, }: { children?: React.ReactNode; initialValue: Partial; - onUpdate?: (preferences: Partial) => Promise; }) => { - const [preferences, setPreferences] = useSetState(initialValue); + const { user } = useUser(); + const [preferredTimezone, setPreferredTimezone] = useLocalStorage( + "rallly.preferredTimezone", + initialValue.timeZone, + ); + const [preferredTimeFormat, setPreferredTimeFormat] = useLocalStorage( + "rallly.preferredTimeFormat", + initialValue.timeFormat, + { + raw: false, + serializer: timeFormatSchema.parse, + deserializer: timeFormatSchema.optional().catch(undefined).parse, + }, + ); + const [preferredWeekStart, setPreferredWeekStart] = useLocalStorage( + "rallly.preferredWeekStart", + initialValue.weekStart, + ); + const updatePreferences = trpc.user.updatePreferences.useMutation(); return ( { - setPreferences(newPreferences); - await onUpdate?.(newPreferences); + if (newPreferences.timeZone) { + setPreferredTimezone(newPreferences.timeZone); + } + + if (newPreferences.timeFormat) { + setPreferredTimeFormat(newPreferences.timeFormat); + } + + if (newPreferences.weekStart) { + setPreferredWeekStart(newPreferences.weekStart); + } + + if (!user.isGuest) { + await updatePreferences.mutateAsync({ + timeZone: newPreferences.timeZone ?? undefined, + timeFormat: newPreferences.timeFormat ?? undefined, + weekStart: newPreferences.weekStart ?? undefined, + }); + } }, }} > diff --git a/apps/web/src/data/get-user.ts b/apps/web/src/data/get-user.ts index d2e48c63b..489d4efb9 100644 --- a/apps/web/src/data/get-user.ts +++ b/apps/web/src/data/get-user.ts @@ -18,6 +18,8 @@ export const getUser = cache(async () => { image: true, locale: true, timeZone: true, + timeFormat: true, + weekStart: true, subscription: { select: { active: true, @@ -37,6 +39,8 @@ export const getUser = cache(async () => { image: user.image ?? undefined, locale: user.locale ?? undefined, timeZone: user.timeZone ?? undefined, + timeFormat: user.timeFormat ?? undefined, + weekStart: user.weekStart ?? undefined, isPro: user.subscription?.active ?? false, }; }); diff --git a/apps/web/src/next-auth.ts b/apps/web/src/next-auth.ts index 63edb07ab..de91f082f 100644 --- a/apps/web/src/next-auth.ts +++ b/apps/web/src/next-auth.ts @@ -4,7 +4,6 @@ import { redirect } from "next/navigation"; import NextAuth from "next-auth"; import type { Provider } from "next-auth/providers"; import { cache } from "react"; -import z from "zod"; import { CustomPrismaAdapter } from "./auth/adapters/prisma"; import { isEmailBanned, isEmailBlocked } from "./auth/helpers/is-email-blocked"; @@ -17,13 +16,6 @@ import { OIDCProvider } from "./auth/providers/oidc"; import { RegistrationTokenProvider } from "./auth/providers/registration-token"; import { nextAuthConfig } from "./next-auth.config"; -const sessionUpdateSchema = z.object({ - locale: z.string().nullish(), - timeFormat: z.enum(["hours12", "hours24"]).nullish(), - timeZone: z.string().nullish(), - weekStart: z.number().nullish(), -}); - const { auth: originalAuth, handlers, @@ -162,43 +154,26 @@ const { return true; }, - async jwt({ token, session }) { - if (session) { - const parsed = sessionUpdateSchema.safeParse(session); - if (parsed.success) { - Object.entries(parsed.data).forEach(([key, value]) => { - token[key] = value; - }); - } else { - console.error(parsed.error); - } - } else { - const userId = token.sub; - const isGuest = userId?.startsWith("guest-"); + async jwt({ token }) { + const userId = token.sub; + const isGuest = userId?.startsWith("guest-"); - if (userId && !isGuest) { - const user = await prisma.user.findUnique({ - where: { - id: userId, - }, - select: { - name: true, - email: true, - timeFormat: true, - timeZone: true, - weekStart: true, - image: true, - }, - }); + if (userId && !isGuest) { + const user = await prisma.user.findUnique({ + where: { + id: userId, + }, + select: { + name: true, + email: true, + image: true, + }, + }); - if (user) { - token.name = user.name; - token.email = user.email; - token.picture = user.image; - token.timeFormat = user.timeFormat; - token.timeZone = user.timeZone; - token.weekStart = user.weekStart; - } + if (user) { + token.name = user.name; + token.email = user.email; + token.picture = user.image; } } diff --git a/apps/web/src/trpc/routers/auth.ts b/apps/web/src/trpc/routers/auth.ts index d7d5b3149..4dcf89d8c 100644 --- a/apps/web/src/trpc/routers/auth.ts +++ b/apps/web/src/trpc/routers/auth.ts @@ -97,6 +97,8 @@ export const auth = router({ token: z.string(), code: z.string(), timeZone: z.string().optional(), + weekStart: z.number().min(0).max(6).optional(), + timeFormat: z.enum(["hours12", "hours24"]).optional(), locale: z.string().optional(), }), ) @@ -118,6 +120,8 @@ export const auth = router({ name, email, timeZone: input.timeZone, + timeFormat: input.timeFormat, + weekStart: input.weekStart, locale: input.locale, }, }); @@ -139,6 +143,9 @@ export const auth = router({ name: user.name, timeZone: input.timeZone, locale: input.locale, + tier: "hobby", + weekStart: input.weekStart, + timeFormat: input.timeFormat, }, }, }); diff --git a/apps/web/src/utils/dayjs.tsx b/apps/web/src/utils/dayjs.tsx index 5ba52c0de..cffab6663 100644 --- a/apps/web/src/utils/dayjs.tsx +++ b/apps/web/src/utils/dayjs.tsx @@ -14,11 +14,11 @@ 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 { useTranslation } from "@/i18n/client"; import { getBrowserTimeZone, normalizeTimeZone } from "@/utils/date-time-utils"; import { useRequiredContext } from "../components/use-required-context"; @@ -203,11 +203,10 @@ export const DayjsProvider: React.FunctionComponent<{ }; }; }> = ({ config, children }) => { - const locale = useParams()?.locale as string; - const l = config?.locale ?? locale ?? "en"; + const locale = config?.locale ?? "en"; const state = useAsync(async () => { - return await dayjsLocales[l].import(); - }, [l]); + return await dayjsLocales[locale].import(); + }, [locale]); const preferredTimeZone = React.useMemo( () => @@ -234,7 +233,7 @@ export const DayjsProvider: React.FunctionComponent<{ } const dayjsLocale = state.value; - const localeConfig = dayjsLocales[l]; + const localeConfig = dayjsLocales[locale]; const localeTimeFormat = localeConfig.timeFormat; if (config?.localeOverrides) { @@ -278,10 +277,11 @@ export const ConnectedDayjsProvider = ({ children, }: React.PropsWithChildren) => { const { preferences } = usePreferences(); + const { i18n } = useTranslation(); return (