mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-01 00:24:13 -05:00
salmon run player pages
This commit is contained in:
parent
5e88655615
commit
23e4f7b720
29
components/common/LinkButton.tsx
Normal file
29
components/common/LinkButton.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { IconButton } from "@chakra-ui/react";
|
||||
import { FiLink, FiTwitter, FiYoutube } from "react-icons/fi";
|
||||
|
||||
const LinkButton = ({ link }: { link: string }) => {
|
||||
return (
|
||||
<a key={link} href={link}>
|
||||
<IconButton
|
||||
aria-label={`Link to ${link}`}
|
||||
icon={<LinkIcon link={link} />}
|
||||
isRound
|
||||
variant="ghost"
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
function LinkIcon({ link }: { link: string }) {
|
||||
if (link.includes("youtube") || link.includes("youtu.be")) {
|
||||
return <FiYoutube />;
|
||||
}
|
||||
|
||||
if (link.includes("twitter")) {
|
||||
return <FiTwitter />;
|
||||
}
|
||||
|
||||
return <FiLink />;
|
||||
}
|
||||
|
||||
export default LinkButton;
|
||||
|
|
@ -5,6 +5,7 @@ const Section: React.FC<BoxProps> = (props) => {
|
|||
const { secondaryBgColor } = useMyTheme();
|
||||
return (
|
||||
<Box
|
||||
as="section"
|
||||
bg={secondaryBgColor}
|
||||
boxShadow="0px 0px 16px 6px rgba(0,0,0,0.1)"
|
||||
p="1.5rem"
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ import { countries, getEmojiFlag } from "countries-list";
|
|||
import { getFullUsername } from "lib/strings";
|
||||
import { useMyTheme } from "lib/useMyTheme";
|
||||
import useUser from "lib/useUser";
|
||||
import Image from "next/image";
|
||||
import { GetUserByIdentifierData } from "prisma/queries/getUserByIdentifier";
|
||||
import { FaGamepad, FaTwitch, FaTwitter, FaYoutube } from "react-icons/fa";
|
||||
import { FiInfo } from "react-icons/fi";
|
||||
import { RiFileTextLine, RiTrophyLine } from "react-icons/ri";
|
||||
|
||||
interface AvatarWithInfoProps {
|
||||
user: NonNullable<GetUserByIdentifierData>;
|
||||
|
|
@ -199,12 +199,14 @@ const AvatarWithInfo: React.FC<AvatarWithInfoProps> = ({
|
|||
href={`/player/${user.player?.switchAccountId}`}
|
||||
prefetch={true}
|
||||
>
|
||||
<Button
|
||||
leftIcon={<RiTrophyLine />}
|
||||
variant="outline"
|
||||
mx={2}
|
||||
>
|
||||
<Trans>View results</Trans>
|
||||
<Button variant="outline" mx={2} size="lg" w={24}>
|
||||
<Image
|
||||
src="/layout/xsearch.png"
|
||||
height={48}
|
||||
width={48}
|
||||
alt="SR"
|
||||
priority
|
||||
/>
|
||||
</Button>
|
||||
</MyLink>
|
||||
)}
|
||||
|
|
@ -213,12 +215,27 @@ const AvatarWithInfo: React.FC<AvatarWithInfoProps> = ({
|
|||
href={`/freeagents?id=${user.freeAgentPost.id}`}
|
||||
prefetch={true}
|
||||
>
|
||||
<Button
|
||||
leftIcon={<RiFileTextLine />}
|
||||
variant="outline"
|
||||
mx={2}
|
||||
>
|
||||
<Trans>View free agent post</Trans>
|
||||
<Button variant="outline" mx={2} size="lg" w={24}>
|
||||
<Image
|
||||
src="/layout/freeagents.png"
|
||||
height={48}
|
||||
width={48}
|
||||
alt="SR"
|
||||
priority
|
||||
/>
|
||||
</Button>
|
||||
</MyLink>
|
||||
)}
|
||||
{true && (
|
||||
<MyLink href={`/sr/player/${user.id}`} prefetch={true}>
|
||||
<Button variant="outline" mx={2} size="lg" w={24}>
|
||||
<Image
|
||||
src="/layout/sr.png"
|
||||
height={48}
|
||||
width={48}
|
||||
alt="SR"
|
||||
priority
|
||||
/>
|
||||
</Button>
|
||||
</MyLink>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import {
|
|||
Checkbox,
|
||||
CheckboxGroup,
|
||||
Flex,
|
||||
IconButton,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Select,
|
||||
|
|
@ -17,6 +16,7 @@ import { Plural, t, Trans } from "@lingui/macro";
|
|||
import { useLingui } from "@lingui/react";
|
||||
import { SalmonRunRecordCategory } from "@prisma/client";
|
||||
import Breadcrumbs from "components/common/Breadcrumbs";
|
||||
import LinkButton from "components/common/LinkButton";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
|
|
@ -32,7 +32,6 @@ import { salmonRunStages } from "lib/lists/stages";
|
|||
import { getRankingString } from "lib/strings";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { FiLink, FiTwitter, FiYoutube } from "react-icons/fi";
|
||||
import { salmonRunCategoryToNatural } from "./new";
|
||||
|
||||
const SalmonRunLeaderboardsPage = ({}) => {
|
||||
|
|
@ -232,14 +231,7 @@ const SalmonRunLeaderboardsPage = ({}) => {
|
|||
|
||||
<TableCell textAlign="center">
|
||||
{record.links.map((link) => (
|
||||
<a key={link} href={link}>
|
||||
<IconButton
|
||||
aria-label={`Link to ${link}`}
|
||||
icon={<LinkIcon link={link} />}
|
||||
isRound
|
||||
variant="ghost"
|
||||
/>
|
||||
</a>
|
||||
<LinkButton key={link} link={link} />
|
||||
))}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
|
@ -255,16 +247,4 @@ const SalmonRunLeaderboardsPage = ({}) => {
|
|||
);
|
||||
};
|
||||
|
||||
function LinkIcon({ link }: { link: string }) {
|
||||
if (link.includes("youtube") || link.includes("youtu.be")) {
|
||||
return <FiYoutube />;
|
||||
}
|
||||
|
||||
if (link.includes("twitter")) {
|
||||
return <FiTwitter />;
|
||||
}
|
||||
|
||||
return <FiLink />;
|
||||
}
|
||||
|
||||
export default SalmonRunLeaderboardsPage;
|
||||
|
|
|
|||
182
pages/sr/player/[id].tsx
Normal file
182
pages/sr/player/[id].tsx
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
import { Box, Flex, Heading, Wrap, WrapItem } from "@chakra-ui/react";
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import Breadcrumbs from "components/common/Breadcrumbs";
|
||||
import LinkButton from "components/common/LinkButton";
|
||||
import MyContainer from "components/common/MyContainer";
|
||||
import MyLink from "components/common/MyLink";
|
||||
import Section from "components/common/Section";
|
||||
import SubText from "components/common/SubText";
|
||||
import UserAvatar from "components/common/UserAvatar";
|
||||
import WeaponImage from "components/common/WeaponImage";
|
||||
import { salmonRunStages } from "lib/lists/stages";
|
||||
import { useMyTheme } from "lib/useMyTheme";
|
||||
import { GetStaticPaths, GetStaticProps } from "next";
|
||||
import Image from "next/image";
|
||||
import { salmonRunCategoryToNatural } from "pages/sr/leaderboards/new";
|
||||
import {
|
||||
getUsersSalmonRunRecords,
|
||||
GetUsersSalmonRunRecordsData,
|
||||
} from "prisma/queries/getUsersSalmonRunRecords";
|
||||
|
||||
interface Props {
|
||||
user: GetUsersSalmonRunRecordsData;
|
||||
}
|
||||
|
||||
const SalmonRunPlayerPage = (props: Props) => {
|
||||
const { gray } = useMyTheme();
|
||||
const user = props.user!;
|
||||
|
||||
return (
|
||||
<MyContainer>
|
||||
<Breadcrumbs
|
||||
pages={[
|
||||
{ name: t`Salmon Run` },
|
||||
{ name: `${user.username}#${user.discriminator}` },
|
||||
]}
|
||||
/>
|
||||
<Flex align="center">
|
||||
<UserAvatar user={user} size="xl" mr={4} />
|
||||
<Box>
|
||||
<Box
|
||||
fontSize="2rem"
|
||||
fontWeight="bold
|
||||
"
|
||||
>
|
||||
{user.username}#{user.discriminator}
|
||||
</Box>
|
||||
<SubText ml="1px" mt="-6px">
|
||||
<Trans>Salmon Run Records</Trans>
|
||||
</SubText>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box>
|
||||
{Object.keys(salmonRunCategoryToNatural)
|
||||
.filter((key) =>
|
||||
user.salmonRunRecords.some((record) => record.category === key)
|
||||
)
|
||||
.map((category) => {
|
||||
return (
|
||||
<Box key={category} mt={4}>
|
||||
<Heading as="h2" size="md" fontFamily="'Rubik', sans-serif">
|
||||
{/* @ts-ignore */}
|
||||
{salmonRunCategoryToNatural[category]}
|
||||
</Heading>
|
||||
<Wrap spacing={4} mt={4}>
|
||||
{salmonRunStages
|
||||
.filter((stage) =>
|
||||
user.salmonRunRecords.some(
|
||||
(record) =>
|
||||
record.category === category &&
|
||||
record.rotation.stage === stage
|
||||
)
|
||||
)
|
||||
.map(
|
||||
(stage) =>
|
||||
user.salmonRunRecords.find(
|
||||
(record) =>
|
||||
record.category === category &&
|
||||
record.rotation.stage === stage
|
||||
)!
|
||||
)
|
||||
.map((record) => (
|
||||
<WrapItem key={record.id}>
|
||||
<Section>
|
||||
<SubText mb={2} mt={2}>
|
||||
{record.rotation.stage}
|
||||
</SubText>
|
||||
<Box color={gray} fontSize="sm" my={2}>
|
||||
{new Date(
|
||||
record.rotation.startTime
|
||||
).toLocaleDateString()}{" "}
|
||||
-{" "}
|
||||
{new Date(
|
||||
record.rotation.endTime
|
||||
).toLocaleDateString()}
|
||||
</Box>
|
||||
<Flex align="center" my={2}>
|
||||
<Box mr={1} fontSize="1.25rem" fontWeight="bold">
|
||||
{record.goldenEggCount}
|
||||
</Box>
|
||||
<Image
|
||||
src="/layout/sr.png"
|
||||
height={30}
|
||||
width={30}
|
||||
alt="SR"
|
||||
priority
|
||||
/>
|
||||
</Flex>
|
||||
<Flex my={2}>
|
||||
{record.rotation.weapons.map((wpn, i) => (
|
||||
<Box key={i} mr={1}>
|
||||
<WeaponImage size={32} name={wpn} />
|
||||
</Box>
|
||||
))}
|
||||
</Flex>
|
||||
{record.roster.length > 1 && (
|
||||
<Flex my={2}>
|
||||
{record.roster
|
||||
.filter(
|
||||
(rosterUser) =>
|
||||
rosterUser.discordId !== user.discordId
|
||||
)
|
||||
.map((user) => (
|
||||
<Box key={user.discordId} mr={1}>
|
||||
<MyLink href={`/u/${user.discordId}`}>
|
||||
<UserAvatar user={user} isSmall mr={1} />
|
||||
</MyLink>
|
||||
</Box>
|
||||
))}
|
||||
</Flex>
|
||||
)}
|
||||
<Flex mt={2}>
|
||||
{record.links.map((link) => (
|
||||
<LinkButton link={link} />
|
||||
))}
|
||||
</Flex>
|
||||
</Section>
|
||||
</WrapItem>
|
||||
))}
|
||||
</Wrap>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</MyContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
return {
|
||||
paths: [],
|
||||
fallback: "blocking",
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {
|
||||
const user = await getUsersSalmonRunRecords(parseInt(params!.id as string));
|
||||
|
||||
if (!user || !user.salmonRunRecords.length) return { notFound: true };
|
||||
|
||||
const categoryStages = new Set<string>();
|
||||
|
||||
user.salmonRunRecords = user.salmonRunRecords
|
||||
.sort((a, b) => b.goldenEggCount - a.goldenEggCount)
|
||||
.filter((record) => {
|
||||
if (!record.approved) return false;
|
||||
|
||||
const id = record.category + record.rotation.stage;
|
||||
if (categoryStages.has(id)) return false;
|
||||
|
||||
categoryStages.add(id);
|
||||
return true;
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
user: JSON.parse(JSON.stringify(user)),
|
||||
},
|
||||
revalidate: 1,
|
||||
};
|
||||
};
|
||||
|
||||
export default SalmonRunPlayerPage;
|
||||
43
prisma/queries/getUsersSalmonRunRecords.ts
Normal file
43
prisma/queries/getUsersSalmonRunRecords.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { Prisma } from "@prisma/client";
|
||||
import prisma from "prisma/client";
|
||||
|
||||
export type GetUsersSalmonRunRecordsData = Prisma.PromiseReturnType<
|
||||
typeof getUsersSalmonRunRecords
|
||||
>;
|
||||
|
||||
export const getUsersSalmonRunRecords = async (id: number) =>
|
||||
prisma.user.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
discordId: true,
|
||||
discordAvatar: true,
|
||||
username: true,
|
||||
discriminator: true,
|
||||
salmonRunRecords: {
|
||||
select: {
|
||||
id: true,
|
||||
category: true,
|
||||
goldenEggCount: true,
|
||||
links: true,
|
||||
approved: true,
|
||||
rotation: {
|
||||
select: {
|
||||
grizzcoWeapon: true,
|
||||
stage: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
weapons: true,
|
||||
},
|
||||
},
|
||||
roster: {
|
||||
select: {
|
||||
discordId: true,
|
||||
discordAvatar: true,
|
||||
username: true,
|
||||
discriminator: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user