mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-02 16:20:36 -05:00
profile page fetch from db
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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`;
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
37
prisma/queries/getUserByIdentifier.ts
Normal file
37
prisma/queries/getUserByIdentifier.ts
Normal 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,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -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}>
|
||||
|
||||
@@ -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>*/}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user