fa page first version

This commit is contained in:
Sendou 2020-01-31 02:24:07 +02:00
parent b94174f6fb
commit 8f46cfa6e6
15 changed files with 401 additions and 165 deletions

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<link id="favicon" rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#232946" />
<meta name="description" content="Competitive Splatoon hub" />

View File

@ -5,7 +5,6 @@ import {
Popover,
PopoverTrigger,
PopoverContent,
PopoverHeader,
PopoverArrow,
PopoverCloseButton,
PopoverBody,
@ -19,9 +18,7 @@ import MyThemeContext from "../themeContext"
import PageHeader from "../components/common/PageHeader"
import { Helmet } from "react-helmet-async"
interface CalendarPageProps {}
const CalendarPage: React.FC<RouteComponentProps> = ({}) => {
const CalendarPage: React.FC<RouteComponentProps> = () => {
const {
themeColor,
darkerBgColor,

View File

@ -1,5 +1,5 @@
import React from "react"
import { Avatar } from "@chakra-ui/core"
import { Avatar, BoxProps } from "@chakra-ui/core"
interface UserAvatarProps {
name: string
@ -7,12 +7,18 @@ interface UserAvatarProps {
size?: undefined | "2xl"
}
const UserAvatar: React.FC<UserAvatarProps> = ({ name, twitterName, size }) => {
const UserAvatar: React.FC<UserAvatarProps & BoxProps> = ({
name,
twitterName,
size,
...props
}) => {
return (
<Avatar
name={name}
src={`https://avatars.io/twitter/${twitterName}`}
size={size}
{...props}
/>
)
}

View File

@ -0,0 +1,40 @@
import React from "react"
import { useQuery } from "@apollo/react-hooks"
import { USER } from "../../graphql/queries/user"
import { UserData, FreeAgentPostsData } from "../../types"
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 PageHeader from "../common/PageHeader"
import { Helmet } from "react-helmet-async"
const FreeAgentsPage: React.FC<RouteComponentProps> = () => {
const { data, error, loading } = useQuery<FreeAgentPostsData>(
FREE_AGENT_POSTS
)
const {
data: userData,
error: userQueryError,
loading: userQueryLoading,
} = useQuery<UserData>(USER)
if (loading || userQueryLoading || !data) return <Loading />
if (error) return <Error errorMessage={error.message} />
if (userQueryError) return <Error errorMessage={userQueryError.message} />
const faPosts = data.freeAgentPosts
return (
<>
<Helmet>
<title>Free Agents | sendou.ink</title>
</Helmet>
<PageHeader title="Free Agents" />
<PostsAccordion posts={faPosts} />
</>
)
}
export default FreeAgentsPage

View File

@ -0,0 +1,166 @@
import React from "react"
import { FreeAgentPost } from "../../types"
import {
Accordion,
AccordionItem,
AccordionHeader,
AccordionIcon,
AccordionPanel,
SimpleGrid,
Box,
Grid,
Icon,
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"
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)
return (
<Accordion allowMultiple>
{posts
.filter(post => !post.hidden)
.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(4, 1fr)"
width="100%"
rowGap="0.5em"
justifyItems="start"
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"
>
{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>
)
}
/*activity?: string
looking_for?: string
past_experience?: string
description?: string*/
export default PostsAccordion

View File

@ -0,0 +1,41 @@
import React from "react"
import { Flex, Box } from "@chakra-ui/core"
import { FaCrosshairs, FaBriefcaseMedical, FaAnchor } from "react-icons/fa"
interface RoleIconsProps {
playstyles: ("FRONTLINE" | "MIDLINE" | "BACKLINE")[]
}
const RoleIcons: React.FC<RoleIconsProps> = ({ playstyles }) => {
return (
<Flex>
<Box
as={FaCrosshairs}
w="30px"
h="auto"
color={playstyles.indexOf("FRONTLINE") === -1 ? "grey" : "green.500"}
title="Frontline/Slayer"
cursor="help"
/>
<Box
as={FaBriefcaseMedical}
w="30px"
h="auto"
color={playstyles.indexOf("MIDLINE") === -1 ? "grey" : "green.500"}
title="Midline/Support"
mx="10px"
cursor="help"
/>
<Box
as={FaAnchor}
w="30px"
h="auto"
color={playstyles.indexOf("BACKLINE") === -1 ? "grey" : "green.500"}
title="Backline/Anchor"
cursor="help"
/>
</Flex>
)
}
export default RoleIcons

View File

@ -0,0 +1,34 @@
import React from "react"
import { Box } from "@chakra-ui/core"
import { FaMicrophone } from "react-icons/fa"
interface VCIconProps {
canVC: "YES" | "USUALLY" | "SOMETIMES" | "NO"
}
const color = {
YES: "green.500",
USUALLY: "yellow.500",
SOMETIMES: "yellow.500",
NO: "red.500",
}
const title = {
YES: "Can VC",
USUALLY: "Can VC usually",
SOMETIMES: "Can VC sometimes",
NO: "Can't VC",
}
const VCIcon: React.FC<VCIconProps> = ({ canVC }) => (
<Box
as={FaMicrophone}
title={title[canVC]}
color={color[canVC]}
w="30px"
h="auto"
cursor="help"
/>
)
export default VCIcon

View File

@ -7,7 +7,7 @@ import MyThemeContext from "../../themeContext"
import "./HomePage.css"
import { Helmet } from "react-helmet-async"
const HomePage: React.FC<RouteComponentProps> = ({}) => {
const HomePage: React.FC<RouteComponentProps> = () => {
const { colorMode, grayWithShade } = useContext(MyThemeContext)
return (
<>

View File

@ -5,7 +5,6 @@ import { useHotkeys } from "react-hotkeys-hook"
import { Box, Flex, Tooltip, IconButton } from "@chakra-ui/core"
import {
FaPencilAlt,
FaSquareFull,
FaRegSquare,
FaRegCircle,
FaRegObjectGroup,
@ -74,137 +73,73 @@ const DraggableToolsSelector: React.FC<DraggableToolsSelectorProps> = ({
</Box>
</strong>
<Flex flexWrap="wrap" justifyContent="center">
<Tooltip
hasArrow
label="Pencil (P)"
placement="top"
aria-label="Pencil tooltip"
zIndex={999}
closeOnClick
>
<IconButton
onClick={() => setTool(Tools.Pencil)}
variant="ghost"
size="lg"
aria-label="Pencil tool"
icon={FaPencilAlt}
/>
</Tooltip>
<Tooltip
hasArrow
label="Line (L)"
placement="top"
aria-label="Line tooltip"
zIndex={999}
closeOnClick
>
<IconButton
onClick={() => setTool(Tools.Line)}
variant="ghost"
size="lg"
aria-label="Line tool"
icon={AiOutlineLine}
/>
</Tooltip>
<Tooltip
hasArrow
label="Rectangle (R)"
placement="top"
aria-label="Rectangle tooltip"
zIndex={999}
closeOnClick
>
<IconButton
onClick={() => setTool(Tools.Rectangle)}
variant="ghost"
size="lg"
aria-label="Rectangle tool"
icon={FaRegSquare}
/>
</Tooltip>
<Tooltip
hasArrow
label="Circle (C)"
placement="top"
aria-label="Circle tooltip"
zIndex={999}
closeOnClick
>
<IconButton
onClick={() => setTool(Tools.Circle)}
variant="ghost"
size="lg"
aria-label="Circle tool"
icon={FaRegCircle}
/>
</Tooltip>
<Tooltip
hasArrow
label="Select (S)"
placement="top"
aria-label="Select tooltip"
zIndex={999}
closeOnClick
>
<IconButton
onClick={() => setTool(Tools.Select)}
variant="ghost"
size="lg"
aria-label="Select tool"
icon={FaRegObjectGroup}
/>
</Tooltip>
<Tooltip
hasArrow
label="Delete selected"
placement="top"
aria-label="Delete tooltip"
zIndex={999}
closeOnClick
>
<IconButton
onClick={() => removeSelected()}
isDisabled={removeIsDisabled}
variant="ghost"
size="lg"
aria-label="Delete tool"
icon={FaTrashAlt}
/>
</Tooltip>
<Tooltip
hasArrow
label="Undo"
placement="top"
aria-label="Undo tooltip"
zIndex={999}
closeOnClick
>
<IconButton
onClick={() => undo()}
isDisabled={undoIsDisabled}
variant="ghost"
size="lg"
aria-label="Undo tool"
icon={FaUndo}
/>
</Tooltip>
<Tooltip
hasArrow
label="Redo"
placement="top"
aria-label="Redo tooltip"
zIndex={999}
closeOnClick
>
<IconButton
onClick={() => redo()}
isDisabled={redoIsDisabled}
variant="ghost"
size="lg"
aria-label="Redo tool"
icon={FaRedo}
/>
</Tooltip>
<IconButton
onClick={() => setTool(Tools.Pencil)}
variant="ghost"
size="lg"
aria-label="Pencil tool"
icon={FaPencilAlt}
title="Pencil (P)"
/>
<IconButton
onClick={() => setTool(Tools.Line)}
variant="ghost"
size="lg"
aria-label="Line tool"
icon={AiOutlineLine}
title="Line (L)"
/>
<IconButton
onClick={() => setTool(Tools.Rectangle)}
variant="ghost"
size="lg"
aria-label="Rectangle tool"
icon={FaRegSquare}
title="Rectangle (R)"
/>
<IconButton
onClick={() => setTool(Tools.Circle)}
variant="ghost"
size="lg"
aria-label="Circle tool"
icon={FaRegCircle}
title="Circle (C)"
/>
<IconButton
onClick={() => setTool(Tools.Select)}
variant="ghost"
size="lg"
aria-label="Select tool"
icon={FaRegObjectGroup}
title="Select (S)"
/>
<IconButton
onClick={() => removeSelected()}
isDisabled={removeIsDisabled}
variant="ghost"
size="lg"
aria-label="Delete tool"
icon={FaTrashAlt}
title="Delete selected"
/>
<IconButton
onClick={() => undo()}
isDisabled={undoIsDisabled}
variant="ghost"
size="lg"
aria-label="Undo tool"
icon={FaUndo}
title="Undo"
/>
<IconButton
onClick={() => redo()}
isDisabled={redoIsDisabled}
variant="ghost"
size="lg"
aria-label="Redo tool"
icon={FaRedo}
title="Redo"
/>
</Flex>
</Box>
</Draggable>

View File

@ -9,7 +9,6 @@ import { Helmet } from "react-helmet-async"
import { Weapon } from "../../types"
import Error from "../common/Error"
import {
IconButton,
Button,
Flex,
Box,
@ -17,17 +16,8 @@ import {
Input,
InputRightElement,
} from "@chakra-ui/core"
import { MdUndo, MdRedo } from "react-icons/md"
import MyThemeContext from "../../themeContext"
import {
FaTrashAlt,
FaUndo,
FaRedo,
FaCloudDownloadAlt,
FaFileDownload,
FaFileUpload,
FaFileImage,
} from "react-icons/fa"
import { FaFileDownload, FaFileUpload, FaFileImage } from "react-icons/fa"
import reef from "../../assets/plannerMaps/reef-sz.png"
import MapSelect from "./MapSelect"
import { RouteComponentProps } from "@reach/router"

View File

@ -1,10 +1,5 @@
import React from "react"
import {
Box,
useColorMode,
useTheme as useChakraTheme,
Flex,
} from "@chakra-ui/core"
import { Box, useColorMode, useTheme as useChakraTheme } from "@chakra-ui/core"
import { MenuBar } from "./MenuBar"
import SideNav from "./SideNav"
@ -15,6 +10,7 @@ import { MyThemeProvider } from "../../themeContext"
import { Theme } from "../../types"
import { Helmet } from "react-helmet-async"
import Footer from "./Footer"
import { useEffect } from "react"
const App: React.FC = () => {
const chakraTheme = useChakraTheme()
@ -52,11 +48,14 @@ const App: React.FC = () => {
} as Theme,
}
useEffect(() => {
const favicon = document.getElementById("favicon") as HTMLLinkElement
if (!favicon) return
favicon.href = `/favicon_${themeColor}.png`
}, [themeColorFromStorage])
return (
<MyThemeProvider value={theme[colorMode]}>
<Helmet>
<link rel="icon" type="image/png" href={`/favicon_${themeColor}.png`} />
</Helmet>
<Box
marginLeft={[null, null, "250px"]}
mt={["4em", null, "0"]}

View File

@ -1,12 +1,13 @@
import React, { Suspense, lazy } from "react"
import { Router } from "@reach/router"
import Loading from "../common/Loading"
import MapPlannerPage from "../plans/MapPlannerPage"
import CalendarPage from "../../calendar/CalendarPage"
const HomePage = lazy(() => import("../home/HomePage"))
const UserPage = lazy(() => import("../user/UserPage"))
const BuildsPage = lazy(() => import("../builds/BuildsPage"))
const CalendarPage = lazy(() => import("../../calendar/CalendarPage"))
const MapPlannerPage = lazy(() => import("../plans/MapPlannerPage"))
const FreeAgentsPage = lazy(() => import("../freeagents/FreeAgentsPage"))
const Routes: React.FC = () => {
return (
@ -17,6 +18,7 @@ const Routes: React.FC = () => {
<BuildsPage path="/builds" />
<MapPlannerPage path="/plans" />
<CalendarPage path="/calendar" />
<FreeAgentsPage path="/freeagents" />
</Router>
</Suspense>
)

View File

@ -1,6 +1,6 @@
import React from "react"
import { Build } from "../../types"
import { Flex, Box } from "@chakra-ui/core"
import { Flex } from "@chakra-ui/core"
import BuildCard from "../builds/BuildCard"
import useLocalStorage from "@rehooks/local-storage"
@ -16,6 +16,7 @@ const BuildTab: React.FC<BuildTabProps> = ({ builds, canModifyBuilds }) => {
<Flex flexWrap="wrap" justifyContent="center">
{builds.map(build => (
<BuildCard
key={build.id}
build={build}
defaultToAPView={APView !== null ? APView : false}
m="0.5em"

View File

@ -1,6 +1,6 @@
import { gql } from "apollo-boost"
import { gql, DocumentNode } from "apollo-boost"
export const freeAgentPosts = gql`
export const FREE_AGENT_POSTS: DocumentNode = gql`
{
freeAgentPosts {
id

View File

@ -128,6 +128,27 @@ export interface Placement {
year: number
}
export interface FreeAgentPost {
id: string
can_vc: "YES" | "USUALLY" | "SOMETIMES" | "NO"
playstyles: ("FRONTLINE" | "MIDLINE" | "BACKLINE")[]
activity?: string
looking_for?: string
past_experience?: string
description?: string
hidden: boolean
createdAt: string
discord_user: {
username: string
discriminator: string
discord_id: string
twitter_name?: string
country?: CountryCode
weapons?: Weapon[]
top500: boolean
}
}
//==============================================================================
// Apollo
//==============================================================================
@ -163,3 +184,7 @@ export interface PlayerInfoData {
export interface PlayerInfoVars {
twitter: string
}
export interface FreeAgentPostsData {
freeAgentPosts: FreeAgentPost[]
}