mirror of
https://github.com/lukevella/rallly.git
synced 2025-08-03 16:38:34 +02:00
🐛 Fix layout not updated when changing profile (#1674)
This commit is contained in:
parent
7faa698b18
commit
5c2bb835e5
6 changed files with 92 additions and 107 deletions
|
@ -1,10 +1,67 @@
|
|||
import { Button } from "@rallly/ui/button";
|
||||
import { DialogTrigger } from "@rallly/ui/dialog";
|
||||
import { TrashIcon } from "lucide-react";
|
||||
|
||||
import type { Params } from "@/app/[locale]/types";
|
||||
import { Trans } from "@/components/trans";
|
||||
import { getUser } from "@/data/get-user";
|
||||
import { getTranslation } from "@/i18n/server";
|
||||
|
||||
import { ProfilePage } from "./profile-page";
|
||||
import {
|
||||
SettingsContent,
|
||||
SettingsSection,
|
||||
} from "../components/settings-layout";
|
||||
import { DeleteAccountDialog } from "./delete-account-dialog";
|
||||
import { ProfileEmailAddress } from "./profile-email-address";
|
||||
import { ProfileSettings } from "./profile-settings";
|
||||
|
||||
export default async function Page() {
|
||||
return <ProfilePage />;
|
||||
const user = await getUser();
|
||||
return (
|
||||
<SettingsContent>
|
||||
<SettingsSection
|
||||
title={<Trans i18nKey="profile" defaults="Profile" />}
|
||||
description={
|
||||
<Trans
|
||||
i18nKey="profileDescription"
|
||||
defaults="Set your public profile information"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ProfileSettings name={user.name} image={user.image} />
|
||||
</SettingsSection>
|
||||
<SettingsSection
|
||||
title={<Trans i18nKey="profileEmailAddress" defaults="Email Address" />}
|
||||
description={
|
||||
<Trans
|
||||
i18nKey="profileEmailAddressDescription"
|
||||
defaults="Your email address is used to log in to your account"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ProfileEmailAddress />
|
||||
</SettingsSection>
|
||||
<hr />
|
||||
<SettingsSection
|
||||
title={<Trans i18nKey="dangerZone" defaults="Danger Zone" />}
|
||||
description={
|
||||
<Trans
|
||||
i18nKey="dangerZoneAccount"
|
||||
defaults="Delete your account permanently. This action cannot be undone."
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DeleteAccountDialog email={user.email}>
|
||||
<DialogTrigger asChild>
|
||||
<Button className="text-destructive">
|
||||
<TrashIcon className="size-4" />
|
||||
<Trans i18nKey="deleteAccount" defaults="Delete Account" />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
</DeleteAccountDialog>
|
||||
</SettingsSection>
|
||||
</SettingsContent>
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: { params: Params }) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@rallly/ui/alert";
|
||||
|
@ -29,7 +31,7 @@ const emailChangeFormData = z.object({
|
|||
|
||||
type EmailChangeFormData = z.infer<typeof emailChangeFormData>;
|
||||
export const ProfileEmailAddress = () => {
|
||||
const { user, refresh } = useUser();
|
||||
const { user } = useUser();
|
||||
const requestEmailChange = trpc.user.requestEmailChange.useMutation();
|
||||
const posthog = usePostHog();
|
||||
const form = useForm<EmailChangeFormData>({
|
||||
|
@ -78,7 +80,7 @@ export const ProfileEmailAddress = () => {
|
|||
}),
|
||||
});
|
||||
}
|
||||
}, [posthog, refresh, t, toast]);
|
||||
}, [posthog, t, toast]);
|
||||
|
||||
const { handleSubmit, formState, reset } = form;
|
||||
return (
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
"use client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import { DialogTrigger } from "@rallly/ui/dialog";
|
||||
import { TrashIcon } from "lucide-react";
|
||||
|
||||
import { Trans } from "@/components/trans";
|
||||
import { useUser } from "@/components/user-provider";
|
||||
|
||||
import {
|
||||
SettingsContent,
|
||||
SettingsSection,
|
||||
} from "../components/settings-layout";
|
||||
import { DeleteAccountDialog } from "./delete-account-dialog";
|
||||
import { ProfileEmailAddress } from "./profile-email-address";
|
||||
import { ProfileSettings } from "./profile-settings";
|
||||
|
||||
export const ProfilePage = () => {
|
||||
const { user } = useUser();
|
||||
|
||||
return (
|
||||
<SettingsContent>
|
||||
<SettingsSection
|
||||
title={<Trans i18nKey="profile" defaults="Profile" />}
|
||||
description={
|
||||
<Trans
|
||||
i18nKey="profileDescription"
|
||||
defaults="Set your public profile information"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ProfileSettings />
|
||||
</SettingsSection>
|
||||
<SettingsSection
|
||||
title={<Trans i18nKey="profileEmailAddress" defaults="Email Address" />}
|
||||
description={
|
||||
<Trans
|
||||
i18nKey="profileEmailAddressDescription"
|
||||
defaults="Your email address is used to log in to your account"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ProfileEmailAddress />
|
||||
</SettingsSection>
|
||||
<hr />
|
||||
|
||||
{user.email ? (
|
||||
<>
|
||||
<hr />
|
||||
<SettingsSection
|
||||
title={<Trans i18nKey="dangerZone" defaults="Danger Zone" />}
|
||||
description={
|
||||
<Trans
|
||||
i18nKey="dangerZoneAccount"
|
||||
defaults="Delete your account permanently. This action cannot be undone."
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DeleteAccountDialog email={user.email}>
|
||||
<DialogTrigger asChild>
|
||||
<Button className="text-destructive">
|
||||
<TrashIcon className="size-4" />
|
||||
<Trans i18nKey="deleteAccount" defaults="Delete Account" />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
</DeleteAccountDialog>
|
||||
</SettingsSection>
|
||||
</>
|
||||
) : null}
|
||||
</SettingsContent>
|
||||
);
|
||||
};
|
|
@ -1,7 +1,6 @@
|
|||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import { useToast } from "@rallly/ui/hooks/use-toast";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import React, { useState } from "react";
|
||||
import { z } from "zod";
|
||||
|
||||
|
@ -39,12 +38,6 @@ function ChangeAvatarButton({ onSuccess }: { onSuccess: () => void }) {
|
|||
defaultValue: "Please upload a JPG or PNG file.",
|
||||
}),
|
||||
});
|
||||
Sentry.captureMessage("Invalid file type", {
|
||||
level: "info",
|
||||
extra: {
|
||||
fileType: file.type,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -59,12 +52,6 @@ function ChangeAvatarButton({ onSuccess }: { onSuccess: () => void }) {
|
|||
defaultValue: "Please upload a file smaller than 2MB.",
|
||||
}),
|
||||
});
|
||||
Sentry.captureMessage("File too large", {
|
||||
level: "info",
|
||||
extra: {
|
||||
fileSize: file.size,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
setIsUploading(true);
|
||||
|
@ -91,7 +78,7 @@ function ChangeAvatarButton({ onSuccess }: { onSuccess: () => void }) {
|
|||
});
|
||||
|
||||
onSuccess();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast({
|
||||
title: t("errorUploadPicture", {
|
||||
defaultValue: "Failed to upload",
|
||||
|
@ -101,7 +88,6 @@ function ChangeAvatarButton({ onSuccess }: { onSuccess: () => void }) {
|
|||
"There was an issue uploading your picture. Please try again later.",
|
||||
}),
|
||||
});
|
||||
Sentry.captureException(error);
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
}
|
||||
|
@ -183,15 +169,16 @@ function Upload() {
|
|||
);
|
||||
}
|
||||
|
||||
export function ProfilePicture() {
|
||||
const { user } = useUser();
|
||||
export function ProfilePicture({
|
||||
name,
|
||||
image,
|
||||
}: {
|
||||
name: string;
|
||||
image?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center gap-x-4">
|
||||
<OptimizedAvatarImage
|
||||
src={user.image ?? undefined}
|
||||
name={user.name}
|
||||
size="lg"
|
||||
/>
|
||||
<OptimizedAvatarImage src={image} name={name} size="lg" />
|
||||
<IfCloudHosted>
|
||||
<Upload />
|
||||
</IfCloudHosted>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"use client";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import {
|
||||
|
@ -9,11 +10,11 @@ import {
|
|||
FormMessage,
|
||||
} from "@rallly/ui/form";
|
||||
import { Input } from "@rallly/ui/input";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
|
||||
import { Trans } from "@/components/trans";
|
||||
import { useUser } from "@/components/user-provider";
|
||||
import { trpc } from "@/trpc/client";
|
||||
|
||||
import { ProfilePicture } from "./profile-picture";
|
||||
|
@ -24,13 +25,18 @@ const profileSettingsFormData = z.object({
|
|||
|
||||
type ProfileSettingsFormData = z.infer<typeof profileSettingsFormData>;
|
||||
|
||||
export const ProfileSettings = () => {
|
||||
const { user, refresh } = useUser();
|
||||
export const ProfileSettings = ({
|
||||
name,
|
||||
image,
|
||||
}: {
|
||||
name: string;
|
||||
image?: string;
|
||||
}) => {
|
||||
const changeName = trpc.user.changeName.useMutation();
|
||||
|
||||
const router = useRouter();
|
||||
const form = useForm<ProfileSettingsFormData>({
|
||||
defaultValues: {
|
||||
name: user.isGuest ? "" : user.name,
|
||||
name,
|
||||
},
|
||||
resolver: zodResolver(profileSettingsFormData),
|
||||
});
|
||||
|
@ -41,15 +47,15 @@ export const ProfileSettings = () => {
|
|||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={handleSubmit(async (data) => {
|
||||
if (data.name !== user.name) {
|
||||
if (data.name !== name) {
|
||||
await changeName.mutateAsync({ name: data.name });
|
||||
}
|
||||
reset(data);
|
||||
await refresh();
|
||||
router.refresh();
|
||||
})}
|
||||
>
|
||||
<div className="flex flex-col gap-y-4">
|
||||
<ProfilePicture />
|
||||
<ProfilePicture name={name} image={image} />
|
||||
<FormField
|
||||
control={control}
|
||||
name="name"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"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 React from "react";
|
||||
|
@ -63,7 +64,7 @@ export const UserProvider = (props: { children?: React.ReactNode }) => {
|
|||
const subscription = useSubscription();
|
||||
const updatePreferences = trpc.user.updatePreferences.useMutation();
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
const router = useRouter();
|
||||
const posthog = usePostHog();
|
||||
|
||||
const isGuest = !user?.email;
|
||||
|
@ -96,7 +97,10 @@ export const UserProvider = (props: { children?: React.ReactNode }) => {
|
|||
image: user?.image ?? null,
|
||||
locale: user?.locale ?? i18n.language,
|
||||
},
|
||||
refresh: session.update,
|
||||
refresh: async (data) => {
|
||||
router.refresh();
|
||||
return await session.update(data);
|
||||
},
|
||||
logout: async () => {
|
||||
await signOut();
|
||||
posthog?.capture("logout");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue