profile page fetch from db

This commit is contained in:
Kalle (Sendou)
2020-11-06 14:15:22 +02:00
parent 9dd810798c
commit d3ff78a9d0
7 changed files with 138 additions and 57 deletions

View File

@@ -7,7 +7,7 @@ import {
stringArg,
} from "@nexus/schema";
import { getMySession } from "lib/getMySession";
import { profileSchemaBackend } from "validators/profile";
import { profileSchemaBackend } from "lib/validators/profile";
export const User = objectType({
name: "User",

View File

@@ -12,3 +12,19 @@ export const getLocalizedMonthYearString = (
year: "numeric",
});
};
export const getFullUsername = ({
username,
discriminator,
}: {
username: string;
discriminator: string;
}) => `${username}#${discriminator}`;
export const getDiscordAvatarUrl = ({
discordId,
discordAvatar,
}: {
discordId: string;
discordAvatar: string;
}) => `https://cdn.discordapp.com/avatars/${discordId}/${discordAvatar}.jpg`;

View File

@@ -10,7 +10,7 @@ import {
useGetXRankPlacementsQuery,
} from "generated/graphql";
import { initializeApollo } from "lib/apollo";
import { getLocalizedMonthYearString } from "lib/getLocalizedMontYearString";
import { getLocalizedMonthYearString } from "lib/strings";
import { GetStaticProps } from "next";
import { useState } from "react";
import XSearch from "scenes/Top500";

View File

