Move user page data loading to layout route

This commit is contained in:
Kalle 2022-05-16 22:56:47 +03:00
parent 185295d54e
commit f2d547fa01
3 changed files with 78 additions and 81 deletions

View File

@ -1,13 +1,72 @@
import { Outlet } from "@remix-run/react";
import type { LoaderFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { Outlet, useLoaderData } from "@remix-run/react";
import { countries } from "countries-list";
import { z } from "zod";
import { Main } from "~/components/Main";
import { SubNav, SubNavLink } from "~/components/SubNav";
import { db } from "~/db";
import type { User } from "~/db/types";
import { useUser } from "~/hooks/useUser";
import { notFoundIfFalsy } from "~/utils/remix";
export const userParamsSchema = z.object({ identifier: z.string() });
export type UserPageLoaderData = Pick<
User,
| "id"
| "discordName"
| "discordAvatar"
| "discordDiscriminator"
| "discordId"
| "youtubeId"
| "twitch"
| "twitter"
> & { country?: { name: string; emoji: string; code: string } };
export const loader: LoaderFunction = ({ params }) => {
const { identifier } = userParamsSchema.parse(params);
const user = notFoundIfFalsy(db.users.findByIdentifier(identifier));
const countryObj = user.country
? countries[user.country as keyof typeof countries]
: undefined;
return json<UserPageLoaderData>({
id: user.id,
discordAvatar: user.discordAvatar,
discordDiscriminator: user.discordDiscriminator,
discordId: user.discordId,
discordName: user.discordName,
twitch: user.twitch,
twitter: user.twitter,
youtubeId: user.youtubeId,
country:
countryObj && user.country
? {
name: countryObj.name,
emoji: countryObj.emoji,
code: user.country,
}
: undefined,
});
};
export default function UserPageLayout() {
const data = useLoaderData<UserPageLoaderData>();
const user = useUser();
const isOwnPage = data.id === user?.id;
return (
<>
<SubNav>
<SubNavLink to="">Profile</SubNavLink>
<SubNavLink to="edit">Edit</SubNavLink>
{isOwnPage ? (
<SubNavLink to="edit" data-cy="edit-page-link">
Edit
</SubNavLink>
) : null}
</SubNav>
<Main>
<Outlet />

View File

@ -1,23 +1,13 @@
import type {
ActionFunction,
LinksFunction,
LoaderFunction,
} from "@remix-run/node";
import { json } from "@remix-run/node";
import { Form, useLoaderData, useTransition } from "@remix-run/react";
import type { ActionFunction, LinksFunction } from "@remix-run/node";
import { Form, useMatches, useTransition } from "@remix-run/react";
import { countries } from "countries-list";
import { Button } from "~/components/Button";
import styles from "~/styles/u-edit.css";
import { z } from "zod";
import { falsyToNull } from "~/utils/zod";
import {
notFoundIfFalsy,
parseRequestFormData,
requireUser,
} from "~/utils/remix";
import { Button } from "~/components/Button";
import { db } from "~/db";
import type { User } from "~/db/types";
import { userParamsSchema } from "./index";
import styles from "~/styles/u-edit.css";
import { parseRequestFormData, requireUser } from "~/utils/remix";
import { falsyToNull } from "~/utils/zod";
import type { UserPageLoaderData } from "../u.$identifier";
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
@ -47,20 +37,9 @@ export const action: ActionFunction = async ({ request }) => {
return null;
};
type UserEditLoaderData = Pick<User, "country">;
export const loader: LoaderFunction = async ({ request, params }) => {
const user = await requireUser(request);
const { identifier } = userParamsSchema.parse(params);
const userFromDb = notFoundIfFalsy(db.users.findByIdentifier(identifier));
if (userFromDb.id !== user.id) throw new Response(null, { status: 401 });
return json<UserEditLoaderData>({ country: userFromDb?.country });
};
export default function UserEditPage() {
const data = useLoaderData<UserEditLoaderData>();
const [, parentRoute] = useMatches();
const data = parentRoute.data as UserPageLoaderData;
const transition = useTransition();
return (
@ -71,7 +50,7 @@ export default function UserEditPage() {
className="u-edit__country-select"
name="country"
id="country"
defaultValue={data.country ?? ""}
defaultValue={data.country?.code ?? ""}
>
<option value="" />
{Object.entries(countries).map(([code, country]) => (

View File

@ -1,59 +1,18 @@
import type { LinksFunction, LoaderFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { db } from "~/db";
import { z } from "zod";
import type { User } from "~/db/types";
import { notFoundIfFalsy } from "~/utils/remix";
import { useLoaderData } from "@remix-run/react";
import type { LinksFunction } from "@remix-run/node";
import { useMatches } from "@remix-run/react";
import { Avatar } from "~/components/Avatar";
import styles from "~/styles/u.css";
import { SocialLink } from "~/components/u/SocialLink";
import { countries } from "countries-list";
import styles from "~/styles/u.css";
import type { UserPageLoaderData } from "../u.$identifier";
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
};
export const userParamsSchema = z.object({ identifier: z.string() });
type UserPageLoaderData = Pick<
User,
| "discordName"
| "discordAvatar"
| "discordDiscriminator"
| "discordId"
| "youtubeId"
| "twitch"
| "twitter"
> & { country?: { name: string; emoji: string } };
export const loader: LoaderFunction = ({ params }) => {
const { identifier } = userParamsSchema.parse(params);
const user = notFoundIfFalsy(db.users.findByIdentifier(identifier));
const countryObj = user.country
? countries[user.country as keyof typeof countries]
: undefined;
return json<UserPageLoaderData>({
discordAvatar: user.discordAvatar,
discordDiscriminator: user.discordDiscriminator,
discordId: user.discordId,
discordName: user.discordName,
twitch: user.twitch,
twitter: user.twitter,
youtubeId: user.youtubeId,
country: countryObj
? {
name: countryObj.name,
emoji: countryObj.emoji,
}
: undefined,
});
};
export default function UserInfoPage() {
const data = useLoaderData<UserPageLoaderData>();
const [, parentRoute] = useMatches();
const data = parentRoute.data as UserPageLoaderData;
return (
<div className="u__avatar-container">
<Avatar