mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-22 11:05:09 -05:00
tournament page ready
This commit is contained in:
@@ -1,115 +1,92 @@
|
||||
import React, { useState, useEffect, useRef } from "react"
|
||||
import jstz from "jstz"
|
||||
import React, { useContext } from "react"
|
||||
import { useQuery } from "@apollo/react-hooks"
|
||||
import {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
PopoverArrow,
|
||||
PopoverCloseButton,
|
||||
PopoverBody,
|
||||
Box,
|
||||
Link,
|
||||
Icon,
|
||||
} from "@chakra-ui/core"
|
||||
UPCOMING_EVENTS,
|
||||
UpcomingEventsData,
|
||||
} from "../../graphql/queries/upcomingEvents"
|
||||
import { RouteComponentProps } from "@reach/router"
|
||||
import { useContext } from "react"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
import PageHeader from "../common/PageHeader"
|
||||
import { Helmet } from "react-helmet-async"
|
||||
import Button from "../elements/Button"
|
||||
import Loading from "../common/Loading"
|
||||
import Error from "../common/Error"
|
||||
import { getWeek, ordinal_suffix_of } from "../../utils/helperFunctions"
|
||||
import SubHeader from "../common/SubHeader"
|
||||
import { Box, Badge, Flex } from "@chakra-ui/core"
|
||||
import { months, days } from "../../utils/lists"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
import TournamentInfo from "./TournamentInfo"
|
||||
|
||||
const CalendarPage: React.FC<RouteComponentProps> = () => {
|
||||
const { darkerBgColor, themeColorWithShade, grayWithShade } = useContext(
|
||||
MyThemeContext
|
||||
)
|
||||
const [showCode, setShowCode] = useState(false)
|
||||
const calenderDiv = useRef<HTMLDivElement | null>(null)
|
||||
const badgeColor: { [key: string]: string } = {
|
||||
Friday: "purple",
|
||||
Saturday: "green",
|
||||
Sunday: "blue",
|
||||
} as const
|
||||
|
||||
const iFrameHTML = `<iframe title="calendar" src="https://calendar.google.com/calendar/embed?height=600&wkst=2&bgcolor=%23ffffff&src=NDNnYnJlamkxbnQyZTY1dWJuZzdvYWkxZGtAZ3JvdXAuY2FsZW5kYXIuZ29vZ2xlLmNvbQ&color=%23E67C73&showTitle=0&showNav=1&showDate=1&showPrint=0&showCalendars=0&ctz=${jstz
|
||||
.determine()
|
||||
.name()}" style=border-width: 0 width=${"90%"} height="1000" frameBorder="0" scrolling="no"></iframe>`
|
||||
const CalendarPage: React.FC<RouteComponentProps> = ({}) => {
|
||||
const { darkerBgColor, grayWithShade } = useContext(MyThemeContext)
|
||||
const { data, error, loading } = useQuery<UpcomingEventsData>(UPCOMING_EVENTS)
|
||||
|
||||
//This is a weird solution but I couldn't get it working just setting the iframe src from a variable
|
||||
useEffect(() => {
|
||||
if (!calenderDiv?.current) return
|
||||
calenderDiv.current.innerHTML = iFrameHTML
|
||||
}, [iFrameHTML])
|
||||
if (loading) return <Loading />
|
||||
if (error) return <Error errorMessage={error.message} />
|
||||
|
||||
const events = data!.upcomingEvents
|
||||
|
||||
let lastPrintedWeek: number | null = null
|
||||
let lastPrintedDay: number | null = null
|
||||
let lastPrintedMonth: number | null = null
|
||||
const thisWeekNumber = getWeek(new Date())
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Competitive Calendar | sendou.ink</title>
|
||||
</Helmet>
|
||||
<PageHeader title="Competitive Calendar" />
|
||||
<Popover placement="top-start">
|
||||
<PopoverTrigger>
|
||||
<Box mt="1em">
|
||||
<Button outlined onClick={() => setShowCode(!showCode)}>
|
||||
Show emoji code
|
||||
</Button>
|
||||
</Box>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent zIndex={4} background={darkerBgColor} width="280px">
|
||||
<PopoverArrow />
|
||||
<PopoverCloseButton />
|
||||
<PopoverBody>
|
||||
<h4>
|
||||
<span role="img" aria-label="money bag emoji">
|
||||
💰
|
||||
</span>{" "}
|
||||
- Has prizes
|
||||
<br />
|
||||
<span role="img" aria-label="masks emoji">
|
||||
🎭
|
||||
</span>{" "}
|
||||
- Unconventional ruleset
|
||||
<br />
|
||||
<span role="img" aria-label="lock emoji">
|
||||
🔒
|
||||
</span>{" "}
|
||||
- Limited registration
|
||||
<br />
|
||||
<span role="img" aria-label="handshake emoji">
|
||||
🤝
|
||||
</span>{" "}
|
||||
- Local event
|
||||
<br />
|
||||
<span role="img" aria-label="dice emoji">
|
||||
🎲
|
||||
</span>{" "}
|
||||
- Solo registration available
|
||||
<br />
|
||||
<span role="img" aria-label="eyes emoji">
|
||||
👀
|
||||
</span>{" "}
|
||||
- No open registration
|
||||
</h4>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Box mt="2em" ref={calenderDiv} />
|
||||
<Box mt="2em" color={grayWithShade}>
|
||||
If an event is missing you can contact{" "}
|
||||
<Link
|
||||
href="https://twitter.com/Kbot_273"
|
||||
color={themeColorWithShade}
|
||||
isExternal
|
||||
>
|
||||
Kbot <Icon name="external-link" mx="2px" />
|
||||
</Link>{" "}
|
||||
about it.
|
||||
<br />
|
||||
You can add the calendar to your personal Google Calendar by pressing
|
||||
the plus button above. If you are using other kind of calendar program
|
||||
you can try{" "}
|
||||
<Link
|
||||
href="https://calendar.google.com/calendar/ical/43gbreji1nt2e65ubng7oai1dk%40group.calendar.google.com/public/basic.ics"
|
||||
color={themeColorWithShade}
|
||||
isExternal
|
||||
>
|
||||
this link <Icon name="external-link" mx="2px" />
|
||||
</Link>{" "}
|
||||
instead.
|
||||
{events.map((event) => {
|
||||
const time = new Date(parseInt(event.date))
|
||||
const weekNumber = getWeek(time)
|
||||
const thisDay = time.getDate()
|
||||
const thisDayOfTheWeek = days[time.getDay()]
|
||||
const thisMonth = time.getMonth()
|
||||
const printWeekHeader = weekNumber !== lastPrintedWeek
|
||||
const printDayHeader =
|
||||
thisDay !== lastPrintedDay || thisMonth !== lastPrintedMonth
|
||||
if (printWeekHeader) {
|
||||
lastPrintedWeek = weekNumber
|
||||
}
|
||||
if (printDayHeader) {
|
||||
lastPrintedDay = thisDay
|
||||
lastPrintedMonth = thisMonth
|
||||
}
|
||||
|
||||
const colorForBadge = badgeColor[thisDayOfTheWeek] ?? "red"
|
||||
|
||||
return (
|
||||
<React.Fragment key={event.discord_invite_url}>
|
||||
{printWeekHeader && (
|
||||
<Box my="2em">
|
||||
<SubHeader>
|
||||
Week {weekNumber}{" "}
|
||||
{thisWeekNumber === weekNumber && <>(This week)</>}
|
||||
</SubHeader>
|
||||
</Box>
|
||||
)}
|
||||
{printDayHeader && (
|
||||
<Flex
|
||||
bg={darkerBgColor}
|
||||
borderRadius="5px"
|
||||
p="0.5em"
|
||||
my="1.5em"
|
||||
alignItems="center"
|
||||
>
|
||||
{months[thisMonth + 1]} {thisDay}
|
||||
{ordinal_suffix_of(thisDay)}
|
||||
<Badge ml="1em" variantColor={colorForBadge}>
|
||||
{thisDayOfTheWeek}
|
||||
</Badge>
|
||||
</Flex>
|
||||
)}
|
||||
<TournamentInfo tournament={event} date={time} />
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
<Box color={grayWithShade} mt="2em">
|
||||
All events listed in your local time: {new Date().toTimeString()}
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
|
||||
73
frontend-react/src/components/calendar/TournamentInfo.tsx
Normal file
73
frontend-react/src/components/calendar/TournamentInfo.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import React, { useState, useContext } from "react"
|
||||
import { CompetitiveFeedEvent } from "../../graphql/queries/upcomingEvents"
|
||||
import Markdown from "../elements/Markdown"
|
||||
import { Heading, Box, Flex, Link as ChakraLink } from "@chakra-ui/core"
|
||||
import Button from "../elements/Button"
|
||||
import { FiInfo, FiClock } from "react-icons/fi"
|
||||
import { FaDiscord } from "react-icons/fa"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
import UserAvatar from "../common/UserAvatar"
|
||||
import { Link } from "@reach/router"
|
||||
|
||||
interface TournamentInfoProps {
|
||||
tournament: CompetitiveFeedEvent
|
||||
date: Date
|
||||
}
|
||||
|
||||
const TournamentInfo: React.FC<TournamentInfoProps> = ({
|
||||
tournament,
|
||||
date,
|
||||
}) => {
|
||||
const { themeColorWithShade, grayWithShade } = useContext(MyThemeContext)
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const poster = tournament.poster_discord_user
|
||||
return (
|
||||
<Box>
|
||||
<Heading fontFamily="'Rubik', sans-serif" size="lg">
|
||||
{tournament.name}
|
||||
</Heading>
|
||||
<Flex alignItems="center" color={grayWithShade}>
|
||||
<Box as={FiClock} mr="0.5em" color={themeColorWithShade} />
|
||||
{date.toLocaleString()}
|
||||
</Flex>
|
||||
|
||||
<Flex alignItems="center" color={grayWithShade} my="0.5em">
|
||||
{poster.twitter_name && (
|
||||
<Box mr="0.5em">
|
||||
<UserAvatar
|
||||
twitterName={poster.twitter_name}
|
||||
name={poster.username}
|
||||
size="sm"
|
||||
/>
|
||||
</Box>
|
||||
)}{" "}
|
||||
<Link to={`/u/${poster.discord_id}`}>
|
||||
<Box>
|
||||
{poster.username}#{poster.discriminator}
|
||||
</Box>
|
||||
</Link>
|
||||
</Flex>
|
||||
|
||||
<Flex flexWrap="wrap" my="1em">
|
||||
<Box mr="1em" mb="1em">
|
||||
<a href={tournament.discord_invite_url}>
|
||||
<Button outlined icon={FaDiscord} width="150px">
|
||||
Join Discord
|
||||
</Button>
|
||||
</a>
|
||||
</Box>
|
||||
<Button
|
||||
outlined={expanded}
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
icon={FiInfo}
|
||||
width="150px"
|
||||
>
|
||||
{expanded ? "Hide info" : "Show info"}
|
||||
</Button>
|
||||
</Flex>
|
||||
{expanded && <Markdown value={tournament.description} />}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default TournamentInfo
|
||||
@@ -19,7 +19,7 @@
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
border-bottom: 4px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-color: var(--sub-header-border);
|
||||
width: 592px; /* half of limiter */
|
||||
margin: 0 20px;
|
||||
|
||||
@@ -3,7 +3,7 @@ import "./SubHeader.css"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
|
||||
interface SubHeaderProps {
|
||||
children: string[] | string
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const SubHeader: React.FC<SubHeaderProps> = ({ children }) => {
|
||||
|
||||
@@ -9,6 +9,7 @@ interface ButtonProps {
|
||||
onClick?: () => void
|
||||
size?: "xs" | "sm" | "lg" | "md"
|
||||
icon?: IconType
|
||||
width?: string
|
||||
outlined?: boolean
|
||||
disabled?: boolean
|
||||
loading?: boolean
|
||||
@@ -21,6 +22,7 @@ const Button: React.FC<ButtonProps> = ({
|
||||
size,
|
||||
disabled,
|
||||
loading,
|
||||
width,
|
||||
outlined = false,
|
||||
}) => {
|
||||
const { themeColor } = useContext(MyThemeContext)
|
||||
@@ -33,6 +35,7 @@ const Button: React.FC<ButtonProps> = ({
|
||||
size={size}
|
||||
isDisabled={disabled}
|
||||
isLoading={loading}
|
||||
width={width ?? undefined}
|
||||
>
|
||||
{children}
|
||||
</ChakraButton>
|
||||
|
||||
@@ -16,13 +16,7 @@ const HomePage: React.FC<RouteComponentProps> = () => {
|
||||
<title>sendou.ink | Competitive Splatoon Hub</title>
|
||||
</Helmet>
|
||||
<Flex flexDirection="column" alignItems="center">
|
||||
<Image
|
||||
className="rgb"
|
||||
src={posterGirl[colorMode]}
|
||||
w="400px"
|
||||
h="auto"
|
||||
title="Picture by borzoic (@borzoic_ on Twitter)"
|
||||
/>
|
||||
<Image className="rgb" src={posterGirl[colorMode]} w="400px" h="auto" />
|
||||
<Heading
|
||||
letterSpacing="1px"
|
||||
fontFamily="'Rubik', sans-serif"
|
||||
|
||||
@@ -12,7 +12,7 @@ const About: React.FC<RouteComponentProps> = () => {
|
||||
<title>About | sendou.ink</title>
|
||||
</Helmet>
|
||||
<div style={{ marginTop: "1em" }}>
|
||||
<Heading size="lg" mb="0.5em">
|
||||
<Heading size="lg" mb="0.5em" fontFamily="'Rubik', sans-serif">
|
||||
Help
|
||||
</Heading>
|
||||
If you need help using the site don't hesitate to ask on our Discord.
|
||||
@@ -29,20 +29,55 @@ const About: React.FC<RouteComponentProps> = () => {
|
||||
<Heading size="lg">Thanks to</Heading>
|
||||
<ul style={{ marginLeft: "1.2em", marginTop: "0.5em" }}>
|
||||
<li>
|
||||
<a href="https://twitter.com/LeanYoshi">Lean</a> (provided the Top
|
||||
500 X Rank data)
|
||||
<Link
|
||||
isExternal
|
||||
color={themeColorWithShade}
|
||||
href="https://twitter.com/LeanYoshi"
|
||||
>
|
||||
Lean
|
||||
</Link>{" "}
|
||||
(provided the Top 500 X Rank data)
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://twitter.com/zorg_z0rg_z0r8">zorg</a> (provided
|
||||
background pictures for the map planner)
|
||||
<Link
|
||||
isExternal
|
||||
color={themeColorWithShade}
|
||||
href="https://twitter.com/zorg_z0rg_z0r8"
|
||||
>
|
||||
zorg
|
||||
</Link>{" "}
|
||||
(provided background pictures for the map planner)
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://twitter.com/ganbawoomy">ganbawoomy</a> (provided
|
||||
the data for tournament browser)
|
||||
<Link
|
||||
isExternal
|
||||
color={themeColorWithShade}
|
||||
href="https://twitter.com/ganbawoomy"
|
||||
>
|
||||
ganbawoomy
|
||||
</Link>{" "}
|
||||
(provided the data for tournament browser)
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://twitter.com/noaim_brn">NoAim™bUrn</a> (gave plenty
|
||||
of useful feedback)
|
||||
<Link
|
||||
isExternal
|
||||
color={themeColorWithShade}
|
||||
href="https://twitter.com/noaim_brn"
|
||||
>
|
||||
NoAim™bUrn
|
||||
</Link>{" "}
|
||||
(gave plenty of useful feedback)
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
isExternal
|
||||
color={themeColorWithShade}
|
||||
href="https://twitter.com/borzoic_"
|
||||
>
|
||||
borzoic
|
||||
</Link>{" "}
|
||||
(art for the site like the inkling on the front page as well as
|
||||
footer pictures)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -27,8 +27,8 @@ import {
|
||||
FiLogOut,
|
||||
FiUser,
|
||||
FiPlus,
|
||||
FiShoppingCart,
|
||||
FiLogIn,
|
||||
FiHeart,
|
||||
} from "react-icons/fi"
|
||||
import { RiTShirt2Line } from "react-icons/ri"
|
||||
import Logo from "./Logo"
|
||||
@@ -148,11 +148,7 @@ export const SideNavContent: React.FC<SideNavProps> = ({ showLogo = true }) => {
|
||||
<NavItem to="calendar" icon={FiCalendar} title="Calendar" />
|
||||
<NavItem to="builds" icon={RiTShirt2Line} title="Builds" />
|
||||
<NavItem to="u" icon={FiUsers} title="User Search" />
|
||||
<NavItem
|
||||
to="freeagents"
|
||||
icon={FiShoppingCart}
|
||||
title="Free Agents"
|
||||
/>
|
||||
<NavItem to="freeagents" icon={FiHeart} title="Free Agents" />
|
||||
<NavItem
|
||||
to="tournaments"
|
||||
icon={FiAward}
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import { gql, DocumentNode } from "apollo-boost"
|
||||
import { User } from "../../types"
|
||||
|
||||
export interface UpcomingEventsData {
|
||||
upcomingEvents: {
|
||||
name: string
|
||||
date: string
|
||||
description: string
|
||||
message_url: string
|
||||
discord_invite_url: string
|
||||
picture_url?: string
|
||||
poster_discord_user: {
|
||||
username: string
|
||||
discriminator: string
|
||||
twitter_name?: string
|
||||
discord_id: string
|
||||
}
|
||||
}[]
|
||||
export interface CompetitiveFeedEvent {
|
||||
name: string
|
||||
date: string
|
||||
description: string
|
||||
message_url: string
|
||||
discord_invite_url: string
|
||||
picture_url?: string
|
||||
poster_discord_user: {
|
||||
username: string
|
||||
discriminator: string
|
||||
twitter_name?: string
|
||||
discord_id: string
|
||||
}
|
||||
}
|
||||
|
||||
export const SEARCH_FOR_USER: DocumentNode = gql`
|
||||
export interface UpcomingEventsData {
|
||||
upcomingEvents: CompetitiveFeedEvent[]
|
||||
}
|
||||
|
||||
export const UPCOMING_EVENTS: DocumentNode = gql`
|
||||
{
|
||||
upcomingEvents {
|
||||
name
|
||||
|
||||
@@ -36,3 +36,11 @@ export const removeFalsy = (obj: { [key: string]: any }) => {
|
||||
})
|
||||
return newObj
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/31810991
|
||||
export const getWeek = (date: Date) => {
|
||||
const onejan = new Date(date.getFullYear(), 0, 1)
|
||||
const today = new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
||||
const dayOfYear = (today.getTime() - onejan.getTime() + 86400000) / 86400000
|
||||
return Math.ceil(dayOfYear / 7)
|
||||
}
|
||||
|
||||
@@ -1586,6 +1586,16 @@ export const months = [
|
||||
"December",
|
||||
] as const
|
||||
|
||||
export const days = [
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
] as const
|
||||
|
||||
export const weapons = [
|
||||
"Sploosh-o-matic",
|
||||
"Neo Sploosh-o-matic",
|
||||
|
||||
Reference in New Issue
Block a user