mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-21 22:53:35 -05:00
free agents infinite scroll
This commit is contained in:
parent
845821bf16
commit
011f75a785
|
|
@ -1,7 +1,5 @@
|
|||
import React, { useContext } from "react"
|
||||
import React from "react"
|
||||
import "./Pagination.css"
|
||||
import { Box } from "@chakra-ui/core"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
import ReactPaginate from "react-paginate"
|
||||
|
||||
interface PaginationProps {
|
||||
|
|
@ -10,76 +8,26 @@ interface PaginationProps {
|
|||
onChange: (page: number) => void
|
||||
}
|
||||
|
||||
const Pagination: React.FC<PaginationProps> = ({
|
||||
pageCount,
|
||||
currentPage,
|
||||
onChange,
|
||||
}) => {
|
||||
const { colorMode, themeColorHex } = useContext(MyThemeContext)
|
||||
const Pagination: React.FC<PaginationProps> = ({ pageCount, onChange }) => {
|
||||
return (
|
||||
<>
|
||||
{/*<Box className="pagination">
|
||||
<Box
|
||||
as="span"
|
||||
style={{
|
||||
color: colorMode === "light" ? "black" : "white",
|
||||
borderBottomColor: themeColorHex,
|
||||
}}
|
||||
onClick={() => onChange(currentPage === 1 ? 1 : currentPage - 1)}
|
||||
>
|
||||
«
|
||||
</Box>
|
||||
{[...Array(Math.min(pageCount, 6))].map((page, index) => {
|
||||
const pageNumber = index + currentPage
|
||||
return (
|
||||
<Box
|
||||
as="span"
|
||||
className={pageNumber === currentPage ? "active" : undefined}
|
||||
style={{
|
||||
color: colorMode === "light" ? "black" : "white",
|
||||
borderBottomColor: themeColorHex,
|
||||
}}
|
||||
key={pageNumber}
|
||||
onClick={() => onChange(pageNumber)}
|
||||
>
|
||||
{pageNumber}
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
|
||||
<Box
|
||||
as="span"
|
||||
style={{
|
||||
color: colorMode === "light" ? "black" : "white",
|
||||
borderBottomColor: themeColorHex,
|
||||
}}
|
||||
onClick={() =>
|
||||
onChange(currentPage === pageCount ? pageCount : currentPage + 1)
|
||||
}
|
||||
>
|
||||
»
|
||||
</Box>
|
||||
</Box>*/}
|
||||
<ReactPaginate
|
||||
previousLabel={<>«</>}
|
||||
nextLabel={<>»</>}
|
||||
breakLabel={"..."}
|
||||
breakClassName={"page"}
|
||||
pageCount={pageCount}
|
||||
marginPagesDisplayed={2}
|
||||
pageRangeDisplayed={3}
|
||||
onPageChange={({ selected }) => onChange(selected)}
|
||||
containerClassName="pagination"
|
||||
pageClassName="page"
|
||||
previousClassName="page"
|
||||
nextClassName="page"
|
||||
pageLinkClassName="page"
|
||||
activeLinkClassName="active-page"
|
||||
disabledClassName="disabled-page"
|
||||
//subContainerClassName={'pages pagination'}
|
||||
activeClassName={"active"}
|
||||
/>
|
||||
</>
|
||||
<ReactPaginate
|
||||
previousLabel={<>«</>}
|
||||
nextLabel={<>»</>}
|
||||
breakLabel={"..."}
|
||||
breakClassName={"page"}
|
||||
pageCount={pageCount}
|
||||
marginPagesDisplayed={2}
|
||||
pageRangeDisplayed={3}
|
||||
onPageChange={({ selected }) => onChange(selected)}
|
||||
containerClassName="pagination"
|
||||
pageClassName="page"
|
||||
previousClassName="page"
|
||||
nextClassName="page"
|
||||
pageLinkClassName="page"
|
||||
activeLinkClassName="active-page"
|
||||
disabledClassName="disabled-page"
|
||||
activeClassName={"active"}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Avatar, BoxProps } from "@chakra-ui/core"
|
|||
interface UserAvatarProps {
|
||||
name: string
|
||||
twitterName?: string
|
||||
size?: undefined | "2xl" | "xs" | "sm"
|
||||
size?: undefined | "2xl" | "lg" | "xs" | "sm"
|
||||
}
|
||||
|
||||
const UserAvatar: React.FC<UserAvatarProps & BoxProps> = ({
|
||||
|
|
|
|||
170
frontend-react/src/components/freeagents/FreeAgentCard.tsx
Normal file
170
frontend-react/src/components/freeagents/FreeAgentCard.tsx
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
import React, { useContext, useState } from "react"
|
||||
import { Flex, Box, Image, IconButton, Heading } from "@chakra-ui/core"
|
||||
import { FreeAgentPost } from "../../types"
|
||||
import UserAvatar from "../common/UserAvatar"
|
||||
import { Link } from "@reach/router"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
import { FaTwitter, FaPlus, FaMinus } from "react-icons/fa"
|
||||
import { top500 } from "../../assets/imageImports"
|
||||
import { countries } from "../../utils/lists"
|
||||
import Flag from "../common/Flag"
|
||||
import RoleIcons from "./RoleIcons"
|
||||
import WeaponImage from "../common/WeaponImage"
|
||||
import VCIcon from "./VCIcon"
|
||||
|
||||
interface FreeAgentCardProps {
|
||||
post: FreeAgentPost
|
||||
}
|
||||
|
||||
const hasExtraInfo = (post: FreeAgentPost) => {
|
||||
const { activity, description, looking_for, past_experience } = post
|
||||
if (!activity && !description && !looking_for && !past_experience) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const FreeAgentCard: React.FC<FreeAgentCardProps> = ({ post }) => {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const { grayWithShade, themeColorWithShade } = useContext(MyThemeContext)
|
||||
const { discord_user } = post
|
||||
const canBeExpanded = hasExtraInfo(post)
|
||||
return (
|
||||
<Flex
|
||||
rounded="lg"
|
||||
overflow="hidden"
|
||||
boxShadow="0px 0px 16px 6px rgba(0,0,0,0.1)"
|
||||
p="25px"
|
||||
flexDirection="column"
|
||||
justifyContent="space-between"
|
||||
maxW="500px"
|
||||
>
|
||||
<Flex justifyContent="space-between" flexWrap="wrap" alignItems="center">
|
||||
<Box color="#999999" width="50px" m="1em">
|
||||
{new Date(parseInt(post.createdAt)).toLocaleDateString()}
|
||||
</Box>
|
||||
<Box width="50px" m="1em">
|
||||
{discord_user.top500 && (
|
||||
<Image
|
||||
src={top500}
|
||||
alt="x rank top 500 logo"
|
||||
height="40px"
|
||||
width="auto"
|
||||
title="Free agent has reached Top 500 in X Rank"
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex flexDirection="column" alignItems="center">
|
||||
<UserAvatar
|
||||
twitterName={discord_user.twitter_name}
|
||||
name={discord_user.username}
|
||||
mr="5px"
|
||||
size="lg"
|
||||
/>
|
||||
|
||||
<Box
|
||||
as="span"
|
||||
fontWeight="semibold"
|
||||
letterSpacing="wide"
|
||||
fontSize="md"
|
||||
mt="0.5em"
|
||||
>
|
||||
<Flex alignItems="center">
|
||||
<Link to={`/u/${discord_user.discord_id}`}>
|
||||
{discord_user.username}#{discord_user.discriminator}
|
||||
</Link>
|
||||
{discord_user.twitter_name && (
|
||||
<a href={`https://twitter.com/${discord_user.twitter_name}`}>
|
||||
<Box color="#1DA1F2" as={FaTwitter} ml="0.5em" />
|
||||
</a>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box color={grayWithShade}>
|
||||
{discord_user.country && (
|
||||
<>
|
||||
<Flag code={discord_user.country} />
|
||||
{countries.find(obj => obj.code === discord_user.country)?.name}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex flexDirection="column" alignItems="center" mt="1em">
|
||||
<Flex justifyContent="center" w="250px">
|
||||
{discord_user?.weapons &&
|
||||
discord_user.weapons.map(wpn => (
|
||||
<Box mx="0.3em" key={wpn}>
|
||||
<WeaponImage englishName={wpn} size="SMALL" />
|
||||
</Box>
|
||||
))}
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex justifyContent="center" mt="2em">
|
||||
<Box mr="0.5em">
|
||||
<RoleIcons playstyles={post.playstyles} />
|
||||
</Box>
|
||||
<Box borderLeft="1px solid" borderColor="#999999" ml="0.5em" px="1em">
|
||||
<VCIcon canVC={post.can_vc} />
|
||||
</Box>
|
||||
</Flex>
|
||||
{canBeExpanded && (
|
||||
<Flex mt="2em" justifyContent="center">
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
aria-label="More information"
|
||||
isRound
|
||||
fontSize="20px"
|
||||
icon={expanded ? FaMinus : FaPlus}
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
{expanded && (
|
||||
<Box whiteSpace="pre-wrap">
|
||||
{post.activity && (
|
||||
<Box>
|
||||
<Heading size="md" color={themeColorWithShade}>
|
||||
Activity
|
||||
</Heading>
|
||||
{post.activity}
|
||||
</Box>
|
||||
)}
|
||||
{post.looking_for && (
|
||||
<Box mt={post.activity ? "1em" : undefined}>
|
||||
<Heading size="md" color={themeColorWithShade}>
|
||||
Looking for
|
||||
</Heading>
|
||||
{post.looking_for}
|
||||
</Box>
|
||||
)}
|
||||
{post.past_experience && (
|
||||
<Box mt={post.activity || post.looking_for ? "1em" : undefined}>
|
||||
<Heading size="md" color={themeColorWithShade}>
|
||||
Past experience
|
||||
</Heading>
|
||||
{post.past_experience}
|
||||
</Box>
|
||||
)}
|
||||
{post.description && (
|
||||
<Box
|
||||
mt={
|
||||
post.activity || post.looking_for || post.past_experience
|
||||
? "1em"
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Heading size="md" color={themeColorWithShade}>
|
||||
Description
|
||||
</Heading>
|
||||
{post.description}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
export default FreeAgentCard
|
||||
|
|
@ -11,13 +11,15 @@ import { FREE_AGENT_POSTS } from "../../graphql/queries/freeAgentPosts"
|
|||
import Loading from "../common/Loading"
|
||||
import Error from "../common/Error"
|
||||
import { RouteComponentProps } from "@reach/router"
|
||||
import PostsAccordion from "./PostsAccordion"
|
||||
import PostsAccordion from "./Posts"
|
||||
import PageHeader from "../common/PageHeader"
|
||||
import { Helmet } from "react-helmet-async"
|
||||
import WeaponSelector from "../common/WeaponSelector"
|
||||
import RadioGroup from "../elements/RadioGroup"
|
||||
import Box from "../elements/Box"
|
||||
import { continents } from "../../utils/lists"
|
||||
import { Collapse } from "@chakra-ui/core"
|
||||
import Button from "../elements/Button"
|
||||
|
||||
const playstyleToEnum = {
|
||||
"Frontline/Slayer": "FRONTLINE",
|
||||
|
|
@ -27,6 +29,7 @@ const playstyleToEnum = {
|
|||
|
||||
const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
|
||||
const [weapon, setWeapon] = useState<Weapon | null>(null)
|
||||
const [show, setShow] = useState(false)
|
||||
const [playstyle, setPlaystyle] = useState<
|
||||
"Any" | "Frontline/Slayer" | "Midline/Support" | "Backline/Anchor"
|
||||
>("Any")
|
||||
|
|
@ -96,35 +99,40 @@ const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
|
|||
<title>Free Agents | sendou.ink</title>
|
||||
</Helmet>
|
||||
<PageHeader title="Free Agents" />
|
||||
<Box maxW="600px" my="1em">
|
||||
<RadioGroup
|
||||
value={playstyle}
|
||||
setValue={setPlaystyle}
|
||||
label="Filter by playstyle"
|
||||
options={[
|
||||
"Any",
|
||||
"Frontline/Slayer",
|
||||
"Midline/Support",
|
||||
"Backline/Anchor",
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
<Box maxW="600px" my="1em">
|
||||
<RadioGroup
|
||||
value={region}
|
||||
setValue={setRegion}
|
||||
label="Filter by region"
|
||||
options={["Any", "Europe", "The Americas", "Oceania", "Other"]}
|
||||
/>
|
||||
</Box>
|
||||
<Box maxW="600px" my="1em">
|
||||
<WeaponSelector
|
||||
label="Filter by weapon"
|
||||
value={weapon}
|
||||
setValue={(weapon: string) => setWeapon(weapon as Weapon)}
|
||||
clearable
|
||||
/>
|
||||
</Box>
|
||||
<Button onClick={() => setShow(!show)}>
|
||||
{show ? "Hide filters" : "Show filters"}
|
||||
</Button>
|
||||
<Collapse mt={4} isOpen={show}>
|
||||
<Box maxW="600px" my="1em">
|
||||
<RadioGroup
|
||||
value={playstyle}
|
||||
setValue={setPlaystyle}
|
||||
label="Filter by playstyle"
|
||||
options={[
|
||||
"Any",
|
||||
"Frontline/Slayer",
|
||||
"Midline/Support",
|
||||
"Backline/Anchor",
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
<Box maxW="600px" my="1em">
|
||||
<RadioGroup
|
||||
value={region}
|
||||
setValue={setRegion}
|
||||
label="Filter by region"
|
||||
options={["Any", "Europe", "The Americas", "Oceania", "Other"]}
|
||||
/>
|
||||
</Box>
|
||||
<Box maxW="600px" my="1em">
|
||||
<WeaponSelector
|
||||
label="Filter by weapon"
|
||||
value={weapon}
|
||||
setValue={(weapon: string) => setWeapon(weapon as Weapon)}
|
||||
clearable
|
||||
/>
|
||||
</Box>
|
||||
</Collapse>
|
||||
<PostsAccordion posts={faPosts.filter(postsFilter)} />
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
52
frontend-react/src/components/freeagents/Posts.tsx
Normal file
52
frontend-react/src/components/freeagents/Posts.tsx
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import React, { useState } from "react"
|
||||
import { FreeAgentPost } from "../../types"
|
||||
import { Grid, Box, Heading } from "@chakra-ui/core"
|
||||
import Alert from "../elements/Alert"
|
||||
import FreeAgentCard from "./FreeAgentCard"
|
||||
import InfiniteScroll from "react-infinite-scroller"
|
||||
import Button from "../elements/Button"
|
||||
|
||||
interface PostsAccordionProps {
|
||||
posts: FreeAgentPost[]
|
||||
}
|
||||
|
||||
const Posts: React.FC<PostsAccordionProps> = ({ posts }) => {
|
||||
const [agentsToShow, setAgentsToShow] = useState(5)
|
||||
|
||||
if (posts.length === 0) {
|
||||
return (
|
||||
<Alert status="info">No free agents found with the current filter</Alert>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Grid
|
||||
gridGap="1em"
|
||||
gridTemplateColumns="repeat(auto-fit, minmax(260px, 1fr))"
|
||||
mt="1em"
|
||||
>
|
||||
<InfiniteScroll
|
||||
pageStart={1}
|
||||
loadMore={page => setAgentsToShow(page * 10)}
|
||||
hasMore={agentsToShow < posts.length}
|
||||
>
|
||||
{posts
|
||||
.filter((post, index) => index < agentsToShow)
|
||||
.map(post => (
|
||||
<FreeAgentCard key={post.id} post={post} />
|
||||
))}
|
||||
</InfiniteScroll>
|
||||
</Grid>
|
||||
<Box w="50%" textAlign="center" mx="auto" mt="1em">
|
||||
<Heading size="sm">No more free agents to show</Heading>
|
||||
<Box mt="1em">
|
||||
<Button outlined onClick={() => window.scrollTo(0, 0)}>
|
||||
Return to the top
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Posts
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
import React from "react"
|
||||
import { FreeAgentPost } from "../../types"
|
||||
import {
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
AccordionHeader,
|
||||
AccordionIcon,
|
||||
AccordionPanel,
|
||||
Box,
|
||||
Grid,
|
||||
Flex,
|
||||
Heading,
|
||||
} from "@chakra-ui/core"
|
||||
import { useContext } from "react"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
import UserAvatar from "../common/UserAvatar"
|
||||
import { Link } from "@reach/router"
|
||||
import Flag from "../common/Flag"
|
||||
import { countries } from "../../utils/lists"
|
||||
import { FaTwitter } from "react-icons/fa"
|
||||
import WeaponImage from "../common/WeaponImage"
|
||||
import RoleIcons from "./RoleIcons"
|
||||
import VCIcon from "./VCIcon"
|
||||
import Alert from "../elements/Alert"
|
||||
|
||||
interface PostsAccordionProps {
|
||||
posts: FreeAgentPost[]
|
||||
}
|
||||
|
||||
const hasExtraInfo = (post: FreeAgentPost) => {
|
||||
const { activity, description, looking_for, past_experience } = post
|
||||
if (!activity && !description && !looking_for && !past_experience) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const PostsAccordion: React.FC<PostsAccordionProps> = ({ posts }) => {
|
||||
const { darkerBgColor } = useContext(MyThemeContext)
|
||||
|
||||
if (posts.length === 0) {
|
||||
return (
|
||||
<Alert status="info">No free agents found with the current filter</Alert>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Accordion allowMultiple>
|
||||
{posts.map(post => {
|
||||
const { discord_user } = post
|
||||
const canBeExpanded = hasExtraInfo(post)
|
||||
return (
|
||||
<AccordionItem key={post.id}>
|
||||
<AccordionHeader cursor={canBeExpanded ? undefined : "default"}>
|
||||
{canBeExpanded ? (
|
||||
<AccordionIcon size="2em" mr="1em" />
|
||||
) : (
|
||||
<Box w="2em" h="2em" mr="1em" />
|
||||
)}
|
||||
<Grid
|
||||
gridTemplateColumns="repeat(auto-fill, 200px)"
|
||||
width="100%"
|
||||
rowGap="0.7em"
|
||||
justifyItems="center"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Link to={`/u/${discord_user.discord_id}`}>
|
||||
<Flex alignItems="center">
|
||||
<UserAvatar
|
||||
twitterName={discord_user.twitter_name}
|
||||
name={discord_user.username}
|
||||
mr="5px"
|
||||
/>
|
||||
<span>
|
||||
{discord_user.username}#{discord_user.discriminator}
|
||||
</span>
|
||||
</Flex>
|
||||
</Link>
|
||||
<Box>
|
||||
{discord_user.country && (
|
||||
<>
|
||||
<Flag code={discord_user.country} />
|
||||
{
|
||||
countries.find(obj => obj.code === discord_user.country)
|
||||
?.name
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
<Flex alignItems="center">
|
||||
{discord_user.twitter_name && (
|
||||
<>
|
||||
<Box as={FaTwitter} />
|
||||
<Box as="span" ml="5px">
|
||||
{discord_user.twitter_name}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
<Box color="#999999">
|
||||
{new Date(parseInt(post.createdAt)).toLocaleDateString()}
|
||||
</Box>
|
||||
<Flex alignItems="center">
|
||||
{discord_user?.weapons &&
|
||||
discord_user.weapons.map(wpn => (
|
||||
<Box mx="0.3em" key={wpn}>
|
||||
<WeaponImage englishName={wpn} size="SMALL" />
|
||||
</Box>
|
||||
))}
|
||||
</Flex>
|
||||
<RoleIcons playstyles={post.playstyles} />
|
||||
<VCIcon canVC={post.can_vc} />
|
||||
</Grid>
|
||||
</AccordionHeader>
|
||||
{canBeExpanded && (
|
||||
<AccordionPanel
|
||||
mt="3px"
|
||||
py={4}
|
||||
background={darkerBgColor}
|
||||
whiteSpace="pre-wrap"
|
||||
textAlign="center"
|
||||
>
|
||||
{post.activity && (
|
||||
<Box>
|
||||
<Heading size="md">Activity</Heading>
|
||||
{post.activity}
|
||||
</Box>
|
||||
)}
|
||||
{post.looking_for && (
|
||||
<Box mt={post.activity ? "1em" : undefined}>
|
||||
<Heading size="md">Looking for</Heading>
|
||||
{post.looking_for}
|
||||
</Box>
|
||||
)}
|
||||
{post.past_experience && (
|
||||
<Box
|
||||
mt={post.activity || post.looking_for ? "1em" : undefined}
|
||||
>
|
||||
<Heading size="md">Past experience</Heading>
|
||||
{post.past_experience}
|
||||
</Box>
|
||||
)}
|
||||
{post.description && (
|
||||
<Box
|
||||
mt={
|
||||
post.activity || post.looking_for || post.past_experience
|
||||
? "1em"
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Heading size="md">Description</Heading>
|
||||
{post.description}
|
||||
</Box>
|
||||
)}
|
||||
</AccordionPanel>
|
||||
)}
|
||||
</AccordionItem>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
)
|
||||
}
|
||||
|
||||
export default PostsAccordion
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react"
|
||||
import { RouteComponentProps } from "@reach/router"
|
||||
import { Image, Heading, Flex, Box } from "@chakra-ui/core"
|
||||
import { Image, Heading, Flex } from "@chakra-ui/core"
|
||||
import { posterGirl } from "../../assets/imageImports"
|
||||
import { useContext } from "react"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user