This commit is contained in:
Luke Vella 2025-04-23 15:13:23 +01:00
parent f838c5401a
commit 22c616e4a0
No known key found for this signature in database
GPG key ID: 469CAD687F0D784C
6 changed files with 78 additions and 100 deletions

View file

@ -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,
@ -42,7 +41,6 @@ export function OTPForm({ token }: { token: string }) {
const locale = i18n.language;
const queryClient = trpc.useUtils();
const posthog = usePostHog();
const authenticateRegistration =
trpc.auth.authenticateRegistration.useMutation();
const searchParams = useSearchParams();
@ -64,11 +62,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") ?? "/",

View file

@ -27,8 +27,8 @@ const formSchema = z.object({
type FormData = z.infer<typeof formSchema>;
const DateTimePreferencesForm = () => {
const { timeFormat, weekStart, timeZone, locale } = useDayjs();
const { preferences, updatePreferences } = usePreferences();
const { timeFormat, weekStart, timeZone } = useDayjs();
const { updatePreferences } = usePreferences();
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
@ -126,25 +126,6 @@ const DateTimePreferencesForm = () => {
>
<Trans i18nKey="save" />
</Button>
{preferences.timeFormat || preferences.weekStart ? (
<Button
onClick={async () => {
updatePreferences({
weekStart: null,
timeFormat: null,
});
form.reset({
weekStart: locale.weekStart,
timeFormat: locale.timeFormat,
});
}}
>
<Trans
defaults="Use locale defaults"
i18nKey="useLocaleDefaults"
/>
</Button>
) : null}
</div>
</form>
</Form>

View file

@ -12,6 +12,7 @@ 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";
@ -64,27 +65,42 @@ export default async function Root({
<PostHogPageView />
<TooltipProvider>
<UserProvider
user={{
id: user?.id ?? session?.user?.id ?? "",
name: user?.name ?? session?.user?.name ?? "",
email: user?.email ?? session?.user?.email ?? undefined,
tier: user ? (user.isPro ? "pro" : "hobby") : "guest",
timeZone:
user?.timeZone ?? session?.user?.timeZone ?? undefined,
timeFormat: session?.user?.timeFormat ?? undefined,
weekStart: session?.user?.weekStart ?? undefined,
image: session?.user?.image ?? undefined,
locale: session?.user?.locale ?? undefined,
}}
user={
user
? {
id: user.id,
name: user.name,
email: user.email,
tier: user
? user.isPro
? "pro"
: "hobby"
: "guest",
image: user.image,
}
: session?.user
? {
id: session.user.id,
tier: "guest",
}
: undefined
}
>
<TimezoneProvider
initialTimezone={session?.user?.timeZone ?? undefined}
<PreferencesProvider
initialValue={{
timeFormat: user?.timeFormat,
timeZone: user?.timeZone,
locale: user?.locale,
weekStart: user?.weekStart,
}}
>
<ConnectedDayjsProvider>
{children}
<TimeZoneChangeDetector />
</ConnectedDayjsProvider>
</TimezoneProvider>
<TimezoneProvider initialTimezone={user?.timeZone}>
<ConnectedDayjsProvider>
{children}
<TimeZoneChangeDetector />
</ConnectedDayjsProvider>
</TimezoneProvider>
</PreferencesProvider>
</UserProvider>
</TooltipProvider>
</PostHogProvider>

View file

@ -5,9 +5,7 @@ import { 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";
@ -15,14 +13,10 @@ 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<{
@ -58,30 +52,37 @@ export const IfGuest = (props: { children?: React.ReactNode }) => {
return <>{props.children}</>;
};
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?: {
id: string;
name: string;
email?: string;
tier: "guest" | "hobby" | "pro";
timeZone?: string;
timeFormat?: "hours12" | "hours24";
weekStart?: number;
image?: string;
locale?: string;
};
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(() => {
@ -90,9 +91,7 @@ export const UserProvider = ({
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
@ -104,12 +103,10 @@ export const UserProvider = ({
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,
},
isAuthenticated: !!user,
refresh: router.refresh,
@ -123,27 +120,7 @@ export const UserProvider = ({
},
}}
>
<PreferencesProvider
initialValue={{
locale: user?.locale ?? undefined,
timeZone: user?.timeZone ?? undefined,
timeFormat: user?.timeFormat ?? undefined,
weekStart: user?.weekStart ?? undefined,
}}
onUpdate={async (newPreferences) => {
if (!isGuest) {
await updatePreferences.mutateAsync({
locale: newPreferences.locale ?? undefined,
timeZone: newPreferences.timeZone ?? undefined,
timeFormat: newPreferences.timeFormat ?? undefined,
weekStart: newPreferences.weekStart ?? undefined,
});
}
router.refresh();
}}
>
{children}
</PreferencesProvider>
{children}
</UserContext.Provider>
);
};

View file

@ -1,8 +1,11 @@
"use client";
import type { TimeFormat } from "@rallly/database";
import React from "react";
import { useSetState } from "react-use";
import { useRequiredContext } from "@/components/use-required-context";
import { trpc } from "@/trpc/client";
type Preferences = {
timeZone?: string | null;
@ -23,13 +26,12 @@ const PreferencesContext = React.createContext<PreferencesContextValue | null>(
export const PreferencesProvider = ({
children,
initialValue,
onUpdate,
}: {
children?: React.ReactNode;
initialValue: Partial<Preferences>;
onUpdate?: (preferences: Partial<Preferences>) => Promise<void>;
}) => {
const [preferences, setPreferences] = useSetState<Preferences>(initialValue);
const updatePreferences = trpc.user.updatePreferences.useMutation();
return (
<PreferencesContext.Provider
@ -37,7 +39,12 @@ export const PreferencesProvider = ({
preferences,
updatePreferences: async (newPreferences) => {
setPreferences(newPreferences);
await onUpdate?.(newPreferences);
await updatePreferences.mutateAsync({
locale: newPreferences.locale ?? undefined,
timeZone: newPreferences.timeZone ?? undefined,
timeFormat: newPreferences.timeFormat ?? undefined,
weekStart: newPreferences.weekStart ?? undefined,
});
},
}}
>

View file

@ -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,
};
});