@@ -1,59 +1,96 @@
import { Box, Button, Divider } from "@chakra-ui/core";
import { t, Trans } from "@lingui/macro";
import { PrismaClient } from "@prisma/client";
import {
GetUserByIdentifierDocument,
useGetUserByIdentifierQuery,
} from "generated/graphql";
import { initializeApollo } from "lib/apollo";
import Breadcrumbs from "components/Breadcrumbs";
import Markdown from "components/Markdown";
import { getFullUsername } from "lib/strings";
import useUser from "lib/useUser";
import { GetStaticPaths, GetStaticProps } from "next";
import { useRouter } from "next/router";
import Profile from "scenes/Profile";
import {
getUserByIdentifier,
GetUserByIdentifierData,
} from "prisma/queries/getUserByIdentifier";
import { useEffect, useState } from "react";
import AvatarWithInfo from "scenes/Profile/components/AvatarWithInfo";
const prisma = new PrismaClient();
// FIXME: should try to make it so that /u/Sendou and /u/234234298348 point to the same page
export const getStaticPaths: GetStaticPaths = async () => {
const users = await prisma.user.findMany({});
const users = await prisma.user.findMany({ include: { profile: true } });
return {
paths: users.map((u) => ({ params: { identifier: u.discordId } })),
paths: users.flatMap((u) =>
u.profile?.customUrlPath
? [
{ params: { identifier: u.discordId } },
{ params: { identifier: u.profile.customUrlPath } },
]
: { params: { identifier: u.discordId } }
),
fallback: true,
};
};
export const getStaticProps: GetStaticProps = async ({ params }) => {
const apolloClient = initializeApollo(null, { prisma: new PrismaClient() });
interface Props {
user: GetUserByIdentifierData;
}
await apolloClient.query({
query: GetUserByIdentifierDocument,
variables: {
// FIXME: why ! needed?
identifier: params!.identifier,
},
});
export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {
const user = await getUserByIdentifier(prisma, "tester");
//const isCustomUrl = isNaN(Number(params!.identifier))
return {
props: {
initialApolloState: apolloClient.cache.extract(),
identifier: params!.identifier,
user,
},
revalidate: 1,
notFound: !user,
//redirect: isCustomUrl ? { destination: "" } : undefined,
};
};
const ProfilePage = ({ identifier }: { identifier: string }) => {
const { data } = useGetUserByIdentifierQuery({
variables: { identifier },
});
const router = useRouter();
const ProfilePage = (props: Props) => {
const [user, setUser] = useState(props.user);
const [showModal, setShowModal] = useState(false);
// FIXME: handle fallback
const getUserByIdentifier = data?.getUserByIdentifier;
if (!getUserByIdentifier && typeof window !== "undefined") {
router.push("/404");
}
useEffect(() => {
setUser(user);
}, [user]);
return getUserByIdentifier ? (
<Profile user={getUserByIdentifier} identifier={identifier} />
) : null;
const [loggedInUser] = useUser();
// same as router.isFallback
if (!user) return null;
return (
<>
<Breadcrumbs
pages={[
{ name: t`Users`, link: "/u" },
{ name: getFullUsername(user) },
]}
/>
<AvatarWithInfo user={user} />
{loggedInUser?.id === user.id && (
<Button onClick={() => setShowModal(true)}>
<Trans>Edit profile</Trans>
</Button>
)}
{/* {showModal && (
<ProfileModal
onClose={() => setShowModal(false)}
existingProfile={user.profile}
/>
)} */}
<Divider my="2em" />
{user.profile?.bio && (
<Box>
<Markdown value={user.profile.bio} />
</Box>
)}
</>
);
};
export default ProfilePage;

View File

@@ -0,0 +1,37 @@
import { PrismaClient } from "@prisma/client";
type Unwrap<T> = T extends Promise<infer U>
? U
: T extends (...args: any) => Promise<infer U>
? U
: T extends (...args: any) => infer U
? U
: T;
export type GetUserByIdentifierData = Unwrap<
ReturnType<typeof getUserByIdentifier>
>;
export const getUserByIdentifier = async (
prisma: PrismaClient,
identifier: string
) => {
return prisma.user.findFirst({
where: {
// this is ok because the values are mutually exclusive: customUrlPath can't contain only numbers etc.
OR: [
{
discordId: identifier,
},
{
profile: {
customUrlPath: identifier.toLowerCase(),
},
},
],
},
include: {
profile: true,
},
});
};

View File

@@ -1,13 +1,14 @@
import { Avatar, Box, Flex, Heading, IconButton } from "@chakra-ui/core";
import WeaponImage from "components/WeaponImage";
import { getEmojiFlag } from "countries-list";
import { GetUserByIdentifierQuery } from "generated/graphql";
import { getDiscordAvatarUrl, getFullUsername } from "lib/strings";
import { useTranslation } from "lib/useMockT";
import { useMyTheme } from "lib/useMyTheme";
import { GetUserByIdentifierData } from "prisma/queries/getUserByIdentifier";
import { FaGamepad, FaTwitch, FaTwitter, FaYoutube } from "react-icons/fa";
interface AvatarWithInfoProps {
user: NonNullable<GetUserByIdentifierQuery["getUserByIdentifier"]>;
user: NonNullable<GetUserByIdentifierData>;
}
const AvatarWithInfo: React.FC<AvatarWithInfoProps> = ({ user }) => {
@@ -40,15 +41,22 @@ const AvatarWithInfo: React.FC<AvatarWithInfoProps> = ({ user }) => {
>
<Avatar
data-cy="profile-page-avatar"
name={user.fullUsername}
src={user.avatarUrl ?? ""}
name={getFullUsername(user)}
src={
user.discordAvatar
? getDiscordAvatarUrl({
discordId: user.discordId,
discordAvatar: user.discordAvatar,
})
: undefined
}
size="2xl"
mb="0.5rem"
/>
<Flex flexDirection="column" justifyContent="center" mb="0.5rem">
<Flex alignItems="center" justifyContent="center" my="0.2rem">
<Heading fontFamily="'Rubik', sans-serif" size="lg">
{user.fullUsername}
{getFullUsername(user)}
</Heading>
{user.profile?.country && (
<Box as="span" ml={2}>

View File

@@ -40,23 +40,6 @@ const Profile: React.FC<Props> = ({ user, identifier }) => {
<Markdown value={user.profile.bio} />
</Box>
)}
{/*FIXME:
<Tabs isFitted variant="line" mt="2em" colorScheme={themeColor}>
<TabList mb="1em">
{tabs.map((tabObj) => (
<Tab key={tabObj.id} color={textColor}>
<Box
as={tabObj.icon}
size="24px"
color={themeColorWithShade}
mr="7px"
/>{" "}
{tabObj.title}
</Tab>
))}
</TabList>
<TabPanels>{tabs.map((tabObj) => tabObj.content)}</TabPanels>
</Tabs>*/}
</>
);
};